All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.apache.openjpa.persistence.AnnotationPersistenceMetaDataParser Maven / Gradle / Ivy

/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.    
 */
package org.apache.openjpa.persistence;

import java.io.File;
import java.io.Serializable;
import java.lang.annotation.Annotation;
import java.lang.reflect.AnnotatedElement;
import java.lang.reflect.Field;
import java.lang.reflect.Member;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.net.URISyntaxException;
import java.net.URL;
import java.security.AccessController;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.TreeSet;
import javax.persistence.Basic;
import javax.persistence.CascadeType;
import javax.persistence.Embeddable;
import javax.persistence.Embedded;
import javax.persistence.EmbeddedId;
import javax.persistence.Entity;
import javax.persistence.EntityListeners;
import javax.persistence.ExcludeDefaultListeners;
import javax.persistence.ExcludeSuperclassListeners;
import javax.persistence.FetchType;
import javax.persistence.FlushModeType;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import static javax.persistence.GenerationType.AUTO;
import javax.persistence.Id;
import javax.persistence.IdClass;
import javax.persistence.Lob;
import javax.persistence.ManyToMany;
import javax.persistence.ManyToOne;
import javax.persistence.MapKey;
import javax.persistence.MappedSuperclass;
import javax.persistence.NamedNativeQueries;
import javax.persistence.NamedNativeQuery;
import javax.persistence.NamedQueries;
import javax.persistence.NamedQuery;
import javax.persistence.OneToMany;
import javax.persistence.OneToOne;
import javax.persistence.OrderBy;
import javax.persistence.PostLoad;
import javax.persistence.PostPersist;
import javax.persistence.PostRemove;
import javax.persistence.PostUpdate;
import javax.persistence.PrePersist;
import javax.persistence.PreRemove;
import javax.persistence.PreUpdate;
import javax.persistence.QueryHint;
import javax.persistence.SequenceGenerator;
import javax.persistence.Version;

import org.apache.commons.lang.StringUtils;
import org.apache.openjpa.conf.OpenJPAConfiguration;
import org.apache.openjpa.event.BeanLifecycleCallbacks;
import org.apache.openjpa.event.LifecycleCallbacks;
import org.apache.openjpa.event.LifecycleEvent;
import org.apache.openjpa.event.MethodLifecycleCallbacks;
import org.apache.openjpa.kernel.QueryLanguages;
import org.apache.openjpa.kernel.jpql.JPQLParser;
import org.apache.openjpa.lib.conf.Configurations;
import org.apache.openjpa.lib.log.Log;
import org.apache.openjpa.lib.util.J2DoPriv5Helper;
import org.apache.openjpa.lib.util.Localizer;
import org.apache.openjpa.meta.ClassMetaData;
import org.apache.openjpa.meta.DelegatingMetaDataFactory;
import org.apache.openjpa.meta.FieldMetaData;
import org.apache.openjpa.meta.JavaTypes;
import org.apache.openjpa.meta.LifecycleMetaData;
import org.apache.openjpa.meta.MetaDataFactory;
import org.apache.openjpa.meta.MetaDataModes;
import org.apache.openjpa.meta.MetaDataRepository;
import org.apache.openjpa.meta.Order;
import org.apache.openjpa.meta.QueryMetaData;
import org.apache.openjpa.meta.SequenceMetaData;
import org.apache.openjpa.meta.UpdateStrategies;
import org.apache.openjpa.meta.ValueMetaData;
import org.apache.openjpa.meta.ValueStrategies;
import org.apache.openjpa.meta.MetaDataDefaults;
import static org.apache.openjpa.persistence.MetaDataTag.*;
import org.apache.openjpa.util.ImplHelper;
import org.apache.openjpa.util.InternalException;
import org.apache.openjpa.util.MetaDataException;
import org.apache.openjpa.util.UnsupportedException;
import org.apache.openjpa.util.UserException;

import serp.util.Numbers;
import serp.util.Strings;

/**
 * Persistence annotation metadata parser. Currently does not parse
 * deployment descriptors.
 *
 * @author Abe White
 * @author Steve Kim
 * @nojavadoc
 */
