Clarify NamespaceStorageSupport.getParentNamespaceStorage()
[yangtools.git] / yang / yang-parser-reactor / src / main / java / org / opendaylight / yangtools / yang / parser / stmt / reactor / NamespaceStorageSupport.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.stmt.reactor;
9
10 import com.google.common.collect.ImmutableMap;
11 import java.util.HashMap;
12 import java.util.Map;
13 import java.util.Map.Entry;
14 import java.util.Optional;
15 import org.opendaylight.yangtools.yang.model.api.meta.IdentifierNamespace;
16 import org.opendaylight.yangtools.yang.parser.spi.meta.NamespaceBehaviour;
17 import org.opendaylight.yangtools.yang.parser.spi.meta.NamespaceBehaviour.NamespaceStorageNode;
18 import org.opendaylight.yangtools.yang.parser.spi.meta.NamespaceBehaviour.Registry;
19 import org.opendaylight.yangtools.yang.parser.spi.meta.NamespaceKeyCriterion;
20 import org.opendaylight.yangtools.yang.parser.spi.meta.NamespaceNotAvailableException;
21 import org.opendaylight.yangtools.yang.parser.spi.meta.StatementNamespace;
22 import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext;
23 import org.opendaylight.yangtools.yang.parser.spi.source.SourceException;
24
25 abstract class NamespaceStorageSupport implements NamespaceStorageNode {
26
27     private Map<Class<?>, Map<?,?>> namespaces = ImmutableMap.of();
28
29     /**
30      * {@inheritDoc}
31      *
32      * <p>
33      * This method override provides bimorphic invocation on this method invocation between
34      * {@link SourceSpecificContext} and the more general {@link NamespaceStorageSupport}. We typically do not expect
35      * the two accesses to overlap.
36      */
37     @Override
38     public abstract NamespaceStorageNode getParentNamespaceStorage();
39
40     /**
41      * Return the registry of a source context.
42      *
43      * @return registry of source context
44      */
45     public abstract Registry getBehaviourRegistry();
46
47     protected void checkLocalNamespaceAllowed(final Class<? extends IdentifierNamespace<?, ?>> type) {
48         // NOOP
49     }
50
51     /**
52      * Occurs when an item is added to model namespace.
53      *
54      * @throws SourceException instance of SourceException
55      */
56     protected <K, V, N extends IdentifierNamespace<K, V>> void onNamespaceElementAdded(final Class<N> type, final K key,
57             final V value) {
58         // NOOP
59     }
60
61     public final <K, V, N extends IdentifierNamespace<K, V>> Optional<Entry<K, V>> getFromNamespace(
62             final Class<N> type, final NamespaceKeyCriterion<K> criterion) {
63         return getBehaviourRegistry().getNamespaceBehaviour(type).getFrom(this, criterion);
64     }
65
66     public final <K, V, N extends IdentifierNamespace<K, V>> Map<K, V> getNamespace(final Class<N> type) {
67         return getBehaviourRegistry().getNamespaceBehaviour(type).getAllFrom(this);
68     }
69
70     @SuppressWarnings("unchecked")
71     final <K, V, N extends IdentifierNamespace<K, V>> Map<K, V> getLocalNamespace(final Class<N> type) {
72         return (Map<K, V>) namespaces.get(type);
73     }
74
75     final <K, V, T extends K, U extends V, N extends IdentifierNamespace<K, V>> void addToNamespace(
76             final Class<N> type, final T key, final U value) {
77         getBehaviourRegistry().getNamespaceBehaviour(type).addTo(this, key, value);
78     }
79
80     final <K, V, T extends K, U extends V, N extends IdentifierNamespace<K, V>> void addToNamespace(
81             final Class<N> type, final Map<T, U> map) {
82         final NamespaceBehaviour<K, V, N> behavior = getBehaviourRegistry().getNamespaceBehaviour(type);
83         for (final Entry<T, U> validationBundle : map.entrySet()) {
84             behavior.addTo(this, validationBundle.getKey(), validationBundle.getValue());
85         }
86     }
87
88     /**
89      * Associate a context with a key within a namespace.
90      *
91      * @param type Namespace type
92      * @param key Key
93      * @param value Context value
94      * @param <K> namespace key type
95      * @param <N> namespace type
96      * @throws NamespaceNotAvailableException when the namespace is not available.
97      */
98     @SuppressWarnings({ "unchecked", "rawtypes" })
99     public final <K, N extends StatementNamespace<K, ?,?>> void addContextToNamespace(final Class<N> type, final K key,
100             final StmtContext<?, ?, ?> value) {
101         getBehaviourRegistry().getNamespaceBehaviour((Class)type).addTo(this, key, value);
102     }
103
104     @SuppressWarnings("unchecked")
105     @Override
106     public <K, V, N extends IdentifierNamespace<K, V>> V getFromLocalStorage(final Class<N> type, final K key) {
107         final Map<K, V> localNamespace = (Map<K, V>) namespaces.get(type);
108         return localNamespace == null ? null : localNamespace.get(key);
109     }
110
111     @Override
112     public <K, V, N extends IdentifierNamespace<K, V>> Map<K, V> getAllFromLocalStorage(final Class<N> type) {
113         @SuppressWarnings("unchecked")
114         final Map<K, V> localNamespace = (Map<K, V>) namespaces.get(type);
115         return localNamespace;
116     }
117
118     private <K, V, N extends IdentifierNamespace<K, V>> Map<K, V> ensureLocalNamespace(final Class<N> type) {
119         @SuppressWarnings("unchecked")
120         Map<K, V> ret = (Map<K,V>) namespaces.get(type);
121         if (ret == null) {
122             checkLocalNamespaceAllowed(type);
123             ret = new HashMap<>(1);
124
125             switch (namespaces.size()) {
126                 case 0:
127                     // We typically have small population of namespaces, use a singleton map
128                     namespaces = ImmutableMap.of(type, ret);
129                     break;
130                 case 1:
131                     // Alright, time to grow to a full HashMap
132                     final Map<Class<?>, Map<?,?>> newNamespaces = new HashMap<>(4);
133                     final Entry<Class<?>, Map<?, ?>> entry = namespaces.entrySet().iterator().next();
134                     newNamespaces.put(entry.getKey(), entry.getValue());
135                     namespaces = newNamespaces;
136                     // fall through
137                 default:
138                     // Already expanded, just put the new namespace
139                     namespaces.put(type, ret);
140             }
141         }
142
143         return ret;
144     }
145
146     @Override
147     public <K, V, N extends IdentifierNamespace<K, V>> V putToLocalStorage(final Class<N> type, final K key,
148             final V value) {
149         final V ret = ensureLocalNamespace(type).put(key, value);
150         onNamespaceElementAdded(type, key, value);
151         return ret;
152     }
153
154     @Override
155     public <K, V, N extends IdentifierNamespace<K, V>> V putToLocalStorageIfAbsent(final Class<N> type, final K key,
156             final V value) {
157         final V ret = ensureLocalNamespace(type).putIfAbsent(key, value);
158         if (ret == null) {
159             onNamespaceElementAdded(type, key, value);
160         }
161         return ret;
162     }
163 }