Package org.jpox.jdo.metadata

Source Code of org.jpox.jdo.metadata.JDOAnnotationReader

/**********************************************************************
Copyright (c) 2006 Andy Jefferson and others. All rights reserved.
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.

Contributors:
    ...
**********************************************************************/
package org.jpox.jdo.metadata;

import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Map;

import javax.jdo.annotations.Column;
import javax.jdo.annotations.DiscriminatorStrategy;
import javax.jdo.annotations.Embedded;
import javax.jdo.annotations.Extension;
import javax.jdo.annotations.FetchGroup;
import javax.jdo.annotations.FetchPlan;
import javax.jdo.annotations.ForeignKey;
import javax.jdo.annotations.ForeignKeyAction;
import javax.jdo.annotations.IdGeneratorStrategy;
import javax.jdo.annotations.Index;
import javax.jdo.annotations.InheritanceStrategy;
import javax.jdo.annotations.Join;
import javax.jdo.annotations.NullValue;
import javax.jdo.annotations.PersistenceModifier;
import javax.jdo.annotations.Persistent;
import javax.jdo.annotations.Query;
import javax.jdo.annotations.SequenceStrategy;
import javax.jdo.annotations.Unique;
import javax.jdo.annotations.VersionStrategy;

import org.jpox.ClassLoaderResolver;
import org.jpox.exceptions.JPOXException;
import org.jpox.metadata.AbstractClassMetaData;
import org.jpox.metadata.AbstractMemberMetaData;
import org.jpox.metadata.ArrayMetaData;
import org.jpox.metadata.ClassMetaData;
import org.jpox.metadata.CollectionMetaData;
import org.jpox.metadata.ColumnMetaData;
import org.jpox.metadata.ContainerMetaData;
import org.jpox.metadata.DiscriminatorMetaData;
import org.jpox.metadata.ElementMetaData;
import org.jpox.metadata.EmbeddedMetaData;
import org.jpox.metadata.ExtensionMetaData;
import org.jpox.metadata.FetchGroupMetaData;
import org.jpox.metadata.FetchPlanMetaData;
import org.jpox.metadata.FieldMetaData;
import org.jpox.metadata.FieldPersistenceModifier;
import org.jpox.metadata.ForeignKeyMetaData;
import org.jpox.metadata.IdentityMetaData;
import org.jpox.metadata.IdentityType;
import org.jpox.metadata.IndexMetaData;
import org.jpox.metadata.InheritanceMetaData;
import org.jpox.metadata.InterfaceMetaData;
import org.jpox.metadata.JoinMetaData;
import org.jpox.metadata.KeyMetaData;
import org.jpox.metadata.MapMetaData;
import org.jpox.metadata.MetaData;
import org.jpox.metadata.MetaDataManager;
import org.jpox.metadata.OrderMetaData;
import org.jpox.metadata.PackageMetaData;
import org.jpox.metadata.PrimaryKeyMetaData;
import org.jpox.metadata.QueryMetaData;
import org.jpox.metadata.SequenceMetaData;
import org.jpox.metadata.UniqueMetaData;
import org.jpox.metadata.ValueMetaData;
import org.jpox.metadata.VersionMetaData;
import org.jpox.metadata.annotations.AbstractAnnotationReader;
import org.jpox.metadata.annotations.AnnotationObject;
import org.jpox.metadata.annotations.Member;
import org.jpox.util.ClassUtils;
import org.jpox.util.JPOXLogger;
import org.jpox.util.StringUtils;

/**
* Implementation for Annotation Reader for JDK 1.5 annotations using the JDO 2.1 definition.
*
* @version $Revision: 1.1 $
*/
public class JDOAnnotationReader extends AbstractAnnotationReader
{
    /**
     * Constructor.
     * @param mgr MetaData manager
     */
    public JDOAnnotationReader(MetaDataManager mgr)
    {
        super(mgr);

        // We support JDO and JPOX annotations in this reader.
        setSupportedAnnotationPackages(new String[] {"javax.jdo", "org.jpox"});
    }

