aQute.bnd.make.calltree.CalltreeResource Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of biz.aQute.bndlib Show documentation
Show all versions of biz.aQute.bndlib Show documentation
bndlib: A Swiss Army Knife for OSGi
package aQute.bnd.make.calltree;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.lang.reflect.Modifier;
import java.util.Collection;
import java.util.Comparator;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;
import java.util.TreeSet;
import aQute.bnd.osgi.ClassDataCollector;
import aQute.bnd.osgi.Clazz;
import aQute.bnd.osgi.Clazz.MethodDef;
import aQute.bnd.osgi.Constants;
import aQute.bnd.osgi.WriteResource;
/**
* Create an XML call tree of a set of classes. The structure of the XML is:
*
*
* calltree ::= <using> <usedby> using ::= <method> *
* usedby ::= <method> * method ::= <ref>
*
*
* The using
element contains methods in the set of classes and
* their references. The usedby
element contains the used methods
* and their references to the set of classes. The ref
element
* contains the class, the method name, the descriptor, and a pretty print
* version of the method. The XML does not contain an XML processor instruction
* to make it easier to include in other XML. The encoding is always UTF-8. This
* class can be used as a resource, just add it to a JAR and the data is
* generated when the resource is written (saving time when the JAR is up to
* date and does not have to be generated). However, the actual write method is
* a static method and can be called as well:
* {@link #writeCalltree(PrintWriter, Collection)}.
*/
public class CalltreeResource extends WriteResource {
Collection classes;
/**
* Create a resource for inclusion that will print a call tree.
*
* @param values the classes for which the call tree is generated.
*/
public CalltreeResource(Collection values) {
this.classes = values;
System.err.println(values);
}
/**
* We set the last modified to 0 so this resource does not force a new JAR
* if all other resources are up to date.
*/
@Override
public long lastModified() {
return 0;
}
/**
* The write method is called to write the resource. We just call the static
* method.
*/
@Override
public void write(OutputStream out) throws Exception {
OutputStreamWriter osw = new OutputStreamWriter(out, Constants.DEFAULT_CHARSET);
PrintWriter pw = new PrintWriter(osw);
try {
writeCalltree(pw, classes);
} finally {
pw.flush();
}
}
/**
* Print the call tree in XML.
*
* @param out The output writer
* @param classes The set of classes
* @throws Exception Any errors
*/
public static void writeCalltree(PrintWriter out, Collection classes) throws Exception {
final Map> using = new TreeMap>(
COMPARATOR);
final Map> usedby = new TreeMap>(
COMPARATOR);
ClassDataCollector cd = new ClassDataCollector() {
// Clazz.MethodDef source;
// Before a method is parsed
@Override
public void method(Clazz.MethodDef source) {
// this.source = source;
xref(using, source, null);
xref(usedby, source, null);
}
// For any reference in the previous method.
// public void reference(Clazz.MethodDef reference) {
// xref(using, source, reference);
// xref(usedby, reference, source);
// }
};
for (Clazz clazz : classes) {
clazz.parseClassFileWithCollector(cd);
}
out.println("");
xref(out, "using", using);
xref(out, "usedby", usedby);
out.println(" ");
}
/*
* Add a new reference
*/
static Comparator COMPARATOR = new Comparator() {
public int compare(MethodDef a, MethodDef b) {
int r = a.getName().compareTo(b.getName());
return r != 0 ? r : a.getDescriptor().toString().compareTo(b.getDescriptor().toString());
}
};
static void xref(Map> references, Clazz.MethodDef source,
Clazz.MethodDef reference) {
Set set = references.get(source);
if (set == null)
references.put(source, set = new TreeSet(COMPARATOR));
if (reference != null)
set.add(reference);
}
/*
* Print out either using or usedby sets
*/
private static void xref(PrintWriter out, String group, Map> references) {
out.println(" <" + group + ">");
for (Map.Entry> entry : references.entrySet()) {
Clazz.MethodDef source = entry.getKey();
Set refs = entry.getValue();
method(out, "method", source, ">");
for (Clazz.MethodDef ref : refs) {
method(out, "ref", ref, "/>");
}
out.println(" ");
}
out.println(" " + group + ">");
}
/*
* Print out a method.
*/
private static void method(PrintWriter out, String element, Clazz.MethodDef source, String closeElement) {
out.println(" <" + element + " class='" + source.getContainingClass().getFQN() + "'"
+ getAccess(source.getAccess()) + (source.isConstructor() ? "" : " name='" + source.getName() + "'")
+ " descriptor='" + source.getDescriptor() + "' pretty='" + source.toString() + "'" + closeElement);
}
private static String getAccess(int access) {
StringBuilder sb = new StringBuilder();
if (Modifier.isPublic(access))
sb.append(" public='true'");
if (Modifier.isStatic(access))
sb.append(" static='true'");
if (Modifier.isProtected(access))
sb.append(" protected='true'");
if (Modifier.isInterface(access))
sb.append(" interface='true'");
return sb.toString();
}
}