View Javadoc

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