595831a7b193eef44cb49dc4b5305fb60bd27b02
[yangtools.git] / parser / yang-parser-reactor / src / main / java / org / opendaylight / yangtools / yang / parser / stmt / reactor / AbstractNamespaceStorage.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 static com.google.common.base.Verify.verifyNotNull;
11
12 import com.google.common.collect.ImmutableMap;
13 import java.util.HashMap;
14 import java.util.Map;
15 import java.util.Map.Entry;
16 import org.eclipse.jdt.annotation.NonNull;
17 import org.opendaylight.yangtools.yang.parser.spi.meta.NamespaceBehaviour;
18 import org.opendaylight.yangtools.yang.parser.spi.meta.NamespaceNotAvailableException;
19 import org.opendaylight.yangtools.yang.parser.spi.meta.NamespaceStorage;
20 import org.opendaylight.yangtools.yang.parser.spi.meta.ParserNamespace;
21 import org.opendaylight.yangtools.yang.parser.spi.source.SourceException;
22 import org.slf4j.Logger;
23 import org.slf4j.LoggerFactory;
24
25 abstract class AbstractNamespaceStorage implements NamespaceStorage {
26     private static final Logger LOG = LoggerFactory.getLogger(AbstractNamespaceStorage.class);
27
28     private Map<ParserNamespace<?, ?>, Map<?, ?>> namespaces = ImmutableMap.of();
29
30     /**
31      * {@inheritDoc}
32      *
33      * <p>
34      * This method override provides bimorphic invocation on this method invocation between
35      * {@link SourceSpecificContext} and the more general {@link AbstractNamespaceStorage}. We typically do not expect
36      * the two accesses to overlap.
37      */
38     @Override
39     public abstract NamespaceStorage getParentStorage();
40
41     /**
42      * Get a namespace behavior.
43      *
44      * @param <K> key type
45      * @param <V> value type
46      * @param namespace Namespace type
47      * @return Namespace behaviour
48      * @throws NamespaceNotAvailableException when the namespace is not available
49      * @throws NullPointerException if {@code namespace} is {@code null}
50      */
51     abstract <K, V> @NonNull NamespaceBehaviour<K, V> getNamespaceBehaviour(ParserNamespace<K, V> namespace);
52
53     // FIXME: 8.0.0: do we really need this method?
54     final void checkLocalNamespaceAllowed(final ParserNamespace<?, ?> type) {
55         // Always no-op. We used to route this towards StatementDefinitionContext, but this method remained
56         // unimplemented even there.
57     }
58
59     /**
60      * Occurs when an item is added to model namespace.
61      *
62      * @throws SourceException instance of SourceException
63      */
64     <K, V> void onNamespaceElementAdded(final ParserNamespace<K, V> type, final K key, final V value) {
65         // NOOP
66     }
67
68     final <K, V> Map<K, V> getNamespace(final ParserNamespace<K, V> type) {
69         return getNamespaceBehaviour(type).getAllFrom(this);
70     }
71
72     @SuppressWarnings("unchecked")
73     final <K, V> Map<K, V> getLocalNamespace(final ParserNamespace<K, V> type) {
74         final var local = verifyNotNull(namespaces, "Attempted to access swept namespaces of %s", this);
75         return (Map<K, V>) local.get(type);
76     }
77
78     final <K, V, T extends K, U extends V> void addToNamespace(final ParserNamespace<K, V> type, final T key,
79             final U value) {
80         getNamespaceBehaviour(type).addTo(this, key, value);
81     }
82
83     @Override
84     public <K, V> V getFromLocalStorage(final ParserNamespace<K, V> type, final K key) {
85         final var localNamespace = getLocalNamespace(type);
86         return localNamespace == null ? null : localNamespace.get(key);
87     }
88
89     @Override
90     public <K, V> Map<K, V> getAllFromLocalStorage(final ParserNamespace<K, V> type) {
91         return getLocalNamespace(type);
92     }
93
94     @Override
95     public <K, V> V putToLocalStorage(final ParserNamespace<K, V> type, final K key, final V value) {
96         final V ret = ensureLocalNamespace(type).put(key, value);
97         onNamespaceElementAdded(type, key, value);
98         return ret;
99     }
100
101     @Override
102     public final <K, V> V putToLocalStorageIfAbsent(final ParserNamespace<K, V> type, final K key, final V value) {
103         final V ret = ensureLocalNamespace(type).putIfAbsent(key, value);
104         if (ret == null) {
105             onNamespaceElementAdded(type, key, value);
106         }
107         return ret;
108     }
109
110     void sweepNamespaces() {
111         namespaces = null;
112         LOG.trace("Swept namespace storages of {}", this);
113     }
114
115     void sweepNamespaces(final Map<ParserNamespace<?, ?>, SweptNamespace> toWipe) {
116         switch (namespaces.size()) {
117             case 0:
118                 namespaces = ImmutableMap.copyOf(toWipe);
119                 return;
120             case 1:
121                 namespaces = new HashMap<>(namespaces);
122                 break;
123             default:
124                 // No-op, we are ready
125         }
126
127         namespaces.putAll(toWipe);
128         LOG.trace("Trimmed namespace storages of {} to {}", this, namespaces.keySet());
129     }
130
131     private <K, V> Map<K, V> ensureLocalNamespace(final ParserNamespace<K, V> type) {
132         var ret = getLocalNamespace(type);
133         if (ret == null) {
134             checkLocalNamespaceAllowed(type);
135             ret = new HashMap<>(1);
136
137             switch (namespaces.size()) {
138                 case 0:
139                     // We typically have small population of namespaces, use a singleton map
140                     namespaces = ImmutableMap.of(type, ret);
141                     break;
142                 case 1:
143                     // Alright, time to grow to a full HashMap
144                     final Map<ParserNamespace<?, ?>, Map<?,?>> newNamespaces = new HashMap<>(4);
145                     final Entry<ParserNamespace<?, ?>, Map<?, ?>> entry = namespaces.entrySet().iterator().next();
146                     newNamespaces.put(entry.getKey(), entry.getValue());
147                     namespaces = newNamespaces;
148                     // fall through
149                 default:
150                     // Already expanded, just put the new namespace
151                     namespaces.put(type, ret);
152             }
153         }
154
155         return ret;
156     }
157 }