You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@openjpa.apache.org by mp...@apache.org on 2007/07/01 21:37:05 UTC

svn commit: r552358 [1/2] - in /openjpa/trunk: openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/ant/ openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/meta/ openjpa-jdbc/src/main/resources/org/apache/openjpa/jdbc/meta/ openjpa-kernel/src/main/java/o...

Author: mprudhom
Date: Sun Jul  1 12:37:04 2007
New Revision: 552358

URL: http://svn.apache.org/viewvc?view=rev&rev=552358
Log:
OPENJPA-239 Patch to support the generation of annotation mappings using the reverse mapping tool

Added:
    openjpa/trunk/openjpa-persistence-jdbc/src/main/java/org/apache/openjpa/persistence/jdbc/AnnotationPersistenceMappingSerializer.java   (with props)
    openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/AnnotationBuilder.java   (with props)
    openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/AnnotationPersistenceMetaDataSerializer.java   (with props)
Modified:
    openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/ant/ReverseMappingToolTask.java
    openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/meta/ReverseMappingTool.java
    openjpa/trunk/openjpa-jdbc/src/main/resources/org/apache/openjpa/jdbc/meta/localizer.properties
    openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/ee/RegistryManagedRuntime.java   (props changed)
    openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/enhance/CodeGenerator.java
    openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/meta/AbstractCFMetaDataFactory.java
    openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/meta/MetaDataModes.java
    openjpa/trunk/openjpa-lib/src/main/java/org/apache/openjpa/lib/util/CodeFormat.java
    openjpa/trunk/openjpa-persistence-jdbc/src/main/java/org/apache/openjpa/persistence/jdbc/PersistenceMappingFactory.java
    openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/PersistenceMetaDataFactory.java
    openjpa/trunk/openjpa-project/src/doc/manual/ref_guide_mapping.xml

Modified: openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/ant/ReverseMappingToolTask.java
URL: http://svn.apache.org/viewvc/openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/ant/ReverseMappingToolTask.java?view=diff&rev=552358&r1=552357&r2=552358
==============================================================================
--- openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/ant/ReverseMappingToolTask.java (original)
+++ openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/ant/ReverseMappingToolTask.java Sun Jul  1 12:37:04 2007
@@ -123,6 +123,14 @@
     }
 
     /**
+     * Set whether to use generic collections on one-to-many and many-to-many
+     * relations instead of untyped collections.
+     */
+    public void setUseGenericCollections(boolean useGenericCollections) {
+        flags.useGenericCollections = useGenericCollections; 
+    }
+
+    /**
      * Set the SQL type map overrides.
      */
     public void setTypeMap(String typeMap) {
@@ -202,6 +210,22 @@
     }
 
     /**
+     * Whether to generate annotations along with generated code. Defaults
+     * to false.
+     */
+    public void setGenerateAnnotations(boolean genAnnotations) {
+        flags.generateAnnotations = genAnnotations;
+    }
+
+    /**
+     * Whether to use field or property-based access on generated code.
+     * Defaults to field-based access.
+     */
+    public void setAccessType(AccessType accessType) {
+        flags.accessType = accessType.getValue();
+    }
+    
+    /**
      * Set a customizer class to use.
      */
     public void setCustomizerClass(String customizerClass) {
@@ -255,6 +279,18 @@
             return new String[]{
                 "package",
                 "class",
+                "none"
+            };
+        }
+    }
+
+    public static class AccessType
+        extends EnumeratedAttribute {
+
+        public String[] getValues() {
+            return new String[]{
+                "field",
+                "property"
             };
         }
     }

Modified: openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/meta/ReverseMappingTool.java
URL: http://svn.apache.org/viewvc/openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/meta/ReverseMappingTool.java?view=diff&rev=552358&r1=552357&r2=552358
==============================================================================
--- openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/meta/ReverseMappingTool.java (original)
+++ openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/meta/ReverseMappingTool.java Sun Jul  1 12:37:04 2007
@@ -136,9 +136,16 @@
      */
     public static final int TABLE_SUBCLASS = 5;
 
+    public static final String LEVEL_NONE = "none";
     public static final String LEVEL_PACKAGE = "package";
     public static final String LEVEL_CLASS = "class";
 
+    /**
+     * Access type for generated source, defaults to field-based access.
+     */
+    public static final String ACCESS_TYPE_FIELD = "field";
+    public static final String ACCESS_TYPE_PROPERTY = "property";
+
     private static Localizer _loc = Localizer.forPackage
         (ReverseMappingTool.class);
 
@@ -176,6 +183,7 @@
     private SchemaGroup _schema = null;
     private boolean _nullAsObj = false;
     private boolean _blobAsObj = false;
+    private boolean _useGenericColl = false;
     private Properties _typeMap = null;
     private boolean _useFK = false;
     private boolean _useSchema = false;
@@ -186,6 +194,8 @@
     private String _idSuffix = "Id";
     private boolean _inverse = true;
     private boolean _detachable = false;
+    private boolean _genAnnotations = false;
+    private String _accessType = ACCESS_TYPE_FIELD;
     private CodeFormat _format = null;
     private ReverseCustomizer _custom = null;
     private String _discStrat = null;
@@ -196,6 +206,9 @@
     // mess up certain customizers (bug 881)
     private Set _abandonedFieldNames = null;
 
+    // generated annotations, key = metadata, val = list of annotations
+    private Map _annos = null;
+
     /**
      * Constructor. Supply configuration.
      */
@@ -327,6 +340,22 @@
     }
 
     /**
+     * Whether to use generic collections on one-to-many and many-to-many
+     * relations instead of untyped collections.
+     */
+    public boolean getUseGenericCollections() {
+        return _useGenericColl;
+    }
+
+    /**
+     * Whether to use generic collections on one-to-many and many-to-many
+     * relations instead of untyped collections.
+     */
+    public void setUseGenericCollections(boolean useGenericCollections) {
+        _useGenericColl = useGenericCollections; 
+    }
+
+    /**
      * Map of JDBC-name to Java-type-name entries that allows customization
      * of reverse mapping columns to field types.
      */