    /**
     * Method to process the "class" level annotations and create the outline ClassMetaData object
     * @param pmd Parent PackageMetaData
     * @param cls The class
     * @param annotations Annotations for this class
     * @param clr ClassLoader resolver
     * @return The ClassMetaData/InterfaceMetaData (or null if no annotations)
     */
    protected AbstractClassMetaData processClassAnnotations(PackageMetaData pmd, Class cls,
            AnnotationObject[] annotations, ClassLoaderResolver clr)
    {
        if (pmd == null)
        {
            // TODO Localise this message
            throw new JPOXException("No PackageMetaData specified to use for annotations");
        }

        AbstractClassMetaData cmd = null;

        if (annotations != null && annotations.length > 0)
        {
            InheritanceMetaData inhmd = null;
            DiscriminatorMetaData dismd = null;
            IdentityMetaData idmd = null;
            PrimaryKeyMetaData pkmd = null;
            VersionMetaData vermd = null;
            JoinMetaData[] joins = null;
            QueryMetaData[] queries = null;
            FetchPlanMetaData[] fetchPlans = null;
            FetchGroupMetaData[] fetchGroups = null;
            SequenceMetaData seqmd = null;
            String tableName = null;
            String catalogName = null;
            String schemaName = null;
            boolean embeddedOnly = false;
            ColumnMetaData[] unmappedColumns = null;
            HashSet<IndexMetaData> indices = null;
            HashSet<UniqueMetaData> uniqueKeys = null;
            HashSet<ForeignKeyMetaData> fks = null;
            HashSet<ExtensionMetaData> extensions = null;

            for (int i=0;i<annotations.length;i++)
            {
                if (isSupportedAnnotation(annotations[i].getName()))
                {
                    HashMap<String, Object> annotationValues = annotations[i].getNameValueMap();
                    String annName = annotations[i].getName();
                    if (annName.equals(JDOAnnotationUtils.PERSISTENCE_CAPABLE))
                    {
                        // PersistenceCapable class
                        String embeddedOnlyString = (String)annotationValues.get("embeddedOnly");
                        String requiresExtent = (String)annotationValues.get("requiresExtent");
                        String detachable = (String)annotationValues.get("detachable");
                        tableName = (String)annotationValues.get("table");
                        catalogName = (String)annotationValues.get("catalog");
                        schemaName = (String)annotationValues.get("schema");

                        javax.jdo.annotations.IdentityType idTypeVal = (javax.jdo.annotations.IdentityType)annotationValues.get("identityType");
                        String identityType = JDOAnnotationUtils.getIdentityTypeString(idTypeVal);

                        String idClassName = null;
                        Class idClass = (Class)annotationValues.get("objectIdClass");
                        if (idClass != null && idClass != void.class)
                        {
                            idClassName = idClass.getName();
                        }

                        if (cls.isInterface())
                        {
                            cmd = mgr.getMetaDataFactory().newInterfaceObject(pmd, ClassUtils.getClassNameForClass(cls),
                                identityType, idClassName, requiresExtent, detachable,
                                embeddedOnlyString, catalogName, schemaName, tableName, null);
                        }
                        else
                        {
                            cmd = mgr.getMetaDataFactory().newClassObject(pmd, ClassUtils.getClassNameForClass(cls),
                                identityType, idClassName, requiresExtent, detachable,
                                embeddedOnlyString, "persistence-capable", null, catalogName, schemaName, tableName, null);
                        }
                        JDOAnnotationUtils.addExtensionsToMetaData(cmd, (Extension[])annotationValues.get("extensions"));

                        // Members typically providing specification of overridden fields/properties
                        Persistent[] members = (Persistent[])annotationValues.get("members");
                        if (members != null)
                        {
                            // Add on the fields/properties direct to the metadata for the class/interface
                            for (int j=0;j<members.length;j++)
                            {
                                String memberName = members[j].name();
                                if (memberName.indexOf('.') > 0)
                                {
                                    memberName = memberName.substring(memberName.lastIndexOf('.')+1);
                                }
                                boolean isField = isMemberOfClassAField(cls, memberName);
                                AbstractMemberMetaData fmd = getFieldMetaDataForPersistent(cmd, members[j], isField);
                                cmd.addMember(fmd);
                            }
                        }
                    }
                    else if (annName.equals(JDOAnnotationUtils.PERSISTENCE_AWARE))
                    {
                        // PersistenceAware class
                        cmd = mgr.getMetaDataFactory().newClassObject(pmd, ClassUtils.getClassNameForClass(cls), null,
                            null, null, null, null, "persistence-aware",
                            null, null, null, null, null);
                    }
                    else if (annName.equals(JDOAnnotationUtils.EMBEDDED_ONLY))
                    {
                        embeddedOnly = true;
                    }
                    else if (annName.equals(JDOAnnotationUtils.VERSION))
                    {
                        VersionStrategy versionStrategy = (VersionStrategy)annotationValues.get("strategy");
                        String strategy = JDOAnnotationUtils.getVersionStrategyString(versionStrategy);
                        String indexed = (String)annotationValues.get("indexed");
                        String column = (String)annotationValues.get("column");
                        Column[] columns = (Column[])annotationValues.get("columns");
                        vermd = new VersionMetaData(strategy, column, indexed);
                        if (columns != null && columns.length > 0)
                        {
                            // Only use the first column
                            ColumnMetaData colmd = JDOAnnotationUtils.getColumnMetaDataForColumn(vermd, columns[0]);
                            vermd.addColumn(colmd);
                        }
                        JDOAnnotationUtils.addExtensionsToMetaData(vermd, (Extension[])annotationValues.get("extensions"));
                    }
                    else if (annName.equals(JDOAnnotationUtils.DATASTORE_IDENTITY))
                    {
                        String strategy = JDOAnnotationUtils.getIdentityStrategyString(
                            (IdGeneratorStrategy)annotationValues.get("strategy"));
                        String customStrategy = (String)annotationValues.get("customStrategy");
                        if (!StringUtils.isWhitespace(customStrategy))
                        {
                            // User has provided a JPOX extension strategy
                            strategy = customStrategy;
                        }
                        String sequence = (String)annotationValues.get("sequence");
                        String column = (String)annotationValues.get("column");
                        Column[] columns = (Column[])annotationValues.get("columns");
                        idmd = new IdentityMetaData(cmd, column, strategy, sequence);
                        if (columns != null && columns.length > 0)
                        {
                            // Only use the first column
                            ColumnMetaData colmd = JDOAnnotationUtils.getColumnMetaDataForColumn(idmd, columns[0]);
                            idmd.addColumn(colmd);
                        }
                        JDOAnnotationUtils.addExtensionsToMetaData(idmd, (Extension[])annotationValues.get("extensions"));
                    }
                    else if (annName.equals(JDOAnnotationUtils.PRIMARY_KEY))
                    {
                        String pkName = (String)annotationValues.get("name");
                        String pkColumn = (String)annotationValues.get("column");
                        Column[] columns = (Column[])annotationValues.get("columns");
                        pkmd = new PrimaryKeyMetaData(null, pkName, pkColumn);
                        if (columns != null && columns.length > 0)
                        {
                            for (int j=0;j<columns.length;j++)
                            {
                                pkmd.addColumn(JDOAnnotationUtils.getColumnMetaDataForColumn(pkmd, columns[j]));
                            }
                        }
                    }
                    else if (annName.equals(JDOAnnotationUtils.JOINS))
                    {
                        if (joins != null)
                        {
                            JPOXLogger.METADATA.warn(LOCALISER.msg("MetaData.Annotations.JoinSpecificationConflict",
                                cmd.getFullClassName()));
                        }
                        Join[] js = (Join[])annotationValues.get("value");
                        if (js != null && js.length > 0)
                        {
                            joins = new JoinMetaData[js.length];
                            for (int j=0;j<js.length;j++)
                            {
                                String deleteAction = JDOAnnotationUtils.getForeignKeyActionString(js[j].deleteAction());
                                joins[j] = new JoinMetaData(cmd, js[j].table(), null, null,
                                    js[j].column(), js[j].outer(), deleteAction, js[i].indexed(), js[i].unique());
                            }
                        }
                    }
                    else if (annName.equals(JDOAnnotationUtils.JOIN))
                    {
                        if (joins != null)
                        {
                            JPOXLogger.METADATA.warn(LOCALISER.msg("MetaData.Annotations.JoinSpecificationConflict",
                                cmd.getFullClassName()));
                        }
                        joins = new JoinMetaData[1];
                        joins[0] = new JoinMetaData(cmd, (String)annotationValues.get("table"), null, null,
                            (String)annotationValues.get("column"), (String)annotationValues.get("outer"),
                            ((ForeignKeyAction)annotationValues.get("deleteAction")).toString(),
                            (String)annotationValues.get("indexed"),
                            (String)annotationValues.get("unique"));
                        JDOAnnotationUtils.addExtensionsToMetaData(joins[0], (Extension[])annotationValues.get("extensions"));
                    }
                    else if (annName.equals(JDOAnnotationUtils.INHERITANCE))
                    {
                        String strategy = JDOAnnotationUtils.getInheritanceStrategyString(
                            (InheritanceStrategy)annotationValues.get("strategy"));
                        String customStrategy = (String)annotationValues.get("customStrategy");
                        if (!StringUtils.isWhitespace(customStrategy))
                        {
                            // User has provided a JPOX extension strategy
                            strategy = customStrategy;
                        }
                        inhmd = new InheritanceMetaData(cmd, strategy);
                    }
                    else if (annName.equals(JDOAnnotationUtils.DISCRIMINATOR))
                    {
                        DiscriminatorStrategy discriminatorStrategy = (DiscriminatorStrategy)annotationValues.get("strategy");
                        String strategy = JDOAnnotationUtils.getDiscriminatorStrategyString(discriminatorStrategy);
                        String column = (String)annotationValues.get("column");
                        String indexed = (String)annotationValues.get("indexed");
                        String value = (String)annotationValues.get("value");
                        Column[] columns = (Column[])annotationValues.get("columns");
                        dismd = new DiscriminatorMetaData(null, column, value, strategy, indexed);
                        if (columns != null && columns.length > 0)
                        {
                            // Only use the first column
                            ColumnMetaData colmd = JDOAnnotationUtils.getColumnMetaDataForColumn(dismd, columns[0]);
                            dismd.setColumnMetaData(colmd);
                        }
                    }
                    else if (annName.equals(JDOAnnotationUtils.QUERIES))
                    {
                        if (queries != null)
                        {
                            JPOXLogger.METADATA.warn(LOCALISER.msg("MetaData.Annotations.QuerySpecificationConflict",
                                cmd.getFullClassName()));
                        }
                        Query[] qs = (Query[])annotationValues.get("value");
                        queries = new QueryMetaData[qs.length];
                        for (int j=0;j<queries.length;j++)
                        {
                            String lang = JDOAnnotationUtils.getQueryLanguageName(qs[j].language());
                            String resultClassName = (qs[j].resultClass() != null && qs[j].resultClass() != void.class ?
                                    qs[j].resultClass().getName() : null);
                            queries[j] = new QueryMetaData(cmd, cls.getName(), qs[j].name(), lang,
                                "" + qs[j].unmodifiable(), resultClassName, null, qs[j].unique(), qs[j].fetchPlan());
                            queries[j].setQuery(qs[j].value());
                            JDOAnnotationUtils.addExtensionsToMetaData(queries[j], qs[j].extensions());
                        }
                    }
                    else if (annName.equals(JDOAnnotationUtils.QUERY))
                    {
                        if (queries != null)
                        {
                            JPOXLogger.METADATA.warn(LOCALISER.msg("MetaData.Annotations.QuerySpecificationConflict",
                                cmd.getFullClassName()));
                        }
                        queries = new QueryMetaData[1];
                        String unmodifiable = "" + annotationValues.get("unmodifiable");
                        Class resultClassValue = (Class)annotationValues.get("resultClass");
                        String resultClassName =
                            (resultClassValue != null && resultClassValue != void.class ? resultClassValue.getName() : null);
                        String lang = JDOAnnotationUtils.getQueryLanguageName((String)annotationValues.get("language"));
                        queries[0] = new QueryMetaData(cmd, cls.getName(), (String)annotationValues.get("name"),
                            lang.toString(), unmodifiable, resultClassName, null, (String)annotationValues.get("unique"),
                            (String)annotationValues.get("fetchPlan"));
                        queries[0].setQuery((String)annotationValues.get("value"));
                        JDOAnnotationUtils.addExtensionsToMetaData(queries[0], (Extension[])annotationValues.get("extensions"));
                    }
                    else if (annName.equals(JDOAnnotationUtils.FETCHPLANS))
                    {
                        if (fetchPlans != null)
                        {
                            JPOXLogger.METADATA.warn(LOCALISER.msg("MetaData.Annotations.FetchPlanSpecificationConflict",
                                cmd.getFullClassName()));
                        }
                        FetchPlan[] plans = (FetchPlan[])annotationValues.get("value");
                        fetchPlans = new FetchPlanMetaData[plans.length];
                        for (int j=0;j<plans.length;j++)
                        {
                            fetchPlans[j] = new FetchPlanMetaData(cmd, plans[j].name(), "" + plans[j].maxFetchDepth(),
                                "" + plans[j].fetchSize());
                            int numGroups = plans[j].fetchGroups().length;
                            for (int k=0;k<numGroups;k++)
                            {
                                FetchGroupMetaData fgmd = new FetchGroupMetaData(fetchPlans[j], null,
                                    plans[j].fetchGroups()[k]);
                                fetchPlans[j].addFetchGroup(fgmd);
                            }
                        }
                    }
                    else if (annName.equals(JDOAnnotationUtils.FETCHPLAN))
                    {
                        if (fetchPlans != null)
                        {
                            JPOXLogger.METADATA.warn(LOCALISER.msg("MetaData.Annotations.FetchPlanSpecificationConflict",
                                cmd.getFullClassName()));
                        }
                        fetchPlans = new FetchPlanMetaData[1];
                        int maxFetchDepth = ((Integer)annotationValues.get("max-fetch-depth")).intValue();
                        int fetchSize = ((Integer)annotationValues.get("fetch-size")).intValue();
                        fetchPlans[0] = new FetchPlanMetaData(cmd, (String)annotationValues.get("name"),
                            "" + maxFetchDepth, "" + fetchSize);
                    }
                    else if (annName.equals(JDOAnnotationUtils.FETCHGROUPS))
                    {
                        if (fetchGroups != null)
                        {
                            JPOXLogger.METADATA.warn(LOCALISER.msg("MetaData.Annotations.FetchGroupSpecificationConflict",
                                cmd.getFullClassName()));
                        }
                        FetchGroup[] groups = (FetchGroup[])annotationValues.get("value");
                        fetchGroups = new FetchGroupMetaData[groups.length];
                        for (int j=0;j<groups.length;j++)
                        {
                            fetchGroups[j] = new FetchGroupMetaData(cmd, groups[j].postLoad(), groups[j].name());
                            int numFields = groups[j].members().length;
                            for (int k=0;k<numFields;k++)
                            {
                                FieldMetaData fmd = mgr.getMetaDataFactory().newFieldObject(fetchGroups[j],
                                    groups[j].members()[k].name(),
                                    null, null, null, null, null, null, null, null, null, null, null,
                                    null, null, null, null, "" + groups[j].members()[k].recursionDepth(), null, null, null, null);
                                fetchGroups[j].addMember(fmd);
                            }
                        }
                    }
                    else if (annName.equals(JDOAnnotationUtils.FETCHGROUP))
                    {
                        if (fetchGroups != null)
                        {
                            JPOXLogger.METADATA.warn(LOCALISER.msg("MetaData.Annotations.FetchGroupSpecificationConflict",
                                cmd.getFullClassName()));
                        }
                        fetchGroups = new FetchGroupMetaData[1];
                        fetchGroups[0] = new FetchGroupMetaData(cmd, (String)annotationValues.get("postLoad"),
                            (String)annotationValues.get("name"));
                        Persistent[] fields = (Persistent[])annotationValues.get("members");
                        if (fields != null)
                        {
                            for (int j=0;j<fields.length;j++)
                            {
                                FieldMetaData fmd = mgr.getMetaDataFactory().newFieldObject(fetchGroups[0], fields[j].name(),
                                    null, null, null, null, null, null, null, null, null, null, null,
                                    null, null, null, null, "" + fields[j].recursionDepth(), null, null, null, null);
                                fetchGroups[0].addMember(fmd);
                            }
                        }
                    }
                    else if (annName.equals(JDOAnnotationUtils.SEQUENCE))
                    {
                        String seqName = (String)annotationValues.get("name");
                        String seqStrategy = JDOAnnotationUtils.getSequenceStrategyString(
                            (SequenceStrategy)annotationValues.get("strategy"));
                        String seqSeq = (String)annotationValues.get("datastoreSequence");
                        Class seqFactory = (Class)annotationValues.get("factoryClass");
                        String seqFactoryClassName = null;
                        if (seqFactory != null && seqFactory != void.class)
                        {
                            seqFactoryClassName = seqFactory.getName();
                        }
                        seqmd = new SequenceMetaData(null, seqName, seqSeq, seqFactoryClassName, seqStrategy, null, null);
                        JDOAnnotationUtils.addExtensionsToMetaData(seqmd, (Extension[])annotationValues.get("extensions"));
                    }
                    else if (annName.equals(JDOAnnotationUtils.INDICES))
                    {
                        // Multiple Indices for the class
                        Index[] values = (Index[])annotationValues.get("value");
                        if (values != null && values.length > 0)
                        {
                            indices = new HashSet<IndexMetaData>(values.length);
                            for (int j=0;j<values.length;j++)
                            {
                                IndexMetaData idxmd = JDOAnnotationUtils.getIndexMetaData(values[j].name(), values[j].table(),
                                    "" + values[j].unique(), values[j].members(), values[j].columns());
                                if (idxmd.getNumberOfColumns() == 0 && idxmd.getNumberOfMembers() == 0)
                                {
                                    JPOXLogger.METADATA.warn(LOCALISER.msg("MetaData.Annotations.IndexAtClassWithoutFieldsColumns",
                                        cls.getName()));
                                }
                                else
                                {
                                    indices.add(idxmd);
                                }
                            }
                        }
                    }
                    else if (annName.equals(JDOAnnotationUtils.INDEX))
                    {
                        // Single Index for the class
                        String name = (String)annotationValues.get("name");
                        String table = (String)annotationValues.get("table");
                        String unique = (String)annotationValues.get("unique");
                        String[] members = (String[])annotationValues.get("members");
                        Column[] columns = (Column[])annotationValues.get("columns");

                        IndexMetaData idxmd = JDOAnnotationUtils.getIndexMetaData(name, table, unique, members, columns);
                        if (idxmd.getNumberOfColumns() == 0 && idxmd.getNumberOfMembers() == 0)
                        {
                            JPOXLogger.METADATA.warn(LOCALISER.msg("MetaData.Annotations.IndexAtClassWithoutFieldsColumns",
                                cls.getName()));
                        }
                        else
                        {
                            indices = new HashSet<IndexMetaData>(1);
                            indices.add(idxmd);
                        }
                    }
                    else if (annName.equals(JDOAnnotationUtils.UNIQUES))
                    {
                        // Multiple Unique Constraints for the class
                        Unique[] values = (Unique[])annotationValues.get("value");
                        if (values != null && values.length > 0)
                        {
                            uniqueKeys = new HashSet<UniqueMetaData>(values.length);
                            for (int j=0;j<values.length;j++)
                            {
                                UniqueMetaData unimd = JDOAnnotationUtils.getUniqueMetaData(values[j].name(),
                                    values[j].table(), "" + values[j].deferred(), values[j].members(), values[j].columns());
                                if (unimd.getNumberOfColumns() == 0 && unimd.getNumberOfMembers() == 0)
                                {
                                    JPOXLogger.METADATA.warn(LOCALISER.msg("MetaData.Annotations.UniqueAtClassWithoutFieldsColumns",
                                        cls.getName()));
                                }
                                else
                                {
                                    uniqueKeys.add(unimd);
                                }
                            }
                        }
                    }
                    else if (annName.equals(JDOAnnotationUtils.UNIQUE))
                    {
                        // Single Unique constraint for the class
                        String name = (String)annotationValues.get("name");
                        String table = (String)annotationValues.get("table");
                        String deferred = (String)annotationValues.get("deferred");
                        String[] members = (String[])annotationValues.get("members");
                        Column[] columns = (Column[])annotationValues.get("columns");

                        UniqueMetaData unimd = JDOAnnotationUtils.getUniqueMetaData(name, table, deferred, members, columns);
                        if (unimd.getNumberOfColumns() == 0 && unimd.getNumberOfMembers() == 0)
                        {
                            JPOXLogger.METADATA.warn(LOCALISER.msg("MetaData.Annotations.UniqueAtClassWithoutFieldsColumns",
                                cls.getName()));
                        }
                        else
                        {
                            uniqueKeys = new HashSet<UniqueMetaData>(1);
                            uniqueKeys.add(unimd);
                        }
                    }
                    else if (annName.equals(JDOAnnotationUtils.FOREIGNKEYS))
                    {
                        // Multiple FKs for the class
                        ForeignKey[] values = (ForeignKey[])annotationValues.get("value");
                        if (values != null && values.length > 0)
                        {
                            fks = new HashSet<ForeignKeyMetaData>(values.length);
                            for (int j=0;j<values.length;j++)
                            {
                                String deleteAction = JDOAnnotationUtils.getForeignKeyActionString(values[j].deleteAction());
                                String updateAction = JDOAnnotationUtils.getForeignKeyActionString(values[j].deleteAction());
                                ForeignKeyMetaData fkmd = JDOAnnotationUtils.getFKMetaData(values[j].name(),
                                    values[j].table(), values[j].unique(), "" + values[j].deferred(),
                                    deleteAction, updateAction, values[j].members(), values[j].columns());
                                if (fkmd.getNumberOfColumns() == 0 && fkmd.getNumberOfMembers() == 0)
                                {
                                    JPOXLogger.METADATA.warn(LOCALISER.msg("MetaData.Annotations.FKAtClassWithoutFieldsColumns",
                                        cls.getName()));
                                }
                                else
                                {
                                    fks.add(fkmd);
                                }
                            }
                        }
                    }
                    else if (annName.equals(JDOAnnotationUtils.FOREIGNKEY))
                    {
                        // Single FK constraint for the class
                        String name = (String)annotationValues.get("name");
                        String table = (String)annotationValues.get("table");
                        String unique = (String)annotationValues.get("unique");
                        String deferred = (String)annotationValues.get("deferred");
                        String deleteAction = JDOAnnotationUtils.getForeignKeyActionString(
                            (ForeignKeyAction)annotationValues.get("deleteAction"));
                        String updateAction = JDOAnnotationUtils.getForeignKeyActionString(
                            (ForeignKeyAction)annotationValues.get("updateAction"));
                        String[] members = (String[])annotationValues.get("members");
                        Column[] columns = (Column[])annotationValues.get("columns");

                        ForeignKeyMetaData fkmd = JDOAnnotationUtils.getFKMetaData(name, table, unique, deferred,
                            deleteAction, updateAction, members, columns);
                        if (fkmd.getNumberOfColumns() == 0 && fkmd.getNumberOfMembers() == 0)
                        {
                            JPOXLogger.METADATA.warn(LOCALISER.msg("MetaData.Annotations.FKAtClassWithoutFieldsColumns",
                                cls.getName()));
                        }
                        else
                        {
                            fks = new HashSet<ForeignKeyMetaData>(1);
                            fks.add(fkmd);
                        }
                    }
                    else if (annName.equals(JDOAnnotationUtils.COLUMNS))
                    {
                        // Unmapped column specification
                        Column[] cols = (Column[])annotationValues.get("value");
                        if (cols != null && cols.length > 0)
                        {
                            unmappedColumns = new ColumnMetaData[cols.length];
                            for (int j=0;j<cols.length;j++)
                            {
                                unmappedColumns[j] = JDOAnnotationUtils.getColumnMetaDataForColumn(cmd, cols[j]);
                                JDOAnnotationUtils.addExtensionsToMetaData(unmappedColumns[j], cols[j].extensions());
                            }
                        }
                    }
                    else if (annName.equals(JDOAnnotationUtils.EXTENSIONS))
                    {
                        Extension[] values = (Extension[])annotationValues.get("value");
                        if (values != null && values.length > 0)
                        {
                            extensions = new HashSet<ExtensionMetaData>(values.length);
                            for (int j=0;j<values.length;j++)
                            {
                                ExtensionMetaData extmd = new ExtensionMetaData(values[j].vendorName(),
                                    values[j].key().toString(), values[j].value().toString());
                                extensions.add(extmd);
                            }
                        }
                    }
                    else if (annName.equals(JDOAnnotationUtils.EXTENSION))
                    {
                        ExtensionMetaData extmd = new ExtensionMetaData((String)annotationValues.get("vendorName"),
                            (String)annotationValues.get("key"), (String)annotationValues.get("value"));
                        extensions = new HashSet<ExtensionMetaData>(1);
                        extensions.add(extmd);
                    }
                    else
                    {
                        JPOXLogger.METADATA.error(LOCALISER.msg("MetaData.Annotations.AnnotationNotProcessed",
                            cls.getName(), annotations[i].getName()));
                    }
                }
            }

            if (cmd != null)
            {
                // Either PersistenceCapable, PersistenceAware or PersistentInterface so build up the metadata
                JPOXLogger.METADATA.info(LOCALISER.msg("MetaData.Annotations.ClassUsingAnnotations", cls.getName(), "JDO"));

                if (cmd instanceof ClassMetaData)
                {
                    pmd.addClass((ClassMetaData)cmd);
                }
                else if (cmd instanceof InterfaceMetaData)
                {
                    pmd.addInterface((InterfaceMetaData)cmd);
                }

                if (embeddedOnly)
                {
                    cmd.setEmbeddedOnly();
                }
                if (idmd != null)
                {
                    // Datastore identity
                    idmd.setParent(cmd);
                    cmd.setIdentityMetaData(idmd);
                }
                if (pkmd != null)
                {
                    // Primary Key
                    pkmd.setParent(cmd);
                    cmd.setPrimaryKeyMetaData(pkmd);
                }

                if (vermd != null)
                {
                    // Version
                    vermd.setParent(cmd);
                    cmd.setVersionMetaData(vermd);
                }

                if (inhmd != null)
                {
                    // Inheritance
                    if (dismd != null)
                    {
                        inhmd.setDiscriminatorMetaData(dismd);
                    }
                    inhmd.setParent(cmd);
                    cmd.setInheritanceMetaData(inhmd);
                }

                if (joins != null && joins.length > 0)
                {
                    // Joins
                    for (int i=0;i<joins.length;i++)
                    {
                        joins[i].setParent(cmd);
                        cmd.addJoin(joins[i]);
                    }
                }
                if (queries != null && queries.length > 0)
                {
                    // Named Queries
                    for (int i=0;i<queries.length;i++)
                    {
                        queries[i].setParent(cmd);
                        cmd.addQuery(queries[i]);
                    }
                }
                if (fetchGroups != null && fetchGroups.length > 0)
                {
                    // Fetch Groups
                    for (int i=0;i<fetchGroups.length;i++)
                    {
                        fetchGroups[i].setParent(cmd);
                        cmd.addFetchGroup(fetchGroups[i]);
                    }
                }

                if (seqmd != null)
                {
                    // Sequence - currently only allowing 1 per class (should really be on the package)
                    seqmd.setParent(cmd.getPackageMetaData());
                    cmd.getPackageMetaData().addSequence(seqmd);
                }

                if (indices != null)
                {
                    Iterator iter = indices.iterator();
                    while (iter.hasNext())
                    {
                        IndexMetaData idxmd = (IndexMetaData)iter.next();
                        idxmd.setParent(cmd);
                        cmd.addIndex(idxmd);
                    }
                }
                if (uniqueKeys != null)
                {
                    Iterator iter = uniqueKeys.iterator();
                    while (iter.hasNext())
                    {
                        UniqueMetaData unimd = (UniqueMetaData)iter.next();
                        unimd.setParent(cmd);
                        cmd.addUniqueConstraint(unimd);
                    }
                }
                if (fks != null)
                {
                    Iterator iter = fks.iterator();
                    while (iter.hasNext())
                    {
                        ForeignKeyMetaData fkmd = (ForeignKeyMetaData)iter.next();
                        fkmd.setParent(cmd);
                        cmd.addForeignKey(fkmd);
                    }
                }
                if (unmappedColumns != null)
                {
                    Iterator iter = fks.iterator();
                    while (iter.hasNext())
                    {
                        ColumnMetaData colmd = (ColumnMetaData)iter.next();
                        colmd.setParent(cmd);
                        cmd.addUnmappedColumn(colmd);
                    }
                }
                if (extensions != null)
                {
                    Iterator<ExtensionMetaData> iter = extensions.iterator();
                    while (iter.hasNext())
                    {
                        ExtensionMetaData extmd = iter.next();
                        cmd.addExtension(extmd.getVendorName(), extmd.getKey(), extmd.getValue());
                    }
                }
            }
        }

        return cmd;
    }

