a78dd7279513bc3d3f968404b1da7ac2cd7421ca
[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.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.slf4j.Logger;
27 import org.slf4j.LoggerFactory;
28
29 public final class StatementSupportBundle implements Immutable, NamespaceBehaviour.Registry {
30
31     private static final StatementSupportBundle EMPTY = new StatementSupportBundle(null, null, ImmutableMap.of(),
32             ImmutableMap.of(), ImmutableTable.of());
33
34     private final StatementSupportBundle parent;
35     private final ImmutableMap<QName, StatementSupport<?, ?, ?>> commonDefinitions;
36     private final ImmutableTable<YangVersion, QName, StatementSupport<?, ?, ?>> versionSpecificDefinitions;
37     private final ImmutableMap<Class<?>, NamespaceBehaviour<?, ?, ?>> namespaceDefinitions;
38     private final ImmutableSet<YangVersion> supportedVersions;
39
40     private StatementSupportBundle(final StatementSupportBundle parent,
41             final ImmutableSet<YangVersion> supportedVersions,
42             final ImmutableMap<QName, StatementSupport<?, ?, ?>> commonStatements,
43             final ImmutableMap<Class<?>, NamespaceBehaviour<?, ?, ?>> namespaces,
44             final ImmutableTable<YangVersion, QName, StatementSupport<?, ?, ?>> versionSpecificStatements) {
45         this.parent = parent;
46         this.supportedVersions = supportedVersions;
47         this.commonDefinitions = commonStatements;
48         this.namespaceDefinitions = namespaces;
49         this.versionSpecificDefinitions = versionSpecificStatements;
50     }
51
52     /**
53      * Returns statement definitions common for all versions.
54      *
55      * @return map of common statement definitions
56      */
57     public ImmutableMap<QName, StatementSupport<?, ?, ?>> getCommonDefinitions() {
58         return commonDefinitions;
59     }
60
61     /**
62      * Returns statement definitions specific for requested version. Result of this method does nit include common
63      * statement definitions.
64      *
65      * @param version
66      *            requested version
67      * @return map of statement definitions specific for requested version, it
68      *         doesn't include common statement definitions.
69      */
70     public ImmutableMap<QName, StatementSupport<?, ?, ?>> getDefinitionsSpecificForVersion(final YangVersion version) {
71         return versionSpecificDefinitions.row(version);
72     }
73
74     /**
75      * Returns all version specific statement definitions. Result of this method does not include common statement
76      * definitions.
77      *
78      * @return table of all version specific statement definitions, it doesn't
79      *         include common statement definitions.
80      */
81     public ImmutableTable<YangVersion, QName, StatementSupport<?, ?, ?>> getAllVersionSpecificDefinitions() {
82         return versionSpecificDefinitions;
83     }
84
85     public ImmutableMap<Class<?>, NamespaceBehaviour<?, ?, ?>> getNamespaceDefinitions() {
86         return namespaceDefinitions;
87     }
88
89     public static Builder builder(final Set<YangVersion> supportedVersions) {
90         return new Builder(supportedVersions, EMPTY);
91     }
92
93     public static Builder derivedFrom(final StatementSupportBundle parent) {
94         return new Builder(parent.getSupportedVersions(), parent);
95     }
96
97     public Set<YangVersion> getSupportedVersions() {
98         return supportedVersions;
99     }
100
101     @Override
102     @SuppressWarnings("unchecked")
103     public <K, V, N extends ParserNamespace<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 ParserNamespace<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 ImmutableSet<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 ParserNamespace<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 }