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