public class AnnotationPersistenceMetaDataParser
    implements MetaDataModes {

    private static final Localizer _loc = Localizer.forPackage
        (AnnotationPersistenceMetaDataParser.class);

    private static final Map _tags =
        new HashMap();

    static {
        _tags.put(EmbeddedId.class, EMBEDDED_ID);
        _tags.put(EntityListeners.class, ENTITY_LISTENERS);
        _tags.put(ExcludeDefaultListeners.class, EXCLUDE_DEFAULT_LISTENERS);
        _tags.put(ExcludeSuperclassListeners.class,
            EXCLUDE_SUPERCLASS_LISTENERS);
        _tags.put(FlushModeType.class, FLUSH_MODE);
        _tags.put(GeneratedValue.class, GENERATED_VALUE);
        _tags.put(Id.class, ID);
        _tags.put(IdClass.class, ID_CLASS);
        _tags.put(MapKey.class, MAP_KEY);
        _tags.put(NamedNativeQueries.class, NATIVE_QUERIES);
        _tags.put(NamedNativeQuery.class, NATIVE_QUERY);
        _tags.put(NamedQueries.class, QUERIES);
        _tags.put(NamedQuery.class, QUERY);
        _tags.put(OrderBy.class, ORDER_BY);
        _tags.put(PostLoad.class, POST_LOAD);
        _tags.put(PostPersist.class, POST_PERSIST);
        _tags.put(PostRemove.class, POST_REMOVE);
        _tags.put(PostUpdate.class, POST_UPDATE);
        _tags.put(PrePersist.class, PRE_PERSIST);
        _tags.put(PreRemove.class, PRE_REMOVE);
        _tags.put(PreUpdate.class, PRE_UPDATE);
        _tags.put(SequenceGenerator.class, SEQ_GENERATOR);
        _tags.put(Version.class, VERSION);
        _tags.put(DataCache.class, DATA_CACHE);
        _tags.put(DataStoreId.class, DATASTORE_ID);
        _tags.put(Dependent.class, DEPENDENT);
        _tags.put(DetachedState.class, DETACHED_STATE);
        _tags.put(ElementDependent.class, ELEM_DEPENDENT);
        _tags.put(ElementType.class, ELEM_TYPE);
        _tags.put(ExternalValues.class, EXTERNAL_VALS);
        _tags.put(Externalizer.class, EXTERNALIZER);
        _tags.put(Factory.class, FACTORY);
        _tags.put(FetchGroup.class, FETCH_GROUP);
        _tags.put(FetchGroups.class, FETCH_GROUPS);
        _tags.put(InverseLogical.class, INVERSE_LOGICAL);
        _tags.put(KeyDependent.class, KEY_DEPENDENT);
        _tags.put(KeyType.class, KEY_TYPE);
        _tags.put(LoadFetchGroup.class, LOAD_FETCH_GROUP);
        _tags.put(LRS.class, LRS);
        _tags.put(ManagedInterface.class, MANAGED_INTERFACE);
        _tags.put(ReadOnly.class, READ_ONLY);
        _tags.put(Type.class, TYPE);
    }

    private final OpenJPAConfiguration _conf;
    private final Log _log;
    private MetaDataRepository _repos = null;
    private ClassLoader _envLoader = null;
    private boolean _override = false;
    private int _mode = MODE_NONE;

    // packages and their parse modes
    private final Map _pkgs = new HashMap();

    // the class we were invoked to parse
    private Class _cls = null;
    private File _file = null;

    /**
     * Constructor; supply configuration.
     */
    public AnnotationPersistenceMetaDataParser(OpenJPAConfiguration conf) {
        _conf = conf;
        _log = conf.getLog(OpenJPAConfiguration.LOG_METADATA);
    }

    /**
     * Configuration supplied on construction.
     */
    public OpenJPAConfiguration getConfiguration() {
        return _conf;
    }

    /**
     * Metadata log.
     */
    public Log getLog() {
        return _log;
    }

    /**
     * Returns the repository for this parser. If none has been set,
     * create a new repository and sets it.
     */
    public MetaDataRepository getRepository() {
        if (_repos == null) {
            MetaDataRepository repos = _conf.newMetaDataRepositoryInstance();
            MetaDataFactory mdf = repos.getMetaDataFactory();
            if (mdf instanceof DelegatingMetaDataFactory)
                mdf = ((DelegatingMetaDataFactory) mdf).getInnermostDelegate();
            if (mdf instanceof PersistenceMetaDataFactory)
                ((PersistenceMetaDataFactory) mdf).setAnnotationParser(this);
            _repos = repos;
        }
        return _repos;
    }

    /**
     * Set the metadata repository for this parser.
     */
    public void setRepository(MetaDataRepository repos) {
        _repos = repos;
    }

    /**
     * Return the environmental class loader to pass on to parsed
     * metadata instances.
     */
    public ClassLoader getEnvClassLoader() {
        return _envLoader;
    }

    /**
     * Set the environmental class loader to pass on to parsed
     * metadata instances.
     */
    public void setEnvClassLoader(ClassLoader loader) {
        _envLoader = loader;
    }

    /**
     * Whether to allow later parses of mapping information to override
     * earlier information for the same class. Defaults to false. Useful
     * when a tool is mapping a class, so that annotation partial mapping
     * information can be used even when mappings are stored in another
     * location.
     */
    public boolean getMappingOverride() {
        return _override;
    }

    /**
     * Whether to allow later parses of mapping information to override
     * earlier information for the same class. Defaults to false. Useful
     * when a tool is mapping a class, so that annotation partial mapping
     * information can be used even when mappings are stored in another
     * location.
     */
    public void setMappingOverride(boolean override) {
        _override = override;
    }

    /**
     * The parse mode.
     */
    public int getMode() {
        return _mode;
    }

    /**
     * The parse mode.
     */
    public void setMode(int mode, boolean on) {
        if (mode == MODE_NONE)
            _mode = MODE_NONE;
        else if (on)
            _mode |= mode;
        else
            _mode &= ~mode;
    }

    /**
     * The parse mode.
     */
    public void setMode(int mode) {
        _mode = mode;
    }

    /**
     * Convenience method for interpreting {@link #getMode}.
     */
    protected boolean isMetaDataMode() {
        return (_mode & MODE_META) != 0;
    }

    /**
     * Convenience method for interpreting {@link #getMode}.
     */
    protected boolean isQueryMode() {
        return (_mode & MODE_QUERY) != 0;
    }

    /**
     * Convenience method for interpreting {@link #getMode}.
     */
    protected boolean isMappingMode() {
        return (_mode & MODE_MAPPING) != 0;
    }

    /**
     * Returns true if we're in mapping mode or in metadata mode with
     * mapping overide enabled.
     */
    protected boolean isMappingOverrideMode() {
        return isMappingMode() || (_override && isMetaDataMode());
    }

    /**
     * Clear caches.
     */
    public void clear() {
        _cls = null;
        _file = null;
        _pkgs.clear();
    }

    /**
     * Parse persistence metadata for the given class.
     */
    public void parse(Class cls) {
        if (_log.isTraceEnabled())
            _log.trace(_loc.get("parse-class", cls.getName()));

        _cls = cls;
        try {
            parsePackageAnnotations();
            ClassMetaData meta = parseClassAnnotations();
            updateSourceMode(meta);
        } finally {
            _cls = null;
            _file = null;
        }
    }

    /**
     * Update the source mode to the class package and class to indicate that
     * we've fully parsed them.
     */
    private void updateSourceMode(ClassMetaData meta) {
        if (_cls.getPackage() != null)
            addSourceMode(_cls.getPackage(), _mode);
        if (meta != null)
            meta.setSourceMode(_mode, true);
    }

    /**
     * Parse information in package-level class annotations.
     */
    private void parsePackageAnnotations() {
        Package pkg = _cls.getPackage();
        if (pkg == null)
            return;

        int pkgMode = getSourceMode(pkg);
        if (pkgMode == 0 && _log.isTraceEnabled())
            _log.trace(_loc.get("parse-package", _cls.getName()));
        if ((pkgMode & _mode) == _mode) // already visited
            return;

        MetaDataTag tag;
        for (Annotation anno : pkg.getDeclaredAnnotations()) {
            tag = _tags.get(anno.annotationType());
            if (tag == null) {
                handleUnknownPackageAnnotation(pkg, anno);
                continue;
            }

            switch (tag) {
                case NATIVE_QUERIES:
                    if (isQueryMode() && (pkgMode & MODE_QUERY) == 0)
                        parseNamedNativeQueries(pkg,
                            ((NamedNativeQueries) anno).value());
                    break;
                case NATIVE_QUERY:
                    if (isQueryMode() && (pkgMode & MODE_QUERY) == 0)
                        parseNamedNativeQueries(pkg, (NamedNativeQuery) anno);
                    break;
                case QUERIES:
                    if (isQueryMode() && (pkgMode & MODE_QUERY) == 0)
                        parseNamedQueries(pkg, ((NamedQueries) anno).value());
                    break;
                case QUERY:
                    if (isQueryMode() && (pkgMode & MODE_QUERY) == 0)
                        parseNamedQueries(pkg, (NamedQuery) anno);
                    break;
                case SEQ_GENERATOR:
                    if (isMappingOverrideMode() &&
                        (pkgMode & MODE_MAPPING) == 0)
                        parseSequenceGenerator(pkg, (SequenceGenerator) anno);
                    break;
                default:
                    throw new UnsupportedException(_loc.get("unsupported", pkg,
                        anno.toString()));
            }
        }

        // always parse mapping stuff after metadata stuff, in case there are
        // dependencies on metadata
        if (isMappingOverrideMode() && (pkgMode & MODE_MAPPING) == 0)
            parsePackageMappingAnnotations(pkg);
    }

    /**
     * Parse package mapping annotations.
     */
    protected void parsePackageMappingAnnotations(Package pkg) {
    }

    /**
     * Allow subclasses to handle unknown annotations.
     */
    protected boolean handleUnknownPackageAnnotation(Package pkg,
        Annotation anno) {
        return false;
    }

    /**
     * The source mode for the given package.
     */
    private int getSourceMode(Package pkg) {
        Number num = _pkgs.get(pkg);
        return (num == null) ? 0 : num.intValue();
    }

    /**
     * Add to the source mode for the given package.
     */
    private void addSourceMode(Package pkg, int mode) {
        Integer num = _pkgs.get(pkg);
        if (num == null)
            num = Numbers.valueOf(mode);
        else
            num = Numbers.valueOf(num.intValue() | mode);
        _pkgs.put(pkg, num);
    }

    /**
     * Read annotations for the current type.
     */
    private ClassMetaData parseClassAnnotations() {
        // check immediately whether the user is using any annotations,
        // regardless of mode.  this prevents adding non-entity classes to
        // repository if we're ignoring these annotations in mapping mode
        if (!((Boolean) AccessController.doPrivileged(J2DoPriv5Helper
            .isAnnotationPresentAction(_cls, Entity.class))).booleanValue()
            && !((Boolean) AccessController.doPrivileged(J2DoPriv5Helper
                .isAnnotationPresentAction(_cls, Embeddable.class)))
                .booleanValue()
            && !((Boolean) AccessController.doPrivileged(J2DoPriv5Helper
                .isAnnotationPresentAction(_cls, MappedSuperclass.class)))
                .booleanValue())
            return null;

        // find / create metadata
        ClassMetaData meta = getMetaData();
        if (meta == null)
            return null;

        Entity entity = (Entity) _cls.getAnnotation(Entity.class);
        MappedSuperclass mapped = (MappedSuperclass)
        _cls.getAnnotation(MappedSuperclass.class);
        if (isMetaDataMode()) {
            meta.setAbstract(mapped != null);
            // while the spec only provides for embedded exclusive, it doesn't
            // seem hard to support otherwise
            if (entity == null)
                meta.setEmbeddedOnly(true);
            else {
                meta.setEmbeddedOnly(false);
                if (!StringUtils.isEmpty(entity.name()))
                    meta.setTypeAlias(entity.name());
            }
        }

        // track fetch groups to parse them after fields, since they
        // rely on field metadata
        FetchGroup[] fgs = null;
        DetachedState detached = null;

        // track listeners since we need to merge them with entity callbacks
        Collection[] listeners = null;
        MetaDataTag tag;
        for (Annotation anno : _cls.getDeclaredAnnotations()) {
            tag = _tags.get(anno.annotationType());
            if (tag == null) {
                handleUnknownClassAnnotation(meta, anno);
                continue;
            }

            switch (tag) {
                case ENTITY_LISTENERS:
                    if (isMetaDataMode())
                        listeners = parseEntityListeners(meta,
                            (EntityListeners) anno);
                    break;
                case EXCLUDE_DEFAULT_LISTENERS:
                    if (isMetaDataMode())
                        meta.getLifecycleMetaData()
                            .setIgnoreSystemListeners(true);
                    break;
                case EXCLUDE_SUPERCLASS_LISTENERS:
                    if (isMetaDataMode())
                        meta.getLifecycleMetaData().setIgnoreSuperclassCallbacks
                            (LifecycleMetaData.IGNORE_HIGH);
                    break;
                case FLUSH_MODE:
                    if (isMetaDataMode())
                        warnFlushMode(meta);
                    break;
                case ID_CLASS:
                    if (isMetaDataMode())
                        meta.setObjectIdType(((IdClass) anno).value(), true);
                    break;
                case NATIVE_QUERIES:
                    if (isQueryMode() && (meta.getSourceMode() & MODE_QUERY) == 0)
                        parseNamedNativeQueries(_cls,
                            ((NamedNativeQueries) anno).value());
                    break;
                case NATIVE_QUERY:
                    if (isQueryMode() && (meta.getSourceMode() & MODE_QUERY) == 0)
                        parseNamedNativeQueries(_cls, (NamedNativeQuery) anno);
                    break;
                case QUERIES:
                    if (isQueryMode() && (meta.getSourceMode() & MODE_QUERY) == 0)
                        parseNamedQueries(_cls, ((NamedQueries) anno).value());
                    break;
                case QUERY:
                    if (isQueryMode() && (meta.getSourceMode() & MODE_QUERY) == 0)
                        parseNamedQueries(_cls, (NamedQuery) anno);
                    break;
                case SEQ_GENERATOR:
                    if (isMappingOverrideMode())
                        parseSequenceGenerator(_cls, (SequenceGenerator) anno);
                    break;
                case DATA_CACHE:
                    if (isMetaDataMode())
                        parseDataCache(meta, (DataCache) anno);
                    break;
                case DATASTORE_ID:
                    if (isMetaDataMode())
                        parseDataStoreId(meta, (DataStoreId) anno);
                    break;
                case DETACHED_STATE:
                    detached = (DetachedState) anno;
                    break;
                case FETCH_GROUP:
                    if (isMetaDataMode())
                        fgs = new FetchGroup[]{ (FetchGroup) anno };
                    break;
                case FETCH_GROUPS:
                    if (isMetaDataMode())
                        fgs = ((FetchGroups) anno).value();
                    break;
                case MANAGED_INTERFACE:
                    if (isMetaDataMode())
                        parseManagedInterface(meta, (ManagedInterface) anno);
                    break;
                default:
                    throw new UnsupportedException(_loc.get("unsupported", _cls,
                        anno.toString()));
            }
        }

        if (isMetaDataMode()) {
            parseDetachedState(meta, detached);

            // merge callback methods with declared listeners
            int[] highs = null;
            if (listeners != null) {
                highs = new int[listeners.length];
                for (int i = 0; i < listeners.length; i++)
                    if (listeners[i] != null)
                        highs[i] = listeners[i].size();
            }
            recordCallbacks(meta, parseCallbackMethods(_cls, listeners, false,
                false, getRepository()), highs, false);

            // scan possibly non-PC hierarchy for callbacks.
            // redundant for PC superclass but we don't know that yet
            // so let LifecycleMetaData determine that
            if (_cls.getSuperclass() != null &&
                !Object.class.equals(_cls.getSuperclass())) {
                recordCallbacks(meta, parseCallbackMethods(_cls.getSuperclass(),
                    null, true, false, getRepository()), null, true);
            }
        }

        for (FieldMetaData fmd : meta.getDeclaredFields())
            if (fmd.getManagement() == FieldMetaData.MANAGE_PERSISTENT)
                parseMemberAnnotations(fmd);
        // parse fetch groups after fields
        if (fgs != null)
            parseFetchGroups(meta, fgs);

        // always parse mapping after metadata in case there are dependencies
        if (isMappingOverrideMode()) {
            parseClassMappingAnnotations(meta);
            for (FieldMetaData fmd : meta.getDeclaredFields())
                if (fmd.getManagement() == FieldMetaData.MANAGE_PERSISTENT)
                    parseMemberMappingAnnotations(fmd);
        }
        return meta;
    }

    /**
     * Parse class mapping annotations.
     */
    protected void parseClassMappingAnnotations(ClassMetaData meta) {
    }

    /**
     * Allow subclasses to handle unknown annotations.
     */
    protected boolean handleUnknownClassAnnotation(ClassMetaData meta,
        Annotation anno) {
        return false;
    }

    /**
     * Find or create metadata for the given type. May return null if
     * this class has already been parsed fully.
     */
    private ClassMetaData getMetaData() {
        ClassMetaData meta = getRepository().getCachedMetaData(_cls);
        if (meta != null &&
            ((isMetaDataMode() && (meta.getSourceMode() & MODE_META) != 0) ||
                (isMappingMode() &&
                    (meta.getSourceMode() & MODE_MAPPING) != 0))) {
            if (_log.isWarnEnabled())
                _log.warn(_loc.get("dup-metadata", _cls.getName()));
            return null;
        }

        if (meta == null) {
            meta = getRepository().addMetaData(_cls);
            meta.setEnvClassLoader(_envLoader);
            meta.setSourceMode(MODE_NONE);
            meta.setSource(getSourceFile(), meta.SRC_ANNOTATIONS);
        }
        return meta;
    }

    /**
     * Determine the source file we're parsing.
     */
    protected File getSourceFile() {
        if (_file != null)
            return _file;

        Class cls = _cls;
        while (cls.getEnclosingClass() != null)
            cls = cls.getEnclosingClass();

        String rsrc = StringUtils.replace(cls.getName(), ".", "/");
        ClassLoader loader = (ClassLoader) AccessController.doPrivileged(
            J2DoPriv5Helper.getClassLoaderAction(cls)); 
        if (loader == null)
            loader = (ClassLoader) AccessController.doPrivileged(
                J2DoPriv5Helper.getSystemClassLoaderAction()); 
        if (loader == null)
            return null;
        URL url = (URL) AccessController.doPrivileged(
            J2DoPriv5Helper.getResourceAction(loader, rsrc + ".java")); 
        if (url == null) {
            url = (URL) AccessController.doPrivileged(
                J2DoPriv5Helper.getResourceAction(loader, rsrc + ".class")); 
            if (url == null)
                return null;
        }
        try {
            _file = new File(url.toURI());
        } catch (URISyntaxException e) {
        } catch (IllegalArgumentException iae) {
            // this is thrown when the URI is non-hierarchical (aka JBoss)
        }
        return _file;
    }

    /**
     * Parse @DataStoreId.
     */
    private void parseDataStoreId(ClassMetaData meta, DataStoreId id) {
        meta.setIdentityType(ClassMetaData.ID_DATASTORE);

        int strat = getGeneratedValueStrategy(meta, id.strategy(),
            id.generator());
        if (strat != -1)
            meta.setIdentityStrategy(strat);
        else {
            switch (id.strategy()) {
                case TABLE:
                case SEQUENCE:
                    // technically we should have separate system table and
                    // sequence generators, but it's easier to just rely on
                    // the system org.apache.openjpa.Sequence setting for both
                    if (StringUtils.isEmpty(id.generator()))
                        meta.setIdentitySequenceName(
                            SequenceMetaData.NAME_SYSTEM);
                    else
                        meta.setIdentitySequenceName(id.generator());
                    break;
                case AUTO:
                    meta.setIdentityStrategy(ValueStrategies.NATIVE);
                    break;
                case IDENTITY:
                    meta.setIdentityStrategy(ValueStrategies.AUTOASSIGN);
                    break;
                default:
                    throw new UnsupportedException(id.strategy().toString());
            }
        }
    }

    /**
     * Warn that @FlushMode is not supported.
     */
    private void warnFlushMode(Object context) {
        if (_log.isWarnEnabled())
            _log.warn(_loc.get("unsupported", "FlushMode", context));
    }

    /**
     * Parse @DataCache.
     */
    private void parseDataCache(ClassMetaData meta, DataCache cache) {
        if (cache.timeout() != Integer.MIN_VALUE)
            meta.setDataCacheTimeout(cache.timeout());
        if (!StringUtils.isEmpty(cache.name()))
            meta.setDataCacheName(cache.name());
        else if (cache.enabled())
            meta.setDataCacheName(
                org.apache.openjpa.datacache.DataCache.NAME_DEFAULT);
        else
            meta.setDataCacheName(null);
        
        meta.setIsCacheable(cache.enabled(), true);
    }

    private void parseManagedInterface(ClassMetaData meta,
        ManagedInterface iface) {
        meta.setManagedInterface(true);
    }

    /**
     * Parse @DetachedState. The annotation may be null.
     */
    private void parseDetachedState(ClassMetaData meta,
        DetachedState detached) {
        if (detached != null) {
            if (!detached.enabled())
                meta.setDetachedState(null);
            else if (StringUtils.isEmpty(detached.fieldName()))
                meta.setDetachedState(ClassMetaData.SYNTHETIC);
            else
                meta.setDetachedState(detached.fieldName());
        } else {
            Field[] fields = (Field[]) AccessController.doPrivileged(
                J2DoPriv5Helper.getDeclaredFieldsAction(
                    meta.getDescribedType())); 
            for (int i = 0; i < fields.length; i++)
                if (((Boolean) AccessController.doPrivileged(J2DoPriv5Helper
                    .isAnnotationPresentAction(fields[i], DetachedState.class)))
                    .booleanValue())
                    meta.setDetachedState(fields[i].getName());
        }
    }

    /**
     * Parse @EntityListeners
     */
    private Collection[] parseEntityListeners
        (ClassMetaData meta, EntityListeners listeners) {
        Class[] classes = listeners.value();
        Collection[] parsed = null;
        for (Class cls : classes)
            parsed = parseCallbackMethods(cls, parsed, true, true, 
                getRepository());
        return parsed;
    }

    /**
     * Parse callback methods into the given array, and return that array,
     * creating one if null. Each index into the array is a collection of
     * callback adapters for that numeric event type.
     *
     * @param sups whether to scan superclasses
     * @param listener whether this is a listener or not
     */
    public static Collection[] parseCallbackMethods
        (Class cls, Collection[] callbacks, boolean sups,
        boolean listener, MetaDataRepository repos) {

        if (cls == null)
            throw new IllegalArgumentException("cls cannot be null");

        // first sort / filter based on inheritance
        Set methods = new TreeSet(MethodComparator.
            getInstance());

        int mods;
        Class sup = cls;
        MethodKey key;
        Set seen = new HashSet();
        do {
            for (Method m : (Method[]) AccessController.doPrivileged(
                J2DoPriv5Helper.getDeclaredMethodsAction(sup))) {
                mods = m.getModifiers();
                if (Modifier.isStatic(mods) || Modifier.isFinal(mods) ||
                    Object.class.equals(m.getDeclaringClass()))
                    continue;

                key = new MethodKey(m);
                if (!seen.contains(key)) {
                    methods.add(m);
                    seen.add(key);
                }
            }
            sup = sup.getSuperclass();
        } while (sups && !Object.class.equals(sup));

        MetaDataDefaults def = repos.getMetaDataFactory().getDefaults();
        for (Method m : methods) {
            for (Annotation anno : (Annotation[]) AccessController
                .doPrivileged(J2DoPriv5Helper
                    .getDeclaredAnnotationsAction(m))) {
                MetaDataTag tag = _tags.get(anno.annotationType());
                if (tag == null)
                    continue;
                int[] events = MetaDataParsers.getEventTypes(tag);
                if (events == null)
                    continue;

                if (callbacks == null)
                    callbacks = (Collection[])
                        new Collection[LifecycleEvent.ALL_EVENTS.length];

                for (int i = 0; i < events.length; i++) {
                    int e = events[i];
                    if (callbacks[e] == null)
                        callbacks[e] = new ArrayList(3);
                    MetaDataParsers.validateMethodsForSameCallback(cls, 
                        callbacks[e], m, tag, def, repos.getLog());
                    if (listener) {
                        callbacks[e].add(new BeanLifecycleCallbacks(cls, m,
                            false));
                    } else {
                        callbacks[e].add(new MethodLifecycleCallbacks(m,
                            false));
                    }
                }
            }
        }
        return callbacks;
    }
    
    /**
     * Store lifecycle metadata.
     */
    private void recordCallbacks(ClassMetaData cls,
        Collection[] callbacks, int[] highs,
        boolean superClass) {
        if (callbacks == null)
            return;
        LifecycleMetaData meta = cls.getLifecycleMetaData();
        LifecycleCallbacks[] array;
        for (int event : LifecycleEvent.ALL_EVENTS) {
            if (callbacks[event] == null)
                continue;
            array = callbacks[event].toArray
                (new LifecycleCallbacks[callbacks[event].size()]);

            if (superClass) {
                meta.setNonPCSuperclassCallbacks(event, array,
                    (highs == null) ? 0 : highs[event]);
            } else {
                meta.setDeclaredCallbacks(event, array,
                    (highs == null) ? 0 : highs[event]);
            }
        }
    }

    /**
     * Create fetch groups.
     * If FetchGroup A includes FetchGroup B, then a bi-link is set between
     * A and B. Both A and B must be declared in the same Class. 
     * 
* Call {@link #parseFetchAttribute(ClassMetaData, * org.apache.openjpa.meta.FetchGroup, FetchAttribute) only after the * bi-links have been established, because a field f will not only add the * fetch group A which explictly includes f to its custom fetch groups but * also will also add any fetch group B that includes A. */ private void parseFetchGroups(ClassMetaData meta, FetchGroup... groups) { org.apache.openjpa.meta.FetchGroup fg; for (FetchGroup group : groups) { if (StringUtils.isEmpty(group.name())) throw new MetaDataException(_loc.get("unnamed-fg", meta)); fg = meta.addDeclaredFetchGroup(group.name()); if (group.postLoad()) fg.setPostLoad(true); for (String s : group.fetchGroups()) { fg.addDeclaredInclude(s); } } // Add the parent-child style bi-links between fetch groups in a // separate pass. for (FetchGroup group:groups) { fg = meta.getFetchGroup(group.name()); String[] includedFetchGropNames = fg.getDeclaredIncludes(); for (String includedFectchGroupName:includedFetchGropNames) { org.apache.openjpa.meta.FetchGroup child = meta.getFetchGroup(includedFectchGroupName); if (child == null) throw new UserException(_loc.get("missing-included-fg", meta.getDescribedType().getName(), fg.getName(), includedFectchGroupName)); child.addContainedBy(fg); } } for (FetchGroup group : groups) { fg = meta.getFetchGroup(group.name()); for (FetchAttribute attr : group.attributes()) parseFetchAttribute(meta, fg, attr); } } /** * Set a field's fetch group. */ private void parseFetchAttribute(ClassMetaData meta, org.apache.openjpa.meta.FetchGroup fg, FetchAttribute attr) { FieldMetaData field = meta.getDeclaredField(attr.name()); if (field == null || field.getManagement() != FieldMetaData.MANAGE_PERSISTENT) throw new MetaDataException(_loc.get("bad-fg-field", fg.getName(), meta, attr.name())); field.setInFetchGroup(fg.getName(), true); Set parentFetchGroups = fg.getContainedBy(); for (Object parentFetchGroup:parentFetchGroups) field.setInFetchGroup(parentFetchGroup.toString(), true); if (attr.recursionDepth() != Integer.MIN_VALUE) fg.setRecursionDepth(field, attr.recursionDepth()); } /** * Read annotations for the given member. */ private void parseMemberAnnotations(FieldMetaData fmd) { // look for persistence strategy in annotation table Member member = getRepository().getMetaDataFactory().getDefaults(). getBackingMember(fmd); PersistenceStrategy pstrat = PersistenceMetaDataDefaults. getPersistenceStrategy(fmd, member); if (pstrat == null) return; fmd.setExplicit(true); AnnotatedElement el = (AnnotatedElement) member; boolean lob = ((Boolean) AccessController.doPrivileged(J2DoPriv5Helper .isAnnotationPresentAction(el, Lob.class))).booleanValue(); if (isMetaDataMode()) { switch (pstrat) { case BASIC: parseBasic(fmd, (Basic) el.getAnnotation(Basic.class), lob); break; case MANY_ONE: parseManyToOne(fmd, (ManyToOne) el.getAnnotation (ManyToOne.class)); break; case ONE_ONE: parseOneToOne(fmd, (OneToOne) el.getAnnotation (OneToOne.class)); break; case EMBEDDED: parseEmbedded(fmd, (Embedded) el.getAnnotation (Embedded.class)); break; case ONE_MANY: parseOneToMany(fmd, (OneToMany) el.getAnnotation (OneToMany.class)); break; case MANY_MANY: parseManyToMany(fmd, (ManyToMany) el.getAnnotation (ManyToMany.class)); break; case PERS: parsePersistent(fmd, (Persistent) el.getAnnotation (Persistent.class)); break; case PERS_COLL: parsePersistentCollection(fmd, (PersistentCollection) el.getAnnotation(PersistentCollection.class)); break; case PERS_MAP: parsePersistentMap(fmd, (PersistentMap) el.getAnnotation(PersistentMap.class)); break; case TRANSIENT: break; default: throw new InternalException(); } } if (isMappingOverrideMode() && lob) parseLobMapping(fmd); // extensions MetaDataTag tag; for (Annotation anno : el.getDeclaredAnnotations()) { tag = _tags.get(anno.annotationType()); if (tag == null) { handleUnknownMemberAnnotation(fmd, anno); continue; } switch (tag) { case FLUSH_MODE: if (isMetaDataMode()) warnFlushMode(fmd); break; case GENERATED_VALUE: if (isMappingOverrideMode()) parseGeneratedValue(fmd, (GeneratedValue) anno); break; case ID: case EMBEDDED_ID: fmd.setPrimaryKey(true); break; case MAP_KEY: if (isMappingOverrideMode()) parseMapKey(fmd, (MapKey) anno); break; case ORDER_BY: parseOrderBy(fmd, (OrderBy) el.getAnnotation(OrderBy.class)); break; case SEQ_GENERATOR: if (isMappingOverrideMode()) parseSequenceGenerator(el, (SequenceGenerator) anno); break; case VERSION: fmd.setVersion(true); break; case DEPENDENT: if (isMetaDataMode() && ((Dependent) anno).value()) fmd.setCascadeDelete(ValueMetaData.CASCADE_AUTO); break; case ELEM_DEPENDENT: if (isMetaDataMode() && ((ElementDependent) anno).value()) fmd.getElement().setCascadeDelete (ValueMetaData.CASCADE_AUTO); break; case ELEM_TYPE: if (isMetaDataMode()) fmd.getElement().setTypeOverride(toOverrideType (((ElementType) anno).value())); break; case EXTERNAL_VALS: if (isMetaDataMode()) fmd.setExternalValues(Strings.join(((ExternalValues) anno).value(), ",")); break; case EXTERNALIZER: if (isMetaDataMode()) fmd.setExternalizer(((Externalizer) anno).value()); break; case FACTORY: if (isMetaDataMode()) fmd.setFactory(((Factory) anno).value()); break; case INVERSE_LOGICAL: if (isMetaDataMode()) fmd.setInverse(((InverseLogical) anno).value()); break; case KEY_DEPENDENT: if (isMetaDataMode() && ((KeyDependent) anno).value()) fmd.getKey() .setCascadeDelete(ValueMetaData.CASCADE_AUTO); break; case KEY_TYPE: if (isMetaDataMode()) fmd.getKey().setTypeOverride(toOverrideType(((KeyType) anno).value())); break; case LOAD_FETCH_GROUP: if (isMetaDataMode()) fmd.setLoadFetchGroup(((LoadFetchGroup) anno).value()); break; case LRS: if (isMetaDataMode()) fmd.setLRS(((LRS) anno).value()); break; case READ_ONLY: if (isMetaDataMode()) parseReadOnly(fmd, (ReadOnly) anno); break; case TYPE: if (isMetaDataMode()) fmd.setTypeOverride(toOverrideType(((Type) anno). value())); break; default: throw new UnsupportedException(_loc.get("unsupported", fmd, anno.toString())); } } } /** * Parse member mapping components. */ protected void parseMemberMappingAnnotations(FieldMetaData fmd) { } /** * Allow subclasses to handle unknown annotations. */ protected boolean handleUnknownMemberAnnotation(FieldMetaData fmd, Annotation anno) { return false; } /** * Convert the given class to its OpenJPA type override equivalent. */ private static Class toOverrideType(Class cls) { return (cls == Entity.class) ? org.apache.openjpa.enhance.PersistenceCapable.class : cls; } /** * Parse @ReadOnly. */ private void parseReadOnly(FieldMetaData fmd, ReadOnly ro) { if (ro.value() == UpdateAction.RESTRICT) fmd.setUpdateStrategy(UpdateStrategies.RESTRICT); else if (ro.value() == UpdateAction.IGNORE) fmd.setUpdateStrategy(UpdateStrategies.IGNORE); else throw new InternalException(); } /** * Sets value generation information for the given field. */ private void parseGeneratedValue(FieldMetaData fmd, GeneratedValue gen) { GenerationType strategy = gen.strategy(); String generator = gen.generator(); parseGeneratedValue(fmd, strategy, generator); } /** * Sets value generation information for the given field. */ static void parseGeneratedValue(FieldMetaData fmd, GenerationType strategy, String generator) { int strat = getGeneratedValueStrategy(fmd, strategy, generator); if (strat != -1) fmd.setValueStrategy(strat); else { switch (strategy) { case TABLE: case SEQUENCE: // technically we should have separate system table and // sequence generators, but it's easier to just rely on // the system org.apache.openjpa.Sequence setting for both if (StringUtils.isEmpty(generator)) fmd.setValueSequenceName(SequenceMetaData.NAME_SYSTEM); else fmd.setValueSequenceName(generator); break; case AUTO: fmd.setValueSequenceName(SequenceMetaData.NAME_SYSTEM); break; case IDENTITY: fmd.setValueStrategy(ValueStrategies.AUTOASSIGN); break; default: throw new UnsupportedException(strategy.toString()); } } } /** * Return the value strategy for the given generator, or -1 if the * strategy depends on the GenerationType rather than the * generator name. */ private static int getGeneratedValueStrategy(Object context, GenerationType strategy, String generator) { if (strategy != AUTO || StringUtils.isEmpty(generator)) return -1; if (Generator.UUID_HEX.equals(generator)) return ValueStrategies.UUID_HEX; if (Generator.UUID_STRING.equals(generator)) return ValueStrategies.UUID_STRING; if (Generator.UUID_TYPE4_HEX.equals(generator)) return ValueStrategies.UUID_TYPE4_HEX; if (Generator.UUID_TYPE4_STRING.equals(generator)) return ValueStrategies.UUID_TYPE4_STRING; throw new MetaDataException(_loc.get("generator-bad-strategy", context, generator)); } /** * Parse @Basic. Given annotation may be null. */ private void parseBasic(FieldMetaData fmd, Basic anno, boolean lob) { Class type = fmd.getDeclaredType(); if (lob && type != String.class && type != char[].class && type != Character[].class && type != byte[].class && type != Byte[].class) fmd.setSerialized(true); else if (!lob) { switch (fmd.getDeclaredTypeCode()) { case JavaTypes.OBJECT: if (Enum.class.isAssignableFrom(type)) break; // else no break case JavaTypes.COLLECTION: case JavaTypes.MAP: case JavaTypes.PC: case JavaTypes.PC_UNTYPED: if (Serializable.class.isAssignableFrom(type)) fmd.setSerialized(true); else throw new MetaDataException(_loc.get("bad-meta-anno", fmd, "Basic")); break; case JavaTypes.ARRAY: if (type == char[].class || type == Character[].class || type == byte[].class || type == Byte[].class) break; if (Serializable.class.isAssignableFrom (type.getComponentType())) fmd.setSerialized(true); else throw new MetaDataException(_loc.get("bad-meta-anno", fmd, "Basic")); break; } } if (anno == null) return; fmd.setInDefaultFetchGroup(anno.fetch() == FetchType.EAGER); if (!anno.optional()) fmd.setNullValue(FieldMetaData.NULL_EXCEPTION); } /** * Parse @ManyToOne. */ private void parseManyToOne(FieldMetaData fmd, ManyToOne anno) { if (!JavaTypes.maybePC(fmd.getValue())) throw new MetaDataException(_loc.get("bad-meta-anno", fmd, "ManyToOne")); // don't specifically exclude relation from DFG b/c that will prevent // us from even reading the fk when reading from the primary table, // which is not what most users will want if (anno.fetch() == FetchType.EAGER) fmd.setInDefaultFetchGroup(true); if (!anno.optional()) fmd.setNullValue(FieldMetaData.NULL_EXCEPTION); if (anno.targetEntity() != void.class) fmd.setTypeOverride(anno.targetEntity()); setCascades(fmd, anno.cascade()); } /** * Parse @OneToOne. */ private void parseOneToOne(FieldMetaData fmd, OneToOne anno) { if (!JavaTypes.maybePC(fmd.getValue())) throw new MetaDataException(_loc.get("bad-meta-anno", fmd, "OneToOne")); // don't specifically exclude relation from DFG b/c that will prevent // us from even reading the fk when reading from the primary table, // which is not what most users will want if (anno.fetch() == FetchType.EAGER) fmd.setInDefaultFetchGroup(true); if (!anno.optional()) fmd.setNullValue(FieldMetaData.NULL_EXCEPTION); if (isMappingOverrideMode() && !StringUtils.isEmpty(anno.mappedBy())) fmd.setMappedBy(anno.mappedBy()); if (anno.targetEntity() != void.class) fmd.setTypeOverride(anno.targetEntity()); setCascades(fmd, anno.cascade()); } /** * Parse @Embedded. Given annotation may be null. */ private void parseEmbedded(FieldMetaData fmd, Embedded anno) { if (!JavaTypes.maybePC(fmd.getValue())) throw new MetaDataException(_loc.get("bad-meta-anno", fmd, "Embedded")); fmd.setInDefaultFetchGroup(true); fmd.setEmbedded(true); if (fmd.getEmbeddedMetaData() == null) fmd.addEmbeddedMetaData(); } /** * Parse @OneToMany. */ private void parseOneToMany(FieldMetaData fmd, OneToMany anno) { switch (fmd.getDeclaredTypeCode()) { case JavaTypes.ARRAY: case JavaTypes.COLLECTION: case JavaTypes.MAP: if (JavaTypes.maybePC(fmd.getElement())) break; // no break default: throw new MetaDataException(_loc.get("bad-meta-anno", fmd, "OneToMany")); } fmd.setInDefaultFetchGroup(anno.fetch() == FetchType.EAGER); if (isMappingOverrideMode() && !StringUtils.isEmpty(anno.mappedBy())) fmd.setMappedBy(anno.mappedBy()); if (anno.targetEntity() != void.class) fmd.getElement().setDeclaredType(anno.targetEntity()); setCascades(fmd.getElement(), anno.cascade()); } /** * Parse @ManyToMany. */ private void parseManyToMany(FieldMetaData fmd, ManyToMany anno) { switch (fmd.getDeclaredTypeCode()) { case JavaTypes.ARRAY: case JavaTypes.COLLECTION: case JavaTypes.MAP: if (JavaTypes.maybePC(fmd.getElement())) break; // no break default: throw new MetaDataException(_loc.get("bad-meta-anno", fmd, "OneToMany")); } fmd.setInDefaultFetchGroup(anno.fetch() == FetchType.EAGER); if (isMappingOverrideMode() && !StringUtils.isEmpty(anno.mappedBy())) fmd.setMappedBy(anno.mappedBy()); if (anno.targetEntity() != void.class) fmd.getElement().setDeclaredType(anno.targetEntity()); setCascades(fmd.getElement(), anno.cascade()); } /** * Parse @MapKey. */ private void parseMapKey(FieldMetaData fmd, MapKey anno) { String name = anno.name(); if (StringUtils.isEmpty(name)) fmd.getKey().setValueMappedBy(ValueMetaData.MAPPED_BY_PK); else fmd.getKey().setValueMappedBy(name); } /** * Setup the field as a LOB mapping. */ protected void parseLobMapping(FieldMetaData fmd) { } /** * Parse @OrderBy. */ private void parseOrderBy(FieldMetaData fmd, OrderBy anno) { String dec = anno.value(); if (dec.length() == 0) dec = Order.ELEMENT + " asc"; fmd.setOrderDeclaration(dec); } /** * Parse @Persistent. */ private void parsePersistent(FieldMetaData fmd, Persistent anno) { switch (fmd.getDeclaredTypeCode()) { case JavaTypes.ARRAY: if (fmd.getDeclaredType() == byte[].class || fmd.getDeclaredType() == Byte[].class || fmd.getDeclaredType() == char[].class || fmd.getDeclaredType() == Character[].class) break; // no break case JavaTypes.COLLECTION: case JavaTypes.MAP: throw new MetaDataException(_loc.get("bad-meta-anno", fmd, "Persistent")); } if (!StringUtils.isEmpty(anno.mappedBy())) fmd.setMappedBy(anno.mappedBy()); fmd.setInDefaultFetchGroup(anno.fetch() == FetchType.EAGER); if (!anno.optional()) fmd.setNullValue(FieldMetaData.NULL_EXCEPTION); setCascades(fmd, anno.cascade()); if (anno.embedded()) { if (!JavaTypes.maybePC(fmd.getValue())) throw new MetaDataException(_loc.get("bad-meta-anno", fmd, "Persistent(embedded=true)")); fmd.setEmbedded(true); if (fmd.getEmbeddedMetaData() == null) fmd.addEmbeddedMetaData(); } } /** * Parse @PersistentCollection. */ private void parsePersistentCollection(FieldMetaData fmd, PersistentCollection anno) { if (fmd.getDeclaredTypeCode() != JavaTypes.ARRAY && fmd.getDeclaredTypeCode() != JavaTypes.COLLECTION) throw new MetaDataException(_loc.get("bad-meta-anno", fmd, "PersistentCollection")); if (!StringUtils.isEmpty(anno.mappedBy())) fmd.setMappedBy(anno.mappedBy()); fmd.setInDefaultFetchGroup(anno.fetch() == FetchType.EAGER); if (anno.elementType() != void.class) fmd.getElement().setDeclaredType(anno.elementType()); setCascades(fmd.getElement(), anno.elementCascade()); if (anno.elementEmbedded()) { if (!JavaTypes.maybePC(fmd.getElement())) throw new MetaDataException(_loc.get("bad-meta-anno", fmd, "PersistentCollection(embeddedElement=true)")); fmd.getElement().setEmbedded(true); if (fmd.getElement().getEmbeddedMetaData() == null) fmd.getElement().addEmbeddedMetaData(); } } /** * Parse @PersistentMap. */ private void parsePersistentMap(FieldMetaData fmd, PersistentMap anno) { if (fmd.getDeclaredTypeCode() != JavaTypes.MAP) throw new MetaDataException(_loc.get("bad-meta-anno", fmd, "PersistentMap")); fmd.setInDefaultFetchGroup(anno.fetch() == FetchType.EAGER); if (anno.keyType() != void.class) fmd.getKey().setDeclaredType(anno.keyType()); if (anno.elementType() != void.class) fmd.getElement().setDeclaredType(anno.elementType()); setCascades(fmd.getKey(), anno.keyCascade()); setCascades(fmd.getElement(), anno.elementCascade()); if (anno.keyEmbedded()) { if (!JavaTypes.maybePC(fmd.getKey())) throw new MetaDataException(_loc.get("bad-meta-anno", fmd, "PersistentMap(embeddedKey=true)")); fmd.getKey().setEmbedded(true); if (fmd.getKey().getEmbeddedMetaData() == null) fmd.getKey().addEmbeddedMetaData(); } if (anno.elementEmbedded()) { if (!JavaTypes.maybePC(fmd.getElement())) throw new MetaDataException(_loc.get("bad-meta-anno", fmd, "PersistentMap(embeddedValue=true)")); fmd.getElement().setEmbedded(true); if (fmd.getElement().getEmbeddedMetaData() == null) fmd.getElement().addEmbeddedMetaData(); } } /** * Set cascades on relation. */ private void setCascades(ValueMetaData vmd, CascadeType[] cascades) { for (CascadeType cascade : cascades) { if (cascade == CascadeType.ALL || cascade == CascadeType.REMOVE) vmd.setCascadeDelete(ValueMetaData.CASCADE_IMMEDIATE); if (cascade == CascadeType.ALL || cascade == CascadeType.PERSIST) vmd.setCascadePersist(ValueMetaData.CASCADE_IMMEDIATE); if (cascade == CascadeType.ALL || cascade == CascadeType.MERGE) vmd.setCascadeAttach(ValueMetaData.CASCADE_IMMEDIATE); if (cascade == CascadeType.ALL || cascade == CascadeType.REFRESH) vmd.setCascadeRefresh(ValueMetaData.CASCADE_IMMEDIATE); } } /** * Parse @SequenceGenerator. */ private void parseSequenceGenerator(AnnotatedElement el, SequenceGenerator gen) { String name = gen.name(); if (StringUtils.isEmpty(name)) throw new MetaDataException(_loc.get("no-seq-name", el)); if (_log.isTraceEnabled()) _log.trace(_loc.get("parse-sequence", name)); SequenceMetaData meta = getRepository().getCachedSequenceMetaData (name); if (meta != null) { if (_log.isWarnEnabled()) _log.warn(_loc.get("dup-sequence", name, el)); return; } // create new sequence meta = getRepository().addSequenceMetaData(name); String seq = gen.sequenceName(); int initial = gen.initialValue(); int allocate = gen.allocationSize(); // don't allow initial of 0 b/c looks like def value if (initial == 0) initial = 1; // create plugin string from info String clsName, props; if (StringUtils.isEmpty(seq)) { clsName = SequenceMetaData.IMPL_NATIVE; props = null; } else if (seq.indexOf('(') != -1) // plugin { clsName = Configurations.getClassName(seq); props = Configurations.getProperties(seq); seq = null; } else { clsName = SequenceMetaData.IMPL_NATIVE; props = null; } meta.setSequencePlugin(Configurations.getPlugin(clsName, props)); meta.setSequence(seq); meta.setInitialValue(initial); meta.setAllocate(allocate); meta.setSource(getSourceFile(), (el instanceof Class) ? el : null, meta.SRC_ANNOTATIONS); } /** * Parse @NamedQuery. */ private void parseNamedQueries(AnnotatedElement el, NamedQuery... queries) { QueryMetaData meta; for (NamedQuery query : queries) { if (StringUtils.isEmpty(query.name())) throw new MetaDataException(_loc.get("no-query-name", el)); if (StringUtils.isEmpty(query.query())) throw new MetaDataException(_loc.get("no-query-string", query.name(), el)); if (_log.isTraceEnabled()) _log.trace(_loc.get("parse-query", query.name())); meta = getRepository().getCachedQueryMetaData(null, query.name()); if (meta != null) { if (_log.isWarnEnabled()) _log.warn(_loc.get("dup-query", query.name(), el)); continue; } meta = getRepository().addQueryMetaData(null, query.name()); meta.setQueryString(query.query()); meta.setLanguage(JPQLParser.LANG_JPQL); for (QueryHint hint : query.hints()) meta.addHint(hint.name(), hint.value()); meta.setSource(getSourceFile(), (el instanceof Class) ? el : null, meta.SRC_ANNOTATIONS); if (isMetaDataMode()) meta.setSourceMode(MODE_META); else if (isMappingMode()) meta.setSourceMode(MODE_MAPPING); else meta.setSourceMode(MODE_QUERY); } } /** * Parse @NamedNativeQuery. */ private void parseNamedNativeQueries(AnnotatedElement el, NamedNativeQuery... queries) { QueryMetaData meta; for (NamedNativeQuery query : queries) { if (StringUtils.isEmpty(query.name())) throw new MetaDataException(_loc.get("no-native-query-name", el)); if (StringUtils.isEmpty(query.query())) throw new MetaDataException(_loc.get("no-native-query-string", query.name(), el)); if (_log.isTraceEnabled()) _log.trace(_loc.get("parse-native-query", query.name())); meta = getRepository().getCachedQueryMetaData(null, query.name()); if (meta != null) { if (_log.isWarnEnabled()) _log.warn(_loc.get("dup-query", query.name(), el)); continue; } meta = getRepository().addQueryMetaData(null, query.name()); meta.setQueryString(query.query()); meta.setLanguage(QueryLanguages.LANG_SQL); Class res = query.resultClass(); if (ImplHelper.isManagedType(getConfiguration(), res)) meta.setCandidateType(res); else if (!void.class.equals(res)) meta.setResultType(res); if (!StringUtils.isEmpty(query.resultSetMapping())) meta.setResultSetMappingName(query.resultSetMapping()); meta.setSource(getSourceFile(), (el instanceof Class) ? el : null, meta.SRC_ANNOTATIONS); if (isMetaDataMode()) meta.setSourceMode(MODE_META); else if (isMappingMode()) meta.setSourceMode(MODE_MAPPING); else meta.setSourceMode(MODE_QUERY); } } private static class MethodKey { private final Method _method; public MethodKey(Method m) { _method = m; } public int hashCode() { int code = 46 * 12 + _method.getName().hashCode(); for (Class param : _method.getParameterTypes()) code = 46 * code + param.hashCode(); return code; } public boolean equals(Object o) { if (!(o instanceof MethodKey)) return false; Method other = ((MethodKey) o)._method; if (!_method.getName().equals(other.getName())) return false; return Arrays.equals(_method.getParameterTypes(), other.getParameterTypes()); } } private static class MethodComparator implements Comparator { private static MethodComparator INSTANCE = null; public static MethodComparator getInstance() { if (INSTANCE == null) INSTANCE = new MethodComparator(); return INSTANCE; } public int compare(Object o1, Object o2) { Method m1 = (Method) o1; Method m2 = (Method) o2; Class c1 = m1.getDeclaringClass(); Class c2 = m2.getDeclaringClass(); if (!c1.equals(c2)) { if (c1.isAssignableFrom(c2)) return -1; else return 1; } int compare = m1.getName ().compareTo (m2.getName ()); if (compare == 0) return m1.hashCode () - m2.hashCode (); return compare; } } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy