Disconnect StatementSupportBundle.Builder from concepts
[yangtools.git] / parser / yang-parser-spi / src / main / java / org / opendaylight / yangtools / yang / parser / spi / meta / StatementSupportBundle.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 com.google.common.base.Preconditions.checkArgument;
11 import static com.google.common.base.Preconditions.checkState;
12 import static java.util.Objects.requireNonNull;
13
14 import com.google.common.collect.HashBasedTable;
15 import com.google.common.collect.ImmutableMap;
16 import com.google.common.collect.ImmutableSet;
17 import com.google.common.collect.ImmutableTable;
18 import com.google.common.collect.Table;
19 import java.util.HashMap;
20 import java.util.Map;
21 import java.util.Set;
22 import org.eclipse.jdt.annotation.NonNull;
23 import org.opendaylight.yangtools.concepts.Immutable;
24 import org.opendaylight.yangtools.concepts.Mutable;
25 import org.opendaylight.yangtools.yang.common.QName;
26 import org.opendaylight.yangtools.yang.common.YangVersion;
27 import org.slf4j.Logger;
28 import org.slf4j.LoggerFactory;
29
30 public final class StatementSupportBundle implements Immutable, NamespaceBehaviour.Registry {
31
32     private static final StatementSupportBundle EMPTY = new StatementSupportBundle(null, null, ImmutableMap.of(),
33             ImmutableMap.of(), ImmutableTable.of());
34
35     private final StatementSupportBundle parent;
36     private final ImmutableMap<QName, StatementSupport<?, ?, ?>> commonDefinitions;
37     private final ImmutableTable<YangVersion, QName, StatementSupport<?, ?, ?>> versionSpecificDefinitions;
38     private final ImmutableMap<Class<?>, NamespaceBehaviour<?, ?, ?>> namespaceDefinitions;
39     private final ImmutableSet<YangVersion> supportedVersions;
40
41     private StatementSupportBundle(final StatementSupportBundle parent,
42             final ImmutableSet<YangVersion> supportedVersions,
43             final ImmutableMap<QName, StatementSupport<?, ?, ?>> commonStatements,
44             final ImmutableMap<Class<?>, NamespaceBehaviour<?, ?, ?>> namespaces,
45             final ImmutableTable<YangVersion, QName, StatementSupport<?, ?, ?>> versionSpecificStatements) {
46         this.parent = parent;
47         this.supportedVersions = supportedVersions;
48         commonDefinitions = commonStatements;
49         namespaceDefinitions = namespaces;
50         versionSpecificDefinitions = versionSpecificStatements;
51     }
52
53     /**
54      * Returns statement definitions common for all versions.
55      *
56      * @return map of common statement definitions
57      */
58     public ImmutableMap<QName, StatementSupport<?, ?, ?>> getCommonDefinitions() {
59         return commonDefinitions;
60     }
61
62     /**
63      * Returns statement definitions specific for requested version. Result of this method does nit include common
64      * statement definitions.
65      *
66      * @param version
67      *            requested version
68      * @return map of statement definitions specific for requested version, it
69      *         doesn't include common statement definitions.
70      */
71     public ImmutableMap<QName, StatementSupport<?, ?, ?>> getDefinitionsSpecificForVersion(final YangVersion version) {
72         return versionSpecificDefinitions.row(version);
73     }
74
75     /**
76      * Returns all version specific statement definitions. Result of this method does not include common statement
77      * definitions.
78      *
79      * @return table of all version specific statement definitions, it doesn't
80      *         include common statement definitions.
81      */
82     public ImmutableTable<YangVersion, QName, StatementSupport<?, ?, ?>> getAllVersionSpecificDefinitions() {
83         return versionSpecificDefinitions;
84     }
85
86     public ImmutableMap<Class<?>, NamespaceBehaviour<?, ?, ?>> getNamespaceDefinitions() {
87         return namespaceDefinitions;
88     }
89
90     public static Builder builder(final Set<YangVersion> supportedVersions) {
91         return new Builder(supportedVersions, EMPTY);
92     }
93
94     public static Builder derivedFrom(final StatementSupportBundle parent) {
95         return new Builder(parent.getSupportedVersions(), parent);
96     }
97
98     public Set<YangVersion> getSupportedVersions() {
99         return supportedVersions;
100     }
101
102     @Override
103     @SuppressWarnings("unchecked")
104     public <K, V, N extends ParserNamespace<K, V>> NamespaceBehaviour<K, V, N> getNamespaceBehaviour(
105             final Class<N> namespace) {
106         final NamespaceBehaviour<?, ?, ?> potential = namespaceDefinitions.get(namespace);
107         if (potential != null) {
108             checkState(namespace.equals(potential.getIdentifier()));
109
110             // Safe cast, previous checkState checks equivalence of key from which type argument are derived
111             return (NamespaceBehaviour<K, V, N>) potential;
112         }
113         if (parent != null) {
114             return parent.getNamespaceBehaviour(namespace);
115         }
116         return null;
117     }
118
119     public <K, V, N extends ParserNamespace<K, V>> boolean hasNamespaceBehaviour(final Class<N> namespace) {
120         if (namespaceDefinitions.containsKey(namespace)) {
121             return true;
122         }
123         if (parent != null) {
124             return parent.hasNamespaceBehaviour(namespace);
125         }
126         return false;
127     }
128
129     public StatementSupport<?, ?, ?> getStatementDefinition(final YangVersion version, final QName stmtName) {
130         StatementSupport<?, ?, ?> result = getVersionSpecificStatementDefinition(version, stmtName);
131         if (result == null) {
132             result = getCommonStatementDefinition(stmtName);
133         }
134
135         return result;
136     }
137
138     private StatementSupport<?, ?, ?> getCommonStatementDefinition(final QName stmtName) {
139         final StatementSupport<?, ?, ?> potential = commonDefinitions.get(stmtName);
140         if (potential != null) {
141             return potential;
142         }
143         if (parent != null) {
144             return parent.getCommonStatementDefinition(stmtName);
145         }
146         return null;
147     }
148
149     private StatementSupport<?, ?, ?> getVersionSpecificStatementDefinition(final YangVersion version,
150             final QName stmtName) {
151         final StatementSupport<?, ?, ?> potential = versionSpecificDefinitions.get(version, stmtName);
152         if (potential != null) {
153             return potential;
154         }
155
156         if (parent != null) {
157             return parent.getVersionSpecificStatementDefinition(version, stmtName);
158         }
159         return null;
160     }
161
162     public static final class Builder implements Mutable {
163         private static final Logger LOG = LoggerFactory.getLogger(Builder.class);
164
165         private final Map<QName, StatementSupport<?, ?, ?>> commonStatements = new HashMap<>();
166         private final Table<YangVersion, QName, StatementSupport<?, ?, ?>> versionSpecificStatements =
167             HashBasedTable.create();
168         private final Map<Class<?>, NamespaceBehaviour<?, ?, ?>> namespaces = new HashMap<>();
169
170         private final ImmutableSet<YangVersion> supportedVersions;
171         private StatementSupportBundle parent;
172
173         Builder(final Set<YangVersion> supportedVersions, final StatementSupportBundle parent) {
174             this.parent = requireNonNull(parent);
175             this.supportedVersions = ImmutableSet.copyOf(supportedVersions);
176         }
177
178         public @NonNull Builder addSupport(final StatementSupport<?, ?, ?> support) {
179             final QName identifier = support.getStatementName();
180             checkNoParentDefinition(identifier);
181
182             checkState(!commonStatements.containsKey(identifier),
183                     "Statement %s already defined in common statement bundle.", identifier);
184             commonStatements.put(identifier, support);
185             return this;
186         }
187
188         public <K, V, N extends ParserNamespace<K, V>> @NonNull Builder addSupport(
189                 final NamespaceBehaviour<K, V, N> namespaceSupport) {
190             final Class<N> identifier = namespaceSupport.getIdentifier();
191             checkState(!namespaces.containsKey(identifier));
192             checkState(!parent.hasNamespaceBehaviour(identifier));
193             namespaces.put(identifier, namespaceSupport);
194             return this;
195         }
196
197         public @NonNull Builder addVersionSpecificSupport(final YangVersion version,
198                 final StatementSupport<?, ?, ?> definition) {
199             checkArgument(supportedVersions.contains(requireNonNull(version)));
200
201             final QName identifier = definition.getStatementName();
202             checkState(!commonStatements.containsKey(identifier),
203                     "Statement %s already defined in common statement bundle.", identifier);
204             checkState(!versionSpecificStatements.contains(version, identifier),
205                     "Statement %s already defined for version %s.", identifier, version);
206             checkNoParentDefinition(identifier);
207             checkState(parent.getVersionSpecificStatementDefinition(version, identifier) == null,
208                     "Statement %s already defined for version %s in parent's statement bundle.", identifier, version);
209             versionSpecificStatements.put(version, identifier, definition);
210             return this;
211         }
212
213         public Set<YangVersion> getSupportedVersions() {
214             return supportedVersions;
215         }
216
217         public @NonNull Builder setParent(final StatementSupportBundle parent) {
218             this.parent = parent;
219             return this;
220         }
221
222         public @NonNull Builder overrideSupport(final StatementSupport<?, ?, ?> support) {
223             final QName identifier = support.getStatementName();
224             checkNoParentDefinition(identifier);
225
226             final StatementSupport<?, ?, ?> previousSupport = commonStatements.replace(identifier, support);
227             checkState(previousSupport != null, "Statement %s was not previously defined", identifier);
228             LOG.debug("Changed statement {} support from {} to {}", identifier, previousSupport, support);
229             return this;
230         }
231
232         /**
233          * Create a {@link StatementSupportBundle} from the contents of this builder.
234          *
235          * @return A StatementSupportBundle
236          * @throws IllegalStateException if parent has not been set
237          */
238         public @NonNull StatementSupportBundle build() {
239             checkState(parent != null, "Parent must not be null");
240             return new StatementSupportBundle(parent, supportedVersions, ImmutableMap.copyOf(commonStatements),
241                     ImmutableMap.copyOf(namespaces), ImmutableTable.copyOf(versionSpecificStatements));
242         }
243
244         private void checkNoParentDefinition(final QName identifier) {
245             checkState(parent.getCommonStatementDefinition(identifier) == null,
246                     "Statement %s is defined in parent's common statement bundle", identifier);
247         }
248     }
249 }