001: /* ClassTransformer.java */
002: package org.quilt.cl;
003:
004: import java.util.Hashtable;
005: import java.util.List;
006:
007: import org.apache.bcel.classfile.JavaClass;
008: import org.apache.bcel.classfile.Method;
009: import org.apache.bcel.generic.ClassGen;
010: import org.apache.bcel.generic.InstructionList;
011: import org.apache.bcel.generic.MethodGen;
012:
013: /**
014: * Transform a JavaClass, if there are any class, method, or graph
015: * transformers. Methods whose names begin with "q$$q" are not
016: * transformed.
017: *
018: * @author <a href="jddixon@users.sourceforge.net">Jim Dixon</a>
019: */
020: public class ClassTransformer {
021:
022: /** For passing application-specific data. */
023: private Hashtable classHash = new Hashtable();
024:
025: /** Class pre- and post-processors. */
026: private List cxf;
027: /** Method pre- and post-processors. */
028: private List mxf;
029: /** Graph processors. */
030: private List gxf;
031: /** Method processor. */
032: private MethodTransformer xformer;
033:
034: /** Aborted by xformer. */
035: boolean aborted_ = false;
036:
037: /**
038: * Creates class transformer and lower-level transformers.
039: * The method transformer is created only if there are processor
040: * lists for it or the graph transformer.
041: *
042: * @param cxf List of class pre/post processors.
043: * @param mxf List of method pre/post processors.
044: * @param gsf List of graph processors.
045: */
046: public ClassTransformer(List cxf, List mxf, List gxf) {
047: this .cxf = cxf;
048: this .mxf = mxf;
049: this .gxf = gxf;
050: xformer = new MethodTransformer(mxf, gxf);
051: }
052:
053: public Hashtable getClassHash() {
054: return classHash;
055: }
056:
057: private void zapClassXformer(ClassXformer cxf, Exception e) {
058: System.err.println("WARNING: exception in " + cxf.getName()
059: + ": transformation will not be applied");
060: e.printStackTrace();
061: cxf = null;
062: }
063:
064: public JavaClass xform(JavaClass jc) {
065: if (jc == null || !jc.isClass()) {
066: throw new IllegalArgumentException(
067: "null or corrupt JavaClass");
068: }
069: ClassGen clazz = new ClassGen(jc);
070:
071: // NEEDS THOUGHT -- do we really want to make all classes
072: // public by default??
073: makePublic(clazz); // in xf[0] ??
074:
075: // apply any preprocessors
076: aborted_ = false;
077: ClassXformer[] xf = new ClassXformer[cxf.size()];
078: for (int i = 0; i < xf.length; i++) {
079: try {
080: xf[i] = (ClassXformer) ((cxf.get(i)).getClass()
081: .newInstance());
082: xf[i].setClassTransformer(this );
083: } catch (IllegalAccessException e) {
084: zapClassXformer(xf[i], e);
085: } catch (InstantiationException e) {
086: zapClassXformer(xf[i], e);
087: }
088: if (xf[i] != null && !aborted_) {
089: xf[i].preMethods(clazz);
090: }
091: }
092:
093: // IF there are transformations to be done at the method or
094: // graph level ...
095: if (mxf.size() > 0 || gxf.size() > 0 && !aborted_) {
096: // extract the methods and do wondrous things with them
097: Method[] methods = clazz.getMethods();
098: for (int i = 0; i < methods.length; i++) {
099: String methodName = methods[i].getName();
100: if (methods[i].getCode() != null
101: && !methodName.startsWith("q$$q")) {
102: // transform the method
103: MethodGen result = xformer.xform(clazz, methods[i]);
104: // put it in the ClassGen if it's OK
105: if (result != null) {
106: clazz.replaceMethod(methods[i], result
107: .getMethod());
108: // be a good citizen:
109: InstructionList ilist = result
110: .getInstructionList();
111: ilist.dispose();
112: result = null;
113: }
114: }
115: }
116: }
117: // apply any postprocessors
118: for (int i = xf.length - 1; i >= 0; i--) {
119: if (xf[i] != null && !aborted_) {
120: xf[i].postMethods(clazz);
121: }
122: }
123: if (clazz == null || aborted_) {
124: System.out
125: .println("ClassTransformer WARNING could not transform class");
126: return jc;
127: } else {
128: return clazz.getJavaClass();
129: }
130: }
131:
132: /**
133: * Make the class public. XXX Consider making this optional.
134: *
135: * @param cg ClassGen template for class to be made public.
136: */
137: protected static void makePublic(ClassGen cg) {
138: int flags = cg.getAccessFlags();
139: flags &= ~(org.apache.bcel.Constants.ACC_PRIVATE | org.apache.bcel.Constants.ACC_PROTECTED);
140: flags |= org.apache.bcel.Constants.ACC_PUBLIC;
141: cg.setAccessFlags(flags);
142: if (!cg.isPublic()) {
143: System.out.println("WARNING: makePublic for "
144: + cg.getClassName() + " failed");
145: }
146: }
147:
148: /** Abort the class transformation. */
149: public void abort() {
150: aborted_ = true;
151: }
152: }
|