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