Fix code smells in yang-parser-spi
[yangtools.git] / yang / 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.base.Preconditions;
15 import com.google.common.collect.HashBasedTable;
16 import com.google.common.collect.ImmutableMap;
17 import com.google.common.collect.ImmutableSet;
18 import com.google.common.collect.ImmutableTable;
19 import com.google.common.collect.Table;
20 import java.util.HashMap;
21 import java.util.Map;
22 import java.util.Set;
23 import org.opendaylight.yangtools.concepts.Immutable;
24 import org.opendaylight.yangtools.yang.common.QName;
25 import org.opendaylight.yangtools.yang.common.YangVersion;
26 import org.opendaylight.yangtools.yang.model.api.meta.IdentifierNamespace;
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 Set<YangVersion> supportedVersions;
40
41     private StatementSupportBundle(final StatementSupportBundle parent,
42             final Set<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         this.commonDefinitions = commonStatements;
49         this.namespaceDefinitions = namespaces;
50         this.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     public <K, V, N extends IdentifierNamespace<K, V>> NamespaceBehaviour<K, V, N> getNamespaceBehaviour(
104             final Class<N> namespace) {
105         final NamespaceBehaviour<?, ?, ?> potential = namespaceDefinitions.get(namespace);
106         if (potential != null) {
107             checkState(namespace.equals(potential.getIdentifier()));
108
109             // Safe cast, previous checkState checks equivalence of key from which type argument are derived
110             return (NamespaceBehaviour<K, V, N>) potential;
111         }
112         if (parent != null) {
113             return parent.getNamespaceBehaviour(namespace);
114         }
115         return null;
116     }
117
118     public <K, V, N extends IdentifierNamespace<K, V>> boolean hasNamespaceBehaviour(final Class<N> namespace) {
119         if (namespaceDefinitions.containsKey(namespace)) {
120             return true;
121         }
122         if (parent != null) {
123             return parent.hasNamespaceBehaviour(namespace);
124         }
125         return false;
126     }
127
128     public StatementSupport<?, ?, ?> getStatementDefinition(final YangVersion version, final QName stmtName) {
129         StatementSupport<?, ?, ?> result = getVersionSpecificStatementDefinition(version, stmtName);
130         if (result == null) {
131             result = getCommonStatementDefinition(stmtName);
132         }
133
134         return result;
135     }
136
137     private StatementSupport<?, ?, ?> getCommonStatementDefinition(final QName stmtName) {
138         final StatementSupport<?, ?, ?> potential = commonDefinitions.get(stmtName);
139         if (potential != null) {
140             return potential;
141         }
142         if (parent != null) {
143             return parent.getCommonStatementDefinition(stmtName);
144         }
145         return null;
146     }
147
148     private StatementSupport<?, ?, ?> getVersionSpecificStatementDefinition(final YangVersion version,
149             final QName stmtName) {
150         final StatementSupport<?, ?, ?> potential = versionSpecificDefinitions.get(version, stmtName);
151         if (potential != null) {
152             return potential;
153         }
154
155         if (parent != null) {
156             return parent.getVersionSpecificStatementDefinition(version, stmtName);
157         }
158         return null;
159     }
160
161     public static class Builder implements org.opendaylight.yangtools.concepts.Builder<StatementSupportBundle> {
162         private static final Logger LOG = LoggerFactory.getLogger(Builder.class);
163
164         private final Map<QName, StatementSupport<?, ?, ?>> commonStatements = new HashMap<>();
165         private final Table<YangVersion, QName, StatementSupport<?, ?, ?>> versionSpecificStatements = HashBasedTable
166                 .create();
167         private final Map<Class<?>, NamespaceBehaviour<?, ?, ?>> namespaces = new HashMap<>();
168
169         private final Set<YangVersion> supportedVersions;
170         private StatementSupportBundle parent;
171
172         Builder(final Set<YangVersion> supportedVersions, final StatementSupportBundle parent) {
173             this.parent = requireNonNull(parent);
174             this.supportedVersions = ImmutableSet.copyOf(supportedVersions);
175         }
176
177         public Builder addSupport(final StatementSupport<?, ?, ?> support) {
178             final QName identifier = support.getStatementName();
179             checkNoParentDefinition(identifier);
180
181             checkState(!commonStatements.containsKey(identifier),
182                     "Statement %s already defined in common statement bundle.", identifier);
183             commonStatements.put(identifier, support);
184             return this;
185         }
186
187         public <K, V, N extends IdentifierNamespace<K, V>> Builder addSupport(
188                 final NamespaceBehaviour<K, V, N> namespaceSupport) {
189             final Class<N> identifier = namespaceSupport.getIdentifier();
190             checkState(!namespaces.containsKey(identifier));
191             checkState(!parent.hasNamespaceBehaviour(identifier));
192             namespaces.put(identifier, namespaceSupport);
193             return this;
194         }
195
196         public Builder addVersionSpecificSupport(final YangVersion version,
197                 final StatementSupport<?, ?, ?> definition) {
198             checkArgument(supportedVersions.contains(requireNonNull(version)));
199
200             final QName identifier = definition.getStatementName();
201             checkState(!commonStatements.containsKey(identifier),
202                     "Statement %s already defined in common statement bundle.", identifier);
203             checkState(!versionSpecificStatements.contains(version, identifier),
204                     "Statement %s already defined for version %s.", identifier, version);
205             checkNoParentDefinition(identifier);
206             checkState(parent.getVersionSpecificStatementDefinition(version, identifier) == null,
207                     "Statement %s already defined for version %s in parent's statement bundle.", identifier, version);
208             versionSpecificStatements.put(version, identifier, definition);
209             return this;
210         }
211
212         public Set<YangVersion> getSupportedVersions() {
213             return supportedVersions;
214         }
215
216         public Builder setParent(final StatementSupportBundle parent) {
217             this.parent = parent;
218             return this;
219         }
220
221         public Builder overrideSupport(final StatementSupport<?, ?, ?> support) {
222             final QName identifier = support.getStatementName();
223             checkNoParentDefinition(identifier);
224
225             final StatementSupport<?, ?, ?> previousSupport = commonStatements.replace(identifier, support);
226             checkState(previousSupport != null, "Statement %s was not previously defined", identifier);
227             LOG.debug("Changed statement {} support from {} to {}", identifier, previousSupport, support);
228             return this;
229         }
230
231         @Override
232         public StatementSupportBundle build() {
233             Preconditions.checkState(parent != null, "Parent must not be null");
234             return new StatementSupportBundle(parent, supportedVersions, ImmutableMap.copyOf(commonStatements),
235                     ImmutableMap.copyOf(namespaces), ImmutableTable.copyOf(versionSpecificStatements));
236         }
237
238         private void checkNoParentDefinition(final QName identifier) {
239             checkState(parent.getCommonStatementDefinition(identifier) == null,
240                     "Statement %s is defined in parent's common statement bundle", identifier);
241         }
242     }
243 }