1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.apache.jdo.impl.enhancer.core;
18
19 import java.util.Collection;
20 import java.util.Enumeration;
21 import java.util.Iterator;
22 import java.util.Arrays;
23 import java.util.Set;
24 import java.util.HashSet;
25 import java.util.Map;
26 import java.util.HashMap;
27
28 import org.apache.jdo.impl.enhancer.classfile.ClassAttribute;
29 import org.apache.jdo.impl.enhancer.classfile.ClassField;
30 import org.apache.jdo.impl.enhancer.classfile.ClassFile;
31 import org.apache.jdo.impl.enhancer.classfile.ClassMethod;
32 import org.apache.jdo.impl.enhancer.classfile.ConstClass;
33 import org.apache.jdo.impl.enhancer.classfile.ConstantPool;
34 import org.apache.jdo.impl.enhancer.classfile.GenericAttribute;
35 import org.apache.jdo.impl.enhancer.meta.EnhancerMetaData;
36 import org.apache.jdo.impl.enhancer.util.Support;
37
38
39
40
41
42 /***
43 * Analyzes a class for enhancement.
44 */
45 final class Analyzer
46 extends Support
47 implements JDOConstants, EnhancerConstants
48 {
49 /***
50 * The class is not to be modified by the enahncer.
51 */
52 static public final int CC_Unenhancable = -2;
53
54 /***
55 * The class is detected to be enhanced already and is not to be modifed.
56 */
57 static public final int CC_PreviouslyEnhanced = -1;
58
59 /***
60 * The enhancement status of the class hasn't been determined yet.
61 */
62 static public final int CC_PersistenceUnknown = 0;
63
64 /***
65 * The class is to be enhanced for persistence-awareness.
66 */
67 static public final int CC_PersistenceAware = 1;
68
69 /***
70 * The class is to be enhanced for specific persistence-capability
71 * (class does extend another persistence-capable class).
72 */
73 static public final int CC_PersistenceCapable = 2;
74
75 /***
76 * The class is to be enhanced for generic and specific
77 * persistence-capability (class does not extend another
78 * persistence-capable class).
79 */
80 static public final int CC_PersistenceCapableRoot = 3;
81
82 /***
83 * The names of the jdo fields of persistene-capable classes.
84 */
85 static private final Set jdoFieldNames = new HashSet();
86 static
87 {
88 jdoFieldNames.add(JDO_PC_jdoStateManager_Name);
89 jdoFieldNames.add(JDO_PC_jdoFlags_Name);
90 jdoFieldNames.add(JDO_PC_jdoInheritedFieldCount_Name);
91 jdoFieldNames.add(JDO_PC_jdoFieldNames_Name);
92 jdoFieldNames.add(JDO_PC_jdoFieldTypes_Name);
93 jdoFieldNames.add(JDO_PC_jdoFieldFlags_Name);
94 jdoFieldNames.add(JDO_PC_jdoPersistenceCapableSuperclass_Name);
95 }
96
97 /***
98 * The classfile's enhancement controller.
99 */
100 private final Controller control;
101
102 /***
103 * The classfile to be enhanced.
104 */
105 private final ClassFile classFile;
106
107 /***
108 * The class name in VM form.
109 */
110 private final String className;
111
112 /***
113 * The class name in user ('.' delimited) form.
114 */
115 private final String userClassName;
116
117 /***
118 * The classfile's constant pool.
119 */
120 private final ConstantPool pool;
121
122 /***
123 * Repository for the enhancement options.
124 */
125 private final Environment env;
126
127 /***
128 * Repository for JDO meta-data on classes.
129 */
130 private final EnhancerMetaData meta;
131
132 /***
133 * What type of class is this with respect to persistence.
134 */
135 private int persistenceType = CC_PersistenceUnknown;
136
137 /***
138 * The name of the persistence-capable superclass if defined.
139 */
140 private String pcSuperClassName;
141
142 /***
143 * The name of the persistence-capable rootclass if defined.
144 */
145 private String pcRootClassName;
146
147 /***
148 * The name of this class or the next persistence-capable superclass
149 * that owns a key class, or the PC rootclass if none defines a key class.
150 */
151 private String pcKeyOwnerClassName;
152
153 /***
154 * The name next persistence-capable superclass that owns a key class,
155 * or the PC rootclass if none defines a key class.
156 */
157 private String pcSuperKeyOwnerClassName;
158
159 /***
160 * The name of the key class if defined.
161 */
162 private String keyClassName;
163
164 /***
165 * The name of the key class of the next persistence-capable superclass
166 * that defines one.
167 */
168 private String superKeyClassName;
169
170 /***
171 * The number of key fields.
172 */
173 private int keyFieldCount;
174
175 /***
176 * The indexes of all key fields.
177 */
178 private int[] keyFieldIndexes;
179
180 /***
181 * The number of managed fields.
182 */
183 private int managedFieldCount;
184
185 /***
186 * The number of annotated fields.
187 */
188 private int annotatedFieldCount;
189
190 /***
191 * The names of all annotated fields sorted by relative field index.
192 */
193 private String[] annotatedFieldNames;
194
195 /***
196 * The type names of all annotated fields sorted by relative field index.
197 */
198 private String[] annotatedFieldSigs;
199
200 /***
201 * The java access modifiers of all annotated fields sorted by relative
202 * field index.
203 */
204 private int[] annotatedFieldMods;
205
206 /***
207 * The jdo flags of all annotated fields sorted by relative field index.
208 */
209 private int[] annotatedFieldFlags;
210
211 /***
212 * The map of found JDO fields.
213 */
214 private final Map jdoLikeFields = new HashMap(20);
215
216 /***
217 * The map of found JDO methods
218 */
219 private final Map jdoLikeMethods = new HashMap(50);
220
221 /***
222 * The map of found JDO methods
223 */
224 private final Map annotatableMethods = new HashMap(100);
225
226 /***
227 * True if a jdo member has been seen in this class.
228 */
229 private boolean hasImplementsPC = false;
230 private boolean hasGenericJDOFields = false;
231 private boolean hasGenericJDOMethods = false;
232 private boolean hasGenericJDOMembers = false;
233 private boolean hasSpecificJDOFields = false;
234 private boolean hasSpecificJDOMethods = false;
235 private boolean hasSpecificJDOMembers = false;
236 private boolean hasCallbackJDOMethods = false;
237 private boolean hasJDOMembers = false;
238
239 /***
240 * True if the class has a default (no-argument) constructor.
241 */
242 private boolean hasDefaultConstructor = false;
243
244
245
246 /***
247 * True if the class has a static initializer block.
248 */
249 private boolean hasStaticInitializer = false;
250
251 /***
252 * True if the class has a clone() method.
253 */
254 private boolean hasCloneMethod = false;
255
256 /***
257 * True if the class has a writeObject(java.io.ObjectOutputStream) method.
258 */
259 private boolean hasWriteObjectMethod = false;
260
261 /***
262 * True if the class has a writeReplace() method.
263 */
264 private boolean hasWriteReplaceMethod = false;
265
266 /***
267 * True if the class has a readObject(java.io.ObjectInputStream) method.
268 */
269 private boolean hasReadObjectMethod = false;
270
271
272
273 /***
274 * Constructor
275 */
276 public Analyzer(Controller control,
277 Environment env)
278 {
279 affirm(control != null);
280 affirm(env != null);
281
282 this.control = control;
283 this.classFile = control.getClassFile();
284 this.className = classFile.classNameString();
285 this.userClassName = classFile.userClassName();
286 this.pool = classFile.pool();
287 this.env = env;
288 this.meta = env.getEnhancerMetaData();
289
290 affirm(classFile != null);
291 affirm(className != null);
292 affirm(userClassName != null);
293 affirm(pool != null);
294 affirm(meta != null);
295 }
296
297 /***
298 * Returns the class file which we are operating on.
299 */
300 public ClassFile getClassFile()
301 {
302 return classFile;
303 }
304
305 /***
306 * Return the persistence type for this class
307 */
308 public int getPersistenceType()
309 {
310 return persistenceType;
311 }
312
313 /***
314 * Returns true if the class has been analyzed already.
315 */
316 public boolean isAnalyzed()
317 {
318 return (persistenceType != CC_PersistenceUnknown);
319 }
320
321 /***
322 * Returns true if the class is one which should be a candidate for
323 * annotation.
324 */
325 public boolean isAnnotateable()
326 {
327 return (persistenceType >= CC_PersistenceUnknown);
328 }
329
330 /***
331 * Returns true if the class is to be enhanced for persistence-capability.
332 */
333 public boolean isAugmentable()
334 {
335 return (persistenceType >= CC_PersistenceCapable);
336 }
337
338 /***
339 * Returns true if the class is to be enhanced as least-derived,
340 * persistence-capable class.
341 */
342 public boolean isAugmentableAsRoot()
343 {
344 return (persistenceType >= CC_PersistenceCapableRoot);
345 }
346
347 /***
348 * Returns the methods that are candidates for annotation.
349 */
350 public Collection getAnnotatableMethods()
351 {
352 return annotatableMethods.values();
353 }
354
355 /***
356 * Returns the name of the persistence-capable superclass if defined.
357 */
358 public String getPCSuperClassName()
359 {
360 return pcSuperClassName;
361 }
362
363 /***
364 * Returns the name of the persistence-capable rootclass if defined.
365 */
366 public String getPCRootClassName()
367 {
368 return pcRootClassName;
369 }
370
371 /***
372 * Returns the name of this class or the next persistence-capable
373 * superclass that owns a key class.
374 */
375 public String getPCKeyOwnerClassName()
376 {
377 return pcKeyOwnerClassName;
378 }
379
380 /***
381 * Returns the name of this class or the next persistence-capable
382 * that owns a key class.
383 */
384 public String getPCSuperKeyOwnerClassName()
385 {
386 return pcSuperKeyOwnerClassName;
387 }
388
389 /***
390 * Returns the name of the key class if defined.
391 */
392 public String getKeyClassName()
393 {
394 return keyClassName;
395 }
396
397 /***
398 * Returns the name of the key class of the next persistence-capable
399 * superclass that defines one.
400 */
401 public String getSuperKeyClassName()
402 {
403 return superKeyClassName;
404 }
405
406 /***
407 * Returns the number of key field.
408 */
409 public int getKeyFieldCount()
410 {
411 return keyFieldCount;
412 }
413
414 /***
415 * Returns the names of the key fields.
416 */
417 public int[] getKeyFieldIndexes()
418 {
419 return keyFieldIndexes;
420 }
421
422 /***
423 * Returns the number of managed field.
424 */
425 public int getManagedFieldCount()
426 {
427 return managedFieldCount;
428 }
429
430 /***
431 * Returns the number of annotated field.
432 */
433 public int getAnnotatedFieldCount()
434 {
435 return annotatedFieldCount;
436 }
437
438 /***
439 * Returns the names of the annotated fields.
440 */
441 public String[] getAnnotatedFieldNames()
442 {
443 return annotatedFieldNames;
444 }
445
446 /***
447 * Returns the types names of the annotated fields.
448 */
449 public String[] getAnnotatedFieldSigs()
450 {
451 return annotatedFieldSigs;
452 }
453
454 /***
455 * Returns the Java access modifiers of the annotated fields.
456 */
457 public int[] getAnnotatedFieldMods()
458 {
459 return annotatedFieldMods;
460 }
461
462 /***
463 * Returns the JDO flags of the annotated fields.
464 */
465 public int[] getAnnotatedFieldFlags()
466 {
467 return annotatedFieldFlags;
468 }
469
470 /***
471 * Returns true if the class has a default (no-argument) constructor.
472 */
473 public boolean hasDefaultConstructor()
474 {
475 return hasDefaultConstructor;
476 }
477
478 /***
479 * Returns true if the class has a static initializer block.
480 */
481 public boolean hasStaticInitializer()
482 {
483 return hasStaticInitializer;
484 }
485
486 /***
487 * Returns true if the class has a clone() method.
488 */
489 public boolean hasCloneMethod()
490 {
491 return hasCloneMethod;
492 }
493
494 /***
495 * Returns true if the class has a writeObject() method.
496 */
497 public boolean hasWriteObjectMethod()
498 {
499 return hasWriteObjectMethod;
500 }
501
502 /***
503 * Returns true if the class has a writeReplace() method.
504 */
505 public boolean hasWriteReplaceMethod()
506 {
507 return hasWriteReplaceMethod;
508 }
509
510 /***
511 * Returns true if the class has a readObject() method.
512 */
513 public boolean hasReadObjectMethod()
514 {
515 return hasReadObjectMethod;
516 }
517
518 /***
519 * Returns true if the class already provides the JDO augmentation.
520 */
521 public boolean hasJDOAugmentation()
522 {
523 return hasJDOMembers;
524 }
525
526
527
528 /***
529 * Analyzes the class for existing augmentation.
530 */
531 public void scan()
532 {
533 env.message("scanning class " + userClassName);
534
535
536 checkForEnhancedAttribute();
537 if (!isAnnotateable()) {
538 return;
539 }
540
541
542 initPersistenceType();
543 if (!isAnnotateable()) {
544 return;
545 }
546 affirm(persistenceType > CC_Unenhancable);
547
548 scanFields();
549 scanMethods();
550
551 if (isAugmentable()) {
552 checkPCFeasibility();
553 checkSpecificAugmentation();
554 checkCallbackAugmentation();
555
556 if (isAugmentableAsRoot()) {
557 checkGenericAugmentation();
558 }
559 }
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580 }
581
582 /***
583 * Scans the attributes of a ClassFile
584 */
585 private void checkForEnhancedAttribute()
586 {
587 for (Enumeration e = classFile.attributes().elements();
588 e.hasMoreElements();) {
589 final ClassAttribute attr = (ClassAttribute)e.nextElement();
590 final String attrName = attr.attrName().asString();
591 if (SUNJDO_PC_EnhancedAttribute.equals(attrName)) {
592 persistenceType = CC_PreviouslyEnhanced;
593
594
595
596 env.message("ignoring previously enhanced class "
597 + userClassName);
598 return;
599 }
600 }
601 }
602
603
604
605 /***
606 * Sets the persistence type of a class according to JDO metadata.
607 */
608 private void initPersistenceType()
609 {
610 affirm(persistenceType == CC_PersistenceUnknown);
611
612
613 final EnhancerMetaData meta = env.getEnhancerMetaData();
614 if (meta.isKnownUnenhancableClass(className)) {
615 persistenceType = CC_Unenhancable;
616 return;
617 }
618
619
620 if (meta.isPersistenceCapableClass(className)) {
621 pcSuperClassName
622 = meta.getPersistenceCapableSuperClass(className);
623 pcRootClassName
624 = meta.getPersistenceCapableRootClass(className);
625 affirm(pcSuperClassName == null || pcRootClassName != null);
626
627 persistenceType
628 = (pcSuperClassName == null
629 ? CC_PersistenceCapableRoot
630 : CC_PersistenceCapable);
631
632
633 affirm(!classFile.isInterface());
634
635
636
637
638 final ConstClass superConstClass = classFile.superName();
639 affirm(superConstClass != null);
640
641
642 affirm(pcSuperClassName == null
643 || !superConstClass.asString().equals("java/lang/Object"));
644
645
646 pcKeyOwnerClassName = className;
647 while (meta.getKeyClass(pcKeyOwnerClassName) == null) {
648 final String pcSuperClassName
649 = meta.getPersistenceCapableSuperClass(
650 pcKeyOwnerClassName);
651 if (pcSuperClassName == null)
652 break;
653 pcKeyOwnerClassName = pcSuperClassName;
654 }
655 affirm(pcKeyOwnerClassName != null);
656
657
658 pcSuperKeyOwnerClassName = pcSuperClassName;
659 if (pcSuperKeyOwnerClassName != null) {
660 while (meta.getKeyClass(pcSuperKeyOwnerClassName) == null) {
661 final String pcSuperClassName
662 = meta.getPersistenceCapableSuperClass(
663 pcSuperKeyOwnerClassName);
664 if (pcSuperClassName == null)
665 break;
666 pcSuperKeyOwnerClassName = pcSuperClassName;
667 }
668 affirm(pcKeyOwnerClassName != null);
669 }
670
671 keyClassName
672 = meta.getKeyClass(className);
673 superKeyClassName
674 = meta.getSuperKeyClass(className);
675 affirm(superKeyClassName == null || pcSuperClassName != null);
676 }
677 }
678
679 /***
680 * Scans the fields.
681 */
682 private void scanFields()
683 {
684
685 final Map annotatedFieldMap = new HashMap();
686
687 if (isAugmentable()) {
688
689 for (final Enumeration e = classFile.fields().elements();
690 e.hasMoreElements();) {
691 final ClassField field = (ClassField)e.nextElement();
692 final String name = field.name().asString();
693 final String sig = field.signature().asString();
694
695
696 if (jdoFieldNames.contains(name)) {
697 continue;
698 }
699
700
701 if (field.isStatic()) {
702 continue;
703 }
704
705
706 if (meta.isKnownNonManagedField(className, name, sig)) {
707 continue;
708 }
709
710
711 Object obj = annotatedFieldMap.put(name, field);
712 affirm(obj == null,
713 ("Error in classfile: repeated declaration of field: "
714 + userClassName + "." + name));
715
716
717 if (field.isFinal()
718 || field.isTransient()) {
719 continue;
720 }
721
722 if (false) {
723 System.out.println("Analyzer.scanFields(): declaring "
724 + className + "." + name + " : " + sig);
725 }
726 meta.declareField(className, name, sig);
727 }
728 }
729
730
731 annotatedFieldCount = annotatedFieldMap.size();
732
733
734 final String[] managedFieldNames = meta.getManagedFields(className);
735 affirm(managedFieldNames != null);
736 managedFieldCount = managedFieldNames.length;
737 final Set managedFieldNamesSet
738 = new HashSet(Arrays.asList(managedFieldNames));
739 affirm(managedFieldNamesSet.size() == managedFieldCount,
740 "JDO metadata: returned duplicate managed fields.");
741 affirm(managedFieldCount <= annotatedFieldCount,
742 "JDO metadata: managed fields exceed annotated fields.");
743
744
745 final String[] keyFieldNames = meta.getKeyFields(className);
746 affirm(keyFieldNames != null);
747 keyFieldCount = keyFieldNames.length;
748 affirm(keyFieldCount == 0 || keyClassName != null,
749 "JDO metadata: returned key fields but no key class.");
750 final Set keyFieldNamesSet
751 = new HashSet(Arrays.asList(keyFieldNames));
752 affirm(keyFieldNamesSet.size() == keyFieldCount,
753 "JDO metadata: returned duplicate key fields.");
754 affirm(keyFieldCount <= managedFieldCount,
755 "JDO metadata: key fields exceed managed fields.");
756
757
758 for (final Enumeration e = classFile.fields().elements();
759 e.hasMoreElements();) {
760 final ClassField field = (ClassField)e.nextElement();
761 final String name = field.name().asString();
762 final String sig = field.signature().asString();
763 final String userFieldName = userClassName + "." + name;
764
765 if (false) {
766 System.out.println("Analyzer.scanFields(): scanning "
767 + className + "." + name + " : " + sig);
768 }
769
770
771 if (name.startsWith("jdo")) {
772 final Object f = jdoLikeFields.put(name, field);
773 affirm(f == null);
774 }
775
776
777 if (!managedFieldNamesSet.contains(name)) {
778 affirm(!meta.isManagedField(className, name));
779
780
781 affirm(!keyFieldNamesSet.contains(name),
782 ("JDO metadata: reported the field " + userFieldName
783 + " to be non-managed but key."));
784 continue;
785 }
786 affirm(meta.isManagedField(className, name));
787
788
789 affirm(!field.isStatic(),
790 ("JDO metadata: reported the field " + userFieldName
791 + " to be managed though it's static."));
792
793
794 affirm(!field.isFinal(),
795 ("JDO metadata: reported the field " + userFieldName
796 + " to be managed though it's final."));
797
798
799
800
801
802
803
804
805
806
807
808 }
809
810
811 final int[] managedFieldFlags
812 = meta.getFieldFlags(className, managedFieldNames);
813
814
815
816 int j = 0;
817 keyFieldIndexes = new int[keyFieldCount];
818 final String[] managedFieldSigs = new String[managedFieldCount];
819 final int[] managedFieldMods = new int[managedFieldCount];
820 for (int i = 0; i < managedFieldCount; i++) {
821 final String name = managedFieldNames[i];
822 affirm(name != null);
823
824
825 final ClassField field = (ClassField)annotatedFieldMap.get(name);
826 affirm(field != null,
827 ("The managed field " + userClassName + "." + name +
828 " is not declared by the class."));
829 affirm(!field.isStatic(),
830 ("The managed field " + userClassName + "." + name +
831 " is static."));
832 affirm(!field.isFinal(),
833 ("The managed field " + userClassName + "." + name +
834 " is final."));
835
836
837 annotatedFieldMap.remove(name);
838
839
840 if (keyFieldNamesSet.contains(name)) {
841 affirm(meta.isKeyField(className, name));
842 keyFieldIndexes[j++] = i;
843 }
844
845
846 managedFieldSigs[i] = field.signature().asString();
847 managedFieldMods[i] = field.access();
848
849
850
851
852 if (!field.isTransient()) {
853 managedFieldFlags[i] |= EnhancerMetaData.SERIALIZABLE;
854 }
855
856 if (false) {
857 System.out.println("managed field: "
858 + className + "." + name + " : {");
859 System.out.println(" sigs = " + managedFieldSigs[i]);
860 System.out.println(" mods = "
861 + Integer.toHexString(managedFieldMods[i]));
862 System.out.println(" flags = "
863 + Integer.toHexString(managedFieldFlags[i]));
864 }
865 }
866
867
868 affirm(keyFieldIndexes.length == keyFieldCount);
869 affirm(keyFieldCount <= managedFieldCount);
870 affirm(managedFieldNames.length == managedFieldCount);
871 affirm(managedFieldSigs.length == managedFieldCount);
872 affirm(managedFieldMods.length == managedFieldCount);
873 affirm(managedFieldFlags.length == managedFieldCount);
874 affirm(managedFieldCount <= annotatedFieldCount);
875
876
877 if (managedFieldCount == annotatedFieldCount) {
878
879 annotatedFieldNames = managedFieldNames;
880 annotatedFieldSigs = managedFieldSigs;
881 annotatedFieldMods = managedFieldMods;
882 annotatedFieldFlags = managedFieldFlags;
883 } else {
884
885 annotatedFieldNames = new String[annotatedFieldCount];
886 annotatedFieldSigs = new String[annotatedFieldCount];
887 annotatedFieldMods = new int[annotatedFieldCount];
888 annotatedFieldFlags = new int[annotatedFieldCount];
889 int i = managedFieldCount;
890 System.arraycopy(managedFieldNames, 0, annotatedFieldNames, 0, i);
891 System.arraycopy(managedFieldSigs, 0, annotatedFieldSigs, 0, i);
892 System.arraycopy(managedFieldMods, 0, annotatedFieldMods, 0, i);
893 System.arraycopy(managedFieldFlags, 0, annotatedFieldFlags, 0, i);
894
895
896 for (Iterator k = annotatedFieldMap.entrySet().iterator();
897 k.hasNext();) {
898 final Map.Entry entry = (Map.Entry)k.next();
899 final String name = (String)entry.getKey();
900 final ClassField field = (ClassField)entry.getValue();
901 affirm(name.equals(field.name().asString()));
902
903 affirm(!field.isStatic(),
904 ("The managed field " + userClassName + "." + name +
905 " is static."));
906
907
908 annotatedFieldNames[i] = name;
909 annotatedFieldSigs[i] = field.signature().asString();
910 annotatedFieldMods[i] = field.access();
911 annotatedFieldFlags[i] = 0x0;
912 i++;
913 }
914 affirm(i == annotatedFieldCount);
915 }
916
917
918 affirm(keyFieldIndexes.length == keyFieldCount);
919 affirm(keyFieldCount <= managedFieldCount);
920 affirm(annotatedFieldNames.length == annotatedFieldCount);
921 affirm(annotatedFieldSigs.length == annotatedFieldCount);
922 affirm(annotatedFieldMods.length == annotatedFieldCount);
923 affirm(annotatedFieldFlags.length == annotatedFieldCount);
924 affirm(managedFieldCount <= annotatedFieldCount);
925 }
926
927 /***
928 * Scans the methods of a ClassFile.
929 */
930 private void scanMethods()
931 {
932
933 for (final Enumeration e = classFile.methods().elements();
934 e.hasMoreElements();) {
935 final ClassMethod method = (ClassMethod)e.nextElement();
936 final String name = method.name().asString();
937 final String sig = method.signature().asString();
938 affirm(name != null);
939 affirm(sig != null);
940
941 final String key = methodKey(name, sig);
942 affirm(key != null);
943
944
945 if (!method.isAbstract() && !method.isNative()) {
946 final Object m = annotatableMethods.put(key, method);
947 affirm(m == null);
948 }
949
950
951 if (name.startsWith("jdo")) {
952 final Object m = jdoLikeMethods.put(key, method);
953 affirm(m == null);
954 continue;
955 }
956
957
958 if (name.equals(NameHelper.constructorName())
959 && sig.equals(NameHelper.constructorSig())) {
960 hasDefaultConstructor = true;
961 continue;
962 }
963
964
965 if (name.equals(JAVA_clinit_Name)
966 && sig.equals(JAVA_clinit_Sig)) {
967 hasStaticInitializer = true;
968 continue;
969 }
970
971
972 if (name.equals(JAVA_Object_clone_Name)
973 && sig.equals(JAVA_Object_clone_Sig)) {
974 hasCloneMethod = true;
975 continue;
976 }
977
978
979 if (name.equals(JAVA_Object_writeObject_Name)
980 && sig.equals(JAVA_Object_writeObject_Sig)) {
981 hasWriteObjectMethod = true;
982 continue;
983 }
984
985
986 if (name.equals(JAVA_Object_writeReplace_Name)
987 && sig.equals(JAVA_Object_writeReplace_Sig)) {
988 hasWriteReplaceMethod = true;
989 continue;
990 }
991
992
993 if (name.equals(JAVA_Object_readObject_Name)
994 && sig.equals(JAVA_Object_readObject_Sig)) {
995 hasReadObjectMethod = true;
996
997
998 Object m = annotatableMethods.remove(key);
999 affirm(m != null);
1000 continue;
1001 }
1002 }
1003
1004
1005 if (hasDefaultConstructor) {
1006 env.message(getI18N("enhancer.class_has_default_constructor"));
1007 } else {
1008 env.message(getI18N("enhancer.class_has_not_default_constructor"));
1009 }
1010
1011
1012 if (hasStaticInitializer) {
1013 env.message(getI18N("enhancer.class_has_static_initializer"));
1014 } else {
1015 env.message(getI18N("enhancer.class_has_not_static_initializer"));
1016 }
1017
1018
1019 if (hasCloneMethod) {
1020 env.message(getI18N("enhancer.class_has_clone_method"));
1021 } else {
1022 env.message(getI18N("enhancer.class_has_not_clone_method"));
1023 }
1024
1025
1026 if (hasWriteObjectMethod) {
1027 env.message(getI18N("enhancer.class_has_writeObject_method"));
1028 } else {
1029 env.message(getI18N("enhancer.class_has_not_writeObject_method"));
1030 }
1031
1032
1033 if (hasWriteReplaceMethod) {
1034 env.message(getI18N("enhancer.class_has_writeReplace_method"));
1035 } else {
1036 env.message(getI18N("enhancer.class_has_not_writeReplace_method"));
1037 }
1038
1039
1040 if (hasReadObjectMethod) {
1041 env.message(getI18N("enhancer.class_has_readObject_method"));
1042 } else {
1043 env.message(getI18N("enhancer.class_has_not_readObject_method"));
1044 }
1045 }
1046
1047 private void checkGenericAugmentation()
1048 {
1049 scanForImplementsPC();
1050 scanForGenericJDOFields();
1051 scanForGenericJDOMethods();
1052
1053 final boolean all
1054 = (hasImplementsPC && hasGenericJDOFields && hasGenericJDOMethods);
1055
1056 final boolean none
1057 = !(hasImplementsPC
1058 || hasGenericJDOFields || hasGenericJDOMethods);
1059
1060 if (all ^ none) {
1061 hasGenericJDOMembers = hasImplementsPC;
1062 env.message(
1063 getI18N("enhancer.class_has_generic_jdo_members",
1064 String.valueOf(hasGenericJDOMembers)));
1065
1066
1067
1068 return;
1069 }
1070
1071 final String key
1072 = "enhancer.class_has_inconsistently_declared_jdo_members";
1073 if (hasGenericJDOFields && !hasGenericJDOMethods) {
1074 env.error(
1075 getI18N(key,
1076 userClassName,
1077 "<generic jdo fields>",
1078 "<generic jdo methods>"));
1079 } else if (!hasGenericJDOFields && hasGenericJDOMethods) {
1080 env.error(
1081 getI18N(key,
1082 userClassName,
1083 "<generic jdo methods>",
1084 "<generic jdo fields>"));
1085 } else if (!hasGenericJDOFields && !hasGenericJDOMethods) {
1086 env.error(
1087 getI18N(key,
1088 userClassName,
1089 "<implements " + JDO_PersistenceCapable_Name + ">",
1090 "<generic jdo members>"));
1091 } else {
1092 env.error(
1093 getI18N(key,
1094 userClassName,
1095 "<generic jdo members>",
1096 "<implements " + JDO_PersistenceCapable_Name + ">"));
1097 }
1098 }
1099
1100 private void checkSpecificAugmentation()
1101 {
1102 scanForSpecificJDOFields();
1103 scanForSpecificJDOMethods();
1104
1105 final boolean all
1106 = (hasSpecificJDOFields && hasSpecificJDOMethods);
1107
1108 final boolean none
1109 = !(hasSpecificJDOFields || hasSpecificJDOMethods);
1110
1111 if (all ^ none) {
1112 hasSpecificJDOMembers = hasSpecificJDOFields;
1113 env.message(
1114 getI18N("enhancer.class_has_specific_jdo_members",
1115 String.valueOf(hasSpecificJDOMembers)));
1116 return;
1117 }
1118
1119 final String key
1120 = "enhancer.class_has_inconsistently_declared_jdo_members";
1121 if (hasSpecificJDOFields && !hasSpecificJDOMethods) {
1122 env.error(
1123 getI18N(key,
1124 userClassName,
1125 "<specific jdo fields>",
1126 "<specific jdo methods>"));
1127 } else {
1128 env.error(
1129 getI18N(key,
1130 userClassName,
1131 "<specific jdo methods>",
1132 "<specific jdo fields>"));
1133 }
1134 }
1135
1136 private void checkCallbackAugmentation()
1137 {
1138 scanForCallbackJDOMethods();
1139 env.message(
1140 getI18N("enhancer.class_has_callback_jdo_methods",
1141 String.valueOf(hasCallbackJDOMethods)));
1142 }
1143
1144 private void checkPCFeasibility()
1145 {
1146 if (!hasDefaultConstructor) {
1147 env.error(
1148 getI18N("enhancer.class_missing_default_constructor",
1149 userClassName));
1150 }
1151 }
1152
1153 /***
1154 * Scans the class for implementing the PC interface.
1155 */
1156 private void scanForImplementsPC()
1157 {
1158 hasImplementsPC = false;
1159 for (final Iterator ifc = classFile.interfaces().iterator();
1160 ifc.hasNext();) {
1161 final ConstClass i = (ConstClass)ifc.next();
1162 if (i.asString().equals(JDO_PersistenceCapable_Path)) {
1163 hasImplementsPC = true;
1164 break;
1165 }
1166 }
1167 env.message(
1168 getI18N("enhancer.class_implements_jdo_pc",
1169 String.valueOf(hasImplementsPC)));
1170 }
1171
1172 /***
1173 * Scans for JDO fields of generic augmentation.
1174 */
1175 private void scanForGenericJDOFields()
1176 {
1177
1178 if (jdoLikeFields.isEmpty()) {
1179 hasGenericJDOFields = false;
1180 env.message(
1181 getI18N("enhancer.class_has_generic_jdo_fields",
1182 String.valueOf(hasGenericJDOFields)));
1183 return;
1184 }
1185
1186
1187 final Set found = new HashSet(10);
1188 final Set missing = new HashSet(10);
1189
1190 scanJDOField(JDO_PC_jdoStateManager_Name,
1191 JDO_PC_jdoStateManager_Sig,
1192 JDO_PC_jdoStateManager_Mods,
1193 found, missing);
1194 scanJDOField(JDO_PC_jdoFlags_Name,
1195 JDO_PC_jdoFlags_Sig,
1196 JDO_PC_jdoFlags_Mods,
1197 found, missing);
1198
1199 if (found.isEmpty() ^ missing.isEmpty()) {
1200 hasGenericJDOFields = missing.isEmpty();
1201 env.message(
1202 getI18N("enhancer.class_has_generic_jdo_fields",
1203 String.valueOf(hasGenericJDOFields)));
1204 return;
1205 }
1206
1207 reportInconsistentJDOMembers(found, missing);
1208 }
1209
1210 /***
1211 * Scans for JDO methods of generic augmentation.
1212 */
1213 private void scanForGenericJDOMethods()
1214 {
1215
1216 if (jdoLikeMethods.isEmpty()) {
1217 hasGenericJDOMethods = false;
1218 env.message(
1219 getI18N("enhancer.class_has_generic_jdo_methods",
1220 String.valueOf(hasGenericJDOMethods)));
1221 return;
1222 }
1223
1224
1225 final Set found = new HashSet(30);
1226 final Set missing = new HashSet(30);
1227
1228 scanJDOMethod(JDO_PC_jdoReplaceStateManager_Name,
1229 JDO_PC_jdoReplaceStateManager_Sig,
1230 JDO_PC_jdoReplaceStateManager_Mods,
1231 found, missing);
1232 scanJDOMethod(JDO_PC_jdoReplaceFlags_Name,
1233 JDO_PC_jdoReplaceFlags_Sig,
1234 JDO_PC_jdoReplaceFlags_Mods,
1235 found, missing);
1236 scanJDOMethod(JDO_PC_jdoGetPersistenceManager_Name,
1237 JDO_PC_jdoGetPersistenceManager_Sig,
1238 JDO_PC_jdoGetPersistenceManager_Mods,
1239 found, missing);
1240 scanJDOMethod(JDO_PC_jdoGetObjectId_Name,
1241 JDO_PC_jdoGetObjectId_Sig,
1242 JDO_PC_jdoGetObjectId_Mods,
1243 found, missing);
1244 scanJDOMethod(JDO_PC_jdoGetTransactionalObjectId_Name,
1245 JDO_PC_jdoGetTransactionalObjectId_Sig,
1246 JDO_PC_jdoGetTransactionalObjectId_Mods,
1247 found, missing);
1248 scanJDOMethod(JDO_PC_jdoIsPersistent_Name,
1249 JDO_PC_jdoIsPersistent_Sig,
1250 JDO_PC_jdoIsPersistent_Mods,
1251 found, missing);
1252 scanJDOMethod(JDO_PC_jdoIsTransactional_Name,
1253 JDO_PC_jdoIsTransactional_Sig,
1254 JDO_PC_jdoIsTransactional_Mods,
1255 found, missing);
1256 scanJDOMethod(JDO_PC_jdoIsNew_Name,
1257 JDO_PC_jdoIsNew_Sig,
1258 JDO_PC_jdoIsNew_Mods,
1259 found, missing);
1260 scanJDOMethod(JDO_PC_jdoIsDeleted_Name,
1261 JDO_PC_jdoIsDeleted_Sig,
1262 JDO_PC_jdoIsDeleted_Mods,
1263 found, missing);
1264 scanJDOMethod(JDO_PC_jdoIsDirty_Name,
1265 JDO_PC_jdoIsDirty_Sig,
1266 JDO_PC_jdoIsDirty_Mods,
1267 found, missing);
1268 scanJDOMethod(JDO_PC_jdoMakeDirty_Name,
1269 JDO_PC_jdoMakeDirty_Sig,
1270 JDO_PC_jdoMakeDirty_Mods,
1271 found, missing);
1272 scanJDOMethod(JDO_PC_jdoPreSerialize_Name,
1273 JDO_PC_jdoPreSerialize_Sig,
1274 JDO_PC_jdoPreSerialize_Mods,
1275 found, missing);
1276 scanJDOMethod(JDO_PC_jdoReplaceFields_Name,
1277 JDO_PC_jdoReplaceFields_Sig,
1278 JDO_PC_jdoReplaceFields_Mods,
1279 found, missing);
1280 scanJDOMethod(JDO_PC_jdoProvideFields_Name,
1281 JDO_PC_jdoProvideFields_Sig,
1282 JDO_PC_jdoProvideFields_Mods,
1283 found, missing);
1284
1285 if (found.isEmpty() ^ missing.isEmpty()) {
1286 hasGenericJDOMethods = missing.isEmpty();
1287 env.message(
1288 getI18N("enhancer.class_has_generic_jdo_methods",
1289 String.valueOf(hasGenericJDOMethods)));
1290 return;
1291 }
1292
1293 reportInconsistentJDOMembers(found, missing);
1294 }
1295
1296 /***
1297 * Scans for JDO fields of specific augmentation.
1298 */
1299 private void scanForSpecificJDOFields()
1300 {
1301
1302 if (jdoLikeFields.isEmpty()) {
1303 hasSpecificJDOFields = false;
1304 env.message(
1305 getI18N("enhancer.class_has_specific_jdo_fields",
1306 String.valueOf(hasSpecificJDOFields)));
1307 return;
1308 }
1309
1310
1311 final Set found = new HashSet(10);
1312 final Set missing = new HashSet(10);
1313
1314 scanJDOField(JDO_PC_jdoInheritedFieldCount_Name,
1315 JDO_PC_jdoInheritedFieldCount_Sig,
1316 JDO_PC_jdoInheritedFieldCount_Mods,
1317 found, missing);
1318 scanJDOField(JDO_PC_jdoFieldNames_Name,
1319 JDO_PC_jdoFieldNames_Sig,
1320 JDO_PC_jdoFieldNames_Mods,
1321 found, missing);
1322 scanJDOField(JDO_PC_jdoFieldTypes_Name,
1323 JDO_PC_jdoFieldTypes_Sig,
1324 JDO_PC_jdoFieldTypes_Mods,
1325 found, missing);
1326 scanJDOField(JDO_PC_jdoFieldFlags_Name,
1327 JDO_PC_jdoFieldFlags_Sig,
1328 JDO_PC_jdoFieldFlags_Mods,
1329 found, missing);
1330 scanJDOField(JDO_PC_jdoPersistenceCapableSuperclass_Name,
1331 JDO_PC_jdoPersistenceCapableSuperclass_Sig,
1332 JDO_PC_jdoPersistenceCapableSuperclass_Mods,
1333 found, missing);
1334
1335 if (found.isEmpty() ^ missing.isEmpty()) {
1336 hasSpecificJDOFields = missing.isEmpty();
1337 env.message(
1338 getI18N("enhancer.class_has_specific_jdo_fields",
1339 String.valueOf(hasSpecificJDOFields)));
1340 return;
1341 }
1342
1343 reportInconsistentJDOMembers(found, missing);
1344 }
1345
1346 /***
1347 * Scans for JDO methods of specific augmentation.
1348 */
1349 private void scanForSpecificJDOMethods()
1350 {
1351
1352 if (jdoLikeMethods.isEmpty()) {
1353 hasSpecificJDOMethods = false;
1354 env.message(
1355 getI18N("enhancer.class_has_specific_jdo_methods",
1356 String.valueOf(hasSpecificJDOMethods)));
1357 return;
1358 }
1359
1360
1361 final Set found = new HashSet(30);
1362 final Set missing = new HashSet(30);
1363
1364 scanJDOMethod(JDO_PC_jdoGetManagedFieldCount_Name,
1365 JDO_PC_jdoGetManagedFieldCount_Sig,
1366 JDO_PC_jdoGetManagedFieldCount_Mods,
1367 found, missing);
1368 scanJDOMethod(JDO_PC_jdoNewInstance_Name,
1369 JDO_PC_jdoNewInstance_Sig,
1370 JDO_PC_jdoNewInstance_Mods,
1371 found, missing);
1372 scanJDOMethod(JDO_PC_jdoNewInstance_Name,
1373 JDO_PC_jdoNewInstance_Sig,
1374 JDO_PC_jdoNewInstance_Mods,
1375 found, missing);
1376 scanJDOMethod(JDO_PC_jdoNewObjectIdInstance_Name,
1377 JDO_PC_jdoNewObjectIdInstance_Sig,
1378 JDO_PC_jdoNewObjectIdInstance_Mods,
1379 found, missing);
1380 scanJDOMethod(JDO_PC_jdoNewObjectIdInstance_Name,
1381 JDO_PC_jdoNewObjectIdInstance_Sig,
1382 JDO_PC_jdoNewObjectIdInstance_Mods,
1383 found, missing);
1384 scanJDOMethod(JDO_PC_jdoCopyKeyFieldsToObjectId_Name,
1385 JDO_PC_jdoCopyKeyFieldsToObjectId_Sig,
1386 JDO_PC_jdoCopyKeyFieldsToObjectId_Mods,
1387 found, missing);
1388 scanJDOMethod(JDO_PC_jdoCopyKeyFieldsToObjectId_OIFS_Name,
1389 JDO_PC_jdoCopyKeyFieldsToObjectId_OIFS_Sig,
1390 JDO_PC_jdoCopyKeyFieldsToObjectId_OIFS_Mods,
1391 found, missing);
1392 scanJDOMethod(JDO_PC_jdoCopyKeyFieldsFromObjectId_OIFC_Name,
1393 JDO_PC_jdoCopyKeyFieldsFromObjectId_OIFC_Sig,
1394 JDO_PC_jdoCopyKeyFieldsFromObjectId_OIFC_Mods,
1395 found, missing);
1396 scanJDOMethod(JDO_PC_jdoReplaceField_Name,
1397 JDO_PC_jdoReplaceField_Sig,
1398 JDO_PC_jdoReplaceField_Mods,
1399 found, missing);
1400 scanJDOMethod(JDO_PC_jdoProvideField_Name,
1401 JDO_PC_jdoProvideField_Sig,
1402 JDO_PC_jdoProvideField_Mods,
1403 found, missing);
1404 scanJDOMethod(JDO_PC_jdoCopyFields_Name,
1405 JDO_PC_jdoCopyFields_Sig,
1406 JDO_PC_jdoCopyFields_Mods,
1407 found, missing);
1408 scanJDOMethod(JDO_PC_jdoCopyField_Name,
1409 JDONameHelper.getJDO_PC_jdoCopyField_Sig(className),
1410 JDO_PC_jdoCopyField_Mods,
1411 found, missing);
1412
1413 if (found.isEmpty() ^ missing.isEmpty()) {
1414 hasSpecificJDOMethods = missing.isEmpty();
1415 env.message(
1416 getI18N("enhancer.class_has_specific_jdo_methods",
1417 String.valueOf(hasSpecificJDOMethods)));
1418 return;
1419 }
1420
1421 reportInconsistentJDOMembers(found, missing);
1422 }
1423
1424 /***
1425 * Scans for JDO methods of generic augmentation.
1426 */
1427 private void scanForCallbackJDOMethods()
1428 {
1429
1430 if (jdoLikeMethods.isEmpty()) {
1431 hasCallbackJDOMethods = false;
1432 env.message(
1433 getI18N("enhancer.class_has_callback_jdo_methods",
1434 String.valueOf(hasCallbackJDOMethods)));
1435 return;
1436 }
1437
1438
1439 final Set found = new HashSet(30);
1440 final Set missing = new HashSet(30);
1441 final boolean annotatable = true;
1442
1443 scanJDOMethod(JDO_IC_jdoPostLoad_Name,
1444 JDO_IC_jdoPostLoad_Sig,
1445 JDO_IC_jdoPostLoad_Mods,
1446 found, missing, !annotatable);
1447
1448 scanJDOMethod(JDO_IC_jdoPreStore_Name,
1449 JDO_IC_jdoPreStore_Sig,
1450 JDO_IC_jdoPreStore_Mods,
1451 found, missing, annotatable);
1452
1453 scanJDOMethod(JDO_IC_jdoPreClear_Name,
1454 JDO_IC_jdoPreClear_Sig,
1455 JDO_IC_jdoPreClear_Mods,
1456 found, missing, !annotatable);
1457
1458 scanJDOMethod(JDO_IC_jdoPreDelete_Name,
1459 JDO_IC_jdoPreDelete_Sig,
1460 JDO_IC_jdoPreDelete_Mods,
1461 found, missing, annotatable);
1462
1463
1464 if (!found.isEmpty()) {
1465 hasCallbackJDOMethods = true;
1466 env.message(
1467 getI18N("enhancer.class_has_callback_jdo_methods",
1468 String.valueOf(hasCallbackJDOMethods)));
1469 }
1470 }
1471
1472 /***
1473 * Verifies a JDO field signature.
1474 */
1475 private void scanJDOField(String fieldName,
1476 String expectedSig,
1477 int expectedMods,
1478 Set found,
1479 Set missing)
1480 {
1481 final ClassField field = (ClassField)jdoLikeFields.get(fieldName);
1482 if (field == null) {
1483 missing.add(fieldName);
1484 return;
1485 }
1486 found.add(fieldName);
1487
1488 final String foundSig = field.signature().asString();
1489 final int foundMods = field.access();
1490 if (!expectedSig.equals(foundSig) || expectedMods != foundMods) {
1491 env.error(
1492 getI18N("enhancer.class_has_illegally_declared_jdo_member",
1493 new Object[]{ userClassName,
1494 fieldName,
1495 expectedSig,
1496 foundSig,
1497 new Integer(expectedMods),
1498 new Integer(foundMods) }));
1499 }
1500 }
1501
1502 /***
1503 * Verifies a JDO method signature.
1504 */
1505 private void scanJDOMethod(String methodName,
1506 String expectedSig,
1507 int expectedMods,
1508 Set found,
1509 Set missing)
1510 {
1511 scanJDOMethod(methodName, expectedSig, expectedMods,
1512 found, missing, true);
1513 }
1514
1515 /***
1516 * Verifies a JDO method signature.
1517 */
1518 private void scanJDOMethod(String methodName,
1519 String expectedSig,
1520 int expectedMods,
1521 Set found,
1522 Set missing,
1523 boolean annotatable)
1524 {
1525 final String key = methodKey(methodName, expectedSig);
1526 final ClassMethod method = (ClassMethod)jdoLikeMethods.get(key);
1527 if (method == null) {
1528 missing.add(key);
1529 return;
1530 }
1531 found.add(key);
1532
1533 final String foundSig = method.signature().asString();
1534 final int foundMods = method.access();
1535 if (!expectedSig.equals(foundSig) || expectedMods != foundMods) {
1536 env.error(
1537 getI18N("enhancer.class_has_illegally_declared_jdo_member",
1538 new Object[]{ userClassName,
1539 methodName,
1540 expectedSig,
1541 foundSig,
1542 new Integer(expectedMods),
1543 new Integer(foundMods) }));
1544 }
1545
1546
1547 if (!annotatable) {
1548 Object m = annotatableMethods.remove(key);
1549 affirm(m != null);
1550 }
1551 }
1552
1553 /***
1554 * Reports an error for some found/missing JDO fields or methods.
1555 */
1556 private void reportInconsistentJDOMembers(Set found,
1557 Set missing)
1558 {
1559 final Iterator fi = found.iterator();
1560 final StringBuffer f = new StringBuffer((String)fi.next());
1561 while (fi.hasNext()) {
1562 f.append(", " + fi.next());
1563 }
1564
1565 final Iterator mi = found.iterator();
1566 final StringBuffer m = new StringBuffer((String)mi.next());
1567 while (mi.hasNext()) {
1568 m.append(", " + mi.next());
1569 }
1570
1571 env.error(
1572 getI18N("enhancer.class_has_inconsistently_declared_jdo_members",
1573 userClassName, f.toString(), m.toString()));
1574 }
1575
1576
1577
1578 static private String methodKey(String name,
1579 String sig)
1580 {
1581 affirm(name != null);
1582 affirm(sig != null && sig.charAt(0) == '(' && sig.indexOf(')') > 0);
1583 final String parms = sig.substring(0, sig.indexOf(')') + 1);
1584 return (name + parms);
1585 }
1586 }