Added 'excludeFiles' option to yang-maven-plugin configuration.
[yangtools.git] / yang / yang-maven-plugin / src / main / java / org / opendaylight / yangtools / yang2sources / plugin / Util.java
1 /*
2  * Copyright (c) 2013 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.yang2sources.plugin;
9
10 import java.io.Closeable;
11 import java.io.File;
12 import java.io.FileInputStream;
13 import java.io.FileNotFoundException;
14 import java.io.FilenameFilter;
15 import java.io.IOException;
16 import java.io.InputStream;
17 import java.util.ArrayList;
18 import java.util.Collection;
19 import java.util.Enumeration;
20 import java.util.List;
21 import java.util.Map;
22 import java.util.Set;
23 import java.util.zip.ZipEntry;
24 import java.util.zip.ZipFile;
25
26 import org.apache.commons.io.FileUtils;
27 import org.apache.maven.artifact.Artifact;
28 import org.apache.maven.plugin.MojoFailureException;
29 import org.apache.maven.plugin.logging.Log;
30 import org.apache.maven.project.MavenProject;
31 import org.opendaylight.yangtools.yang.model.api.Module;
32 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
33
34 import com.google.common.base.Preconditions;
35 import com.google.common.collect.Lists;
36 import com.google.common.collect.Maps;
37
38 final class Util {
39     static final String YANG_SUFFIX = "yang";
40
41     // Cache for listed directories and found yang files. Typically yang files
42     // are utilized twice. First: code is generated during generate-sources
43     // phase Second: yang files are copied as resources during
44     // generate-resources phase. This cache ensures that yang files are listed
45     // only once.
46     private static Map<File, Collection<File>> cache = Maps.newHashMapWithExpectedSize(10);
47
48     /**
49      * List files recursively and return as array of String paths. Use cache of
50      * size 1.
51      */
52     static Collection<File> listFiles(File root) throws FileNotFoundException {
53         if (cache.get(root) != null)
54             return cache.get(root);
55
56         if (!root.exists()) {
57             throw new FileNotFoundException(root.toString());
58         }
59
60         Collection<File> yangFiles = FileUtils.listFiles(root, new String[] { YANG_SUFFIX }, true);
61
62         toCache(root, yangFiles);
63         return yangFiles;
64     }
65
66     static Collection<File> listFiles(File root, File[] excludedFiles, Log log) throws FileNotFoundException {
67         if (!root.exists()) {
68             throw new FileNotFoundException(root.toString());
69         }
70         Collection<File> result = new ArrayList<>();
71         Collection<File> yangFiles = FileUtils.listFiles(root, new String[] { YANG_SUFFIX }, true);
72         for (File f : yangFiles) {
73             boolean excluded = false;
74             for (File ex : excludedFiles) {
75                 if (ex.equals(f)) {
76                     excluded = true;
77                     break;
78                 }
79             }
80             if (excluded) {
81                 if(log != null) {
82                     log.info(Util.message("%s file excluded %s", YangToSourcesProcessor.LOG_PREFIX,
83                             Util.YANG_SUFFIX.toUpperCase(), f));
84                 }
85             } else {
86                 result.add(f);
87             }
88         }
89
90         return result;
91     }
92
93     static List<InputStream> listFilesAsStream(File rootDir, File[] excludedFiles, Log log)
94             throws FileNotFoundException {
95         List<InputStream> is = new ArrayList<InputStream>();
96
97         Collection<File> files = listFiles(rootDir, excludedFiles, log);
98         for (File f : files) {
99             is.add(new NamedFileInputStream(f));
100         }
101
102         return is;
103     }
104
105     static class NamedFileInputStream extends FileInputStream {
106         private final File file;
107
108         NamedFileInputStream(File file) throws FileNotFoundException {
109             super(file);
110             this.file = file;
111         }
112
113         @Override
114         public String toString() {
115             return getClass().getSimpleName() + "{" + file + "}";
116         }
117     }
118
119     private static void toCache(final File rootDir, final Collection<File> yangFiles) {
120         cache.put(rootDir, yangFiles);
121     }
122
123     /**
124      * Instantiate object from fully qualified class name
125      */
126     static <T> T getInstance(String codeGeneratorClass, Class<T> baseType) throws ClassNotFoundException,
127             InstantiationException, IllegalAccessException {
128         return baseType.cast(resolveClass(codeGeneratorClass, baseType).newInstance());
129     }
130
131     private static Class<?> resolveClass(String codeGeneratorClass, Class<?> baseType) throws ClassNotFoundException {
132         Class<?> clazz = Class.forName(codeGeneratorClass);
133
134         if (!isImplemented(baseType, clazz))
135             throw new IllegalArgumentException("Code generator " + clazz + " has to implement " + baseType);
136         return clazz;
137     }
138
139     private static boolean isImplemented(Class<?> expectedIface, Class<?> byClazz) {
140         for (Class<?> iface : byClazz.getInterfaces()) {
141             if (iface.equals(expectedIface))
142                 return true;
143         }
144         return false;
145     }
146
147     static String message(String message, String logPrefix, Object... args) {
148         String innerMessage = String.format(message, args);
149         return String.format("%s %s", logPrefix, innerMessage);
150     }
151
152     static List<File> getClassPath(MavenProject project) {
153         List<File> dependencies = Lists.newArrayList();
154         for (Artifact element : project.getArtifacts()) {
155             File asFile = element.getFile();
156             if (isJar(asFile) || asFile.isDirectory()) {
157                 dependencies.add(asFile);
158             }
159         }
160         return dependencies;
161     }
162
163     private static final String JAR_SUFFIX = ".jar";
164
165     private static boolean isJar(File element) {
166         return (element.isFile() && element.getName().endsWith(JAR_SUFFIX)) ? true : false;
167     }
168
169     static <T> T checkNotNull(T obj, String paramName) {
170         return Preconditions.checkNotNull(obj, "Parameter " + paramName + " is null");
171     }
172
173     final static class YangsInZipsResult implements Closeable {
174         final List<InputStream> yangStreams;
175         private final List<Closeable> zipInputStreams;
176
177         private YangsInZipsResult(List<InputStream> yangStreams, List<Closeable> zipInputStreams) {
178             this.yangStreams = yangStreams;
179             this.zipInputStreams = zipInputStreams;
180         }
181
182         @Override
183         public void close() throws IOException {
184             for (InputStream is : yangStreams) {
185                 is.close();
186             }
187             for (Closeable is : zipInputStreams) {
188                 is.close();
189             }
190         }
191     }
192
193     static YangsInZipsResult findYangFilesInDependenciesAsStream(Log log, MavenProject project)
194             throws MojoFailureException {
195         List<InputStream> yangsFromDependencies = new ArrayList<>();
196         List<Closeable> zips = new ArrayList<>();
197         try {
198             List<File> filesOnCp = Util.getClassPath(project);
199             log.info(Util.message("Searching for yang files in following dependencies: %s",
200                     YangToSourcesProcessor.LOG_PREFIX, filesOnCp));
201
202             for (File file : filesOnCp) {
203                 List<String> foundFilesForReporting = new ArrayList<>();
204                 // is it jar file or directory?
205                 if (file.isDirectory()) {
206                     File yangDir = new File(file, YangToSourcesProcessor.META_INF_YANG_STRING);
207                     if (yangDir.exists() && yangDir.isDirectory()) {
208                         File[] yangFiles = yangDir.listFiles(new FilenameFilter() {
209                             @Override
210                             public boolean accept(File dir, String name) {
211                                 return name.endsWith(".yang") && new File(dir, name).isFile();
212                             }
213                         });
214                         for (File yangFile : yangFiles) {
215                             yangsFromDependencies.add(new NamedFileInputStream(yangFile));
216                         }
217                     }
218
219                 } else {
220                     ZipFile zip = new ZipFile(file);
221                     zips.add(zip);
222
223                     Enumeration<? extends ZipEntry> entries = zip.entries();
224                     while (entries.hasMoreElements()) {
225                         ZipEntry entry = entries.nextElement();
226                         String entryName = entry.getName();
227
228                         if (entryName.startsWith(YangToSourcesProcessor.META_INF_YANG_STRING_JAR)) {
229                             if (entry.isDirectory() == false && entryName.endsWith(".yang")) {
230                                 foundFilesForReporting.add(entryName);
231                                 // This will be closed after all strams are
232                                 // parsed.
233                                 InputStream entryStream = zip.getInputStream(entry);
234                                 yangsFromDependencies.add(entryStream);
235                             }
236                         }
237                     }
238                 }
239                 if (foundFilesForReporting.size() > 0) {
240                     log.info(Util.message("Found %d yang files in %s: %s", YangToSourcesProcessor.LOG_PREFIX,
241                             foundFilesForReporting.size(), file, foundFilesForReporting));
242                 }
243
244             }
245         } catch (Exception e) {
246             throw new MojoFailureException(e.getMessage(), e);
247         }
248         return new YangsInZipsResult(yangsFromDependencies, zips);
249     }
250
251     final static class ContextHolder {
252         private final SchemaContext context;
253         private final Set<Module> yangModules;
254
255         ContextHolder(SchemaContext context, Set<Module> yangModules) {
256             this.context = context;
257             this.yangModules = yangModules;
258         }
259
260         SchemaContext getContext() {
261             return context;
262         }
263
264         Set<Module> getYangModules() {
265             return yangModules;
266         }
267     }
268
269 }