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