KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > csdl > jblanket > modifier > MethodCollector


1 package csdl.jblanket.modifier;
2
3 import csdl.jblanket.JBlanketException;
4 import csdl.jblanket.methodset.MethodInfo;
5 import csdl.jblanket.methodset.MethodSet;
6 import csdl.jblanket.methodset.MethodSetManager;
7
8 import java.io.File JavaDoc;
9 import java.io.FileInputStream JavaDoc;
10 import java.io.FileNotFoundException JavaDoc;
11 import java.io.FileOutputStream JavaDoc;
12 import java.io.IOException JavaDoc;
13 import java.text.DateFormat JavaDoc;
14 import java.text.SimpleDateFormat JavaDoc;
15 import java.util.ArrayList JavaDoc;
16 import java.util.Date JavaDoc;
17 import java.util.Properties JavaDoc;
18
19 import org.apache.tools.ant.DirectoryScanner;
20
21 /**
22  * Collects the type signatures of methods invoked during JUnit testing.
23  * <p>
24  * This class is used during the second step in JBlanket -- recording the method invocations per
25  * JUnit test class with <code>storeMethodTypeSignature</code>. This method is inserted into the
26  * byte code of every non-abstract or non-native method in .class files that should be included in
27  * coverage. After every test class is executed, the results are stored in a "COVER-*.xml" file,
28  * where * is the fully qualified name of the test class. Methods invoked on the server during
29  * testing are recorded in "COVER-&lt;class&gt;.xml" where &lt;class&gt; is the fully qualified
30  * name of the first class invoked on the server.
31  * <p>
32  * To use this class, the "jblanket.dir" system property should be set to a directory in which
33  * all JBlanket results will be stored. If none is set, the default value is:
34  * <ul>
35  * System.getProperty("user.home")\jblanket.
36  * </ul>
37  * <p>
38  * All public static methods of this class are invoked by other methods at run time from
39  * <code>storeMethodTypeSignature</code> and are coded in this manner to simplify instrumentation.
40  *
41  * @author Joy M. Agustin
42  * @version $Id: MethodCollector.java,v 1.2 2005/03/08 08:02:01 timshadel Exp $id
43  */

