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