@@ -479,6 +508,39 @@
     }
 
     /**
+     * Whether to generate annotations along with generated code. Defaults
+     * to false.
+     */
+    public boolean getGenerateAnnotations() {
+        return _genAnnotations;
+    }
+
+    /**
+     * Whether to generate annotations along with generated code. Defaults
+     * to false.
+     */
+    public void setGenerateAnnotations(boolean genAnnotations) {
+        _genAnnotations = genAnnotations;
+    }
+
+    /**
+     * Whether to use field or property-based access on generated code.
+     * Defaults to field-based access.
+     */
+    public String getAccessType() {
+        return _accessType;
+    }
+
+    /**
+     * Whether to use field or property-based access on generated code.
+     * Defaults to field-based access.
+     */
+    public void setAccessType(String accessType) {
+        this._accessType = ACCESS_TYPE_PROPERTY.equalsIgnoreCase(accessType) ?
+            ACCESS_TYPE_PROPERTY : ACCESS_TYPE_FIELD;
+    }
+
+    /**
      * The code formatter for the generated Java code.
      */
     public CodeFormat getCodeFormat() {
@@ -784,7 +846,11 @@
                 _log.info(_loc.get("class-code", mappings[i]));
 
             ApplicationIdTool aid = newApplicationIdTool(mappings[i]);
-            gen = new ReverseCodeGenerator(mappings[i], aid);
+            if (getGenerateAnnotations())
+                gen = new AnnotatedCodeGenerator(mappings[i], aid);
+            else
+                gen = new ReverseCodeGenerator(mappings[i], aid);
+
             gen.generateCode();
 
             if (output == null) {
@@ -852,6 +918,31 @@
         return files;
     }
 
+    public void buildAnnotations() {
+        Map output = new HashMap();
+        // pretend mappings are all resolved
+        ClassMapping[] mappings = getMappings();
+        for (int i = 0; i < mappings.length; i++)
+            mappings[i].setResolve(MODE_META | MODE_MAPPING, true);
+
+        // store in user's configured IO
+        MetaDataFactory mdf = _conf.newMetaDataFactoryInstance();
+        mdf.setRepository(getRepository());
+        mdf.setStoreDirectory(_dir);
+        mdf.store(mappings, new QueryMetaData[0], new SequenceMetaData[0],
+            MODE_META | MODE_MAPPING | MODE_ANN_MAPPING, output);
+        _annos = output;
+    }
+
+    /**
+     * Returns a list of stringified annotations for specified meta.
+     */
+    protected List getAnnotationsForMeta(Object meta) {
+        if (null == _annos)
+            return null;
+        return (List) _annos.get(meta);
+    }
+
     /**
      * Generate and write the application identity code.
      */
@@ -1619,6 +1710,7 @@
         tool.setUseForeignKeyName(getUseForeignKeyName());
         tool.setNullableAsObject(getNullableAsObject());
         tool.setBlobAsObject(getBlobAsObject());
+        tool.setUseGenericCollections(getUseGenericCollections());
         tool.setPrimaryKeyOnJoin(getPrimaryKeyOnJoin());
         tool.setUseDataStoreIdentity(getUseDataStoreIdentity());
         tool.setUseBuiltinIdentityClass(getUseBuiltinIdentityClass());
@@ -1626,6 +1718,7 @@
         tool.setIdentityClassSuffix(getIdentityClassSuffix());
         tool.setInverseRelations(getInverseRelations());
         tool.setDetachable(getDetachable());
+        tool.setGenerateAnnotations(getGenerateAnnotations());
         tool.setCustomizer(getCustomizer());
         tool.setCodeFormat(getCodeFormat());
         return tool;
@@ -1667,6 +1760,9 @@
      * type instead.</li>
      * <li><i>-blobAsObject/-bo &lt;true/t | false/f&gt;</i>: Set to true
      * to make all binary columns map to Object rather than byte[].</li>
+     * <li><i>-useGenericCollections/-gc &lt;true/t | false/f&gt;</i>: Set to
+     * true to use generic collections on OneToMany and ManyToMany relations
+     * (requires JDK 1.5 or higher).</li>
      * <li><i>-typeMap/-typ &lt;types&gt;</i>: Default mapping of SQL type
      * names to Java classes.</li>
      * <li><i>-primaryKeyOnJoin/-pkj &lt;true/t | false/f&gt;</i>: Set to true
@@ -1690,9 +1786,13 @@
      * discriminator strategy to place on base classes.</li>
      * <li><i>-versionStrategy/-vs &lt;strategy&gt;</i>: The default
      * version strategy to place on base classes.</li>
-     * <li><i>-metadata/-md &lt;class | package&gt;</i>: Specify the level the
-     * metadata should be generated at. Defaults to generating a
+     * <li><i>-metadata/-md &lt;class | package | none&gt;</i>: Specify the
+     * level the metadata should be generated at. Defaults to generating a
      * single package-level metadata file.</li>
+     * <li><i>-annotations/-ann &lt;true/t | false/f&gt;</i>: Set to true to
+     * generate JPA annotations in generated code.</li>
+     * <li><i>-accessType/-access &lt;field | property&gt;</i>: Change access
+     * type for generated annotations. Defaults to field access.</li>
      * <li><i>-customizerClass/-cc &lt;class name&gt;</i>: The full class
      * name of a {@link ReverseCustomizer} implementation to use to
      * customize the reverse mapping process. Optional.</li>
@@ -1749,6 +1849,8 @@
             ("nullableAsObject", "no", flags.nullableAsObject);
         flags.blobAsObject = opts.removeBooleanProperty
             ("blobAsObject", "bo", flags.blobAsObject);
+        flags.useGenericCollections = opts.removeBooleanProperty
+            ("useGenericCollections", "gc", flags.useGenericCollections);
         flags.primaryKeyOnJoin = opts.removeBooleanProperty
             ("primaryKeyOnJoin", "pkj", flags.primaryKeyOnJoin);
         flags.useDataStoreIdentity = opts.removeBooleanProperty
@@ -1768,7 +1870,11 @@
         flags.versionStrategy = opts.removeProperty
             ("versionStrategy", "vs", flags.versionStrategy);
         flags.metaDataLevel = opts.removeProperty
-            ("metadata", "md", flags.metaDataLevel);
+            ("metadata", "md", flags.metaDataLevel);        
+        flags.generateAnnotations = opts.removeBooleanProperty
+            ("annotations", "ann", flags.generateAnnotations);
+        flags.accessType = opts.removeProperty
+            ("accessType", "access", flags.accessType);
 
         String typeMap = opts.removeProperty("typeMap", "typ", null);
         if (typeMap != null)
@@ -1869,6 +1975,7 @@
         tool.setUseForeignKeyName(flags.useForeignKeyName);
         tool.setNullableAsObject(flags.nullableAsObject);
         tool.setBlobAsObject(flags.blobAsObject);
+        tool.setUseGenericCollections(flags.useGenericCollections);
         tool.setTypeMap(flags.typeMap);
         tool.setPrimaryKeyOnJoin(flags.primaryKeyOnJoin);
         tool.setUseDataStoreIdentity(flags.useDataStoreIdentity);
@@ -1877,16 +1984,24 @@
         tool.setIdentityClassSuffix(flags.identityClassSuffix);
         tool.setInverseRelations(flags.inverseRelations);
         tool.setDetachable(flags.detachable);
+        tool.setGenerateAnnotations(flags.generateAnnotations);
+        tool.setAccessType(flags.accessType);
         tool.setCustomizer(flags.customizer);
         tool.setCodeFormat(flags.format);
 
         // run
         log.info(_loc.get("revtool-map"));
         tool.run();
+        if (flags.generateAnnotations) {
+            log.info(_loc.get("revtool-gen-annos"));
+            tool.buildAnnotations();
+        }
         log.info(_loc.get("revtool-write-code"));
         tool.recordCode();
-        log.info(_loc.get("revtool-write-metadata"));
-        tool.recordMetaData(LEVEL_CLASS.equals(flags.metaDataLevel));
+        if (!LEVEL_NONE.equals(flags.metaDataLevel)) {
+            log.info(_loc.get("revtool-write-metadata"));
+            tool.recordMetaData(LEVEL_CLASS.equals(flags.metaDataLevel));
+        }
     }
 
     /**
@@ -1900,6 +2015,7 @@
         public boolean useForeignKeyName = false;
         public boolean nullableAsObject = false;
         public boolean blobAsObject = false;
+        public boolean useGenericCollections = false;
         public Properties typeMap = null;
         public boolean primaryKeyOnJoin = false;
         public boolean useDataStoreIdentity = false;
@@ -1908,6 +2024,8 @@
         public String identityClassSuffix = "Id";
         public boolean inverseRelations = true;
         public boolean detachable = false;
+        public boolean generateAnnotations = false;
+        public String accessType = ACCESS_TYPE_FIELD;
         public String metaDataLevel = LEVEL_PACKAGE;
         public String discriminatorStrategy = null;
         public String versionStrategy = null;
@@ -1983,8 +2101,8 @@
     private class ReverseCodeGenerator
         extends CodeGenerator {
 
-        private final ClassMapping _mapping;
-        private final ApplicationIdTool _appid;
+        protected final ClassMapping _mapping;
+        protected final ApplicationIdTool _appid;
 
         public ReverseCodeGenerator(ClassMapping mapping,
             ApplicationIdTool aid) {
@@ -2046,5 +2164,37 @@
                 return null;
             return _custom.getFieldCode((FieldMapping) field);
         }
+
+        protected boolean useGenericCollections() {
+            return _useGenericColl;
+        }
+    }
+
+    private class AnnotatedCodeGenerator
+        extends ReverseCodeGenerator {
+
+        public AnnotatedCodeGenerator (ClassMapping mapping,
+            ApplicationIdTool aid) {
+            super (mapping, aid);
+        }
+
+        public Set getImportPackages() {
+            Set pkgs = super.getImportPackages();
+            pkgs.add("javax.persistence");
+            return pkgs;
+        }
+
+        protected List getClassAnnotations() {
+            return getAnnotationsForMeta(_mapping);
+        }
+
+        protected List getFieldAnnotations(FieldMetaData field) {
+            return getAnnotationsForMeta(field);
+        }
+
+        protected boolean usePropertyBasedAccess () {
+            return ACCESS_TYPE_PROPERTY.equals(_accessType);
+        }
+
     }
 }

Modified: openjpa/trunk/openjpa-jdbc/src/main/resources/org/apache/openjpa/jdbc/meta/localizer.properties
URL: http://svn.apache.org/viewvc/openjpa/trunk/openjpa-jdbc/src/main/resources/org/apache/openjpa/jdbc/meta/localizer.properties?view=diff&rev=552358&r1=552357&r2=552358
==============================================================================
--- openjpa/trunk/openjpa-jdbc/src/main/resources/org/apache/openjpa/jdbc/meta/localizer.properties (original)
+++ openjpa/trunk/openjpa-jdbc/src/main/resources/org/apache/openjpa/jdbc/meta/localizer.properties Sun Jul  1 12:37:04 2007
@@ -359,6 +359,7 @@
 revtool-write-code: Writing generated class source code.
 revtool-write-appid: Writing generated application identity classes.
 revtool-write-metadata: Writing generated metadata.
+revtool-gen-annos: Generating annotations.
 revtool-usage: Usage: java org.apache.openjpa.jdbc.meta.ReverseMappingTool\n\
 	\t[-properties/-p <properties file or resource>]\n\
 	\t[-<property name> <property value>]*\n\
@@ -369,6 +370,7 @@
 	\t[-useForeignKeyName/-fkn <true/t | false/f>]\n\
 	\t[-nullableAsObject/-no <true/t | false/f>]\n\
 	\t[-blobAsObject/-bo <true/t | false/f>]\n\
+	\t[-useGenericCollections/-gc <true/t | false/f>]\n\
 	\t[-typeMap/-type <types>]\n\
 	\t[-primaryKeyOnJoin/-pkj <true/t | false/f>]\n\
 	\t[-useDatastoreIdentity/-ds <true/t | false/f>]\n\
@@ -379,7 +381,9 @@
 	\t[-detachable/-det <true/t | false/f>]\n\
 	\t[-discriminatorStrategy/-ds <strategy>]\n\
 	\t[-versionStrategy/-vs <strategy>]\n\
-	\t[-metadata/-md <package | class>]\n\
+	\t[-metadata/-md <package | class | none>]\n\
+	\t[-annotations/-ann <true/t | false/f>]\n\
+	\t[-accessType/-access <field | property>]\n\
 	\t[-customizerClass/-cc <full class name>]\n\
 	\t[-customizerProperties/-cp <properties file or resource>]\n\
 	\t[-customizer/-c.<property name> <property value>]*\n\

Propchange: openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/ee/RegistryManagedRuntime.java
------------------------------------------------------------------------------
    svn:eol-style = native

Modified: openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/enhance/CodeGenerator.java
URL: http://svn.apache.org/viewvc/openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/enhance/CodeGenerator.java?view=diff&rev=552358&r1=552357&r2=552358
==============================================================================
--- openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/enhance/CodeGenerator.java (original)
+++ openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/enhance/CodeGenerator.java Sun Jul  1 12:37:04 2007
@@ -24,8 +24,10 @@
 import java.util.Iterator;
 import java.util.Set;
 import java.util.TreeSet;
+import java.util.List;
 
 import org.apache.commons.lang.StringUtils;
+import org.apache.commons.lang.WordUtils;
 import org.apache.openjpa.conf.OpenJPAConfiguration;
 import org.apache.openjpa.lib.util.CodeFormat;
 import org.apache.openjpa.lib.util.Files;
@@ -307,27 +309,47 @@
             propertyName = propertyName.substring(1);
         String fieldType = Strings.getClassName(fmd.getDeclaredType());
 
+        String keyType = null;
+        String elementType = null;
+        String paramType = "";
+        if (useGenericCollections()) {
+            if (fmd.getDeclaredTypeCode() == JavaTypes.COLLECTION) {
+                Class elmCls = fmd.getElement().getDeclaredType();
+                elementType = Strings.getClassName(elmCls);
+                paramType = decs.getParametrizedType(
+                    new String[] {elementType});
+            } else if (fmd.getDeclaredTypeCode() == JavaTypes.MAP) {
+                Class keyCls = fmd.getKey().getDeclaredType();
+                Class elmCls = fmd.getElement().getDeclaredType();
+                keyType = Strings.getClassName(keyCls);
+                elementType = Strings.getClassName(elmCls);
+                paramType = decs.getParametrizedType(
+                    new String[] {keyType, elementType});
+            }
+        }
+
         String fieldValue = getInitialValue(fmd);
         if (fieldValue == null) {
             if ("Set".equals(fieldType))
-                fieldValue = "new HashSet" + decs.getParens();
+                fieldValue = "new HashSet" + paramType + decs.getParens();
             else if ("TreeSet".equals(fieldType))
-                fieldValue = "new TreeSet" + decs.getParens();
+                fieldValue = "new TreeSet" + paramType + decs.getParens();
             else if ("Collection".equals(fieldType))
-                fieldValue = "new ArrayList" + decs.getParens();
+                fieldValue = "new ArrayList" + paramType + decs.getParens();
             else if ("Map".equals(fieldType))
-                fieldValue = "new HashMap" + decs.getParens();
+                fieldValue = "new HashMap" + paramType + decs.getParens();
             else if ("TreeMap".equals(fieldType))
-                fieldValue = "new TreeMap" + decs.getParens();
-            else if (fmd.getDeclaredTypeCode() == JavaTypes.COLLECTION
-                || fmd.getDeclaredTypeCode() == JavaTypes.MAP)
-                fieldValue = "new " + fieldType + decs.getParens();
+                fieldValue = "new TreeMap" + paramType + decs.getParens();
+            else if (fmd.getDeclaredTypeCode() == JavaTypes.COLLECTION ||
+                fmd.getDeclaredTypeCode() == JavaTypes.MAP)
+                fieldValue = "new " + fieldType + paramType + decs.getParens();
             else
                 fieldValue = "";
         }
         if (fieldValue.length() > 0)
             fieldValue = " = " + fieldValue;
 
+        boolean fieldAccess = !usePropertyBasedAccess();
         String custom = getDeclaration(fmd);
         if (decs.length() > 0)
             decs.endl();
@@ -339,12 +361,18 @@
             templ.setParameter("capFieldName", capFieldName);
             templ.setParameter("propertyName", propertyName);
             templ.setParameter("fieldType", fieldType);
+            templ.setParameter("keyType", keyType);
+            templ.setParameter("elementType", elementType);
             templ.setParameter("fieldValue", fieldValue);
             decs.append(templ.toString());
         } else {
+            if (fieldAccess)
+                writeAnnotations(decs, getFieldAnnotations(fmd), 1);
             decs.tab().append("private ").append(fieldType).
-                append(" ").append(fieldName).append(fieldValue).
-                append(";");
+                append(paramType).append(" ").append(fieldName).
+                append(fieldValue).append(";");
+            if (fieldAccess)
+                decs.endl();
         }
 
         custom = getFieldCode(fmd);
@@ -357,11 +385,16 @@
             templ.setParameter("capFieldName", capFieldName);
             templ.setParameter("propertyName", propertyName);
             templ.setParameter("fieldType", fieldType);
+            templ.setParameter("keyType", keyType);
+            templ.setParameter("elementType", elementType);
             templ.setParameter("fieldValue", fieldValue);
             code.append(templ.toString());
         } else {
             // getter
-            code.tab().append("public ").append(fieldType).append(" ");
+            if (!fieldAccess)
+                writeAnnotations(code, getFieldAnnotations(fmd), 1);
+            code.tab().append("public ").append(fieldType).append(paramType).
+                 append(" ");
             if ("boolean".equalsIgnoreCase(fieldType))
                 code.append("is");
             else
@@ -374,8 +407,8 @@
 
             // setter
             code.tab().append("public void set").append(capFieldName);
-            code.openParen(true).append(fieldType).append(" ").
-                append(propertyName).closeParen();
+            code.openParen(true).append(fieldType).append(paramType).
+                append(" ").append(propertyName).closeParen();
             code.openBrace(2).endl();
             code.tab(2);
             if (propertyName.equals(fieldName))
@@ -403,6 +436,7 @@
             append(" * ").append(getClass().getName()).endl().
             append(" */").endl();
 
