2 * Copyright (c) 2020 PANTHEON.tech, s.r.o. and others. All rights reserved.
4 * This program and the accompanying materials are made available under the
5 * terms of the Eclipse Public License v1.0 which accompanies this distribution,
6 * and is available at http://www.eclipse.org/legal/epl-v10.html
8 package org.opendaylight.yangtools.yang.model.spi.meta;
10 import static java.util.Objects.requireNonNull;
12 import com.google.common.collect.ImmutableList;
13 import com.google.common.collect.ImmutableMap;
14 import java.util.Collection;
15 import java.util.LinkedHashMap;
17 import java.util.Optional;
18 import org.eclipse.jdt.annotation.NonNull;
19 import org.opendaylight.yangtools.yang.common.Empty;
20 import org.opendaylight.yangtools.yang.common.QName;
21 import org.opendaylight.yangtools.yang.model.api.meta.DeclaredStatement;
22 import org.opendaylight.yangtools.yang.model.api.meta.EffectiveStatement;
23 import org.opendaylight.yangtools.yang.model.api.meta.IdentifierNamespace;
24 import org.opendaylight.yangtools.yang.model.api.stmt.CaseEffectiveStatement;
25 import org.opendaylight.yangtools.yang.model.api.stmt.ChoiceEffectiveStatement;
26 import org.opendaylight.yangtools.yang.model.api.stmt.DataTreeEffectiveStatement;
27 import org.opendaylight.yangtools.yang.model.api.stmt.NamespacedEffectiveStatement;
28 import org.opendaylight.yangtools.yang.model.api.stmt.SchemaTreeEffectiveStatement;
29 import org.opendaylight.yangtools.yang.model.api.stmt.TypedefEffectiveStatement;
32 * Baseline stateless implementation of an EffectiveStatement. This class adds a few default implementations and
33 * namespace dispatch, but does not actually force any state on its subclasses. This approach adds requirements for an
34 * implementation, but it leaves it up to the final class to provide object layout.
37 * This finds immense value in catering the common case, for example effective statements which can, but typically
38 * do not, contain substatements.
40 * @param <A> Argument type ({@link Empty} if statement does not have argument.)
41 * @param <D> Class representing declared version of this statement.
43 abstract class AbstractEffectiveStatement<A, D extends DeclaredStatement<A>>
44 extends AbstractModelStatement<A> implements EffectiveStatement<A, D> {
46 public final <K, V, N extends IdentifierNamespace<K, V>> Optional<V> get(final Class<N> namespace,
48 return Optional.ofNullable(getAll(namespace).get(requireNonNull(identifier)));
52 public final <K, V, N extends IdentifierNamespace<K, V>> Map<K, V> getAll(final Class<N> namespace) {
53 final Optional<? extends Map<K, V>> ret = getNamespaceContents(requireNonNull(namespace));
54 return ret.isPresent() ? ret.get() : ImmutableMap.of();
58 public Collection<? extends EffectiveStatement<?, ?>> effectiveSubstatements() {
59 return ImmutableList.of();
63 * Return the statement-specific contents of specified namespace, if available.
65 * @param namespace Requested namespace
66 * @return Namespace contents, if available.
68 protected <K, V, N extends IdentifierNamespace<K, V>> Optional<? extends Map<K, V>> getNamespaceContents(
69 final @NonNull Class<N> namespace) {
70 return Optional.empty();
74 * Utility method for recovering singleton lists squashed by {@link #maskList(ImmutableList)}.
76 * @param masked list to unmask
77 * @return Unmasked list
78 * @throws NullPointerException if masked is null
79 * @throws ClassCastException if masked object does not match EffectiveStatement
81 @SuppressWarnings({ "rawtypes", "unchecked" })
82 protected static final @NonNull ImmutableList<? extends @NonNull EffectiveStatement<?, ?>> unmaskList(
83 final @NonNull Object masked) {
84 return (ImmutableList) unmaskList(masked, EffectiveStatement.class);
87 // TODO: below methods need to find a better place, this is just a temporary hideout as their public class is on
90 * Create a Map containing the contents of the schema tree. Retur
91 * @param substatements Substatements to index
92 * @return Index of the schema tree as a mutable Map
93 * @throws NullPointerException if {@code substatements} is null
95 protected static @NonNull Map<QName, SchemaTreeEffectiveStatement<?>> createSchemaTreeNamespace(
96 final Collection<? extends EffectiveStatement<?, ?>> substatements) {
97 final Map<QName, SchemaTreeEffectiveStatement<?>> schemaChildren = new LinkedHashMap<>();
98 substatements.stream().filter(SchemaTreeEffectiveStatement.class::isInstance)
99 .forEach(child -> putChild(schemaChildren, (SchemaTreeEffectiveStatement<?>) child, "schema tree"));
100 return schemaChildren;
103 protected static @NonNull ImmutableMap<QName, DataTreeEffectiveStatement<?>> createDataTreeNamespace(
104 final Collection<SchemaTreeEffectiveStatement<?>> schemaTreeStatements,
105 // Note: this dance is needed to not retain ImmutableMap$Values
106 final ImmutableMap<QName, SchemaTreeEffectiveStatement<?>> schemaTreeNamespace) {
107 final Map<QName, DataTreeEffectiveStatement<?>> dataChildren = new LinkedHashMap<>();
108 boolean sameAsSchema = true;
110 for (SchemaTreeEffectiveStatement<?> child : schemaTreeStatements) {
111 if (!indexDataTree(dataChildren, child)) {
112 sameAsSchema = false;
116 // This is a mighty hack to lower memory usage: if we consumed all schema tree children as data nodes,
117 // the two maps are equal and hence we can share the instance.
118 return sameAsSchema ? (ImmutableMap) schemaTreeNamespace : ImmutableMap.copyOf(dataChildren);
121 protected static @NonNull ImmutableMap<QName, TypedefEffectiveStatement> createTypedefNamespace(
122 final Collection<? extends EffectiveStatement<?, ?>> substatements) {
123 final Map<QName, TypedefEffectiveStatement> typedefs = new LinkedHashMap<>();
125 substatements.stream().filter(TypedefEffectiveStatement.class::isInstance)
126 .forEach(child -> putChild(typedefs, (TypedefEffectiveStatement) child, "typedef"));
128 return ImmutableMap.copyOf(typedefs);
131 private static boolean indexDataTree(final Map<QName, DataTreeEffectiveStatement<?>> map,
132 final EffectiveStatement<?, ?> stmt) {
133 if (stmt instanceof DataTreeEffectiveStatement) {
134 putChild(map, (DataTreeEffectiveStatement<?>) stmt, "data tree");
136 } else if (stmt instanceof ChoiceEffectiveStatement) {
137 // For choice statements go through all their cases and fetch their data children
138 for (EffectiveStatement<?, ?> choiceChild : stmt.effectiveSubstatements()) {
139 if (choiceChild instanceof CaseEffectiveStatement) {
140 for (EffectiveStatement<?, ?> caseChild : choiceChild.effectiveSubstatements()) {
141 indexDataTree(map, caseChild);
145 } else if (stmt instanceof CaseEffectiveStatement) {
146 // For case statements go through all their statements
147 for (EffectiveStatement<?, ?> child : stmt.effectiveSubstatements()) {
148 indexDataTree(map, child);
154 private static <T extends NamespacedEffectiveStatement<?>> void putChild(final Map<QName, T> map, final T child,
155 final String namespace) {
156 final QName id = child.getIdentifier();
157 final T prev = map.putIfAbsent(id, child);
159 throw new SubstatementIndexingException(
160 "Cannot add " + namespace + " child with name " + id + ", a conflicting child already exists");