Add a knob to control warnings about unkeyed lists
[yangtools.git] / tools / yang-model-validator / src / main / java / org / opendaylight / yangtools / yang / validator / SystemTestUtils.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.validator;
9
10 import static com.google.common.base.Preconditions.checkArgument;
11 import static java.util.Objects.requireNonNull;
12
13 import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
14 import java.io.File;
15 import java.io.FileFilter;
16 import java.io.FileNotFoundException;
17 import java.io.IOException;
18 import java.nio.charset.StandardCharsets;
19 import java.nio.file.Files;
20 import java.nio.file.Paths;
21 import java.util.ArrayList;
22 import java.util.Arrays;
23 import java.util.Collection;
24 import java.util.Iterator;
25 import java.util.List;
26 import java.util.Locale;
27 import java.util.ServiceLoader;
28 import java.util.Set;
29 import java.util.regex.Matcher;
30 import java.util.regex.Pattern;
31 import org.eclipse.jdt.annotation.NonNull;
32 import org.opendaylight.yangtools.yang.common.QName;
33 import org.opendaylight.yangtools.yang.common.YangConstants;
34 import org.opendaylight.yangtools.yang.model.api.EffectiveModelContext;
35 import org.opendaylight.yangtools.yang.model.repo.api.YangTextSchemaSource;
36 import org.opendaylight.yangtools.yang.parser.api.YangParser;
37 import org.opendaylight.yangtools.yang.parser.api.YangParserConfiguration;
38 import org.opendaylight.yangtools.yang.parser.api.YangParserException;
39 import org.opendaylight.yangtools.yang.parser.api.YangParserFactory;
40
41 final class SystemTestUtils {
42
43     private static final Pattern MODULE_PATTERN = Pattern.compile("module(.*?)\\{");
44     private static final Pattern WHITESPACES = Pattern.compile("\\s+");
45     private static final @NonNull YangParserFactory PARSER_FACTORY;
46
47     static {
48         final Iterator<@NonNull YangParserFactory> it = ServiceLoader.load(YangParserFactory.class).iterator();
49         if (!it.hasNext()) {
50             throw new IllegalStateException("No YangParserFactory found");
51         }
52         PARSER_FACTORY = it.next();
53     }
54
55     private SystemTestUtils() {
56         // Hidden on purpose
57     }
58
59     static final FileFilter YANG_FILE_FILTER = file -> {
60         final String name = file.getName().toLowerCase(Locale.ENGLISH);
61         return name.endsWith(YangConstants.RFC6020_YANG_FILE_EXTENSION) && file.isFile();
62     };
63
64     static EffectiveModelContext parseYangSources(final List<String> yangLibDirs, final List<String> yangTestFiles,
65             final Set<QName> supportedFeatures, final boolean recursiveSearch,
66             final boolean warnForUnkeyedLists) throws IOException, YangParserException {
67         /*
68          * Current dir "." should be always present implicitly in the list of
69          * directories where dependencies are searched for
70          */
71         if (!yangLibDirs.contains(".")) {
72             yangLibDirs.add(".");
73         }
74
75         final List<File> libFiles = new ArrayList<>();
76         for (final String yangLibDir : yangLibDirs) {
77             libFiles.addAll(getYangFiles(yangLibDir, recursiveSearch));
78         }
79
80         final List<File> testFiles = new ArrayList<>();
81         for (final String yangTestFile : yangTestFiles) {
82             if (!yangTestFile.endsWith(YangConstants.RFC6020_YANG_FILE_EXTENSION)) {
83                 testFiles.add(findInFiles(libFiles, yangTestFile));
84             } else {
85                 testFiles.add(new File(yangTestFile));
86             }
87         }
88
89         return parseYangSources(supportedFeatures, testFiles, libFiles, warnForUnkeyedLists);
90     }
91
92     static EffectiveModelContext parseYangSources(final Set<QName> supportedFeatures, final List<File> testFiles,
93             final List<File> libFiles,  final boolean warnForUnkeyedLists) throws IOException, YangParserException {
94         checkArgument(!testFiles.isEmpty(), "No yang sources");
95
96         final YangParserConfiguration configuration = YangParserConfiguration.builder()
97                 .warnForUnkeyedLists(warnForUnkeyedLists).build();
98         final YangParser parser = PARSER_FACTORY.createParser(configuration);
99         if (supportedFeatures != null) {
100             parser.setSupportedFeatures(supportedFeatures);
101         }
102
103         for (File file : testFiles) {
104             parser.addSource(YangTextSchemaSource.forPath(file.toPath()));
105         }
106         for (File file : libFiles) {
107             parser.addLibSource(YangTextSchemaSource.forPath(file.toPath()));
108         }
109
110         return parser.buildEffectiveModel();
111     }
112
113     private static File findInFiles(final List<File> libFiles, final String yangTestFile) throws IOException {
114         for (final File file : libFiles) {
115             if (WHITESPACES.matcher(getModelNameFromFile(file)).replaceAll("").equals(yangTestFile)) {
116                 return file;
117             }
118         }
119         throw new FileNotFoundException("Model with specific module-name does not exist : " + yangTestFile);
120     }
121
122     private static String getModelNameFromFile(final File file) throws IOException {
123         final String fileAsString = readFile(file.getAbsolutePath());
124         final Matcher matcher = MODULE_PATTERN.matcher(fileAsString);
125         return matcher.find() ? matcher.group(1) : "";
126     }
127
128     private static String readFile(final String path) throws IOException {
129         return new String(Files.readAllBytes(Paths.get(path)), StandardCharsets.UTF_8);
130     }
131
132     @SuppressFBWarnings("NP_NULL_ON_SOME_PATH_FROM_RETURN_VALUE")
133     private static Collection<File> getYangFiles(final String yangSourcesDirectoryPath, final boolean recursiveSearch)
134             throws FileNotFoundException {
135         final File testSourcesDir = new File(yangSourcesDirectoryPath);
136         if (!testSourcesDir.isDirectory()) {
137             throw new FileNotFoundException(String.format("%s no such directory", yangSourcesDirectoryPath));
138         }
139
140         return recursiveSearch ? searchYangFiles(testSourcesDir)
141             : Arrays.asList(testSourcesDir.listFiles(YANG_FILE_FILTER));
142     }
143
144     @SuppressFBWarnings("NP_NULL_ON_SOME_PATH_FROM_RETURN_VALUE")
145     private static List<File> searchYangFiles(final File dir) {
146         requireNonNull(dir);
147         checkArgument(dir.isDirectory(), "File %s is not a directory", dir.getPath());
148
149         final List<File> yangFiles = new ArrayList<>();
150         for (final File file : dir.listFiles()) {
151             if (file.isDirectory()) {
152                 yangFiles.addAll(searchYangFiles(file));
153             } else if (file.isFile()
154                     && file.getName().toLowerCase(Locale.ENGLISH).endsWith(YangConstants.RFC6020_YANG_FILE_EXTENSION)) {
155                 yangFiles.add(file);
156             }
157         }
158
159         return yangFiles;
160     }
161 }