Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support for options to writing output to a file and generate XML #14

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
164 changes: 144 additions & 20 deletions src/main/java/gr/gousiosg/javacg/stat/ClassVisitor.java
Original file line number Diff line number Diff line change
Expand Up @@ -28,58 +28,182 @@

package gr.gousiosg.javacg.stat;

import org.apache.bcel.classfile.Constant;
import org.apache.bcel.classfile.ConstantPool;
import org.apache.bcel.classfile.EmptyVisitor;
import org.apache.bcel.classfile.JavaClass;
import org.apache.bcel.classfile.Method;
import org.apache.bcel.classfile.*;
import org.apache.bcel.generic.ConstantPoolGen;
import org.apache.bcel.generic.MethodGen;

import java.io.PrintStream;

/**
* The simplest of class visitors, invokes the method visitor class for each
* method found.
*/
public class ClassVisitor extends EmptyVisitor {

private static final String OBJECT = Object.class.getName();
private static final String ENUMERATION = Enum.class.getName();
private JavaClass clazz;
private ConstantPoolGen constants;
private String classReferenceFormat;

public ClassVisitor(JavaClass jc) {
private PrintStream output;
private FormatEnumaration format;
private ConstantPool currentConstantPool;

public ClassVisitor(JavaClass jc, PrintStream outputStream, FormatEnumaration format) {
clazz = jc;
this.format = format;
this.output = outputStream;
constants = new ConstantPoolGen(clazz.getConstantPool());
classReferenceFormat = "C:" + clazz.getClassName() + " %s";
switch (format) {
case TXT:
classReferenceFormat = "C:" + clazz.getClassName() + " %s";
break;
case XML:
classReferenceFormat = "<constant tag=\"%s\">%s</constant>";
break;
default:
throw new RuntimeException("Unsupported format "+format);
}

}

public void visitJavaClass(JavaClass jc) {
switch (format) {
case XML:
output.println("<class name=\""+jc.getClassName()+"\">");
String[] names = jc.getInterfaceNames();
if (names != null && names.length>0) {
output.println("<interfaces>");
for (String name:names) {
output.print("<interface>");
output.print(name);
output.println("</interface>");
}
output.println("</interfaces>");
}
if (OBJECT.equals(jc.getSuperclassName())==false) {
output.print("<super>");
output.print(jc.getSuperclassName());
output.println("</super>");
}
break;
}
jc.getConstantPool().accept(this);
Method[] methods = jc.getMethods();
for (int i = 0; i < methods.length; i++)
for (int i = 0; i < methods.length; i++) {
methods[i].accept(this);
}
switch (format) {
case XML:
output.println("</class>");
break;
}
}

public void visitConstantPool(ConstantPool constantPool) {
for (int i = 0; i < constantPool.getLength(); i++) {
Constant constant = constantPool.getConstant(i);
if (constant == null)
continue;
if (constant.getTag() == 7) {
String referencedClass =
constantPool.constantToString(constant);
System.out.println(String.format(classReferenceFormat,
referencedClass));
}
switch (format) {
case TXT:
for (int i = 0; i < constantPool.getLength(); i++) {
Constant constant = constantPool.getConstant(i);
if (constant == null)
continue;
if (constant.getTag() == 7) {
String referencedClass =
constantPool.constantToString(constant);
output.println(String.format(classReferenceFormat,
referencedClass));
}
}
case XML:
for (int i = 0; i < constantPool.getLength(); i++) {
Constant constant = constantPool.getConstant(i);
if (constant == null)
continue;
if (constant.getTag() == 7) {
output.print("<constant index=\"");
output.print(i);
output.print("\">");
String referencedClass =
constantPool.constantToString(constant);
output.print(xmlEscapeText(referencedClass));
output.println("</constant>");
}
}
}
}

public void visitMethod(Method method) {
MethodGen mg = new MethodGen(method, clazz.getClassName(), constants);
MethodVisitor visitor = new MethodVisitor(mg, clazz);
MethodVisitor visitor = new MethodVisitor(mg, clazz,output,format);
visitor.start();
}

public void start() {
visitJavaClass(clazz);
}

/**
* Encode special charaters for XML
* @param t
* @return
*/
static String xmlEscapeText(String t) {
StringBuilder sb = null;
for(int i = 0; i < t.length(); i++){
char c = t.charAt(i);
switch(c){
case '<':
if (sb==null) {
sb = new StringBuilder(t.length()+10);
sb.append(t.substring(0,i));
}
sb.append("&lt;");
break;
case '>':
if (sb==null) {
sb = new StringBuilder(t.length()+10);
sb.append(t.substring(0,i));
}
sb.append("&gt;");
break;
case '\"':
if (sb==null) {
sb = new StringBuilder(t.length()+10);
sb.append(t.substring(0,i));
}
sb.append("&quot;");
break;
case '&':
if (sb==null) {
sb = new StringBuilder(t.length()+10);
sb.append(t.substring(0,i));
}
sb.append("&amp;");
break;
case '\'':
if (sb==null) {
sb = new StringBuilder(t.length()+10);
sb.append(t.substring(0,i));
}
sb.append("&apos;");
break;
default:
if(c>0x7e || c<0x20) {
if (sb==null) {
sb = new StringBuilder(t.length()+10);
sb.append(t.substring(0,i));
}
sb.append("&#"+((int)c)+";");
}else if (sb != null) {
sb.append(c);
}
}
}
if (sb==null) {
//re-use original string
return t;
} else {
return sb.toString();
}
}
}
5 changes: 5 additions & 0 deletions src/main/java/gr/gousiosg/javacg/stat/FormatEnumaration.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package gr.gousiosg.javacg.stat;

public enum FormatEnumaration {
TXT,XML
}
103 changes: 101 additions & 2 deletions src/main/java/gr/gousiosg/javacg/stat/JCallGraph.java
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,12 @@
package gr.gousiosg.javacg.stat;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.PrintStream;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;

Expand All @@ -46,9 +50,90 @@
public class JCallGraph {

public static void main(String[] args) {
FormatEnumaration format = FormatEnumaration.TXT;
PrintStream outputStream = System.out;
List<String> jars = new ArrayList<>(args.length);
if (args.length==0) {
printHelp();
}
for (int i =0;i<args.length;i++) {
String arg = args[i];
if (arg.startsWith("-")) {
//additional parameters
switch (arg) {
case "-out" :
case "-o" :
File file = new File(args[++i]);
if (file.exists()) {
if (file.isFile()==false) {
System.err.println(args[i]+" is not a file");
System.exit(-1);
}
if (file.canWrite()==false) {
System.err.println("Cannot write to "+args[i]);
System.exit(-1);
}
} else {
try {
file.createNewFile();
} catch (IOException e) {
System.err.println("Cannot create file "+args[i]);
System.err.println(e.getMessage());
System.exit(-1);
}
}
try {
outputStream = new PrintStream(file);
} catch (FileNotFoundException e) {
System.err.println("Cannot create file "+args[i]);
System.err.println(e.getMessage());
System.exit(-1);
}
break;
case "-f" :
case "-format" :
if (i+1<args.length) {
String form = args[++i].toLowerCase();
switch (form) {
case "txt":
format = FormatEnumaration.TXT;
break;
case "xml":
format = FormatEnumaration.XML;
break;
default:
System.err.println("Unsupported format value "+args[i]);
printHelp();
System.exit(-1);
}
} else {
System.err.println("Format value is not provided");
printHelp();
System.exit(-1);
}
break;
case "-h" :
case "-help" :
case "-?" :
printHelp();
break;
default:
System.err.println("Unknown parameter "+arg);
System.exit(-1);
}
} else {
jars.add(arg);
}
}
ClassParser cp;
try {
for (String arg : args) {
switch (format) {
case XML:
outputStream.println("<?xml version=\"1.1\" encoding=\"UTF-8\"?>");
outputStream.println("<root>");
break;
}
for (String arg : jars) {

File f = new File(arg);

Expand All @@ -68,13 +153,27 @@ public static void main(String[] args) {
continue;

cp = new ClassParser(arg,entry.getName());
ClassVisitor visitor = new ClassVisitor(cp.parse());
ClassVisitor visitor = new ClassVisitor(cp.parse(),outputStream,format);
visitor.start();
}
}
switch (format) {
case XML:
outputStream.println("</root>");
break;
}
} catch (IOException e) {
System.err.println("Error while processing jar: " + e.getMessage());
e.printStackTrace();
}
}

private static void printHelp() {
System.out.println("Constructs a callgraph out of a JAR archive. Can combine multiple archives into a single call graph");
System.out.println("See https://github.com/gousiosg/java-callgraph for more details");
System.out.println("Options:");
System.out.println("-h, -help, -? This help message");
System.out.println("-f, -format Set output format. Possible values 'xml' or 'txt' (default)");
System.out.println("-o, -out File for output. Default - to console");
}
}
Loading