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