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