Add OnDemandSchemaTreeStorageNode
[yangtools.git] / yang / 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.AbstractIdentifiable;
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.meta.EffectiveStatement;
24 import org.opendaylight.yangtools.yang.model.api.meta.IdentifierNamespace;
25
26 /**
27  * Definition / implementation of specific Identifier Namespace behaviour. A namespace behaviour is built on top
28  * of a tree of {@link NamespaceStorageNode} which represents local context of one of types defined
29  * n {@link StorageNodeType}.
30  *
31  * <p>
32  * For common behaviour models please use static factories {@link #global(Class)}, {@link #sourceLocal(Class)} and
33  * {@link #treeScoped(Class)}.
34  *
35  * @param <K> Key type
36  * @param <V> Value type
37  * @param <N> Namespace Type
38  */
39 public abstract class NamespaceBehaviour<K, V, N extends IdentifierNamespace<K, V>>
40         extends AbstractIdentifiable<Class<N>> {
41
42     public enum StorageNodeType {
43         /**
44          * Global storage, visible from all sources.
45          */
46         GLOBAL,
47         /**
48          * Storage of the root statement of a particular source and any sources it is importing.
49          */
50         // FIXME: 6.0.0: this is a misnomer and should be renamed
51         SOURCE_LOCAL_SPECIAL,
52         /**
53          * Storage of a single statement.
54          */
55         STATEMENT_LOCAL,
56         /**
57          * Storage of the root statement of a particular source.
58          */
59         ROOT_STATEMENT_LOCAL
60     }
61
62     public interface Registry {
63         /**
64          * Get a namespace behavior.
65          *
66          * @param type Namespace type class
67          * @param <K> key type
68          * @param <V> value type
69          * @param <N> namespace type
70          * @return Namespace behaviour
71          * @throws NamespaceNotAvailableException when the namespace is not available
72          */
73         <K, V, N extends IdentifierNamespace<K, V>> NamespaceBehaviour<K, V, N> getNamespaceBehaviour(Class<N> type);
74     }
75
76     public interface NamespaceStorageNode {
77         /**
78          * Return local namespace behaviour type.
79          *
80          * @return local namespace behaviour type {@link NamespaceBehaviour}
81          */
82         StorageNodeType getStorageNodeType();
83
84         @Nullable NamespaceStorageNode getParentNamespaceStorage();
85
86         <K, V, N extends IdentifierNamespace<K, V>> @Nullable V getFromLocalStorage(Class<N> type, K key);
87
88         <K, V, N extends IdentifierNamespace<K, V>> @Nullable Map<K, V> getAllFromLocalStorage(Class<N> type);
89
90         /**
91          * Populate specified namespace with a key/value pair, overwriting previous contents. Similar to
92          * {@link Map#put(Object, Object)}.
93          *
94          * @param type Namespace identifier
95          * @param key Key
96          * @param value Value
97          * @return Previously-stored value, or null if the key was not present
98          */
99         <K, V, N extends IdentifierNamespace<K, V>> @Nullable V putToLocalStorage(Class<N> type, K key, V value);
100
101         /**
102          * Populate specified namespace with a key/value pair unless the key is already associated with a value. Similar
103          * to {@link Map#putIfAbsent(Object, Object)}.
104          *
105          * @param type Namespace identifier
106          * @param key Key
107          * @param value Value
108          * @return Preexisting value or null if there was no previous mapping
109          */
110         <K, V, N extends IdentifierNamespace<K, V>> @Nullable V putToLocalStorageIfAbsent(Class<N> type, K key,
111                 V value);
112     }
113
114     /**
115      * Interface implemented by {@link NamespaceStorageNode}s which support dynamic addition of child elements as they
116      * are requested. This means that such a node can, defer creation of child namespace storage nodes, in effect lazily
117      * expanding this namespace on an if-needed basis.
118      */
119     @Beta
120     public interface OnDemandSchemaTreeStorageNode extends NamespaceStorageNode {
121         /**
122          * Request that a new member of this node's schema tree statement be added. Implementations are required to
123          * perform lookup in their internal structure and create a child if tracktable. Resulting node is expected to
124          * have been registered with local storage, so that it is accessible through
125          * {@link #getFromLocalStorage(Class, Object)}.
126          *
127          * <p>
128          * This method must not change its mind about a child's presence -- once it returns non-present, it has to be
129          * always returning non-present.
130          *
131          * @param qname node identifier of the child being requested
132          * @return Requested child, if it is present.
133          */
134         <D extends DeclaredStatement<QName>, E extends EffectiveStatement<QName, D>>
135             @Nullable StmtContext<QName, D, E> requestSchemaTreeChild(QName qname);
136     }
137
138     protected NamespaceBehaviour(final Class<N> identifier) {
139         super(identifier);
140     }
141
142     /**
143      * Creates a global namespace behaviour for supplied namespace type. Global behaviour stores and loads all values
144      * from root {@link NamespaceStorageNode} with type of {@link StorageNodeType#GLOBAL}.
145      *
146      * @param identifier Namespace identifier.
147      * @param <K> type parameter
148      * @param <V> type parameter
149      * @param <N> type parameter
150      * @return global namespace behaviour for supplied namespace type.
151      */
152     public static <K, V, N extends IdentifierNamespace<K, V>> @NonNull NamespaceBehaviour<K, V, N> global(
153             final Class<N> identifier) {
154         return new StorageSpecific<>(identifier, StorageNodeType.GLOBAL);
155     }
156
157     /**
158      * Creates source-local namespace behaviour for supplied namespace type. Source-local namespace behaviour stores
159      * and loads all values from closest {@link NamespaceStorageNode} ancestor with type
160      * of {@link StorageNodeType#SOURCE_LOCAL_SPECIAL}.
161      *
162      * @param identifier Namespace identifier.
163      * @param <K> type parameter
164      * @param <V> type parameter
165      * @param <N> type parameter
166      * @return source-local namespace behaviour for supplied namespace type.
167      */
168     public static <K, V, N extends IdentifierNamespace<K, V>> @NonNull NamespaceBehaviour<K, V, N> sourceLocal(
169             final Class<N> identifier) {
170         return new StorageSpecific<>(identifier, StorageNodeType.SOURCE_LOCAL_SPECIAL);
171     }
172
173     public static <K, V, N extends IdentifierNamespace<K, V>> @NonNull NamespaceBehaviour<K, V, N> statementLocal(
174            final Class<N> identifier) {
175         return new StorageSpecific<>(identifier, StorageNodeType.STATEMENT_LOCAL);
176     }
177
178     /**
179      * Creates a root-statement-local namespace behaviour for supplied namespace type. Root-statement-local namespace
180      * behaviour stores and loads all values from closest {@link NamespaceStorageNode} ancestor with type
181      * of {@link StorageNodeType#ROOT_STATEMENT_LOCAL}.
182      *
183      * @param identifier Namespace identifier.
184      * @param <K> type parameter
185      * @param <V> type parameter
186      * @param <N> type parameter
187      * @return root-statement-local namespace behaviour for supplied namespace type.
188      */
189     public static <K, V, N extends IdentifierNamespace<K, V>> @NonNull NamespaceBehaviour<K, V, N> rootStatementLocal(
190             final Class<N> identifier) {
191         return new StorageSpecific<>(identifier, StorageNodeType.ROOT_STATEMENT_LOCAL);
192     }
193
194     /**
195      * Creates tree-scoped namespace behaviour for supplied namespace type. Tree-scoped namespace behaviour searches
196      * for value in all storage nodes up to the root and stores values in supplied node.
197      *
198      * @param identifier
199      *            Namespace identifier.
200      * @param <K> type parameter
201      * @param <V> type parameter
202      * @param <N> type parameter
203      * @return tree-scoped namespace behaviour for supplied namespace type.
204      */
205     public static <K, V, N extends IdentifierNamespace<K, V>> @NonNull NamespaceBehaviour<K, V, N> treeScoped(
206             final Class<N> identifier) {
207         return new TreeScoped<>(identifier);
208     }
209
210     /**
211      * Returns a value from model namespace storage according to key param class.
212      *
213      * @param storage namespace storage
214      * @param key type parameter
215      * @return value from model namespace storage according to key param class
216      */
217     public abstract V getFrom(NamespaceStorageNode storage, K key);
218
219     /**
220      * Returns the key/value mapping best matching specified criterion.
221      *
222      * @param storage namespace storage
223      * @param criterion selection criterion
224      * @return Selected mapping, if available.
225      */
226     public final Optional<Entry<K, V>> getFrom(final NamespaceStorageNode storage,
227             final NamespaceKeyCriterion<K> criterion) {
228         final Map<K, V> mappings = getAllFrom(storage);
229         if (mappings == null) {
230             return Optional.empty();
231         }
232
233         Entry<K, V> match = null;
234         for (Entry<K, V> entry : mappings.entrySet()) {
235             final K key = entry.getKey();
236             if (criterion.match(key)) {
237                 if (match != null) {
238                     final K selected = criterion.select(match.getKey(), key);
239                     if (selected.equals(match.getKey())) {
240                         continue;
241                     }
242
243                     Verify.verify(selected == key, "Criterion %s selected invalid key %s from candidates [%s %s]",
244                             selected, match.getKey(), key);
245                 }
246
247                 match = entry;
248             }
249         }
250
251         return Optional.ofNullable(match);
252     }
253
254     /**
255      * Returns all values of a keys of param class from model namespace storage.
256      *
257      * @param storage namespace storage
258      * @return all values of keys of param class from model namespace storage
259      */
260     public abstract Map<K, V> getAllFrom(NamespaceStorageNode storage);
261
262     /**
263      * Adds a key/value to corresponding namespace storage according to param class.
264      *
265      * @param storage namespace storage
266      * @param key type parameter
267      * @param value type parameter
268      */
269     public abstract void addTo(NamespaceStorageNode storage, K key, V value);
270
271     protected final V getFromLocalStorage(final NamespaceStorageNode storage, final K key) {
272         return storage.getFromLocalStorage(getIdentifier(), key);
273     }
274
275     protected final Map<K, V> getAllFromLocalStorage(final NamespaceStorageNode storage) {
276         return storage.getAllFromLocalStorage(getIdentifier());
277     }
278
279     protected final void addToStorage(final NamespaceStorageNode storage, final K key, final V value) {
280         storage.putToLocalStorage(getIdentifier(), key, value);
281     }
282
283     static final class StorageSpecific<K, V, N extends IdentifierNamespace<K, V>> extends NamespaceBehaviour<K, V, N> {
284         private final StorageNodeType storageType;
285
286         StorageSpecific(final Class<N> identifier, final StorageNodeType type) {
287             super(identifier);
288             storageType = requireNonNull(type);
289         }
290
291         @Override
292         public V getFrom(final NamespaceStorageNode storage, final K key) {
293             NamespaceStorageNode current = findClosestTowardsRoot(storage, storageType);
294             return getFromLocalStorage(current, key);
295         }
296
297         @Override
298         public Map<K, V> getAllFrom(final NamespaceStorageNode storage) {
299             NamespaceStorageNode current = storage;
300             while (current.getStorageNodeType() != storageType) {
301                 current = current.getParentNamespaceStorage();
302             }
303
304             return getAllFromLocalStorage(current);
305         }
306
307         @Override
308         public void addTo(final NamespaceBehaviour.NamespaceStorageNode storage, final K key, final V value) {
309             NamespaceStorageNode current = findClosestTowardsRoot(storage, storageType);
310             addToStorage(current, key, value);
311         }
312
313         @Override
314         protected ToStringHelper addToStringAttributes(final ToStringHelper helper) {
315             return super.addToStringAttributes(helper.add("type", storageType));
316         }
317     }
318
319     static final class TreeScoped<K, V, N extends IdentifierNamespace<K, V>> extends NamespaceBehaviour<K, V, N> {
320
321         TreeScoped(final Class<N> identifier) {
322             super(identifier);
323         }
324
325         @Override
326         public V getFrom(final NamespaceStorageNode storage, final K key) {
327             NamespaceStorageNode current = storage;
328             while (current != null) {
329                 final V val = getFromLocalStorage(current, key);
330                 if (val != null) {
331                     return val;
332                 }
333                 current = current.getParentNamespaceStorage();
334             }
335             return null;
336         }
337
338         @Override
339         public Map<K, V> getAllFrom(final NamespaceStorageNode storage) {
340             NamespaceStorageNode current = storage;
341             while (current != null) {
342                 final Map<K, V> val = getAllFromLocalStorage(current);
343                 if (val != null) {
344                     return val;
345                 }
346                 current = current.getParentNamespaceStorage();
347             }
348             return null;
349         }
350
351         @Override
352         public void addTo(final NamespaceStorageNode storage, final K key, final V value) {
353             addToStorage(storage, key, value);
354         }
355
356     }
357
358     protected static NamespaceStorageNode findClosestTowardsRoot(final NamespaceStorageNode storage,
359             final StorageNodeType type) {
360         NamespaceStorageNode current = storage;
361         while (current != null && current.getStorageNodeType() != type) {
362             current = current.getParentNamespaceStorage();
363         }
364         return current;
365     }
366
367     @Override
368     protected ToStringHelper addToStringAttributes(final ToStringHelper helper) {
369         return helper.add("identifier", getIdentifier().getName());
370     }
371 }