Possibility to visualize multiple JAR files.
authorSvjatoslav Agejenko <svjatoslav@svjatoslav.eu>
Tue, 26 Dec 2017 02:02:14 +0000 (04:02 +0200)
committerSvjatoslav Agejenko <svjatoslav@svjatoslav.eu>
Tue, 26 Dec 2017 02:02:14 +0000 (04:02 +0200)
Added support for array of arrays.

src/main/java/eu/svjatoslav/inspector/java/CommandlineConfiguration.java
src/main/java/eu/svjatoslav/inspector/java/Main.java
src/main/java/eu/svjatoslav/inspector/java/structure/ClassDescriptor.java
src/main/java/eu/svjatoslav/inspector/java/structure/FieldDescriptor.java

index cea0962..bbb57b2 100644 (file)
@@ -1,14 +1,16 @@
 package eu.svjatoslav.inspector.java;
 
 import eu.svjatoslav.commons.commandline.parameterparser.Parser;
-import eu.svjatoslav.commons.commandline.parameterparser.parameter.FileParameter;
+import eu.svjatoslav.commons.commandline.parameterparser.parameter.FileParameters;
+import eu.svjatoslav.commons.commandline.parameterparser.parameter.NullParameter;
 import eu.svjatoslav.commons.commandline.parameterparser.parameter.StringParameter;
 import eu.svjatoslav.commons.commandline.parameterparser.parameter.StringParameters;
 
 public class CommandlineConfiguration {
 
-    public FileParameter jarFile;
+    public FileParameters jarFiles;
     public StringParameter graphName;
+    private NullParameter showDebug;
 
     public CommandlineConfiguration (String args[]){
         Parser parser = buildCommandlineParameterParser();
@@ -19,11 +21,15 @@ public class CommandlineConfiguration {
 
     }
 
+    public boolean isDebug(){
+        return showDebug.isSpecified();
+    }
+
     public Parser buildCommandlineParameterParser() {
         Parser parser = new Parser();
 
-        jarFile = parser.add(
-                new FileParameter("JAR file"))
+        jarFiles = parser.add(
+                new FileParameters("JAR file(s)"))
                 .mustExist()
                 .addAliases("-j");
 
@@ -32,6 +38,10 @@ public class CommandlineConfiguration {
                 .setMandatory()
                 .addAliases("-n");
 
+        showDebug = parser.add(
+                new NullParameter("show debug info"))
+                .addAliases("-d");
+
         parser.add(
                 new StringParameters("whitelist glob"))
                 .addAliases("-w");
index d651d2e..58963ff 100644 (file)
@@ -3,9 +3,15 @@ package eu.svjatoslav.inspector.java;
 import eu.svjatoslav.inspector.java.structure.ClassGraph;
 
 import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
 import java.net.MalformedURLException;
 import java.net.URL;
 import java.net.URLClassLoader;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.jar.JarEntry;
+import java.util.jar.JarInputStream;
 
 import static java.io.File.separator;
 import static java.lang.System.getProperty;
@@ -14,26 +20,81 @@ import static java.lang.System.getProperty;
  * This class acts as a commandline interface for JavaInspect.
  */
 public class Main {
-       public static void main(String[] args) throws MalformedURLException, ClassNotFoundException {
-        CommandlineConfiguration commandlineConfiguration = new CommandlineConfiguration(args);
-        System.out.println("Commandline config validated");
+    public static void main(String[] args) throws IOException, ClassNotFoundException {
+        CommandlineConfiguration configuration = new CommandlineConfiguration(args);
 
-        File jarFile = commandlineConfiguration.jarFile.getValue();
+        List<File> jarFiles = configuration.jarFiles.getValue();
 
         URLClassLoader classLoader = new URLClassLoader(
-                new URL[]{jarFile.toURL()},
-                commandlineConfiguration.getClass().getClassLoader());
+                getFileUrls(jarFiles),
+                configuration.getClass().getClassLoader());
 
-        Class classToLoad = Class.forName("eu.svjatoslav.sixth.e3d.gui.GuiComponent", true, classLoader);
+        ClassGraph classGraph = new ClassGraph();
+        classGraph.setTargetDirectoryPath(getProperty("user.dir") + separator);
+        classGraph.setKeepDotFile(true);
 
+        for (File jarFile : jarFiles)
+            addJarToGraph(jarFile, classLoader, classGraph, configuration);
 
-               ClassGraph cg = new ClassGraph();
-               cg.setTargetDirectoryPath(getProperty("user.dir") + separator);
+        classGraph.generateGraph(configuration.graphName.getValue());
 
-//             cg.addProject(projectDir);
-//             cg.whitelistClassGlob(packageGlob);
-               cg.setKeepDotFile(true);
-               cg.add(classToLoad);
-               cg.generateGraph(commandlineConfiguration.graphName.getValue());
-       }
+        if (configuration.isDebug())
+            System.out.println("Graph ready.");
+    }
+
+    private static URL[] getFileUrls(List<File> jarFiles) {
+        List<URL> urls = new ArrayList<>();
+        jarFiles.forEach((File file) -> {
+            try {
+                urls.add(file.toURI().toURL());
+            } catch (MalformedURLException e) {
+                throw new RuntimeException(e);
+            }
+        });
+
+        return urls.toArray(new URL[urls.size()]);
+    }
+
+    private static void addJarToGraph(
+            File jarFile, URLClassLoader classLoader, ClassGraph classGraph, CommandlineConfiguration configuration)
+            throws IOException, ClassNotFoundException {
+
+        for (String className : getClassNamesFromJar(jarFile)) {
+            if (configuration.isDebug())
+                System.out.println("Adding class to graph: " + className);
+
+            classGraph.add(loadClassByName(classLoader, className));
+        }
+    }
+
+    private static Class loadClassByName(URLClassLoader classLoader, String className) throws ClassNotFoundException {
+        return Class.forName(className, true, classLoader);
+    }
+
+    public static List<String> getClassNamesFromJar(File jarFile) throws IOException {
+        List<String> result = new ArrayList<>();
+        try (
+                JarInputStream jarInputStream = new JarInputStream(new FileInputStream(jarFile));
+        ) {
+            while (true) {
+                JarEntry jarEntry = jarInputStream.getNextJarEntry();
+                if (jarEntry == null)
+                    break;
+
+                if (isClassFile(jarEntry))
+                    result.add(getClassNameFromFileName(jarEntry));
+            }
+
+            return result;
+        }
+    }
+
+    private static boolean isClassFile(JarEntry jarEntry) {
+        return jarEntry.getName().endsWith(".class");
+    }
+
+    private static String getClassNameFromFileName(JarEntry jarEntry) {
+        String result = jarEntry.getName().replaceAll("/", "\\.");
+        return result.substring(0, result.lastIndexOf('.'));
+    }
 }
index 6a94130..5bfeb25 100755 (executable)
@@ -19,7 +19,7 @@ import java.util.*;
 public class ClassDescriptor implements GraphElement, Comparable<ClassDescriptor> {
 
     private static final int MAX_REFERECNES_COUNT = 10;
-    private final Map<String, FieldDescriptor> nameToFieldMap = new TreeMap<String, FieldDescriptor>();
+    private final Map<String, FieldDescriptor> nameToFieldMap = new TreeMap<>();
     private final SortedSet<MethodDescriptor> methods = new TreeSet<MethodDescriptor>();
     private final ClassGraph classGraph;
     boolean isEnum;
@@ -107,9 +107,7 @@ public class ClassDescriptor implements GraphElement, Comparable<ClassDescriptor
 
         result.append("\n");
         result.append("    // field references to other classes\n");
-        for (final Map.Entry<String, FieldDescriptor> entry : nameToFieldMap
-                .entrySet())
-            result.append(entry.getValue().getDot());
+        nameToFieldMap.forEach((fieldName, field) -> result.append(field.getDot()));
     }
 
     private void enlistFields(final StringBuffer result) {
@@ -326,9 +324,12 @@ public class ClassDescriptor implements GraphElement, Comparable<ClassDescriptor
                 + fullyQualifiedName
                 .replace('.', '_')
                 .replace(";", "")
-                .replace("[L", "")
                 .replace("[[", "")
+                .replace("[L", "")
+                .replace("[[L", "") // array of arrays
+                .replace("[[[L", "") // array of arrays of arrays
                 .replace('$', '_');
+
         return result;
     }
 
index 1213e2d..11959da 100755 (executable)
@@ -21,24 +21,24 @@ import java.util.List;
 
 public class FieldDescriptor implements GraphElement {
 
-    private final ClassDescriptor parentClassDescriptior;
+    private final ClassDescriptor parentClassDescriptor;
     private final List<ClassDescriptor> typeArguments = new ArrayList<ClassDescriptor>();
     private String name;
     private ClassDescriptor type;
     private boolean isInherited;
 
     public FieldDescriptor(final ClassDescriptor parent) {
-        parentClassDescriptior = parent;
+        parentClassDescriptor = parent;
     }
 
     public void analyzeField(final Field field) {
 
         if (!field.getDeclaringClass().getName()
-                .equals(parentClassDescriptior.getFullyQualifiedName()))
+                .equals(parentClassDescriptor.getFullyQualifiedName()))
             isInherited = true;
 
         name = field.getName();
-        type = parentClassDescriptior.getClassGraph().getOrCreateClassDescriptor(
+        type = parentClassDescriptor.getClassGraph().getOrCreateClassDescriptor(
                 field.getType());
         type.registerReference();
 
@@ -48,7 +48,7 @@ public class FieldDescriptor implements GraphElement {
             for (final Type type : fieldParameterizedGenericType.getActualTypeArguments())
                 if (type instanceof Class) {
                     final Class aClass = (Class) type;
-                    final ClassDescriptor genericTypeDescriptor = parentClassDescriptior
+                    final ClassDescriptor genericTypeDescriptor = parentClassDescriptor
                             .getClassGraph().getOrCreateClassDescriptor(aClass);
                     genericTypeDescriptor.registerReference();
                     typeArguments.add(genericTypeDescriptor);
@@ -81,10 +81,10 @@ public class FieldDescriptor implements GraphElement {
         // main type
         boolean showLink = type.areReferencesShown();
 
-        if (type == parentClassDescriptior)
+        if (type == parentClassDescriptor)
             showLink = false;
 
-        if (parentClassDescriptior.isEnum)
+        if (parentClassDescriptor.isEnum)
             showLink = false;
 
         if (showLink)
@@ -104,7 +104,7 @@ public class FieldDescriptor implements GraphElement {
         final StringBuffer result = new StringBuffer();
 
         result.append("        // " + name + "\n");
-        if (parentClassDescriptior.isEnum && (type == parentClassDescriptior)) {
+        if (parentClassDescriptor.isEnum && (type == parentClassDescriptor)) {
             result.append("        <TR><TD colspan=\"2\" PORT=\"" + name);
             result.append("\" ALIGN=\"left\"><FONT POINT-SIZE=\"11.0\">");
             result.append(name + "</FONT></TD></TR>\n");
@@ -126,7 +126,7 @@ public class FieldDescriptor implements GraphElement {
 
     @Override
     public String getGraphId() {
-        return parentClassDescriptior.getGraphId() + ":" + name;
+        return parentClassDescriptor.getGraphId() + ":" + name;
     }
 
     protected int getOutsideVisibleReferencesCount() {