    /**
     * Convenience method to process the annotations for a field/property.
     * The passed annotations may have been specified on the field or on the getter methods.
     * @param cmd The ClassMetaData/InterfaceMetaData to update
     * @param member The field/property
     * @param annotations Annotations for the field/property
     * @param propertyAccessor if there are properties for this class
     * @return The FieldMetaData/PropertyMetaData that was added (if any)
     */
    protected AbstractMemberMetaData processMemberAnnotations(AbstractClassMetaData cmd, Member member,
            AnnotationObject[] annotations, boolean propertyAccessor)
    {
        AbstractMemberMetaData mmd = null;
        if (annotations != null && annotations.length > 0)
        {
            boolean primaryKey = false;
            boolean serialised = false;
            boolean nonPersistentField = false;
            boolean transactionalField = false;

            Class[] elementTypes = null;
            String embeddedElement = null;
            String serializedElement = null;
            String dependentElement = null;

            Class keyType = null;
            String embeddedKey = null;
            String serializedKey = null;
            String dependentKey = null;

            Class valueType = null;
            String embeddedValue = null;
            String serializedValue = null;
            String dependentValue = null;

            String embeddedOwnerField = null;
            String embeddedNullIndicatorColumn = null;
            String embeddedNullIndicatorValue = null;
            Persistent[] embeddedMembers = null;
            Persistent[] embeddedElementMembers = null;
            Persistent[] embeddedKeyMembers = null;
            Persistent[] embeddedValueMembers = null;

            ColumnMetaData[] colmds = null;
            JoinMetaData joinmd = null;
            ElementMetaData elemmd = null;
            KeyMetaData keymd = null;
            ValueMetaData valuemd = null;
            OrderMetaData ordermd = null;
            IndexMetaData idxmd = null;
            UniqueMetaData unimd = null;
            ForeignKeyMetaData fkmd = null;
            HashSet<ExtensionMetaData> extensions = null;

            for (int i=0;i<annotations.length;i++)
            {
                if (isSupportedAnnotation(annotations[i].getName()))
                {
                    String annName = annotations[i].getName();
                    HashMap<String, Object> annotationValues = annotations[i].getNameValueMap();
                    if (annName.equals(JDOAnnotationUtils.PERSISTENT))
                    {
                        String pk = "" + annotationValues.get("primaryKey");
                        String dfg = (String)annotationValues.get("defaultFetchGroup");
                        String nullValue = JDOAnnotationUtils.getNullValueString((NullValue)annotationValues.get("nullValue"));
                        String embedded = (String)annotationValues.get("embedded");
                        String serialized = (String)annotationValues.get("serialized");
                        String dependent = (String)annotationValues.get("dependent");
                        String valueStrategy = JDOAnnotationUtils.getIdentityStrategyString(
                            (IdGeneratorStrategy)annotationValues.get("valueStrategy"));
                        String customValueStrategy = (String)annotationValues.get("customValueStrategy");
                        if (!StringUtils.isWhitespace(customValueStrategy))
                        {
                            // User has provided a JPOX extension strategy
                            valueStrategy = customValueStrategy;
                        }
                        String modifier = JDOAnnotationUtils.getFieldPersistenceModifierString(
                            (PersistenceModifier)annotationValues.get("persistenceModifier"));
                        if (modifier == null)
                        {
                            modifier = FieldPersistenceModifier.PERSISTENT.toString();
                        }
                        String sequence = (String)annotationValues.get("sequence");
                        String mappedBy = (String)annotationValues.get("mappedBy");
                        String table = (String)annotationValues.get("table");
                        String column = (String)annotationValues.get("column");
                        String loadFetchGroup = (String)annotationValues.get("loadFetchGroup");
                        String fieldTypeName = null;
                        int recursionDepth = ((Integer)annotationValues.get("recursionDepth")).intValue();
                        Class[] fieldTypes = (Class[])annotationValues.get("types");
                        if (fieldTypes != null && fieldTypes.length > 0)
                        {
                            StringBuffer typeStr = new StringBuffer();
                            for (int j=0;j<fieldTypes.length;j++)
                            {
                                if (typeStr.length() > 0)
                                {
                                    typeStr.append(',');
                                }
                                if (fieldTypes[j] != null && fieldTypes[j] != void.class)
                                {
                                    typeStr.append(fieldTypes[j].getName());
                                }
                            }
                            fieldTypeName = typeStr.toString();
                        }
                        dependentElement = (String)annotationValues.get("dependentElement");
                        serializedElement = (String)annotationValues.get("serializedElement");
                        embeddedElement = (String)annotationValues.get("embeddedElement");
                        dependentKey = (String)annotationValues.get("dependentKey");
                        serializedKey = (String)annotationValues.get("serializedKey");
                        embeddedKey = (String)annotationValues.get("embeddedKey");
                        dependentValue = (String)annotationValues.get("dependentValue");
                        serializedValue = (String)annotationValues.get("serializedValue");
                        embeddedValue = (String)annotationValues.get("embeddedValue");

                        if (member.isProperty())
                        {
                            // Property
                            mmd = mgr.getMetaDataFactory().newPropertyObject(cmd, member.getName(), pk,
                                modifier, dfg, nullValue, embedded, serialized, dependent, mappedBy, column,
                                table, null, null, null, null, null, "" + recursionDepth,
                                loadFetchGroup, valueStrategy, sequence, fieldTypeName, null);
                        }
                        else
                        {
                            // Field
                            mmd = mgr.getMetaDataFactory().newFieldObject(cmd, member.getName(), pk,
                                modifier, dfg, nullValue, embedded, serialized, dependent, mappedBy, column,
                                table, null, null, null, null, null, "" + recursionDepth,
                                loadFetchGroup, valueStrategy, sequence, fieldTypeName);
                        }

                        // Add any columns defined on the @Persistent
                        Column[] columns = (Column[])annotationValues.get("columns");
                        if (columns != null && columns.length > 0)
                        {
                            for (int j=0;j<columns.length;j++)
                            {
                                mmd.addColumn(JDOAnnotationUtils.getColumnMetaDataForColumn(mmd, columns[j]));
                            }
                        }
                        JDOAnnotationUtils.addExtensionsToMetaData(mmd, (Extension[])annotationValues.get("extensions"));
                    }
                    else if (annName.equals(JDOAnnotationUtils.PRIMARY_KEY))
                    {
                        primaryKey = true;
                        if (cmd.getIdentityType() == IdentityType.DATASTORE)
                        {
                            // ClassMetaData was created as DATASTORE so change it to APPLICATION
                            cmd.setIdentityType(IdentityType.APPLICATION);
                        }
                    }
                    else if (annName.equals(JDOAnnotationUtils.SERIALIZED))
                    {
                        serialised = true;
                    }
                    else if (annName.equals(JDOAnnotationUtils.NOTPERSISTENT))
                    {
                        nonPersistentField = true;
                    }
                    else if (annName.equals(JDOAnnotationUtils.TRANSACTIONAL))
                    {
                        transactionalField = true;
                    }
                    else if (annName.equals(JDOAnnotationUtils.COLUMNS))
                    {
                        // Multiple column specification
                        Column[] cols = (Column[])annotationValues.get("value");
                        if (cols != null && cols.length > 0)
                        {
                            colmds = new ColumnMetaData[cols.length];
                            for (int j=0;j<cols.length;j++)
                            {
                                colmds[j] = JDOAnnotationUtils.getColumnMetaDataForColumn(mmd, cols[j]);
                                JDOAnnotationUtils.addExtensionsToMetaData(colmds[j], cols[j].extensions());
                            }
                        }
                    }
                    else if (annName.equals(JDOAnnotationUtils.COLUMN))
                    {
                        // Single column specification
                        colmds = new ColumnMetaData[1];
                        colmds[0] = JDOAnnotationUtils.getColumnMetaDataForAnnotations(null, annotationValues);
                        JDOAnnotationUtils.addExtensionsToMetaData(colmds[0], (Extension[])annotationValues.get("extensions"));
                    }
                    else if (annName.equals(JDOAnnotationUtils.JOIN))
                    {
                        String joinColumn = (String)annotationValues.get("column");
                        String joinOuter = (String)annotationValues.get("outer");
                        String deleteAction = JDOAnnotationUtils.getForeignKeyActionString(
                            (ForeignKeyAction)annotationValues.get("deleteAction"));
                        String pkName = (String)annotationValues.get("primaryKey");
                        String fkName = (String)annotationValues.get("foreignKey");
                        String generateFK = (String)annotationValues.get("generateForeignKey");
                        String indexed = (String)annotationValues.get("indexed");
                        String indexName = (String)annotationValues.get("index");
                        String unique = (String)annotationValues.get("unique");
                        String uniqueName = (String)annotationValues.get("uniqueKey");
                        String generatePK = (String)annotationValues.get("generatePrimaryKey");
                        if (!StringUtils.isWhitespace(uniqueName))
                        {
                            unique = "true";
                        }
                        if (!StringUtils.isWhitespace(indexName))
                        {
                            indexed = "true";
                        }
                        Column[] joinColumns = (Column[])annotationValues.get("columns");
                        joinmd = new JoinMetaData(null, null, null, null, joinColumn, joinOuter, deleteAction, indexed, unique);

                        if (!StringUtils.isWhitespace(pkName))
                        {
                            PrimaryKeyMetaData pkmd = new PrimaryKeyMetaData(joinmd, pkName, null);
                            joinmd.setPrimaryKeyMetaData(pkmd);
                        }
                        else if (generatePK != null && generatePK.equalsIgnoreCase("true"))
                        {
                            joinmd.setPrimaryKeyMetaData(new PrimaryKeyMetaData(joinmd, null, null));
                        }

                        if (!StringUtils.isWhitespace(fkName))
                        {
                            ForeignKeyMetaData joinFkmd = joinmd.getForeignKeyMetaData();
                            if (joinFkmd == null)
                            {
                                joinFkmd = new ForeignKeyMetaData(fkName, null, null, null, null, null);
                                joinmd.setForeignKeyMetaData(joinFkmd);
                            }
                            else
                            {
                                joinFkmd.setName(fkName);
                            }
                        }
                        else if (generateFK != null && generateFK.equalsIgnoreCase("true"))
                        {
                            joinmd.setForeignKeyMetaData(new ForeignKeyMetaData(null, null, null, null, null, null));
                        }

                        if (!StringUtils.isWhitespace(indexName))
                        {
                            IndexMetaData joinIdxmd = joinmd.getIndexMetaData();
                            if (joinIdxmd == null)
                            {
                                joinIdxmd = new IndexMetaData(indexName, null, null);
                                joinmd.setIndexMetaData(joinIdxmd);
                            }
                            else
                            {
                                joinIdxmd.setName(indexName);
                            }
                        }

                        if (!StringUtils.isWhitespace(uniqueName))
                        {
                            UniqueMetaData joinUnimd = joinmd.getUniqueMetaData();
                            if (joinUnimd == null)
                            {
                                joinUnimd = new UniqueMetaData(uniqueName, null, null);
                                joinmd.setUniqueMetaData(joinUnimd);
                            }
                            else
                            {
                                joinUnimd.setName(uniqueName);
                            }
                        }
                        if (joinColumns != null && joinColumns.length > 0)
                        {
                            for (int j=0;j<joinColumns.length;j++)
                            {
                                joinmd.addColumn(JDOAnnotationUtils.getColumnMetaDataForColumn(joinmd, joinColumns[j]));
                            }
                        }
                        JDOAnnotationUtils.addExtensionsToMetaData(joinmd, (Extension[])annotationValues.get("extensions"));
                    }
                    else if (annName.equals(JDOAnnotationUtils.ELEMENT))
                    {
                        // Element of a Collection/Array
                        elementTypes = (Class[])annotationValues.get("types");
                        embeddedElement = (String)annotationValues.get("embedded");
                        serializedElement = (String)annotationValues.get("serialized");
                        dependentElement = (String)annotationValues.get("dependent");
                        String elementColumn = (String)annotationValues.get("column");
                        String elementDeleteAction = JDOAnnotationUtils.getForeignKeyActionString(
                            (ForeignKeyAction)annotationValues.get("deleteAction"));
                        String elementUpdateAction = JDOAnnotationUtils.getForeignKeyActionString(
                            (ForeignKeyAction)annotationValues.get("updateAction"));
                        String elementMappedBy = (String)annotationValues.get("mappedBy");
                        Column[] elementColumns = (Column[])annotationValues.get("columns");
                        String fkName = (String)annotationValues.get("foreignKey");
                        String generateFK = (String)annotationValues.get("generateForeignKey");
                        String indexed = (String)annotationValues.get("indexed");
                        String indexName = (String)annotationValues.get("index");
                        String unique = (String)annotationValues.get("unique");
                        String uniqueName = (String)annotationValues.get("uniqueKey");
                        if (!StringUtils.isWhitespace(uniqueName))
                        {
                            unique = "true";
                        }
                        if (!StringUtils.isWhitespace(indexName))
                        {
                            indexed = "true";
                        }
                        elemmd = new ElementMetaData(null, elementColumn, elementDeleteAction, elementUpdateAction,
                            indexed,  unique, elementMappedBy);
                        if (!StringUtils.isWhitespace(fkName))
                        {
                            ForeignKeyMetaData elemFkmd = elemmd.getForeignKeyMetaData();
                            if (elemFkmd == null)
                            {
                                elemFkmd = new ForeignKeyMetaData(fkName, null, null, null, null, null);
                                elemmd.setForeignKeyMetaData(elemFkmd);
                            }
                            else
                            {
                                elemFkmd.setName(fkName);
                            }
                        }
                        else if (generateFK != null && generateFK.equalsIgnoreCase("true"))
                        {
                            elemmd.setForeignKeyMetaData(new ForeignKeyMetaData(null, null, null, null, null, null));
                        }

                        if (!StringUtils.isWhitespace(indexName))
                        {
                            IndexMetaData elemIdxmd = elemmd.getIndexMetaData();
                            if (elemIdxmd == null)
                            {
                                elemIdxmd = new IndexMetaData(indexName, null, null);
                                elemmd.setIndexMetaData(elemIdxmd);
                            }
                            else
                            {
                                elemIdxmd.setName(indexName);
                            }
                        }

                        if (!StringUtils.isWhitespace(uniqueName))
                        {
                            UniqueMetaData elemUnimd = elemmd.getUniqueMetaData();
                            if (elemUnimd == null)
                            {
                                elemUnimd = new UniqueMetaData(uniqueName, null, null);
                                elemmd.setUniqueMetaData(elemUnimd);
                            }
                            else
                            {
                                elemUnimd.setName(uniqueName);
                            }
                        }
                        if (elementColumns != null && elementColumns.length > 0)
                        {
                            for (int j=0;j<elementColumns.length;j++)
                            {
                                elemmd.addColumn(JDOAnnotationUtils.getColumnMetaDataForColumn(elemmd, elementColumns[j]));
                            }
                        }
                        JDOAnnotationUtils.addExtensionsToMetaData(elemmd, (Extension[])annotationValues.get("extensions"));

                        Embedded[] embeddedMappings = (Embedded[])annotationValues.get("embeddedMapping");
                        if (embeddedMappings != null && embeddedMappings.length > 0)
                        {
                            // Embedded element
                            EmbeddedMetaData embmd = new EmbeddedMetaData(elemmd, embeddedMappings[0].ownerMember(),
                                embeddedMappings[0].nullIndicatorColumn(), embeddedMappings[0].nullIndicatorValue());
                            elemmd.setEmbeddedMetaData(embmd);
                            embeddedElementMembers = embeddedMappings[0].members();
                            // Delay addition of embeddedElementMembers til completion of this loop so we have the element type
                        }
                    }
                    else if (annName.equals(JDOAnnotationUtils.KEY))
                    {
                        // Key of a Map
                        Class[] keyTypes = (Class[])annotationValues.get("types");
                        if (keyTypes != null && keyTypes.length > 0)
                        {
                            // TODO Support more than 1 value
                            keyType = keyTypes[0];
                        }
                        embeddedKey = (String)annotationValues.get("embedded");
                        serializedKey = (String)annotationValues.get("serialized");
                        dependentKey = (String)annotationValues.get("dependent");
                        String keyColumn = (String)annotationValues.get("column");
                        String keyDeleteAction = JDOAnnotationUtils.getForeignKeyActionString(
                            (ForeignKeyAction)annotationValues.get("deleteAction"));
                        String keyUpdateAction = JDOAnnotationUtils.getForeignKeyActionString(
                            (ForeignKeyAction)annotationValues.get("updateAction"));
                        String keyMappedBy = (String)annotationValues.get("mappedBy");
                        Column[] keyColumns = (Column[])annotationValues.get("columns");
                        String fkName = (String)annotationValues.get("foreignKey");
                        String generateFK = (String)annotationValues.get("generateForeignKey");
                        String indexed = (String)annotationValues.get("indexed");
                        String indexName = (String)annotationValues.get("index");
                        String unique = (String)annotationValues.get("unique");
                        String uniqueName = (String)annotationValues.get("uniqueKey");
                        if (!StringUtils.isWhitespace(uniqueName))
                        {
                            unique = "true";
                        }
                        if (!StringUtils.isWhitespace(indexName))
                        {
                            indexed = "true";
                        }
                        keymd = new KeyMetaData(null, keyColumn, keyDeleteAction, keyUpdateAction, indexed, unique,
                            keyMappedBy);
                        if (!StringUtils.isWhitespace(fkName))
                        {
                            ForeignKeyMetaData keyFkmd = keymd.getForeignKeyMetaData();
                            if (keyFkmd == null)
                            {
                                keyFkmd = new ForeignKeyMetaData(fkName, null, null, null, null, null);
                                keymd.setForeignKeyMetaData(keyFkmd);
                            }
                            else
                            {
                                keyFkmd.setName(fkName);
                            }
                        }
                        else if (generateFK != null && generateFK.equalsIgnoreCase("true"))
                        {
                            keymd.setForeignKeyMetaData(new ForeignKeyMetaData(null, null, null, null, null, null));
                        }

                        if (!StringUtils.isWhitespace(indexName))
                        {
                            IndexMetaData keyIdxmd = keymd.getIndexMetaData();
                            if (keyIdxmd == null)
                            {
                                keyIdxmd = new IndexMetaData(indexName, null, null);
                                keymd.setIndexMetaData(keyIdxmd);
                            }
                            else
                            {
                                keyIdxmd.setName(indexName);
                            }
                        }

                        if (!StringUtils.isWhitespace(uniqueName))
                        {
                            UniqueMetaData keyUnimd = keymd.getUniqueMetaData();
                            if (keyUnimd == null)
                            {
                                keyUnimd = new UniqueMetaData(uniqueName, null, null);
                                keymd.setUniqueMetaData(keyUnimd);
                            }
                            else
                            {
                                keyUnimd.setName(uniqueName);
                            }
                        }
                        if (keyColumns != null && keyColumns.length > 0)
                        {
                            for (int j=0;j<keyColumns.length;j++)
                            {
                                keymd.addColumn(JDOAnnotationUtils.getColumnMetaDataForColumn(keymd, keyColumns[j]));
                            }
                        }
                        JDOAnnotationUtils.addExtensionsToMetaData(keymd, (Extension[])annotationValues.get("extensions"));

                        Embedded[] embeddedMappings = (Embedded[])annotationValues.get("embeddedMapping");
                        if (embeddedMappings != null && embeddedMappings.length > 0)
                        {
                            // Embedded key
                            EmbeddedMetaData embmd = new EmbeddedMetaData(keymd, embeddedMappings[0].ownerMember(),
                                embeddedMappings[0].nullIndicatorColumn(), embeddedMappings[0].nullIndicatorValue());
                            keymd.setEmbeddedMetaData(embmd);
                            embeddedKeyMembers = embeddedMappings[0].members();
                            // Delay addition of embeddedKeyMembers til completion of this loop so we have the key type
                        }
                    }
                    else if (annName.equals(JDOAnnotationUtils.VALUE))
                    {
                        // Value of a Map
                        Class[] valueTypes = (Class[])annotationValues.get("types");
                        if (valueTypes != null && valueTypes.length > 0)
                        {
                            // TODO Support more than 1 value
                            valueType = valueTypes[0];
                        }
                        embeddedValue = (String)annotationValues.get("embedded");
                        serializedValue = (String)annotationValues.get("serialized");
                        dependentValue = (String)annotationValues.get("dependent");
                        String valueColumn = (String)annotationValues.get("column");
                        String valueDeleteAction = JDOAnnotationUtils.getForeignKeyActionString(
                            (ForeignKeyAction)annotationValues.get("deleteAction"));
                        String valueUpdateAction = JDOAnnotationUtils.getForeignKeyActionString(
                            (ForeignKeyAction)annotationValues.get("updateAction"));
                        String valueMappedBy = (String)annotationValues.get("mappedBy");
                        Column[] valueColumns = (Column[])annotationValues.get("columns");
                        String fkName = (String)annotationValues.get("foreignKey");
                        String generateFK = (String)annotationValues.get("generateForeignKey");
                        String indexed = (String)annotationValues.get("indexed");
                        String indexName = (String)annotationValues.get("index");
                        String unique = (String)annotationValues.get("unique");
                        String uniqueName = (String)annotationValues.get("uniqueKey");
                        if (!StringUtils.isWhitespace(uniqueName))
                        {
                            unique = "true";
                        }
                        if (!StringUtils.isWhitespace(indexName))
                        {
                            indexed = "true";
                        }
                        valuemd = new ValueMetaData(null, valueColumn, valueDeleteAction, valueUpdateAction, indexed,
                            unique, valueMappedBy);
                        if (!StringUtils.isWhitespace(fkName))
                        {
                            ForeignKeyMetaData valueFkmd = valuemd.getForeignKeyMetaData();
                            if (valueFkmd == null)
                            {
                                valueFkmd = new ForeignKeyMetaData(fkName, null, null, null, null, null);
                                valuemd.setForeignKeyMetaData(valueFkmd);
                            }
                            else
                            {
                                valueFkmd.setName(fkName);
                            }
                        }
                        else if (generateFK != null && generateFK.equalsIgnoreCase("true"))
                        {
                            valuemd.setForeignKeyMetaData(new ForeignKeyMetaData(null, null, null, null, null, null));
                        }

                        if (!StringUtils.isWhitespace(indexName))
                        {
                            IndexMetaData valueIdxmd = valuemd.getIndexMetaData();
                            if (valueIdxmd == null)
                            {
                                valueIdxmd = new IndexMetaData(indexName, null, null);
                                valuemd.setIndexMetaData(valueIdxmd);
                            }
                            else
                            {
                                valueIdxmd.setName(indexName);
                            }
                        }

                        if (!StringUtils.isWhitespace(uniqueName))
                        {
                            UniqueMetaData valueUnimd = valuemd.getUniqueMetaData();
                            if (valueUnimd == null)
                            {
                                valueUnimd = new UniqueMetaData(uniqueName, null, null);
                                valuemd.setUniqueMetaData(valueUnimd);
                            }
                            else
                            {
                                valueUnimd.setName(uniqueName);
                            }
                        }
                        if (valueColumns != null && valueColumns.length > 0)
                        {
                            for (int j=0;j<valueColumns.length;j++)
                            {
                                valuemd.addColumn(JDOAnnotationUtils.getColumnMetaDataForColumn(valuemd, valueColumns[j]));
                            }
                        }
                        JDOAnnotationUtils.addExtensionsToMetaData(valuemd, (Extension[])annotationValues.get("extensions"));

                        Embedded[] embeddedMappings = (Embedded[])annotationValues.get("embeddedMapping");
                        if (embeddedMappings != null && embeddedMappings.length > 0)
                        {
                            // Embedded value
                            EmbeddedMetaData embmd = new EmbeddedMetaData(valuemd, embeddedMappings[0].ownerMember(),
                                embeddedMappings[0].nullIndicatorColumn(), embeddedMappings[0].nullIndicatorValue());
                            valuemd.setEmbeddedMetaData(embmd);
                            embeddedValueMembers = embeddedMappings[0].members();
                            // Delay addition of embeddedValueMembers til completion of this loop so we have the value type
                        }
                    }
                    else if (annName.equals(JDOAnnotationUtils.ORDER))
                    {
                        String orderColumn = (String)annotationValues.get("column");
                        String orderMappedBy = (String)annotationValues.get("mappedBy");
                        Column[] orderColumns = (Column[])annotationValues.get("columns");
                        ordermd = new OrderMetaData(orderColumn, null, orderMappedBy);
                        if (orderColumns != null && orderColumns.length > 0)
                        {
                            for (int j=0;j<orderColumns.length;j++)
                            {
                                ordermd.addColumn(JDOAnnotationUtils.getColumnMetaDataForColumn(ordermd, orderColumns[j]));
                            }
                        }
                        JDOAnnotationUtils.addExtensionsToMetaData(ordermd, (Extension[])annotationValues.get("extensions"));
                    }
                    else if (annName.equals(JDOAnnotationUtils.EMBEDDED))
                    {
                        embeddedOwnerField = (String)annotationValues.get("ownerMember");
                        embeddedNullIndicatorColumn = (String)annotationValues.get("nullIndicatorColumn");
                        embeddedNullIndicatorValue = (String)annotationValues.get("nullIndicatorValue");
                        embeddedMembers = (Persistent[])annotationValues.get("members");
                    }
                    else if (annName.equals(JDOAnnotationUtils.INDEX))
                    {
                        // Index for the field
                        String name = (String)annotationValues.get("name");
                        String table = (String)annotationValues.get("table");
                        String unique = (String)annotationValues.get("unique");
                        String[] members = (String[])annotationValues.get("members");
                        Column[] columns = (Column[])annotationValues.get("columns");

                        idxmd = JDOAnnotationUtils.getIndexMetaData(name, table, unique, members, columns);
                    }
                    else if (annName.equals(JDOAnnotationUtils.UNIQUE))
                    {
                        // Unique for the field
                        String name = (String)annotationValues.get("name");
                        String table = (String)annotationValues.get("table");
                        String deferred = (String)annotationValues.get("deferred");
                        String[] members = (String[])annotationValues.get("members");
                        Column[] columns = (Column[])annotationValues.get("columns");

                        unimd = JDOAnnotationUtils.getUniqueMetaData(name, table, deferred, members, columns);
                    }
                    else if (annName.equals(JDOAnnotationUtils.FOREIGNKEY))
                    {
                        // ForeignKey for field
                        String name = (String)annotationValues.get("name");
                        String table = (String)annotationValues.get("table");
                        String unique = (String)annotationValues.get("unique");
                        String deferred = (String)annotationValues.get("deferred");
                        String deleteAction = JDOAnnotationUtils.getForeignKeyActionString(
                            (ForeignKeyAction)annotationValues.get("deleteAction"));
                        String updateAction = JDOAnnotationUtils.getForeignKeyActionString(
                            (ForeignKeyAction)annotationValues.get("updateAction"));
                        String[] members = (String[])annotationValues.get("members");
                        Column[] columns = (Column[])annotationValues.get("columns");

                        fkmd = JDOAnnotationUtils.getFKMetaData(name, table, unique, deferred, deleteAction, updateAction,
                            members, columns);
                    }
                    else if (annName.equals(JDOAnnotationUtils.EXTENSIONS))
                    {
                        Extension[] values = (Extension[])annotationValues.get("value");
                        if (values != null && values.length > 0)
                        {
                            extensions = new HashSet<ExtensionMetaData>(values.length);
                            for (int j=0;j<values.length;j++)
                            {
                                ExtensionMetaData extmd = new ExtensionMetaData(values[j].vendorName(),
                                    values[j].key().toString(), values[j].value().toString());
                                extensions.add(extmd);
                            }
                        }
                    }
                    else if (annName.equals(JDOAnnotationUtils.EXTENSION))
                    {
                        ExtensionMetaData extmd = new ExtensionMetaData((String)annotationValues.get("vendorName"),
                            (String)annotationValues.get("key"), (String)annotationValues.get("value"));
                        extensions = new HashSet<ExtensionMetaData>(1);
                        extensions.add(extmd);
                    }
                    else
                    {
                        JPOXLogger.METADATA.error(LOCALISER.msg("MetaData.Annotations.AnnotationNotProcessed",
                            member.getName(), annotations[i].getName()));
                    }
                }
            }

            if (mmd == null &&
                (transactionalField || nonPersistentField || primaryKey || colmds != null || serialised ||
                 embeddedOwnerField != null || embeddedNullIndicatorColumn != null || embeddedNullIndicatorValue != null ||
                 embeddedMembers != null ||
                 elemmd != null || keymd != null || valuemd != null || ordermd != null ||
                 idxmd != null || unimd != null || fkmd != null))
            {
                // @Persistent not supplied but other relevant annotations defined, so add default metadata element
                if (member.isProperty())
                {
                    mmd = mgr.getMetaDataFactory().newPropertyObject(cmd, member.getName(),
                        "" + primaryKey, null, null, null, null, null, null, null, null, null, null,
                        null, null, null, null, null, null, null, null, null, null);
                }
                else
                {
                    mmd = mgr.getMetaDataFactory().newFieldObject(cmd, member.getName(),
                        "" + primaryKey, null, null, null, null, null, null, null, null, null, null,
                        null, null, null, null, null, null, null, null, null);
                }
            }

            if (mmd != null)
            {
                cmd.addMember(mmd);

                if (primaryKey)
                {
                    mmd.setPrimaryKey();
                }
                if (serialised)
                {
                    mmd.setSerialised();
                }
                if (nonPersistentField)
                {
                    mmd.setNotPersistent();
                }
                if (transactionalField)
                {
                    mmd.setTransactional();
                }

                // Add any embedded info
                if (embeddedOwnerField != null || embeddedNullIndicatorColumn != null ||
                    embeddedNullIndicatorValue != null || embeddedMembers != null)
                {
                    EmbeddedMetaData embmd = new EmbeddedMetaData(mmd, embeddedOwnerField,
                        embeddedNullIndicatorColumn, embeddedNullIndicatorValue);
                    mmd.setEmbeddedMetaData(embmd);
                    if (embeddedMembers != null && embeddedMembers.length > 0)
                    {
                        for (int j=0;j<embeddedMembers.length;j++)
                        {
                            // Add the metadata for the embedded field/property to the embedded metadata
                            String memberName = embeddedMembers[j].name();
                            if (memberName.indexOf('.') > 0)
                            {
                                memberName = memberName.substring(memberName.lastIndexOf('.')+1);
                            }
                            AbstractMemberMetaData embfmd = getFieldMetaDataForPersistent(embmd, embeddedMembers[j],
                                isMemberOfClassAField(member.getType(), memberName));
                            embmd.addMember(embfmd);
                        }
                    }
                }

                // If the field is a container then add its container element
                ContainerMetaData contmd = null;
                if (Collection.class.isAssignableFrom(member.getType()))
                {
                    Class collectionElementType = null;
                    StringBuffer elementTypeStr = new StringBuffer();
                    if (elementTypes != null && elementTypes.length > 0 && elementTypes[0] != void.class)
                    {
                        // User-specified element type(s)
                        for (int j=0;j<elementTypes.length;j++)
                        {
                            if (elementTypeStr.length() > 0)
                            {
                                elementTypeStr.append(',');
                            }
                            elementTypeStr.append(elementTypes[j].getName());
                        }
                        collectionElementType = elementTypes[0]; // Use the first only
                    }
                    else
                    {
                        // Try to derive element type from generics info
                        if (member.getGenericType() instanceof ParameterizedType)
                        {
                            ParameterizedType paramtype = (ParameterizedType)member.getGenericType();
                            if (paramtype.getActualTypeArguments().length == 1)
                            {
                                collectionElementType = ((Class)paramtype.getActualTypeArguments()[0]);
                                elementTypeStr.append(collectionElementType.getName());
                            }
                        }
                    }

                    contmd = new CollectionMetaData(mmd, elementTypeStr.toString(),
                        embeddedElement, dependentElement, serializedElement);

                    // Add any embedded element mappings
                    if (embeddedElementMembers != null)
                    {
                        EmbeddedMetaData embmd = elemmd.getEmbeddedMetaData();
                        for (int j=0;j<embeddedElementMembers.length;j++)
                        {
                            // Add the metadata for the embedded element to the embedded metadata
                            String memberName = embeddedElementMembers[j].name();
                            if (memberName.indexOf('.') > 0)
                            {
                                memberName = memberName.substring(memberName.lastIndexOf('.')+1);
                            }
                            AbstractMemberMetaData embfmd = getFieldMetaDataForPersistent(embmd, embeddedElementMembers[j],
                                isMemberOfClassAField(collectionElementType, memberName));
                            embmd.addMember(embfmd);
                        }
                    }
                }
                else if (member.getType().isArray())
                {
                    StringBuffer elementTypeStr = new StringBuffer();
                    if (elementTypes != null && elementTypes.length > 0 && elementTypes[0] != void.class)
                    {
                        // User-specified element type(s)
                        for (int j=0;j<elementTypes.length;j++)
                        {
                            if (elementTypeStr.length() > 0)
                            {
                                elementTypeStr.append(',');
                            }
                            elementTypeStr.append(elementTypes[j].getName());
                        }
                    }
                    else
                    {
                        // Derive from component type
                        elementTypeStr.append(member.getType().getComponentType().getName());
                    }
                    contmd = new ArrayMetaData(mmd, elementTypeStr.toString(),
                        embeddedElement, dependentElement, serializedElement);
                }
                else if (Map.class.isAssignableFrom(member.getType()))
                {
                    Class mapKeyType = null;
                    if (keyType != null && keyType != void.class)
                    {
                        // User-specified key type
                        mapKeyType = keyType;
                    }
                    else
                    {
                        // Try to derive key type from generics info
                        if (member.getGenericType() instanceof ParameterizedType)
                        {
                            ParameterizedType paramtype = (ParameterizedType)member.getGenericType();
                            if (paramtype.getActualTypeArguments().length == 2)
                            {
                                mapKeyType = ((Class)paramtype.getActualTypeArguments()[0]);
                            }
                        }
                    }

                    Class mapValueType = null;
                    if (valueType != null && valueType != void.class)
                    {
                        // User-specified value type
                        mapValueType = valueType;
                    }
                    else
                    {
                        // Try to derive value type from generics info
                        if (member.getGenericType() instanceof ParameterizedType)
                        {
                            ParameterizedType paramtype = (ParameterizedType)member.getGenericType();
                            if (paramtype.getActualTypeArguments().length == 2)
                            {
                                mapValueType = ((Class)paramtype.getActualTypeArguments()[1]);
                            }
                        }
                    }

                    contmd = new MapMetaData(mmd,
                        (mapKeyType != null ? mapKeyType.getName() : null), embeddedKey, dependentKey, serializedKey,
                        (mapValueType != null ? mapValueType.getName() : null), embeddedValue, dependentValue, serializedValue);

                    if (embeddedKeyMembers != null)
                    {
                        // Add any embedded key mappings
                        EmbeddedMetaData embmd = keymd.getEmbeddedMetaData();
                        for (int j=0;j<embeddedKeyMembers.length;j++)
                        {
                            // Add the metadata for the embedded key to the embedded metadata
                            String memberName = embeddedKeyMembers[j].name();
                            if (memberName.indexOf('.') > 0)
                            {
                                memberName = memberName.substring(memberName.lastIndexOf('.')+1);
                            }
                            AbstractMemberMetaData embfmd = getFieldMetaDataForPersistent(embmd, embeddedKeyMembers[j],
                                isMemberOfClassAField(mapKeyType, memberName));
                            embmd.addMember(embfmd);
                        }
                    }

                    if (embeddedValueMembers != null)
                    {
                        // Add any embedded value mappings
                        EmbeddedMetaData embmd = valuemd.getEmbeddedMetaData();
                        for (int j=0;j<embeddedValueMembers.length;j++)
                        {
                            // Add the metadata for the embedded value to the embedded metadata
                            String memberName = embeddedValueMembers[j].name();
                            if (memberName.indexOf('.') > 0)
                            {
                                memberName = memberName.substring(memberName.lastIndexOf('.')+1);
                            }
                            AbstractMemberMetaData embfmd = getFieldMetaDataForPersistent(embmd, embeddedValueMembers[j],
                                isMemberOfClassAField(mapValueType, memberName));
                            embmd.addMember(embfmd);
                        }
                    }
                }
                if (contmd != null)
                {
                    mmd.setContainer(contmd);
                   
                    if (elemmd != null)
                    {
                        elemmd.setParent(mmd);
                        mmd.setElementMetaData(elemmd);
                    }
                    if (keymd != null)
                    {
                        keymd.setParent(mmd);
                        mmd.setKeyMetaData(keymd);
                    }
                    if (valuemd != null)
                    {
                        valuemd.setParent(mmd);
                        mmd.setValueMetaData(valuemd);
                    }
                    if (ordermd != null)
                    {
                        ordermd.setParent(mmd);
                        mmd.setOrderMetaData(ordermd);
                    }
                }
                if (joinmd != null)
                {
                    joinmd.setParent(mmd);
                    mmd.setJoinMetaData(joinmd);
                }
                if (colmds != null)
                {
                    for (int i=0;i<colmds.length;i++)
                    {
                        mmd.addColumn(colmds[i]);
                    }
                }
                if (idxmd != null)
                {
                    mmd.setIndexMetaData(idxmd);
                }
                if (unimd != null)
                {
                    mmd.setUniqueMetaData(unimd);
                }
                if (fkmd != null)
                {
                    mmd.setForeignKeyMetaData(fkmd);
                }
                if (extensions != null)
                {
                    Iterator<ExtensionMetaData> iter = extensions.iterator();
                    while (iter.hasNext())
                    {
                        ExtensionMetaData extmd = iter.next();
                        mmd.addExtension(extmd.getVendorName(), extmd.getKey(), extmd.getValue());
                    }
                }
            }
        }

        return mmd;
    }

