2 * Copyright (c) 2015 Cisco Systems, Inc. and others. All rights reserved.
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
8 package org.opendaylight.yangtools.yang.parser.spi;
10 import com.google.common.annotations.Beta;
11 import java.util.Collection;
12 import java.util.Iterator;
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;
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}.
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.
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<>();
42 @SuppressWarnings({ "rawtypes", "unchecked" })
43 private SchemaTreeNamespace() {
44 super((Class) SchemaTreeNamespace.class);
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;
54 public StmtContext<?, D, E> get(final QName key) {
55 // FIXME: 7.0.0: this method needs to be well-defined
63 * This method is analogous to {@link SchemaTreeAwareEffectiveStatement#findSchemaTreeNode(QName)}.
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);
72 // An existing node takes precedence, if it does not exist try to request it
73 return existing != null ? existing : requestFrom(storageNode, key);
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;
83 public Map<QName, StmtContext<?, D, E>> getAllFrom(final NamespaceStorageNode storage) {
84 // FIXME: 7.0.0: this method needs to be well-defined
88 @SuppressWarnings("unchecked")
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);
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());
103 * Find statement context identified by interpreting specified {@link SchemaNodeIdentifier} starting at specified
104 * {@link StmtContext}.
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
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);
118 QName nextPath = iterator.next();
119 @SuppressWarnings("unchecked")
120 StmtContext<?, ?, ?> current = (StmtContext<?, ?, ?>) root.getFromNamespace(SchemaTreeNamespace.class,
122 if (current == null) {
123 return Optional.ofNullable(tryToFindUnknownStatement(nextPath.getLocalName(), root));
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));
133 current = nextNodeCtx;
135 return Optional.ofNullable(current);
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;
151 private static NamespaceStorageNode globalOrStatementSpecific(final NamespaceStorageNode storage) {
152 NamespaceStorageNode current = storage;
153 while (!isLocalOrGlobal(current.getStorageNodeType())) {
154 current = current.getParentNamespaceStorage();
159 private static boolean isLocalOrGlobal(final StorageNodeType type) {
160 return type == StorageNodeType.STATEMENT_LOCAL || type == StorageNodeType.GLOBAL;