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