+        writeAnnotations(code, getClassAnnotations(), 0);
         code.append("public class ").append(className);
         if (extendsName.length() > 0)
             code.extendsDec(1).append(" ").append(extendsName);
@@ -427,6 +461,21 @@
     }
 
     /**
+     * Appends the given list of annotations to code buffer.
+     */
+    private void writeAnnotations (CodeFormat code, List ann,
+        int tabLevel) {
+        if (ann == null || ann.size() == 0)
+            return;
+        for (Iterator i = ann.iterator(); i.hasNext();) {
+            if (tabLevel > 0)
+                code.tab(tabLevel);
+            String s = (String) i.next();
+            code.append(s).endl();
+        }
+    }
+
+    /**
      * Append the opening code-level brace to the code; this can be
      * overridden to add code to the top of the class.
      */
@@ -503,6 +552,9 @@
      * <li>${capFieldName}: The capitalized field name.</li>
      * <li>${propertyName}: The field name without leading '_', if any.</li>
      * <li>${fieldType}: The field's type name.</li>
+     * <li>${keyType}: Key type name for maps, null otherwise.</li>
+     * <li>${elementType}: Element type name for collections, null otherwise.
+     * </li>
      * <li>${fieldValue}: The field's initial value, in the form
      * " = &lt;value&gt;", or empty string if none.</li>
      * </ul> Returns null by default.
