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