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