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