Added ability to specify root classes.
[javainspect.git] / src / main / java / eu / svjatoslav / inspector / java / commandline / Main.java
1 package eu.svjatoslav.inspector.java.commandline;
2
3 import eu.svjatoslav.inspector.java.structure.ClassGraph;
4
5 import java.io.File;
6 import java.io.FileInputStream;
7 import java.io.IOException;
8 import java.net.MalformedURLException;
9 import java.net.URL;
10 import java.net.URLClassLoader;
11 import java.util.ArrayList;
12 import java.util.List;
13 import java.util.jar.JarEntry;
14 import java.util.jar.JarInputStream;
15
16 /**
17  * This class acts as a commandline interface for JavaInspect.
18  */
19 public class Main {
20     public static void main(String[] args) throws IOException, ClassNotFoundException {
21         CommandlineConfiguration configuration = new CommandlineConfiguration(args);
22         if (!configuration.configurationOk)
23             System.exit(1);
24             
25         getClassGraph(configuration).generateGraph(configuration.graphName.getValue());
26
27         if (configuration.isDebug())
28             System.out.println("Graph ready.");
29     }
30
31     private static ClassGraph getClassGraph(CommandlineConfiguration configuration) throws IOException, ClassNotFoundException {
32         List<File> jarFiles = configuration.jarFiles.getValue();
33
34         URLClassLoader classLoader = new URLClassLoader(
35                 getFileUrls(jarFiles),
36                 configuration.getClass().getClassLoader());
37
38         ClassGraph classGraph = new ClassGraph();
39
40         if (configuration.targetDirectory.isSpecified())
41             classGraph.setTargetDirectory(configuration.targetDirectory.getValue());
42
43         if (configuration.targetImageType.isSpecified())
44             classGraph.setTargetImageType(configuration.targetImageType.getValue());
45
46         classGraph.setKeepDotFile(configuration.keepDotFile.getValue());
47
48         if (configuration.rootClasses.isSpecified()){
49             // add only selected root classes
50             for (String rootClass : configuration.rootClasses.getValue())
51                 attemptClassAdditionByName(classLoader, classGraph, configuration, rootClass);
52
53         } else {
54             // add all classes in the jar files to graph
55             for (File jarFile : jarFiles)
56                 addJarToGraph(jarFile, classLoader, classGraph, configuration);
57         }
58         configuration.blacklistGlob.getValue().forEach(classGraph::blacklistClassGlob);
59         configuration.whitelistGlob.getValue().forEach(classGraph::whitelistClassGlob);
60
61         if (configuration.hideOrphanedClasses.getValue())
62             classGraph.hideOrphanedClasses();
63
64         return classGraph;
65     }
66
67     private static URL[] getFileUrls(List<File> jarFiles) {
68         List<URL> urls = new ArrayList<>();
69         jarFiles.forEach((File file) -> {
70             try {
71                 urls.add(file.toURI().toURL());
72             } catch (MalformedURLException e) {
73                 throw new RuntimeException(e);
74             }
75         });
76
77         return urls.toArray(new URL[urls.size()]);
78     }
79
80     private static void addJarToGraph(
81             File jarFile, URLClassLoader classLoader, ClassGraph classGraph, CommandlineConfiguration configuration)
82             throws IOException, ClassNotFoundException {
83
84         for (String className : getClassNamesFromJar(jarFile))
85             attemptClassAdditionByName(classLoader, classGraph, configuration, className);
86     }
87
88     private static void attemptClassAdditionByName(URLClassLoader classLoader, ClassGraph classGraph, CommandlineConfiguration configuration, String className) throws ClassNotFoundException {
89         if (configuration.isDebug())
90             System.out.println("Adding class to graph: " + className);
91         try {
92             classGraph.add(loadClassByName(classLoader, className));
93         } catch (NoClassDefFoundError e){
94             if (configuration.isDebug())
95                 System.out.println("Class definition was not found.");
96             // Sometimes referenced classes are not found in the same Jar.
97             // Let's ignore this and proceed with the classes that we have.
98         }
99     }
100
101     private static Class loadClassByName(URLClassLoader classLoader, String className) throws ClassNotFoundException {
102         return Class.forName(className, true, classLoader);
103     }
104
105     public static List<String> getClassNamesFromJar(File jarFile) throws IOException {
106         List<String> result = new ArrayList<>();
107         try (
108                 JarInputStream jarInputStream = new JarInputStream(new FileInputStream(jarFile))
109         ) {
110             while (true) {
111                 JarEntry jarEntry = jarInputStream.getNextJarEntry();
112                 if (jarEntry == null)
113                     break;
114
115                 if (isClassFile(jarEntry))
116                     result.add(getClassNameFromFileName(jarEntry));
117             }
118
119             return result;
120         }
121     }
122
123     private static boolean isClassFile(JarEntry jarEntry) {
124         return jarEntry.getName().endsWith(".class");
125     }
126
127     private static String getClassNameFromFileName(JarEntry jarEntry) {
128         String result = jarEntry.getName().replaceAll("/", "\\.");
129         return result.substring(0, result.lastIndexOf('.'));
130     }
131 }