@@ -521,6 +573,9 @@
      * <li>${capFieldName}: The capitalized field name.</li>
      * <li>${propertyName}: The field name without leading '_', if any.</li>
      * <li>${fieldType}: The field's type name.</li>
+     * <li>${keyType}: Key type name for maps, null otherwise.</li>
+     * <li>${elementType}: Element type name for collections, null otherwise.
+     * </li>
      * <li>${fieldValue}: The field's initial value, in the form
      * "= &lt;value&gt;", or empty string if none.</li>
      * </ul>
@@ -529,4 +584,37 @@
 	{
 		return null;
 	}
+
+    /**
+     * Whether to use property-based access on generated code.
+     * Defaults to false (field-based).
+     */    
+    protected boolean usePropertyBasedAccess () {
+        return false;
+    }
+
+    /**
+     * Return class-level annotations. Returns null by default.
+     */
+    protected List getClassAnnotations() {
+        return null;
+    }
+
+    /**
+     * Return field-level annotations. Returns null by default.
+     */
+    protected List getFieldAnnotations(FieldMetaData field) {
+        return null;
+    }
+
+    /**
+     * Whether to use generic collections on one-to-many and many-to-many
+     * relations instead of untyped collections.
+     *
+     * Override in descendants to change default behavior.
+     */
+    protected boolean useGenericCollections() {
+        return false;
+    }
+
 }