    /**
     * Method to take the passed in outline ClassMetaData and process the annotations for
     * method adding any necessary MetaData to the ClassMetaData.
     * @param cmd The ClassMetaData/InterfaceMetaData (to be updated)
     * @param method The method
     */
    protected void processMethodAnnotations(AbstractClassMetaData cmd, Method method)
    {
        //do nothing
    }

    /**
     * Convenience method to create MetaData for a @Persistent annotation
     * representing a field or property.
     * @param parent Parent MetaData
     * @param member The @Persistent annotation
     * @param isField Whether this is a field (otherwise is a property)
     * @return The metadata for the field/property
     */
    private AbstractMemberMetaData getFieldMetaDataForPersistent(MetaData parent, Persistent member, boolean isField)
    {
        String modifier = JDOAnnotationUtils.getFieldPersistenceModifierString(member.persistenceModifier());
        String nullValue = JDOAnnotationUtils.getNullValueString(member.nullValue());
        String valueStrategy = JDOAnnotationUtils.getIdentityStrategyString(member.valueStrategy());

        String fieldTypeName = null;
        Class[] fieldTypes = member.types();
        if (fieldTypes != null && fieldTypes.length > 0)
        {
            StringBuffer typeStr = new StringBuffer();
            for (int j=0;j<fieldTypes.length;j++)
            {
                if (typeStr.length() > 0)
                {
                    typeStr.append(',');
                }
                if (fieldTypes[j] != null && fieldTypes[j] != void.class)
                {
                    typeStr.append(fieldTypes[j].getName());
                }
            }
            fieldTypeName = typeStr.toString();
        }

        AbstractMemberMetaData fmd = null;
        if (isField)
        {
            fmd = mgr.getMetaDataFactory().newFieldObject(parent, member.name(),
                "" + member.primaryKey(), modifier, member.defaultFetchGroup(), nullValue,
                member.embedded(), member.serialized(), member.dependent(), member.mappedBy(),
                member.column(), member.table(), null, null, null, null, null, null, member.loadFetchGroup(),
                valueStrategy, member.sequence(), fieldTypeName);
        }
        else
        {
            fmd = mgr.getMetaDataFactory().newPropertyObject(parent, member.name(),
                "" + member.primaryKey(), modifier, member.defaultFetchGroup(), nullValue,
                member.embedded(), member.serialized(), member.dependent(), member.mappedBy(),
                member.column(), member.table(), null, null, null, null, null, null, member.loadFetchGroup(),
                valueStrategy, member.sequence(), fieldTypeName, null);
        }

        // Add any columns defined on the @Persistent
        Column[] columns = member.columns();
        if (columns != null && columns.length > 0)
        {
            for (int j=0;j<columns.length;j++)
            {
                fmd.addColumn(JDOAnnotationUtils.getColumnMetaDataForColumn(fmd, columns[j]));
            }
        }
        return fmd;
    }

    /**
     * Convenience method that tries to find if a specified member name (field or property) is for a field.
     * @param cls The class
     * @param memberName Name of the member
     * @return Whether it is a field (else it's a property).
     */
    private boolean isMemberOfClassAField(Class cls, String memberName)
    {
        try
        {
            cls.getDeclaredField(memberName);
        }
        catch (NoSuchFieldException nsfe)
        {
            return false;
        }
        // TODO It is possible that a memberName is a field AND a property. What do we do then ?
        return true;
    }
}
TOP

Related Classes of org.jpox.jdo.metadata.JDOAnnotationReader

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.