Import annotations to yang-parser-rfc7950
[yangtools.git] / yang / yang-parser-spi / src / main / java / org / opendaylight / yangtools / yang / parser / spi / SchemaTreeNamespace.java
1 /*
2  * Copyright (c) 2015 Cisco Systems, Inc. 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.spi;
9
10 import com.google.common.annotations.Beta;
11 import java.util.Collection;
12 import java.util.Iterator;
13 import java.util.Map;
14 import java.util.Optional;
15 import org.eclipse.jdt.annotation.NonNull;
16 import org.opendaylight.yangtools.yang.common.QName;
17 import org.opendaylight.yangtools.yang.model.api.meta.DeclaredStatement;
18 import org.opendaylight.yangtools.yang.model.api.meta.EffectiveStatement;
19 import org.opendaylight.yangtools.yang.model.api.stmt.SchemaNodeIdentifier;
20 import org.opendaylight.yangtools.yang.model.api.stmt.SchemaTreeAwareEffectiveStatement;
21 import org.opendaylight.yangtools.yang.model.api.stmt.SchemaTreeEffectiveStatement;
22 import org.opendaylight.yangtools.yang.model.api.stmt.UnknownStatement;
23 import org.opendaylight.yangtools.yang.parser.spi.meta.NamespaceBehaviour;
24 import org.opendaylight.yangtools.yang.parser.spi.meta.StatementNamespace;
25 import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext;
26 import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContextUtils;
27 import org.opendaylight.yangtools.yang.parser.spi.source.SourceException;
28
29 /**
30  * Statement local namespace, which holds direct schema node descendants. This corresponds to the contents of the schema
31  * tree as exposed through {@link SchemaTreeAwareEffectiveStatement}.
32  */
33 // FIXME: 7.0.0: this contract seems to fall on the reactor side of things rather than parser-spi. Consider moving this
34 //               into yang-(parser-)reactor-api.
35 @Beta
36 public final class SchemaTreeNamespace<D extends DeclaredStatement<QName>,
37             E extends SchemaTreeEffectiveStatement<D>>
38         extends NamespaceBehaviour<QName, StmtContext<?, D, E>, SchemaTreeNamespace<D, E>>
39         implements StatementNamespace<QName, D, E> {
40     private static final @NonNull SchemaTreeNamespace<?, ?> INSTANCE = new SchemaTreeNamespace<>();
41
42     @SuppressWarnings({ "rawtypes", "unchecked" })
43     private SchemaTreeNamespace() {
44         super((Class) SchemaTreeNamespace.class);
45     }
46
47     @SuppressWarnings("unchecked")
48     public static <D extends DeclaredStatement<QName>, E extends SchemaTreeEffectiveStatement<D>>
49             @NonNull SchemaTreeNamespace<D, E> getInstance() {
50         return (SchemaTreeNamespace<D, E>) INSTANCE;
51     }
52
53     @Override
54     public StmtContext<?, D, E> get(final QName key) {
55         // FIXME: 7.0.0: this method needs to be well-defined
56         return null;
57     }
58
59     /**
60      * {@inheritDoc}
61      *
62      * <p>
63      * This method is analogous to {@link SchemaTreeAwareEffectiveStatement#findSchemaTreeNode(QName)}.
64      */
65     @Override
66     public StmtContext<?, D, E> getFrom(final NamespaceStorageNode storage, final QName key) {
67         // Get the backing storage node for the requested storage
68         final NamespaceStorageNode storageNode = globalOrStatementSpecific(storage);
69         // Check try to look up existing node
70         final StmtContext<?, D, E> existing = storageNode.getFromLocalStorage(getIdentifier(), key);
71
72         // An existing node takes precedence, if it does not exist try to request it
73         return existing != null ? existing : requestFrom(storageNode, key);
74     }
75
76     private static <D extends DeclaredStatement<QName>, E extends EffectiveStatement<QName, D>>
77             StmtContext<?, D, E> requestFrom(final NamespaceStorageNode storageNode, final QName key) {
78         return storageNode instanceof OnDemandSchemaTreeStorageNode
79             ? ((OnDemandSchemaTreeStorageNode) storageNode).requestSchemaTreeChild(key) : null;
80     }
81
82     @Override
83     public Map<QName, StmtContext<?, D, E>> getAllFrom(final NamespaceStorageNode storage) {
84         // FIXME: 7.0.0: this method needs to be well-defined
85         return null;
86     }
87
88     @SuppressWarnings("unchecked")
89     @Override
90     public void addTo(final NamespaceStorageNode storage, final QName key, final StmtContext<?, D, E> value) {
91         final StmtContext<?, D, E> prev = globalOrStatementSpecific(storage).putToLocalStorageIfAbsent(
92             SchemaTreeNamespace.class, key, value);
93
94         if (prev != null) {
95             throw new SourceException(value.getStatementSourceReference(),
96                 "Error in module '%s': cannot add '%s'. Node name collision: '%s' already declared at %s",
97                 value.getRoot().rawStatementArgument(), key, prev.getStatementArgument(),
98                 prev.getStatementSourceReference());
99         }
100     }
101
102     /**
103      * Find statement context identified by interpreting specified {@link SchemaNodeIdentifier} starting at specified
104      * {@link StmtContext}.
105      *
106      * @param root Search root context
107      * @param identifier {@link SchemaNodeIdentifier} relative to search root
108      * @return Matching statement context, if present.
109      * @throws NullPointerException if any of the arguments is null
110      */
111     public static Optional<StmtContext<?, ?, ?>> findNode(final StmtContext<?, ?, ?> root,
112             final SchemaNodeIdentifier identifier) {
113         final Iterator<QName> iterator = identifier.getNodeIdentifiers().iterator();
114         if (!iterator.hasNext()) {
115             return Optional.of(root);
116         }
117
118         QName nextPath = iterator.next();
119         @SuppressWarnings("unchecked")
120         StmtContext<?, ?, ?> current = (StmtContext<?, ?, ?>) root.getFromNamespace(SchemaTreeNamespace.class,
121             nextPath);
122         if (current == null) {
123             return Optional.ofNullable(tryToFindUnknownStatement(nextPath.getLocalName(), root));
124         }
125         while (current != null && iterator.hasNext()) {
126             nextPath = iterator.next();
127             @SuppressWarnings("unchecked")
128             final StmtContext<?, ?, ?> nextNodeCtx = (StmtContext<?, ?, ?>) current.getFromNamespace(
129                 SchemaTreeNamespace.class, nextPath);
130             if (nextNodeCtx == null) {
131                 return Optional.ofNullable(tryToFindUnknownStatement(nextPath.getLocalName(), current));
132             }
133             current = nextNodeCtx;
134         }
135         return Optional.ofNullable(current);
136     }
137
138     @SuppressWarnings("unchecked")
139     private static StmtContext<?, ?, ?> tryToFindUnknownStatement(final String localName,
140             final StmtContext<?, ?, ?> current) {
141         final Collection<? extends StmtContext<?, ?, ?>> unknownSubstatements = StmtContextUtils.findAllSubstatements(
142             current, UnknownStatement.class);
143         for (final StmtContext<?, ?, ?> unknownSubstatement : unknownSubstatements) {
144             if (localName.equals(unknownSubstatement.rawStatementArgument())) {
145                 return unknownSubstatement;
146             }
147         }
148         return null;
149     }
150
151     private static NamespaceStorageNode globalOrStatementSpecific(final NamespaceStorageNode storage) {
152         NamespaceStorageNode current = storage;
153         while (!isLocalOrGlobal(current.getStorageNodeType())) {
154             current = current.getParentNamespaceStorage();
155         }
156         return current;
157     }
158
159     private static boolean isLocalOrGlobal(final StorageNodeType type) {
160         return type == StorageNodeType.STATEMENT_LOCAL || type == StorageNodeType.GLOBAL;
161     }
162 }