1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18 package org.apache.jdo.impl.enhancer.util;
19
20 import java.util.Collection;
21 import java.util.Iterator;
22 import java.util.Enumeration;
23 import java.util.List;
24
25 import java.io.PrintWriter;
26 import java.io.StringWriter;
27 import java.io.IOException;
28 import java.io.DataInputStream;
29
30 import org.apache.jdo.impl.enhancer.EnhancerFatalError;
31 import org.apache.jdo.impl.enhancer.JdoMetaMain;
32 import org.apache.jdo.impl.enhancer.classfile.ClassFile;
33 import org.apache.jdo.impl.enhancer.classfile.ClassMethod;
34 import org.apache.jdo.impl.enhancer.classfile.CodeAttribute;
35 import org.apache.jdo.impl.enhancer.classfile.ConstClass;
36 import org.apache.jdo.impl.enhancer.classfile.ConstFieldRef;
37 import org.apache.jdo.impl.enhancer.classfile.ConstMethodRef;
38 import org.apache.jdo.impl.enhancer.classfile.ConstNameAndType;
39 import org.apache.jdo.impl.enhancer.classfile.Descriptor;
40 import org.apache.jdo.impl.enhancer.classfile.Insn;
41 import org.apache.jdo.impl.enhancer.classfile.InsnConstOp;
42 import org.apache.jdo.impl.enhancer.classfile.VMConstants;
43 import org.apache.jdo.impl.enhancer.meta.EnhancerMetaData;
44 import org.apache.jdo.impl.enhancer.meta.EnhancerMetaDataFatalError;
45 import org.apache.jdo.impl.enhancer.meta.EnhancerMetaDataUserException;
46
47
48
49
50
51 /***
52 * Utility class for testing a class file for correct annotation.
53 *
54 * @author Martin Zaun
55 */
56 public class AnnotationTest
57 extends JdoMetaMain
58 {
59
60 static public final int AFFIRMATIVE = 1;
61 static public final int NEGATIVE = 0;
62 static public final int ERROR = -1;
63
64
65
66 private boolean verbose;
67 private String className;
68 private String classFileName;
69 private ClassFile classFile;
70
71 public AnnotationTest(PrintWriter out,
72 PrintWriter err)
73 {
74 super(out, err);
75 }
76
77 private int checkGetPutField(PrintWriter out,
78 Insn insn,
79 boolean jdoMethod)
80 throws EnhancerMetaDataUserException, EnhancerMetaDataFatalError
81 {
82
83 final InsnConstOp fieldInsn = (InsnConstOp)insn;
84 final ConstFieldRef fieldRef = (ConstFieldRef)fieldInsn.value();
85 final ConstClass declClass = fieldRef.className();
86 final String declClassName = declClass.asString();
87 final ConstNameAndType fieldNameAndType = fieldRef.nameAndType();
88 final String fieldName = fieldNameAndType.name().asString();
89 final String fieldType = fieldNameAndType.signature().asString();
90
91
92 final int res;
93 if (jdoMeta.isKnownNonManagedField(declClassName,
94 fieldName, fieldType)) {
95 if (false) {
96 out.println(" --- unannotated field access: "
97 + declClassName + "." + fieldName);
98 }
99 res = NEGATIVE;
100 } else if (jdoMethod) {
101 if (false) {
102 out.println(" --- unannotated field access: "
103 + declClassName + "." + fieldName);
104 }
105 res = NEGATIVE;
106 } else if (jdoMeta.isPersistenceCapableClass(declClassName)
107 && (fieldName.equals("jdoStateManager")
108 || fieldName.equals("jdoFlags"))) {
109 if (false) {
110 out.println(" --- unannotated field access: "
111 + declClassName + "." + fieldName);
112 }
113 res = NEGATIVE;
114 } else {
115 out.println(" !!! ERROR: missing annotation of field access: "
116 + declClassName + "." + fieldName);
117 res = ERROR;
118 }
119 return res;
120 }
121
122 private int checkInvokeStatic(PrintWriter out,
123 Insn insn,
124 boolean jdoMethod)
125 throws EnhancerMetaDataUserException, EnhancerMetaDataFatalError
126 {
127
128 final InsnConstOp methodInsn = (InsnConstOp)insn;
129 final ConstMethodRef methodRef = (ConstMethodRef)methodInsn.value();
130 final ConstClass declClass = methodRef.className();
131 final String declClassName = declClass.asString();
132 final ConstNameAndType methodNameAndType = methodRef.nameAndType();
133 final String methodName = methodNameAndType.name().asString();
134 final String methodType = methodNameAndType.signature().asString();
135
136 if (!methodName.startsWith("jdoSet")
137 && (!methodName.startsWith("jdoGet")
138 || methodName.equals("jdoGetManagedFieldCount"))) {
139 return NEGATIVE;
140 }
141 final String fieldName = methodName.substring(6);
142
143 final int res;
144 final String fieldType;
145 if (methodName.startsWith("jdoGet")) {
146 fieldType = Descriptor.extractResultSig(methodType);
147 } else {
148 final String argSig = Descriptor.extractArgSig(methodType);
149 final int idx = Descriptor.nextSigElement(argSig, 0);
150 fieldType = argSig.substring(idx);
151 }
152 affirm(fieldType != null);
153
154
155 if (jdoMeta.isKnownNonManagedField(declClassName,
156 fieldName, fieldType)) {
157 out.println(" !!! ERROR: annotated access to non-managed field: "
158 + declClassName + "." + fieldName);
159 res = ERROR;
160 } else if (jdoMethod) {
161 out.println(" !!! ERROR: annotated field access in JDO method: "
162 + declClassName + "." + fieldName);
163 res = ERROR;
164 } else {
165 if (verbose) {
166 out.println(" +++ annotated field access: "
167 + declClassName + "." + fieldName);
168 }
169 res = AFFIRMATIVE;
170 }
171
172 return res;
173 }
174
175 private int hasAnnotation(PrintWriter out,
176 ClassMethod method,
177 String methodName)
178 throws EnhancerMetaDataUserException, EnhancerMetaDataFatalError
179 {
180 final CodeAttribute codeAttr = method.codeAttribute();
181
182
183 if (codeAttr == null)
184 return NEGATIVE;
185
186 int res = NEGATIVE;
187
188
189 final boolean jdoMethod
190 = ((methodName.startsWith("jdo")
191 && !(methodName.equals("jdoPreStore()")
192 || methodName.equals("jdoPreDelete()")))
193 || methodName.equals("readObject(java.io.ObjectInputStream)"));
194
195
196 final Insn firstInsn = codeAttr.theCode();
197 Insn insn = firstInsn.next();
198 while (insn != null) {
199 switch(insn.opcode()) {
200 case VMConstants.opc_getfield:
201 case VMConstants.opc_putfield: {
202 final int r = checkGetPutField(out, insn, jdoMethod);
203 if (r < NEGATIVE) {
204 res = ERROR;
205 }
206 break;
207 }
208 case VMConstants.opc_invokestatic: {
209 final int r = checkInvokeStatic(out, insn, jdoMethod);
210 if (r < NEGATIVE) {
211 res = ERROR;
212 } else if (r > NEGATIVE) {
213 if (res == NEGATIVE) {
214 res = AFFIRMATIVE;
215 }
216 }
217 break;
218 }
219 default:
220 }
221
222 insn = insn.next();
223 }
224
225 return res;
226 }
227
228 private int testAnnotation(PrintWriter out)
229 throws EnhancerMetaDataUserException, EnhancerMetaDataFatalError
230 {
231 affirm(ERROR < NEGATIVE && NEGATIVE < AFFIRMATIVE);
232 affirm(classFile);
233
234 int res = NEGATIVE;
235
236 Enumeration e = classFile.methods().elements();
237 while (e.hasMoreElements()) {
238 final ClassMethod method = (ClassMethod)e.nextElement();
239 final String methodSig = method.signature().asString();
240 final String methodArgs = Descriptor.userMethodArgs(methodSig);
241 final String methodName = method.name().asString() + methodArgs;
242
243
244 final StringWriter s = new StringWriter();
245 int r = hasAnnotation(new PrintWriter(s), method, methodName);
246 if (r < NEGATIVE) {
247 out.println(" !!! ERROR: incorrect annotation in: "
248 + methodName);
249 out.println(s.toString());
250 res = ERROR;
251 } else if (r == NEGATIVE) {
252 if (verbose) {
253 out.println(" --- not annotated: "
254 + methodName);
255 out.println(s.toString());
256 }
257 } else {
258 affirm(r > NEGATIVE);
259 if (verbose) {
260 out.println(" +++ has correct annotation: "
261 + methodName);
262 out.println(s.toString());
263 }
264 if (res == NEGATIVE) {
265 res = AFFIRMATIVE;
266 }
267 }
268 }
269
270 return res;
271 }
272
273 private int parseClass(PrintWriter out)
274 {
275 DataInputStream dis = null;
276 try {
277 affirm(className == null ^ classFileName == null);
278 if (className != null) {
279 dis = new DataInputStream(openClassInputStream(className));
280 } else {
281 dis = new DataInputStream(openFileInputStream(classFileName));
282 }
283 final boolean allowJDK12ClassFiles = true;
284 classFile = new ClassFile(dis, allowJDK12ClassFiles);
285
286
287 final String userClassName
288 = classFile.className().asString().replace('/', '.');
289
290 affirm(className == null || className.equals(userClassName));
291 out.println(" +++ parsed classfile");
292 } catch (ClassFormatError ex) {
293 out.println(" !!! ERROR: format error when parsing class: "
294 + className);
295 out.println(" error: " + err);
296 return ERROR;
297 } catch (IOException ex) {
298 out.println(" !!! ERROR: exception while reading class: "
299 + className);
300 out.println(" exception: " + ex);
301 return ERROR;
302 } finally {
303 closeInputStream(dis);
304 }
305
306 affirm(classFile);
307 return AFFIRMATIVE;
308 }
309
310 private int test(PrintWriter out,
311 String className,
312 String classFileName)
313 throws EnhancerMetaDataUserException, EnhancerMetaDataFatalError
314 {
315 this.className = className;
316 this.classFileName = classFileName;
317 affirm(className == null ^ classFileName == null);
318 final String name = (className != null ? className : classFileName);
319
320 if (verbose) {
321 out.println("-------------------------------------------------------------------------------");
322 out.println();
323 out.println("Test class for correct annotation: "
324 + name + " ...");
325 }
326
327
328 StringWriter s = new StringWriter();
329 if (parseClass(new PrintWriter(s)) <= NEGATIVE) {
330 out.println();
331 out.println("!!! ERROR: failed parsing class: " + name);
332 out.println(s.toString());
333 return ERROR;
334 }
335
336 if (verbose) {
337 out.println();
338 out.println("+++ parsed class: " + name);
339 out.println(s.toString());
340 }
341
342
343 s = new StringWriter();
344 final int r = testAnnotation(new PrintWriter(s));
345 if (r < NEGATIVE) {
346 out.println();
347 out.println("!!! ERROR: incorrect annotation: " + name);
348 out.println(s.toString());
349 return ERROR;
350 }
351
352 if (r == NEGATIVE) {
353 out.println();
354 out.println("--- class not annotated: " + name);
355 } else {
356 out.println();
357 out.println("+++ class annotated: " + name);
358 }
359 if (verbose) {
360 out.println(s.toString());
361 }
362
363 return r;
364 }
365
366 protected int test(PrintWriter out,
367 boolean verbose,
368 List classNames,
369 List classFileNames)
370 {
371 affirm(classNames);
372 this.verbose = verbose;
373
374 out.println();
375 out.println("AnnotationTest: Testing Classes for JDO Persistence-Capability Enhancement");
376
377 int nofFailed = 0;
378 final int all = classNames.size() + classFileNames.size();
379 for (int i = 0; i < classNames.size(); i++) {
380 if (test(out, (String)classNames.get(i), null) < NEGATIVE) {
381 nofFailed++;
382 }
383 }
384 for (int i = 0; i < classFileNames.size(); i++) {
385 if (test(out, null, (String)classFileNames.get(i)) < NEGATIVE) {
386 nofFailed++;
387 }
388 }
389 final int nofPassed = all - nofFailed;
390
391 out.println();
392 out.println("AnnotationTest: Summary: TESTED: " + all
393 + " PASSED: " + nofPassed
394 + " FAILED: " + nofFailed);
395 return nofFailed;
396 }
397
398
399
400 /***
401 * Run the annotation test.
402 */
403 protected int process()
404 {
405
406 return test(out, options.verbose.value,
407 options.classNames, options.classFileNames);
408 }
409
410 static public void main(String[] args)
411 {
412 final PrintWriter out = new PrintWriter(System.out, true);
413 out.println("--> AnnotationTest.main()");
414 final AnnotationTest main = new AnnotationTest(out, out);
415 int res = main.run(args);
416 out.println("<-- AnnotationTest.main(): exit = " + res);
417 System.exit(res);
418 }
419 }