Modified: openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/meta/AbstractCFMetaDataFactory.java
URL: http://svn.apache.org/viewvc/openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/meta/AbstractCFMetaDataFactory.java?view=diff&rev=552358&r1=552357&r2=552358
==============================================================================
--- openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/meta/AbstractCFMetaDataFactory.java (original)
+++ openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/meta/AbstractCFMetaDataFactory.java Sun Jul  1 12:37:04 2007
@@ -192,7 +192,10 @@
         Parser parser;
         if (mode != MODE_QUERY) {
             int sermode = (isMappingOnlyFactory()) ? mode : mode | MODE_META;
-            ser = newSerializer();
+            if ((mode & MODE_ANN_MAPPING) != 0)
+                ser = newAnnotationSerializer();
+            else
+                ser = newSerializer();
             ser.setMode(sermode);
             if (metaFiles != null) {
                 parser = newParser(false);
@@ -231,7 +234,10 @@
             for (int i = 0; !qFiles && i < queries.length; i++)
                 qFiles = queries[i].getSourceMode() == MODE_QUERY;
             if (qFiles) {
-                ser = newSerializer();
+                if ((mode & MODE_ANN_MAPPING) != 0)
+                    ser = newAnnotationSerializer();
+                else
+                    ser = newSerializer();
                 ser.setMode(MODE_QUERY);
                 if (queryFiles != null) {
                     parser = newParser(false);
@@ -542,6 +548,11 @@
      * Create a new metadata serializer.
      */
     protected abstract Serializer newSerializer();
+
+    /**
+     * Create a new annotation metadata serializer.
+     */
+    protected abstract Serializer newAnnotationSerializer();
 
     /**
      * Return the metadata that defines the given query, if any.

Modified: openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/meta/MetaDataModes.java
URL: http://svn.apache.org/viewvc/openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/meta/MetaDataModes.java?view=diff&rev=552358&r1=552357&r2=552358
==============================================================================
--- openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/meta/MetaDataModes.java (original)
+++ openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/meta/MetaDataModes.java Sun Jul  1 12:37:04 2007
@@ -32,4 +32,5 @@
     public static final int MODE_MAPPING = 2;
     public static final int MODE_QUERY = 4;
     public static final int MODE_MAPPING_INIT = 8;
+    public static final int MODE_ANN_MAPPING = 16;
 }

Modified: openjpa/trunk/openjpa-lib/src/main/java/org/apache/openjpa/lib/util/CodeFormat.java
URL: http://svn.apache.org/viewvc/openjpa/trunk/openjpa-lib/src/main/java/org/apache/openjpa/lib/util/CodeFormat.java?view=diff&rev=552358&r1=552357&r2=552358
==============================================================================
--- openjpa/trunk/openjpa-lib/src/main/java/org/apache/openjpa/lib/util/CodeFormat.java (original)
+++ openjpa/trunk/openjpa-lib/src/main/java/org/apache/openjpa/lib/util/CodeFormat.java Sun Jul  1 12:37:04 2007
@@ -308,6 +308,22 @@
         return tabs.toString();
     }
 
+
+    /**
+     * Returns parametrized type string for given type(s).
+     */
+    public String getParametrizedType(String[] typenames) {
+        StringBuffer buf = new StringBuffer ();
+        buf.append("<");
+        for (int i = 0; i < typenames.length; i++) {
+            if (i > 0)
+                buf.append(", ");
+            buf.append(typenames[i]);
+        }
+        buf.append(">");
+        return buf.toString();
+    }
+
     /**
      * Return the field name for given suggested name, possibly adding
      * leading underscore.

Added: openjpa/trunk/openjpa-persistence-jdbc/src/main/java/org/apache/openjpa/persistence/jdbc/AnnotationPersistenceMappingSerializer.java
URL: http://svn.apache.org/viewvc/openjpa/trunk/openjpa-persistence-jdbc/src/main/java/org/apache/openjpa/persistence/jdbc/AnnotationPersistenceMappingSerializer.java?view=auto&rev=552358
==============================================================================
--- openjpa/trunk/openjpa-persistence-jdbc/src/main/java/org/apache/openjpa/persistence/jdbc/AnnotationPersistenceMappingSerializer.java (added)
+++ openjpa/trunk/openjpa-persistence-jdbc/src/main/java/org/apache/openjpa/persistence/jdbc/AnnotationPersistenceMappingSerializer.java Sun Jul  1 12:37:04 2007
@@ -0,0 +1,793 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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.openjpa.persistence.jdbc;
+
+import org.apache.openjpa.persistence.AnnotationPersistenceMetaDataSerializer;
+import org.apache.openjpa.persistence.PersistenceStrategy;
+import org.apache.openjpa.persistence.AnnotationBuilder;
+import org.apache.openjpa.jdbc.meta.QueryResultMapping;
+import org.apache.openjpa.jdbc.meta.MappingRepository;
+import org.apache.openjpa.jdbc.meta.ClassMapping;
+import org.apache.openjpa.jdbc.meta.FieldMapping;
+import org.apache.openjpa.jdbc.meta.ClassMappingInfo;
+import org.apache.openjpa.jdbc.meta.DiscriminatorMappingInfo;
+import org.apache.openjpa.jdbc.meta.MappingInfo;
+import org.apache.openjpa.jdbc.meta.SequenceMapping;
+import org.apache.openjpa.jdbc.meta.ValueMappingInfo;
+import org.apache.openjpa.jdbc.meta.strats.FlatClassStrategy;
+import org.apache.openjpa.jdbc.meta.strats.VerticalClassStrategy;
+import org.apache.openjpa.jdbc.meta.strats.FullClassStrategy;
+import org.apache.openjpa.jdbc.meta.strats.EnumValueHandler;
+import org.apache.openjpa.jdbc.conf.JDBCConfiguration;
+import org.apache.openjpa.jdbc.schema.*;
+import org.apache.openjpa.jdbc.schema.Unique;
+import org.apache.openjpa.jdbc.sql.DBDictionary;
+import org.apache.openjpa.meta.MetaDataRepository;
+import org.apache.openjpa.meta.ClassMetaData;
+import org.apache.openjpa.meta.FieldMetaData;
+import org.apache.openjpa.meta.JavaTypes;
+import org.apache.openjpa.meta.SequenceMetaData;
+import org.apache.openjpa.meta.MetaDataModes;
+import org.apache.commons.lang.StringUtils;
+
+import java.util.List;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Map;
+import java.util.HashMap;
+import java.sql.Types;
+import java.lang.annotation.Annotation;
+
+import serp.util.Strings;
+
+import javax.persistence.TemporalType;
+import javax.persistence.EnumType;
+import javax.persistence.InheritanceType;
+import javax.persistence.Table;
+import javax.persistence.SecondaryTable;
+import javax.persistence.Inheritance;
+import javax.persistence.DiscriminatorValue;
+import javax.persistence.DiscriminatorColumn;
+import javax.persistence.JoinTable;
+import javax.persistence.Lob;
+import javax.persistence.Temporal;
+import javax.persistence.Enumerated;
+import javax.persistence.UniqueConstraint;
+import javax.persistence.TableGenerator;
+import javax.persistence.JoinColumns;
+import javax.persistence.JoinColumn;
+import javax.persistence.PrimaryKeyJoinColumn;
+import javax.persistence.PrimaryKeyJoinColumns;
+import javax.persistence.SqlResultSetMapping;
+import javax.persistence.EntityResult;
+import javax.persistence.FieldResult;
+import javax.persistence.ColumnResult;
+
+//@todo: javadocs
+
+/**
+ * Serializes persistence mappings as annotations.
+ *
+ * @since 1.0.0
+ * @author Steve Kim
+ * @author Gokhan Ergul
+ * @nojavadoc
+ */
+public class AnnotationPersistenceMappingSerializer
+    extends AnnotationPersistenceMetaDataSerializer {
+
+    private static final int TYPE_RESULTMAP = TYPE_QUERY + 1;
+
+    private List<QueryResultMapping> _results = null;
+    private boolean _sync = false;
+
+    private Map<QueryResultMapping, List<AnnotationBuilder>> _rsmAnnos = null;
+    
+    /**
+     * Constructor. Supply configuration.
+     */
+    public AnnotationPersistenceMappingSerializer(JDBCConfiguration conf) {
+        super(conf);
+    }
+
+    /**
+     * Whether to automatically synchronize mapping info with data available
+     * from mapped components before serialization. Defaults to false.
+     */
+    public boolean getSyncMappingInfo() {
+        return _sync;
+    }
+
+    /**
+     * Whether to automatically synchronize mapping info with data available
+     * from mapped components before serialization. Defaults to false.
+     */
+    public void setSyncMappingInfo(boolean sync) {
+        _sync = sync;
+    }
+
+    /**
+     * Adds the given result set mapping to local cache.
+     */
+    public void addQueryResultMapping(QueryResultMapping meta) {
+        if (_results == null)
+            _results = new ArrayList<QueryResultMapping>();
+        _results.add(meta);
+    }
+
+    /**
+     * Removes given result set mapping from the local cache.
+     */
+    public boolean removeQueryResultMapping(QueryResultMapping meta) {
+        return _results != null && _results.remove(meta);
+    }
+
+    @Override
+    public void addAll(MetaDataRepository repos) {
+        super.addAll(repos);
+        for (QueryResultMapping res : ((MappingRepository) repos)
+            .getQueryResultMappings())
+            addQueryResultMapping(res);
+    }
+
+    @Override
+    public boolean removeAll(MetaDataRepository repos) {
+        boolean removed = super.removeAll(repos);
+        for (QueryResultMapping res : ((MappingRepository) repos)
+            .getQueryResultMappings())
+            removed |= removeQueryResultMapping(res);
+        return removed;
+    }
+
+    @Override
+    public void clear() {
+        super.clear();
+        if (_results != null)
+            _results.clear();
+    }
+
+    /**
+     * Add an annotation builder to list of builders for the specified
+     * class metadata.
+     */
+    protected void addAnnotation(AnnotationBuilder ab, QueryResultMapping meta) {
+        if (_rsmAnnos == null)
+            _rsmAnnos = new HashMap<QueryResultMapping, List<AnnotationBuilder>>();
+        List<AnnotationBuilder> list = _rsmAnnos.get(meta);
+        if (list == null) {
+            list = new ArrayList<AnnotationBuilder>();
+            _rsmAnnos.put(meta, list);
+        }
+        list.add(ab);
+    }
+
+    /**
+     * Creates an an annotation builder for the specified class metadata
+     * and adds it to list of builders.
+     */
+    protected AnnotationBuilder addAnnotation(
+        Class<? extends Annotation> annType, QueryResultMapping meta) {
+        AnnotationBuilder ab = newAnnotationBuilder(annType);
+        if (meta == null)
+            return ab;
+        addAnnotation(ab, meta);
+        return ab;
+    }
+
+
+    @Override
+    protected void serializeClass(ClassMetaData meta) {
+        if (_sync && isMappingMode() && meta instanceof ClassMapping) {
+            // sync if resolved and mapped
+            ClassMapping cls = (ClassMapping) meta;
+            if ((cls.getResolve() & MetaDataModes.MODE_MAPPING) != 0 &&
+                cls.isMapped()) {
+                cls.syncMappingInfo();
+                cls.getDiscriminator().syncMappingInfo();
+                cls.getVersion().syncMappingInfo();
+                FieldMapping[] fields;
+                if (cls.getEmbeddingMetaData() == null)
+                    fields = cls.getDefinedFieldMappings();
+                else
+                    fields = cls.getFieldMappings();
+                for (FieldMapping f : fields)
+                    f.syncMappingInfo();
+            }
+        }
+        super.serializeClass(meta);
+    }
+
+    @Override
+    protected void serializeClassMappingContent(ClassMetaData mapping) {
+        ClassMapping cls = (ClassMapping) mapping;
+        ClassMappingInfo info = cls.getMappingInfo();
+        AnnotationBuilder abTable = addAnnotation(Table.class, mapping);
+        serializeTable(info.getTableName(), Strings
+            .getClassName(mapping.getDescribedType()), null,
+            info.getUniques(), abTable);
+        serializeColumns(info, ColType.PK_JOIN, null, abTable, cls);
+        for (String second : info.getSecondaryTableNames()) {
+            AnnotationBuilder abSecTable =
+                addAnnotation(SecondaryTable.class, mapping);
+            serializeTable(second, null, info, null, abSecTable);
+        }
+    }
+
+    @Override
+    protected void serializeInheritanceContent(ClassMetaData mapping) {
+        ClassMapping cls = (ClassMapping) mapping;
+        ClassMappingInfo info = cls.getMappingInfo();
+        DiscriminatorMappingInfo dinfo = cls.getDiscriminator()
+            .getMappingInfo();
+        String strat = info.getHierarchyStrategy();
+        if (null == strat)
+            return;
+        String itypecls = Strings.getClassName(InheritanceType.class);
+        AnnotationBuilder abInheritance =
+            addAnnotation(Inheritance.class, mapping);
+        if (FlatClassStrategy.ALIAS.equals(strat))
+            abInheritance.add("strategy", itypecls + ".SINGLE_TABLE");
+        else if (VerticalClassStrategy.ALIAS.equals(strat))
+            abInheritance.add("strategy", itypecls + ".JOINED");
+        else if (FullClassStrategy.ALIAS.equals(strat))
+            abInheritance.add("strategy", itypecls + ".TABLE_PER_CLASS");
+        if (dinfo.getValue() != null) {
+            AnnotationBuilder abDiscVal =
+                addAnnotation(DiscriminatorValue.class, mapping);
+            abDiscVal.add(null, dinfo.getValue());
+        }
+        AnnotationBuilder abDiscCol =
+            addAnnotation(DiscriminatorColumn.class, mapping);
+        serializeColumns(dinfo, ColType.DISC, null, abDiscCol, null);
+    }
+
+    /**
+     * Serialize table optionally listing primary-key-joins stored
+     * in the given {@link org.apache.openjpa.jdbc.meta.ClassMappingInfo}.
+     */
+    private void serializeTable(String table, String defaultName,
+        ClassMappingInfo secondaryInfo, Unique[] uniques,
+        AnnotationBuilder ab) {
+        List<Column> cols = null;
+        if (secondaryInfo != null)
+            cols = (List<Column>) secondaryInfo.getSecondaryTableJoinColumns
+                (table);
+
+        boolean print = (cols != null && cols.size() > 0) ||
+            (uniques != null && uniques.length > 0);
+        if (table != null
+            && (defaultName == null || !defaultName.equals(table))) {
+            print = true;
+            int index = table.indexOf('.');
+            if (index < 0)
+                ab.add("name", table);
+            else {
+                ab.add("schema", table.substring(0, index));
+                ab.add("name", table.substring(index + 1));
+            }
+        }
+        if (print) {
+            if (cols != null) {
+                for (Column col : cols)
+                    serializeColumn(col, ColType.PK_JOIN,
+                        null, false, ab, null);
+            }
+            if (uniques != null) {
+                for (Unique unique: uniques) {
+                    AnnotationBuilder abUniqueConst =
+                        newAnnotationBuilder(UniqueConstraint.class);
+                    serializeUniqueConstraint(unique, abUniqueConst);
+                    ab.add("uniqueConstraints", abUniqueConst);
+                }
+            }
+        }
+    }
+
+    @Override
+    protected boolean serializeAttributeOverride(FieldMetaData fmd,
+        FieldMetaData orig) {
+        if (orig == null || fmd == orig)
+            return false;
+
+        FieldMapping field = (FieldMapping) fmd;
+        FieldMapping field2 = (FieldMapping) orig;
+        if (field.getMappingInfo().hasSchemaComponents()
+            || field2.getMappingInfo().hasSchemaComponents())
+            return true;
+
+        ValueMappingInfo info = field.getValueInfo();
+        List<Column> cols = (List<Column>) info.getColumns();
+        if (cols == null || cols.size() == 0)
+            return false;
+        ValueMappingInfo info2 = field2.getValueInfo();
+        List<Column> cols2 = (List<Column>) info2.getColumns();
+        if (cols2 == null || cols2.size() != cols.size())
+            return true;
+        if (cols.size() != 1)
+            return true;
+
+        Column col;
+        Column col2;
+        for (int i = 0; i < cols.size(); i++) {
+            col = cols.get(i);
+            col2 = cols2.get(i);
+            if (!StringUtils.equals(col.getName(), col2.getName()))
+                return true;
+            if (!StringUtils.equals(col.getTypeName(), col2.getTypeName()))
+                return true;
+            if (col.getSize() != col2.getSize())
+                return true;
+            if (col.getDecimalDigits() != col2.getDecimalDigits())
+                return true;
+            if (col.getFlag(Column.FLAG_UNINSERTABLE)
+                != col2.getFlag(Column.FLAG_UNINSERTABLE))
+                return true;
+            if (col.getFlag(Column.FLAG_UNUPDATABLE)
+                != col2.getFlag(Column.FLAG_UNUPDATABLE))
+                return true;
+        }
+        return false;
+    }
+
+    @Override
+    protected void serializeAttributeOverrideMappingContent(FieldMetaData fmd,
+        FieldMetaData orig, AnnotationBuilder ab) {
+        FieldMapping fm = (FieldMapping) fmd;
+        serializeColumns(fm.getValueInfo(), ColType.COL, fm.getMappingInfo()
+            .getTableName(), ab, fmd);
+    }
+
+    @Override
+    protected PersistenceStrategy getStrategy(FieldMetaData fmd) {
+        PersistenceStrategy strat = super.getStrategy(fmd);
+        FieldMapping field = (FieldMapping) fmd;
+        switch (strat) {
+            case MANY_MANY:
+                // we can differentiate a one-many by the fact that there is no
+                // secondary table join, or the fk is unique
+                if (field.getMappedBy() == null
+                    && (field.getMappingInfo().getJoinDirection()
+                    == MappingInfo.JOIN_NONE
+                    || field.getElementMapping().getValueInfo().getUnique()
+                    != null))
+                    return PersistenceStrategy.ONE_MANY;
+                break;
+            case MANY_ONE:
+                // inverse join cols or unique fk?
+                if (field.getValueInfo().getJoinDirection()
+                    == MappingInfo.JOIN_INVERSE
+                    || field.getValueInfo().getUnique() != null)
+                    return PersistenceStrategy.ONE_ONE;
+
+                // scan for primary-key-join-column
+                List<Column> cols = field.getValueInfo().getColumns();
+                boolean pkJoin = cols != null && cols.size() > 0;
+                for (int i = 0; pkJoin && i < cols.size(); i++)
+                    pkJoin = cols.get(i).getFlag(Column.FLAG_PK_JOIN);
+                if (pkJoin)
+                    return PersistenceStrategy.ONE_ONE;
+                break;
+        }
+        return strat;
+    }
+
+    @Override
+    protected void serializeFieldMappingContent(FieldMetaData fmd,
+        PersistenceStrategy strategy, AnnotationBuilder ab) {
+        if (fmd.getMappedBy() != null)
+            return;
+
+        // while I'd like to do auto detection based on join directions, etc.
+        // the distinguished column / table / etc names forces our hand
+        // esp for OpenJPA custom mappings.
+        FieldMapping field = (FieldMapping) fmd;
+        switch (strategy) {
+            case ONE_ONE:
+            case MANY_ONE:
+                serializeColumns(field.getValueInfo(), ColType.JOIN,
+                    field.getMappingInfo().getTableName(), null, fmd);
+                return;
+            case ONE_MANY:
+                if (field.getMappingInfo().getJoinDirection() ==
+                    MappingInfo.JOIN_NONE) {
+                    serializeColumns(field.getElementMapping().getValueInfo(),
+                        ColType.JOIN, null, null, fmd);
+                    return;
+                }
+                // else no break
+            case MANY_MANY:
+                if (field.getMappingInfo().hasSchemaComponents()
+                    || field.getElementMapping().getValueInfo()
+                    .hasSchemaComponents()) {
+                    AnnotationBuilder abJoinTbl =
+                        addAnnotation(JoinTable.class, fmd);
+                    String table = field.getMappingInfo().getTableName();
+                    if (table != null) {
+                        int index = table.indexOf('.');
+                        if (index < 0)
+                            abJoinTbl.add("name", table);
+                        else {
+                            abJoinTbl.add("schema", table.substring(0, index));
+                            abJoinTbl.add("name", table.substring(index + 1));
+                        }
+                    }
+                    serializeColumns(field.getMappingInfo(),
+                        ColType.JOIN, null, abJoinTbl, null);
+                    serializeColumns(field.getElementMapping().getValueInfo(),
+                        ColType.INVERSE, null, abJoinTbl, null);
+                }
+                return;
+        }
+
+        serializeColumns(field.getValueInfo(), ColType.COL,
+            field.getMappingInfo().getTableName(), null, fmd);
+        if (strategy == PersistenceStrategy.BASIC && isLob(field)) {
+            addAnnotation(Lob.class, fmd);
+        }
+        TemporalType temporal = getTemporal(field);
+        if (temporal != null) {
+            addAnnotation(Temporal.class, fmd).
+                add(null, temporal);
+        }
+
+        EnumType enumType = getEnumType(field);
+        if (enumType != null && enumType != EnumType.ORDINAL) {
+            addAnnotation(Enumerated.class, fmd).
+                add(null, enumType);
+        }
+    }
+
+    /**
+     * Determine if the field is a lob.
+     */
+    private boolean isLob(FieldMapping field) {
+        for (Column col : (List<Column>) field.getValueInfo().getColumns())
+            if (col.getType() == Types.BLOB || col.getType() == Types.CLOB)
+                return true;
+        return false;
+    }
+
+    /**
+     * Return field's temporal type.
+     */
+    private TemporalType getTemporal(FieldMapping field) {
+        if (field.getDeclaredTypeCode() != JavaTypes.DATE
+            && field.getDeclaredTypeCode() != JavaTypes.CALENDAR)
+            return null;
+
+        DBDictionary dict = ((JDBCConfiguration) getConfiguration())
+            .getDBDictionaryInstance();
+        int def = dict.getJDBCType(field.getTypeCode(), false);
+        for (Column col : (List<Column>) field.getValueInfo().getColumns()) {
+            if (col.getType() == def)
+                continue;
+            switch (col.getType()) {
+                case Types.DATE:
+                    return TemporalType.DATE;
+                case Types.TIME:
+                    return TemporalType.TIME;
+                case Types.TIMESTAMP:
+                    return TemporalType.TIMESTAMP;
+            }
+        }
+        return null;
+    }
+
+    /**
+     * Return enum type for the field.
+     */
+    protected EnumType getEnumType(FieldMapping field) {
+        if (field.getDeclaredTypeCode() != JavaTypes.OBJECT)
+            return null;
+        if (!(field.getHandler() instanceof EnumValueHandler))
+            return null;
+        return ((EnumValueHandler) field.getHandler()).getStoreOrdinal()
+            ? EnumType.ORDINAL : EnumType.STRING;
+    }
+
+    /**
+     * Serialize the columns in the given mapping info.
+     */
+    private void serializeColumns(MappingInfo info, ColType type,
+        String secondary, AnnotationBuilder ab, Object meta) {
+        List<Column> cols = (List<Column>) info.getColumns();
+        if (cols == null)
+            return;
+        AnnotationBuilder abContainer = ab;
+        if (cols.size() > 1) {
+            Class grpType = type.getColumnGroupAnnotationType();
+            if (null != grpType) {
+                AnnotationBuilder abGrp = newAnnotationBuilder(grpType);
+                if (null == ab)
+                    addAnnotation(abGrp, meta);
+                else
+                    ab.add(null, abGrp);
+                abContainer = abGrp;
+            }
+        }
+        for (Column col : cols)
+            serializeColumn(col, type, secondary,
+                info.getUnique() != null, abContainer, meta);
+    }
+
+    /**
+     * Serialize a single column.
+     */
+    private void serializeColumn(Column col, ColType type, String secondary,
+        boolean unique, AnnotationBuilder ab, Object meta) {
+        FieldMetaData fmd = meta instanceof FieldMetaData ?
+            (FieldMetaData) meta : null;
+        AnnotationBuilder abCol = newAnnotationBuilder(
+            type.getColumnAnnotationType());
+        if (col.getName() != null && (null == fmd ||
+            !col.getName().equalsIgnoreCase(fmd.getName())))
+            abCol.add("name", col.getName());
+        if (col.getTypeName() != null)
+            abCol.add("columnDefinition", col.getTypeName());
+        if (col.getTarget() != null
+            && (type == ColType.JOIN || type == ColType.INVERSE
+            || type == ColType.PK_JOIN))
+            abCol.add("referencedColumnName", col.getTarget());
+        if (type == ColType.COL || type == ColType.JOIN
+            || type == ColType.PK_JOIN) {
+            if (unique)
+                abCol.add("unique", true);
+            if (col.isNotNull())
+                abCol.add("nullable", false);
+            if (col.getFlag(Column.FLAG_UNINSERTABLE))
+                abCol.add("insertable", false);
+            if (col.getFlag(Column.FLAG_UNUPDATABLE))
+                abCol.add("updatable", false);
+            if (secondary != null)
+                abCol.add("table", secondary);
+
+            if (type == ColType.COL) {
+                if (col.getSize() > 0 && col.getSize() != 255)
+                    abCol.add("length", col.getSize());
+                if (col.getDecimalDigits() != 0)
+                    abCol.add("scale", col.getDecimalDigits());
+            }
+        }
+
+        if (type != ColType.COL || abCol.hasComponents()) {
+            if (null != ab) {
+                String key = null;
+                if (ab.getType() == JoinTable.class) {
+                    switch(type) {
+                        case JOIN:
+                            key = "joinColumns";
+                            break;
+                        case INVERSE:
+                            key = "inverseJoinColumns";
+                            break;
+                    }
+                }
+                ab.add(key, abCol);
+            } else {
+                addAnnotation(abCol, meta);
+            }                
+        }
+    }
+
+    private void serializeUniqueConstraint(Unique unique,
+        AnnotationBuilder ab) {
+        StringBuilder sb = new StringBuilder();
+        Column[] columns = unique.getColumns();
+        for (Column column:columns) {
+            if (sb.length() > 0)
+                sb.append(", ");
+            sb.append(column.getName());
+        }
+        if (columns.length > 1)
+            sb.insert(0, "{").append("}");
+        ab.add("columnNames", sb.toString());
+    }
+
+    @Override
+    protected SerializationComparator newSerializationComparator() {
+        return new AnnotationPersistenceMappingSerializer.
+            MappingSerializationComparator();
+    }
+
+    @Override
+    protected void addSystemMappingElements(Collection toSerialize) {
+        if (isQueryMode())
+            toSerialize.addAll(getQueryResultMappings(null));
+    }
+
+    @Override
+    protected int type(Object o) {
+        int type = super.type(o);
+        if (type == -1 && o instanceof QueryResultMapping)
+            return TYPE_RESULTMAP;
+        return type;
+    }
+
+    /**
+     * Return the result set mappings for the given scope.
+     */
+    private List<QueryResultMapping> getQueryResultMappings(ClassMetaData cm) {
+        if (_results == null || _results.isEmpty())
+            return (List<QueryResultMapping>) Collections.EMPTY_LIST;
+
+        List<QueryResultMapping> result = null;
+        for (int i = 0; i < _results.size(); i++) {
+            QueryResultMapping element = _results.get(i);
+            if ((cm == null && element.getSourceScope() != null) || (cm != null
+                && element.getSourceScope() != cm.getDescribedType()))
+                continue;
+
+            if (result == null)
+                result = new ArrayList<QueryResultMapping>(_results.size() - i);
+            result.add(element);
+        }
+        return (result == null)
+            ? (List<QueryResultMapping>) Collections.EMPTY_LIST : result;
+    }
+
+    @Override
+    protected void serializeSystemMappingElement(Object obj) {
+        if (obj instanceof QueryResultMapping)
+            serializeQueryResultMapping((QueryResultMapping) obj, null);
+    }
+
+    @Override
+    protected void serializeQueryMappings(ClassMetaData meta) {
+        for (QueryResultMapping res : getQueryResultMappings(meta))
+            serializeQueryResultMapping(res, meta);
+    }
+
+    /**
+     * Serialize given result set mapping.
+     */
+    private void serializeQueryResultMapping(QueryResultMapping meta,
+        ClassMetaData clsmeta) {
+        AnnotationBuilder ab = addAnnotation(SqlResultSetMapping.class, meta);
+        if (null != clsmeta)
+            addAnnotation(ab, clsmeta);
+        ab.add("name", meta.getName());
+        for (QueryResultMapping.PCResult pc : meta.getPCResults()) {
+            AnnotationBuilder abEntRes =
+                newAnnotationBuilder(EntityResult.class);
+            ab.add("entities", abEntRes);
+            abEntRes.add("entityClass", pc.getCandidateType());
+            Object discrim = pc.getMapping(pc.DISCRIMINATOR);
+            if (discrim != null)
+                abEntRes.add("discriminatorColumn", discrim.toString());
+
+            for (String path : pc.getMappingPaths()) {
+                AnnotationBuilder abFldRes =
+                    newAnnotationBuilder(FieldResult.class);
+                abEntRes.add("fields", abFldRes);
+                abFldRes.add("name", path);
+                abFldRes.add("column", pc.getMapping(path).toString());
+            }
+        }
+        for (Object col : meta.getColumnResults()) {
+            AnnotationBuilder abColRes =
+                newAnnotationBuilder(ColumnResult.class);
+            abColRes.add("name", col.toString());
+        }
+    }
+
+    @Override
+    protected void serializeSequence(SequenceMetaData meta) {
+        if (SequenceMapping.IMPL_VALUE_TABLE.equals(meta.getSequencePlugin())) {
+            super.serializeSequence(meta);
+            return;
+        }
+
+        AnnotationBuilder abTblGen = addAnnotation(TableGenerator.class, meta);
+        SequenceMapping seq = (SequenceMapping) meta;
+        abTblGen.add("name", seq.getName());
+        String table = seq.getTable();
+        if (table != null) {
+            int dotIdx = table.indexOf('.');
+            if (dotIdx == -1)
+                abTblGen.add("table", table);
+            else {
+                abTblGen.add("table", table.substring(dotIdx + 1));
+                abTblGen.add("schema", table.substring(0, dotIdx));
+            }
+        }
+        if (!StringUtils.isEmpty(seq.getPrimaryKeyColumn()))
+            abTblGen.add("pkColumnName", seq.getPrimaryKeyColumn());
+        if (!StringUtils.isEmpty(seq.getSequenceColumn()))
+            abTblGen.add("valueColumnName", seq.getSequenceColumn());
+        if (!StringUtils.isEmpty(seq.getPrimaryKeyValue()))
+            abTblGen.add("pkColumnValue", seq.getPrimaryKeyValue());
+        if (seq.getAllocate() != 50 && seq.getAllocate() != -1)
+            abTblGen.add("allocationSize", seq.getAllocate() + "");
+        if (seq.getInitialValue() != 0 && seq.getInitialValue() != -1)
+            abTblGen.add("initialValue", seq.getInitialValue() + "");
+    }
+
+    /**
+     * Column types serialized under different names.
+     */
+    private static enum ColType {
+
+        COL,
+        JOIN,
+        INVERSE,
+        PK_JOIN,
+        DISC;
+
+        private Class<? extends Annotation> getColumnAnnotationType() {
+            switch(this) {
+                case COL:
+                    return javax.persistence.Column.class;
+                case JOIN:
+                case INVERSE:
+                    return JoinColumn.class;
+                case PK_JOIN:
+                    return PrimaryKeyJoinColumn.class;
+                case DISC:
+                    return DiscriminatorColumn.class;
+            }
+            return null;
+        }
+
+        private Class<? extends Annotation> getColumnGroupAnnotationType() {
+            switch(this) {
+                case JOIN:
+                case INVERSE:
+                    return JoinColumns.class;
+                case PK_JOIN:
+                    return PrimaryKeyJoinColumns.class;
+            }
+            return null;
+        }
+
+    }
+
+    /**
+     * Extends {@link SerializationComparator} for store-specific tags such
+     * as &lt;sql-result-set-mapping&gt;.
+     *
+     * @author Pinaki Poddar
+     */
+    protected class MappingSerializationComparator
+        extends SerializationComparator {
+
+        protected int compareUnknown(Object o1, Object o2) {
+            if (!(o1 instanceof QueryResultMapping))
+                return super.compareUnknown(o1, o2);
+
+            QueryResultMapping res1 = (QueryResultMapping) o1;
+            QueryResultMapping res2 = (QueryResultMapping) o2;
+
+            // system scope before class scope
+            Object scope1 = res1.getSourceScope();
+            Object scope2 = res2.getSourceScope();
+            if (scope1 == null && scope2 != null)
+                return -1;
+            if (scope1 != null && scope2 == null)
+                return 1;
+
+            // compare on listing index, or if none/same, use name
+            int listingIndex1 = res1.getListingIndex();
+            int listingIndex2 = res2.getListingIndex();
+            if (listingIndex1 != listingIndex2)
+                return listingIndex1 - listingIndex2;
+            return res1.getName ().compareTo (res2.getName ());
+		}
+	}
+}

Propchange: openjpa/trunk/openjpa-persistence-jdbc/src/main/java/org/apache/openjpa/persistence/jdbc/AnnotationPersistenceMappingSerializer.java
------------------------------------------------------------------------------
    svn:eol-style = native

Modified: openjpa/trunk/openjpa-persistence-jdbc/src/main/java/org/apache/openjpa/persistence/jdbc/PersistenceMappingFactory.java
URL: http://svn.apache.org/viewvc/openjpa/trunk/openjpa-persistence-jdbc/src/main/java/org/apache/openjpa/persistence/jdbc/PersistenceMappingFactory.java?view=diff&rev=552358&r1=552357&r2=552358
==============================================================================
--- openjpa/trunk/openjpa-persistence-jdbc/src/main/java/org/apache/openjpa/persistence/jdbc/PersistenceMappingFactory.java (original)
+++ openjpa/trunk/openjpa-persistence-jdbc/src/main/java/org/apache/openjpa/persistence/jdbc/PersistenceMappingFactory.java Sun Jul  1 12:37:04 2007
@@ -21,10 +21,11 @@
 import org.apache.openjpa.jdbc.conf.JDBCConfiguration;
 import org.apache.openjpa.jdbc.meta.MappingRepository;
 import org.apache.openjpa.meta.MetaDataFactory;
-import org.apache.openjpa.persistence.AnnotationPersistenceMetaDataParser;
 import org.apache.openjpa.persistence.PersistenceMetaDataFactory;
 import org.apache.openjpa.persistence.XMLPersistenceMetaDataParser;
 import org.apache.openjpa.persistence.XMLPersistenceMetaDataSerializer;
+import org.apache.openjpa.persistence.AnnotationPersistenceMetaDataParser;
+import org.apache.openjpa.persistence.AnnotationPersistenceMetaDataSerializer;
 
 /**
  * {@link MetaDataFactory} for JPA mapping information.
@@ -46,6 +47,15 @@
             parser.setMappingOverride(((MappingRepository) repos).
                 getStrategyInstaller().isAdapting());
         return parser;
+    }
+
+    protected AnnotationPersistenceMetaDataSerializer newAnnotationSerializer()
+    {
+        AnnotationPersistenceMappingSerializer ser =
+            new AnnotationPersistenceMappingSerializer((JDBCConfiguration)
+            repos.getConfiguration());
+        ser.setSyncMappingInfo(true);
+        return ser;
     }
 
     @Override

Added: openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/AnnotationBuilder.java
URL: http://svn.apache.org/viewvc/openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/AnnotationBuilder.java?view=auto&rev=552358
==============================================================================
--- openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/AnnotationBuilder.java (added)
+++ openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/AnnotationBuilder.java Sun Jul  1 12:37:04 2007
@@ -0,0 +1,200 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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.openjpa.persistence;
+
+import serp.util.Strings;
+
+import java.util.List;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.EnumSet;
+import java.lang.annotation.Annotation;
+
+import org.apache.commons.lang.StringUtils;
+
+/**
+ * Helper class to stringify annotation declarations.
+ *
+ * @author Gokhan Ergul
+ * @since 1.0.0
+ */
+public class AnnotationBuilder {
+
+    private Class<? extends Annotation> type;
+    private List<AnnotationEntry> components =
+        new ArrayList<AnnotationEntry>();
+
+    protected AnnotationBuilder(Class<? extends Annotation> type) {
+        this.type = type;
+    }
+
+    public Class<? extends Annotation> getType() {
+        return this.type;
+    }
+
+    public AnnotationBuilder add(String key, String val) {
+        return doAdd(key, val);
+    }
+
+    public AnnotationBuilder add(String key, boolean val) {
+        return doAdd(key, val);
+    }
+
+    public AnnotationBuilder add(String key, int val) {
+        return doAdd(key, val);
+    }
+
+    public AnnotationBuilder add(String key, Class val) {
+        return doAdd(key, val);
+    }
+
+    public AnnotationBuilder add(String key, EnumSet val) {
+        return doAdd(key, val);
+    }
+
+    public AnnotationBuilder add(String key, Enum val) {
+        return doAdd(key, val);
+    }
+
+    @SuppressWarnings("unchecked")
+    public AnnotationBuilder add(String key, AnnotationBuilder val) {
+        if (null == val)
+            return this;
+        AnnotationEntry ae = find(key);
+        if (null == ae) {
+            doAdd(key, val);
+        } else {
+            List<AnnotationBuilder> list;
+            if (ae.value instanceof List) {
+                list = (List<AnnotationBuilder>) ae.value;
+            } else if (ae.value instanceof AnnotationBuilder) {
+                list = new ArrayList<AnnotationBuilder> ();
+                list.add((AnnotationBuilder) ae.value);
+                ae.value = list;
+            } else {
+                throw new IllegalArgumentException(
+                    "Unexpected type: " + ae.value);
+            }
+            list.add(val);
+        }
+        return this;
+    }
+
+    public boolean hasComponents() {
+        return components.size() > 0;
+    }
+
+    private AnnotationBuilder doAdd (String key, Object val) {
+        if (null != val)
+            components.add(new AnnotationEntry(key, val));
+        return this;        
+    }
+
+    private AnnotationEntry find(String key) {
+        for(AnnotationEntry ae: components) {
+            // null key references considered equal
+            if (StringUtils.equals(ae.key, key))
+                return ae;
+        }
+        return null;
+    }
+
+    static String enumToString(Enum e) {
+        StringBuilder sb = new StringBuilder();
+        sb.append(Strings.getClassName(e.getClass())).
+            append(".").append(e);
+        return sb.toString();
+    }
+
+    static String enumSetToString(EnumSet set) {
+        StringBuilder sb = new StringBuilder();
+        for (Iterator i = set.iterator(); i.hasNext();) {
+            Object e =  i.next();
+            sb.append(Strings.getClassName(e.getClass())).
+                append(".").append(e);
+            if (i.hasNext())
+                sb.append(", ");
+        }
+        return sb.toString();
+    }
+
+    protected void toString(StringBuilder sb) {
+        sb.append("@").append(Strings.getClassName(type));
+        if (components.size() == 0)
+            return;
+        sb.append("(");
+        for (Iterator<AnnotationEntry> i = components.iterator(); i.hasNext();) 
+        {
+            AnnotationEntry e = i.next();
+            e.toString(sb);
+            if (i.hasNext())
+                sb.append(", ");
+        }
+        sb.append(")");        
+    }
+
+    public String toString() {
+        StringBuilder sb = new StringBuilder();
+        toString(sb);
+        return sb.toString();
+    }
+
+}
+
+class AnnotationEntry {
+
+    String key;
+    Object value;
+
+    AnnotationEntry(String key, Object value) {
+        this.key = key;
+        this.value = value;
+    }
+
+    @SuppressWarnings("unchecked")
+    void toString(StringBuilder sb) {
+        if (null != key)
+            sb.append(key).append("=");
+
+        List.class.getTypeParameters();
+        if (value instanceof List) {
+            sb.append("{");
+            List<AnnotationBuilder> l = (List<AnnotationBuilder>) value;
+            for (Iterator<AnnotationBuilder> i = l.iterator(); i.hasNext();) {
+                AnnotationBuilder ab =  i.next();
+                sb.append(ab.toString());
+                if (i.hasNext())
+                    sb.append(", ");
+            }
+            sb.append("}");
+        } else if (value instanceof Class) {
+            String cls = ((Class) value).getName().replace('$', '.');
+            sb.append(cls).append(".class");
+        } else if (value instanceof String) {
+            sb.append('"').append(value).append('"');
+        } else if (value instanceof Enum) {
+            sb.append(AnnotationBuilder.enumToString((Enum) value));
+        } else if (value instanceof EnumSet) {
+            sb.append(AnnotationBuilder.enumSetToString((EnumSet) value));
+        } else {
+            sb.append(value);
+        }
+    }
+
+}

Propchange: openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/AnnotationBuilder.java
------------------------------------------------------------------------------
    svn:eol-style = native