b5a9757ea953b7e936a58c61dfdb4b8df70fdbdd
[yangtools.git] / yang / yang-parser-rfc7950 / src / main / java / org / opendaylight / yangtools / yang / parser / rfc7950 / stmt / AbstractSchemaEffectiveDocumentedNode.java
1 /*
2  * Copyright (c) 2018 Pantheon Technologies, s.r.o. and others.  All rights reserved.
3  *
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
7  */
8 package org.opendaylight.yangtools.yang.parser.rfc7950.stmt;
9
10 import static com.google.common.base.Verify.verify;
11 import static java.util.Objects.requireNonNull;
12
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.LinkedHashMap;
18 import java.util.Map;
19 import java.util.Optional;
20 import org.eclipse.jdt.annotation.NonNull;
21 import org.opendaylight.yangtools.yang.common.QName;
22 import org.opendaylight.yangtools.yang.model.api.DataNodeContainer;
23 import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
24 import org.opendaylight.yangtools.yang.model.api.meta.DeclaredStatement;
25 import org.opendaylight.yangtools.yang.model.api.meta.IdentifierNamespace;
26 import org.opendaylight.yangtools.yang.model.api.stmt.CaseEffectiveStatement;
27 import org.opendaylight.yangtools.yang.model.api.stmt.ChoiceEffectiveStatement;
28 import org.opendaylight.yangtools.yang.model.api.stmt.DataTreeAwareEffectiveStatement;
29 import org.opendaylight.yangtools.yang.model.api.stmt.DataTreeEffectiveStatement;
30 import org.opendaylight.yangtools.yang.model.api.stmt.SchemaTreeAwareEffectiveStatement;
31 import org.opendaylight.yangtools.yang.model.api.stmt.SchemaTreeEffectiveStatement;
32 import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext;
33 import org.opendaylight.yangtools.yang.parser.spi.source.SourceException;
34 import org.opendaylight.yangtools.yang.parser.spi.source.StatementSourceReference;
35
36 /**
37  * An {@link AbstractEffectiveDocumentedNode} which can optionally support {@link SchemaTreeAwareEffectiveStatement}.
38  *
39  * @param <A> Argument type ({@link Void} if statement does not have argument.)
40  * @param <D> Class representing declared version of this statement.
41  * @author Robert Varga
42  */
43 @Beta
44 public abstract class AbstractSchemaEffectiveDocumentedNode<A, D extends DeclaredStatement<A>>
45         extends AbstractEffectiveDocumentedNode<A, D> {
46     private final ImmutableMap<QName, DataTreeEffectiveStatement<?>> dataTreeNamespace;
47     private final ImmutableMap<QName, SchemaTreeEffectiveStatement<?>> schemaTreeNamespace;
48
49     protected AbstractSchemaEffectiveDocumentedNode(final StmtContext<A, D, ?> ctx) {
50         super(ctx);
51
52         // This check is rather weird, but comes from our desire to lower memory footprint while providing both
53         // EffectiveStatements and SchemaNode interfaces -- which do not overlap completely where child lookups are
54         // concerned. This ensures that we have SchemaTree index available for use with child lookups.
55         if (this instanceof SchemaTreeAwareEffectiveStatement || this instanceof DataNodeContainer) {
56             final StatementSourceReference ref = ctx.getStatementSourceReference();
57             final Map<QName, SchemaTreeEffectiveStatement<?>> schemaChildren = new LinkedHashMap<>();
58             streamEffectiveSubstatements(SchemaTreeEffectiveStatement.class).forEach(child -> {
59                 putChild(schemaChildren, child, ref, "schema");
60             });
61             schemaTreeNamespace = ImmutableMap.copyOf(schemaChildren);
62
63             if (this instanceof DataTreeAwareEffectiveStatement && !schemaTreeNamespace.isEmpty()) {
64                 final Map<QName, DataTreeEffectiveStatement<?>> dataChildren = new LinkedHashMap<>();
65                 boolean sameAsSchema = true;
66
67                 for (SchemaTreeEffectiveStatement<?> child : schemaTreeNamespace.values()) {
68                     if (child instanceof DataTreeEffectiveStatement) {
69                         putChild(dataChildren, (DataTreeEffectiveStatement<?>) child, ref, "data");
70                     } else {
71                         sameAsSchema = false;
72                         putChoiceDataChildren(dataChildren, ref, child);
73                     }
74                 }
75
76                 // This is a mighty hack to lower memory usage: if we consumed all schema tree children as data nodes,
77                 // the two maps are equal and hence we can share the instance.
78                 dataTreeNamespace = sameAsSchema ? (ImmutableMap) schemaTreeNamespace
79                         : ImmutableMap.copyOf(dataChildren);
80             } else {
81                 dataTreeNamespace = ImmutableMap.of();
82             }
83         } else {
84             dataTreeNamespace = ImmutableMap.of();
85             schemaTreeNamespace = ImmutableMap.of();
86         }
87     }
88
89     @Override
90     @SuppressWarnings("unchecked")
91     protected <K, V, N extends IdentifierNamespace<K, V>> Optional<? extends Map<K, V>> getNamespaceContents(
92             final Class<N> namespace) {
93         if (this instanceof SchemaTreeAwareEffectiveStatement
94                 && SchemaTreeAwareEffectiveStatement.Namespace.class.equals(namespace)) {
95             return Optional.of((Map<K, V>) schemaTreeNamespace);
96         }
97         if (this instanceof DataTreeAwareEffectiveStatement
98                 && DataTreeAwareEffectiveStatement.Namespace.class.equals(namespace)) {
99             return Optional.of((Map<K, V>) dataTreeNamespace);
100         }
101         return super.getNamespaceContents(namespace);
102     }
103
104     protected final <T> @NonNull ImmutableSet<T> derivedSet(final VarHandle vh, final @NonNull Class<T> clazz) {
105         final ImmutableSet<T> existing = (ImmutableSet<T>) vh.getAcquire(this);
106         return existing != null ? existing : loadSet(vh, clazz);
107     }
108
109     /**
110      * Indexing support for {@link DataNodeContainer#findDataChildByName(QName)}.
111      */
112     protected final Optional<DataSchemaNode> findDataSchemaNode(final QName name) {
113         // Only DataNodeContainer subclasses should be calling this method
114         verify(this instanceof DataNodeContainer);
115         final SchemaTreeEffectiveStatement<?> child = schemaTreeNamespace.get(requireNonNull(name));
116         return child instanceof DataSchemaNode ? Optional.of((DataSchemaNode) child) : Optional.empty();
117     }
118
119     @SuppressWarnings("unchecked")
120     private <T> @NonNull ImmutableSet<T> loadSet(final VarHandle vh, final @NonNull Class<T> clazz) {
121         final ImmutableSet<T> computed = ImmutableSet.copyOf(allSubstatementsOfType(clazz));
122         final Object witness = vh.compareAndExchangeRelease(this, null, computed);
123         return witness == null ? computed : (ImmutableSet<T>) witness;
124     }
125
126     private static <T extends SchemaTreeEffectiveStatement<?>> void putChild(final Map<QName, T> map,
127             final T child, final StatementSourceReference ref, final String tree) {
128         final QName id = child.getIdentifier();
129         final T prev = map.putIfAbsent(id, child);
130         SourceException.throwIf(prev != null, ref,
131                 "Cannot add %s tree child with name %s, a conflicting child already exists", tree, id);
132     }
133
134     private static void putChoiceDataChildren(final Map<QName, DataTreeEffectiveStatement<?>> map,
135             final StatementSourceReference ref, final SchemaTreeEffectiveStatement<?> child) {
136         // For choice statements go through all their cases and fetch their data children
137         if (child instanceof ChoiceEffectiveStatement) {
138             child.streamEffectiveSubstatements(CaseEffectiveStatement.class).forEach(
139                 caseStmt -> caseStmt.streamEffectiveSubstatements(SchemaTreeEffectiveStatement.class).forEach(stmt -> {
140                     if (stmt instanceof DataTreeEffectiveStatement) {
141                         putChild(map, (DataTreeEffectiveStatement<?>) stmt, ref, "data");
142                     } else {
143                         putChoiceDataChildren(map, ref, stmt);
144                     }
145                 }));
146         }
147     }
148 }