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