1 /* StmtRegistry.java */
2 package org.quilt.cover.stmt;
3
4 import java.lang.reflect.Field;
5 import java.util.Iterator;
6 import java.util.Hashtable;
7 import java.util.Map;
8 import java.util.Set;
9 import org.quilt.cl.*;
10 import org.quilt.reg.*;
11
12 /***
13 * <p>Registry for statement coverage information. As Quilt-instrumented
14 * classes are loaded, they register their <code>q$$q</code> hit count
15 * arrays and are assigned an ID unique in the life of the registry.
16 * The registry maintains: </p>
17 * <ul>
18 * <li><b>hit counts</b>, keyed on class name</li>
19 * <li><b>method end counter indexes</b>, keyed on class and method name</li>
20 * <li><b>line number ranges</b>, keyed on class and counter index</li>
21 * </ul>
22 * <p>This and other information in the registry allows it the generate
23 * a number of reports summarizing coverage at</p>
24 * <ul>
25 * <li><b>package</b> level (soon)</li>
26 * <li><b>class</b> level (now)</li>
27 * <li><b>method</b> level (now)</li>
28 * <li><b>line</b> level (soonish)</li>
29 * </ul>
30 *
31 * <p>The registry is associated with the Quilt class loader when it
32 * is created. Information can been retrieved from the registry at
33 * any time. It will be accumulated as new instances of Quilt-instrumented
34 * classes are run.</p>
35 *
36 * @author <a href="mailto:jddixon@users.sourceforge.net">Jim Dixon</a>
37 */
38 public class StmtRegistry extends QuiltRegistry {
39
40 /*** XXX */
41 private static StmtRegistry INSTANCE = null;
42
43 /*** Returns a reference to the latest instance to announce itself. */
44 public static StmtRegistry getInstance () {
45 return INSTANCE;
46 }
47
48 /*** Maps class name to array of names of instrumented methods */
49 private Map methodNames = new Hashtable(); // key className, value String[]
50 /*** Maps class name to array of index of last counters for each method */
51 private Map methodEnds = new Hashtable(); // key className, value int[]
52
53 /***
54 * Constructor specifying Quilt class loader the registry is
55 * associated with.
56 */
57 public StmtRegistry (QuiltClassLoader qcl) {
58 super(qcl);
59 INSTANCE = this; // XXX the horror
60 ClassAction classAct = new ClassAction(this);
61
62 cxf = new ClassXformer[] { classAct };
63 mxf = new MethodXformer[] { new MethodAction(this) };
64 gxf = new GraphXformer[] { new GraphAction(this, classAct) };
65 setTransformers();
66 }
67
68 /***
69 * Clear counters associated with registry entries.
70 */
71 public void reset() {
72 // XXX DO NOTHING FOR NOW XXX
73 }
74
75 /***
76 * Dump the registry as plain text.
77 * @todo More elaborate reports.
78 */
79 public String getReport() {
80 StringBuffer sb = new StringBuffer()
81 .append("\n=========================\n")
82 .append( " QUILT COVERAGE REPORT \n");
83 if (isEmpty()) {
84 sb.append("* the registry is empty *\n");
85 } else {
86 Set keys = keySet();
87 Iterator i = keys.iterator();
88 while (i.hasNext()) {
89 // class --> hit count arrays
90 String[] name = (String[]) i.next();
91 int [] counts = (int[])get(name);
92 int count = counts.length;
93 String className = name[0];
94
95 sb.append(className + ": " + count + " counters, "
96 + getClassCoverage(className) + "% coverage\n ");
97 // // DEBUG ONLY: DUMP COUNTER ARRAY
98 // for (int k = 0; k < count; k++) {
99 // sb.append(" " + counts[k]);
100 // }
101 // sb.append("\n");
102 // // END
103
104 String [] methods = (String[])methodNames.get(className);
105 if (methods == null) {
106 sb.append(" NULL\n");
107 } else {
108 // there can be more than one <init> method :-(
109 for (int k = 0; k < methods.length; k++) {
110 sb.append(" ").append(methods[k])
111 .append(" ").append(getMethodCoverage(className,k))
112 .append("% coverage\n");
113 }
114 }
115 }
116 }
117 sb.append("=========================\n");
118 return sb.toString();
119 }
120
121 /***
122 * Get the percentage of counters in the class that have counts
123 * greater than zero. The percentage is rounded down. If no
124 * class information is found, returns zero.
125 *
126 * @return an integer between 0 and 100, zero if class not found
127 */
128 int getClassCoverage (String className) {
129 int nonZero = 0;
130 int[] hitCounts = (int[]) get(new String[] {className});
131 if (hitCounts != null) {
132 for (int k = 0; k < hitCounts.length; k++) {
133 if (hitCounts[k] > 0) {
134 nonZero++;
135 }
136 }
137 nonZero = (nonZero*100)/hitCounts.length;
138 }
139 return nonZero;
140 }
141
142 /***
143 * Get the percentage of counters in the Nth method that have counts
144 * greater than zero. The percentage is rounded down. If no
145 * class or method information is found, returns zero.
146 *
147 * XXX The 'methodEnds' array actually contains cumulative counts,
148 * so values must be reduced by one.
149 *
150 * @return an integer between 0 and 100, zero if class not found
151 */
152 int getMethodCoverage (String className, int n) {
153 int nonZero = 0;
154 // XXX should report if either of these two is null or if
155 // cardinalities differ
156 int[] hitCounts = (int[]) get(new String[] {className});
157 int[] ends = (int[]) methodEnds.get(className);
158 if ( n < 0 || n >= hitCounts.length ) {
159 throw new IllegalArgumentException("index out of range");
160 }
161 int counterCount = 0;
162 int lastCounter = ends[n] -1 ;
163 int firstCounter = n == 0 ? 0 : ends[n - 1];
164 if (hitCounts != null && ends != null) {
165 for (int k = firstCounter; k <= lastCounter; k++) {
166 counterCount++;
167 if (hitCounts[k] > 0) {
168 nonZero++;
169 }
170 }
171 if (counterCount > 0) {
172 nonZero = (nonZero*100)/counterCount;
173 }
174 }
175 return nonZero;
176 }
177
178 // GET/PUT METHODS //////////////////////////////////////////////
179 // CLASSID ////////////////////////////////////////////
180 private static int nextClassID = 0;
181
182 public int getClassID (String className) {
183 int classID = -1;
184 try {
185 Field qField = Class.forName(className).getField("q$$qID");
186 // a trifle uncertain about the argument
187 classID = qField.getInt(qField);
188 } catch (Exception e) {
189 // just ignore any errors
190 // DEBUG
191 System.out.println("StmtRegistry.getClassID("
192 + className + ") failed - " + e);
193 // END
194 }
195 return classID;
196 }
197 // HIT COUNTS /////////////////////////////////////////
198 /***
199 * Get a reference to the hit count array for a class.
200 */
201 public int [] getCounts (String className) {
202 int[] counts = null;
203 try {
204 counts = (int[])get ( new String[] {className} );
205 } catch (Exception e) {
206 // just ignore any errors
207 System.out.println("StmtRegistry.getCounts ("
208 + className + ") failed - " + e);
209 }
210 return counts;
211 }
212 /***
213 * Register a class by passing a reference to its integer hit count
214 * array. Returns a class ID which is unique within this run. The
215 * ID will be stored in a static in the class, public static int q$$qID
216 *
217 * XXX So far absolutely no value to using String array as key; could
218 * just be String.
219 *
220 * @param className Name of the class being registered.
221 * @param counts Reference to the class's public static hit count array.
222 * @return A unique class ID on success, -1 on failure.
223 */
224
225 public int registerCounts (String className, int [] counts) {
226 int classID = -1;
227 try {
228 put ( new String[] {className} , counts );
229 classID = nextClassID++;
230 } catch (Exception e) {
231 // just ignore any errors
232 System.out.println("StmtRegistry.registerCounts for "
233 + className + ", q$$q) failed - " + e);
234 }
235 return classID;
236 }
237
238 public void registerMethods (String className, String [] methods,
239 int [] endCounts) {
240 if (className == null || methods == null || endCounts == null) {
241 throw new IllegalArgumentException("null parameter");
242 }
243 methodNames.put ( className, methods );
244 methodEnds.put ( className, endCounts );
245 }
246 // VERSION ////////////////////////////////////////////
247 public int getQuiltVersion (String className) {
248 int quiltVersion = -1;
249 try {
250 Field qField = Class.forName(className).getField("q$$qVer");
251 // a trifle uncertain about the argument
252 quiltVersion = qField.getInt(qField);
253 } catch (Exception e) {
254 // just ignore any errors
255 // DEBUG
256 System.out.println("StmtRegistry.getClassID("
257 + className + ") failed - " + e);
258 // END
259 }
260 return quiltVersion;
261 }
262 // EPHEMERA ///////////////////////////////////////////
263 /*** Maps class name to temporary data structure */
264 private Map ephemera = new Hashtable(); // key className
265
266 /*** get reference to temporary data for class */
267 Ephemera getEphemera (String className) {
268 if (ephemera.containsKey(className)) {
269 return (Ephemera) ephemera.get(className);
270 } else {
271 return null;
272 }
273 }
274 /*** add temporary data for class to registry */
275 boolean putEphemera (String className, Ephemera eph) {
276 if (ephemera.containsKey(className)) {
277 return false; // operation failed
278 }
279 // XXX Should allow for failure
280 ephemera.put(className, eph);
281 return true;
282 }
283 /***
284 * Remove the reference from the registry.
285 *
286 * @param className Name of the class we are storing information about
287 * @return The reference or null if none was found or null was the
288 * stored value.
289 */
290 Ephemera removeEphemera (String className) {
291 return (Ephemera) ephemera.remove(className);
292 }
293 // EXPERIMENT ///////////////////////////////////////////////////
294 /*** STUB */
295 public int registerClass(String name, org.quilt.cover.stmt.QIC junk) {
296 System.out.println(
297 "**************************"
298 + "\nQCL.registerClass " + name
299 + "\n**************************" );
300 return 52; // <=================================
301 }
302
303 }
This page was automatically generated by Maven