Remove NamespaceStmtCtx.get(All)FromNamespace()
[yangtools.git] / parser / yang-parser-spi / src / main / java / org / opendaylight / yangtools / yang / parser / spi / meta / NamespaceBehaviour.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.spi.meta;
9
10 import static java.util.Objects.requireNonNull;
11
12 import com.google.common.annotations.Beta;
13 import com.google.common.base.MoreObjects.ToStringHelper;
14 import com.google.common.base.Verify;
15 import java.util.Map;
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;
26
27 /**
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}.
31  *
32  * <p>
33  * For common behaviour models please use static factories {@link #global(ParserNamespace)},
34  * {@link #sourceLocal(ParserNamespace)} and {@link #treeScoped(ParserNamespace)}.
35  *
36  * @param <K> Key type
37  * @param <V> Value type
38  */
39 public abstract class NamespaceBehaviour<K, V> extends AbstractSimpleIdentifiable<ParserNamespace<K, V>> {
40
41     public enum StorageNodeType {
42         /**
43          * Global storage, visible from all sources.
44          */
45         GLOBAL,
46         /**
47          * Storage of the root statement of a particular source and any sources it is importing.
48          */
49         // FIXME: 7.0.0: this is a misnomer and should be renamed
50         SOURCE_LOCAL_SPECIAL,
51         /**
52          * Storage of a single statement.
53          */
54         STATEMENT_LOCAL,
55         /**
56          * Storage of the root statement of a particular source.
57          */
58         ROOT_STATEMENT_LOCAL
59     }
60
61     public interface Registry {
62         /**
63          * Get a namespace behavior.
64          *
65          * @param type Namespace type
66          * @param <K> key type
67          * @param <V> value type
68          * @return Namespace behaviour
69          * @throws NamespaceNotAvailableException when the namespace is not available
70          */
71         <K, V> NamespaceBehaviour<K, V> getNamespaceBehaviour(ParserNamespace<K, V> type);
72     }
73
74     public interface NamespaceStorageNode {
75         /**
76          * Return local namespace behaviour type.
77          *
78          * @return local namespace behaviour type {@link NamespaceBehaviour}
79          */
80         StorageNodeType getStorageNodeType();
81
82         @Nullable NamespaceStorageNode getParentNamespaceStorage();
83
84         <K, V> @Nullable V getFromLocalStorage(ParserNamespace<K, V> type, K key);
85
86         <K, V> @Nullable Map<K, V> getAllFromLocalStorage(ParserNamespace<K, V> type);
87
88         /**
89          * Populate specified namespace with a key/value pair, overwriting previous contents. Similar to
90          * {@link Map#put(Object, Object)}.
91          *
92          * @param type Namespace identifier
93          * @param key Key
94          * @param value Value
95          * @return Previously-stored value, or null if the key was not present
96          */
97         <K, V> @Nullable V putToLocalStorage(ParserNamespace<K, V> type, K key, V value);
98
99         /**
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)}.
102          *
103          * @param type Namespace identifier
104          * @param key Key
105          * @param value Value
106          * @return Preexisting value or null if there was no previous mapping
107          */
108         <K, V> @Nullable V putToLocalStorageIfAbsent(ParserNamespace<K, V> type, K key, V value);
109     }
110
111     /**
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.
115      */
116     @Beta
117     public interface OnDemandSchemaTreeStorageNode extends NamespaceStorageNode {
118         /**
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)}.
123          *
124          * <p>
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.
127          *
128          * <p>
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)}.
132          *
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
136          */
137         <D extends DeclaredStatement<QName>, E extends SchemaTreeEffectiveStatement<D>>
138             @Nullable StmtContext<QName, D, E> requestSchemaTreeChild(QName qname);
139     }
140
141     protected NamespaceBehaviour(final ParserNamespace<K, V> identifier) {
142         super(identifier);
143     }
144
145     /**
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}.
148      *
149      * @param identifier Namespace identifier.
150      * @param <K> type parameter
151      * @param <V> type parameter
152      * @return global namespace behaviour for supplied namespace type.
153      */
154     public static <K, V> @NonNull NamespaceBehaviour<K, V> global(final ParserNamespace<K, V> identifier) {
155         return new StorageSpecific<>(identifier, StorageNodeType.GLOBAL);
156     }
157
158     /**
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}.
162      *
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.
167      */
168     public static <K, V> @NonNull NamespaceBehaviour<K, V> sourceLocal(final ParserNamespace<K, V> identifier) {
169         return new StorageSpecific<>(identifier, StorageNodeType.SOURCE_LOCAL_SPECIAL);
170     }
171
172     public static <K, V> @NonNull NamespaceBehaviour<K, V> statementLocal(final ParserNamespace<K, V> identifier) {
173         return new StatementLocal<>(identifier);
174     }
175
176     /**
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}.
180      *
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.
185      */
186     public static <K, V> @NonNull NamespaceBehaviour<K, V> rootStatementLocal(final ParserNamespace<K, V> identifier) {
187         return new StorageSpecific<>(identifier, StorageNodeType.ROOT_STATEMENT_LOCAL);
188     }
189
190     /**
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.
193      *
194      * @param identifier
195      *            Namespace identifier.
196      * @param <K> type parameter
197      * @param <V> type parameter
198      * @return tree-scoped namespace behaviour for supplied namespace type.
199      */
200     public static <K, V> @NonNull NamespaceBehaviour<K, V> treeScoped(final ParserNamespace<K, V> identifier) {
201         return new TreeScoped<>(identifier);
202     }
203
204     /**
205      * Returns a value from model namespace storage according to key param class.
206      *
207      * @param storage namespace storage
208      * @param key type parameter
209      * @return value from model namespace storage according to key param class
210      */
211     public abstract V getFrom(NamespaceStorageNode storage, K key);
212
213     /**
214      * Returns the key/value mapping best matching specified criterion.
215      *
216      * @param storage namespace storage
217      * @param criterion selection criterion
218      * @return Selected mapping, if available.
219      */
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();
225         }
226
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)) {
231                 if (match != null) {
232                     final K selected = criterion.select(match.getKey(), key);
233                     if (selected.equals(match.getKey())) {
234                         continue;
235                     }
236
237                     Verify.verify(selected == key, "Criterion %s selected invalid key %s from candidates [%s %s]",
238                             selected, match.getKey(), key);
239                 }
240
241                 match = entry;
242             }
243         }
244
245         return Optional.ofNullable(match);
246     }
247
248     /**
249      * Returns all values of a keys of param class from model namespace storage.
250      *
251      * @param storage namespace storage
252      * @return all values of keys of param class from model namespace storage
253      */
254     public abstract Map<K, V> getAllFrom(NamespaceStorageNode storage);
255
256     /**
257      * Adds a key/value to corresponding namespace storage according to param class.
258      *
259      * @param storage namespace storage
260      * @param key type parameter
261      * @param value type parameter
262      */
263     public abstract void addTo(NamespaceStorageNode storage, K key, V value);
264
265     protected final V getFromLocalStorage(final NamespaceStorageNode storage, final K key) {
266         return storage.getFromLocalStorage(getIdentifier(), key);
267     }
268
269     protected final Map<K, V> getAllFromLocalStorage(final NamespaceStorageNode storage) {
270         return storage.getAllFromLocalStorage(getIdentifier());
271     }
272
273     protected final void addToStorage(final NamespaceStorageNode storage, final K key, final V value) {
274         storage.putToLocalStorage(getIdentifier(), key, value);
275     }
276
277     abstract static class AbstractSpecific<K, V> extends NamespaceBehaviour<K, V> {
278         AbstractSpecific(final ParserNamespace<K, V> identifier) {
279             super(identifier);
280         }
281
282         @Override
283         public final V getFrom(final NamespaceStorageNode storage, final K key) {
284             return getFromLocalStorage(findStorageNode(storage), key);
285         }
286
287         @Override
288         public final Map<K, V> getAllFrom(final NamespaceStorageNode storage) {
289             return getAllFromLocalStorage(findStorageNode(storage));
290         }
291
292         @Override
293         public final void addTo(final NamespaceStorageNode storage, final K key, final V value) {
294             addToStorage(findStorageNode(storage), key, value);
295         }
296
297         abstract NamespaceStorageNode findStorageNode(NamespaceStorageNode storage);
298     }
299
300     static final class StatementLocal<K, V> extends AbstractSpecific<K, V> {
301         StatementLocal(final ParserNamespace<K, V> identifier) {
302             super(identifier);
303         }
304
305         @Override
306         NamespaceStorageNode findStorageNode(final NamespaceStorageNode storage) {
307             return storage;
308         }
309     }
310
311     static final class StorageSpecific<K, V> extends AbstractSpecific<K, V> {
312         private final StorageNodeType storageType;
313
314         StorageSpecific(final ParserNamespace<K, V> identifier, final StorageNodeType type) {
315             super(identifier);
316             storageType = requireNonNull(type);
317         }
318
319         @Override
320         NamespaceStorageNode findStorageNode(final NamespaceStorageNode storage) {
321             return findClosestTowardsRoot(storage, storageType);
322         }
323
324         @Override
325         protected ToStringHelper addToStringAttributes(final ToStringHelper helper) {
326             return super.addToStringAttributes(helper.add("type", storageType));
327         }
328     }
329
330     static final class TreeScoped<K, V> extends NamespaceBehaviour<K, V> {
331         TreeScoped(final ParserNamespace<K, V> identifier) {
332             super(identifier);
333         }
334
335         @Override
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);
340                 if (val != null) {
341                     return val;
342                 }
343                 current = current.getParentNamespaceStorage();
344             }
345             return null;
346         }
347
348         @Override
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);
353                 if (val != null) {
354                     return val;
355                 }
356                 current = current.getParentNamespaceStorage();
357             }
358             return null;
359         }
360
361         @Override
362         public void addTo(final NamespaceStorageNode storage, final K key, final V value) {
363             addToStorage(storage, key, value);
364         }
365
366     }
367
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();
373         }
374         return current;
375     }
376
377     @Override
378     protected ToStringHelper addToStringAttributes(final ToStringHelper helper) {
379         return helper.add("identifier", getIdentifier());
380     }
381 }