001: package csdl.jblanket.modifier;
002:
003: import csdl.jblanket.JBlanketException;
004: import csdl.jblanket.methodset.MethodSetManager;
005: import csdl.jblanket.util.MethodCategories;
006:
007: import java.io.File;
008: import java.io.IOException;
009:
010: import org.apache.bcel.classfile.ClassParser;
011: import org.apache.bcel.classfile.JavaClass;
012: import org.apache.bcel.classfile.Method;
013: import org.apache.bcel.generic.ConstantPoolGen;
014: import org.apache.bcel.generic.MethodGen;
015: import org.apache.bcel.generic.ObjectType;
016: import org.apache.bcel.generic.Type;
017:
018: /**
019: * Provides a Modifer for Java byte code in class files. Methods in a class are modified only
020: * once. However, even though a class was previously modified, it still needs to be processed by
021: * this class so that all of its methods can be recorded in the set of total methods.
022: *
023: * @author Joy M. Agustin
024: * @version $Id: ClassModifier.java,v 1.4 2005/02/19 05:55:19 timshadel Exp $
025: */
026: class ClassModifier {
027:
028: /** Describes if JBlanket should execute in verbose mode */
029: private boolean verbose;
030: /** Grammar for names of test classes */
031: private String testGrammar;
032:
033: /** Describes if one-line methods should be excluded from the coverage measurement */
034: private boolean excludeOneLineMethods;
035: /** Describes if constructors should be excluded from the coverage measurement */
036: private boolean excludeConstructors;
037: /** Describes if a file should be used to exclude methods from the coverage measurement */
038: private boolean excludeIndividualMethods;
039:
040: /** Counts the methods found in the system */
041: private MethodCounter counter;
042:
043: /** File to modify */
044: private File classFile;
045: /** JavaClass for classFile */
046: private JavaClass clazz;
047:
048: /**
049: * Constructs a new ClassModifier object.
050: * @param verbose describes if JBlanke should execute in verbose mode.
051: * @param testGrammar the grammar describing test class names.
052: * @param excludeOneLineMethods describes exclusion of one-line methods.
053: * @param excludeConstructors describes exclusion of constructors.
054: * @param excludeIndividualMethods TODO
055: * @param counter the MethodCounter collecting all method type signatures.
056: * @param classFile the fully qualified path to a class file to process.
057: *
058: * @throws JBlanketException if unable to get byte code from <code>classFile</code>.
059: */
060: public ClassModifier(boolean verbose, String testGrammar,
061: boolean excludeOneLineMethods, boolean excludeConstructors,
062: boolean excludeIndividualMethods, MethodCounter counter,
063: File classFile) throws JBlanketException {
064:
065: this .verbose = verbose;
066: this .testGrammar = testGrammar;
067:
068: this .excludeOneLineMethods = excludeOneLineMethods;
069: this .excludeConstructors = excludeConstructors;
070: this .excludeIndividualMethods = excludeIndividualMethods;
071:
072: this .counter = counter;
073:
074: this .classFile = classFile;
075:
076: // retrieve class byte code -- throws IOException
077: try {
078: this .clazz = (new ClassParser(this .classFile
079: .getAbsolutePath())).parse();
080: } catch (IOException e) {
081: throw new JBlanketException("Unable to parse "
082: + this .classFile.getAbsolutePath(), e);
083: }
084: }
085:
086: /**
087: * Constructs a new ClassModifier object.
088: *
089: * @param verbose describes if JBlanke should execute in verbose mode.
090: * @param counter the MethodCounter collecting all method type signatures.
091: * @param classFile the fully qualified path to a class file to process.
092: * @throws JBlanketException if unable to get byte code from <code>classFile</code>.
093: */
094: public ClassModifier(boolean verbose, MethodCounter counter,
095: File classFile) throws JBlanketException {
096: this (verbose, null, false, false, false, counter, classFile);
097: }
098:
099: /**
100: * Modifies byte code in the class represented by this ClassModifier.
101: *
102: * @throws JBlanketException if unable to store modified byte code.
103: */
104: public void modifyMethods() throws JBlanketException {
105:
106: Method[] methods = clazz.getMethods();
107:
108: // count all methods in the class
109: this .counter.findAllMethods(clazz, this .excludeConstructors);
110:
111: // modify methods if they weren't previously modified
112: // if (!isModified()) {
113:
114: // process methods
115: ConstantPoolGen pool = new ConstantPoolGen(clazz
116: .getConstantPool());
117: for (int j = 0; j < methods.length; j++) {
118: MethodGen method = new MethodGen(methods[j], clazz
119: .getClassName(), pool);
120:
121: // TODO: Add "excluded individual methods"
122: MethodModifier methodModifier = new MethodModifier(
123: this .verbose, this .testGrammar,
124: this .excludeOneLineMethods,
125: this .excludeIndividualMethods,
126: this .excludeConstructors, method);
127: methods[j] = methodModifier.processMethod(pool,
128: isModified());
129: }
130: clazz.setConstantPool(pool.getFinalConstantPool());
131: //}
132:
133: // dump the modified class back into its current directory
134: try {
135: clazz.dump(this .classFile.getAbsoluteFile());
136: } catch (IOException e) {
137: throw new JBlanketException("Unable to save modified file "
138: + this .classFile.getAbsolutePath());
139: }
140: }
141:
142: /**
143: * Checks if a class has already been modified.
144: * <p>
145: * If <code>clazz</code> references the <code>MethodCollector.storeMethodTypeSignature</code>
146: * method, then all methods in the class were previously modified.
147: *
148: * NOTE: method is package private for testing, else should be private.
149: *
150: * @return true if the class was previously modified or cannot be modified, false otherwise.
151: */
152: boolean isModified() {
153:
154: ConstantPoolGen pool = new ConstantPoolGen(clazz
155: .getConstantPool());
156:
157: // will not modify native classes; abstract classes can have non-abstract methods.
158: // catch the abstract methods when modifying them.
159: if (clazz.isNative()) {
160: return true;
161: }
162:
163: Type[] parameters = new Type[] { Type.STRING, Type.STRING,
164: new ObjectType("java.util.ArrayList"), Type.STRING };
165: String methodSignature = Type.getMethodSignature(Type.VOID,
166: parameters);
167:
168: int result = pool.lookupMethodref(
169: "csdl.jblanket.modifier.MethodCollector",
170: "storeMethodTypeSignature", methodSignature);
171: return result != -1;
172: }
173:
174: /**
175: * Records the excluded methods in the class represented by this ClassModifier.
176: */
177: public void excludeMethods() {
178:
179: Method[] methods = clazz.getMethods();
180:
181: // process methods
182: ConstantPoolGen pool = new ConstantPoolGen(clazz
183: .getConstantPool());
184: for (int j = 0; j < methods.length; j++) {
185: MethodGen method = new MethodGen(methods[j], clazz
186: .getClassName(), pool);
187: MethodModifier methodModifier = new MethodModifier(
188: this .verbose, this .testGrammar,
189: this .excludeOneLineMethods, false,
190: this .excludeConstructors, method);
191: methodModifier.excludeMethod(MethodSetManager.getInstance()
192: .getMethodSet(
193: MethodCategories.getInstance().getFileName(
194: "excludedFile")));
195: }
196: }
197: }
|