3e4ff416403947fead54af1a82cb3ddef8f7cae3
[yangtools.git] / yang / yang-parser-impl / src / main / java / org / opendaylight / yangtools / yang / parser / stmt / reactor / CrossSourceStatementReactor.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 com.google.common.base.Preconditions;
11 import com.google.common.collect.ImmutableMap;
12 import com.google.common.io.ByteSource;
13 import java.io.IOException;
14 import java.io.InputStream;
15 import java.util.Arrays;
16 import java.util.Collection;
17 import java.util.EnumMap;
18 import java.util.List;
19 import java.util.Map;
20 import java.util.Optional;
21 import java.util.Set;
22 import javax.annotation.Nonnull;
23 import org.opendaylight.yangtools.yang.common.QName;
24 import org.opendaylight.yangtools.yang.common.QNameModule;
25 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
26 import org.opendaylight.yangtools.yang.model.parser.api.YangSyntaxErrorException;
27 import org.opendaylight.yangtools.yang.model.repo.api.StatementParserMode;
28 import org.opendaylight.yangtools.yang.model.repo.api.YangTextSchemaSource;
29 import org.opendaylight.yangtools.yang.parser.rfc6020.repo.YangStatementStreamSource;
30 import org.opendaylight.yangtools.yang.parser.spi.meta.ModelProcessingPhase;
31 import org.opendaylight.yangtools.yang.parser.spi.meta.ReactorException;
32 import org.opendaylight.yangtools.yang.parser.spi.meta.StatementSupportBundle;
33 import org.opendaylight.yangtools.yang.parser.spi.source.StatementStreamSource;
34 import org.opendaylight.yangtools.yang.parser.spi.validation.ValidationBundlesNamespace.ValidationBundleType;
35 import org.opendaylight.yangtools.yang.parser.stmt.rfc6020.YangStatementSourceImpl;
36
37 public final class CrossSourceStatementReactor {
38     private final Map<ModelProcessingPhase, StatementSupportBundle> supportedTerminology;
39     private final Map<ValidationBundleType, Collection<?>> supportedValidation;
40
41     CrossSourceStatementReactor(final Map<ModelProcessingPhase, StatementSupportBundle> supportedTerminology,
42             final Map<ValidationBundleType, Collection<?>> supportedValidation) {
43         this.supportedTerminology = ImmutableMap.copyOf(supportedTerminology);
44         this.supportedValidation = ImmutableMap.copyOf(supportedValidation);
45     }
46
47     /**
48      * Create a new {@link Builder}.
49      *
50      * @return A new builder.
51      */
52     public static Builder builder() {
53         return new Builder();
54     }
55
56     /**
57      * Start a new reactor build using the default statement parser mode with all features and deviations enabled.
58      *
59      * @return A new {@link BuildAction}.
60      */
61     public BuildAction newBuild() {
62         return newBuild(StatementParserMode.DEFAULT_MODE);
63     }
64
65     /**
66      * Start a new reactor build using the default statement parser mode and enabling only the specified features
67      * and all deviations.
68      *
69      * @param supportedFeatures The set of supported features in the final SchemaContext
70      * @return A new {@link BuildAction}.
71      *
72      * @deprecated Use {@link #newBuild()} and then call setSupportedFeatures() on the created BuildAction instead.
73      */
74     @Deprecated
75     public BuildAction newBuild(final Set<QName> supportedFeatures) {
76         final BuildAction buildAction = newBuild();
77         if (supportedFeatures != null) {
78             buildAction.setSupportedFeatures(supportedFeatures);
79         }
80
81         return buildAction;
82     }
83
84     /**
85      * Start a new reactor build using the default statement parser mode and enabling only the specified features
86      * and all deviations.
87      *
88      * @param supportedFeatures The set of supported features in the final SchemaContext, if present.
89      * @return A new {@link BuildAction}.
90      *
91      * @deprecated Use {@link #newBuild()} and then call setSupportedFeatures() on the created BuildAction instead.
92      */
93     @Deprecated
94     public BuildAction newBuild(final Optional<Set<QName>> supportedFeatures) {
95         final BuildAction buildAction = newBuild();
96         if (supportedFeatures.isPresent()) {
97             buildAction.setSupportedFeatures(supportedFeatures.get());
98         }
99
100         return buildAction;
101     }
102
103     /**
104      * Start a new reactor build using the specified statement parser mode and enabling all features and deviations.
105      *
106      * @param statementParserMode Parser mode to use
107      * @return A new {@link BuildAction}.
108      * @throws NullPointerException if statementParserMode is null
109      */
110     public BuildAction newBuild(final StatementParserMode statementParserMode) {
111         return new BuildAction(statementParserMode);
112     }
113
114     /**
115      * Start a new reactor build using the specified statement parser mode and enabling only the specified features
116      * and all deviations.
117      *
118      * @param statementParserMode Parser mode to use
119      * @param supportedFeatures The set of supported features in the final SchemaContext
120      * @return A new {@link BuildAction}.
121      * @throws NullPointerException if statementParserMode is null
122      *
123      * @deprecated Use {@link #newBuild(StatementParserMode)} and then call setSupportedFeatures() on the created
124      *             BuildAction instead.
125      */
126     @Deprecated
127     public BuildAction newBuild(final StatementParserMode statementParserMode,
128             final Set<QName> supportedFeatures) {
129         final BuildAction buildAction = new BuildAction(statementParserMode);
130         if (supportedFeatures != null) {
131             buildAction.setSupportedFeatures(supportedFeatures);
132         }
133
134         return buildAction;
135     }
136
137     /**
138      * Start a new reactor build using the specified statement parser mode and enabling only the specified features
139      * and all deviations.
140      *
141      * @param statementParserMode Parser mode to use
142      * @param supportedFeatures The set of supported features in the final SchemaContext, or absent if all features
143      *                          encountered should be supported.
144      * @return A new {@link BuildAction}.
145      * @throws NullPointerException if statementParserMode is null
146      *
147      * @deprecated Use {@link #newBuild(StatementParserMode)} and then call setSupportedFeatures() on the created
148      *             BuildAction instead.
149      */
150     @Deprecated
151     public BuildAction newBuild(final StatementParserMode statementParserMode,
152             final Optional<Set<QName>> supportedFeatures) {
153         final BuildAction buildAction = new BuildAction(statementParserMode);
154         if (supportedFeatures.isPresent()) {
155             buildAction.setSupportedFeatures(supportedFeatures.get());
156         }
157
158         return buildAction;
159     }
160
161     public static class Builder implements org.opendaylight.yangtools.concepts.Builder<CrossSourceStatementReactor> {
162         private final Map<ValidationBundleType, Collection<?>> validationBundles =
163                 new EnumMap<>(ValidationBundleType.class);
164         private final Map<ModelProcessingPhase, StatementSupportBundle> bundles =
165                 new EnumMap<>(ModelProcessingPhase.class);
166
167         public Builder setBundle(final ModelProcessingPhase phase, final StatementSupportBundle bundle) {
168             bundles.put(phase, bundle);
169             return this;
170         }
171
172         public Builder setValidationBundle(final ValidationBundleType type, final Collection<?> validationBundle) {
173             validationBundles.put(type, validationBundle);
174             return this;
175         }
176
177         @Override
178         public CrossSourceStatementReactor build() {
179             return new CrossSourceStatementReactor(bundles, validationBundles);
180         }
181     }
182
183     public class BuildAction {
184         private final BuildGlobalContext context;
185         private boolean supportedFeaturesSet = false;
186         private boolean modulesDeviatedByModulesSet = false;
187
188         BuildAction(@Nonnull final StatementParserMode statementParserMode) {
189             this.context = new BuildGlobalContext(supportedTerminology,supportedValidation,
190                     Preconditions.checkNotNull(statementParserMode));
191         }
192
193         /**
194          * Add main source. All main sources are present in resulting SchemaContext.
195          *
196          * @param source
197          *            which should be added into main sources
198          */
199         public void addSource(final StatementStreamSource source) {
200             context.addSource(source);
201         }
202
203         /**
204          * Add main sources. All main sources are present in resulting SchemaContext.
205          *
206          * @param sources
207          *            which should be added into main sources
208          */
209         public void addSources(final StatementStreamSource... sources) {
210             addSources(Arrays.asList(sources));
211         }
212
213         public void addSources(final Collection<? extends StatementStreamSource> sources) {
214             for (final StatementStreamSource source : sources) {
215                 context.addSource(source);
216             }
217         }
218
219         /**
220          * Add library sources. Only library sources required by main sources are present in resulting SchemaContext.
221          * Any other library sources are ignored and this also applies to error reporting.
222          *
223          * <p>
224          * Library sources are not supported in semantic version mode currently.
225          *
226          * @param libSources
227          *            yang sources which should be added into library sources
228          */
229         public void addLibSources(final StatementStreamSource... libSources) {
230             addLibSources(Arrays.asList(libSources));
231         }
232
233         public void addLibSources(final Collection<StatementStreamSource> libSources) {
234             for (final StatementStreamSource libSource : libSources) {
235                 context.addLibSource(libSource);
236             }
237         }
238
239         /**
240          * Set supported features based on which all if-feature statements in the
241          * parsed YANG modules will be resolved.
242          *
243          * @param supportedFeatures
244          *            Set of supported features in the final SchemaContext.
245          *            If the set is empty, no features encountered will be supported.
246          */
247         public void setSupportedFeatures(@Nonnull final Set<QName> supportedFeatures) {
248             Preconditions.checkState(!supportedFeaturesSet, "Supported features should be set only once.");
249             context.setSupportedFeatures(Preconditions.checkNotNull(supportedFeatures));
250             supportedFeaturesSet = true;
251         }
252
253         /**
254          * Set YANG modules which can be deviated by specified modules during the parsing process.
255          * Map key (QNameModule) denotes a module which can be deviated by the modules in the Map value.
256          *
257          * @param modulesDeviatedByModules
258          *            Map of YANG modules (Map key) which can be deviated by specified modules (Map value) in the final
259          *            SchemaContext. If the map is empty, no deviations encountered will be supported.
260          */
261         public void setModulesWithSupportedDeviations(
262                 @Nonnull final Map<QNameModule, Set<QNameModule>> modulesDeviatedByModules) {
263             Preconditions.checkState(!modulesDeviatedByModulesSet,
264                     "Modules with supported deviations should be set only once.");
265             context.setModulesDeviatedByModules(Preconditions.checkNotNull(modulesDeviatedByModules));
266             modulesDeviatedByModulesSet = true;
267         }
268
269         /**
270          * Build the effective model context.
271          */
272         public EffectiveModelContext build() throws ReactorException {
273             return context.build();
274         }
275
276         public EffectiveSchemaContext buildEffective() throws ReactorException {
277             return context.buildEffective();
278         }
279
280         /**
281          * Add specified sources and assemble the resulting SchemaContext.
282          *
283          * @deprecated Use {@link #addSources(Collection)} and {@link #buildEffective()} instead.
284          */
285         @Deprecated
286         public SchemaContext buildEffective(final Collection<ByteSource> yangByteSources) throws ReactorException,
287                 IOException {
288             for (final ByteSource source : yangByteSources) {
289                 if (source instanceof YangTextSchemaSource) {
290                     try {
291                         addSource(YangStatementStreamSource.create((YangTextSchemaSource) source));
292                     } catch (YangSyntaxErrorException e) {
293                         throw new IOException("Source " + source + " failed to parse", e);
294                     }
295                 } else {
296                     addSource(new YangStatementSourceImpl(source.openStream()));
297                 }
298             }
299
300             return buildEffective();
301         }
302
303         /**
304          * Add specified sources and assemble the resulting SchemaContext.
305          *
306          * @deprecated Use {@link #addSources(Collection)} and {@link #buildEffective()} instead.
307          */
308         @Deprecated
309         public SchemaContext buildEffective(final List<InputStream> yangInputStreams) throws ReactorException {
310             for (final InputStream yangInputStream : yangInputStreams) {
311                 addSource(new YangStatementSourceImpl(yangInputStream));
312             }
313
314             return buildEffective();
315         }
316     }
317 }