Package org.datanucleus.api.jdo.metadata

Source Code of org.datanucleus.api.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.datanucleus.api.jdo.metadata;

import java.lang.reflect.Method;
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.datanucleus.ClassLoaderResolver;
import org.datanucleus.exceptions.NucleusException;
import org.datanucleus.metadata.AbstractClassMetaData;
import org.datanucleus.metadata.AbstractMemberMetaData;
import org.datanucleus.metadata.ArrayMetaData;
import org.datanucleus.metadata.ClassMetaData;
import org.datanucleus.metadata.ClassPersistenceModifier;
import org.datanucleus.metadata.CollectionMetaData;
import org.datanucleus.metadata.ColumnMetaData;
import org.datanucleus.metadata.ContainerMetaData;
import org.datanucleus.metadata.DiscriminatorMetaData;
import org.datanucleus.metadata.ElementMetaData;
import org.datanucleus.metadata.EmbeddedMetaData;
import org.datanucleus.metadata.ExtensionMetaData;
import org.datanucleus.metadata.FetchGroupMetaData;
import org.datanucleus.metadata.FetchPlanMetaData;
import org.datanucleus.metadata.FieldMetaData;
import org.datanucleus.metadata.FieldPersistenceModifier;
import org.datanucleus.metadata.ForeignKeyMetaData;
import org.datanucleus.metadata.IdentityMetaData;
import org.datanucleus.metadata.IdentityStrategy;
import org.datanucleus.metadata.IdentityType;
import org.datanucleus.metadata.IndexMetaData;
import org.datanucleus.metadata.IndexedValue;
import org.datanucleus.metadata.InheritanceMetaData;
import org.datanucleus.metadata.InterfaceMetaData;
import org.datanucleus.metadata.JoinMetaData;
import org.datanucleus.metadata.KeyMetaData;
import org.datanucleus.metadata.MapMetaData;
import org.datanucleus.metadata.MetaData;
import org.datanucleus.metadata.MetaDataManager;
import org.datanucleus.metadata.OrderMetaData;
import org.datanucleus.metadata.PackageMetaData;
import org.datanucleus.metadata.PrimaryKeyMetaData;
import org.datanucleus.metadata.PropertyMetaData;
import org.datanucleus.metadata.QueryMetaData;
import org.datanucleus.metadata.SequenceMetaData;
import org.datanucleus.metadata.UniqueMetaData;
import org.datanucleus.metadata.ValueMetaData;
import org.datanucleus.metadata.VersionMetaData;
import org.datanucleus.metadata.annotations.AbstractAnnotationReader;
import org.datanucleus.metadata.annotations.AnnotationObject;
import org.datanucleus.metadata.annotations.Member;
import org.datanucleus.util.ClassUtils;
import org.datanucleus.util.NucleusLogger;
import org.datanucleus.util.StringUtils;

