Promote SchemaSourceRepresentation
[yangtools.git] / parser / yang-test-util / src / main / java / org / opendaylight / yangtools / yang / test / util / YangParserTestUtils.java
1 /*
2  * Copyright (c) 2016 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.test.util;
9
10 import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
11 import java.io.File;
12 import java.io.FileFilter;
13 import java.io.IOException;
14 import java.net.URI;
15 import java.net.URISyntaxException;
16 import java.util.ArrayList;
17 import java.util.Arrays;
18 import java.util.Collection;
19 import java.util.Iterator;
20 import java.util.List;
21 import java.util.Locale;
22 import java.util.ServiceLoader;
23 import java.util.Set;
24 import java.util.stream.Collectors;
25 import org.eclipse.jdt.annotation.NonNull;
26 import org.opendaylight.yangtools.yang.common.QName;
27 import org.opendaylight.yangtools.yang.common.YangConstants;
28 import org.opendaylight.yangtools.yang.model.api.EffectiveModelContext;
29 import org.opendaylight.yangtools.yang.model.api.source.SourceRepresentation;
30 import org.opendaylight.yangtools.yang.model.api.stmt.FeatureSet;
31 import org.opendaylight.yangtools.yang.model.spi.source.YangTextSource;
32 import org.opendaylight.yangtools.yang.parser.api.YangParser;
33 import org.opendaylight.yangtools.yang.parser.api.YangParserConfiguration;
34 import org.opendaylight.yangtools.yang.parser.api.YangParserException;
35 import org.opendaylight.yangtools.yang.parser.api.YangParserFactory;
36 import org.opendaylight.yangtools.yang.parser.api.YangSyntaxErrorException;
37
38 /**
39  * Utility class which provides convenience methods for producing effective schema context based on the supplied
40  * YANG/YIN sources or paths to these sources.
41  */
42 public final class YangParserTestUtils {
43     private static final FileFilter YANG_FILE_FILTER = file -> {
44         // Locale keeps SpotBugs happy. It should not matter that much anyway.
45         final String name = file.getName().toLowerCase(Locale.ENGLISH);
46         return name.endsWith(YangConstants.RFC6020_YANG_FILE_EXTENSION) && file.isFile();
47     };
48
49     private static final @NonNull YangParserFactory PARSER_FACTORY;
50
51     static {
52         final Iterator<@NonNull YangParserFactory> it = ServiceLoader.load(YangParserFactory.class).iterator();
53         if (!it.hasNext()) {
54             throw new IllegalStateException("No YangParserFactory found");
55         }
56         PARSER_FACTORY = it.next();
57     }
58
59     private YangParserTestUtils() {
60         // Hidden on purpose
61     }
62
63     /**
64      * Creates a new effective schema context containing the specified YANG source. Statement parser mode is set to
65      * default mode and all YANG features are supported.
66      *
67      * @param resource relative path to the YANG file to be parsed
68      *
69      * @return effective schema context
70      */
71     public static EffectiveModelContext parseYangResource(final String resource) {
72         return parseYangResource(resource, YangParserConfiguration.DEFAULT);
73     }
74
75     /**
76      * Creates a new effective schema context containing the specified YANG source. All YANG features are supported.
77      *
78      * @param resource relative path to the YANG file to be parsed
79      * @param config parser configuration
80      * @return effective schema context
81      */
82     public static EffectiveModelContext parseYangResource(final String resource, final YangParserConfiguration config) {
83         return parseYangResource(resource, config, null);
84     }
85
86     /**
87      * Creates a new effective schema context containing the specified YANG source. Statement parser mode is set to
88      * default mode.
89      *
90      * @param resource relative path to the YANG file to be parsed
91      * @param supportedFeatures set of supported features based on which all if-feature statements in the parsed YANG
92      *                          model are resolved
93      * @return effective schema context
94      */
95     public static EffectiveModelContext parseYangResource(final String resource, final Set<QName> supportedFeatures) {
96         return parseYangResource(resource, YangParserConfiguration.DEFAULT, supportedFeatures);
97     }
98
99     /**
100      * Creates a new effective schema context containing the specified YANG source.
101      *
102      * @param resource relative path to the YANG file to be parsed
103      * @param supportedFeatures set of supported features based on which all if-feature statements in the parsed YANG
104      *                          model are resolved
105      * @param config parser configuration
106      * @return effective schema context
107      */
108     public static EffectiveModelContext parseYangResource(final String resource, final YangParserConfiguration config,
109             final Set<QName> supportedFeatures) {
110         return parseYangSources(config, supportedFeatures,
111             YangTextSource.forResource(YangParserTestUtils.class, resource));
112     }
113
114     /**
115      * Creates a new effective schema context containing the specified YANG sources. Statement parser mode is set to
116      * default mode and all YANG features are supported.
117      *
118      * @param files YANG files to be parsed
119      * @return effective schema context
120      */
121     public static EffectiveModelContext parseYangFiles(final File... files) {
122         return parseYangFiles(Arrays.asList(files));
123     }
124
125     /**
126      * Creates a new effective schema context containing the specified YANG sources. Statement parser mode is set to
127      * default mode and all YANG features are supported.
128      *
129      * @param files collection of YANG files to be parsed
130      * @return effective schema context
131      */
132     public static EffectiveModelContext parseYangFiles(final Collection<File> files) {
133         return parseYangFiles(YangParserConfiguration.DEFAULT, files);
134     }
135
136     /**
137      * Creates a new effective schema context containing the specified YANG sources. Statement parser mode is set to
138      * default mode.
139      *
140      * @param supportedFeatures set of supported features based on which all if-feature statements in the parsed YANG
141      *                          models are resolved
142      * @param files YANG files to be parsed
143      * @return effective schema context
144      */
145     public static EffectiveModelContext parseYangFiles(final Set<QName> supportedFeatures, final File... files) {
146         return parseYangFiles(supportedFeatures, Arrays.asList(files));
147     }
148
149     public static EffectiveModelContext parseYangFiles(final Set<QName> supportedFeatures,
150             final Collection<File> files) {
151         return parseYangFiles(supportedFeatures, YangParserConfiguration.DEFAULT, files);
152     }
153
154     /**
155      * Creates a new effective schema context containing the specified YANG sources. All YANG features are supported.
156      *
157      * @param config parser configuration
158      * @param files YANG files to be parsed
159      * @return effective schema context
160      */
161     public static EffectiveModelContext parseYangFiles(final YangParserConfiguration config, final File... files) {
162         return parseYangFiles(config, Arrays.asList(files));
163     }
164
165     /**
166      * Creates a new effective schema context containing the specified YANG sources. All YANG features are supported.
167      *
168      * @param config parser configuration
169      * @param files collection of YANG files to be parsed
170      * @return effective schema context
171      */
172     public static EffectiveModelContext parseYangFiles(final YangParserConfiguration config,
173             final Collection<File> files) {
174         return parseYangFiles(null, config, files);
175     }
176
177     /**
178      * Creates a new effective schema context containing the specified YANG sources.
179      *
180      * @param supportedFeatures set of supported features based on which all if-feature statements in the parsed YANG
181      *                          models are resolved
182      * @param config parser configuration
183      * @param files YANG files to be parsed
184      * @return effective schema context
185      */
186     public static EffectiveModelContext parseYangFiles(final Set<QName> supportedFeatures,
187             final YangParserConfiguration config, final File... files) {
188         return parseYangFiles(supportedFeatures, config, Arrays.asList(files));
189     }
190
191     /**
192      * Creates a new effective schema context containing the specified YANG sources.
193      *
194      * @param supportedFeatures set of supported features based on which all if-feature statements in the parsed YANG
195      *                          models are resolved
196      * @param config parser configuration
197      * @param files YANG files to be parsed
198      * @return effective schema context
199      */
200     //  FIXME: use Java.nio.file.Path
201     public static EffectiveModelContext parseYangFiles(final Set<QName> supportedFeatures,
202             final YangParserConfiguration config, final Collection<File> files) {
203         return parseSources(config, supportedFeatures,
204             files.stream().map(file -> YangTextSource.forPath(file.toPath())).collect(Collectors.toList()));
205     }
206
207     /**
208      * Creates a new effective schema context containing the specified YANG sources. Statement parser mode is set to
209      * default mode and all YANG features are supported.
210      *
211      * @param resourcePath relative path to the directory with YANG files to be parsed
212      * @return effective schema context
213      */
214     public static EffectiveModelContext parseYangResourceDirectory(final String resourcePath) {
215         return parseYangResourceDirectory(resourcePath, YangParserConfiguration.DEFAULT);
216     }
217
218     /**
219      * Creates a new effective schema context containing the specified YANG sources. All YANG features are supported.
220      *
221      * @param resourcePath relative path to the directory with YANG files to be parsed
222      * @param config parser configuration
223      * @return effective schema context
224      */
225     public static EffectiveModelContext parseYangResourceDirectory(final String resourcePath,
226             final YangParserConfiguration config) {
227         return parseYangResourceDirectory(resourcePath, null, config);
228     }
229
230     /**
231      * Creates a new effective schema context containing the specified YANG sources. Statement parser mode is set to
232      * default mode.
233      *
234      * @param resourcePath relative path to the directory with YANG files to be parsed
235      * @param supportedFeatures set of supported features based on which all if-feature statements in the parsed YANG
236      *                          models are resolved
237      * @return effective schema context
238      */
239     public static EffectiveModelContext parseYangResourceDirectory(final String resourcePath,
240             final Set<QName> supportedFeatures) {
241         return parseYangResourceDirectory(resourcePath, supportedFeatures, YangParserConfiguration.DEFAULT);
242     }
243
244     /**
245      * Creates a new effective schema context containing the specified YANG sources.
246      *
247      * @param resourcePath relative path to the directory with YANG files to be parsed
248      * @param supportedFeatures set of supported features based on which all if-feature statements in the parsed YANG
249      *                          models are resolved
250      * @param config parser configuration
251      * @return effective schema context
252      */
253     @SuppressFBWarnings(value = "NP_NULL_ON_SOME_PATH_FROM_RETURN_VALUE", justification = "Wrong inferent on listFiles")
254     public static EffectiveModelContext parseYangResourceDirectory(final String resourcePath,
255             final Set<QName> supportedFeatures, final YangParserConfiguration config) {
256         final URI directoryPath;
257         try {
258             directoryPath = YangParserTestUtils.class.getResource(resourcePath).toURI();
259         } catch (URISyntaxException e) {
260             throw new IllegalArgumentException("Failed to open resource " + resourcePath, e);
261         }
262         return parseYangFiles(supportedFeatures, config, new File(directoryPath).listFiles(YANG_FILE_FILTER));
263     }
264
265     /**
266      * Creates a new effective schema context containing the specified YANG sources. Statement parser mode is set to
267      * default mode and all YANG features are supported.
268      *
269      * @param clazz Resource lookup base
270      * @param resources Resource names to be looked up
271      * @return effective schema context
272      */
273     public static EffectiveModelContext parseYangResources(final Class<?> clazz, final String... resources) {
274         return parseYangResources(clazz, Arrays.asList(resources));
275     }
276
277     public static EffectiveModelContext parseYangResources(final Class<?> clazz, final Collection<String> resources) {
278         final var sources = new ArrayList<YangTextSource>(resources.size());
279         for (final String r : resources) {
280             sources.add(YangTextSource.forResource(clazz, r));
281         }
282         return parseSources(YangParserConfiguration.DEFAULT, null, sources);
283     }
284
285     /**
286      * Creates a new effective schema context containing the specified YANG sources. Statement parser mode is set to
287      * default mode.
288      *
289      * @param yangDirs relative paths to the directories containing YANG files to be parsed
290      * @param yangFiles relative paths to the YANG files to be parsed
291      * @param supportedFeatures set of supported features based on which all if-feature statements in the parsed YANG
292      *                          models are resolved
293      * @return effective schema context
294      */
295     public static EffectiveModelContext parseYangResources(final List<String> yangDirs, final List<String> yangFiles,
296             final Set<QName> supportedFeatures) {
297         return parseYangResources(yangDirs, yangFiles, supportedFeatures, YangParserConfiguration.DEFAULT);
298     }
299
300     /**
301      * Creates a new effective schema context containing the specified YANG sources. All YANG features are supported.
302      *
303      * @param yangResourceDirs relative paths to the directories containing YANG files to be parsed
304      * @param yangResources relative paths to the YANG files to be parsed
305      * @param config parser configuration
306      * @return effective schema context
307      */
308     public static EffectiveModelContext parseYangResources(final List<String> yangResourceDirs,
309             final List<String> yangResources, final YangParserConfiguration config) {
310         return parseYangResources(yangResourceDirs, yangResources, null, config);
311     }
312
313     /**
314      * Creates a new effective schema context containing the specified YANG sources.
315      *
316      * @param yangResourceDirs relative paths to the directories containing YANG files to be parsed
317      * @param yangResources relative paths to the YANG files to be parsed
318      * @param supportedFeatures set of supported features based on which all if-feature statements in the parsed YANG
319      *                          models are resolved
320      * @param config parser configuration
321      * @return effective schema context
322      */
323     public static EffectiveModelContext parseYangResources(final List<String> yangResourceDirs,
324             final List<String> yangResources, final Set<QName> supportedFeatures,
325             final YangParserConfiguration config) {
326         final List<File> allYangFiles = new ArrayList<>();
327         for (final String yangDir : yangResourceDirs) {
328             allYangFiles.addAll(getYangFiles(yangDir));
329         }
330
331         for (final String yangFile : yangResources) {
332             try {
333                 allYangFiles.add(new File(YangParserTestUtils.class.getResource(yangFile).toURI()));
334             } catch (URISyntaxException e) {
335                 throw new IllegalArgumentException("Invalid resource " + yangFile, e);
336             }
337         }
338
339         return parseYangFiles(supportedFeatures, config, allYangFiles);
340     }
341
342     public static EffectiveModelContext parseYangSources(final YangParserConfiguration config,
343             final Set<QName> supportedFeatures, final YangTextSource... sources) {
344         return parseSources(config, supportedFeatures, Arrays.asList(sources));
345     }
346
347     public static EffectiveModelContext parseSources(final YangParserConfiguration config,
348             final Set<QName> supportedFeatures, final Collection<? extends SourceRepresentation> sources) {
349         final YangParser parser = PARSER_FACTORY.createParser(config);
350         if (supportedFeatures != null) {
351             parser.setSupportedFeatures(FeatureSet.of(supportedFeatures));
352         }
353
354         try {
355             parser.addSources(sources);
356         } catch (YangSyntaxErrorException e) {
357             throw new IllegalArgumentException("Malformed source", e);
358         } catch (IOException e) {
359             throw new IllegalArgumentException("Failed to read a source", e);
360         }
361
362         try {
363             return parser.buildEffectiveModel();
364         } catch (YangParserException e) {
365             throw new IllegalStateException("Failed to assemble SchemaContext", e);
366         }
367     }
368
369     /**
370      * Creates a new effective schema context containing the specified YANG sources.
371      *
372      * @param sources list of yang sources in plain string
373      * @return effective schema context
374      */
375     public static EffectiveModelContext parseYang(final String... sources) {
376         return parseSources(YangParserConfiguration.DEFAULT, null,
377             Arrays.stream(sources).map(LiteralYangTextSource::ofLiteral).toList());
378     }
379
380     @SuppressFBWarnings(value = "NP_NULL_ON_SOME_PATH_FROM_RETURN_VALUE", justification = "Wrong inferent on listFiles")
381     private static Collection<File> getYangFiles(final String resourcePath) {
382         final URI directoryPath;
383         try {
384             directoryPath = YangParserTestUtils.class.getResource(resourcePath).toURI();
385         } catch (URISyntaxException e) {
386             throw new IllegalArgumentException("Failed to open resource directory " + resourcePath, e);
387         }
388         return Arrays.asList(new File(directoryPath).listFiles(YANG_FILE_FILTER));
389     }
390 }