You are viewing a plain text version of this content. The canonical link for it is here.
Posted to jdo-commits@db.apache.org by mb...@apache.org on 2005/05/22 19:55:54 UTC
svn commit: r171351 [8/16] - in /incubator/jdo/trunk/enhancer20: ./ src/
src/conf/ src/java/ src/java/org/ src/java/org/apache/
src/java/org/apache/jdo/ src/java/org/apache/jdo/enhancer/
src/java/org/apache/jdo/impl/ src/java/org/apache/jdo/impl/enhancer/
src/java/org/apache/jdo/impl/enhancer/classfile/
src/java/org/apache/jdo/impl/enhancer/core/
src/java/org/apache/jdo/impl/enhancer/generator/
src/java/org/apache/jdo/impl/enhancer/meta/
src/java/org/apache/jdo/impl/enhancer/meta/model/
src/java/org/apache/jdo/impl/enhancer/meta/prop/
src/java/org/apache/jdo/impl/enhancer/meta/util/
src/java/org/apache/jdo/impl/enhancer/util/ test/ test/sempdept/
test/sempdept/src/ test/sempdept/src/empdept/
Added: incubator/jdo/trunk/enhancer20/src/java/org/apache/jdo/impl/enhancer/core/Analyzer.java
URL: http://svn.apache.org/viewcvs/incubator/jdo/trunk/enhancer20/src/java/org/apache/jdo/impl/enhancer/core/Analyzer.java?rev=171351&view=auto
==============================================================================
--- incubator/jdo/trunk/enhancer20/src/java/org/apache/jdo/impl/enhancer/core/Analyzer.java (added)
+++ incubator/jdo/trunk/enhancer20/src/java/org/apache/jdo/impl/enhancer/core/Analyzer.java Sun May 22 10:55:51 2005
@@ -0,0 +1,1586 @@
+/*
+ * Copyright 2005 The Apache Software Foundation.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.jdo.impl.enhancer.core;
+
+import java.util.Collection;
+import java.util.Enumeration;
+import java.util.Iterator;
+import java.util.Arrays;
+import java.util.Set;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.HashMap;
+
+import org.apache.jdo.impl.enhancer.classfile.ClassAttribute;
+import org.apache.jdo.impl.enhancer.classfile.ClassField;
+import org.apache.jdo.impl.enhancer.classfile.ClassFile;
+import org.apache.jdo.impl.enhancer.classfile.ClassMethod;
+import org.apache.jdo.impl.enhancer.classfile.ConstClass;
+import org.apache.jdo.impl.enhancer.classfile.ConstantPool;
+import org.apache.jdo.impl.enhancer.classfile.GenericAttribute;
+import org.apache.jdo.impl.enhancer.meta.EnhancerMetaData;
+import org.apache.jdo.impl.enhancer.util.Support;
+
+
+
+
+
+/**
+ * Analyzes a class for enhancement.
+ */
+final class Analyzer
+ extends Support
+ implements JDOConstants, EnhancerConstants
+{
+ /**
+ * The class is not to be modified by the enahncer.
+ */
+ static public final int CC_Unenhancable = -2;
+
+ /**
+ * The class is detected to be enhanced already and is not to be modifed.
+ */
+ static public final int CC_PreviouslyEnhanced = -1;
+
+ /**
+ * The enhancement status of the class hasn't been determined yet.
+ */
+ static public final int CC_PersistenceUnknown = 0;
+
+ /**
+ * The class is to be enhanced for persistence-awareness.
+ */
+ static public final int CC_PersistenceAware = 1;
+
+ /**
+ * The class is to be enhanced for specific persistence-capability
+ * (class does extend another persistence-capable class).
+ */
+ static public final int CC_PersistenceCapable = 2;
+
+ /**
+ * The class is to be enhanced for generic and specific
+ * persistence-capability (class does not extend another
+ * persistence-capable class).
+ */
+ static public final int CC_PersistenceCapableRoot = 3;
+
+ /**
+ * The names of the jdo fields of persistene-capable classes.
+ */
+ static private final Set jdoFieldNames = new HashSet();
+ static
+ {
+ jdoFieldNames.add(JDO_PC_jdoStateManager_Name);
+ jdoFieldNames.add(JDO_PC_jdoFlags_Name);
+ jdoFieldNames.add(JDO_PC_jdoInheritedFieldCount_Name);
+ jdoFieldNames.add(JDO_PC_jdoFieldNames_Name);
+ jdoFieldNames.add(JDO_PC_jdoFieldTypes_Name);
+ jdoFieldNames.add(JDO_PC_jdoFieldFlags_Name);
+ jdoFieldNames.add(JDO_PC_jdoPersistenceCapableSuperclass_Name);
+ }
+
+ /**
+ * The classfile's enhancement controller.
+ */
+ private final Controller control;
+
+ /**
+ * The classfile to be enhanced.
+ */
+ private final ClassFile classFile;
+
+ /**
+ * The class name in VM form.
+ */
+ private final String className;
+
+ /**
+ * The class name in user ('.' delimited) form.
+ */
+ private final String userClassName;
+
+ /**
+ * The classfile's constant pool.
+ */
+ private final ConstantPool pool;
+
+ /**
+ * Repository for the enhancement options.
+ */
+ private final Environment env;
+
+ /**
+ * Repository for JDO meta-data on classes.
+ */
+ private final EnhancerMetaData meta;
+
+ /**
+ * What type of class is this with respect to persistence.
+ */
+ private int persistenceType = CC_PersistenceUnknown;
+
+ /**
+ * The name of the persistence-capable superclass if defined.
+ */
+ private String pcSuperClassName;
+
+ /**
+ * The name of the persistence-capable rootclass if defined.
+ */
+ private String pcRootClassName;
+
+ /**
+ * The name of this class or the next persistence-capable superclass
+ * that owns a key class, or the PC rootclass if none defines a key class.
+ */
+ private String pcKeyOwnerClassName;
+
+ /**
+ * The name next persistence-capable superclass that owns a key class,
+ * or the PC rootclass if none defines a key class.
+ */
+ private String pcSuperKeyOwnerClassName;
+
+ /**
+ * The name of the key class if defined.
+ */
+ private String keyClassName;
+
+ /**
+ * The name of the key class of the next persistence-capable superclass
+ * that defines one.
+ */
+ private String superKeyClassName;
+
+ /**
+ * The number of key fields.
+ */
+ private int keyFieldCount;
+
+ /**
+ * The indexes of all key fields.
+ */
+ private int[] keyFieldIndexes;
+
+ /**
+ * The number of managed fields.
+ */
+ private int managedFieldCount;
+
+ /**
+ * The number of annotated fields.
+ */
+ private int annotatedFieldCount;
+
+ /**
+ * The names of all annotated fields sorted by relative field index.
+ */
+ private String[] annotatedFieldNames;
+
+ /**
+ * The type names of all annotated fields sorted by relative field index.
+ */
+ private String[] annotatedFieldSigs;
+
+ /**
+ * The java access modifiers of all annotated fields sorted by relative
+ * field index.
+ */
+ private int[] annotatedFieldMods;
+
+ /**
+ * The jdo flags of all annotated fields sorted by relative field index.
+ */
+ private int[] annotatedFieldFlags;
+
+ /**
+ * The map of found JDO fields.
+ */
+ private final Map jdoLikeFields = new HashMap(20);
+
+ /**
+ * The map of found JDO methods
+ */
+ private final Map jdoLikeMethods = new HashMap(50);
+
+ /**
+ * The map of found JDO methods
+ */
+ private final Map annotatableMethods = new HashMap(100);
+
+ /**
+ * True if a jdo member has been seen in this class.
+ */
+ private boolean hasImplementsPC = false;
+ private boolean hasGenericJDOFields = false;
+ private boolean hasGenericJDOMethods = false;
+ private boolean hasGenericJDOMembers = false;
+ private boolean hasSpecificJDOFields = false;
+ private boolean hasSpecificJDOMethods = false;
+ private boolean hasSpecificJDOMembers = false;
+ private boolean hasCallbackJDOMethods = false;
+ private boolean hasJDOMembers = false;
+
+ /**
+ * True if the class has a default (no-argument) constructor.
+ */
+ private boolean hasDefaultConstructor = false;
+
+ //^olsen: performance opt.: make these fields of type ClassMethod
+
+ /**
+ * True if the class has a static initializer block.
+ */
+ private boolean hasStaticInitializer = false;
+
+ /**
+ * True if the class has a clone() method.
+ */
+ private boolean hasCloneMethod = false;
+
+ /**
+ * True if the class has a writeObject(java.io.ObjectOutputStream) method.
+ */
+ private boolean hasWriteObjectMethod = false;
+
+ /**
+ * True if the class has a writeReplace() method.
+ */
+ private boolean hasWriteReplaceMethod = false;
+
+ /**
+ * True if the class has a readObject(java.io.ObjectInputStream) method.
+ */
+ private boolean hasReadObjectMethod = false;
+
+ // ----------------------------------------------------------------------
+
+ /**
+ * Constructor
+ */
+ public Analyzer(Controller control,
+ Environment env)
+ {
+ affirm(control != null);
+ affirm(env != null);
+
+ this.control = control;
+ this.classFile = control.getClassFile();
+ this.className = classFile.classNameString();
+ this.userClassName = classFile.userClassName();
+ this.pool = classFile.pool();
+ this.env = env;
+ this.meta = env.getEnhancerMetaData();
+
+ affirm(classFile != null);
+ affirm(className != null);
+ affirm(userClassName != null);
+ affirm(pool != null);
+ affirm(meta != null);
+ }
+
+ /**
+ * Returns the class file which we are operating on.
+ */
+ public ClassFile getClassFile()
+ {
+ return classFile;
+ }
+
+ /**
+ * Return the persistence type for this class
+ */
+ public int getPersistenceType()
+ {
+ return persistenceType;
+ }
+
+ /**
+ * Returns true if the class has been analyzed already.
+ */
+ public boolean isAnalyzed()
+ {
+ return (persistenceType != CC_PersistenceUnknown);
+ }
+
+ /**
+ * Returns true if the class is one which should be a candidate for
+ * annotation.
+ */
+ public boolean isAnnotateable()
+ {
+ return (persistenceType >= CC_PersistenceUnknown);
+ }
+
+ /**
+ * Returns true if the class is to be enhanced for persistence-capability.
+ */
+ public boolean isAugmentable()
+ {
+ return (persistenceType >= CC_PersistenceCapable);
+ }
+
+ /**
+ * Returns true if the class is to be enhanced as least-derived,
+ * persistence-capable class.
+ */
+ public boolean isAugmentableAsRoot()
+ {
+ return (persistenceType >= CC_PersistenceCapableRoot);
+ }
+
+ /**
+ * Returns the methods that are candidates for annotation.
+ */
+ public Collection getAnnotatableMethods()
+ {
+ return annotatableMethods.values();
+ }
+
+ /**
+ * Returns the name of the persistence-capable superclass if defined.
+ */
+ public String getPCSuperClassName()
+ {
+ return pcSuperClassName;
+ }
+
+ /**
+ * Returns the name of the persistence-capable rootclass if defined.
+ */
+ public String getPCRootClassName()
+ {
+ return pcRootClassName;
+ }
+
+ /**
+ * Returns the name of this class or the next persistence-capable
+ * superclass that owns a key class.
+ */
+ public String getPCKeyOwnerClassName()
+ {
+ return pcKeyOwnerClassName;
+ }
+
+ /**
+ * Returns the name of this class or the next persistence-capable
+ * that owns a key class.
+ */
+ public String getPCSuperKeyOwnerClassName()
+ {
+ return pcSuperKeyOwnerClassName;
+ }
+
+ /**
+ * Returns the name of the key class if defined.
+ */
+ public String getKeyClassName()
+ {
+ return keyClassName;
+ }
+
+ /**
+ * Returns the name of the key class of the next persistence-capable
+ * superclass that defines one.
+ */
+ public String getSuperKeyClassName()
+ {
+ return superKeyClassName;
+ }
+
+ /**
+ * Returns the number of key field.
+ */
+ public int getKeyFieldCount()
+ {
+ return keyFieldCount;
+ }
+
+ /**
+ * Returns the names of the key fields.
+ */
+ public int[] getKeyFieldIndexes()
+ {
+ return keyFieldIndexes;
+ }
+
+ /**
+ * Returns the number of managed field.
+ */
+ public int getManagedFieldCount()
+ {
+ return managedFieldCount;
+ }
+
+ /**
+ * Returns the number of annotated field.
+ */
+ public int getAnnotatedFieldCount()
+ {
+ return annotatedFieldCount;
+ }
+
+ /**
+ * Returns the names of the annotated fields.
+ */
+ public String[] getAnnotatedFieldNames()
+ {
+ return annotatedFieldNames;
+ }
+
+ /**
+ * Returns the types names of the annotated fields.
+ */
+ public String[] getAnnotatedFieldSigs()
+ {
+ return annotatedFieldSigs;
+ }
+
+ /**
+ * Returns the Java access modifiers of the annotated fields.
+ */
+ public int[] getAnnotatedFieldMods()
+ {
+ return annotatedFieldMods;
+ }
+
+ /**
+ * Returns the JDO flags of the annotated fields.
+ */
+ public int[] getAnnotatedFieldFlags()
+ {
+ return annotatedFieldFlags;
+ }
+
+ /**
+ * Returns true if the class has a default (no-argument) constructor.
+ */
+ public boolean hasDefaultConstructor()
+ {
+ return hasDefaultConstructor;
+ }
+
+ /**
+ * Returns true if the class has a static initializer block.
+ */
+ public boolean hasStaticInitializer()
+ {
+ return hasStaticInitializer;
+ }
+
+ /**
+ * Returns true if the class has a clone() method.
+ */
+ public boolean hasCloneMethod()
+ {
+ return hasCloneMethod;
+ }
+
+ /**
+ * Returns true if the class has a writeObject() method.
+ */
+ public boolean hasWriteObjectMethod()
+ {
+ return hasWriteObjectMethod;
+ }
+
+ /**
+ * Returns true if the class has a writeReplace() method.
+ */
+ public boolean hasWriteReplaceMethod()
+ {
+ return hasWriteReplaceMethod;
+ }
+
+ /**
+ * Returns true if the class has a readObject() method.
+ */
+ public boolean hasReadObjectMethod()
+ {
+ return hasReadObjectMethod;
+ }
+
+ /**
+ * Returns true if the class already provides the JDO augmentation.
+ */
+ public boolean hasJDOAugmentation()
+ {
+ return hasJDOMembers;
+ }
+
+ // ----------------------------------------------------------------------
+
+ /**
+ * Analyzes the class for existing augmentation.
+ */
+ public void scan()
+ {
+ env.message("scanning class " + userClassName);
+
+ // skip previously enhanced files
+ checkForEnhancedAttribute();
+ if (!isAnnotateable()) {
+ return;
+ }
+
+ // skip unenhancable files
+ initPersistenceType();
+ if (!isAnnotateable()) {
+ return;
+ }
+ affirm(persistenceType > CC_Unenhancable);
+
+ scanFields();
+ scanMethods();
+
+ if (isAugmentable()) {
+ checkPCFeasibility();
+ checkSpecificAugmentation();
+ checkCallbackAugmentation();
+
+ if (isAugmentableAsRoot()) {
+ checkGenericAugmentation();
+ }
+ }
+
+ //^olsen: check
+/*
+ //@olsen: check whether member starts with the reserved jdo prefix
+ if (methodName.startsWith("jdo")) {
+ //@olsen: issue a warning only
+ env.warning(
+ getI18N("enhancer.class_has_jdo_like_member",
+ userClassName, methodName));
+ return;
+ }
+ //@olsen: check whether member starts with the reserved jdo prefix
+ if (fieldName.startsWith("jdo")) {
+ //@olsen: issue a warning only
+ env.warning(
+ getI18N("enhancer.class_has_jdo_like_member",
+ userClassName, fieldName));
+ return;
+ }
+*/
+ }
+
+ /**
+ * Scans the attributes of a ClassFile
+ */
+ private void checkForEnhancedAttribute()
+ {
+ for (Enumeration e = classFile.attributes().elements();
+ e.hasMoreElements();) {
+ final ClassAttribute attr = (ClassAttribute)e.nextElement();
+ final String attrName = attr.attrName().asString();
+ if (SUNJDO_PC_EnhancedAttribute.equals(attrName)) {
+ persistenceType = CC_PreviouslyEnhanced;
+
+ // At some point we may want to consider stripping old
+ // annotations and re-annotating, but not yet
+ env.message("ignoring previously enhanced class "
+ + userClassName);
+ return;
+ }
+ }
+ }
+
+ // ----------------------------------------------------------------------
+
+ /**
+ * Sets the persistence type of a class according to JDO metadata.
+ */
+ private void initPersistenceType()
+ {
+ affirm(persistenceType == CC_PersistenceUnknown);
+
+ // check if class is known not to be changed
+ final EnhancerMetaData meta = env.getEnhancerMetaData();
+ if (meta.isKnownUnenhancableClass(className)) {
+ persistenceType = CC_Unenhancable;
+ return;
+ }
+
+ // check if class is persistence-capable
+ if (meta.isPersistenceCapableClass(className)) {
+ pcSuperClassName
+ = meta.getPersistenceCapableSuperClass(className);
+ pcRootClassName
+ = meta.getPersistenceCapableRootClass(className);
+ affirm(pcSuperClassName == null || pcRootClassName != null);
+
+ persistenceType
+ = (pcSuperClassName == null
+ ? CC_PersistenceCapableRoot
+ : CC_PersistenceCapable);
+
+ //^olsen: assert consistency between Java and JDO metadata
+ affirm(!classFile.isInterface());
+ //affirm(!classFile.isInnerClass());
+
+ //^olsen: assert consistency between Java and JDO metadata
+ // disallow enhancing classes not derived from java.lang.Object
+ final ConstClass superConstClass = classFile.superName();
+ affirm(superConstClass != null);
+
+ // non-pc-root classes must not derive from java.lang.Object
+ affirm(pcSuperClassName == null
+ || !superConstClass.asString().equals("java/lang/Object"));
+
+ // define the PC key owner class
+ pcKeyOwnerClassName = className;
+ while (meta.getKeyClass(pcKeyOwnerClassName) == null) {
+ final String pcSuperClassName
+ = meta.getPersistenceCapableSuperClass(
+ pcKeyOwnerClassName);
+ if (pcSuperClassName == null)
+ break;
+ pcKeyOwnerClassName = pcSuperClassName;
+ }
+ affirm(pcKeyOwnerClassName != null);
+
+ // define the PC super key owner class
+ pcSuperKeyOwnerClassName = pcSuperClassName;
+ if (pcSuperKeyOwnerClassName != null) {
+ while (meta.getKeyClass(pcSuperKeyOwnerClassName) == null) {
+ final String pcSuperClassName
+ = meta.getPersistenceCapableSuperClass(
+ pcSuperKeyOwnerClassName);
+ if (pcSuperClassName == null)
+ break;
+ pcSuperKeyOwnerClassName = pcSuperClassName;
+ }
+ affirm(pcKeyOwnerClassName != null);
+ }
+
+ keyClassName
+ = meta.getKeyClass(className);
+ superKeyClassName
+ = meta.getSuperKeyClass(className);
+ affirm(superKeyClassName == null || pcSuperClassName != null);
+ }
+ }
+
+ /**
+ * Scans the fields.
+ */
+ private void scanFields()
+ {
+ // all fields for which accessor/mutator needs to be generated
+ final Map annotatedFieldMap = new HashMap();
+
+ if (isAugmentable()) {
+ // loop over class fields to declare them to the model
+ for (final Enumeration e = classFile.fields().elements();
+ e.hasMoreElements();) {
+ final ClassField field = (ClassField)e.nextElement();
+ final String name = field.name().asString();
+ final String sig = field.signature().asString();
+
+ // skip jdo fields
+ if (jdoFieldNames.contains(name)) {
+ continue;
+ }
+
+ // skip static fields
+ if (field.isStatic()) {
+ continue;
+ }
+
+ // skip known non-managed fields
+ if (meta.isKnownNonManagedField(className, name, sig)) {
+ continue;
+ }
+
+ // remember field requiring accessor/mutator
+ Object obj = annotatedFieldMap.put(name, field);
+ affirm(obj == null,
+ ("Error in classfile: repeated declaration of field: "
+ + userClassName + "." + name));
+
+ // skip final, transient fields
+ if (field.isFinal()
+ || field.isTransient()) {
+ continue;
+ }
+
+ if (false) {
+ System.out.println("Analyzer.scanFields(): declaring "
+ + className + "." + name + " : " + sig);
+ }
+ meta.declareField(className, name, sig);
+ }
+ }
+
+ // nr of fields needing accessor/mutator methods
+ annotatedFieldCount = annotatedFieldMap.size();
+
+ // get managed field names from meta data
+ final String[] managedFieldNames = meta.getManagedFields(className);
+ affirm(managedFieldNames != null);
+ managedFieldCount = managedFieldNames.length;
+ final Set managedFieldNamesSet
+ = new HashSet(Arrays.asList(managedFieldNames));
+ affirm(managedFieldNamesSet.size() == managedFieldCount,
+ "JDO metadata: returned duplicate managed fields.");
+ affirm(managedFieldCount <= annotatedFieldCount,
+ "JDO metadata: managed fields exceed annotated fields.");
+
+ // data structures for key fields
+ final String[] keyFieldNames = meta.getKeyFields(className);
+ affirm(keyFieldNames != null);
+ keyFieldCount = keyFieldNames.length;
+ affirm(keyFieldCount == 0 || keyClassName != null,
+ "JDO metadata: returned key fields but no key class.");
+ final Set keyFieldNamesSet
+ = new HashSet(Arrays.asList(keyFieldNames));
+ affirm(keyFieldNamesSet.size() == keyFieldCount,
+ "JDO metadata: returned duplicate key fields.");
+ affirm(keyFieldCount <= managedFieldCount,
+ "JDO metadata: key fields exceed managed fields.");
+
+ // loop over class fields to compute 'jdo*' and key/managed fields
+ for (final Enumeration e = classFile.fields().elements();
+ e.hasMoreElements();) {
+ final ClassField field = (ClassField)e.nextElement();
+ final String name = field.name().asString();
+ final String sig = field.signature().asString();
+ final String userFieldName = userClassName + "." + name;
+
+ if (false) {
+ System.out.println("Analyzer.scanFields(): scanning "
+ + className + "." + name + " : " + sig);
+ }
+
+ // map 'jdo*' field names to class fields
+ if (name.startsWith("jdo")) {
+ final Object f = jdoLikeFields.put(name, field);
+ affirm(f == null);
+ }
+
+ // skip non-managed fields
+ if (!managedFieldNamesSet.contains(name)) {
+ affirm(!meta.isManagedField(className, name));
+
+ // check for non-managed key field
+ affirm(!keyFieldNamesSet.contains(name),
+ ("JDO metadata: reported the field " + userFieldName
+ + " to be non-managed but key."));
+ continue;
+ }
+ affirm(meta.isManagedField(className, name));
+
+ // check for managed static field
+ affirm(!field.isStatic(),
+ ("JDO metadata: reported the field " + userFieldName
+ + " to be managed though it's static."));
+
+ // check for managed final field
+ affirm(!field.isFinal(),
+ ("JDO metadata: reported the field " + userFieldName
+ + " to be managed though it's final."));
+
+ // allow for managed transient fields
+
+//^olsen: adopt
+/*
+ r[i++] = hasField(
+ out,
+ Modifier.PRIVATE | Modifier.FINAL | Modifier.STATIC,
+ long.class,
+ "serialVersionUID");
+*/
+ }
+
+ // get the managed field flags ordered by relative index
+ final int[] managedFieldFlags
+ = meta.getFieldFlags(className, managedFieldNames);
+
+ // compute the managed field types ordered by relative index
+ // and key field indexes
+ int j = 0;
+ keyFieldIndexes = new int[keyFieldCount];
+ final String[] managedFieldSigs = new String[managedFieldCount];
+ final int[] managedFieldMods = new int[managedFieldCount];
+ for (int i = 0; i < managedFieldCount; i++) {
+ final String name = managedFieldNames[i];
+ affirm(name != null);
+
+ // assert consistency between Java and JDO metadata
+ final ClassField field = (ClassField)annotatedFieldMap.get(name);
+ affirm(field != null,
+ ("The managed field " + userClassName + "." + name +
+ " is not declared by the class."));
+ affirm(!field.isStatic(),
+ ("The managed field " + userClassName + "." + name +
+ " is static."));
+ affirm(!field.isFinal(),
+ ("The managed field " + userClassName + "." + name +
+ " is final."));
+
+ // mark managed field as taken care of
+ annotatedFieldMap.remove(name);
+
+ // assign key field index
+ if (keyFieldNamesSet.contains(name)) {
+ affirm(meta.isKeyField(className, name));
+ keyFieldIndexes[j++] = i;
+ }
+
+ // add field type and Java access modifers
+ managedFieldSigs[i] = field.signature().asString();
+ managedFieldMods[i] = field.access();
+
+ // set the serializable bit if field is not (Java) transient
+ // This code might be removed as soon as the metadata is able
+ // to retrieve the info as part of meta.getFieldFlags.
+ if (!field.isTransient()) {
+ managedFieldFlags[i] |= EnhancerMetaData.SERIALIZABLE;
+ }
+
+ if (false) {
+ System.out.println("managed field: "
+ + className + "." + name + " : {");
+ System.out.println(" sigs = " + managedFieldSigs[i]);
+ System.out.println(" mods = "
+ + Integer.toHexString(managedFieldMods[i]));
+ System.out.println(" flags = "
+ + Integer.toHexString(managedFieldFlags[i]));
+ }
+ }
+
+ // post conditions of managed/key field processing
+ affirm(keyFieldIndexes.length == keyFieldCount);
+ affirm(keyFieldCount <= managedFieldCount);
+ affirm(managedFieldNames.length == managedFieldCount);
+ affirm(managedFieldSigs.length == managedFieldCount);
+ affirm(managedFieldMods.length == managedFieldCount);
+ affirm(managedFieldFlags.length == managedFieldCount);
+ affirm(managedFieldCount <= annotatedFieldCount);
+
+ // assign the annotated field arrays
+ if (managedFieldCount == annotatedFieldCount) {
+ // return if the annotated fields are equal to the managed ones
+ annotatedFieldNames = managedFieldNames;
+ annotatedFieldSigs = managedFieldSigs;
+ annotatedFieldMods = managedFieldMods;
+ annotatedFieldFlags = managedFieldFlags;
+ } else {
+ // fill the annotated field arrays with the managed ones
+ annotatedFieldNames = new String[annotatedFieldCount];
+ annotatedFieldSigs = new String[annotatedFieldCount];
+ annotatedFieldMods = new int[annotatedFieldCount];
+ annotatedFieldFlags = new int[annotatedFieldCount];
+ int i = managedFieldCount;
+ System.arraycopy(managedFieldNames, 0, annotatedFieldNames, 0, i);
+ System.arraycopy(managedFieldSigs, 0, annotatedFieldSigs, 0, i);
+ System.arraycopy(managedFieldMods, 0, annotatedFieldMods, 0, i);
+ System.arraycopy(managedFieldFlags, 0, annotatedFieldFlags, 0, i);
+
+ // append the annotated, non-managed fields
+ for (Iterator k = annotatedFieldMap.entrySet().iterator();
+ k.hasNext();) {
+ final Map.Entry entry = (Map.Entry)k.next();
+ final String name = (String)entry.getKey();
+ final ClassField field = (ClassField)entry.getValue();
+ affirm(name.equals(field.name().asString()));
+
+ affirm(!field.isStatic(),
+ ("The managed field " + userClassName + "." + name +
+ " is static."));
+
+ // add field type and Java access modifers
+ annotatedFieldNames[i] = name;
+ annotatedFieldSigs[i] = field.signature().asString();
+ annotatedFieldMods[i] = field.access();
+ annotatedFieldFlags[i] = 0x0; // direct read/write access
+ i++;
+ }
+ affirm(i == annotatedFieldCount);
+ }
+
+ // post conditions
+ affirm(keyFieldIndexes.length == keyFieldCount);
+ affirm(keyFieldCount <= managedFieldCount);
+ affirm(annotatedFieldNames.length == annotatedFieldCount);
+ affirm(annotatedFieldSigs.length == annotatedFieldCount);
+ affirm(annotatedFieldMods.length == annotatedFieldCount);
+ affirm(annotatedFieldFlags.length == annotatedFieldCount);
+ affirm(managedFieldCount <= annotatedFieldCount);
+ }
+
+ /**
+ * Scans the methods of a ClassFile.
+ */
+ private void scanMethods()
+ {
+ // check methods
+ for (final Enumeration e = classFile.methods().elements();
+ e.hasMoreElements();) {
+ final ClassMethod method = (ClassMethod)e.nextElement();
+ final String name = method.name().asString();
+ final String sig = method.signature().asString();
+ affirm(name != null);
+ affirm(sig != null);
+
+ final String key = methodKey(name, sig);
+ affirm(key != null);
+
+ // for non-abstract, non-native methods, map names to class methods
+ if (!method.isAbstract() && !method.isNative()) {
+ final Object m = annotatableMethods.put(key, method);
+ affirm(m == null);
+ }
+
+ // for 'jdo*' like methods, map names to class methods
+ if (name.startsWith("jdo")) {
+ final Object m = jdoLikeMethods.put(key, method);
+ affirm(m == null);
+ continue;
+ }
+
+ // check for a default constructor by name and signature
+ if (name.equals(NameHelper.constructorName())
+ && sig.equals(NameHelper.constructorSig())) {
+ hasDefaultConstructor = true;
+ continue;
+ }
+
+ // check for a static initializer block by name and signature
+ if (name.equals(JAVA_clinit_Name)
+ && sig.equals(JAVA_clinit_Sig)) {
+ hasStaticInitializer = true;
+ continue;
+ }
+
+ // check for method clone() by name and signature
+ if (name.equals(JAVA_Object_clone_Name)
+ && sig.equals(JAVA_Object_clone_Sig)) {
+ hasCloneMethod = true;
+ continue;
+ }
+
+ // check for method writeObject() by name and signature
+ if (name.equals(JAVA_Object_writeObject_Name)
+ && sig.equals(JAVA_Object_writeObject_Sig)) {
+ hasWriteObjectMethod = true;
+ continue;
+ }
+
+ // check for method writeReplace() by name and signature
+ if (name.equals(JAVA_Object_writeReplace_Name)
+ && sig.equals(JAVA_Object_writeReplace_Sig)) {
+ hasWriteReplaceMethod = true;
+ continue;
+ }
+
+ // check for method readObject() by name and signature
+ if (name.equals(JAVA_Object_readObject_Name)
+ && sig.equals(JAVA_Object_readObject_Sig)) {
+ hasReadObjectMethod = true;
+
+ // remove readObject() method from annotation candidates
+ Object m = annotatableMethods.remove(key);
+ affirm(m != null);
+ continue;
+ }
+ }
+
+ // check for a default constructor by name and signature
+ if (hasDefaultConstructor) {
+ env.message(getI18N("enhancer.class_has_default_constructor"));
+ } else {
+ env.message(getI18N("enhancer.class_has_not_default_constructor"));
+ }
+
+ // check for a static initializer block by name and signature
+ if (hasStaticInitializer) {
+ env.message(getI18N("enhancer.class_has_static_initializer"));
+ } else {
+ env.message(getI18N("enhancer.class_has_not_static_initializer"));
+ }
+
+ // check for method clone() by name and signature
+ if (hasCloneMethod) {
+ env.message(getI18N("enhancer.class_has_clone_method"));
+ } else {
+ env.message(getI18N("enhancer.class_has_not_clone_method"));
+ }
+
+ // check for method writeObject() by name and signature
+ if (hasWriteObjectMethod) {
+ env.message(getI18N("enhancer.class_has_writeObject_method"));
+ } else {
+ env.message(getI18N("enhancer.class_has_not_writeObject_method"));
+ }
+
+ // check for method writeReplace() by name and signature
+ if (hasWriteReplaceMethod) {
+ env.message(getI18N("enhancer.class_has_writeReplace_method"));
+ } else {
+ env.message(getI18N("enhancer.class_has_not_writeReplace_method"));
+ }
+
+ // check for method readObject() by name and signature
+ if (hasReadObjectMethod) {
+ env.message(getI18N("enhancer.class_has_readObject_method"));
+ } else {
+ env.message(getI18N("enhancer.class_has_not_readObject_method"));
+ }
+ }
+
+ private void checkGenericAugmentation()
+ {
+ scanForImplementsPC();
+ scanForGenericJDOFields();
+ scanForGenericJDOMethods();
+
+ final boolean all
+ = (hasImplementsPC && hasGenericJDOFields && hasGenericJDOMethods);
+ //^olsen: check
+ final boolean none
+ = !(hasImplementsPC
+ || hasGenericJDOFields || hasGenericJDOMethods);
+
+ if (all ^ none) {
+ hasGenericJDOMembers = hasImplementsPC;
+ env.message(
+ getI18N("enhancer.class_has_generic_jdo_members",
+ String.valueOf(hasGenericJDOMembers)));
+
+ //^olsen: check for specific enhancement
+
+ return;
+ }
+
+ final String key
+ = "enhancer.class_has_inconsistently_declared_jdo_members";
+ if (hasGenericJDOFields && !hasGenericJDOMethods) {
+ env.error(
+ getI18N(key,
+ userClassName,
+ "<generic jdo fields>",
+ "<generic jdo methods>"));
+ } else if (!hasGenericJDOFields && hasGenericJDOMethods) {
+ env.error(
+ getI18N(key,
+ userClassName,
+ "<generic jdo methods>",
+ "<generic jdo fields>"));
+ } else if (!hasGenericJDOFields && !hasGenericJDOMethods) {
+ env.error(
+ getI18N(key,
+ userClassName,
+ "<implements " + JDO_PersistenceCapable_Name + ">",
+ "<generic jdo members>"));
+ } else {
+ env.error(
+ getI18N(key,
+ userClassName,
+ "<generic jdo members>",
+ "<implements " + JDO_PersistenceCapable_Name + ">"));
+ }
+ }
+
+ private void checkSpecificAugmentation()
+ {
+ scanForSpecificJDOFields();
+ scanForSpecificJDOMethods();
+
+ final boolean all
+ = (hasSpecificJDOFields && hasSpecificJDOMethods);
+ //^olsen: check
+ final boolean none
+ = !(hasSpecificJDOFields || hasSpecificJDOMethods);
+
+ if (all ^ none) {
+ hasSpecificJDOMembers = hasSpecificJDOFields;
+ env.message(
+ getI18N("enhancer.class_has_specific_jdo_members",
+ String.valueOf(hasSpecificJDOMembers)));
+ return;
+ }
+
+ final String key
+ = "enhancer.class_has_inconsistently_declared_jdo_members";
+ if (hasSpecificJDOFields && !hasSpecificJDOMethods) {
+ env.error(
+ getI18N(key,
+ userClassName,
+ "<specific jdo fields>",
+ "<specific jdo methods>"));
+ } else {
+ env.error(
+ getI18N(key,
+ userClassName,
+ "<specific jdo methods>",
+ "<specific jdo fields>"));
+ }
+ }
+
+ private void checkCallbackAugmentation()
+ {
+ scanForCallbackJDOMethods();
+ env.message(
+ getI18N("enhancer.class_has_callback_jdo_methods",
+ String.valueOf(hasCallbackJDOMethods)));
+ }
+
+ private void checkPCFeasibility()
+ {
+ if (!hasDefaultConstructor) {
+ env.error(
+ getI18N("enhancer.class_missing_default_constructor",
+ userClassName));
+ }
+ }
+
+ /**
+ * Scans the class for implementing the PC interface.
+ */
+ private void scanForImplementsPC()
+ {
+ hasImplementsPC = false;
+ for (final Iterator ifc = classFile.interfaces().iterator();
+ ifc.hasNext();) {
+ final ConstClass i = (ConstClass)ifc.next();
+ if (i.asString().equals(JDO_PersistenceCapable_Path)) {
+ hasImplementsPC = true;
+ break;
+ }
+ }
+ env.message(
+ getI18N("enhancer.class_implements_jdo_pc",
+ String.valueOf(hasImplementsPC)));
+ }
+
+ /**
+ * Scans for JDO fields of generic augmentation.
+ */
+ private void scanForGenericJDOFields()
+ {
+ // performance shortcut
+ if (jdoLikeFields.isEmpty()) {
+ hasGenericJDOFields = false;
+ env.message(
+ getI18N("enhancer.class_has_generic_jdo_fields",
+ String.valueOf(hasGenericJDOFields)));
+ return;
+ }
+
+ // sets of found/missing 'jdo*' members
+ final Set found = new HashSet(10);
+ final Set missing = new HashSet(10);
+
+ scanJDOField(JDO_PC_jdoStateManager_Name,
+ JDO_PC_jdoStateManager_Sig,
+ JDO_PC_jdoStateManager_Mods,
+ found, missing);
+ scanJDOField(JDO_PC_jdoFlags_Name,
+ JDO_PC_jdoFlags_Sig,
+ JDO_PC_jdoFlags_Mods,
+ found, missing);
+
+ if (found.isEmpty() ^ missing.isEmpty()) {
+ hasGenericJDOFields = missing.isEmpty();
+ env.message(
+ getI18N("enhancer.class_has_generic_jdo_fields",
+ String.valueOf(hasGenericJDOFields)));
+ return;
+ }
+
+ reportInconsistentJDOMembers(found, missing);
+ }
+
+ /**
+ * Scans for JDO methods of generic augmentation.
+ */
+ private void scanForGenericJDOMethods()
+ {
+ // performance shortcut
+ if (jdoLikeMethods.isEmpty()) {
+ hasGenericJDOMethods = false;
+ env.message(
+ getI18N("enhancer.class_has_generic_jdo_methods",
+ String.valueOf(hasGenericJDOMethods)));
+ return;
+ }
+
+ // sets of found/missing 'jdo*' members
+ final Set found = new HashSet(30);
+ final Set missing = new HashSet(30);
+
+ scanJDOMethod(JDO_PC_jdoReplaceStateManager_Name,
+ JDO_PC_jdoReplaceStateManager_Sig,
+ JDO_PC_jdoReplaceStateManager_Mods,
+ found, missing);
+ scanJDOMethod(JDO_PC_jdoReplaceFlags_Name,
+ JDO_PC_jdoReplaceFlags_Sig,
+ JDO_PC_jdoReplaceFlags_Mods,
+ found, missing);
+ scanJDOMethod(JDO_PC_jdoGetPersistenceManager_Name,
+ JDO_PC_jdoGetPersistenceManager_Sig,
+ JDO_PC_jdoGetPersistenceManager_Mods,
+ found, missing);
+ scanJDOMethod(JDO_PC_jdoGetObjectId_Name,
+ JDO_PC_jdoGetObjectId_Sig,
+ JDO_PC_jdoGetObjectId_Mods,
+ found, missing);
+ scanJDOMethod(JDO_PC_jdoGetTransactionalObjectId_Name,
+ JDO_PC_jdoGetTransactionalObjectId_Sig,
+ JDO_PC_jdoGetTransactionalObjectId_Mods,
+ found, missing);
+ scanJDOMethod(JDO_PC_jdoIsPersistent_Name,
+ JDO_PC_jdoIsPersistent_Sig,
+ JDO_PC_jdoIsPersistent_Mods,
+ found, missing);
+ scanJDOMethod(JDO_PC_jdoIsTransactional_Name,
+ JDO_PC_jdoIsTransactional_Sig,
+ JDO_PC_jdoIsTransactional_Mods,
+ found, missing);
+ scanJDOMethod(JDO_PC_jdoIsNew_Name,
+ JDO_PC_jdoIsNew_Sig,
+ JDO_PC_jdoIsNew_Mods,
+ found, missing);
+ scanJDOMethod(JDO_PC_jdoIsDeleted_Name,
+ JDO_PC_jdoIsDeleted_Sig,
+ JDO_PC_jdoIsDeleted_Mods,
+ found, missing);
+ scanJDOMethod(JDO_PC_jdoIsDirty_Name,
+ JDO_PC_jdoIsDirty_Sig,
+ JDO_PC_jdoIsDirty_Mods,
+ found, missing);
+ scanJDOMethod(JDO_PC_jdoMakeDirty_Name,
+ JDO_PC_jdoMakeDirty_Sig,
+ JDO_PC_jdoMakeDirty_Mods,
+ found, missing);
+ scanJDOMethod(JDO_PC_jdoPreSerialize_Name,
+ JDO_PC_jdoPreSerialize_Sig,
+ JDO_PC_jdoPreSerialize_Mods,
+ found, missing);
+ scanJDOMethod(JDO_PC_jdoReplaceFields_Name,
+ JDO_PC_jdoReplaceFields_Sig,
+ JDO_PC_jdoReplaceFields_Mods,
+ found, missing);
+ scanJDOMethod(JDO_PC_jdoProvideFields_Name,
+ JDO_PC_jdoProvideFields_Sig,
+ JDO_PC_jdoProvideFields_Mods,
+ found, missing);
+
+ if (found.isEmpty() ^ missing.isEmpty()) {
+ hasGenericJDOMethods = missing.isEmpty();
+ env.message(
+ getI18N("enhancer.class_has_generic_jdo_methods",
+ String.valueOf(hasGenericJDOMethods)));
+ return;
+ }
+
+ reportInconsistentJDOMembers(found, missing);
+ }
+
+ /**
+ * Scans for JDO fields of specific augmentation.
+ */
+ private void scanForSpecificJDOFields()
+ {
+ // performance shortcut
+ if (jdoLikeFields.isEmpty()) {
+ hasSpecificJDOFields = false;
+ env.message(
+ getI18N("enhancer.class_has_specific_jdo_fields",
+ String.valueOf(hasSpecificJDOFields)));
+ return;
+ }
+
+ // sets of found/missing 'jdo*' members
+ final Set found = new HashSet(10);
+ final Set missing = new HashSet(10);
+
+ scanJDOField(JDO_PC_jdoInheritedFieldCount_Name,
+ JDO_PC_jdoInheritedFieldCount_Sig,
+ JDO_PC_jdoInheritedFieldCount_Mods,
+ found, missing);
+ scanJDOField(JDO_PC_jdoFieldNames_Name,
+ JDO_PC_jdoFieldNames_Sig,
+ JDO_PC_jdoFieldNames_Mods,
+ found, missing);
+ scanJDOField(JDO_PC_jdoFieldTypes_Name,
+ JDO_PC_jdoFieldTypes_Sig,
+ JDO_PC_jdoFieldTypes_Mods,
+ found, missing);
+ scanJDOField(JDO_PC_jdoFieldFlags_Name,
+ JDO_PC_jdoFieldFlags_Sig,
+ JDO_PC_jdoFieldFlags_Mods,
+ found, missing);
+ scanJDOField(JDO_PC_jdoPersistenceCapableSuperclass_Name,
+ JDO_PC_jdoPersistenceCapableSuperclass_Sig,
+ JDO_PC_jdoPersistenceCapableSuperclass_Mods,
+ found, missing);
+
+ if (found.isEmpty() ^ missing.isEmpty()) {
+ hasSpecificJDOFields = missing.isEmpty();
+ env.message(
+ getI18N("enhancer.class_has_specific_jdo_fields",
+ String.valueOf(hasSpecificJDOFields)));
+ return;
+ }
+
+ reportInconsistentJDOMembers(found, missing);
+ }
+
+ /**
+ * Scans for JDO methods of specific augmentation.
+ */
+ private void scanForSpecificJDOMethods()
+ {
+ // performance shortcut
+ if (jdoLikeMethods.isEmpty()) {
+ hasSpecificJDOMethods = false;
+ env.message(
+ getI18N("enhancer.class_has_specific_jdo_methods",
+ String.valueOf(hasSpecificJDOMethods)));
+ return;
+ }
+
+ // sets of found/missing 'jdo*' members
+ final Set found = new HashSet(30);
+ final Set missing = new HashSet(30);
+
+ scanJDOMethod(JDO_PC_jdoGetManagedFieldCount_Name,
+ JDO_PC_jdoGetManagedFieldCount_Sig,
+ JDO_PC_jdoGetManagedFieldCount_Mods,
+ found, missing);
+ scanJDOMethod(JDO_PC_jdoNewInstance_Name,
+ JDO_PC_jdoNewInstance_Sig,
+ JDO_PC_jdoNewInstance_Mods,
+ found, missing);
+ scanJDOMethod(JDO_PC_jdoNewInstance_Name,
+ JDO_PC_jdoNewInstance_Sig,
+ JDO_PC_jdoNewInstance_Mods,
+ found, missing);
+ scanJDOMethod(JDO_PC_jdoNewObjectIdInstance_Name,
+ JDO_PC_jdoNewObjectIdInstance_Sig,
+ JDO_PC_jdoNewObjectIdInstance_Mods,
+ found, missing);
+ scanJDOMethod(JDO_PC_jdoNewObjectIdInstance_Name,
+ JDO_PC_jdoNewObjectIdInstance_Sig,
+ JDO_PC_jdoNewObjectIdInstance_Mods,
+ found, missing);
+ scanJDOMethod(JDO_PC_jdoCopyKeyFieldsToObjectId_Name,
+ JDO_PC_jdoCopyKeyFieldsToObjectId_Sig,
+ JDO_PC_jdoCopyKeyFieldsToObjectId_Mods,
+ found, missing);
+ scanJDOMethod(JDO_PC_jdoCopyKeyFieldsToObjectId_OIFS_Name,
+ JDO_PC_jdoCopyKeyFieldsToObjectId_OIFS_Sig,
+ JDO_PC_jdoCopyKeyFieldsToObjectId_OIFS_Mods,
+ found, missing);
+ scanJDOMethod(JDO_PC_jdoCopyKeyFieldsFromObjectId_OIFC_Name,
+ JDO_PC_jdoCopyKeyFieldsFromObjectId_OIFC_Sig,
+ JDO_PC_jdoCopyKeyFieldsFromObjectId_OIFC_Mods,
+ found, missing);
+ scanJDOMethod(JDO_PC_jdoReplaceField_Name,
+ JDO_PC_jdoReplaceField_Sig,
+ JDO_PC_jdoReplaceField_Mods,
+ found, missing);
+ scanJDOMethod(JDO_PC_jdoProvideField_Name,
+ JDO_PC_jdoProvideField_Sig,
+ JDO_PC_jdoProvideField_Mods,
+ found, missing);
+ scanJDOMethod(JDO_PC_jdoCopyFields_Name,
+ JDO_PC_jdoCopyFields_Sig,
+ JDO_PC_jdoCopyFields_Mods,
+ found, missing);
+ scanJDOMethod(JDO_PC_jdoCopyField_Name,
+ JDONameHelper.getJDO_PC_jdoCopyField_Sig(className),
+ JDO_PC_jdoCopyField_Mods,
+ found, missing);
+
+ if (found.isEmpty() ^ missing.isEmpty()) {
+ hasSpecificJDOMethods = missing.isEmpty();
+ env.message(
+ getI18N("enhancer.class_has_specific_jdo_methods",
+ String.valueOf(hasSpecificJDOMethods)));
+ return;
+ }
+
+ reportInconsistentJDOMembers(found, missing);
+ }
+
+ /**
+ * Scans for JDO methods of generic augmentation.
+ */
+ private void scanForCallbackJDOMethods()
+ {
+ // performance shortcut
+ if (jdoLikeMethods.isEmpty()) {
+ hasCallbackJDOMethods = false;
+ env.message(
+ getI18N("enhancer.class_has_callback_jdo_methods",
+ String.valueOf(hasCallbackJDOMethods)));
+ return;
+ }
+
+ // sets of found/missing 'jdo*' members
+ final Set found = new HashSet(30);
+ final Set missing = new HashSet(30);
+ final boolean annotatable = true;
+
+ scanJDOMethod(JDO_IC_jdoPostLoad_Name,
+ JDO_IC_jdoPostLoad_Sig,
+ JDO_IC_jdoPostLoad_Mods,
+ found, missing, !annotatable);
+
+ scanJDOMethod(JDO_IC_jdoPreStore_Name,
+ JDO_IC_jdoPreStore_Sig,
+ JDO_IC_jdoPreStore_Mods,
+ found, missing, annotatable);
+
+ scanJDOMethod(JDO_IC_jdoPreClear_Name,
+ JDO_IC_jdoPreClear_Sig,
+ JDO_IC_jdoPreClear_Mods,
+ found, missing, !annotatable);
+
+ scanJDOMethod(JDO_IC_jdoPreDelete_Name,
+ JDO_IC_jdoPreDelete_Sig,
+ JDO_IC_jdoPreDelete_Mods,
+ found, missing, annotatable);
+
+ // no requirement to check for 'missing' methods
+ if (!found.isEmpty()) {
+ hasCallbackJDOMethods = true;
+ env.message(
+ getI18N("enhancer.class_has_callback_jdo_methods",
+ String.valueOf(hasCallbackJDOMethods)));
+ }
+ }
+
+ /**
+ * Verifies a JDO field signature.
+ */
+ private void scanJDOField(String fieldName,
+ String expectedSig,
+ int expectedMods,
+ Set found,
+ Set missing)
+ {
+ final ClassField field = (ClassField)jdoLikeFields.get(fieldName);
+ if (field == null) {
+ missing.add(fieldName);
+ return;
+ }
+ found.add(fieldName);
+
+ final String foundSig = field.signature().asString();
+ final int foundMods = field.access();
+ if (!expectedSig.equals(foundSig) || expectedMods != foundMods) {
+ env.error(
+ getI18N("enhancer.class_has_illegally_declared_jdo_member",
+ new Object[]{ userClassName,
+ fieldName,
+ expectedSig,
+ foundSig,
+ new Integer(expectedMods),
+ new Integer(foundMods) }));
+ }
+ }
+
+ /**
+ * Verifies a JDO method signature.
+ */
+ private void scanJDOMethod(String methodName,
+ String expectedSig,
+ int expectedMods,
+ Set found,
+ Set missing)
+ {
+ scanJDOMethod(methodName, expectedSig, expectedMods,
+ found, missing, true);
+ }
+
+ /**
+ * Verifies a JDO method signature.
+ */
+ private void scanJDOMethod(String methodName,
+ String expectedSig,
+ int expectedMods,
+ Set found,
+ Set missing,
+ boolean annotatable)
+ {
+ final String key = methodKey(methodName, expectedSig);
+ final ClassMethod method = (ClassMethod)jdoLikeMethods.get(key);
+ if (method == null) {
+ missing.add(key);
+ return;
+ }
+ found.add(key);
+
+ final String foundSig = method.signature().asString();
+ final int foundMods = method.access();
+ if (!expectedSig.equals(foundSig) || expectedMods != foundMods) {
+ env.error(
+ getI18N("enhancer.class_has_illegally_declared_jdo_member",
+ new Object[]{ userClassName,
+ methodName,
+ expectedSig,
+ foundSig,
+ new Integer(expectedMods),
+ new Integer(foundMods) }));
+ }
+
+ // remove jdo method from annotation candidates
+ if (!annotatable) {
+ Object m = annotatableMethods.remove(key);
+ affirm(m != null);
+ }
+ }
+
+ /**
+ * Reports an error for some found/missing JDO fields or methods.
+ */
+ private void reportInconsistentJDOMembers(Set found,
+ Set missing)
+ {
+ final Iterator fi = found.iterator();
+ final StringBuffer f = new StringBuffer((String)fi.next());
+ while (fi.hasNext()) {
+ f.append(", " + fi.next());
+ }
+
+ final Iterator mi = found.iterator();
+ final StringBuffer m = new StringBuffer((String)mi.next());
+ while (mi.hasNext()) {
+ m.append(", " + mi.next());
+ }
+
+ env.error(
+ getI18N("enhancer.class_has_inconsistently_declared_jdo_members",
+ userClassName, f.toString(), m.toString()));
+ }
+
+ // ----------------------------------------------------------------------
+
+ static private String methodKey(String name,
+ String sig)
+ {
+ affirm(name != null);
+ affirm(sig != null && sig.charAt(0) == '(' && sig.indexOf(')') > 0);
+ final String parms = sig.substring(0, sig.indexOf(')') + 1);
+ return (name + parms);
+ }
+}
Added: incubator/jdo/trunk/enhancer20/src/java/org/apache/jdo/impl/enhancer/core/Annotater.java
URL: http://svn.apache.org/viewcvs/incubator/jdo/trunk/enhancer20/src/java/org/apache/jdo/impl/enhancer/core/Annotater.java?rev=171351&view=auto
==============================================================================
--- incubator/jdo/trunk/enhancer20/src/java/org/apache/jdo/impl/enhancer/core/Annotater.java (added)
+++ incubator/jdo/trunk/enhancer20/src/java/org/apache/jdo/impl/enhancer/core/Annotater.java Sun May 22 10:55:51 2005
@@ -0,0 +1,354 @@
+/*
+ * Copyright 2005 The Apache Software Foundation.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package org.apache.jdo.impl.enhancer.core;
+
+import java.util.Iterator;
+
+import org.apache.jdo.impl.enhancer.classfile.ClassFile;
+import org.apache.jdo.impl.enhancer.classfile.ClassMethod;
+import org.apache.jdo.impl.enhancer.classfile.CodeAttribute;
+import org.apache.jdo.impl.enhancer.classfile.ConstClass;
+import org.apache.jdo.impl.enhancer.classfile.ConstFieldRef;
+import org.apache.jdo.impl.enhancer.classfile.ConstMethodRef;
+import org.apache.jdo.impl.enhancer.classfile.ConstNameAndType;
+import org.apache.jdo.impl.enhancer.classfile.ConstantPool;
+import org.apache.jdo.impl.enhancer.classfile.Descriptor;
+import org.apache.jdo.impl.enhancer.classfile.Insn;
+import org.apache.jdo.impl.enhancer.classfile.InsnConstOp;
+import org.apache.jdo.impl.enhancer.classfile.VMConstants;
+import org.apache.jdo.impl.enhancer.meta.EnhancerMetaData;
+import org.apache.jdo.impl.enhancer.util.Support;
+
+
+
+
+
+/**
+ * Handles the augmentation actions for a method.
+ */
+class Annotater
+ extends Support
+ implements VMConstants
+{
+ /**
+ * The classfile's enhancement controller.
+ */
+ private final Controller control;
+
+ /**
+ * The class analyzer for this class.
+ */
+ private final Analyzer analyzer;
+
+ /**
+ * The classfile to be enhanced.
+ */
+ private final ClassFile classFile;
+
+ /**
+ * The class name in user ('.' delimited) form.
+ */
+ private final String userClassName;
+
+ /**
+ * The classfile's constant pool.
+ */
+ private final ConstantPool pool;
+
+ /**
+ * Repository for the enhancer options.
+ */
+ private final Environment env;
+
+ /**
+ * Repository for JDO meta-data on classes.
+ */
+ private final EnhancerMetaData meta;
+
+ /**
+ * Constructor
+ */
+ public Annotater(Controller control,
+ Analyzer analyzer,
+ Environment env)
+ {
+ affirm(control != null);
+ affirm(analyzer != null);
+ affirm(env != null);
+
+ this.control = control;
+ this.analyzer = analyzer;
+ this.env = env;
+ this.meta = env.getEnhancerMetaData();
+ this.classFile = control.getClassFile();
+ this.userClassName = classFile.userClassName();
+ this.pool = classFile.pool();
+
+ affirm(classFile != null);
+ affirm(userClassName != null);
+ affirm(meta != null);
+ affirm(pool != null);
+ }
+
+ /**
+ * Performs necessary annotation actions on the class.
+ */
+ public void annotate()
+ {
+ affirm(analyzer.isAnnotateable() && !env.noAnnotate());
+ env.message("annotating class " + userClassName);
+
+ boolean annotated = false;
+ for (final Iterator i = analyzer.getAnnotatableMethods().iterator();
+ i.hasNext();) {
+ final ClassMethod method = (ClassMethod)i.next();
+ annotated |= annotated(method);
+ }
+
+ // notify controller if class changed
+ if (annotated) {
+ control.noteUpdate();
+ }
+
+//^olsen: reenable
+/*
+ //@olsen: do special annotation if detected super.clone()
+ if ((annotate & SuperClone) != 0) {
+ //final String superName = control.classFile().superName().asString();
+ //annotateClone(method, superName);
+ }
+*/
+ }
+
+ /**
+ * Annotate the class method. For now, brute force rules.
+ */
+ private boolean annotated(ClassMethod method)
+ {
+ boolean annotated = false;
+ final CodeAttribute codeAttr = method.codeAttribute();
+ if (codeAttr == null) {
+ return annotated;
+ }
+
+ env.message(
+ "annotating: " + userClassName
+ + "." + method.name().asString()
+ + Descriptor.userMethodArgs(method.signature().asString()));
+
+ // first instruction is a target
+ final Insn firstInsn = codeAttr.theCode();
+ affirm(firstInsn.opcode() == Insn.opc_target);
+ Insn insn = firstInsn.next();
+ while (insn != null) {
+ switch(insn.opcode()) {
+ case opc_getfield:
+ case opc_putfield:
+ final Insn newInsn = insnAnnotation(insn);
+ if (insn != newInsn) {
+ annotated = true;
+ }
+ insn = newInsn;
+ break;
+ default:
+ }
+
+ insn = insn.next();
+ }
+
+ return annotated;
+ }
+
+ /**
+ * Generate annotations for put/getfield instructions.
+ */
+ private Insn insnAnnotation(final Insn insn)
+ {
+ if (false) {
+ System.out.println("MethodAnnotator.insnAnnotation(): ");
+ insn.printInsn(System.out);
+ System.out.println();
+ }
+
+ affirm(insn.opcode() == opc_getfield || insn.opcode() == opc_putfield);
+ final boolean isGet = (insn.opcode() == opc_getfield);
+
+ // get the instruction arguments
+ final InsnConstOp fieldInsn = (InsnConstOp)insn;
+ final ConstFieldRef fieldRef = (ConstFieldRef)fieldInsn.value();
+
+ final ConstNameAndType fieldNameAndType = fieldRef.nameAndType();
+ final String fieldName = fieldNameAndType.name().asString();
+ final String fieldType = fieldNameAndType.signature().asString();
+
+ final String qualifyingClassName = fieldRef.className().asString();
+ // get the field's declaring class from the model
+ final String declClassName =
+ meta.getDeclaringClass(qualifyingClassName, fieldName);
+ affirm(declClassName != null, "Cannot get declaring class of "
+ + qualifyingClassName + "." + fieldName);
+ final ConstClass declClass = pool.addClass(declClassName);
+
+ // check if field is known to be non-managed
+ if (meta.isKnownNonManagedField(declClassName, fieldName, fieldType)) {
+ return insn;
+ }
+
+ // never annotate a jdo field; such may occur in pre-enhanced clone()
+ if (meta.isPersistenceCapableClass(declClassName)
+ && (fieldName.equals(JDOConstants.JDO_PC_jdoStateManager_Name)
+ || fieldName.equals(JDOConstants.JDO_PC_jdoFlags_Name))) {
+ return insn;
+ }
+
+ if (false) {
+ System.out.println(" " + (isGet ? "get" : "put") + "field "
+ + declClassName + "." + fieldName
+ + " : " + fieldType);
+ }
+
+ final String methodName;
+ final String methodSig;
+ if (isGet) {
+ methodName = "jdoGet" + fieldName;
+ methodSig = "(L" + declClassName + ";)" + fieldType;
+ } else {
+ methodName = "jdoSet" + fieldName;
+ methodSig = "(L" + declClassName + ";" + fieldType + ")V";
+ }
+
+ if (false) {
+ System.out.println(" "
+ + declClassName + "." + methodName
+ + " : " + methodSig);
+ }
+
+ // call the PC's static accessor/mutator
+ final Insn frag = Insn.create(opc_invokestatic,
+ pool.addMethodRef(declClassName,
+ methodName,
+ methodSig));
+
+ //insn.prev().insert(Insn.create(opc_nop));
+ // replace instruction
+ final Insn prev = insn.prev();
+ insn.remove();
+
+ // replace current instruction with new fragment
+ final Insn last = prev.insert(frag);
+ return last;
+ }
+
+ private void annotateClone(ClassMethod method,
+ String superName)
+ {
+ //^olsen: extend for full support of inheritance on PC classes
+
+ if (false) {
+ final String methodName = method.name().asString();
+ final String methodSig = method.signature().asString();
+ System.out.println("annotateClone()");
+ System.out.println(" methodName = " + methodName);
+ System.out.println(" methodSig = " + methodSig);
+ System.out.println(" superName = " + superName);
+ }
+
+ final CodeAttribute codeAttr = method.codeAttribute();
+ for (Insn insn = codeAttr.theCode();
+ insn != null;
+ insn = insn.next()) {
+
+ // Found the clone method. See if it is the flavor of clone()
+ // which does a super.clone() call, and if it is, add
+ // field initializations for the jdoStateManager and jdoFlags
+ // fields.
+ if (insn.opcode() != opc_invokespecial)
+ continue;
+
+ final InsnConstOp invoke = (InsnConstOp)insn;
+ final ConstMethodRef methodRef = (ConstMethodRef)invoke.value();
+ final ConstNameAndType methodNT = methodRef.nameAndType();
+ final String methodName = methodNT.name().asString();
+ final String methodSig = methodNT.signature().asString();
+
+ if (!(methodName.equals("clone")
+ && methodSig.equals("()Ljava/lang/Object;")))
+ continue;
+
+ if (false) {
+ final ConstClass methodClass = methodRef.className();
+ final String methodClassName = methodClass.asString();
+ System.out.println(" found invocation of: "
+ + methodClassName
+ + "." + methodName + methodSig);
+ }
+
+ // check whether next instruction already is a downcast to a
+ // class implementing PersistenceCapable
+ final String thisClass = classFile.classNameString();
+ final Insn checkCastInsn = insn.next();
+ final boolean needCheckcast;
+ if (checkCastInsn.opcode() != opc_checkcast) {
+ needCheckcast = true;
+ } else {
+ ConstClass target =
+ (ConstClass) ((InsnConstOp)checkCastInsn).value();
+ if (target.asString().equals(thisClass)) {
+ insn = checkCastInsn;
+ needCheckcast = false;
+ } else {
+ needCheckcast = true;
+ }
+ }
+
+ // clear jdo fields of clone
+ {
+ // duplicate downcasted reference
+ final Insn newInsn = Insn.create(opc_dup);
+ if (needCheckcast) {
+ newInsn.append(Insn.create(opc_checkcast,
+ pool.addClass(thisClass)));
+ }
+ newInsn.append(Insn.create(opc_dup));
+
+ // clear jdo fields
+ newInsn.append(Insn.create(opc_aconst_null));
+ newInsn.append(Insn.create(
+ opc_putfield,
+ pool.addFieldRef(
+ thisClass,
+ JDOConstants.JDO_PC_jdoStateManager_Name,
+ JDOConstants.JDO_PC_jdoStateManager_Sig)));
+ newInsn.append(Insn.create(opc_iconst_0));
+ newInsn.append(Insn.create(
+ opc_putfield,
+ pool.addFieldRef(
+ thisClass,
+ JDOConstants.JDO_PC_jdoFlags_Name,
+ JDOConstants.JDO_PC_jdoFlags_Sig)));
+
+ // insert code
+ insn.insert(newInsn);
+
+ // increase stack
+ final int annotationStack = 3;
+ codeAttr.setStackUsed(codeAttr.stackUsed() + annotationStack);
+ }
+ }
+ }
+}