/**
* Implementation for Annotation Reader for java annotations using the JDO definition.
*/
public class JDOAnnotationReader extends AbstractAnnotationReader
{
    /**
     * Constructor.
     * @param mgr MetaData manager
     */
    public JDOAnnotationReader(MetaDataManager mgr)
    {
        super(mgr);

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

    /**
     * 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 NucleusException("No PackageMetaData specified to use for annotations");
        }

        AbstractClassMetaData cmd = null;

        if (annotations != null && annotations.length > 0)
        {
            // Process @PersistenceCapable/@PersistenceAware to determine if persistable in any way
            for (int i=0;i<annotations.length;i++)
            {
                String annName = annotations[i].getName();

                if (annName.equals(JDOAnnotationUtils.PERSISTENCE_CAPABLE))
                {
                    // PersistenceCapable class
                    HashMap<String, Object> annotationValues = annotations[i].getNameValueMap();

                    if (cmd != null && cmd.getPersistenceModifier() == ClassPersistenceModifier.PERSISTENCE_AWARE)
                    {
                        throw new NucleusException("Class " + cls.getName() +
                            " is annotated as @PersistenceAware yet it also has @PersistenceCapable!" +
                        " You cannot have both annotations!");
                    }

                    if (cls.isInterface())
                    {
                        cmd = new InterfaceMetaData(pmd, ClassUtils.getClassNameForClass(cls));
                    }
                    else
                    {
                        cmd = new ClassMetaData(pmd, ClassUtils.getClassNameForClass(cls));
                    }
                    cmd.setTable((String)annotationValues.get("table"));
                    cmd.setCatalog((String)annotationValues.get("catalog"));
                    cmd.setSchema((String)annotationValues.get("schema"));
                    cmd.setDetachable((String)annotationValues.get("detachable"));
                    cmd.setRequiresExtent((String)annotationValues.get("requiresExtent"));
                    String idClassName = null;
                    Class idClass = (Class)annotationValues.get("objectIdClass");
                    if (idClass != null && idClass != void.class)
                    {
                        idClassName = idClass.getName();
                    }
                    cmd.setObjectIdClass(idClassName);
                    cmd.setPersistenceModifier(ClassPersistenceModifier.PERSISTENCE_CAPABLE);
                    cmd.setEmbeddedOnly((String)annotationValues.get("embeddedOnly"));
                    javax.jdo.annotations.IdentityType idTypeVal = (javax.jdo.annotations.IdentityType)annotationValues.get("identityType");
                    String identityType = JDOAnnotationUtils.getIdentityTypeString(idTypeVal);
                    cmd.setIdentityType(IdentityType.getIdentityType(identityType));
                    cmd.setCacheable((String)annotationValues.get("cacheable"));
                    String serializeRead = (String)annotationValues.get("serializeRead");
                    if (serializeRead != null)
                    {
                        cmd.setSerializeRead(serializeRead.equals("true") ? true : false);
                    }

                    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
                    if (cmd != null && cmd.getPersistenceModifier() == ClassPersistenceModifier.PERSISTENCE_CAPABLE)
                    {
                        throw new NucleusException("Class " + cls.getName() +
                            " is annotated as @PersistenceCapable yet it also has @PersistenceAware!" +
                        " You cannot have both annotations!");
                    }
                    cmd = new ClassMetaData(pmd, ClassUtils.getClassNameForClass(cls));
                    cmd.setPersistenceModifier(ClassPersistenceModifier.PERSISTENCE_AWARE);
                }
            }

            if (cmd != null)
            {
                // Class is persistable so process remaining annotations
                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 cacheable = 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++)
                {
                    HashMap<String, Object> annotationValues = annotations[i].getNameValueMap();
                    String annName = annotations[i].getName();
                    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();
                        vermd.setStrategy(strategy);
                        vermd.setColumnName(column);
                        vermd.setIndexed(IndexedValue.getIndexedValue(indexed));
                        if (columns != null && columns.length > 0)
                        {
                            // Only use the first column
                            ColumnMetaData colmd =
                                JDOAnnotationUtils.getColumnMetaDataForColumnAnnotation(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 an 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();
                        idmd.setColumnName(column);
                        idmd.setValueStrategy(IdentityStrategy.getIdentityStrategy(strategy));
                        idmd.setSequence(sequence);
                        if (columns != null && columns.length > 0)
                        {
                            // Only use the first column
                            ColumnMetaData colmd =
                                JDOAnnotationUtils.getColumnMetaDataForColumnAnnotation(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();
                        pkmd.setName(pkName);
                        pkmd.setColumnName(pkColumn);
                        if (columns != null && columns.length > 0)
                        {
                            for (int j=0;j<columns.length;j++)
                            {
                                pkmd.addColumn(JDOAnnotationUtils.getColumnMetaDataForColumnAnnotation(columns[j]));
                            }
                        }
                    }
                    else if (annName.equals(JDOAnnotationUtils.JOINS))
                    {
                        if (joins != null)
                        {
                            NucleusLogger.METADATA.warn(LOCALISER.msg("044210", 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++)
                            {
                                joins[j] = new JoinMetaData();
                                joins[j].setTable(js[j].table());
                                joins[j].setColumnName(js[j].column());
                                joins[j].setIndexed(IndexedValue.getIndexedValue(js[j].indexed()));
                                joins[j].setOuter(js[j].outer());
                                joins[j].setUnique(js[j].unique());
                                joins[j].setDeleteAction(JDOAnnotationUtils.getForeignKeyActionString(js[j].deleteAction()));
                            }
                        }
                    }
                    else if (annName.equals(JDOAnnotationUtils.JOIN))
                    {
                        if (joins != null)
                        {
                            NucleusLogger.METADATA.warn(LOCALISER.msg("044210", cmd.getFullClassName()));
                        }
                        joins = new JoinMetaData[1];
                        joins[0] = new JoinMetaData();
                        joins[0].setTable((String)annotationValues.get("table"));
                        joins[0].setColumnName((String)annotationValues.get("column"));
                        joins[0].setIndexed(IndexedValue.getIndexedValue((String)annotationValues.get("indexed")));
                        joins[0].setOuter((String)annotationValues.get("outer"));
                        joins[0].setUnique((String)annotationValues.get("unique"));
                        joins[0].setDeleteAction(((ForeignKeyAction)annotationValues.get("deleteAction")).toString());
                        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 an extension strategy
                            strategy = customStrategy;
                        }
                        inhmd = new InheritanceMetaData();
                        inhmd.setStrategy(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();
                        dismd.setColumnName(column);
                        dismd.setValue(value);
                        dismd.setStrategy(strategy);
                        dismd.setIndexed(indexed);
                        if (columns != null && columns.length > 0)
                        {
                            // Only use the first column
                            ColumnMetaData colmd =
                                JDOAnnotationUtils.getColumnMetaDataForColumnAnnotation(columns[0]);
                            dismd.setColumnMetaData(colmd);
                        }
                    }
                    else if (annName.equals(JDOAnnotationUtils.QUERIES))
                    {
                        if (queries != null)
                        {
                            NucleusLogger.METADATA.warn(LOCALISER.msg("044209", 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(qs[j].name());
                            queries[j].setScope(cls.getName());
                            queries[j].setLanguage(lang);
                            queries[j].setUnmodifiable(qs[j].unmodifiable());
                            queries[j].setResultClass(resultClassName);
                            queries[j].setUnique(qs[j].unique());
                            queries[j].setFetchPlanName(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)
                        {
                            NucleusLogger.METADATA.warn(LOCALISER.msg("044209", 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((String)annotationValues.get("name"));
                        queries[0].setScope(cls.getName());
                        queries[0].setLanguage(lang);
                        queries[0].setUnmodifiable(unmodifiable);
                        queries[0].setResultClass(resultClassName);
                        queries[0].setUnique((String)annotationValues.get("unique"));
                        queries[0].setFetchPlanName((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)
                        {
                            NucleusLogger.METADATA.warn(LOCALISER.msg("044207", 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(plans[j].name());
                            fetchPlans[j].setMaxFetchDepth(plans[j].maxFetchDepth());
                            fetchPlans[j].setFetchSize(plans[j].fetchSize());
                            int numGroups = plans[j].fetchGroups().length;
                            for (int k=0;k<numGroups;k++)
                            {
                                FetchGroupMetaData fgmd = new FetchGroupMetaData(plans[j].fetchGroups()[k]);
                                fetchPlans[j].addFetchGroup(fgmd);
                            }
                        }
                    }
                    else if (annName.equals(JDOAnnotationUtils.FETCHPLAN))
                    {
                        if (fetchPlans != null)
                        {
                            NucleusLogger.METADATA.warn(LOCALISER.msg("044207", cmd.getFullClassName()));
                        }
                        fetchPlans = new FetchPlanMetaData[1];
                        int maxFetchDepth = ((Integer)annotationValues.get("maxFetchDepth")).intValue();
                        int fetchSize = ((Integer)annotationValues.get("fetchSize")).intValue();
                        fetchPlans[0] = new FetchPlanMetaData((String)annotationValues.get("name"));
                        fetchPlans[0].setMaxFetchDepth(maxFetchDepth);
                        fetchPlans[0].setFetchSize(fetchSize);
                    }
                    else if (annName.equals(JDOAnnotationUtils.FETCHGROUPS))
                    {
                        if (fetchGroups != null)
                        {
                            NucleusLogger.METADATA.warn(LOCALISER.msg("044208", 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(groups[j].name());
                            fetchGroups[j].setPostLoad(groups[j].postLoad());
                            int numFields = groups[j].members().length;
                            for (int k=0;k<numFields;k++)
                            {
                                FieldMetaData fmd = new FieldMetaData(fetchGroups[j],
                                    groups[j].members()[k].name());
                                fmd.setRecursionDepth(groups[j].members()[k].recursionDepth());
                                fetchGroups[j].addMember(fmd);
                            }
                            int numGroups = groups[j].fetchGroups().length;
                            for (int k=0;k<numGroups;k++)
                            {
                                FetchGroupMetaData subgrp = new FetchGroupMetaData(groups[j].fetchGroups()[k]);
                                fetchGroups[j].addFetchGroup(subgrp);
                            }
                        }
                    }
                    else if (annName.equals(JDOAnnotationUtils.FETCHGROUP))
                    {
                        if (fetchGroups != null)
                        {
                            NucleusLogger.METADATA.warn(LOCALISER.msg("044208", cmd.getFullClassName()));
                        }
                        fetchGroups = new FetchGroupMetaData[1];
                        fetchGroups[0] = new FetchGroupMetaData((String)annotationValues.get("name"));
                        fetchGroups[0].setPostLoad((String)annotationValues.get("postLoad"));
                        Persistent[] fields = (Persistent[])annotationValues.get("members");
                        if (fields != null)
                        {
                            for (int j=0;j<fields.length;j++)
                            {
                                FieldMetaData fmd = new FieldMetaData(fetchGroups[0],
                                    fields[j].name());
                                fmd.setRecursionDepth(fields[j].recursionDepth());
                                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();
                        }
                        Integer seqSize = (Integer)annotationValues.get("allocationSize");
                        Integer seqStart = (Integer)annotationValues.get("initialValue");

                        seqmd = new SequenceMetaData(seqName, seqStrategy);
                        seqmd.setFactoryClass(seqFactoryClassName);
                        seqmd.setDatastoreSequence(seqSeq);
                        if (seqSize != null)
                        {
                            seqmd.setAllocationSize(seqSize);
                        }
                        if (seqStart != null)
                        {
                            seqmd.setInitialValue(seqStart);
                        }
                        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)
                                {
                                    NucleusLogger.METADATA.warn(LOCALISER.msg("044204", 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)
                        {
                            NucleusLogger.METADATA.warn(LOCALISER.msg("044204", 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)
                                {
                                    NucleusLogger.METADATA.warn(LOCALISER.msg("044205", 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)
                        {
                            NucleusLogger.METADATA.warn(LOCALISER.msg("044205", 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].updateAction());
                                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)
                                {
                                    NucleusLogger.METADATA.warn(LOCALISER.msg("044206", 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)
                        {
                            NucleusLogger.METADATA.warn(LOCALISER.msg("044206", 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.getColumnMetaDataForColumnAnnotation(cols[j]);
                                JDOAnnotationUtils.addExtensionsToMetaData(unmappedColumns[j], cols[j].extensions());
                            }
                        }
                    }
                    else if (annName.equals(JDOAnnotationUtils.CACHEABLE))
                    {
                        String cache = (String)annotationValues.get("value");
                        if (cache != null)
                        {
                            cacheable = cache;
                        }
                    }
                    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
                    {
                        if (!annName.equals(JDOAnnotationUtils.PERSISTENCE_CAPABLE) &&
                                !annName.equals(JDOAnnotationUtils.PERSISTENCE_AWARE))
                        {
                            NucleusLogger.METADATA.error(LOCALISER.msg("044203", cls.getName(),
                                annotations[i].getName()));
                        }
                    }
                }

                // Either PersistenceCapable, PersistenceAware or PersistentInterface so build up the metadata
                NucleusLogger.METADATA.info(LOCALISER.msg("044200", cls.getName(), "JDO"));

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

                if (embeddedOnly)
                {
                    cmd.setEmbeddedOnly(true);
                }
                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);
                }
                else if (dismd != null)
                {
                    inhmd = new InheritanceMetaData();
                    inhmd.setDiscriminatorMetaData(dismd);
                    cmd.setInheritanceMetaData(inhmd);
                }

                if (joins != null && joins.length > 0)
                {
                    // Joins
                    for (int i=0;i<joins.length;i++)
                    {
                        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)
                    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)
                {
                    for (int i=0;i<unmappedColumns.length;i++)
                    {
                        ColumnMetaData colmd = unmappedColumns[i];
                        colmd.setParent(cmd);
                        cmd.addUnmappedColumn(colmd);
                    }
                }
                if (cacheable != null && cacheable.equalsIgnoreCase("false"))
                {
                    cmd.setCacheable(false);
                }
                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;
            String cacheable = null;

            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++)
            {
                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 an 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();
                    cacheable = (String)annotationValues.get("cacheable");
                    Class[] fieldTypes = (Class[])annotationValues.get("types");
                    if (fieldTypes != null && fieldTypes.length > 0)
                    {
                        StringBuilder typeStr = new StringBuilder();
                        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 = new PropertyMetaData(cmd, member.getName());
                    }
                    else
                    {
                        // Field
                        mmd = new FieldMetaData(cmd, member.getName());
                    }
                    mmd.setPersistenceModifier(modifier);
                    mmd.setDefaultFetchGroup(dfg);
                    mmd.setPrimaryKey(pk);
                    mmd.setEmbedded(embedded);
                    mmd.setSerialised(serialized);
                    mmd.setDependent(dependent);
                    mmd.setNullValue(org.datanucleus.metadata.NullValue.getNullValue(nullValue));
                    mmd.setMappedBy(mappedBy);
                    mmd.setColumn(column);
                    mmd.setTable(table);
                    mmd.setRecursionDepth(recursionDepth);
                    mmd.setLoadFetchGroup(loadFetchGroup);
                    mmd.setValueStrategy(valueStrategy);
                    mmd.setSequence(sequence);
                    mmd.setFieldTypes(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.getColumnMetaDataForColumnAnnotation(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.getColumnMetaDataForColumnAnnotation(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(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();
                    joinmd.setColumnName(joinColumn);
                    joinmd.setOuter(joinOuter);
                    joinmd.setIndexed(IndexedValue.getIndexedValue(indexed));
                    joinmd.setUnique(unique);
                    joinmd.setDeleteAction(deleteAction);

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

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

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

                    if (!StringUtils.isWhitespace(uniqueName))
                    {
                        UniqueMetaData joinUnimd = joinmd.getUniqueMetaData();
                        if (joinUnimd == null)
                        {
                            joinUnimd = new UniqueMetaData();
                            joinmd.setUniqueMetaData(joinUnimd);
                        }
                        joinUnimd.setName(uniqueName);
                    }
                    if (joinColumns != null && joinColumns.length > 0)
                    {
                        for (int j=0;j<joinColumns.length;j++)
                        {
                            joinmd.addColumn(JDOAnnotationUtils.getColumnMetaDataForColumnAnnotation(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();
                    elemmd.setColumnName(elementColumn);
                    elemmd.setDeleteAction(elementDeleteAction);
                    elemmd.setUpdateAction(elementUpdateAction);
                    elemmd.setIndexed(IndexedValue.getIndexedValue(indexed));
                    elemmd.setUnique(unique);
                    elemmd.setMappedBy(elementMappedBy);
                    if (!StringUtils.isWhitespace(fkName))
                    {
                        ForeignKeyMetaData elemFkmd = elemmd.getForeignKeyMetaData();
                        if (elemFkmd == null)
                        {
                            elemFkmd = new ForeignKeyMetaData();
                            elemFkmd.setName(fkName);
                            elemmd.setForeignKeyMetaData(elemFkmd);
                        }
                        else
                        {
                            elemFkmd.setName(fkName);
                        }
                    }
                    else if (generateFK != null && generateFK.equalsIgnoreCase("true"))
                    {
                        elemmd.setForeignKeyMetaData(new ForeignKeyMetaData());
                    }

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

                    if (!StringUtils.isWhitespace(uniqueName))
                    {
                        UniqueMetaData elemUnimd = elemmd.getUniqueMetaData();
                        if (elemUnimd == null)
                        {
                            elemUnimd = new UniqueMetaData();
                            elemmd.setUniqueMetaData(elemUnimd);
                        }
                        elemUnimd.setName(uniqueName);
                    }
                    if (elementColumns != null && elementColumns.length > 0)
                    {
                        for (int j=0;j<elementColumns.length;j++)
                        {
                            elemmd.addColumn(JDOAnnotationUtils.getColumnMetaDataForColumnAnnotation(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();
                        embmd.setOwnerMember(embeddedMappings[0].ownerMember());
                        embmd.setNullIndicatorColumn(embeddedMappings[0].nullIndicatorColumn());
                        embmd.setNullIndicatorValue(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();
                    keymd.setColumnName(keyColumn);
                    keymd.setDeleteAction(keyDeleteAction);
                    keymd.setUpdateAction(keyUpdateAction);
                    keymd.setIndexed(IndexedValue.getIndexedValue(indexed));
                    keymd.setUnique(unique);
                    keymd.setMappedBy(keyMappedBy);
                    if (!StringUtils.isWhitespace(fkName))
                    {
                        ForeignKeyMetaData keyFkmd = keymd.getForeignKeyMetaData();
                        if (keyFkmd == null)
                        {
                            keyFkmd = new ForeignKeyMetaData();
                            keyFkmd.setName(fkName);
                            keymd.setForeignKeyMetaData(keyFkmd);
                        }
                        else
                        {
                            keyFkmd.setName(fkName);
                        }
                    }
                    else if (generateFK != null && generateFK.equalsIgnoreCase("true"))
                    {
                        keymd.setForeignKeyMetaData(new ForeignKeyMetaData());
                    }

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

                    if (!StringUtils.isWhitespace(uniqueName))
                    {
                        UniqueMetaData keyUnimd = keymd.getUniqueMetaData();
                        if (keyUnimd == null)
                        {
                            keyUnimd = new UniqueMetaData();
                            keymd.setUniqueMetaData(keyUnimd);
                        }
                        keyUnimd.setName(uniqueName);
                    }
                    if (keyColumns != null && keyColumns.length > 0)
                    {
                        for (int j=0;j<keyColumns.length;j++)
                        {
                            keymd.addColumn(JDOAnnotationUtils.getColumnMetaDataForColumnAnnotation(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();
                        embmd.setOwnerMember(embeddedMappings[0].ownerMember());
                        embmd.setNullIndicatorColumn(embeddedMappings[0].nullIndicatorColumn());
                        embmd.setNullIndicatorValue(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();
                    valuemd.setColumnName(valueColumn);
                    valuemd.setDeleteAction(valueDeleteAction);
                    valuemd.setUpdateAction(valueUpdateAction);
                    valuemd.setIndexed(IndexedValue.getIndexedValue(indexed));
                    valuemd.setUnique(unique);
                    valuemd.setMappedBy(valueMappedBy);
                    if (!StringUtils.isWhitespace(fkName))
                    {
                        ForeignKeyMetaData valueFkmd = valuemd.getForeignKeyMetaData();
                        if (valueFkmd == null)
                        {
                            valueFkmd = new ForeignKeyMetaData();
                            valueFkmd.setName(fkName);
                            valuemd.setForeignKeyMetaData(valueFkmd);
                        }
                        else
                        {
                            valueFkmd.setName(fkName);
                        }
                    }
                    else if (generateFK != null && generateFK.equalsIgnoreCase("true"))
                    {
                        valuemd.setForeignKeyMetaData(new ForeignKeyMetaData());
                    }

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

                    if (!StringUtils.isWhitespace(uniqueName))
                    {
                        UniqueMetaData valueUnimd = valuemd.getUniqueMetaData();
                        if (valueUnimd == null)
                        {
                            valueUnimd = new UniqueMetaData();
                            valuemd.setUniqueMetaData(valueUnimd);
                        }
                        valueUnimd.setName(uniqueName);
                    }
                    if (valueColumns != null && valueColumns.length > 0)
                    {
                        for (int j=0;j<valueColumns.length;j++)
                        {
                            valuemd.addColumn(JDOAnnotationUtils.getColumnMetaDataForColumnAnnotation(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();
                        embmd.setOwnerMember(embeddedMappings[0].ownerMember());
                        embmd.setNullIndicatorColumn(embeddedMappings[0].nullIndicatorColumn());
                        embmd.setNullIndicatorValue(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();
                    ordermd.setColumnName(orderColumn);
                    ordermd.setMappedBy(orderMappedBy);
                    if (orderColumns != null && orderColumns.length > 0)
                    {
                        for (int j=0;j<orderColumns.length;j++)
                        {
                            ordermd.addColumn(JDOAnnotationUtils.getColumnMetaDataForColumnAnnotation(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.CACHEABLE))
                {
                    String cache = (String)annotationValues.get("value");
                    if (cache != null)
                    {
                        cacheable = cache;
                    }
                }
                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
                {
                    NucleusLogger.METADATA.error(LOCALISER.msg("044203",
                        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 || joinmd != null ||
                 extensions != null))
            {
                // @Persistent not supplied but other relevant annotations defined, so add default metadata element
                if (member.isProperty())
                {
                    mmd = new PropertyMetaData(cmd, member.getName());
                }
                else
                {
                    mmd = new FieldMetaData(cmd, member.getName());
                }
                if (primaryKey)
                {
                    mmd.setPersistenceModifier(FieldPersistenceModifier.PERSISTENT.toString());
                    mmd.setPrimaryKey("" + primaryKey);
                }
                if (serialised)
                {
                    mmd.setPersistenceModifier(FieldPersistenceModifier.PERSISTENT.toString());
                }
            }

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

                if (primaryKey)
                {
                    mmd.setPrimaryKey(true);
                }
                if (serialised)
                {
                    mmd.setSerialised(true);
                }
                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();
                    embmd.setOwnerMember(embeddedOwnerField);
                    embmd.setNullIndicatorColumn(embeddedNullIndicatorColumn);
                    embmd.setNullIndicatorValue(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;
                    StringBuilder elementTypeStr = new StringBuilder();
                    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
                        collectionElementType =
                            ClassUtils.getCollectionElementType(member.getType(), member.getGenericType());
                    }

                    contmd = new CollectionMetaData();
                    CollectionMetaData collmd = (CollectionMetaData)contmd;
                    collmd.setElementType(elementTypeStr.toString());
                    collmd.setEmbeddedElement(embeddedElement);
                    collmd.setSerializedElement(serializedElement);
                    collmd.setDependentElement(dependentElement);

                    // 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())
                {
                    StringBuilder elementTypeStr = new StringBuilder();
                    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();
                    ArrayMetaData arrmd = (ArrayMetaData)contmd;
                    arrmd.setElementType(elementTypeStr.toString());
                    arrmd.setEmbeddedElement(embeddedElement);
                    arrmd.setSerializedElement(serializedElement);
                    arrmd.setDependentElement(dependentElement);
                }
                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
                        mapKeyType = ClassUtils.getMapKeyType(member.getType(), member.getGenericType());
                    }

                    Class mapValueType = null;
                    if (valueType != null && valueType != void.class)
                    {
                        // User-specified value type
                        mapValueType = valueType;
                    }
                    else
                    {
                        // Try to derive value type from generics info
                        mapValueType = ClassUtils.getMapValueType(member.getType(), member.getGenericType());
                    }

                    contmd = new MapMetaData();
                    MapMetaData mapmd = (MapMetaData)contmd;
                    mapmd.setKeyType((mapKeyType != null ? mapKeyType.getName() : null));
                    mapmd.setEmbeddedKey(embeddedKey);
                    mapmd.setSerializedKey(serializedKey);
                    mapmd.setDependentKey(dependentKey);
                    mapmd.setValueType((mapValueType != null ? mapValueType.getName() : null));
                    mapmd.setEmbeddedValue(embeddedValue);
                    mapmd.setSerializedValue(serializedValue);
                    mapmd.setDependentValue(dependentValue);

                    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 (elemmd.getMappedBy() != null && mmd.getMappedBy() == null)
                        {
                            // With collection/array this is the same as mapped-by on the field
                            mmd.setMappedBy(elemmd.getMappedBy());
                        }
                    }
                    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)
                {
                    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 (cacheable != null && cacheable.equalsIgnoreCase("false"))
                {
                    mmd.setCacheable(false);
                }
                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)
        {
            StringBuilder typeStr = new StringBuilder();
            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 = new FieldMetaData(parent, member.name());
        }
        else
        {
            fmd = new PropertyMetaData(parent, member.name());
        }
        fmd.setPersistenceModifier(modifier);
        fmd.setDefaultFetchGroup(member.defaultFetchGroup());
        fmd.setPrimaryKey("" + member.primaryKey());
        fmd.setEmbedded(member.embedded());
        fmd.setSerialised(member.serialized());
        fmd.setDependent(member.dependent());
        fmd.setNullValue(org.datanucleus.metadata.NullValue.getNullValue(nullValue));
        fmd.setMappedBy(member.mappedBy());
        fmd.setColumn(member.column());
        fmd.setTable(member.table());
        fmd.setLoadFetchGroup(member.loadFetchGroup());
        fmd.setValueStrategy(valueStrategy);
        fmd.setSequence(member.sequence());
        fmd.setFieldTypes(fieldTypeName);

        // 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.getColumnMetaDataForColumnAnnotation(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.datanucleus.api.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.