diff --git a/example.png b/example.png
new file mode 100644
index 0000000..87962ed
Binary files /dev/null and b/example.png differ
diff --git a/pom.xml b/pom.xml
new file mode 100644
index 0000000..02f2ab1
--- /dev/null
+++ b/pom.xml
@@ -0,0 +1,135 @@
+
+ 4.0.0
+
+ nl.yooze
+ yooze
+ jar
+ 0.1-SNAPSHOT
+ yooze
+ www.cjib.nl
+
+
+
+ org.apache.maven.plugins
+ maven-compiler-plugin
+ 2.3.2
+
+ 1.6
+ 1.6
+
+
+
+
+ org.codehaus.mojo
+ build-helper-maven-plugin
+ 1.5
+
+
+ add-source
+ generate-sources
+
+ add-test-source
+
+
+
+ src/testdummies/java
+
+
+
+
+
+
+
+
+
+ org.neo4j
+ neo4j
+ 1.4.M06
+
+
+ commons-io
+ commons-io
+ 1.4
+
+
+ junit
+ junit
+ 4.8.2
+ jar
+ test
+
+
+ org.springframework
+ spring-beans
+ 3.0.5.RELEASE
+ jar
+ compile
+
+
+
+ org.springframework
+ spring-test
+ 3.0.5.RELEASE
+ jar
+ compile
+
+
+ javax.annotation
+ jsr250-api
+ 1.0
+ test
+
+
+ org.springframework
+ spring-context
+ 3.0.5.RELEASE
+ jar
+ compile
+
+
+ javassist
+ javassist
+ 3.12.1.GA
+ jar
+ compile
+
+
+
+ ch.qos.logback
+ logback-core
+ 0.9.29
+ jar
+ compile
+
+
+
+ org.slf4j
+ slf4j-api
+ 1.6.1
+ jar
+ compile
+
+
+
+ ch.qos.logback
+ logback-classic
+ 0.9.29
+ jar
+ compile
+
+
+ org.xeustechnologies
+ jtar
+ 1.0.4
+
+
+
+ org.codehaus.jackson
+ jackson-mapper-asl
+ 1.9.10
+
+
+
+
diff --git a/src/._main b/src/._main
new file mode 100644
index 0000000..3f98c2f
Binary files /dev/null and b/src/._main differ
diff --git a/src/main/java/yooze/ClassByteCountingInputStream.java b/src/main/java/yooze/ClassByteCountingInputStream.java
new file mode 100644
index 0000000..f4ee304
--- /dev/null
+++ b/src/main/java/yooze/ClassByteCountingInputStream.java
@@ -0,0 +1,30 @@
+package yooze;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+public class ClassByteCountingInputStream extends InputStream {
+
+ private InputStream nestedStream;
+ private String className;
+ private long count=0;
+
+ public ClassByteCountingInputStream(String className,
+ InputStream inputStream) {
+ this.className=className;
+ this.nestedStream=inputStream;
+ }
+
+ @Override
+ public int read() throws IOException {
+ count++;
+ return nestedStream.read();
+ }
+
+ @Override
+ public void close() throws IOException {
+ nestedStream.close();
+ Statistics.addBytecodeSizeForClass(className, count);
+ }
+
+}
diff --git a/src/main/java/yooze/ClassCache.java b/src/main/java/yooze/ClassCache.java
new file mode 100644
index 0000000..d26a2dd
--- /dev/null
+++ b/src/main/java/yooze/ClassCache.java
@@ -0,0 +1,28 @@
+package yooze;
+
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
+import yooze.domain.ClassModel;
+
+public class ClassCache {
+ private final static Map entries = new ConcurrentHashMap();
+
+ public static boolean contains(String classname) {
+ return entries.containsKey(classname);
+ }
+
+ public static ClassModel createNewDummyModel(String name) {
+ ClassModel classModel = new ClassModel(name);
+ entries.put(name, classModel);
+ return classModel;
+ }
+
+ public static ClassModel get(String className) {
+ return entries.get(className);
+ }
+
+ public static void add(String className, ClassModel model) {
+ entries.put(className, model);
+ }
+}
diff --git a/src/main/java/yooze/ClassModelBuilder.java b/src/main/java/yooze/ClassModelBuilder.java
new file mode 100644
index 0000000..66f97e7
--- /dev/null
+++ b/src/main/java/yooze/ClassModelBuilder.java
@@ -0,0 +1,177 @@
+package yooze;
+
+import java.util.Set;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import javassist.ClassPool;
+import javassist.CtClass;
+import javassist.CtMethod;
+import javassist.NotFoundException;
+import javassist.bytecode.ConstPool;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import yooze.domain.ClassModel;
+import yooze.domain.MethodCallModel;
+import yooze.domain.MethodModel;
+
+/**
+ * Builds a ClassModel.
+ */
+public class ClassModelBuilder {
+ private static Logger log = LoggerFactory.getLogger(ClassModelBuilder.class);
+
+ private Pattern[] packageIncludePatterns;
+ private Pattern[] packageExcludePatterns;
+ private ClassPool pool;
+
+ public ClassModelBuilder(ClassPool pool) {
+ this.pool = pool;
+ }
+
+ public ClassModel scanClassOrSkip(String className) {
+ if (shouldSkip(className))
+ return null;
+ if (ClassCache.contains(className)) {
+ return ClassCache.get(className);
+ }
+ log.info("scanning {}", className);
+
+ return scan(className);
+
+ }
+
+ private ClassModel scan(String className) {
+ ClassModel model = new ClassModel(className);
+ ClassCache.add(className, model);
+ try {
+ return tryScan(className, model);
+ } catch (Exception e) {
+ log.warn("Loading class,", e);
+ return null;
+ }
+ }
+
+ private ClassModel tryScan(String className, ClassModel model) throws NotFoundException {
+ CtClass ctClass = pool.get(className);
+ if (isScannable(ctClass)) {
+ ConstPool constPool = ctClass.getClassFile().getConstPool();
+
+ addClassReferences(model, constPool);
+ addMethods(model, ctClass);
+ resolveMethodReferences();
+ return model;
+ } else {
+ return null;
+ }
+ }
+
+ private boolean isScannable(CtClass ctClass) {
+ return !ctClass.isFrozen();
+ }
+
+ private void resolveMethodReferences() {
+ for (MethodModel method : MethodCache.getInstance().getMethods()) {
+ for (MethodCallModel methodCall : method.getMethodCalls()) {
+ MethodModel calledMethod = methodCall.getCalledMethod();
+ if (calledMethod != null && calledMethod != method) {
+ calledMethod.addCaller(method);
+ }
+ }
+ }
+ }
+
+ private void addMethods(ClassModel containingClass, CtClass ctClass) {
+ CtMethod[] methods = ctClass.getMethods();
+ for (CtMethod method : methods) {
+ containingClass.addMethod(MethodModel.create(containingClass, method));
+ }
+ }
+
+ private void addClassReferences(ClassModel model, ConstPool constPool) {
+ Set classNames = constPool.getClassNames();
+ for (String classResourcename : classNames) {
+ String refClassName = Util.toClassName(classResourcename);
+ addClassReference(model, refClassName);
+ }
+ }
+
+ private void addClassReference(ClassModel model, String refClassName) {
+ /* recursive invocation */
+ ClassModel scannedClass = scanClassOrSkip(refClassName);
+ if (scannedClass != null && !scannedClass.equals(model)) {
+ model.addReference(scannedClass);
+ }
+ }
+
+ private boolean shouldSkip(String className) {
+ if (!isIncluded(className)) {
+ log.debug("skipping {}", className);
+ return true;
+ }
+ if (isExcluded(className)) {
+ log.debug("skipping {}", className);
+ return true;
+ }
+
+ // if (isInnerClass(className)) {
+ // log.debug("skipping inner class {}", className);
+ // return true;
+ // }
+ //
+ // if (className.equals("java.lang.Object")) {
+ // return true;
+ // }
+ return false;
+ }
+
+ private boolean isInnerClass(String className) {
+ return className.contains("$");
+ }
+
+ private boolean isExcluded(String className) {
+ if (packageExcludePatterns != null) {
+ for (Pattern excludePattern : packageExcludePatterns) {
+ Matcher matcher = excludePattern.matcher(className);
+ if (matcher.matches()) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ private boolean isIncluded(String className) {
+ if (packageIncludePatterns != null) {
+ for (Pattern includePattern : packageIncludePatterns) {
+ Matcher matcher = includePattern.matcher(className);
+ if (matcher.matches()) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ public void setPackageIncludePatterns(String... packageIncludePatterns) {
+ if (packageIncludePatterns != null) {
+ this.packageIncludePatterns = new Pattern[packageIncludePatterns.length];
+ int i = 0;
+ for (String pattern : packageIncludePatterns) {
+ this.packageIncludePatterns[i++] = Pattern.compile(pattern);
+ }
+ }
+ }
+
+ public void setPackageExcludePatterns(String... packageExcludePatterns) {
+ if (packageExcludePatterns != null) {
+ this.packageExcludePatterns = new Pattern[packageExcludePatterns.length];
+ int i = 0;
+ for (String pattern : packageExcludePatterns) {
+ this.packageExcludePatterns[i++] = Pattern.compile(pattern);
+ }
+ }
+ }
+}
diff --git a/src/main/java/yooze/ClassesDirScanner.java b/src/main/java/yooze/ClassesDirScanner.java
new file mode 100644
index 0000000..724cddd
--- /dev/null
+++ b/src/main/java/yooze/ClassesDirScanner.java
@@ -0,0 +1,28 @@
+package yooze;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+
+import javassist.ClassPath;
+import yooze.scanner.Scanner;
+
+/**
+ * reads classes as .class files from a directory
+ */
+public class ClassesDirScanner implements Scanner {
+
+ @Override
+ public List scanArchive(String archiveName) throws IOException {
+ return scanArchive(new File(archiveName));
+ }
+
+ @Override
+ public List scanArchive(File file) throws IOException {
+ List result = new ArrayList();
+ result.add(new DirClassPath(file));
+ return result;
+ }
+
+}
diff --git a/src/main/java/yooze/DirClassPath.java b/src/main/java/yooze/DirClassPath.java
new file mode 100644
index 0000000..bc1c88c
--- /dev/null
+++ b/src/main/java/yooze/DirClassPath.java
@@ -0,0 +1,90 @@
+package yooze;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.List;
+
+import javassist.ClassPath;
+import javassist.NotFoundException;
+
+public class DirClassPath implements ClassPath, Inspectable {
+
+ private final File dir;
+
+ public DirClassPath(File dir) {
+ super();
+ this.dir = dir;
+ }
+
+ public void close() {
+ }
+
+ public URL find(String className) {
+ try {
+ return new URL("file:///" + dir.getCanonicalPath() + "/" + Util.toClassResource(className));
+ } catch (MalformedURLException e) {
+ throw new RuntimeException(e);
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ @Override
+ public String toString() {
+ return "DirectoryClasspath[" + dir + "]";
+ }
+
+ public List getClasses() {
+ List classes = new ArrayList();
+ getClasses(dir, dir, classes);
+ return classes;
+ }
+
+ private void getClasses(File root, File dir, List classes) {
+ File[] fileList = dir.listFiles();
+ for (File entry : fileList) {
+ if (entry.isDirectory()) {
+ // recurse deeper
+ getClasses(root, entry, classes);
+ } else if (isClassFile(entry)) {
+ classes.add(createQualifiedClassNameFromFileLocation(root, entry));
+ }
+ }
+ }
+
+ private boolean isClassFile(File entry) {
+ return entry.isFile() && entry.getName().endsWith(".class");
+ }
+
+ private String createQualifiedClassNameFromFileLocation(File root, File classFile) {
+ String absolutePath = classFile.getAbsolutePath();
+ String relativePath = absolutePath.substring(root.getAbsolutePath().length() + 1);
+ String packageFormat = relativePath.replaceAll("\\\\", ".").replaceAll("/", ".");
+ String substring = packageFormat.substring(0, packageFormat.length() - 6);
+ return substring;
+ }
+
+ @Override
+ public String getResourceName() {
+ return dir.getName();
+ }
+
+ @Override
+ public InputStream openClassfile(String className) throws NotFoundException {
+ File classFile = new File(dir, Util.toClassResource(className) + ".class");
+ if (!classFile.exists()) {
+ return null;
+ }
+ try {
+ return new ClassByteCountingInputStream(className, new FileInputStream(classFile));
+ } catch (FileNotFoundException e) {
+ throw new RuntimeException(e);
+ }
+ }
+}
diff --git a/src/main/java/yooze/DotPrinter.java b/src/main/java/yooze/DotPrinter.java
new file mode 100644
index 0000000..a0f809b
--- /dev/null
+++ b/src/main/java/yooze/DotPrinter.java
@@ -0,0 +1,59 @@
+package yooze;
+
+import java.io.OutputStream;
+import java.io.PrintStream;
+import java.util.ArrayList;
+
+import yooze.domain.ClassModel;
+import yooze.domain.Graph;
+
+/**
+ * prints the graph as a graphviz dot file. Takes care of circular dependencies. Not threadsafe.
+ */
+public class DotPrinter extends PrintStream {
+
+ private ArrayList printedRelations;
+
+ public DotPrinter(OutputStream out) {
+ super(out);
+ }
+
+ public void print(Graph g) {
+ printedRelations = new ArrayList();
+ println("digraph \"" + g.getName() + "\" {");
+ println("graph [size=100,100];");
+ for (ClassModel cm : g.getChildren()) {
+ print(cm);
+ }
+ println("}");
+ close();
+ }
+
+ private void print(ClassModel cm) {
+ double boxsize = Math.sqrt(Statistics.getByteCodeSizeForClass(cm.getName()));
+
+ print("\"");
+ print(cm.getName());
+ println("\" [shape=box, height=" + boxsize / 20 + "];");
+
+ if (cm.getReferences() == null || cm.getReferences().size() == 0) {
+ print("\"");
+ print(cm.getName());
+ println("\";");
+ } else {
+ for (ClassModel ref : cm.getReferences()) {
+ String relation = cm.getName() + "-" + ref.getName();
+ if (!printedRelations.contains(relation)) {
+ print("\"");
+ print(cm.getName());
+ print("\" -> \"");
+ print(ref.getName());
+ println("\"");
+ printedRelations.add(relation);
+ print(ref);
+ }
+ }
+ }
+ }
+
+}
diff --git a/src/main/java/yooze/GraphBuilder.java b/src/main/java/yooze/GraphBuilder.java
new file mode 100644
index 0000000..478d796
--- /dev/null
+++ b/src/main/java/yooze/GraphBuilder.java
@@ -0,0 +1,105 @@
+package yooze;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.List;
+
+import javassist.ClassPath;
+import javassist.ClassPool;
+import yooze.domain.ClassModel;
+import yooze.domain.Graph;
+import yooze.scanner.ArchiveScanner;
+import yooze.scanner.LibScanner;
+import yooze.scanner.Scanner;
+import yooze.scanner.TgzScanner;
+
+/**
+ * Builds a class dependency graph from given classpath. Delegates to ClassModelBuilder.
+ */
+public class GraphBuilder {
+
+ private Scanner scanner;
+ private ClassModelBuilder classModelBuilder;
+ private String[] packageIncludePatterns;
+ private String[] packageExcludePatterns;
+
+ /**
+ * Factory method for getting a builder that does earfiles
+ */
+ public static GraphBuilder getEarBuilder() {
+ return new GraphBuilder(new ArchiveScanner());
+ }
+
+ /**
+ * Factory method for getting a builder that does (downloaded) .tar.gz files
+ */
+ public static GraphBuilder getDefaultTgzBuilder() {
+ GraphBuilder tgzBuilder = new GraphBuilder(new TgzScanner());
+ tgzBuilder.setPackageExcludePatterns("java.*", "sun.*", "com.sun.*");
+ return tgzBuilder;
+ }
+
+ /**
+ * Factory method for getting a builder that scans a lib directory (containing jars)
+ */
+ public static GraphBuilder getLibDirectoryBuilder() {
+ return new GraphBuilder(new LibScanner());
+ }
+
+ /**
+ * Factory method for getting a builder that scans a directory containing classes
+ */
+ public static GraphBuilder getClassesDirectoryBuilder() {
+ return new GraphBuilder(new ClassesDirScanner());
+ }
+
+ private GraphBuilder(Scanner scanner) {
+ super();
+ this.scanner = scanner;
+ }
+
+ public Graph build(String archive) throws IOException {
+ return buildClassDepencyGraph(new File(archive));
+ }
+
+ public Graph buildClassDepencyGraph(File archiveFile) throws IOException {
+ List cpList = scanner.scanArchive(archiveFile);
+
+ ClassPool pool = ClassPool.getDefault();
+ for (ClassPath cp : cpList) {
+ pool.appendClassPath(cp);
+ }
+
+ Graph graph = createClassDependencyGraph(pool, cpList);
+ graph.setName(archiveFile.getName());
+ return graph;
+ }
+
+ private Graph createClassDependencyGraph(ClassPool pool, List classpath) {
+ Graph graph = new Graph();
+ classModelBuilder = new ClassModelBuilder(pool);
+ classModelBuilder.setPackageExcludePatterns(packageExcludePatterns);
+ classModelBuilder.setPackageIncludePatterns(packageIncludePatterns);
+ for (ClassPath lib : classpath) {
+ assert (lib instanceof Inspectable);
+
+ List classes = ((Inspectable) lib).getClasses();
+ for (String className : classes) {
+ ClassModel newModel = classModelBuilder.scanClassOrSkip(className);
+ if (newModel != null) {
+ graph.add(newModel);
+ }
+ }
+ }
+ return graph;
+ }
+
+ public void setPackageIncludePatterns(String... packageIncludePatterns) {
+ this.packageIncludePatterns = packageIncludePatterns;
+ }
+
+ public void setPackageExcludePatterns(String... packageExcludePatterns) {
+ this.packageExcludePatterns = packageExcludePatterns;
+ }
+
+}
diff --git a/src/main/java/yooze/Inspectable.java b/src/main/java/yooze/Inspectable.java
new file mode 100644
index 0000000..07bb261
--- /dev/null
+++ b/src/main/java/yooze/Inspectable.java
@@ -0,0 +1,8 @@
+package yooze;
+
+import java.util.List;
+
+public interface Inspectable {
+ public String getResourceName();
+ public List getClasses();
+}
diff --git a/src/main/java/yooze/JarClassPath.java b/src/main/java/yooze/JarClassPath.java
new file mode 100644
index 0000000..27712d6
--- /dev/null
+++ b/src/main/java/yooze/JarClassPath.java
@@ -0,0 +1,78 @@
+package yooze;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.Enumeration;
+import java.util.List;
+import java.util.jar.JarEntry;
+import java.util.jar.JarFile;
+import java.util.zip.ZipEntry;
+
+import javassist.ClassPath;
+import javassist.NotFoundException;
+
+public class JarClassPath implements ClassPath, Inspectable {
+
+ private final JarFile jar;
+
+ public JarClassPath(JarFile jar) {
+ super();
+ this.jar = jar;
+ }
+
+ public void close() {
+ try {
+ jar.close();
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ public URL find(String className) {
+ try {
+ return new URL("file:///"+jar.getName()+"!"+Util.toClassResource(className));
+ } catch (MalformedURLException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+
+
+ @Override
+ public String toString() {
+ return "JarClasspath[" + jar.getName() + "]";
+ }
+
+ public List getClasses() {
+ List classes = new ArrayList();
+ for (Enumeration entries = jar.entries(); entries
+ .hasMoreElements();) {
+ String name = entries.nextElement().getName().replaceAll("/", ".");
+ if (name.endsWith(".class")) {
+ classes.add(name.substring(0, name.length() - 6));
+ }
+ }
+ return classes;
+ }
+
+
+ public String getResourceName() {
+ return jar.getName();
+ }
+
+ @Override
+ public InputStream openClassfile(String className) throws NotFoundException {
+ ZipEntry entry = jar.getEntry(Util.toClassResource(className));
+ if (entry == null) {
+ return null;
+ }
+ try {
+ return new ClassByteCountingInputStream(className,jar.getInputStream(entry));
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ }
+}
diff --git a/src/main/java/yooze/MethodCache.java b/src/main/java/yooze/MethodCache.java
new file mode 100644
index 0000000..72e464a
--- /dev/null
+++ b/src/main/java/yooze/MethodCache.java
@@ -0,0 +1,32 @@
+package yooze;
+
+import java.util.Collection;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
+import yooze.domain.MethodModel;
+
+public class MethodCache {
+ private final Map entries = new ConcurrentHashMap();
+ private final static MethodCache instance = new MethodCache();
+
+ public static MethodCache getInstance() {
+ return instance;
+ }
+
+ public boolean contains(String classname) {
+ return entries.containsKey(classname);
+ }
+
+ public MethodModel get(String fullName) {
+ return entries.get(fullName);
+ }
+
+ public void add(MethodModel methodmodel) {
+ entries.put(methodmodel.getFullname(), methodmodel);
+ }
+
+ public Collection getMethods() {
+ return entries.values();
+ }
+}
diff --git a/src/main/java/yooze/Statistics.java b/src/main/java/yooze/Statistics.java
new file mode 100644
index 0000000..e938ce2
--- /dev/null
+++ b/src/main/java/yooze/Statistics.java
@@ -0,0 +1,16 @@
+package yooze;
+
+import java.util.concurrent.ConcurrentHashMap;
+
+public class Statistics {
+ private static ConcurrentHashMap bytecodeSizes=new ConcurrentHashMap();
+
+ public static void addBytecodeSizeForClass(String className, long size){
+ bytecodeSizes.put(className, size);
+ }
+
+ public static Long getByteCodeSizeForClass(String className){
+ Long value = bytecodeSizes.get(className);
+ return value==null?0:value;
+ }
+}
diff --git a/src/main/java/yooze/Util.java b/src/main/java/yooze/Util.java
new file mode 100644
index 0000000..27b5274
--- /dev/null
+++ b/src/main/java/yooze/Util.java
@@ -0,0 +1,39 @@
+package yooze;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipFile;
+
+import org.apache.commons.io.IOUtils;
+
+public class Util {
+ private final static Pattern fileNamePattern = Pattern.compile(".+/(.+)");
+
+ public static File extractFile(ZipFile file, ZipEntry entry)
+ throws IOException {
+ String name=entry.getName();
+ Matcher m = fileNamePattern.matcher(name);
+ if (m.matches()){//chop off path
+ name=m.group(1);
+ }
+
+ File tempFile = File.createTempFile(name, ".file");
+ InputStream in = file.getInputStream(entry);
+ FileOutputStream out = new FileOutputStream(tempFile);
+ IOUtils.copy(in, out);
+ return tempFile;
+ }
+
+ public static String toClassResource(String className) {
+ return className.replaceAll("\\.", "/")+".class";
+ }
+
+ public static String toClassName(String classResource) {
+ return classResource.replaceAll("/", ".");
+ }
+}
diff --git a/src/main/java/yooze/domain/ClassModel.java b/src/main/java/yooze/domain/ClassModel.java
new file mode 100644
index 0000000..694a04a
--- /dev/null
+++ b/src/main/java/yooze/domain/ClassModel.java
@@ -0,0 +1,67 @@
+package yooze.domain;
+
+import java.util.ArrayList;
+import java.util.List;
+
+
+/**
+ *
+ */
+public class ClassModel {
+ final private String name;
+
+ final private List references = new ArrayList();
+ final private List methods = new ArrayList();
+
+ public ClassModel(String name) {
+ super();
+ this.name = name;
+ }
+
+ public void addReference(ClassModel classModel) {
+ references.add(classModel);
+ }
+
+ public void addMethod(MethodModel method) {
+ methods.add(method);
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public List getReferences() {
+ return new ArrayList(references);
+ }
+
+ @Override
+ public String toString() {
+ return "class " + name;
+ }
+
+ @Override
+ public int hashCode() {
+ final int prime = 31;
+ int result = 1;
+ result = prime * result + ((name == null) ? 0 : name.hashCode());
+ return result;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj)
+ return true;
+ if (obj == null)
+ return false;
+ if (getClass() != obj.getClass())
+ return false;
+ ClassModel other = (ClassModel) obj;
+ if (name == null) {
+ if (other.name != null)
+ return false;
+ } else if (!name.equals(other.name))
+ return false;
+ return true;
+ }
+
+}
diff --git a/src/main/java/yooze/domain/Graph.java b/src/main/java/yooze/domain/Graph.java
new file mode 100644
index 0000000..01385b1
--- /dev/null
+++ b/src/main/java/yooze/domain/Graph.java
@@ -0,0 +1,28 @@
+package yooze.domain;
+
+import java.util.ArrayList;
+import java.util.List;
+
+
+public class Graph {
+ private String name;
+
+ private final List classes = new ArrayList();
+
+ public void add(ClassModel model) {
+ classes.add(model);
+ }
+
+ public List getChildren() {
+ return classes;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+}
diff --git a/src/main/java/yooze/domain/MethodCallModel.java b/src/main/java/yooze/domain/MethodCallModel.java
new file mode 100644
index 0000000..5c1fccd
--- /dev/null
+++ b/src/main/java/yooze/domain/MethodCallModel.java
@@ -0,0 +1,32 @@
+package yooze.domain;
+
+import javassist.NotFoundException;
+import javassist.expr.MethodCall;
+import yooze.ClassCache;
+import yooze.MethodCache;
+
+public class MethodCallModel {
+ private MethodCall methodCall;
+
+ public MethodCallModel(MethodCall m) {
+ this.methodCall = m;
+ }
+
+ public ClassModel getCalledClass() {
+ return ClassCache.get(methodCall.getClassName());
+ }
+
+ public MethodModel getCalledMethod() {
+ try {
+ ParameterList parameterList = ParameterList.create(methodCall.getMethod());
+ return MethodCache.getInstance().get(
+ createQualifiedMethodname(parameterList));
+ } catch (NotFoundException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ private String createQualifiedMethodname(ParameterList parameterList) {
+ return methodCall.getClassName() + "." + methodCall.getMethodName() + "(" + parameterList.asText() + ")";
+ }
+}
diff --git a/src/main/java/yooze/domain/MethodModel.java b/src/main/java/yooze/domain/MethodModel.java
new file mode 100644
index 0000000..dbd4d9c
--- /dev/null
+++ b/src/main/java/yooze/domain/MethodModel.java
@@ -0,0 +1,91 @@
+package yooze.domain;
+
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+import javassist.CannotCompileException;
+import javassist.CtMethod;
+import javassist.expr.ExprEditor;
+import javassist.expr.MethodCall;
+import yooze.MethodCache;
+
+public class MethodModel {
+ private ClassModel containingClass;
+ private String name;
+ private ParameterList parameterList;
+ private final List methodCalls = new ArrayList();
+ private final Set callers = new HashSet();
+
+ public static MethodModel create(ClassModel containingClass, CtMethod method) {
+ MethodModel methodModel = new MethodModel(containingClass, method);
+ MethodCache.getInstance().add(methodModel);
+ methodModel.scanSubsequentMethodCalls(method);
+ return methodModel;
+ }
+
+ private MethodModel(ClassModel containingClass, CtMethod method) {
+ this.containingClass = containingClass;
+ name = method.getName();
+ parameterList = ParameterList.create(method);
+ }
+
+ public void addCaller(MethodModel method) {
+ callers.add(method);
+ }
+
+ public List getCallers() {
+ return new ArrayList(callers);
+ }
+
+ public List getMethodCalls() {
+ return methodCalls;
+ }
+
+ private void scanSubsequentMethodCalls(CtMethod method) {
+ try {
+ method.instrument(new ExprEditor() {
+ @Override
+ public void edit(MethodCall m) throws CannotCompileException {
+ MethodCallModel methodCallModel = new MethodCallModel(m);
+ methodCalls.add(methodCallModel);
+ }
+ });
+ } catch (CannotCompileException e) {
+ }
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public String getFullname() {
+ return containingClass.getName() + "." + name + "(" + parameterList.asText() + ")";
+ }
+
+ @Override
+ public String toString() {
+ return getFullname();
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (obj == this) {
+ return true;
+ }
+ if (obj == null) {
+ return false;
+ }
+ if (!(obj instanceof MethodModel)) {
+ return false;
+ }
+ MethodModel other = (MethodModel) obj;
+ return this.getFullname().equals(other.getFullname());
+ }
+
+ @Override
+ public int hashCode() {
+ return getFullname().hashCode();
+ }
+}
diff --git a/src/main/java/yooze/domain/ParameterList.java b/src/main/java/yooze/domain/ParameterList.java
new file mode 100644
index 0000000..9c2f39b
--- /dev/null
+++ b/src/main/java/yooze/domain/ParameterList.java
@@ -0,0 +1,58 @@
+package yooze.domain;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import javassist.CtClass;
+import javassist.CtMethod;
+import javassist.NotFoundException;
+
+public class ParameterList {
+
+ private List parameters = new ArrayList();
+
+ public static ParameterList create(CtMethod method) {
+ ParameterList parameterList = new ParameterList();
+ parameterList.parameters = getParameters(method);
+ return parameterList;
+ }
+
+ private static List getParameters(CtMethod method) {
+ try {
+ List parameters = new ArrayList();
+ CtClass[] parameterTypes = method.getParameterTypes();
+ for (CtClass parameterType : parameterTypes) {
+ parameters.add(new ParameterModel(parameterType));
+ }
+ return parameters;
+
+ } catch (NotFoundException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ public String asText() {
+ String parameterText = "";
+ int index = 0;
+ int size = parameters.size();
+ for (ParameterModel parameter : parameters) {
+ parameterText = addType(parameterText, parameter);
+ parameterText = addComma(parameterText, index, size);
+ index++;
+ }
+ return parameterText;
+ }
+
+ private String addComma(String parameterText, int i, int size) {
+ if (i < (size - 1)) {
+ parameterText += ", ";
+ }
+ return parameterText;
+ }
+
+ private String addType(String parameterText, ParameterModel parameter) {
+ parameterText += parameter.getType().getName();
+ return parameterText;
+ }
+
+}
diff --git a/src/main/java/yooze/domain/ParameterModel.java b/src/main/java/yooze/domain/ParameterModel.java
new file mode 100644
index 0000000..34893da
--- /dev/null
+++ b/src/main/java/yooze/domain/ParameterModel.java
@@ -0,0 +1,22 @@
+package yooze.domain;
+
+import yooze.ClassCache;
+import javassist.CtClass;
+
+public class ParameterModel {
+ private ClassModel type;
+ private String name;
+
+ public ParameterModel(CtClass typeAsCtClass) {
+ String classname = typeAsCtClass.getName();
+ type = ClassCache.get(classname);
+ if (type == null) {
+ type = ClassCache.createNewDummyModel(classname);
+ }
+ name = "";// javassist doesn't give me this (?)
+ }
+
+ public ClassModel getType() {
+ return type;
+ }
+}
diff --git a/src/main/java/yooze/dto/MethodDto.java b/src/main/java/yooze/dto/MethodDto.java
new file mode 100644
index 0000000..d047dc7
--- /dev/null
+++ b/src/main/java/yooze/dto/MethodDto.java
@@ -0,0 +1,41 @@
+package yooze.dto;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import yooze.domain.MethodModel;
+
+public class MethodDto {
+ private String name;
+ private List callers = new ArrayList();
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public List getCallers() {
+ return callers;
+ }
+
+ public void setCallers(List callers) {
+ this.callers = callers;
+ }
+
+ public void addCaller(MethodDto caller) {
+ callers.add(caller);
+ }
+
+ public static MethodDto create(MethodModel startMethod) {
+ MethodDto methodDto = new MethodDto();
+ methodDto.setName(startMethod.getFullname());
+ for (MethodModel caller : startMethod.getCallers()) {
+ methodDto.addCaller(create(caller));
+ }
+ return methodDto;
+ }
+
+}
diff --git a/src/main/java/yooze/etc/ClassBuilder.java b/src/main/java/yooze/etc/ClassBuilder.java
new file mode 100644
index 0000000..2751b28
--- /dev/null
+++ b/src/main/java/yooze/etc/ClassBuilder.java
@@ -0,0 +1,48 @@
+package yooze.etc;
+
+import java.util.Iterator;
+import java.util.List;
+import java.util.concurrent.ConcurrentHashMap;
+
+import yooze.Util;
+import yooze.domain.ClassModel;
+
+import javassist.ClassPath;
+import javassist.ClassPool;
+import javassist.CtClass;
+import javassist.NotFoundException;
+import javassist.bytecode.ConstPool;
+
+/**
+ * @deprecated does not seem to be in use
+ *
+ */
+public class ClassBuilder {
+
+ private final ConcurrentHashMap classes = new ConcurrentHashMap();
+
+ private ClassPool pool = ClassPool.getDefault();
+
+ private ClassBuilder(List classpaths) {
+ super();
+ for (ClassPath cpEntry : classpaths) {
+ pool.appendClassPath(cpEntry);
+ }
+ }
+
+ public ClassModel load(String className) throws NotFoundException {
+ ClassModel model;
+ if ((model = classes.get(className)) != null) {
+ return model;
+ }
+
+ CtClass ctClass = pool.getCtClass(className);
+ model = new ClassModel(className);
+ ConstPool constPool = ctClass.getClassFile().getConstPool();
+ for (Iterator> classNames = constPool.getClassNames().iterator(); classNames.hasNext();) {
+ model.addReference(load(Util.toClassName(classNames.next().toString())));
+ }
+ return model;
+
+ }
+}
diff --git a/src/main/java/yooze/etc/Neo4jDao.java b/src/main/java/yooze/etc/Neo4jDao.java
new file mode 100644
index 0000000..ad08c2d
--- /dev/null
+++ b/src/main/java/yooze/etc/Neo4jDao.java
@@ -0,0 +1,89 @@
+package yooze.etc;
+
+import java.util.concurrent.ConcurrentHashMap;
+
+import org.neo4j.graphdb.Direction;
+import org.neo4j.graphdb.GraphDatabaseService;
+import org.neo4j.graphdb.Node;
+import org.neo4j.graphdb.RelationshipType;
+import org.neo4j.graphdb.ReturnableEvaluator;
+import org.neo4j.graphdb.StopEvaluator;
+import org.neo4j.graphdb.Transaction;
+import org.neo4j.graphdb.Traverser;
+import org.neo4j.kernel.EmbeddedGraphDatabase;
+
+import yooze.domain.ClassModel;
+
+public class Neo4jDao {
+ private GraphDatabaseService graphDb;
+ private Node rootNode;
+ private ConcurrentHashMap nodes = new ConcurrentHashMap();
+
+ public enum MyRelationshipTypes implements RelationshipType {
+ CONTAINS, REFERENCES
+ }
+
+ public Neo4jDao(String database) {
+ graphDb = new EmbeddedGraphDatabase(database);
+ Transaction tx = graphDb.beginTx();
+ try {
+ rootNode = graphDb.createNode();
+ rootNode.setProperty("name", "root");
+ tx.success();
+ } finally {
+ tx.finish();
+ }
+ }
+
+ public void insertClass(ClassModel classModel) {
+ Transaction tx = graphDb.beginTx();
+ try {
+ Node newNode = findOrCreateNode(classModel);
+
+ for (ClassModel ref : classModel.getReferences()) {
+ Node refNode = findOrCreateNode(ref);
+ newNode.createRelationshipTo(refNode,
+ MyRelationshipTypes.REFERENCES);
+ }
+ tx.success();
+ } finally {
+ tx.finish();
+ }
+ }
+
+ private Node findOrCreateNode(ClassModel classModel) {
+ Node newNode = findNode(classModel.getName());
+ if (newNode == null) {
+ Transaction tx = graphDb.beginTx();
+ try {
+ newNode = graphDb.createNode();
+ newNode.setProperty("name", classModel.getName());
+
+ tx.success();
+ } finally {
+ tx.finish();
+ }
+ }
+ return newNode;
+ }
+
+ public Node findNode(String className) {
+ Node node = nodes.get(className);
+ if (node != null) {
+ return node;
+ }
+ // do "tablescan". is there a better way?
+ Traverser classesTraverser = rootNode.traverse(
+ Traverser.Order.BREADTH_FIRST, StopEvaluator.DEPTH_ONE,
+ ReturnableEvaluator.ALL_BUT_START_NODE,
+ MyRelationshipTypes.CONTAINS, Direction.OUTGOING);
+ for (Node nextNode : classesTraverser) {
+ if (nextNode.getProperty("name").equals(className)) {
+ nodes.put(className, nextNode);
+ return nextNode;
+ }
+ }
+ return null;// not found
+
+ }
+}
diff --git a/src/main/java/yooze/etc/Yooze.java b/src/main/java/yooze/etc/Yooze.java
new file mode 100644
index 0000000..30a4660
--- /dev/null
+++ b/src/main/java/yooze/etc/Yooze.java
@@ -0,0 +1,41 @@
+package yooze.etc;
+
+import java.io.IOException;
+
+import yooze.GraphBuilder;
+import yooze.domain.ClassModel;
+import yooze.domain.Graph;
+
+public class Yooze {
+ private String neo4jDb;
+
+ public static void main(String[] args) throws IOException {
+ String neoDb=args[0];
+ String earfile = args[1];
+ String in = args[2];
+ String ex = args[2];
+ new Yooze(neoDb).createNeoGraph(earfile, in,ex);
+
+ }
+
+ public Yooze(String neo4jDb) {
+ super();
+ this.neo4jDb = neo4jDb;
+ }
+
+ public void createNeoGraph(String archive, String packageIncludePatterns, String packageExcludePatterns)
+ throws IOException{
+ GraphBuilder libDirectoryBuilder = GraphBuilder.getLibDirectoryBuilder();
+ libDirectoryBuilder.setPackageExcludePatterns(packageExcludePatterns);
+ libDirectoryBuilder.setPackageIncludePatterns(packageIncludePatterns);
+
+ Graph graph = libDirectoryBuilder.build(archive);
+
+ Neo4jDao neo4jDao = new Neo4jDao(neo4jDb);
+ for (ClassModel model:graph.getChildren()){
+ neo4jDao.insertClass(model);
+ }
+ }
+
+
+}
diff --git a/src/main/java/yooze/scanner/ArchiveScanner.java b/src/main/java/yooze/scanner/ArchiveScanner.java
new file mode 100644
index 0000000..6f14aa4
--- /dev/null
+++ b/src/main/java/yooze/scanner/ArchiveScanner.java
@@ -0,0 +1,192 @@
+package yooze.scanner;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Enumeration;
+import java.util.List;
+import java.util.jar.JarEntry;
+import java.util.jar.JarFile;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+import java.util.zip.ZipEntry;
+
+import javassist.ClassPath;
+
+import org.apache.commons.io.IOUtils;
+
+import yooze.DirClassPath;
+import yooze.JarClassPath;
+import yooze.Util;
+
+/**
+ * @author shautvast TODO separate models for wars
+ */
+public class ArchiveScanner implements Scanner {
+ private final static Pattern classPattern = Pattern.compile("WEB-INF/classes/(.*)\\.class");
+ private final static Pattern packagePattern = Pattern.compile("(.+\\/+)(.+\\.class)");
+
+ public List scanArchive(String archiveName) throws IOException {
+ return scanArchive(new File(archiveName));
+ }
+
+ public List scanArchive(File archiveFile) throws IOException {
+ List result = new ArrayList();
+ if (!archiveFile.exists()) {
+ return result;
+ }
+
+ if (archiveFile.isDirectory()) {
+ File[] list = archiveFile.listFiles();
+ for (File entry : list) {
+ if (entry.getName().endsWith(".jar")) {
+ result.add(new JarClassPath(new JarFile(entry)));
+ } else if (entry.getName().endsWith(".class")) {
+ result.add(new DirClassPath(archiveFile));
+ break;
+ }
+ }
+
+ return result;
+ }
+
+ JarFile archive = new JarFile(archiveFile);
+ if (archive.getName().endsWith(".ear")) {
+ result.addAll(scanRoot(archive));
+ result.addAll(scanWars(getWars(archive)));
+
+ } else if (archive.getName().endsWith(".war")) {
+ result.addAll(scanWars(Arrays.asList(new JarFile[] { archive })));
+ } else {
+ // treat as jar file
+ result.add(new JarClassPath(archive));
+ }
+ return result;
+ }
+
+ private List scanWars(List wars) {
+ List classpaths = new ArrayList();
+ for (JarFile war : wars) {
+ classpaths.addAll(scanWar(war));
+ }
+ return classpaths;
+ }
+
+ private List scanWar(JarFile warfile) {
+ List classpaths = new ArrayList();
+ File classesDir = createTempLocation(classpaths);
+ classpaths.add(new DirClassPath(classesDir));
+ for (Enumeration entries = warfile.entries(); entries.hasMoreElements();) {
+ JarEntry entry = (JarEntry) entries.nextElement();
+ if (isArchive(entry)) {
+ try {
+ addToClasspath(warfile, classpaths, entry);
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ } else {
+ extractClass(warfile, entry, classesDir);
+ }
+ }
+ return classpaths;
+ }
+
+ private File createTempLocation(List classpaths) {
+ File classesDir = new File(new File(System.getProperty("java.io.tmpdir")), "classes"
+ + System.currentTimeMillis());
+ boolean dirsMade = classesDir.mkdirs();
+ if (!dirsMade) {
+ throw new RuntimeException("Directory " + classesDir + " could not be created");
+ }
+ return classesDir;
+ }
+
+ private void addToClasspath(JarFile warfile, List classpaths, JarEntry entry) throws IOException {
+ File jarFile = Util.extractFile(warfile, entry);
+ classpaths.add(new JarClassPath(new JarFile(jarFile)));
+ }
+
+ private boolean isArchive(JarEntry entry) {
+ String name = entry.getName();
+ return name.startsWith("WEB-INF/lib") && name.endsWith(".jar");
+ }
+
+ private File extractClass(JarFile warfile, ZipEntry entry, File classesDir) {
+ Matcher matcher = classPattern.matcher(entry.getName());
+ if (matcher.matches()) {
+ String className = matcher.group(1) + ".class";
+ Matcher matcher2 = packagePattern.matcher(className);
+ if (matcher2.matches()) {
+ String packageName = matcher2.group(1);
+ File classDir = createUnarchivedPackageDicectory(classesDir, packageName);
+ String simpleClassName = matcher2.group(2);
+ File classFile = new File(classDir, simpleClassName);
+ return createUnarchivedClassfile(warfile, entry, classFile);
+ }
+ }
+ return null;
+ }
+
+ private File createUnarchivedClassfile(JarFile warfile, ZipEntry entry, File classFile) {
+ try {
+ FileOutputStream out = new FileOutputStream(classFile);
+ IOUtils.copy(warfile.getInputStream(entry), out);
+ return classFile;
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ private File createUnarchivedPackageDicectory(File classesDir, String packageName) {
+ File classDir = new File(classesDir, packageName);
+ if (!classDir.exists()) {
+ boolean packageDirsMade = classDir.mkdirs();
+ if (!packageDirsMade) {
+ throw new RuntimeException("Directory " + classDir + " could not be created");
+ }
+ }
+ return classDir;
+ }
+
+ private List getWars(JarFile earfile) {
+ List wars = new ArrayList();
+ for (Enumeration entries = earfile.entries(); entries.hasMoreElements();) {
+ JarEntry entry = (JarEntry) entries.nextElement();
+ if (isWarfile(entry)) {
+ try {
+ File warFile = Util.extractFile(earfile, entry);
+ wars.add(new JarFile(warFile));
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ }
+ }
+ return wars;
+ }
+
+ private boolean isWarfile(JarEntry entry) {
+ return entry.getName().endsWith(".war");
+ }
+
+ private List scanRoot(JarFile earfile) {
+ List classpaths = new ArrayList();
+ for (Enumeration entries = earfile.entries(); entries.hasMoreElements();) {
+ JarEntry entry = (JarEntry) entries.nextElement();
+ if (isJarfile(entry)) {
+ try {
+ addToClasspath(earfile, classpaths, entry);
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ }
+ }
+ return classpaths;
+ }
+
+ private boolean isJarfile(JarEntry entry) {
+ return entry.getName().endsWith(".jar");
+ }
+
+}
diff --git a/src/main/java/yooze/scanner/LibScanner.java b/src/main/java/yooze/scanner/LibScanner.java
new file mode 100644
index 0000000..bd0747d
--- /dev/null
+++ b/src/main/java/yooze/scanner/LibScanner.java
@@ -0,0 +1,47 @@
+package yooze.scanner;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.jar.JarFile;
+
+import yooze.JarClassPath;
+
+import javassist.ClassPath;
+
+/**
+ * Scans a directory ("lib") recursively for jarfiles and adds them to the
+ * classpath
+ *
+ * @author sander
+ *
+ */
+public class LibScanner implements Scanner {
+
+ @Override
+ public List scanArchive(String archiveName) throws IOException {
+ return scanArchive(new File(archiveName));
+ }
+
+ @Override
+ public List scanArchive(File file) throws IOException {
+ List classpaths = new ArrayList();
+ return doScanArchive(classpaths,file);
+ }
+
+ private List doScanArchive(List classpaths, File file)
+ throws IOException {
+
+ File[] entries = file.listFiles();
+ for (File entry : entries) {
+ if (entry.isDirectory()) {
+ doScanArchive(classpaths, entry);
+ } else if (entry.getName().endsWith(".jar")) {
+ classpaths.add(new JarClassPath(new JarFile(entry)));
+ }
+ }
+ return classpaths;
+ }
+
+}
diff --git a/src/main/java/yooze/scanner/Scanner.java b/src/main/java/yooze/scanner/Scanner.java
new file mode 100644
index 0000000..d7883ae
--- /dev/null
+++ b/src/main/java/yooze/scanner/Scanner.java
@@ -0,0 +1,13 @@
+package yooze.scanner;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.List;
+
+import javassist.ClassPath;
+
+public interface Scanner {
+ public List scanArchive(String archiveName) throws IOException;
+
+ public List scanArchive(File file) throws IOException;
+}
diff --git a/src/main/java/yooze/scanner/TgzScanner.java b/src/main/java/yooze/scanner/TgzScanner.java
new file mode 100644
index 0000000..bb9302d
--- /dev/null
+++ b/src/main/java/yooze/scanner/TgzScanner.java
@@ -0,0 +1,65 @@
+package yooze.scanner;
+
+import java.io.BufferedInputStream;
+import java.io.BufferedOutputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.jar.JarFile;
+import java.util.zip.GZIPInputStream;
+
+import javassist.ClassPath;
+
+import org.xeustechnologies.jtar.TarEntry;
+import org.xeustechnologies.jtar.TarInputStream;
+
+import yooze.JarClassPath;
+
+/**
+ * @author sander
+ *
+ */
+public class TgzScanner implements Scanner {
+ public List scanArchive(String archiveName) throws IOException {
+ return scanArchive(new File(archiveName));
+ }
+
+ public List scanArchive(File file) throws IOException {
+ List classpaths = new ArrayList();
+ TarInputStream tarInputStream = new TarInputStream(new GZIPInputStream(
+ new BufferedInputStream(new FileInputStream(file))));
+ TarEntry entry;
+ while ((entry = tarInputStream.getNextEntry()) != null) {
+ if (entry.getName().endsWith(".jar")) {
+ int count;
+ byte data[] = new byte[2048];
+
+ File tempFile = File.createTempFile(
+ singleName(entry.getName()), ".jar");
+ FileOutputStream fos = new FileOutputStream(tempFile);
+ BufferedOutputStream dest = new BufferedOutputStream(fos);
+
+ while ((count = tarInputStream.read(data)) != -1) {
+ dest.write(data, 0, count);
+ }
+ dest.flush();
+ dest.close();
+ classpaths.add(new JarClassPath(new JarFile(tempFile)));
+ }
+ }
+ tarInputStream.close();
+ return classpaths;
+ }
+
+ private String singleName(String name) {
+ int slash = name.lastIndexOf("/");
+ if (slash > -1) {
+ return name.substring(slash + 1);
+ } else {
+ return name;
+ }
+ }
+}
diff --git a/src/test/java/yooze/AssTest.java b/src/test/java/yooze/AssTest.java
new file mode 100644
index 0000000..965b95b
--- /dev/null
+++ b/src/test/java/yooze/AssTest.java
@@ -0,0 +1,22 @@
+package yooze;
+
+import java.util.Iterator;
+
+import javassist.ClassPool;
+import javassist.CtClass;
+import javassist.NotFoundException;
+import javassist.bytecode.ConstPool;
+
+public class AssTest {
+ public static void main(String[] args) throws NotFoundException {
+ ClassPool pool = ClassPool.getDefault();
+ CtClass c1 = pool.get("yooze.Class1");
+ ConstPool constPool = c1.getClassFile().getConstPool();
+
+
+ for (Iterator iterator = constPool.getClassNames().iterator();iterator.hasNext();){
+ System.out.println(iterator.next());
+ }
+
+ }
+}
diff --git a/src/test/java/yooze/Config.java b/src/test/java/yooze/Config.java
new file mode 100644
index 0000000..e2f3952
--- /dev/null
+++ b/src/test/java/yooze/Config.java
@@ -0,0 +1,26 @@
+package yooze;
+
+import java.io.File;
+
+public class Config {
+ private File earFile;
+ private File tgzFile;
+
+ public void setEarFile(File earFile) {
+ this.earFile = earFile;
+ }
+
+ public File getEarFile() {
+ return earFile;
+ }
+
+ public File getTgzFile() {
+ return tgzFile;
+ }
+
+ public void setTgzFile(File tgzFile) {
+ this.tgzFile = tgzFile;
+ }
+
+
+}
diff --git a/src/test/java/yooze/DotPrinterTest.java b/src/test/java/yooze/DotPrinterTest.java
new file mode 100644
index 0000000..2f84733
--- /dev/null
+++ b/src/test/java/yooze/DotPrinterTest.java
@@ -0,0 +1,64 @@
+package yooze;
+
+import static org.junit.Assert.assertEquals;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.springframework.test.context.ContextConfiguration;
+import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
+
+import yooze.domain.Graph;
+
+@RunWith(SpringJUnit4ClassRunner.class)
+@ContextConfiguration(locations = "classpath:applicationContext-test.xml")
+public class DotPrinterTest {
+
+ @Test
+ public void dotPrinting() throws IOException {
+ GraphBuilder directoryBuilder = GraphBuilder.getClassesDirectoryBuilder();
+ directoryBuilder.setPackageExcludePatterns(".*?Class4");
+ directoryBuilder.setPackageIncludePatterns(".*?.Class.");
+ Graph graph = directoryBuilder.build("target/test-classes");
+
+ ByteArrayOutputStream bytes = new ByteArrayOutputStream(500);
+ DotPrinter d = new DotPrinter(bytes);
+ d.print(graph);
+ String dotText = new String(bytes.toByteArray());
+ d.close();
+ String expectedDotText = "digraph \"test-classes\" {\r\n" //
+ + "graph [size=100,100];\r\n"
+ + "\"yooze.Class1\" [shape=box, height=0.0];\r\n" //
+ + "\"yooze.Class1\" -> \"yooze.Class2\"\r\n"
+ + "\"yooze.Class2\" [shape=box, height=0.0];\r\n"
+ + "\"yooze.Class2\" -> \"yooze.Class3\"\r\n"
+ + "\"yooze.Class3\" [shape=box, height=0.0];\r\n"
+ + "\"yooze.Class3\" -> \"yooze.Class1\"\r\n"
+ + "\"yooze.Class1\" [shape=box, height=0.0];\r\n"
+ + "\"yooze.Class2\" [shape=box, height=0.0];\r\n" //
+ + "\"yooze.Class3\" [shape=box, height=0.0];\r\n" + "}\r\n";
+
+ assertEquals(expectedDotText, dotText);
+ }
+
+ @Test
+ public void noReference() throws IOException {
+ GraphBuilder directoryBuilder = GraphBuilder.getClassesDirectoryBuilder();
+ directoryBuilder.setPackageExcludePatterns("");
+ directoryBuilder.setPackageIncludePatterns("yooze.Class4");
+ Graph graph = directoryBuilder.build("target/test-classes");
+ ByteArrayOutputStream bytes = new ByteArrayOutputStream(1000);
+ DotPrinter d = new DotPrinter(bytes);
+ d.print(graph);
+ String dotText = new String(bytes.toByteArray());
+ String expectedDotText = "digraph \"test-classes\" {\r\n" //
+ + "graph [size=100,100];\r\n"//
+ + "\"yooze.Class4\" [shape=box, height=0.0];\r\n" //
+ + "\"yooze.Class4\";\r\n" //
+ + "}\r\n";
+ d.close();
+ assertEquals(expectedDotText, dotText);
+ }
+}
diff --git a/src/test/java/yooze/EarScannerTest.java b/src/test/java/yooze/EarScannerTest.java
new file mode 100644
index 0000000..5371a74
--- /dev/null
+++ b/src/test/java/yooze/EarScannerTest.java
@@ -0,0 +1,50 @@
+package yooze;
+
+import static org.hamcrest.core.Is.is;
+import static org.junit.Assert.assertThat;
+import static org.junit.Assert.assertTrue;
+
+import java.io.IOException;
+import java.util.List;
+
+import javassist.ClassPath;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.test.context.ContextConfiguration;
+import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
+
+import yooze.scanner.ArchiveScanner;
+
+@RunWith(SpringJUnit4ClassRunner.class)
+@ContextConfiguration(locations = "classpath:applicationContext-test.xml")
+public class EarScannerTest {
+
+ @Autowired
+ private Config config;
+
+ @Test
+ public void scanner() throws IOException {
+ List classpaths = new ArchiveScanner().scanArchive(config
+ .getEarFile());
+ for (ClassPath path : classpaths) {
+ if (path instanceof DirClassPath) {
+ List classes = ((Inspectable) path).getClasses();
+ assertThat(classes.size(), is(49));
+ }
+ if (path instanceof JarClassPath) {
+ String name = ((Inspectable) path).getResourceName();
+ if (name.contains("standard")) {
+ List classes = ((Inspectable) path).getClasses();
+ assertTrue(classes.contains("org.apache.taglibs.standard.tag.common.sql.DataSourceUtil"));
+ }
+ }
+ }
+ }
+
+ public void setConfig(Config config) {
+ this.config = config;
+ }
+
+}
diff --git a/src/test/java/yooze/GraphTest.java b/src/test/java/yooze/GraphTest.java
new file mode 100644
index 0000000..8c7058c
--- /dev/null
+++ b/src/test/java/yooze/GraphTest.java
@@ -0,0 +1,40 @@
+package yooze;
+
+import java.io.IOException;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.springframework.test.context.ContextConfiguration;
+import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
+
+import yooze.domain.ClassModel;
+import yooze.domain.Graph;
+import static org.junit.Assert.assertTrue;
+
+@RunWith(SpringJUnit4ClassRunner.class)
+@ContextConfiguration(locations = "classpath:applicationContext-test.xml")
+public class GraphTest {
+
+ @Test
+ public void buildGraph() throws IOException{
+// new Yooze("/tmp/test").createNeoGraph("target/test-classes", ".*?.Class.");
+ GraphBuilder libDirectoryBuilder = GraphBuilder.getLibDirectoryBuilder();
+ libDirectoryBuilder.setPackageIncludePatterns(".*?.Class.");
+ libDirectoryBuilder.setPackageExcludePatterns("");
+ Graph graph = libDirectoryBuilder.build("target/test-classes");
+ ClassModel class1=graph.getChildren().get(0);
+
+ assertTrue(class1!=null);
+ ClassModel class2Dummy=new ClassModel("yooze.Class2");
+ assertTrue(class1.getReferences().contains(class2Dummy));
+
+ ClassModel class2=class1.getReferences().get(0);
+ assertTrue (class2.getName().equals("yooze.Class2"));
+ ClassModel class3=class2.getReferences().get(0);
+ assertTrue (class3.getName().equals("yooze.Class3"));
+
+ assertTrue(class2.getReferences().contains(class3));
+ assertTrue(class3.getReferences().contains(class1));
+
+ }
+}
diff --git a/src/test/java/yooze/LargePackageTest.java b/src/test/java/yooze/LargePackageTest.java
new file mode 100644
index 0000000..c7127e4
--- /dev/null
+++ b/src/test/java/yooze/LargePackageTest.java
@@ -0,0 +1,40 @@
+package yooze;
+
+import java.io.FileOutputStream;
+import java.io.IOException;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.test.context.ContextConfiguration;
+import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
+
+import yooze.domain.Graph;
+
+@RunWith(SpringJUnit4ClassRunner.class)
+@ContextConfiguration(locations = "classpath:applicationContext-test.xml")
+public class LargePackageTest {
+
+ @Autowired
+ private Config config;
+
+ @Test
+ public void largePackage() throws IOException{
+ GraphBuilder earBuilder = GraphBuilder.getEarBuilder();
+ earBuilder.setPackageIncludePatterns("");
+ earBuilder.setPackageExcludePatterns("java.*");
+ Graph graph = earBuilder.buildClassDepencyGraph(config.getEarFile());
+ new DotPrinter(new FileOutputStream("/tmp/example.dot")).print(graph);
+
+ }
+
+ public Config getConfig() {
+ return config;
+ }
+
+ public void setConfig(Config config) {
+ this.config = config;
+ }
+
+
+}
diff --git a/src/test/java/yooze/MethodReferencesTest.java b/src/test/java/yooze/MethodReferencesTest.java
new file mode 100644
index 0000000..c016824
--- /dev/null
+++ b/src/test/java/yooze/MethodReferencesTest.java
@@ -0,0 +1,40 @@
+package yooze;
+
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.util.List;
+
+import junit.framework.Assert;
+
+import org.codehaus.jackson.JsonGenerator;
+import org.codehaus.jackson.map.ObjectMapper;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.springframework.test.context.ContextConfiguration;
+import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
+
+import yooze.domain.Graph;
+import yooze.domain.MethodModel;
+import yooze.dto.MethodDto;
+
+@RunWith(SpringJUnit4ClassRunner.class)
+@ContextConfiguration(locations = "classpath:applicationContext-test.xml")
+public class MethodReferencesTest {
+ @Test
+ public void test() throws IOException {
+ GraphBuilder directoryBuilder = GraphBuilder.getClassesDirectoryBuilder();
+ directoryBuilder.setPackageIncludePatterns("yooze.Class.*");
+ Graph graph = directoryBuilder.build("target/test-classes");
+ MethodModel mm1 = MethodCache.getInstance().get("yooze.Class1.rup(int)");
+ Assert.assertNotNull(mm1);
+ MethodModel mm2 = MethodCache.getInstance().get("yooze.Class3.dof()");
+ Assert.assertNotNull(mm2);
+ List callers = mm1.getCallers();
+ Assert.assertTrue(callers.contains(mm2));
+ MethodDto dto = MethodDto.create(MethodCache.getInstance().get("yooze.Class1.zoef(yooze.Class2)"));
+ JsonGenerator jg = new ObjectMapper().getJsonFactory().createJsonGenerator(
+ new FileOutputStream("c:\\ff\\out.json"));
+ jg.writeObject(dto);
+ jg.close();
+ }
+}
diff --git a/src/test/java/yooze/TgzBuilderTest.java b/src/test/java/yooze/TgzBuilderTest.java
new file mode 100644
index 0000000..05630f0
--- /dev/null
+++ b/src/test/java/yooze/TgzBuilderTest.java
@@ -0,0 +1,52 @@
+package yooze;
+
+import java.io.IOException;
+import java.util.ArrayList;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.test.context.ContextConfiguration;
+import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
+
+import yooze.domain.ClassModel;
+import yooze.domain.Graph;
+import static org.junit.Assert.assertTrue;
+
+@RunWith(SpringJUnit4ClassRunner.class)
+@ContextConfiguration(locations = "classpath:applicationContext-test.xml")
+public class TgzBuilderTest {
+
+ @Autowired
+ private Config config;
+
+ @Test
+ public void tgzBuilder() throws IOException {
+ GraphBuilder tgzBuilder = GraphBuilder.getDefaultTgzBuilder();
+ tgzBuilder.setPackageIncludePatterns("nl.*");
+ tgzBuilder.setPackageExcludePatterns("");
+ Graph graph = tgzBuilder.buildClassDepencyGraph(config.getTgzFile());
+
+ ArrayList names = new ArrayList();
+
+ for (ClassModel cm : graph.getChildren()) {
+ names.add(cm.getName());
+ }
+ assertTrue(names.contains("nl.jssl.jas.Main"));
+ assertTrue(names.contains("nl.jssl.jas.agent.Agent"));
+ assertTrue(names
+ .contains("nl.jssl.jas.instrumentation.ClassTransformer"));
+ assertTrue(names
+ .contains("nl.jssl.jas.instrumentation.JavassistInstrumenter"));
+ assertTrue(names.contains("nl.jssl.jas.measurement.Measurement"));
+ assertTrue(names.contains("nl.jssl.jas.measurement.Stopwatch"));
+ assertTrue(names.contains("nl.jssl.testjas.TestClass"));
+ assertTrue(names.contains("nl.jssl.testjas.Instrument"));
+ assertTrue(names.contains("nl.jssl.testjas.AgentTest"));
+
+ }
+
+ public void setConfig(Config config) {
+ this.config = config;
+ }
+}
diff --git a/src/test/resources/.DS_Store b/src/test/resources/.DS_Store
new file mode 100644
index 0000000..5008ddf
Binary files /dev/null and b/src/test/resources/.DS_Store differ
diff --git a/src/test/resources/._.DS_Store b/src/test/resources/._.DS_Store
new file mode 100644
index 0000000..338bd7b
Binary files /dev/null and b/src/test/resources/._.DS_Store differ
diff --git a/src/test/resources/agent.tar.gz b/src/test/resources/agent.tar.gz
new file mode 100644
index 0000000..95dba71
Binary files /dev/null and b/src/test/resources/agent.tar.gz differ
diff --git a/src/test/resources/applicationContext-test.xml b/src/test/resources/applicationContext-test.xml
new file mode 100644
index 0000000..bc05591
--- /dev/null
+++ b/src/test/resources/applicationContext-test.xml
@@ -0,0 +1,9 @@
+
+
+
+
+
+
+
diff --git a/src/test/resources/examples.ear b/src/test/resources/examples.ear
new file mode 100644
index 0000000..03e43c8
Binary files /dev/null and b/src/test/resources/examples.ear differ
diff --git a/src/test/resources/examples.war b/src/test/resources/examples.war
new file mode 100644
index 0000000..bf0e058
Binary files /dev/null and b/src/test/resources/examples.war differ
diff --git a/src/test/resources/logback.xml b/src/test/resources/logback.xml
new file mode 100644
index 0000000..2a6fe54
--- /dev/null
+++ b/src/test/resources/logback.xml
@@ -0,0 +1,12 @@
+
+
+
+
+ %d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/testdummies/java/yooze/Class1.java b/src/testdummies/java/yooze/Class1.java
new file mode 100644
index 0000000..4c49bc3
--- /dev/null
+++ b/src/testdummies/java/yooze/Class1.java
@@ -0,0 +1,15 @@
+package yooze;
+
+public class Class1 {
+ Class2 class2;
+
+ public void rup(int k) {
+ System.out.println(k);
+ class2.annoy();
+ zoef(class2);
+ }
+
+ public void zoef(Class2 envy) {
+ System.out.println(envy);
+ }
+}
diff --git a/src/testdummies/java/yooze/Class2.java b/src/testdummies/java/yooze/Class2.java
new file mode 100644
index 0000000..8c18115
--- /dev/null
+++ b/src/testdummies/java/yooze/Class2.java
@@ -0,0 +1,10 @@
+package yooze;
+
+public class Class2 {
+ public static Class3 class3=new Class3();
+
+ public void annoy() {
+ // TODO Auto-generated method stub
+
+ }
+}
diff --git a/src/testdummies/java/yooze/Class3.java b/src/testdummies/java/yooze/Class3.java
new file mode 100644
index 0000000..e911bcf
--- /dev/null
+++ b/src/testdummies/java/yooze/Class3.java
@@ -0,0 +1,14 @@
+package yooze;
+
+public class Class3 {
+ public static Class1 class1 = new Class1();
+
+ public static void annoyStatic() {
+ // TODO Auto-generated method stub
+
+ }
+
+ public void dof() {
+ class1.rup(1);
+ }
+}
diff --git a/src/testdummies/java/yooze/Class4.java b/src/testdummies/java/yooze/Class4.java
new file mode 100644
index 0000000..3a5601d
--- /dev/null
+++ b/src/testdummies/java/yooze/Class4.java
@@ -0,0 +1,8 @@
+package yooze;
+
+public class Class4 {
+
+ public void fla() {
+ new Class3().dof();
+ }
+}