Clarify NamespaceStorageSupport.getParentNamespaceStorage()
[yangtools.git] / yang / yang-parser-rfc7950 / src / main / java / org / opendaylight / yangtools / yang / parser / rfc7950 / namespace / ChildSchemaNodeNamespace.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.rfc7950.namespace;
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.opendaylight.yangtools.yang.common.QName;
16 import org.opendaylight.yangtools.yang.model.api.meta.DeclaredStatement;
17 import org.opendaylight.yangtools.yang.model.api.meta.EffectiveStatement;
18 import org.opendaylight.yangtools.yang.model.api.stmt.SchemaNodeIdentifier;
19 import org.opendaylight.yangtools.yang.model.api.stmt.UnknownStatement;
20 import org.opendaylight.yangtools.yang.parser.spi.meta.NamespaceBehaviour;
21 import org.opendaylight.yangtools.yang.parser.spi.meta.StatementNamespace;
22 import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext;
23 import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContextUtils;
24 import org.opendaylight.yangtools.yang.parser.spi.source.SourceException;
25
26 /**
27  * Statement local namespace, which holds direct schema node descendants.
28  */
29 @Beta
30 public final class ChildSchemaNodeNamespace<D extends DeclaredStatement<QName>, E extends EffectiveStatement<QName, D>>
31     extends NamespaceBehaviour<QName, StmtContext<?, D, E>, ChildSchemaNodeNamespace<D, E>>
32     implements StatementNamespace<QName, D, E> {
33
34     public ChildSchemaNodeNamespace() {
35         super((Class) ChildSchemaNodeNamespace.class);
36     }
37
38     @Override
39     public StmtContext<?, D, E> get(final QName key) {
40         // TODO Auto-generated method stub
41         return null;
42     }
43
44     @Override
45     public StmtContext<?, D, E> getFrom(final NamespaceStorageNode storage, final QName key) {
46         return globalOrStatementSpecific(storage).getFromLocalStorage(getIdentifier(), key);
47     }
48
49     @Override
50     public Map<QName, StmtContext<?, D, E>> getAllFrom(final NamespaceStorageNode storage) {
51         // TODO Auto-generated method stub
52         return null;
53     }
54
55     @SuppressWarnings("unchecked")
56     @Override
57     public void addTo(final NamespaceStorageNode storage, final QName key, final StmtContext<?, D, E> value) {
58         final StmtContext<?, D, E> prev = globalOrStatementSpecific(storage).putToLocalStorageIfAbsent(
59             ChildSchemaNodeNamespace.class, key, value);
60
61         if (prev != null) {
62             throw new SourceException(value.getStatementSourceReference(),
63                 "Error in module '%s': cannot add '%s'. Node name collision: '%s' already declared at %s",
64                 value.getRoot().getStatementArgument(), key, prev.getStatementArgument(),
65                 prev.getStatementSourceReference());
66         }
67     }
68
69     /**
70      * Find statement context identified by interpreting specified {@link SchemaNodeIdentifier} starting at specified
71      * {@link StmtContext}.
72      *
73      * @param root Search root context
74      * @param identifier {@link SchemaNodeIdentifier} relative to search root
75      * @return Matching statement context, if present.
76      * @throws NullPointerException if any of the arguments is null
77      */
78     public static Optional<StmtContext<?, ?, ?>> findNode(final StmtContext<?, ?, ?> root,
79             final SchemaNodeIdentifier identifier) {
80         final Iterator<QName> iterator = identifier.getNodeIdentifiers().iterator();
81         if (!iterator.hasNext()) {
82             return Optional.of(root);
83         }
84
85         QName nextPath = iterator.next();
86         @SuppressWarnings("unchecked")
87         StmtContext<?, ?, ?> current = (StmtContext<?, ?, ?>) root.getFromNamespace(ChildSchemaNodeNamespace.class,
88             nextPath);
89         if (current == null) {
90             return Optional.ofNullable(tryToFindUnknownStatement(nextPath.getLocalName(), root));
91         }
92         while (current != null && iterator.hasNext()) {
93             nextPath = iterator.next();
94             @SuppressWarnings("unchecked")
95             final StmtContext<?, ?, ?> nextNodeCtx = (StmtContext<?, ?, ?>) current.getFromNamespace(
96                 ChildSchemaNodeNamespace.class, nextPath);
97             if (nextNodeCtx == null) {
98                 return Optional.ofNullable(tryToFindUnknownStatement(nextPath.getLocalName(), current));
99             }
100             current = nextNodeCtx;
101         }
102         return Optional.ofNullable(current);
103     }
104
105     @SuppressWarnings("unchecked")
106     private static StmtContext<?, ?, ?> tryToFindUnknownStatement(final String localName,
107             final StmtContext<?, ?, ?> current) {
108         final Collection<? extends StmtContext<?, ?, ?>> unknownSubstatements = StmtContextUtils.findAllSubstatements(
109             current, UnknownStatement.class);
110         for (final StmtContext<?, ?, ?> unknownSubstatement : unknownSubstatements) {
111             if (localName.equals(unknownSubstatement.rawStatementArgument())) {
112                 return unknownSubstatement;
113             }
114         }
115         return null;
116     }
117
118     private static NamespaceStorageNode globalOrStatementSpecific(final NamespaceStorageNode storage) {
119         NamespaceStorageNode current = storage;
120         while (!isLocalOrGlobal(current.getStorageNodeType())) {
121             current = current.getParentNamespaceStorage();
122         }
123         return current;
124     }
125
126     private static boolean isLocalOrGlobal(final StorageNodeType type) {
127         return type == StorageNodeType.STATEMENT_LOCAL || type == StorageNodeType.GLOBAL;
128     }
129 }