Populate xpath/ hierarchy
[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.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.SchemaTreeNamespace;
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(Class)}, {@link #sourceLocal(Class)} and
34  * {@link #treeScoped(Class)}.
35  *
36  * @param <K> Key type
37  * @param <V> Value type
38  * @param <N> Namespace Type
39  */
40 public abstract class NamespaceBehaviour<K, V, N extends ParserNamespace<K, V>>
41         extends AbstractSimpleIdentifiable<Class<N>> {
42
43     public enum StorageNodeType {
44         /**
45          * Global storage, visible from all sources.
46          */
47         GLOBAL,
48         /**
49          * Storage of the root statement of a particular source and any sources it is importing.
50          */
51         // FIXME: 7.0.0: this is a misnomer and should be renamed
52         SOURCE_LOCAL_SPECIAL,
53         /**
54          * Storage of a single statement.
55          */
56         STATEMENT_LOCAL,
57         /**
58          * Storage of the root statement of a particular source.
59          */
60         ROOT_STATEMENT_LOCAL
61     }
62
63     public interface Registry {
64         /**
65          * Get a namespace behavior.
66          *
67          * @param type Namespace type class
68          * @param <K> key type
69          * @param <V> value type
70          * @param <N> namespace type
71          * @return Namespace behaviour
72          * @throws NamespaceNotAvailableException when the namespace is not available
73          */
74         <K, V, N extends ParserNamespace<K, V>> NamespaceBehaviour<K, V, N> getNamespaceBehaviour(Class<N> type);
75     }
76
77     public interface NamespaceStorageNode {
78         /**
79          * Return local namespace behaviour type.
80          *
81          * @return local namespace behaviour type {@link NamespaceBehaviour}
82          */
83         StorageNodeType getStorageNodeType();
84
85         @Nullable NamespaceStorageNode getParentNamespaceStorage();
86
87         <K, V, N extends ParserNamespace<K, V>> @Nullable V getFromLocalStorage(Class<N> type, K key);
88
89         <K, V, N extends ParserNamespace<K, V>> @Nullable Map<K, V> getAllFromLocalStorage(Class<N> type);
90
91         /**
92          * Populate specified namespace with a key/value pair, overwriting previous contents. Similar to
93          * {@link Map#put(Object, Object)}.
94          *
95          * @param type Namespace identifier
96          * @param key Key
97          * @param value Value
98          * @return Previously-stored value, or null if the key was not present
99          */
100         <K, V, N extends ParserNamespace<K, V>> @Nullable V putToLocalStorage(Class<N> type, K key, V value);
101
102         /**
103          * Populate specified namespace with a key/value pair unless the key is already associated with a value. Similar
104          * to {@link Map#putIfAbsent(Object, Object)}.
105          *
106          * @param type Namespace identifier
107          * @param key Key
108          * @param value Value
109          * @return Preexisting value or null if there was no previous mapping
110          */
111         <K, V, N extends ParserNamespace<K, V>> @Nullable V putToLocalStorageIfAbsent(Class<N> type, K key, 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 tractable. 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          * <p>
132          * The results produced by this method are expected to be consistent with
133          * {@link SchemaTreeAwareEffectiveStatement#findSchemaTreeNode(QName)} and
134          * {@link SchemaTreeNamespace#getFrom(NamespaceStorageNode, QName)}.
135          *
136          * @param qname node identifier of the child being requested
137          * @return Requested child, if it is present.
138          * @throws NullPointerException in {@code qname} is null
139          */
140         <D extends DeclaredStatement<QName>, E extends SchemaTreeEffectiveStatement<D>>
141             @Nullable StmtContext<QName, D, E> requestSchemaTreeChild(QName qname);
142     }
143
144     protected NamespaceBehaviour(final Class<N> identifier) {
145         super(identifier);
146     }
147
148     /**
149      * Creates a global namespace behaviour for supplied namespace type. Global behaviour stores and loads all values
150      * from root {@link NamespaceStorageNode} with type of {@link StorageNodeType#GLOBAL}.
151      *
152      * @param identifier Namespace identifier.
153      * @param <K> type parameter
154      * @param <V> type parameter
155      * @param <N> type parameter
156      * @return global namespace behaviour for supplied namespace type.
157      */
158     public static <K, V, N extends ParserNamespace<K, V>> @NonNull NamespaceBehaviour<K, V, N> global(
159             final Class<N> identifier) {
160         return new StorageSpecific<>(identifier, StorageNodeType.GLOBAL);
161     }
162
163     /**
164      * Creates source-local namespace behaviour for supplied namespace type. Source-local namespace behaviour stores
165      * and loads all values from closest {@link NamespaceStorageNode} ancestor with type
166      * of {@link StorageNodeType#SOURCE_LOCAL_SPECIAL}.
167      *
168      * @param identifier Namespace identifier.
169      * @param <K> type parameter
170      * @param <V> type parameter
171      * @param <N> type parameter
172      * @return source-local namespace behaviour for supplied namespace type.
173      */
174     public static <K, V, N extends ParserNamespace<K, V>> @NonNull NamespaceBehaviour<K, V, N> sourceLocal(
175             final Class<N> identifier) {
176         return new StorageSpecific<>(identifier, StorageNodeType.SOURCE_LOCAL_SPECIAL);
177     }
178
179     public static <K, V, N extends ParserNamespace<K, V>> @NonNull NamespaceBehaviour<K, V, N> statementLocal(
180            final Class<N> identifier) {
181         return new StatementLocal<>(identifier);
182     }
183
184     /**
185      * Creates a root-statement-local namespace behaviour for supplied namespace type. Root-statement-local namespace
186      * behaviour stores and loads all values from closest {@link NamespaceStorageNode} ancestor with type
187      * of {@link StorageNodeType#ROOT_STATEMENT_LOCAL}.
188      *
189      * @param identifier Namespace identifier.
190      * @param <K> type parameter
191      * @param <V> type parameter
192      * @param <N> type parameter
193      * @return root-statement-local namespace behaviour for supplied namespace type.
194      */
195     public static <K, V, N extends ParserNamespace<K, V>> @NonNull NamespaceBehaviour<K, V, N> rootStatementLocal(
196             final Class<N> identifier) {
197         return new StorageSpecific<>(identifier, StorageNodeType.ROOT_STATEMENT_LOCAL);
198     }
199
200     /**
201      * Creates tree-scoped namespace behaviour for supplied namespace type. Tree-scoped namespace behaviour searches
202      * for value in all storage nodes up to the root and stores values in supplied node.
203      *
204      * @param identifier
205      *            Namespace identifier.
206      * @param <K> type parameter
207      * @param <V> type parameter
208      * @param <N> type parameter
209      * @return tree-scoped namespace behaviour for supplied namespace type.
210      */
211     public static <K, V, N extends ParserNamespace<K, V>> @NonNull NamespaceBehaviour<K, V, N> treeScoped(
212             final Class<N> identifier) {
213         return new TreeScoped<>(identifier);
214     }
215
216     /**
217      * Returns a value from model namespace storage according to key param class.
218      *
219      * @param storage namespace storage
220      * @param key type parameter
221      * @return value from model namespace storage according to key param class
222      */
223     public abstract V getFrom(NamespaceStorageNode storage, K key);
224
225     /**
226      * Returns the key/value mapping best matching specified criterion.
227      *
228      * @param storage namespace storage
229      * @param criterion selection criterion
230      * @return Selected mapping, if available.
231      */
232     public final Optional<Entry<K, V>> getFrom(final NamespaceStorageNode storage,
233             final NamespaceKeyCriterion<K> criterion) {
234         final Map<K, V> mappings = getAllFrom(storage);
235         if (mappings == null) {
236             return Optional.empty();
237         }
238
239         Entry<K, V> match = null;
240         for (Entry<K, V> entry : mappings.entrySet()) {
241             final K key = entry.getKey();
242             if (criterion.match(key)) {
243                 if (match != null) {
244                     final K selected = criterion.select(match.getKey(), key);
245                     if (selected.equals(match.getKey())) {
246                         continue;
247                     }
248
249                     Verify.verify(selected == key, "Criterion %s selected invalid key %s from candidates [%s %s]",
250                             selected, match.getKey(), key);
251                 }
252
253                 match = entry;
254             }
255         }
256
257         return Optional.ofNullable(match);
258     }
259
260     /**
261      * Returns all values of a keys of param class from model namespace storage.
262      *
263      * @param storage namespace storage
264      * @return all values of keys of param class from model namespace storage
265      */
266     public abstract Map<K, V> getAllFrom(NamespaceStorageNode storage);
267
268     /**
269      * Adds a key/value to corresponding namespace storage according to param class.
270      *
271      * @param storage namespace storage
272      * @param key type parameter
273      * @param value type parameter
274      */
275     public abstract void addTo(NamespaceStorageNode storage, K key, V value);
276
277     protected final V getFromLocalStorage(final NamespaceStorageNode storage, final K key) {
278         return storage.getFromLocalStorage(getIdentifier(), key);
279     }
280
281     protected final Map<K, V> getAllFromLocalStorage(final NamespaceStorageNode storage) {
282         return storage.getAllFromLocalStorage(getIdentifier());
283     }
284
285     protected final void addToStorage(final NamespaceStorageNode storage, final K key, final V value) {
286         storage.putToLocalStorage(getIdentifier(), key, value);
287     }
288
289     abstract static class AbstractSpecific<K, V, N extends ParserNamespace<K, V>>
290             extends NamespaceBehaviour<K, V, N> {
291         AbstractSpecific(final Class<N> identifier) {
292             super(identifier);
293         }
294
295         @Override
296         public final V getFrom(final NamespaceStorageNode storage, final K key) {
297             return getFromLocalStorage(findStorageNode(storage), key);
298         }
299
300         @Override
301         public final Map<K, V> getAllFrom(final NamespaceStorageNode storage) {
302             return getAllFromLocalStorage(findStorageNode(storage));
303         }
304
305         @Override
306         public final void addTo(final NamespaceStorageNode storage, final K key, final V value) {
307             addToStorage(findStorageNode(storage), key, value);
308         }
309
310         abstract NamespaceStorageNode findStorageNode(NamespaceStorageNode storage);
311     }
312
313     static final class StatementLocal<K, V, N extends ParserNamespace<K, V>> extends AbstractSpecific<K, V, N> {
314         StatementLocal(final Class<N> identifier) {
315             super(identifier);
316         }
317
318         @Override
319         NamespaceStorageNode findStorageNode(final NamespaceStorageNode storage) {
320             return storage;
321         }
322     }
323
324     static final class StorageSpecific<K, V, N extends ParserNamespace<K, V>> extends AbstractSpecific<K, V, N> {
325         private final StorageNodeType storageType;
326
327         StorageSpecific(final Class<N> identifier, final StorageNodeType type) {
328             super(identifier);
329             storageType = requireNonNull(type);
330         }
331
332         @Override
333         NamespaceStorageNode findStorageNode(final NamespaceStorageNode storage) {
334             return findClosestTowardsRoot(storage, storageType);
335         }
336
337         @Override
338         protected ToStringHelper addToStringAttributes(final ToStringHelper helper) {
339             return super.addToStringAttributes(helper.add("type", storageType));
340         }
341     }
342
343     static final class TreeScoped<K, V, N extends ParserNamespace<K, V>> extends NamespaceBehaviour<K, V, N> {
344         TreeScoped(final Class<N> identifier) {
345             super(identifier);
346         }
347
348         @Override
349         public V getFrom(final NamespaceStorageNode storage, final K key) {
350             NamespaceStorageNode current = storage;
351             while (current != null) {
352                 final V val = getFromLocalStorage(current, key);
353                 if (val != null) {
354                     return val;
355                 }
356                 current = current.getParentNamespaceStorage();
357             }
358             return null;
359         }
360
361         @Override
362         public Map<K, V> getAllFrom(final NamespaceStorageNode storage) {
363             NamespaceStorageNode current = storage;
364             while (current != null) {
365                 final Map<K, V> val = getAllFromLocalStorage(current);
366                 if (val != null) {
367                     return val;
368                 }
369                 current = current.getParentNamespaceStorage();
370             }
371             return null;
372         }
373
374         @Override
375         public void addTo(final NamespaceStorageNode storage, final K key, final V value) {
376             addToStorage(storage, key, value);
377         }
378
379     }
380
381     protected static NamespaceStorageNode findClosestTowardsRoot(final NamespaceStorageNode storage,
382             final StorageNodeType type) {
383         NamespaceStorageNode current = storage;
384         while (current != null && current.getStorageNodeType() != type) {
385             current = current.getParentNamespaceStorage();
386         }
387         return current;
388     }
389
390     @Override
391     protected ToStringHelper addToStringAttributes(final ToStringHelper helper) {
392         return helper.add("identifier", getIdentifier().getName());
393     }
394 }