2 * Copyright (c) 2018 Pantheon Technologies, 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.parser.rfc7950.stmt;
10 import static com.google.common.base.Verify.verify;
11 import static java.util.Objects.requireNonNull;
13 import com.google.common.annotations.Beta;
14 import com.google.common.collect.ImmutableMap;
15 import com.google.common.collect.ImmutableSet;
16 import java.lang.invoke.VarHandle;
17 import java.util.Collection;
18 import java.util.Collections;
19 import java.util.LinkedHashMap;
21 import java.util.Map.Entry;
22 import java.util.Optional;
23 import org.eclipse.jdt.annotation.NonNull;
24 import org.opendaylight.yangtools.yang.common.QName;
25 import org.opendaylight.yangtools.yang.model.api.DataNodeContainer;
26 import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
27 import org.opendaylight.yangtools.yang.model.api.meta.DeclaredStatement;
28 import org.opendaylight.yangtools.yang.model.api.meta.EffectiveStatement;
29 import org.opendaylight.yangtools.yang.model.api.meta.IdentifierNamespace;
30 import org.opendaylight.yangtools.yang.model.api.stmt.CaseEffectiveStatement;
31 import org.opendaylight.yangtools.yang.model.api.stmt.ChoiceEffectiveStatement;
32 import org.opendaylight.yangtools.yang.model.api.stmt.DataTreeAwareEffectiveStatement;
33 import org.opendaylight.yangtools.yang.model.api.stmt.DataTreeEffectiveStatement;
34 import org.opendaylight.yangtools.yang.model.api.stmt.SchemaTreeAwareEffectiveStatement;
35 import org.opendaylight.yangtools.yang.model.api.stmt.SchemaTreeEffectiveStatement;
36 import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext;
37 import org.opendaylight.yangtools.yang.parser.spi.source.SourceException;
38 import org.opendaylight.yangtools.yang.parser.spi.source.StatementSourceReference;
41 * An {@link AbstractEffectiveDocumentedNode} which can optionally support {@link SchemaTreeAwareEffectiveStatement}.
43 * @param <A> Argument type ({@link Void} if statement does not have argument.)
44 * @param <D> Class representing declared version of this statement.
45 * @author Robert Varga
48 public abstract class AbstractSchemaEffectiveDocumentedNode<A, D extends DeclaredStatement<A>>
49 extends AbstractEffectiveDocumentedNode<A, D> {
50 private final Map<QName, DataTreeEffectiveStatement<?>> dataTreeNamespace;
51 private final Map<QName, SchemaTreeEffectiveStatement<?>> schemaTreeNamespace;
53 protected AbstractSchemaEffectiveDocumentedNode(final StmtContext<A, D, ?> ctx) {
56 // This check is rather weird, but comes from our desire to lower memory footprint while providing both
57 // EffectiveStatements and SchemaNode interfaces -- which do not overlap completely where child lookups are
58 // concerned. This ensures that we have SchemaTree index available for use with child lookups.
59 if (this instanceof SchemaTreeAwareEffectiveStatement || this instanceof DataNodeContainer) {
60 schemaTreeNamespace = createSchemaTreeNamespace(ctx.getStatementSourceReference(),
61 effectiveSubstatements());
63 schemaTreeNamespace = ImmutableMap.of();
65 if (this instanceof DataTreeAwareEffectiveStatement && !schemaTreeNamespace.isEmpty()) {
66 dataTreeNamespace = createDataTreeNamespace(ctx.getStatementSourceReference(), schemaTreeNamespace);
68 dataTreeNamespace = ImmutableMap.of();
73 @SuppressWarnings("unchecked")
74 protected <K, V, N extends IdentifierNamespace<K, V>> Optional<? extends Map<K, V>> getNamespaceContents(
75 final Class<N> namespace) {
76 if (this instanceof SchemaTreeAwareEffectiveStatement
77 && SchemaTreeAwareEffectiveStatement.Namespace.class.equals(namespace)) {
78 return Optional.of((Map<K, V>) schemaTreeNamespace);
80 if (this instanceof DataTreeAwareEffectiveStatement
81 && DataTreeAwareEffectiveStatement.Namespace.class.equals(namespace)) {
82 return Optional.of((Map<K, V>) dataTreeNamespace);
84 return super.getNamespaceContents(namespace);
87 protected final <T> @NonNull ImmutableSet<T> derivedSet(final VarHandle vh, final @NonNull Class<T> clazz) {
88 final ImmutableSet<T> existing = (ImmutableSet<T>) vh.getAcquire(this);
89 return existing != null ? existing : loadSet(vh, clazz);
93 * Indexing support for {@link DataNodeContainer#findDataChildByName(QName)}.
95 protected final Optional<DataSchemaNode> findDataSchemaNode(final QName name) {
96 // Only DataNodeContainer subclasses should be calling this method
97 verify(this instanceof DataNodeContainer);
98 final SchemaTreeEffectiveStatement<?> child = schemaTreeNamespace.get(requireNonNull(name));
99 return child instanceof DataSchemaNode ? Optional.of((DataSchemaNode) child) : Optional.empty();
102 static @NonNull Map<QName, SchemaTreeEffectiveStatement<?>> createSchemaTreeNamespace(
103 final StatementSourceReference ref, final Collection<? extends EffectiveStatement<?, ?>> substatements) {
104 final Map<QName, SchemaTreeEffectiveStatement<?>> schemaChildren = new LinkedHashMap<>();
105 substatements.stream().filter(SchemaTreeEffectiveStatement.class::isInstance)
106 .forEach(child -> putChild(schemaChildren, (SchemaTreeEffectiveStatement) child, ref, "schema"));
107 return toImmutable(schemaChildren);
110 static @NonNull Map<QName, DataTreeEffectiveStatement<?>> createDataTreeNamespace(
111 final StatementSourceReference ref,
112 final Map<QName, SchemaTreeEffectiveStatement<?>> schemaTreeNamespace) {
113 final Map<QName, DataTreeEffectiveStatement<?>> dataChildren = new LinkedHashMap<>();
114 boolean sameAsSchema = true;
116 for (SchemaTreeEffectiveStatement<?> child : schemaTreeNamespace.values()) {
117 if (child instanceof DataTreeEffectiveStatement) {
118 putChild(dataChildren, (DataTreeEffectiveStatement<?>) child, ref, "data");
120 sameAsSchema = false;
121 putChoiceDataChildren(dataChildren, ref, child);
125 // This is a mighty hack to lower memory usage: if we consumed all schema tree children as data nodes,
126 // the two maps are equal and hence we can share the instance.
127 return sameAsSchema ? (Map) schemaTreeNamespace : toImmutable(dataChildren);
130 private static <K, V> Map<K, V> toImmutable(final Map<K, V> map) {
131 switch (map.size()) {
133 return ImmutableMap.of();
135 // Special case: singleton ImmutableMap is actually SingletonImmutableBiMap, which allocates its inverse
136 // view and its keySet() when asked for values() -- which costs us 64 bytes (40+24).
137 // java.util.Collections can do the same job for less memory.
138 final Entry<K, V> entry = map.entrySet().iterator().next();
139 return Collections.singletonMap(entry.getKey(), entry.getValue());
141 return ImmutableMap.copyOf(map);
145 @SuppressWarnings("unchecked")
146 private <T> @NonNull ImmutableSet<T> loadSet(final VarHandle vh, final @NonNull Class<T> clazz) {
147 final ImmutableSet<T> computed = ImmutableSet.copyOf(allSubstatementsOfType(clazz));
148 final Object witness = vh.compareAndExchangeRelease(this, null, computed);
149 return witness == null ? computed : (ImmutableSet<T>) witness;
152 private static <T extends SchemaTreeEffectiveStatement<?>> void putChild(final Map<QName, T> map,
153 final T child, final StatementSourceReference ref, final String tree) {
154 final QName id = child.getIdentifier();
155 final T prev = map.putIfAbsent(id, child);
156 SourceException.throwIf(prev != null, ref,
157 "Cannot add %s tree child with name %s, a conflicting child already exists", tree, id);
160 private static void putChoiceDataChildren(final Map<QName, DataTreeEffectiveStatement<?>> map,
161 final StatementSourceReference ref, final SchemaTreeEffectiveStatement<?> child) {
162 // For choice statements go through all their cases and fetch their data children
163 if (child instanceof ChoiceEffectiveStatement) {
164 child.streamEffectiveSubstatements(CaseEffectiveStatement.class).forEach(
165 caseStmt -> caseStmt.streamEffectiveSubstatements(SchemaTreeEffectiveStatement.class).forEach(stmt -> {
166 if (stmt instanceof DataTreeEffectiveStatement) {
167 putChild(map, (DataTreeEffectiveStatement<?>) stmt, ref, "data");
169 putChoiceDataChildren(map, ref, stmt);