From 4aabbddd2f592ec80977a20cf2ad58cc3ede802b Mon Sep 17 00:00:00 2001 From: Svjatoslav Agejenko Date: Tue, 26 Dec 2017 04:02:14 +0200 Subject: [PATCH] Possibility to visualize multiple JAR files. Added support for array of arrays. --- .../java/CommandlineConfiguration.java | 18 +++- .../eu/svjatoslav/inspector/java/Main.java | 91 ++++++++++++++++--- .../java/structure/ClassDescriptor.java | 11 ++- .../java/structure/FieldDescriptor.java | 18 ++-- 4 files changed, 105 insertions(+), 33 deletions(-) diff --git a/src/main/java/eu/svjatoslav/inspector/java/CommandlineConfiguration.java b/src/main/java/eu/svjatoslav/inspector/java/CommandlineConfiguration.java index cea0962..bbb57b2 100644 --- a/src/main/java/eu/svjatoslav/inspector/java/CommandlineConfiguration.java +++ b/src/main/java/eu/svjatoslav/inspector/java/CommandlineConfiguration.java @@ -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"); diff --git a/src/main/java/eu/svjatoslav/inspector/java/Main.java b/src/main/java/eu/svjatoslav/inspector/java/Main.java index d651d2e..58963ff 100644 --- a/src/main/java/eu/svjatoslav/inspector/java/Main.java +++ b/src/main/java/eu/svjatoslav/inspector/java/Main.java @@ -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 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 jarFiles) { + List 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 getClassNamesFromJar(File jarFile) throws IOException { + List 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('.')); + } } diff --git a/src/main/java/eu/svjatoslav/inspector/java/structure/ClassDescriptor.java b/src/main/java/eu/svjatoslav/inspector/java/structure/ClassDescriptor.java index 6a94130..5bfeb25 100755 --- a/src/main/java/eu/svjatoslav/inspector/java/structure/ClassDescriptor.java +++ b/src/main/java/eu/svjatoslav/inspector/java/structure/ClassDescriptor.java @@ -19,7 +19,7 @@ import java.util.*; public class ClassDescriptor implements GraphElement, Comparable { private static final int MAX_REFERECNES_COUNT = 10; - private final Map nameToFieldMap = new TreeMap(); + private final Map nameToFieldMap = new TreeMap<>(); private final SortedSet methods = new TreeSet(); private final ClassGraph classGraph; boolean isEnum; @@ -107,9 +107,7 @@ public class ClassDescriptor implements GraphElement, Comparable 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 typeArguments = new ArrayList(); 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(" "); result.append(name + "\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() { -- 2.20.1