View Javadoc

1   /*
2    * Copyright 2005 The Apache Software Foundation.
3    * 
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at 
7    * 
8    *     http://www.apache.org/licenses/LICENSE-2.0
9    * 
10   * Unless required by applicable law or agreed to in writing, software 
11   * distributed under the License is distributed on an "AS IS" BASIS, 
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
13   * See the License for the specific language governing permissions and 
14   * limitations under the License.
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     //^olsen: performance opt.: make these fields of type ClassMethod
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         // skip previously enhanced files
536         checkForEnhancedAttribute();
537         if (!isAnnotateable()) {
538             return;
539         }
540 
541         // skip unenhancable files
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         //^olsen: check
562 /*
563         //@olsen: check whether member starts with the reserved jdo prefix
564         if (methodName.startsWith("jdo")) {
565             //@olsen: issue a warning only
566             env.warning(
567                 getI18N("enhancer.class_has_jdo_like_member",
568                         userClassName, methodName));
569             return;
570         }
571         //@olsen: check whether member starts with the reserved jdo prefix
572         if (fieldName.startsWith("jdo")) {
573             //@olsen: issue a warning only
574             env.warning(
575                 getI18N("enhancer.class_has_jdo_like_member",
576                         userClassName, fieldName));
577             return;
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                 // At some point we may want to consider stripping old
595                 // annotations and re-annotating, but not yet
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         // check if class is known not to be changed
613         final EnhancerMetaData meta = env.getEnhancerMetaData();
614         if (meta.isKnownUnenhancableClass(className)) {
615             persistenceType = CC_Unenhancable;
616             return;
617         }
618 
619         // check if class is persistence-capable
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             //^olsen: assert consistency between Java and JDO metadata
633             affirm(!classFile.isInterface());
634             //affirm(!classFile.isInnerClass());
635 
636             //^olsen: assert consistency between Java and JDO metadata
637             // disallow enhancing classes not derived from java.lang.Object
638             final ConstClass superConstClass = classFile.superName();
639             affirm(superConstClass != null);
640 
641             // non-pc-root classes must not derive from java.lang.Object
642             affirm(pcSuperClassName == null
643                    || !superConstClass.asString().equals("java/lang/Object"));
644 
645             // define the PC key owner class
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             // define the PC super key owner class
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         // all fields for which accessor/mutator needs to be generated
685         final Map annotatedFieldMap = new HashMap();
686 
687         if (isAugmentable()) {
688             // loop over class fields to declare them to the model
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                 // skip jdo fields
696                 if (jdoFieldNames.contains(name)) {
697                     continue;
698                 }
699 
700                 // skip static fields
701                 if (field.isStatic()) {
702                     continue;
703                 }
704 
705                 // skip known non-managed fields
706                 if (meta.isKnownNonManagedField(className, name, sig)) {
707                     continue;
708                 }
709 
710                 // remember field requiring accessor/mutator
711                 Object obj = annotatedFieldMap.put(name, field);
712                 affirm(obj == null,
713                    ("Error in classfile: repeated declaration of field: "
714                     + userClassName + "." + name));
715 
716                 // skip final, transient fields
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         // nr of fields needing accessor/mutator methods
731         annotatedFieldCount = annotatedFieldMap.size();
732         
733         // get managed field names from meta data
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         // data structures for key fields
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         // loop over class fields to compute 'jdo*' and key/managed fields
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             // map 'jdo*' field names to class fields
771             if (name.startsWith("jdo")) {
772                 final Object f = jdoLikeFields.put(name, field);
773                 affirm(f == null);
774             }
775 
776             // skip non-managed fields
777             if (!managedFieldNamesSet.contains(name)) {
778                 affirm(!meta.isManagedField(className, name));
779 
780                 // check for non-managed key field
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             // check for managed static field
789             affirm(!field.isStatic(),
790                    ("JDO metadata: reported the field " + userFieldName
791                     + " to be managed though it's static."));
792 
793             // check for managed final field
794             affirm(!field.isFinal(),
795                    ("JDO metadata: reported the field " + userFieldName
796                     + " to be managed though it's final."));
797 
798             // allow for managed transient fields
799 
800 //^olsen: adopt
801 /*
802             r[i++] = hasField(
803                 out,
804                 Modifier.PRIVATE | Modifier.FINAL | Modifier.STATIC,
805                 long.class,
806                 "serialVersionUID");
807 */
808         }
809         
810         // get the managed field flags ordered by relative index
811         final int[] managedFieldFlags
812             = meta.getFieldFlags(className, managedFieldNames);
813 
814         // compute the managed field types ordered by relative index
815         // and key field indexes
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             // assert consistency between Java and JDO metadata
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             // mark managed field as taken care of
837             annotatedFieldMap.remove(name);
838 
839             // assign key field index
840             if (keyFieldNamesSet.contains(name)) {
841                 affirm(meta.isKeyField(className, name));
842                 keyFieldIndexes[j++] = i;
843             }
844             
845             // add field type and Java access modifers
846             managedFieldSigs[i] = field.signature().asString();
847             managedFieldMods[i] = field.access();
848 
849             // set the serializable bit if field is not (Java) transient
850             // This code might be removed as soon as the metadata is able
851             // to retrieve the info as part of meta.getFieldFlags.
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         // post conditions of managed/key field processing
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         // assign the annotated field arrays
877         if (managedFieldCount == annotatedFieldCount) {
878             // return if the annotated fields are equal to the managed ones
879             annotatedFieldNames = managedFieldNames;
880             annotatedFieldSigs = managedFieldSigs;
881             annotatedFieldMods = managedFieldMods;
882             annotatedFieldFlags = managedFieldFlags;
883         } else {
884             // fill the annotated field arrays with the managed ones
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             // append the annotated, non-managed fields
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                 // add field type and Java access modifers
908                 annotatedFieldNames[i] = name;
909                 annotatedFieldSigs[i] = field.signature().asString();
910                 annotatedFieldMods[i] = field.access();
911                 annotatedFieldFlags[i] = 0x0; // direct read/write access
912                 i++;
913             }
914             affirm(i == annotatedFieldCount);
915         }
916         
917         // post conditions
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         // check methods
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             // for non-abstract, non-native methods, map names to class methods
945             if (!method.isAbstract() && !method.isNative()) {
946                 final Object m = annotatableMethods.put(key, method);
947                 affirm(m == null);
948             }
949 
950             // for 'jdo*' like methods, map names to class methods
951             if (name.startsWith("jdo")) {
952                 final Object m = jdoLikeMethods.put(key, method);
953                 affirm(m == null);
954                 continue;
955             }
956 
957             // check for a default constructor by name and signature
958             if (name.equals(NameHelper.constructorName())
959                 && sig.equals(NameHelper.constructorSig())) {
960                 hasDefaultConstructor = true;
961                 continue;
962             }
963             
964             // check for a static initializer block by name and signature
965             if (name.equals(JAVA_clinit_Name)
966                 && sig.equals(JAVA_clinit_Sig)) {
967                 hasStaticInitializer = true;
968                 continue;
969             }
970 
971             // check for method clone() by name and signature
972             if (name.equals(JAVA_Object_clone_Name)
973                 && sig.equals(JAVA_Object_clone_Sig)) {
974                 hasCloneMethod = true;
975                 continue;
976             }
977 
978             // check for method writeObject() by name and signature
979             if (name.equals(JAVA_Object_writeObject_Name)
980                 && sig.equals(JAVA_Object_writeObject_Sig)) {
981                 hasWriteObjectMethod = true;
982                 continue;
983             }
984 
985             // check for method writeReplace() by name and signature
986             if (name.equals(JAVA_Object_writeReplace_Name)
987                 && sig.equals(JAVA_Object_writeReplace_Sig)) {
988                 hasWriteReplaceMethod = true;
989                 continue;
990             }
991 
992             // check for method readObject() by name and signature
993             if (name.equals(JAVA_Object_readObject_Name)
994                 && sig.equals(JAVA_Object_readObject_Sig)) {
995                 hasReadObjectMethod = true;
996 
997                 // remove readObject() method from annotation candidates
998                 Object m = annotatableMethods.remove(key);
999                 affirm(m != null);
1000                 continue;
1001             }
1002         }
1003 
1004         // check for a default constructor by name and signature
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         // check for a static initializer block by name and signature
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         // check for method clone() by name and signature
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         // check for method writeObject() by name and signature
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         // check for method writeReplace() by name and signature
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         // check for method readObject() by name and signature
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         //^olsen: check
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             //^olsen: check for specific enhancement
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         //^olsen: check
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         // performance shortcut
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         // sets of found/missing 'jdo*' members
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         // performance shortcut
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         // sets of found/missing 'jdo*' members
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         // performance shortcut
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         // sets of found/missing 'jdo*' members
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         // performance shortcut
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         // sets of found/missing 'jdo*' members
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         // performance shortcut
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         // sets of found/missing 'jdo*' members
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         // no requirement to check for 'missing' methods
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         // remove jdo method from annotation candidates
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 }