/.idea/
dependency-reduced-pom.xml
/example/target
-/*.iml
\ No newline at end of file
+/*.iml
+/JavaInspect.dot
+/JavaInspect.svg
\ No newline at end of file
#!/bin/bash
#
# Launcher script for JavaInspect utility:
-# http://www2.svjatoslav.eu/gitbrowse/javainspect/doc/
+# https://www3.svjatoslav.eu/projects/javainspect/
#
java -jar /opt/javainspect/javainspect.jar "$@"
<dependency>
<groupId>eu.svjatoslav</groupId>
<artifactId>svjatoslavcommons</artifactId>
- <version>1.7</version>
+ <version>1.8</version>
</dependency>
</dependencies>
public class RenderJavaInspect {
- private static void fullProjectExample() {
- // Create graph
- final ClassGraph graph = new ClassGraph();
-
- // Recursively scan current directory for Java source code and attempt
- // to detect class names from there to be added to the graph.
- graph.addProject(".");
-
- // do not show single classes with no relationships on the graph
- graph.hideOrphanedClasses();
-
- // Produce SVG image titled "JavaInspect full project.png" to the
- // user Desktop directory.
- graph.generateGraph("JavaInspect full project");
- }
-
+ /**
+ * Running this method via IDE will produce 2 files in project root directory:
+ * JavaInspect.svg (JavaInspect utility visualizes itself) and
+ * JavaInspect.dot (GraphViz dot file, for reference).
+ */
private static void handpickClassesExample() {
/*
* This example demonstrates generating of class graph from hand picked
public static void main(final String[] args) {
handpickClassesExample();
-
- fullProjectExample();
-
}
}
package eu.svjatoslav.inspector.java.commandline;
-import eu.svjatoslav.inspector.java.commandline.CommandlineConfiguration;
import eu.svjatoslav.inspector.java.structure.ClassGraph;
import java.io.File;
import java.util.jar.JarEntry;
import java.util.jar.JarInputStream;
-import static java.io.File.separator;
-import static java.lang.System.getProperty;
-
/**
* This class acts as a commandline interface for JavaInspect.
*/
ClassGraph classGraph = new ClassGraph();
- classGraph.setTargetDirectory(getTargetDirectory(configuration));
+ if (configuration.targetDirectory.isSpecified())
+ classGraph.setTargetDirectory(configuration.targetDirectory.getValue());
if (configuration.targetImageType.isSpecified())
classGraph.setTargetImageType(configuration.targetImageType.getValue());
return classGraph;
}
- private static File getTargetDirectory(CommandlineConfiguration configuration) {
- if (configuration.targetDirectory.isSpecified())
- return configuration.targetDirectory.getValue();
-
- // default to current directory
- return new File(getProperty("user.dir") + separator);
- }
-
private static URL[] getFileUrls(List<File> jarFiles) {
List<URL> urls = new ArrayList<>();
jarFiles.forEach((File file) -> {
String2 result = new String2("");
Arrays.stream(TargetImageType.values())
- .forEach(targetImageType -> result.addSuffix(", ", targetImageType.fileExtension));
+ .forEach(targetImageType -> result.appendWithSeparator(", ", targetImageType.fileExtension));
- result.addPrefix("options: ");
+ result.prepend("options: ");
return result.toString();
}
+++ /dev/null
-/*
- * JavaInspect - Utility to visualize java software
- * Copyright (C) 2013-2020, Svjatoslav Agejenko, svjatoslav@svjatoslav.eu
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of version 3 of the GNU Lesser General Public License
- * or later as published by the Free Software Foundation.
- */
-
-package eu.svjatoslav.inspector.java.methods;
-
-import eu.svjatoslav.commons.string.tokenizer.InvalidSyntaxException;
-import eu.svjatoslav.commons.string.tokenizer.Tokenizer;
-import eu.svjatoslav.commons.string.tokenizer.TokenizerMatch;
-
-public class Annotation {
-
- private String name;
-
- public Annotation(final Tokenizer tokenizer) throws InvalidSyntaxException {
-
- name = tokenizer.getNextToken().token;
-
- if (!tokenizer.consumeIfNextToken("("))
- return;
-
- int depth = 1;
-
- while (true) {
- final TokenizerMatch token = tokenizer.getNextToken();
-
- if (token == null)
- return;
-
- if ("(".equals(token.token))
- depth++;
- if (")".equals(token.token))
- depth--;
-
- if (depth == 0)
- return;
- }
-
- }
-
-}
+++ /dev/null
-/*
- * JavaInspect - Utility to visualize java software
- * Copyright (C) 2013-2020, Svjatoslav Agejenko, svjatoslav@svjatoslav.eu
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of version 3 of the GNU Lesser General Public License
- * or later as published by the Free Software Foundation.
- */
-
-package eu.svjatoslav.inspector.java.methods;
-
-import eu.svjatoslav.commons.string.tokenizer.InvalidSyntaxException;
-import eu.svjatoslav.commons.string.tokenizer.Tokenizer;
-
-import java.util.ArrayList;
-import java.util.List;
-
-public class ClassReference {
-
- final List<ClassReference> typeParameters = new ArrayList<>();
- public String name;
-
- public ClassReference(final Tokenizer tokenizer)
- throws InvalidSyntaxException {
- name = tokenizer.getNextToken().token;
-
- if (!tokenizer.consumeIfNextToken("<"))
- return;
-
- while (true) {
- final ClassReference parameterType = new ClassReference(tokenizer);
- typeParameters.add(parameterType);
-
- if (!tokenizer.consumeIfNextToken(","))
- break;
- }
-
- tokenizer.expectAndConsumeNextToken(">");
- }
-
- @Override
- public String toString() {
- final EnumerationBuffer result = new EnumerationBuffer();
-
- result.append(name);
-
- if (typeParameters.size() > 0) {
- result.append("<");
- for (final ClassReference classReference : typeParameters)
- result.appendEnumeration(classReference.toString());
- result.append(">");
- }
-
- return result.toString();
- }
-}
+++ /dev/null
-/*
- * JavaInspect - Utility to visualize java software
- * Copyright (C) 2013-2020, Svjatoslav Agejenko, svjatoslav@svjatoslav.eu
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of version 3 of the GNU Lesser General Public License
- * or later as published by the Free Software Foundation.
- */
-
-package eu.svjatoslav.inspector.java.methods;
-
-import eu.svjatoslav.commons.string.tokenizer.InvalidSyntaxException;
-import eu.svjatoslav.commons.string.tokenizer.Tokenizer;
-import eu.svjatoslav.commons.string.tokenizer.TokenizerMatch;
-
-import java.util.ArrayList;
-import java.util.List;
-
-public class Clazz {
-
- public final List<ClassReference> implementedInterfaces = new ArrayList<>();
- private final String packageName;
- private final String className;
- private final boolean isInterface;
- public ClassReference superClass;
-
- public Clazz(final String packageName, final String className,
- final Tokenizer tokenizer, final boolean isInterface)
- throws InvalidSyntaxException {
-
- this.packageName = packageName;
- this.className = className;
- this.isInterface = isInterface;
-
- while (true) {
- final TokenizerMatch match = tokenizer.getNextToken();
-
- if ("extends".equals(match.token)) {
- superClass = new ClassReference(tokenizer);
- continue;
- }
-
- if ("implements".equals(match.token)) {
- while (true) {
- implementedInterfaces.add(new ClassReference(tokenizer));
-
- if (tokenizer.consumeIfNextToken(","))
- continue;
-
- break;
- }
- continue;
- }
-
- if ("{".equals(match.token)) {
- parseClassBody(tokenizer);
- break;
- }
-
- }
- }
-
- public String getFullName() {
- return packageName + "." + className;
- }
-
- public void parseClassBody(final Tokenizer tokenizer) {
- tokenizer.skipUntilDataEnd();
- }
-
- @Override
- public String toString() {
- final EnumerationBuffer result = new EnumerationBuffer();
-
- result.append(packageName + " -> " + className + " ");
-
- if (isInterface)
- result.append("(interface)");
- else
- result.append("(class)");
- result.append("\n");
-
- if (superClass != null)
- result.append(" super: " + superClass.toString() + "\n");
-
- if (implementedInterfaces.size() > 0) {
- result.append(" implements: ");
- for (final ClassReference classReference : implementedInterfaces)
- result.appendEnumeration(classReference.toString());
- result.append("\n");
- }
-
- return result.toString();
- }
-
-}
+++ /dev/null
-/*
- * JavaInspect - Utility to visualize java software
- * Copyright (C) 2013-2020, Svjatoslav Agejenko, svjatoslav@svjatoslav.eu
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of version 3 of the GNU Lesser General Public License
- * or later as published by the Free Software Foundation.
- */
-
-package eu.svjatoslav.inspector.java.methods;
-
-public class EnumerationBuffer {
-
- private final String enumerationDelimiter;
-
- private final StringBuffer buffer = new StringBuffer();
-
- public int enumeratedEntitiesCount = 0;
-
- public EnumerationBuffer() {
- this(", ");
- }
-
- public EnumerationBuffer(final String enumerationDelimiter) {
- this.enumerationDelimiter = enumerationDelimiter;
- }
-
- public void append(final String value) {
- buffer.append(value);
- }
-
- public void appendEnumeration(final String value) {
- if (enumeratedEntitiesCount > 0)
- buffer.append(enumerationDelimiter);
-
- buffer.append(value);
- enumeratedEntitiesCount++;
- }
-
- public void resetEnumeration() {
- enumeratedEntitiesCount = 0;
- }
-
- @Override
- public String toString() {
- return buffer.toString();
- }
-
-}
+++ /dev/null
-/*
- * JavaInspect - Utility to visualize java software
- * Copyright (C) 2013-2020, Svjatoslav Agejenko, svjatoslav@svjatoslav.eu
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of version 3 of the GNU Lesser General Public License
- * or later as published by the Free Software Foundation.
- */
-
-package eu.svjatoslav.inspector.java.methods;
-
-public class Import {
-
- String path;
- boolean isStatic = false;
-
-}
+++ /dev/null
-/*
- * JavaInspect - Utility to visualize java software
- * Copyright (C) 2013-2020, Svjatoslav Agejenko, svjatoslav@svjatoslav.eu
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of version 3 of the GNU Lesser General Public License
- * or later as published by the Free Software Foundation.
- */
-
-package eu.svjatoslav.inspector.java.methods;
-
-import eu.svjatoslav.commons.string.tokenizer.InvalidSyntaxException;
-import eu.svjatoslav.commons.string.tokenizer.Tokenizer;
-import eu.svjatoslav.commons.string.tokenizer.TokenizerMatch;
-
-import java.io.File;
-import java.io.IOException;
-import java.util.ArrayList;
-import java.util.List;
-
-import static eu.svjatoslav.commons.file.IOHelper.getFileContentsAsString;
-import static eu.svjatoslav.commons.string.tokenizer.Terminator.TerminationStrategy.DROP;
-import static eu.svjatoslav.commons.string.tokenizer.Terminator.TerminationStrategy.PRESERVE;
-
-public class JavaFile {
-
- public static final String UTF_8 = "UTF-8";
- public final List<Clazz> classes = new ArrayList<>();
- String contents;
- private final List<Import> imports = new ArrayList<>();
- private final File file;
- private String packageName;
-
- public JavaFile(final File file) throws IOException, InvalidSyntaxException {
- this.file = file;
- parse();
- }
-
- public void parse() throws IOException, InvalidSyntaxException {
- System.out.println("java file: " + file);
-
- contents = getFileContentsAsString(file);
-
- final Tokenizer tokenizer = new Tokenizer(contents);
-
- // empty space
- tokenizer.addTerminator(" ", DROP);
- tokenizer.addTerminator("\t", DROP);
- tokenizer.addTerminator("\n", DROP);
-
- tokenizer.addTerminator(";", PRESERVE);
- tokenizer.addTerminator("{", PRESERVE);
- tokenizer.addTerminator("}", PRESERVE);
- tokenizer.addTerminator("(", PRESERVE);
- tokenizer.addTerminator(")", PRESERVE);
- tokenizer.addTerminator("[", PRESERVE);
- tokenizer.addTerminator("]", PRESERVE);
- tokenizer.addTerminator("<", PRESERVE);
- tokenizer.addTerminator(">", PRESERVE);
- tokenizer.addTerminator(",", PRESERVE);
- tokenizer.addTerminator("@", PRESERVE);
-
- // comments
- tokenizer.addTerminator("//", "\n", DROP);
- tokenizer.addTerminator("/*", "*/", DROP);
-
- final Modifiers modifiers = new Modifiers();
-
- while (true) {
- final TokenizerMatch match = tokenizer.getNextToken();
- if (match == null)
- break;
-
- if (match.token.equals("package")) {
- parsePackage(tokenizer);
- continue;
- }
-
- if (match.token.equals("import")) {
- parseImport(tokenizer);
- continue;
- }
-
- final boolean wasModifier = modifiers.parseModifier(match.token);
- if (wasModifier)
- continue;
-
- if ("class".equals(match.token)) {
- parseClass(tokenizer);
- continue;
- }
-
- if ("interface".equals(match.token)) {
- parseInterface(tokenizer);
- continue;
- }
-
- if ("@".equals(match.token)) {
- new Annotation(tokenizer);
- continue;
- }
-
- System.out.println(" " + modifiers.toString() + " "
- + match.token);
- modifiers.reset();
- skipUntilSemicolon(tokenizer);
- }
-
- }
-
- private void parseClass(final Tokenizer tokenizer)
- throws InvalidSyntaxException {
-
- final TokenizerMatch match = tokenizer.getNextToken();
- final Clazz clazz = new Clazz(packageName, match.token, tokenizer,
- false);
- // System.out.println(clazz.toString());
- classes.add(clazz);
-
- }
-
- private void parseImport(final Tokenizer tokenizer)
- throws InvalidSyntaxException {
-
- final Import imp = new Import();
-
- final TokenizerMatch match = tokenizer.getNextToken();
-
- if (match.token.equals("static")) {
- imp.isStatic = true;
- imp.path = tokenizer.getNextToken().token;
- } else
- imp.path = match.token;
-
- imports.add(imp);
-
- tokenizer.expectAndConsumeNextToken(";");
- }
-
- private void parseInterface(final Tokenizer tokenizer)
- throws InvalidSyntaxException {
-
- final TokenizerMatch match = tokenizer.getNextToken();
- final Clazz clazz = new Clazz(packageName, match.token, tokenizer, true);
- // System.out.println(clazz.toString());
- classes.add(clazz);
- }
-
- private void parsePackage(final Tokenizer tokenizer)
- throws InvalidSyntaxException {
-
- final TokenizerMatch match = tokenizer.getNextToken();
-
- packageName = match.token;
-
- tokenizer.expectAndConsumeNextToken(";");
- }
-
- public void skipUntilSemicolon(final Tokenizer tokenizer) throws InvalidSyntaxException {
- while (true) {
- final TokenizerMatch token = tokenizer.getNextToken();
-
- if (token == null)
- return;
-
- if (token.token.equals(";"))
- return;
- }
- }
-
-}
+++ /dev/null
-/*
- * JavaInspect - Utility to visualize java software
- * Copyright (C) 2013-2020, Svjatoslav Agejenko, svjatoslav@svjatoslav.eu
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of version 3 of the GNU Lesser General Public License
- * or later as published by the Free Software Foundation.
- */
-
-package eu.svjatoslav.inspector.java.methods;
-
-public class Modifiers {
-
- Access access = Access.DEFAULT;
- boolean isStatic = false;
- boolean isFinal = false;
- boolean isAbstract = false;
-
- public boolean parseModifier(final String string) {
- for (final Access access : Access.values())
- if (access.name.equals(string)) {
- this.access = access;
- return true;
- }
-
- if ("static".equals(string)) {
- isStatic = true;
- return true;
- }
-
- if ("final".equals(string)) {
- isFinal = true;
- return true;
- }
-
- if ("abstract".equals(string)) {
- isAbstract = true;
- return true;
- }
-
- return false;
- }
-
- public void reset() {
- isStatic = false;
- isFinal = false;
- access = Access.DEFAULT;
- }
-
- @Override
- public String toString() {
- final StringBuilder result = new StringBuilder();
-
- result.append(access.name);
-
- if (isStatic) {
- if (result.length() > 0)
- result.append(" ");
- result.append("static");
- }
-
- if (isFinal) {
- if (result.length() > 0)
- result.append(" ");
- result.append("final");
- }
-
- return result.toString();
- }
-
- public enum Access {
- PUBLIC("public"), PROTECTED("protected"), DEFAULT(""), PRIVATE(
- "private");
-
- public final String name;
-
- Access(final String name) {
- this.name = name;
- }
- }
-}
+++ /dev/null
-/*
- * JavaInspect - Utility to visualize java software
- * Copyright (C) 2013-2020, Svjatoslav Agejenko, svjatoslav@svjatoslav.eu
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of version 3 of the GNU Lesser General Public License
- * or later as published by the Free Software Foundation.
- */
-
-package eu.svjatoslav.inspector.java.methods;
-
-import java.util.HashMap;
-import java.util.Map;
-
-public class Package {
-
- Map<String, Clazz> classes = new HashMap<>();
-
-}
+++ /dev/null
-/*
- * JavaInspect - Utility to visualize java software
- * Copyright (C) 2013-2020, Svjatoslav Agejenko, svjatoslav@svjatoslav.eu
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of version 3 of the GNU Lesser General Public License
- * or later as published by the Free Software Foundation.
- */
-
-package eu.svjatoslav.inspector.java.methods;
-
-import java.util.HashMap;
-import java.util.Map;
-
-public class Project {
-
- Map<String, Package> packages = new HashMap<>();
-
-}
+++ /dev/null
-/*
- * JavaInspect - Utility to visualize java software
- * Copyright (C) 2013-2020, Svjatoslav Agejenko, svjatoslav@svjatoslav.eu
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of version 3 of the GNU Lesser General Public License
- * or later as published by the Free Software Foundation.
- */
-
-package eu.svjatoslav.inspector.java.methods;
-
-import eu.svjatoslav.commons.file.FilePathParser;
-import eu.svjatoslav.commons.string.tokenizer.InvalidSyntaxException;
-
-import java.io.File;
-import java.io.IOException;
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-
-public class ProjectScanner {
-
- public final List<JavaFile> javaFiles = new ArrayList<>();
- private final File scanPath;
- Map<File, Project> projects = new HashMap<>();
-
- public ProjectScanner(final File projectPath) {
- scanPath = projectPath;
- parse();
- }
-
- public List<Clazz> getAllClasses() {
- final List<Clazz> result = new ArrayList<>();
-
- for (final JavaFile file : javaFiles)
- result.addAll(file.classes);
-
- return result;
- }
-
- public void parse() {
-
- if (!scanPath.exists())
- System.out.println("Path not found: " + scanPath);
-
- if (!scanPath.canRead())
- System.out.println("Cannot read path: " + scanPath);
-
- if (scanPath.isDirectory())
- parseDirectory(scanPath);
-
- if (scanPath.isFile())
- parseFile(scanPath);
- }
-
- public void parseDirectory(final File file) {
-
- File[] filesList = file.listFiles();
- if (filesList == null) throw new RuntimeException("Cannot scan directory: " + file);
-
- for (final File subFile : filesList) {
-
- if (subFile.isFile())
- parseFile(subFile);
-
- if (subFile.isDirectory())
- parseDirectory(subFile);
- }
- }
-
- public void parseFile(final File file) {
- final String fileExtension = FilePathParser.getFileExtension(file);
- if ("java".equalsIgnoreCase(fileExtension))
- try {
- final JavaFile javaFile = new JavaFile(file);
- javaFiles.add(javaFile);
- } catch (final IOException e) {
- System.out.println("Error parsing file: " + file.toString()
- + ": " + e.toString());
- } catch (final InvalidSyntaxException e) {
- System.out.println("Syntax error occured while parsing file: "
- + file.toString() + ": " + e.toString());
- }
- }
-
-}
+++ /dev/null
-/*
- * JavaInspect - Utility to visualize java software
- * Copyright (C) 2013-2020, Svjatoslav Agejenko, svjatoslav@svjatoslav.eu
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of version 3 of the GNU Lesser General Public License
- * or later as published by the Free Software Foundation.
- */
-
-package eu.svjatoslav.inspector.java.methods;
-
-/**
- * This package contains quickly hacked together Java language parser.
- * Goal is to start visualizing method call references and other things
- * which are not easily readable at runtime via reflection but are available
- * when parsing java source code directly.
- * <p>
- * Work in progress...
- * <p>
- * Currently in is useful just for detecting class names and packages.
- */
-
package eu.svjatoslav.inspector.java.structure;
-import eu.svjatoslav.commons.file.CommonPathResolver;
-import eu.svjatoslav.commons.string.WildCardMatcher;
-import eu.svjatoslav.inspector.java.methods.Clazz;
-import eu.svjatoslav.inspector.java.methods.ProjectScanner;
+import eu.svjatoslav.commons.string.GlobMatcher;
import java.io.File;
import java.io.IOException;
import java.util.List;
import java.util.Map;
-import static eu.svjatoslav.inspector.java.methods.JavaFile.UTF_8;
+import static java.io.File.separator;
+import static java.lang.System.getProperty;
public class ClassGraph {
private final List<String> whitelistClassGlobs = new ArrayList<>();
TargetImageType targetImageType = TargetImageType.SVG;
- private File targetDirectory = CommonPathResolver.getDesktopDirectory();
private boolean keepDotFile;
+ /**
+ * Default to current directory
+ */
+ private File targetDirectory = new File(getProperty("user.dir") + separator);
+
public ClassGraph() {
}
getOrCreateClassDescriptor(object.getClass());
}
- /**
- * @param path path to recursively scan for java source code could be
- * relative to current project or absolute
- */
- public void addProject(final String path) {
- final ProjectScanner projectScanner = new ProjectScanner(new File(path));
- for (final Clazz clazz : projectScanner.getAllClasses())
- try {
- System.out.println("Class full name: " + clazz.getFullName());
- final Class c = Class.forName(clazz.getFullName());
- addObject(c);
- } catch (final Exception exception) {
- System.out.println("cannot add class: "
- + exception.getMessage());
- }
- }
-
public void blacklistClassGlob(final String glob) {
blacklistClassGlobs.add(glob);
}
try {
// write DOT file to disk
- final PrintWriter out = new PrintWriter(dotFile, UTF_8);
+ final PrintWriter out = new PrintWriter(dotFile, "UTF-8");
out.write(getDot());
out.close();
protected boolean isClassShown(final String className) {
for (final String pattern : blacklistClassGlobs)
- if (WildCardMatcher.match(className, pattern))
+ if (GlobMatcher.match(className, pattern))
return false;
if (!whitelistClassGlobs.isEmpty()) {
for (final String pattern : whitelistClassGlobs)
- if (WildCardMatcher.match(className, pattern))
+ if (GlobMatcher.match(className, pattern))
return true;
return false;
}
/.idea/
/target/
+/example.svg
\ No newline at end of file
public class RenderUsingReflection {
+ /**
+ * If you run this method using IDE, then example.svg file shall appear in project root directory.
+ */
public static void main(final String[] args) {
new ClassGraph().add(SampleClass.class, SampleClass2.class)