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