44 public class MethodCollector {
45
46   /** Holds the class name used to store method information */
47   private static String JavaDoc currentClassName;
48
49   /** Prefix of file name for JBlanket intermediate coverage data from running JUnit tests */
50   private static final String JavaDoc COVER_PREFIX = "COVER-";
51   /** File extension for XML files */
52   private static final String JavaDoc XML_EXTENSION = ".xml";
53
54   /** Time stamp to store in JBlanket summary output files */
55   private Date JavaDoc earliestDate;
56
57   /** Separator between directories */
58   private static final String JavaDoc SLASH = File.separator;
59
60   /** Empty constructor. */
61   private MethodCollector() { }
62
63   /**
64    * Stores the type signature of <code>methodName</code> whenever the modified method
65    * <code>methodName</code> is invoked. The name of the current test class is stored so that
66    * methods invoked in different test classes can be stored in different files.
67    * <p>
68    * ASSUMPTION: no test class calls the methods in another test class. Therefore, results from
69    * running test cases will be stored in separate files and not overwrite other results.
70    *
71    * @param className the fully qualified name of class containing <code>methodName</code>.
72    * @param methodName the name of the method invoked.
73    * @param paramList the list of parameter types of <code>methodName</code>.
74    * @param testGrammar the grammar describing names of test classes. The only acceptable grammars
75    * are 'Test*.class' and '*Test.class'.
76    * @throws JBlanketException if cannot find 'jblanket.dir' system property.
77    */

78   public static synchronized void storeMethodTypeSignature(String JavaDoc className, String JavaDoc methodName,
79                                                            ArrayList JavaDoc paramList, String JavaDoc testGrammar)
80       throws JBlanketException {
81
82     // default name of constructors
83
final String JavaDoc constructor = "<init>";
84
85     // check if class is a test class
86
if ((currentClassName == null) || isTestClass(className, testGrammar)) {
87       currentClassName = className;
88     }
89
90     // get MethodSet instance for storing the method's type signature
91
MethodSet methodSet = MethodSetManager.getInstance().getMethodSet(currentClassName);
92     ArrayList JavaDoc reconstructedList = new ArrayList JavaDoc();
93
94     // check parameter types before adding them to methodInfo object
95
for (int i = 0; i < paramList.size(); i++) {
96       reconstructedList.add(reconstructType((String JavaDoc) paramList.get(i)));
97     }
98
99     // reconstruct names of constructors from default name '<init>'
100
if (constructor.equals(methodName)) {
101       methodName = removePackagePrefix(className);
102     }
103     if ("<clinit>".equals(methodName)) {
104         methodName = MethodCollector.removePackagePrefix(className) + "[static initializer]";
105     }
106     methodName = methodName.replaceAll("<", "_").replaceAll(">", "_");
107
108     // create a MethodInfo object to hold type signature
109
MethodInfo methodInfo = new MethodInfo(className, methodName, reconstructedList);
110
111     // if a new method was invoked, write out methodSet
112
if (methodSet.add(methodInfo)) {
113       String JavaDoc jblanketDir = getJBlanketDir();
114       String JavaDoc outFileName = jblanketDir + SLASH + COVER_PREFIX + currentClassName + XML_EXTENSION;
115
116       // get the current time as the last time outFileName was updated.
117
storeMethodData(methodSet, outFileName, currentClassName, new Date JavaDoc());
118     }
119   }
120
121   /**
122    * Verifies if <code>className</code> is a part of grammar defined by <code>testGrammar</code>.
123    * The only grammars acceptable are class names that either begins or ends with 'Test' and end
124    * with either '.java' or '.class' file types.
125    *
126    * @param className name of class to verify.
127    * @param testGrammar the grammar defining test class names.
128    * @return true if a part of <code>testGrammar</code>, false otherwise.
129    */

130   public static boolean isTestClass(String JavaDoc className, String JavaDoc testGrammar) {
131
132     // remove any '.class' or '.java' from testGrammar or last '.'
133
if (testGrammar.endsWith(".java") || testGrammar.endsWith(".class")
134         || testGrammar.lastIndexOf('.') == testGrammar.length() - 1) {
135       testGrammar = testGrammar.substring(0, testGrammar.lastIndexOf('.'));
136     }
137
138     if (className.indexOf('.') > -1) {
139       // keep only the name of the class, removing package prefix
140
className = className.substring(className.lastIndexOf('.') + 1, className.length());
141     }
142
143     // check if className is part of testGrammar
144
if (testGrammar.endsWith("*")) {
145       String JavaDoc test = testGrammar.substring(0, testGrammar.length() - 1);
146       return className.startsWith(test);
147     }
148     else if (testGrammar.startsWith("*")) {
149       String JavaDoc test = testGrammar.substring(1, testGrammar.length());
150       return className.endsWith(test);
151     }
152
153     return false;
154   }
155
156   /**
157    * Gets the output directory for intermediate and final JBlanket results. If the 'jblanket.dir'
158    * system property is not found, the default value is:
159    * <p>
160    * System.getProperty("user.home")\jblanket
161    *
162    * @return the JBlanket output directory.
163    */

164   public static String JavaDoc getJBlanketDir() {
165
166     // 1. check the system properties
167
String JavaDoc jblanketDir = System.getProperty("jblanket.dir");
168
169     // 2. check if in Tomcat and its conf/*.properties files
170
if (jblanketDir == null || jblanketDir.equals("")) {
171       jblanketDir = getJBlanketDirInTomcatProperties();
172     }
173
174     // 3. jblanket.dir not found anywhere, so set default
175
if (jblanketDir == null || jblanketDir.equals("")) {
176       jblanketDir = System.getProperty("user.home") + SLASH + "jblanket";
177       // keeping message as warning for those who might not have set directory with JUnit fork on
178
String JavaDoc message = "Warning: jblanket.dir system property missing. Using: " + jblanketDir;
179       System.out.println(message);
180       // set default value
181
System.setProperty("jblanket.dir", jblanketDir);
182     }
183
184     // create jblanket directory if does not exist
185
File JavaDoc jblanketDirectory = new File JavaDoc(jblanketDir);
186     if (!jblanketDirectory.exists()) {
187       jblanketDirectory.mkdirs();
188     }
189     return jblanketDir;
190   }
191
192   /**
193    * Gets the 'jblanket.dir' property from Tomcat if the current JVM is running Tomcat.
194    * All *.properties files in the &lt;CATALINA_HOME&gt;/conf directory are loaded and
195    * checked. The first occurrence of 'jblanket.dir' is returned.
196    *
197    * @return the jblanket.dir property, or null if it doesn't exist.
198    */

199   private static String JavaDoc getJBlanketDirInTomcatProperties() {
200     
201     // First, try to get the Properties file using catalina.home.
202
File JavaDoc catalinaHome = new File JavaDoc(System.getProperty("catalina.home", ""));
203     if (!catalinaHome.exists()) {
204       return null;
205     }
206
207     // look for the Tomcat/conf/.properties file(s)
208
File JavaDoc confDir = new File JavaDoc(catalinaHome, "conf");
209     DirectoryScanner scanner = new DirectoryScanner();
210     scanner.setBasedir(confDir);
211     scanner.setIncludes(new String JavaDoc[] {"*.properties"});
212     scanner.scan();
213     String JavaDoc[] files = scanner.getIncludedFiles();
214
215     // check each file for jblanket.dir property
216
for (int i = 0; i < files.length; i++) {
217       Properties JavaDoc properties = new Properties JavaDoc();
218       try {
219         properties.load(new FileInputStream JavaDoc(new File JavaDoc(confDir, files[i])));
220       }
221       catch (Exception JavaDoc e) {
222         // do nothing -- if something is wrong with .properties file, don't care;
223
// instead assume that jblanket.dir property does not exist
224
}
225
226       // return first occurrence of jblanket.dir
227
String JavaDoc tomcatJBlanketDir = properties.getProperty("jblanket.dir", null);
228       if (tomcatJBlanketDir != null) {
229         return tomcatJBlanketDir;
230       }
231     }
232
233     // did not find jblanket.dir property
234
return null;
235   }
236
237   /**
238    * Reconstructs the type signature of <code>type</code> from the way it is stored in the
239    * Constant Pool to the way it is found in the source code. This method is required because
240    * type signatures in the Constant Pool are different from the way it is coded.
241    * <p>
242    * For example: <pre>
243    * [Ljava/lang/String; ==> java.lang.String[]
244    * </pre>
245    *
246    * @param type the raw parameter type.
247    * @return the reconstructed parameter type.
248    */

249   public static String JavaDoc reconstructType(String JavaDoc type) {
250
251     // if type is an array, find out how many dimensions
252
int arrayDimensions = 0;
253
254     while (type.indexOf("[") > -1 && type.length() > 1) {
255       arrayDimensions++;
256       type = type.substring(1, type.length());
257     }
258
259     StringBuffer JavaDoc reconstructedType = null;
260
261     // base type
262
if (type.length() == 1) {
263       reconstructedType = new StringBuffer JavaDoc(reconstructBaseType(type));
264     }
265     // object type
266
else {
267       // problem fixed: although 'L' denotes an object, this code fails when the name of a class
268
// starts with 'L'. Therefore, get the first instance of 'L' instead of last.
269
//int classNameStart = type.lastIndexOf('L') + 1;
270
int classNameStart = type.indexOf('L') + 1;
271       int classNameEnd = type.lastIndexOf(';');
272       type = type.substring(classNameStart, classNameEnd);
273       reconstructedType = new StringBuffer JavaDoc(type.replace('/', '.'));
274     }
275
276     while (arrayDimensions > 0) {
277       reconstructedType.append("[]");
278       arrayDimensions--;
279     }
280     return reconstructedType.toString();
281   }
282
283   /**
284    * Reconstructs a base type from the way it is stored in the Constant
285    * Pool to the way it is found in source code.
286    * <p>
287    * For example: <pre>
288    * I ==> int
289    * </pre>
290    *
291    * @param type the raw base type.
292    * @return the reconstructed base type.
293    */

294   private static String JavaDoc reconstructBaseType(String JavaDoc type) {
295
296     char t = type.charAt(0);
297     switch (t) {
298       case 'B':
299         return "byte";
300       case 'C':
301         return "char";
302       case 'D':
303         return "double";
304       case 'F':
305         return "float";
306       case 'I':
307         return "int";
308       case 'J':
309         return "long";
310       case 'S':
311         return "short";
312       case 'Z':
313         return "boolean";
314       default:
315         // would throw an Exception, but difficult to implement since this is a piece of the
316
// method modification InstructionList
317
return "Error! type is an invalid string";
318     }
319   }
320
321   /**
322    * Stores all the method information gathered in <code>jblanketSet</code> for
323    * <code>className</code> into <code>outFileName</code>.
324    *
325    * @param methodSet the collection of method information to output.
326    * @param outFileName the fully qualified path of the output file.
327    * @param className the name of the class.
328    * @param timeStamp the time stamp in milliseconds to include in file className.
329    * @throws JBlanketException if cannot find <code>outFileName</code> or
330    * if cannot write to <code>outFileName</code>.
331    */

332   public static synchronized void storeMethodData(MethodSet methodSet, String JavaDoc outFileName,
333                                                   String JavaDoc className, Date JavaDoc timeStamp)
334     throws JBlanketException {
335
336     // store test class results
337
MethodSet fileMethodSet = new MethodSet();
338     try {
339       FileInputStream JavaDoc istream = new FileInputStream JavaDoc(new File JavaDoc(outFileName));
340       // throws ParseException, FileNotFoundException, IOException
341
fileMethodSet.load(istream);
342       istream.close();
343     }
344     catch (Exception JavaDoc e) {
345       // do nothing. if file doesn't exist, don't care.
346
}
347
348     FileOutputStream JavaDoc fostream = null;
349     try {
350       fostream = new FileOutputStream JavaDoc(outFileName);
351     }
352     catch (FileNotFoundException JavaDoc e) {
353       throw new JBlanketException("Unable to open file " + outFileName, e);
354     }
355
356     fileMethodSet.union(methodSet);
357     try {
358       fileMethodSet.store(fostream, className, timeStamp);
359     }
360     catch (IOException JavaDoc e) {
361       throw new JBlanketException("Unable to store MethodSet to " + outFileName, e);
362     }
363
364     try {
365       fostream.close();
366     }
367     catch (IOException JavaDoc e) {
368       throw new JBlanketException("Unable to close file " + outFileName, e);
369     }
370   }
371
372   /**
373    * Removes the package prefix from <code>className</code>.
374    *
375    * @param className the fully qualified name of the class.
376    * @return the name of the class in <code>className</code>.
377    */

378   public static String JavaDoc removePackagePrefix(String JavaDoc className) {
379     return className.substring(className.lastIndexOf('.') + 1, className.length());
380   }
381   
382   /**
383    * Gets the Date format used by JBlanket.
384    *
385    * @return the DateFormat used by JBlanket.
386    */

387   public static DateFormat JavaDoc getDateFormat() {
388     return new SimpleDateFormat JavaDoc("MMM d, yyyy h:mm:ss a");
389   }
390 }
391
Popular Tags