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