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.meta;
10 import static java.util.Objects.requireNonNull;
12 import com.google.common.annotations.Beta;
13 import com.google.common.base.MoreObjects.ToStringHelper;
14 import com.google.common.base.Verify;
16 import java.util.Map.Entry;
17 import java.util.Optional;
18 import org.eclipse.jdt.annotation.NonNull;
19 import org.eclipse.jdt.annotation.Nullable;
20 import org.opendaylight.yangtools.concepts.AbstractSimpleIdentifiable;
21 import org.opendaylight.yangtools.yang.common.QName;
22 import org.opendaylight.yangtools.yang.model.api.meta.DeclaredStatement;
23 import org.opendaylight.yangtools.yang.model.api.stmt.SchemaTreeAwareEffectiveStatement;
24 import org.opendaylight.yangtools.yang.model.api.stmt.SchemaTreeEffectiveStatement;
25 import org.opendaylight.yangtools.yang.parser.spi.NamespaceBehaviours;
28 * Definition / implementation of specific Identifier Namespace behaviour. A namespace behaviour is built on top
29 * of a tree of {@link NamespaceStorageNode} which represents local context of one of types defined
30 * n {@link StorageNodeType}.
33 * For common behaviour models please use static factories {@link #global(ParserNamespace)},
34 * {@link #sourceLocal(ParserNamespace)} and {@link #treeScoped(ParserNamespace)}.
37 * @param <V> Value type
39 public abstract class NamespaceBehaviour<K, V> extends AbstractSimpleIdentifiable<ParserNamespace<K, V>> {
41 public enum StorageNodeType {
43 * Global storage, visible from all sources.
47 * Storage of the root statement of a particular source and any sources it is importing.
49 // FIXME: 7.0.0: this is a misnomer and should be renamed
52 * Storage of a single statement.
56 * Storage of the root statement of a particular source.
61 public interface Registry {
63 * Get a namespace behavior.
65 * @param type Namespace type
67 * @param <V> value type
68 * @return Namespace behaviour
69 * @throws NamespaceNotAvailableException when the namespace is not available
71 <K, V> NamespaceBehaviour<K, V> getNamespaceBehaviour(ParserNamespace<K, V> type);
74 public interface NamespaceStorageNode {
76 * Return local namespace behaviour type.
78 * @return local namespace behaviour type {@link NamespaceBehaviour}
80 StorageNodeType getStorageNodeType();
82 @Nullable NamespaceStorageNode getParentNamespaceStorage();
84 <K, V> @Nullable V getFromLocalStorage(ParserNamespace<K, V> type, K key);
86 <K, V> @Nullable Map<K, V> getAllFromLocalStorage(ParserNamespace<K, V> type);
89 * Populate specified namespace with a key/value pair, overwriting previous contents. Similar to
90 * {@link Map#put(Object, Object)}.
92 * @param type Namespace identifier
95 * @return Previously-stored value, or null if the key was not present
97 <K, V> @Nullable V putToLocalStorage(ParserNamespace<K, V> type, K key, V value);
100 * Populate specified namespace with a key/value pair unless the key is already associated with a value. Similar
101 * to {@link Map#putIfAbsent(Object, Object)}.
103 * @param type Namespace identifier
106 * @return Preexisting value or null if there was no previous mapping
108 <K, V> @Nullable V putToLocalStorageIfAbsent(ParserNamespace<K, V> type, K key, V value);
112 * Interface implemented by {@link NamespaceStorageNode}s which support dynamic addition of child elements as they
113 * are requested. This means that such a node can, defer creation of child namespace storage nodes, in effect lazily
114 * expanding this namespace on an if-needed basis.
117 public interface OnDemandSchemaTreeStorageNode extends NamespaceStorageNode {
119 * Request that a new member of this node's schema tree statement be added. Implementations are required to
120 * perform lookup in their internal structure and create a child if tractable. Resulting node is expected to
121 * have been registered with local storage, so that it is accessible through
122 * {@link #getFromLocalStorage(ParserNamespace, Object)}.
125 * This method must not change its mind about a child's presence -- once it returns non-present, it has to be
126 * always returning non-present.
129 * The results produced by this method are expected to be consistent with
130 * {@link SchemaTreeAwareEffectiveStatement#findSchemaTreeNode(QName)} and
131 * {@link NamespaceBehaviours#SCHEMA_TREE}'s {@code getFrom(NamespaceStorageNode, QName)}.
133 * @param qname node identifier of the child being requested
134 * @return Requested child, if it is present.
135 * @throws NullPointerException in {@code qname} is null
137 <D extends DeclaredStatement<QName>, E extends SchemaTreeEffectiveStatement<D>>
138 @Nullable StmtContext<QName, D, E> requestSchemaTreeChild(QName qname);
141 protected NamespaceBehaviour(final ParserNamespace<K, V> identifier) {
146 * Creates a global namespace behaviour for supplied namespace type. Global behaviour stores and loads all values
147 * from root {@link NamespaceStorageNode} with type of {@link StorageNodeType#GLOBAL}.
149 * @param identifier Namespace identifier.
150 * @param <K> type parameter
151 * @param <V> type parameter
152 * @return global namespace behaviour for supplied namespace type.
154 public static <K, V> @NonNull NamespaceBehaviour<K, V> global(final ParserNamespace<K, V> identifier) {
155 return new StorageSpecific<>(identifier, StorageNodeType.GLOBAL);
159 * Creates source-local namespace behaviour for supplied namespace type. Source-local namespace behaviour stores
160 * and loads all values from closest {@link NamespaceStorageNode} ancestor with type
161 * of {@link StorageNodeType#SOURCE_LOCAL_SPECIAL}.
163 * @param identifier Namespace identifier.
164 * @param <K> type parameter
165 * @param <V> type parameter
166 * @return source-local namespace behaviour for supplied namespace type.
168 public static <K, V> @NonNull NamespaceBehaviour<K, V> sourceLocal(final ParserNamespace<K, V> identifier) {
169 return new StorageSpecific<>(identifier, StorageNodeType.SOURCE_LOCAL_SPECIAL);
172 public static <K, V> @NonNull NamespaceBehaviour<K, V> statementLocal(final ParserNamespace<K, V> identifier) {
173 return new StatementLocal<>(identifier);
177 * Creates a root-statement-local namespace behaviour for supplied namespace type. Root-statement-local namespace
178 * behaviour stores and loads all values from closest {@link NamespaceStorageNode} ancestor with type
179 * of {@link StorageNodeType#ROOT_STATEMENT_LOCAL}.
181 * @param identifier Namespace identifier.
182 * @param <K> type parameter
183 * @param <V> type parameter
184 * @return root-statement-local namespace behaviour for supplied namespace type.
186 public static <K, V> @NonNull NamespaceBehaviour<K, V> rootStatementLocal(final ParserNamespace<K, V> identifier) {
187 return new StorageSpecific<>(identifier, StorageNodeType.ROOT_STATEMENT_LOCAL);
191 * Creates tree-scoped namespace behaviour for supplied namespace type. Tree-scoped namespace behaviour searches
192 * for value in all storage nodes up to the root and stores values in supplied node.
195 * Namespace identifier.
196 * @param <K> type parameter
197 * @param <V> type parameter
198 * @return tree-scoped namespace behaviour for supplied namespace type.
200 public static <K, V> @NonNull NamespaceBehaviour<K, V> treeScoped(final ParserNamespace<K, V> identifier) {
201 return new TreeScoped<>(identifier);
205 * Returns a value from model namespace storage according to key param class.
207 * @param storage namespace storage
208 * @param key type parameter
209 * @return value from model namespace storage according to key param class
211 public abstract V getFrom(NamespaceStorageNode storage, K key);
214 * Returns the key/value mapping best matching specified criterion.
216 * @param storage namespace storage
217 * @param criterion selection criterion
218 * @return Selected mapping, if available.
220 public final Optional<Entry<K, V>> getFrom(final NamespaceStorageNode storage,
221 final NamespaceKeyCriterion<K> criterion) {
222 final Map<K, V> mappings = getAllFrom(storage);
223 if (mappings == null) {
224 return Optional.empty();
227 Entry<K, V> match = null;
228 for (Entry<K, V> entry : mappings.entrySet()) {
229 final K key = entry.getKey();
230 if (criterion.match(key)) {
232 final K selected = criterion.select(match.getKey(), key);
233 if (selected.equals(match.getKey())) {
237 Verify.verify(selected == key, "Criterion %s selected invalid key %s from candidates [%s %s]",
238 selected, match.getKey(), key);
245 return Optional.ofNullable(match);
249 * Returns all values of a keys of param class from model namespace storage.
251 * @param storage namespace storage
252 * @return all values of keys of param class from model namespace storage
254 public abstract Map<K, V> getAllFrom(NamespaceStorageNode storage);
257 * Adds a key/value to corresponding namespace storage according to param class.
259 * @param storage namespace storage
260 * @param key type parameter
261 * @param value type parameter
263 public abstract void addTo(NamespaceStorageNode storage, K key, V value);
265 protected final V getFromLocalStorage(final NamespaceStorageNode storage, final K key) {
266 return storage.getFromLocalStorage(getIdentifier(), key);
269 protected final Map<K, V> getAllFromLocalStorage(final NamespaceStorageNode storage) {
270 return storage.getAllFromLocalStorage(getIdentifier());
273 protected final void addToStorage(final NamespaceStorageNode storage, final K key, final V value) {
274 storage.putToLocalStorage(getIdentifier(), key, value);
277 abstract static class AbstractSpecific<K, V> extends NamespaceBehaviour<K, V> {
278 AbstractSpecific(final ParserNamespace<K, V> identifier) {
283 public final V getFrom(final NamespaceStorageNode storage, final K key) {
284 return getFromLocalStorage(findStorageNode(storage), key);
288 public final Map<K, V> getAllFrom(final NamespaceStorageNode storage) {
289 return getAllFromLocalStorage(findStorageNode(storage));
293 public final void addTo(final NamespaceStorageNode storage, final K key, final V value) {
294 addToStorage(findStorageNode(storage), key, value);
297 abstract NamespaceStorageNode findStorageNode(NamespaceStorageNode storage);
300 static final class StatementLocal<K, V> extends AbstractSpecific<K, V> {
301 StatementLocal(final ParserNamespace<K, V> identifier) {
306 NamespaceStorageNode findStorageNode(final NamespaceStorageNode storage) {
311 static final class StorageSpecific<K, V> extends AbstractSpecific<K, V> {
312 private final StorageNodeType storageType;
314 StorageSpecific(final ParserNamespace<K, V> identifier, final StorageNodeType type) {
316 storageType = requireNonNull(type);
320 NamespaceStorageNode findStorageNode(final NamespaceStorageNode storage) {
321 return findClosestTowardsRoot(storage, storageType);
325 protected ToStringHelper addToStringAttributes(final ToStringHelper helper) {
326 return super.addToStringAttributes(helper.add("type", storageType));
330 static final class TreeScoped<K, V> extends NamespaceBehaviour<K, V> {
331 TreeScoped(final ParserNamespace<K, V> identifier) {
336 public V getFrom(final NamespaceStorageNode storage, final K key) {
337 NamespaceStorageNode current = storage;
338 while (current != null) {
339 final V val = getFromLocalStorage(current, key);
343 current = current.getParentNamespaceStorage();
349 public Map<K, V> getAllFrom(final NamespaceStorageNode storage) {
350 NamespaceStorageNode current = storage;
351 while (current != null) {
352 final Map<K, V> val = getAllFromLocalStorage(current);
356 current = current.getParentNamespaceStorage();
362 public void addTo(final NamespaceStorageNode storage, final K key, final V value) {
363 addToStorage(storage, key, value);
368 protected static NamespaceStorageNode findClosestTowardsRoot(final NamespaceStorageNode storage,
369 final StorageNodeType type) {
370 NamespaceStorageNode current = storage;
371 while (current != null && current.getStorageNodeType() != type) {
372 current = current.getParentNamespaceStorage();
378 protected ToStringHelper addToStringAttributes(final ToStringHelper helper) {
379 return helper.add("identifier", getIdentifier());