0001: /*
0002: * Licensed to the Apache Software Foundation (ASF) under one
0003: * or more contributor license agreements. See the NOTICE file
0004: * distributed with this work for additional information
0005: * regarding copyright ownership. The ASF licenses this file
0006: * to you under the Apache License, Version 2.0 (the
0007: * "License"); you may not use this file except in compliance
0008: * with the License. You may obtain a copy of the License at
0009: *
0010: * http://www.apache.org/licenses/LICENSE-2.0
0011: *
0012: * Unless required by applicable law or agreed to in writing,
0013: * software distributed under the License is distributed on an
0014: * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
0015: * KIND, either express or implied. See the License for the
0016: * specific language governing permissions and limitations
0017: * under the License.
0018: */
0019: package org.apache.openjpa.persistence;
0020:
0021: import java.io.File;
0022: import java.io.Serializable;
0023: import java.lang.annotation.Annotation;
0024: import java.lang.reflect.AnnotatedElement;
0025: import java.lang.reflect.Field;
0026: import java.lang.reflect.Member;
0027: import java.lang.reflect.Method;
0028: import java.lang.reflect.Modifier;
0029: import java.net.URISyntaxException;
0030: import java.net.URL;
0031: import java.security.AccessController;
0032: import java.util.ArrayList;
0033: import java.util.Arrays;
0034: import java.util.Collection;
0035: import java.util.Comparator;
0036: import java.util.HashMap;
0037: import java.util.HashSet;
0038: import java.util.Map;
0039: import java.util.Set;
0040: import java.util.TreeSet;
0041: import javax.persistence.Basic;
0042: import javax.persistence.CascadeType;
0043: import javax.persistence.Embeddable;
0044: import javax.persistence.Embedded;
0045: import javax.persistence.EmbeddedId;
0046: import javax.persistence.Entity;
0047: import javax.persistence.EntityListeners;
0048: import javax.persistence.ExcludeDefaultListeners;
0049: import javax.persistence.ExcludeSuperclassListeners;
0050: import javax.persistence.FetchType;
0051: import javax.persistence.FlushModeType;
0052: import javax.persistence.GeneratedValue;
0053: import javax.persistence.GenerationType;
0054: import static javax.persistence.GenerationType.AUTO;
0055: import javax.persistence.Id;
0056: import javax.persistence.IdClass;
0057: import javax.persistence.Lob;
0058: import javax.persistence.ManyToMany;
0059: import javax.persistence.ManyToOne;
0060: import javax.persistence.MapKey;
0061: import javax.persistence.MappedSuperclass;
0062: import javax.persistence.NamedNativeQueries;
0063: import javax.persistence.NamedNativeQuery;
0064: import javax.persistence.NamedQueries;
0065: import javax.persistence.NamedQuery;
0066: import javax.persistence.OneToMany;
0067: import javax.persistence.OneToOne;
0068: import javax.persistence.OrderBy;
0069: import javax.persistence.PostLoad;
0070: import javax.persistence.PostPersist;
0071: import javax.persistence.PostRemove;
0072: import javax.persistence.PostUpdate;
0073: import javax.persistence.PrePersist;
0074: import javax.persistence.PreRemove;
0075: import javax.persistence.PreUpdate;
0076: import javax.persistence.QueryHint;
0077: import javax.persistence.SequenceGenerator;
0078: import javax.persistence.Version;
0079:
0080: import org.apache.commons.lang.StringUtils;
0081: import org.apache.openjpa.conf.OpenJPAConfiguration;
0082: import org.apache.openjpa.event.BeanLifecycleCallbacks;
0083: import org.apache.openjpa.event.LifecycleCallbacks;
0084: import org.apache.openjpa.event.LifecycleEvent;
0085: import org.apache.openjpa.event.MethodLifecycleCallbacks;
0086: import org.apache.openjpa.kernel.QueryLanguages;
0087: import org.apache.openjpa.kernel.jpql.JPQLParser;
0088: import org.apache.openjpa.lib.conf.Configurations;
0089: import org.apache.openjpa.lib.log.Log;
0090: import org.apache.openjpa.lib.util.J2DoPriv5Helper;
0091: import org.apache.openjpa.lib.util.Localizer;
0092: import org.apache.openjpa.meta.ClassMetaData;
0093: import org.apache.openjpa.meta.DelegatingMetaDataFactory;
0094: import org.apache.openjpa.meta.FieldMetaData;
0095: import org.apache.openjpa.meta.JavaTypes;
0096: import org.apache.openjpa.meta.LifecycleMetaData;
0097: import org.apache.openjpa.meta.MetaDataFactory;
0098: import org.apache.openjpa.meta.MetaDataModes;
0099: import org.apache.openjpa.meta.MetaDataRepository;
0100: import org.apache.openjpa.meta.Order;
0101: import org.apache.openjpa.meta.QueryMetaData;
0102: import org.apache.openjpa.meta.SequenceMetaData;
0103: import org.apache.openjpa.meta.UpdateStrategies;
0104: import org.apache.openjpa.meta.ValueMetaData;
0105: import org.apache.openjpa.meta.ValueStrategies;
0106: import org.apache.openjpa.meta.MetaDataDefaults;
0107: import static org.apache.openjpa.persistence.MetaDataTag.*;
0108: import org.apache.openjpa.util.ImplHelper;
0109: import org.apache.openjpa.util.InternalException;
0110: import org.apache.openjpa.util.MetaDataException;
0111: import org.apache.openjpa.util.UnsupportedException;
0112: import org.apache.openjpa.util.UserException;
0113:
0114: import serp.util.Numbers;
0115: import serp.util.Strings;
0116:
0117: /**
0118: * Persistence annotation metadata parser. Currently does not parse
0119: * deployment descriptors.
0120: *
0121: * @author Abe White
0122: * @author Steve Kim
0123: * @nojavadoc
0124: */
0125: public class AnnotationPersistenceMetaDataParser implements
0126: MetaDataModes {
0127:
0128: private static final Localizer _loc = Localizer
0129: .forPackage(AnnotationPersistenceMetaDataParser.class);
0130:
0131: private static final Map<Class, MetaDataTag> _tags = new HashMap<Class, MetaDataTag>();
0132:
0133: static {
0134: _tags.put(EmbeddedId.class, EMBEDDED_ID);
0135: _tags.put(EntityListeners.class, ENTITY_LISTENERS);
0136: _tags.put(ExcludeDefaultListeners.class,
0137: EXCLUDE_DEFAULT_LISTENERS);
0138: _tags.put(ExcludeSuperclassListeners.class,
0139: EXCLUDE_SUPERCLASS_LISTENERS);
0140: _tags.put(FlushModeType.class, FLUSH_MODE);
0141: _tags.put(GeneratedValue.class, GENERATED_VALUE);
0142: _tags.put(Id.class, ID);
0143: _tags.put(IdClass.class, ID_CLASS);
0144: _tags.put(MapKey.class, MAP_KEY);
0145: _tags.put(NamedNativeQueries.class, NATIVE_QUERIES);
0146: _tags.put(NamedNativeQuery.class, NATIVE_QUERY);
0147: _tags.put(NamedQueries.class, QUERIES);
0148: _tags.put(NamedQuery.class, QUERY);
0149: _tags.put(OrderBy.class, ORDER_BY);
0150: _tags.put(PostLoad.class, POST_LOAD);
0151: _tags.put(PostPersist.class, POST_PERSIST);
0152: _tags.put(PostRemove.class, POST_REMOVE);
0153: _tags.put(PostUpdate.class, POST_UPDATE);
0154: _tags.put(PrePersist.class, PRE_PERSIST);
0155: _tags.put(PreRemove.class, PRE_REMOVE);
0156: _tags.put(PreUpdate.class, PRE_UPDATE);
0157: _tags.put(SequenceGenerator.class, SEQ_GENERATOR);
0158: _tags.put(Version.class, VERSION);
0159: _tags.put(DataCache.class, DATA_CACHE);
0160: _tags.put(DataStoreId.class, DATASTORE_ID);
0161: _tags.put(Dependent.class, DEPENDENT);
0162: _tags.put(DetachedState.class, DETACHED_STATE);
0163: _tags.put(ElementDependent.class, ELEM_DEPENDENT);
0164: _tags.put(ElementType.class, ELEM_TYPE);
0165: _tags.put(ExternalValues.class, EXTERNAL_VALS);
0166: _tags.put(Externalizer.class, EXTERNALIZER);
0167: _tags.put(Factory.class, FACTORY);
0168: _tags.put(FetchGroup.class, FETCH_GROUP);
0169: _tags.put(FetchGroups.class, FETCH_GROUPS);
0170: _tags.put(InverseLogical.class, INVERSE_LOGICAL);
0171: _tags.put(KeyDependent.class, KEY_DEPENDENT);
0172: _tags.put(KeyType.class, KEY_TYPE);
0173: _tags.put(LoadFetchGroup.class, LOAD_FETCH_GROUP);
0174: _tags.put(LRS.class, LRS);
0175: _tags.put(ManagedInterface.class, MANAGED_INTERFACE);
0176: _tags.put(ReadOnly.class, READ_ONLY);
0177: _tags.put(Type.class, TYPE);
0178: }
0179:
0180: private final OpenJPAConfiguration _conf;
0181: private final Log _log;
0182: private MetaDataRepository _repos = null;
0183: private ClassLoader _envLoader = null;
0184: private boolean _override = false;
0185: private int _mode = MODE_NONE;
0186:
0187: // packages and their parse modes
0188: private final Map<Package, Integer> _pkgs = new HashMap<Package, Integer>();
0189:
0190: // the class we were invoked to parse
0191: private Class _cls = null;
0192: private File _file = null;
0193:
0194: /**
0195: * Constructor; supply configuration.
0196: */
0197: public AnnotationPersistenceMetaDataParser(OpenJPAConfiguration conf) {
0198: _conf = conf;
0199: _log = conf.getLog(OpenJPAConfiguration.LOG_METADATA);
0200: }
0201:
0202: /**
0203: * Configuration supplied on construction.
0204: */
0205: public OpenJPAConfiguration getConfiguration() {
0206: return _conf;
0207: }
0208:
0209: /**
0210: * Metadata log.
0211: */
0212: public Log getLog() {
0213: return _log;
0214: }
0215:
0216: /**
0217: * Returns the repository for this parser. If none has been set,
0218: * create a new repository and sets it.
0219: */
0220: public MetaDataRepository getRepository() {
0221: if (_repos == null) {
0222: MetaDataRepository repos = _conf
0223: .newMetaDataRepositoryInstance();
0224: MetaDataFactory mdf = repos.getMetaDataFactory();
0225: if (mdf instanceof DelegatingMetaDataFactory)
0226: mdf = ((DelegatingMetaDataFactory) mdf)
0227: .getInnermostDelegate();
0228: if (mdf instanceof PersistenceMetaDataFactory)
0229: ((PersistenceMetaDataFactory) mdf)
0230: .setAnnotationParser(this );
0231: _repos = repos;
0232: }
0233: return _repos;
0234: }
0235:
0236: /**
0237: * Set the metadata repository for this parser.
0238: */
0239: public void setRepository(MetaDataRepository repos) {
0240: _repos = repos;
0241: }
0242:
0243: /**
0244: * Return the environmental class loader to pass on to parsed
0245: * metadata instances.
0246: */
0247: public ClassLoader getEnvClassLoader() {
0248: return _envLoader;
0249: }
0250:
0251: /**
0252: * Set the environmental class loader to pass on to parsed
0253: * metadata instances.
0254: */
0255: public void setEnvClassLoader(ClassLoader loader) {
0256: _envLoader = loader;
0257: }
0258:
0259: /**
0260: * Whether to allow later parses of mapping information to override
0261: * earlier information for the same class. Defaults to false. Useful
0262: * when a tool is mapping a class, so that annotation partial mapping
0263: * information can be used even when mappings are stored in another
0264: * location.
0265: */
0266: public boolean getMappingOverride() {
0267: return _override;
0268: }
0269:
0270: /**
0271: * Whether to allow later parses of mapping information to override
0272: * earlier information for the same class. Defaults to false. Useful
0273: * when a tool is mapping a class, so that annotation partial mapping
0274: * information can be used even when mappings are stored in another
0275: * location.
0276: */
0277: public void setMappingOverride(boolean override) {
0278: _override = override;
0279: }
0280:
0281: /**
0282: * The parse mode.
0283: */
0284: public int getMode() {
0285: return _mode;
0286: }
0287:
0288: /**
0289: * The parse mode.
0290: */
0291: public void setMode(int mode, boolean on) {
0292: if (mode == MODE_NONE)
0293: _mode = MODE_NONE;
0294: else if (on)
0295: _mode |= mode;
0296: else
0297: _mode &= ~mode;
0298: }
0299:
0300: /**
0301: * The parse mode.
0302: */
0303: public void setMode(int mode) {
0304: _mode = mode;
0305: }
0306:
0307: /**
0308: * Convenience method for interpreting {@link #getMode}.
0309: */
0310: protected boolean isMetaDataMode() {
0311: return (_mode & MODE_META) != 0;
0312: }
0313:
0314: /**
0315: * Convenience method for interpreting {@link #getMode}.
0316: */
0317: protected boolean isQueryMode() {
0318: return (_mode & MODE_QUERY) != 0;
0319: }
0320:
0321: /**
0322: * Convenience method for interpreting {@link #getMode}.
0323: */
0324: protected boolean isMappingMode() {
0325: return (_mode & MODE_MAPPING) != 0;
0326: }
0327:
0328: /**
0329: * Returns true if we're in mapping mode or in metadata mode with
0330: * mapping overide enabled.
0331: */
0332: protected boolean isMappingOverrideMode() {
0333: return isMappingMode() || (_override && isMetaDataMode());
0334: }
0335:
0336: /**
0337: * Clear caches.
0338: */
0339: public void clear() {
0340: _cls = null;
0341: _file = null;
0342: _pkgs.clear();
0343: }
0344:
0345: /**
0346: * Parse persistence metadata for the given class.
0347: */
0348: public void parse(Class cls) {
0349: if (_log.isTraceEnabled())
0350: _log.trace(_loc.get("parse-class", cls.getName()));
0351:
0352: _cls = cls;
0353: try {
0354: parsePackageAnnotations();
0355: ClassMetaData meta = parseClassAnnotations();
0356: updateSourceMode(meta);
0357: } finally {
0358: _cls = null;
0359: _file = null;
0360: }
0361: }
0362:
0363: /**
0364: * Update the source mode to the class package and class to indicate that
0365: * we've fully parsed them.
0366: */
0367: private void updateSourceMode(ClassMetaData meta) {
0368: if (_cls.getPackage() != null)
0369: addSourceMode(_cls.getPackage(), _mode);
0370: if (meta != null)
0371: meta.setSourceMode(_mode, true);
0372: }
0373:
0374: /**
0375: * Parse information in package-level class annotations.
0376: */
0377: private void parsePackageAnnotations() {
0378: Package pkg = _cls.getPackage();
0379: if (pkg == null)
0380: return;
0381:
0382: int pkgMode = getSourceMode(pkg);
0383: if (pkgMode == 0 && _log.isTraceEnabled())
0384: _log.trace(_loc.get("parse-package", _cls.getName()));
0385: if ((pkgMode & _mode) == _mode) // already visited
0386: return;
0387:
0388: MetaDataTag tag;
0389: for (Annotation anno : pkg.getDeclaredAnnotations()) {
0390: tag = _tags.get(anno.annotationType());
0391: if (tag == null) {
0392: handleUnknownPackageAnnotation(pkg, anno);
0393: continue;
0394: }
0395:
0396: switch (tag) {
0397: case NATIVE_QUERIES:
0398: if (isQueryMode() && (pkgMode & MODE_QUERY) == 0)
0399: parseNamedNativeQueries(pkg,
0400: ((NamedNativeQueries) anno).value());
0401: break;
0402: case NATIVE_QUERY:
0403: if (isQueryMode() && (pkgMode & MODE_QUERY) == 0)
0404: parseNamedNativeQueries(pkg,
0405: (NamedNativeQuery) anno);
0406: break;
0407: case QUERIES:
0408: if (isQueryMode() && (pkgMode & MODE_QUERY) == 0)
0409: parseNamedQueries(pkg, ((NamedQueries) anno)
0410: .value());
0411: break;
0412: case QUERY:
0413: if (isQueryMode() && (pkgMode & MODE_QUERY) == 0)
0414: parseNamedQueries(pkg, (NamedQuery) anno);
0415: break;
0416: case SEQ_GENERATOR:
0417: if (isMappingOverrideMode()
0418: && (pkgMode & MODE_MAPPING) == 0)
0419: parseSequenceGenerator(pkg,
0420: (SequenceGenerator) anno);
0421: break;
0422: default:
0423: throw new UnsupportedException(_loc.get("unsupported",
0424: pkg, anno.toString()));
0425: }
0426: }
0427:
0428: // always parse mapping stuff after metadata stuff, in case there are
0429: // dependencies on metadata
0430: if (isMappingOverrideMode() && (pkgMode & MODE_MAPPING) == 0)
0431: parsePackageMappingAnnotations(pkg);
0432: }
0433:
0434: /**
0435: * Parse package mapping annotations.
0436: */
0437: protected void parsePackageMappingAnnotations(Package pkg) {
0438: }
0439:
0440: /**
0441: * Allow subclasses to handle unknown annotations.
0442: */
0443: protected boolean handleUnknownPackageAnnotation(Package pkg,
0444: Annotation anno) {
0445: return false;
0446: }
0447:
0448: /**
0449: * The source mode for the given package.
0450: */
0451: private int getSourceMode(Package pkg) {
0452: Number num = _pkgs.get(pkg);
0453: return (num == null) ? 0 : num.intValue();
0454: }
0455:
0456: /**
0457: * Add to the source mode for the given package.
0458: */
0459: private void addSourceMode(Package pkg, int mode) {
0460: Integer num = _pkgs.get(pkg);
0461: if (num == null)
0462: num = Numbers.valueOf(mode);
0463: else
0464: num = Numbers.valueOf(num.intValue() | mode);
0465: _pkgs.put(pkg, num);
0466: }
0467:
0468: /**
0469: * Read annotations for the current type.
0470: */
0471: private ClassMetaData parseClassAnnotations() {
0472: // check immediately whether the user is using any annotations,
0473: // regardless of mode. this prevents adding non-entity classes to
0474: // repository if we're ignoring these annotations in mapping mode
0475: if (!((Boolean) AccessController.doPrivileged(J2DoPriv5Helper
0476: .isAnnotationPresentAction(_cls, Entity.class)))
0477: .booleanValue()
0478: && !((Boolean) AccessController
0479: .doPrivileged(J2DoPriv5Helper
0480: .isAnnotationPresentAction(_cls,
0481: Embeddable.class)))
0482: .booleanValue()
0483: && !((Boolean) AccessController
0484: .doPrivileged(J2DoPriv5Helper
0485: .isAnnotationPresentAction(_cls,
0486: MappedSuperclass.class)))
0487: .booleanValue())
0488: return null;
0489:
0490: // find / create metadata
0491: ClassMetaData meta = getMetaData();
0492: if (meta == null)
0493: return null;
0494:
0495: Entity entity = (Entity) _cls.getAnnotation(Entity.class);
0496: if (isMetaDataMode()) {
0497: // while the spec only provides for embedded exclusive, it doesn't
0498: // seem hard to support otherwise
0499: if (entity == null)
0500: meta.setEmbeddedOnly(true);
0501: else {
0502: meta.setEmbeddedOnly(false);
0503: if (!StringUtils.isEmpty(entity.name()))
0504: meta.setTypeAlias(entity.name());
0505: }
0506: }
0507:
0508: // track fetch groups to parse them after fields, since they
0509: // rely on field metadata
0510: FetchGroup[] fgs = null;
0511: DetachedState detached = null;
0512:
0513: // track listeners since we need to merge them with entity callbacks
0514: Collection<LifecycleCallbacks>[] listeners = null;
0515: MetaDataTag tag;
0516: for (Annotation anno : _cls.getDeclaredAnnotations()) {
0517: tag = _tags.get(anno.annotationType());
0518: if (tag == null) {
0519: handleUnknownClassAnnotation(meta, anno);
0520: continue;
0521: }
0522:
0523: switch (tag) {
0524: case ENTITY_LISTENERS:
0525: if (isMetaDataMode())
0526: listeners = parseEntityListeners(meta,
0527: (EntityListeners) anno);
0528: break;
0529: case EXCLUDE_DEFAULT_LISTENERS:
0530: if (isMetaDataMode())
0531: meta.getLifecycleMetaData()
0532: .setIgnoreSystemListeners(true);
0533: break;
0534: case EXCLUDE_SUPERCLASS_LISTENERS:
0535: if (isMetaDataMode())
0536: meta.getLifecycleMetaData()
0537: .setIgnoreSuperclassCallbacks(
0538: LifecycleMetaData.IGNORE_HIGH);
0539: break;
0540: case FLUSH_MODE:
0541: if (isMetaDataMode())
0542: warnFlushMode(meta);
0543: break;
0544: case ID_CLASS:
0545: if (isMetaDataMode())
0546: meta
0547: .setObjectIdType(((IdClass) anno).value(),
0548: true);
0549: break;
0550: case NATIVE_QUERIES:
0551: if (isQueryMode())
0552: parseNamedNativeQueries(_cls,
0553: ((NamedNativeQueries) anno).value());
0554: break;
0555: case NATIVE_QUERY:
0556: if (isQueryMode())
0557: parseNamedNativeQueries(_cls,
0558: (NamedNativeQuery) anno);
0559: break;
0560: case QUERIES:
0561: if (isQueryMode())
0562: parseNamedQueries(_cls, ((NamedQueries) anno)
0563: .value());
0564: break;
0565: case QUERY:
0566: if (isQueryMode())
0567: parseNamedQueries(_cls, (NamedQuery) anno);
0568: break;
0569: case SEQ_GENERATOR:
0570: if (isMappingOverrideMode())
0571: parseSequenceGenerator(_cls,
0572: (SequenceGenerator) anno);
0573: break;
0574: case DATA_CACHE:
0575: if (isMetaDataMode())
0576: parseDataCache(meta, (DataCache) anno);
0577: break;
0578: case DATASTORE_ID:
0579: if (isMetaDataMode())
0580: parseDataStoreId(meta, (DataStoreId) anno);
0581: break;
0582: case DETACHED_STATE:
0583: detached = (DetachedState) anno;
0584: break;
0585: case FETCH_GROUP:
0586: if (isMetaDataMode())
0587: fgs = new FetchGroup[] { (FetchGroup) anno };
0588: break;
0589: case FETCH_GROUPS:
0590: if (isMetaDataMode())
0591: fgs = ((FetchGroups) anno).value();
0592: break;
0593: case MANAGED_INTERFACE:
0594: if (isMetaDataMode())
0595: parseManagedInterface(meta, (ManagedInterface) anno);
0596: break;
0597: default:
0598: throw new UnsupportedException(_loc.get("unsupported",
0599: _cls, anno.toString()));
0600: }
0601: }
0602:
0603: if (isMetaDataMode()) {
0604: parseDetachedState(meta, detached);
0605:
0606: // merge callback methods with declared listeners
0607: int[] highs = null;
0608: if (listeners != null) {
0609: highs = new int[listeners.length];
0610: for (int i = 0; i < listeners.length; i++)
0611: if (listeners[i] != null)
0612: highs[i] = listeners[i].size();
0613: }
0614: recordCallbacks(meta, parseCallbackMethods(_cls, listeners,
0615: false, false, getRepository()), highs, false);
0616:
0617: // scan possibly non-PC hierarchy for callbacks.
0618: // redundant for PC superclass but we don't know that yet
0619: // so let LifecycleMetaData determine that
0620: if (_cls.getSuperclass() != null
0621: && !Object.class.equals(_cls.getSuperclass())) {
0622: recordCallbacks(meta, parseCallbackMethods(_cls
0623: .getSuperclass(), null, true, false,
0624: getRepository()), null, true);
0625: }
0626: }
0627:
0628: for (FieldMetaData fmd : meta.getDeclaredFields())
0629: if (fmd.getManagement() == FieldMetaData.MANAGE_PERSISTENT)
0630: parseMemberAnnotations(fmd);
0631: // parse fetch groups after fields
0632: if (fgs != null)
0633: parseFetchGroups(meta, fgs);
0634:
0635: // always parse mapping after metadata in case there are dependencies
0636: if (isMappingOverrideMode()) {
0637: parseClassMappingAnnotations(meta);
0638: for (FieldMetaData fmd : meta.getDeclaredFields())
0639: if (fmd.getManagement() == FieldMetaData.MANAGE_PERSISTENT)
0640: parseMemberMappingAnnotations(fmd);
0641: }
0642: return meta;
0643: }
0644:
0645: /**
0646: * Parse class mapping annotations.
0647: */
0648: protected void parseClassMappingAnnotations(ClassMetaData meta) {
0649: }
0650:
0651: /**
0652: * Allow subclasses to handle unknown annotations.
0653: */
0654: protected boolean handleUnknownClassAnnotation(ClassMetaData meta,
0655: Annotation anno) {
0656: return false;
0657: }
0658:
0659: /**
0660: * Find or create metadata for the given type. May return null if
0661: * this class has already been parsed fully.
0662: */
0663: private ClassMetaData getMetaData() {
0664: ClassMetaData meta = getRepository().getCachedMetaData(_cls);
0665: if (meta != null
0666: && ((isMetaDataMode() && (meta.getSourceMode() & MODE_META) != 0) || (isMappingMode() && (meta
0667: .getSourceMode() & MODE_MAPPING) != 0))) {
0668: if (_log.isWarnEnabled())
0669: _log.warn(_loc.get("dup-metadata", _cls.getName()));
0670: return null;
0671: }
0672:
0673: if (meta == null) {
0674: meta = getRepository().addMetaData(_cls);
0675: meta.setEnvClassLoader(_envLoader);
0676: meta.setSourceMode(MODE_NONE);
0677: meta.setSource(getSourceFile(), meta.SRC_ANNOTATIONS);
0678: }
0679: return meta;
0680: }
0681:
0682: /**
0683: * Determine the source file we're parsing.
0684: */
0685: protected File getSourceFile() {
0686: if (_file != null)
0687: return _file;
0688:
0689: Class cls = _cls;
0690: while (cls.getEnclosingClass() != null)
0691: cls = cls.getEnclosingClass();
0692:
0693: String rsrc = StringUtils.replace(cls.getName(), ".", "/");
0694: ClassLoader loader = (ClassLoader) AccessController
0695: .doPrivileged(J2DoPriv5Helper.getClassLoaderAction(cls));
0696: if (loader == null)
0697: loader = (ClassLoader) AccessController
0698: .doPrivileged(J2DoPriv5Helper
0699: .getSystemClassLoaderAction());
0700: if (loader == null)
0701: return null;
0702: URL url = (URL) AccessController.doPrivileged(J2DoPriv5Helper
0703: .getResourceAction(loader, rsrc + ".java"));
0704: if (url == null) {
0705: url = (URL) AccessController.doPrivileged(J2DoPriv5Helper
0706: .getResourceAction(loader, rsrc + ".class"));
0707: if (url == null)
0708: return null;
0709: }
0710: try {
0711: _file = new File(url.toURI());
0712: } catch (URISyntaxException e) {
0713: } catch (IllegalArgumentException iae) {
0714: // this is thrown when the URI is non-hierarchical (aka JBoss)
0715: }
0716: return _file;
0717: }
0718:
0719: /**
0720: * Parse @DataStoreId.
0721: */
0722: private void parseDataStoreId(ClassMetaData meta, DataStoreId id) {
0723: meta.setIdentityType(ClassMetaData.ID_DATASTORE);
0724:
0725: int strat = getGeneratedValueStrategy(meta, id.strategy(), id
0726: .generator());
0727: if (strat != -1)
0728: meta.setIdentityStrategy(strat);
0729: else {
0730: switch (id.strategy()) {
0731: case TABLE:
0732: case SEQUENCE:
0733: // technically we should have separate system table and
0734: // sequence generators, but it's easier to just rely on
0735: // the system org.apache.openjpa.Sequence setting for both
0736: if (StringUtils.isEmpty(id.generator()))
0737: meta
0738: .setIdentitySequenceName(SequenceMetaData.NAME_SYSTEM);
0739: else
0740: meta.setIdentitySequenceName(id.generator());
0741: break;
0742: case AUTO:
0743: meta.setIdentityStrategy(ValueStrategies.NATIVE);
0744: break;
0745: case IDENTITY:
0746: meta.setIdentityStrategy(ValueStrategies.AUTOASSIGN);
0747: break;
0748: default:
0749: throw new UnsupportedException(id.strategy().toString());
0750: }
0751: }
0752: }
0753:
0754: /**
0755: * Warn that @FlushMode is not supported.
0756: */
0757: private void warnFlushMode(Object context) {
0758: if (_log.isWarnEnabled())
0759: _log.warn(_loc.get("unsupported", "FlushMode", context));
0760: }
0761:
0762: /**
0763: * Parse @DataCache.
0764: */
0765: private void parseDataCache(ClassMetaData meta, DataCache cache) {
0766: if (cache.timeout() != Integer.MIN_VALUE)
0767: meta.setDataCacheTimeout(cache.timeout());
0768: if (!StringUtils.isEmpty(cache.name()))
0769: meta.setDataCacheName(cache.name());
0770: else if (cache.enabled())
0771: meta
0772: .setDataCacheName(org.apache.openjpa.datacache.DataCache.NAME_DEFAULT);
0773: else
0774: meta.setDataCacheName(null);
0775: }
0776:
0777: private void parseManagedInterface(ClassMetaData meta,
0778: ManagedInterface iface) {
0779: meta.setManagedInterface(true);
0780: }
0781:
0782: /**
0783: * Parse @DetachedState. The annotation may be null.
0784: */
0785: private void parseDetachedState(ClassMetaData meta,
0786: DetachedState detached) {
0787: if (detached != null) {
0788: if (!detached.enabled())
0789: meta.setDetachedState(null);
0790: else if (StringUtils.isEmpty(detached.fieldName()))
0791: meta.setDetachedState(ClassMetaData.SYNTHETIC);
0792: else
0793: meta.setDetachedState(detached.fieldName());
0794: } else {
0795: Field[] fields = (Field[]) AccessController
0796: .doPrivileged(J2DoPriv5Helper
0797: .getDeclaredFieldsAction(meta
0798: .getDescribedType()));
0799: for (int i = 0; i < fields.length; i++)
0800: if (((Boolean) AccessController
0801: .doPrivileged(J2DoPriv5Helper
0802: .isAnnotationPresentAction(fields[i],
0803: DetachedState.class)))
0804: .booleanValue())
0805: meta.setDetachedState(fields[i].getName());
0806: }
0807: }
0808:
0809: /**
0810: * Parse @EntityListeners
0811: */
0812: private Collection<LifecycleCallbacks>[] parseEntityListeners(
0813: ClassMetaData meta, EntityListeners listeners) {
0814: Class[] classes = listeners.value();
0815: Collection<LifecycleCallbacks>[] parsed = null;
0816: for (Class cls : classes)
0817: parsed = parseCallbackMethods(cls, parsed, true, true,
0818: getRepository());
0819: return parsed;
0820: }
0821:
0822: /**
0823: * Parse callback methods into the given array, and return that array,
0824: * creating one if null. Each index into the array is a collection of
0825: * callback adapters for that numeric event type.
0826: *
0827: * @param sups whether to scan superclasses
0828: * @param listener whether this is a listener or not
0829: */
0830: public static Collection<LifecycleCallbacks>[] parseCallbackMethods(
0831: Class cls, Collection<LifecycleCallbacks>[] callbacks,
0832: boolean sups, boolean listener, MetaDataRepository repos) {
0833:
0834: if (cls == null)
0835: throw new IllegalArgumentException("cls cannot be null");
0836:
0837: // first sort / filter based on inheritance
0838: Set<Method> methods = new TreeSet<Method>(MethodComparator
0839: .getInstance());
0840:
0841: int mods;
0842: Class sup = cls;
0843: MethodKey key;
0844: Set<MethodKey> seen = new HashSet<MethodKey>();
0845: do {
0846: for (Method m : (Method[]) AccessController
0847: .doPrivileged(J2DoPriv5Helper
0848: .getDeclaredMethodsAction(sup))) {
0849: mods = m.getModifiers();
0850: if (Modifier.isStatic(mods) || Modifier.isFinal(mods)
0851: || Object.class.equals(m.getDeclaringClass()))
0852: continue;
0853:
0854: key = new MethodKey(m);
0855: if (!seen.contains(key)) {
0856: methods.add(m);
0857: seen.add(key);
0858: }
0859: }
0860: sup = sup.getSuperclass();
0861: } while (sups && !Object.class.equals(sup));
0862:
0863: MetaDataDefaults def = repos.getMetaDataFactory().getDefaults();
0864: for (Method m : methods) {
0865: for (Annotation anno : (Annotation[]) AccessController
0866: .doPrivileged(J2DoPriv5Helper
0867: .getDeclaredAnnotationsAction(m))) {
0868: MetaDataTag tag = _tags.get(anno.annotationType());
0869: if (tag == null)
0870: continue;
0871: int[] events = MetaDataParsers.getEventTypes(tag);
0872: if (events == null)
0873: continue;
0874:
0875: if (callbacks == null)
0876: callbacks = (Collection<LifecycleCallbacks>[]) new Collection[LifecycleEvent.ALL_EVENTS.length];
0877:
0878: for (int i = 0; i < events.length; i++) {
0879: int e = events[i];
0880: if (callbacks[e] == null)
0881: callbacks[e] = new ArrayList(3);
0882: MetaDataParsers.validateMethodsForSameCallback(cls,
0883: callbacks[e], m, tag, def, repos.getLog());
0884: if (listener) {
0885: callbacks[e].add(new BeanLifecycleCallbacks(
0886: cls, m, false));
0887: } else {
0888: callbacks[e].add(new MethodLifecycleCallbacks(
0889: m, false));
0890: }
0891: }
0892: }
0893: }
0894: return callbacks;
0895: }
0896:
0897: /**
0898: * Store lifecycle metadata.
0899: */
0900: private void recordCallbacks(ClassMetaData cls,
0901: Collection<LifecycleCallbacks>[] callbacks, int[] highs,
0902: boolean super Class) {
0903: if (callbacks == null)
0904: return;
0905: LifecycleMetaData meta = cls.getLifecycleMetaData();
0906: LifecycleCallbacks[] array;
0907: for (int event : LifecycleEvent.ALL_EVENTS) {
0908: if (callbacks[event] == null)
0909: continue;
0910: array = callbacks[event]
0911: .toArray(new LifecycleCallbacks[callbacks[event]
0912: .size()]);
0913:
0914: if (super Class) {
0915: meta.setNonPCSuperclassCallbacks(event, array,
0916: (highs == null) ? 0 : highs[event]);
0917: } else {
0918: meta.setDeclaredCallbacks(event, array,
0919: (highs == null) ? 0 : highs[event]);
0920: }
0921: }
0922: }
0923:
0924: /**
0925: * Create fetch groups.
0926: * If FetchGroup A includes FetchGroup B, then a bi-link is set between
0927: * A and B. Both A and B must be declared in the same Class.
0928: * <br>
0929: * Call {@link #parseFetchAttribute(ClassMetaData,
0930: * org.apache.openjpa.meta.FetchGroup, FetchAttribute) only after the
0931: * bi-links have been established, because a field f will not only add the
0932: * fetch group A which explictly includes f to its custom fetch groups but
0933: * also will also add any fetch group B that includes A.
0934: */
0935: private void parseFetchGroups(ClassMetaData meta,
0936: FetchGroup... groups) {
0937: org.apache.openjpa.meta.FetchGroup fg;
0938: for (FetchGroup group : groups) {
0939: if (StringUtils.isEmpty(group.name()))
0940: throw new MetaDataException(_loc
0941: .get("unnamed-fg", meta));
0942:
0943: fg = meta.addDeclaredFetchGroup(group.name());
0944: if (group.postLoad())
0945: fg.setPostLoad(true);
0946: for (String s : group.fetchGroups()) {
0947: fg.addDeclaredInclude(s);
0948: }
0949: }
0950: // Add the parent-child style bi-links between fetch groups in a
0951: // separate pass.
0952: for (FetchGroup group : groups) {
0953: fg = meta.getFetchGroup(group.name());
0954: String[] includedFetchGropNames = fg.getDeclaredIncludes();
0955: for (String includedFectchGroupName : includedFetchGropNames) {
0956: org.apache.openjpa.meta.FetchGroup child = meta
0957: .getFetchGroup(includedFectchGroupName);
0958: if (child == null)
0959: throw new UserException(
0960: _loc
0961: .get("missing-included-fg", meta
0962: .getDescribedType()
0963: .getName(), fg.getName(),
0964: includedFectchGroupName));
0965: child.addContainedBy(fg);
0966: }
0967: }
0968:
0969: for (FetchGroup group : groups) {
0970: fg = meta.getFetchGroup(group.name());
0971: for (FetchAttribute attr : group.attributes())
0972: parseFetchAttribute(meta, fg, attr);
0973: }
0974: }
0975:
0976: /**
0977: * Set a field's fetch group.
0978: */
0979: private void parseFetchAttribute(ClassMetaData meta,
0980: org.apache.openjpa.meta.FetchGroup fg, FetchAttribute attr) {
0981: FieldMetaData field = meta.getDeclaredField(attr.name());
0982: if (field == null
0983: || field.getManagement() != FieldMetaData.MANAGE_PERSISTENT)
0984: throw new MetaDataException(_loc.get("bad-fg-field", fg
0985: .getName(), meta, attr.name()));
0986:
0987: field.setInFetchGroup(fg.getName(), true);
0988: Set parentFetchGroups = fg.getContainedBy();
0989: for (Object parentFetchGroup : parentFetchGroups)
0990: field.setInFetchGroup(parentFetchGroup.toString(), true);
0991: if (attr.recursionDepth() != Integer.MIN_VALUE)
0992: fg.setRecursionDepth(field, attr.recursionDepth());
0993: }
0994:
0995: /**
0996: * Read annotations for the given member.
0997: */
0998: private void parseMemberAnnotations(FieldMetaData fmd) {
0999: // look for persistence strategy in annotation table
1000: Member member = getRepository().getMetaDataFactory()
1001: .getDefaults().getBackingMember(fmd);
1002: PersistenceStrategy pstrat = PersistenceMetaDataDefaults
1003: .getPersistenceStrategy(fmd, member);
1004: if (pstrat == null)
1005: return;
1006: fmd.setExplicit(true);
1007:
1008: AnnotatedElement el = (AnnotatedElement) member;
1009: boolean lob = ((Boolean) AccessController
1010: .doPrivileged(J2DoPriv5Helper
1011: .isAnnotationPresentAction(el, Lob.class)))
1012: .booleanValue();
1013: if (isMetaDataMode()) {
1014: switch (pstrat) {
1015: case BASIC:
1016: parseBasic(fmd, (Basic) el.getAnnotation(Basic.class),
1017: lob);
1018: break;
1019: case MANY_ONE:
1020: parseManyToOne(fmd, (ManyToOne) el
1021: .getAnnotation(ManyToOne.class));
1022: break;
1023: case ONE_ONE:
1024: parseOneToOne(fmd, (OneToOne) el
1025: .getAnnotation(OneToOne.class));
1026: break;
1027: case EMBEDDED:
1028: parseEmbedded(fmd, (Embedded) el
1029: .getAnnotation(Embedded.class));
1030: break;
1031: case ONE_MANY:
1032: parseOneToMany(fmd, (OneToMany) el
1033: .getAnnotation(OneToMany.class));
1034: break;
1035: case MANY_MANY:
1036: parseManyToMany(fmd, (ManyToMany) el
1037: .getAnnotation(ManyToMany.class));
1038: break;
1039: case PERS:
1040: parsePersistent(fmd, (Persistent) el
1041: .getAnnotation(Persistent.class));
1042: break;
1043: case PERS_COLL:
1044: parsePersistentCollection(
1045: fmd,
1046: (PersistentCollection) el
1047: .getAnnotation(PersistentCollection.class));
1048: break;
1049: case PERS_MAP:
1050: parsePersistentMap(fmd, (PersistentMap) el
1051: .getAnnotation(PersistentMap.class));
1052: break;
1053: case TRANSIENT:
1054: break;
1055: default:
1056: throw new InternalException();
1057: }
1058: }
1059:
1060: if (isMappingOverrideMode() && lob)
1061: parseLobMapping(fmd);
1062:
1063: // extensions
1064: MetaDataTag tag;
1065: for (Annotation anno : el.getDeclaredAnnotations()) {
1066: tag = _tags.get(anno.annotationType());
1067: if (tag == null) {
1068: handleUnknownMemberAnnotation(fmd, anno);
1069: continue;
1070: }
1071:
1072: switch (tag) {
1073: case FLUSH_MODE:
1074: if (isMetaDataMode())
1075: warnFlushMode(fmd);
1076: break;
1077: case GENERATED_VALUE:
1078: if (isMappingOverrideMode())
1079: parseGeneratedValue(fmd, (GeneratedValue) anno);
1080: break;
1081: case ID:
1082: case EMBEDDED_ID:
1083: fmd.setPrimaryKey(true);
1084: break;
1085: case MAP_KEY:
1086: if (isMappingOverrideMode())
1087: parseMapKey(fmd, (MapKey) anno);
1088: break;
1089: case ORDER_BY:
1090: parseOrderBy(fmd, (OrderBy) el
1091: .getAnnotation(OrderBy.class));
1092: break;
1093: case SEQ_GENERATOR:
1094: if (isMappingOverrideMode())
1095: parseSequenceGenerator(el, (SequenceGenerator) anno);
1096: break;
1097: case VERSION:
1098: fmd.setVersion(true);
1099: break;
1100: case DEPENDENT:
1101: if (isMetaDataMode() && ((Dependent) anno).value())
1102: fmd.setCascadeDelete(ValueMetaData.CASCADE_AUTO);
1103: break;
1104: case ELEM_DEPENDENT:
1105: if (isMetaDataMode()
1106: && ((ElementDependent) anno).value())
1107: fmd.getElement().setCascadeDelete(
1108: ValueMetaData.CASCADE_AUTO);
1109: break;
1110: case ELEM_TYPE:
1111: if (isMetaDataMode())
1112: fmd.getElement()
1113: .setTypeOverride(
1114: toOverrideType(((ElementType) anno)
1115: .value()));
1116: break;
1117: case EXTERNAL_VALS:
1118: if (isMetaDataMode())
1119: fmd.setExternalValues(Strings.join(
1120: ((ExternalValues) anno).value(), ","));
1121: break;
1122: case EXTERNALIZER:
1123: if (isMetaDataMode())
1124: fmd.setExternalizer(((Externalizer) anno).value());
1125: break;
1126: case FACTORY:
1127: if (isMetaDataMode())
1128: fmd.setFactory(((Factory) anno).value());
1129: break;
1130: case INVERSE_LOGICAL:
1131: if (isMetaDataMode())
1132: fmd.setInverse(((InverseLogical) anno).value());
1133: break;
1134: case KEY_DEPENDENT:
1135: if (isMetaDataMode() && ((KeyDependent) anno).value())
1136: fmd.getKey().setCascadeDelete(
1137: ValueMetaData.CASCADE_AUTO);
1138: break;
1139: case KEY_TYPE:
1140: if (isMetaDataMode())
1141: fmd.getKey().setTypeOverride(
1142: toOverrideType(((KeyType) anno).value()));
1143: break;
1144: case LOAD_FETCH_GROUP:
1145: if (isMetaDataMode())
1146: fmd.setLoadFetchGroup(((LoadFetchGroup) anno)
1147: .value());
1148: break;
1149: case LRS:
1150: if (isMetaDataMode())
1151: fmd.setLRS(((LRS) anno).value());
1152: break;
1153: case READ_ONLY:
1154: if (isMetaDataMode())
1155: parseReadOnly(fmd, (ReadOnly) anno);
1156: break;
1157: case TYPE:
1158: if (isMetaDataMode())
1159: fmd.setTypeOverride(toOverrideType(((Type) anno)
1160: .value()));
1161: break;
1162: default:
1163: throw new UnsupportedException(_loc.get("unsupported",
1164: fmd, anno.toString()));
1165: }
1166: }
1167: }
1168:
1169: /**
1170: * Parse member mapping components.
1171: */
1172: protected void parseMemberMappingAnnotations(FieldMetaData fmd) {
1173: }
1174:
1175: /**
1176: * Allow subclasses to handle unknown annotations.
1177: */
1178: protected boolean handleUnknownMemberAnnotation(FieldMetaData fmd,
1179: Annotation anno) {
1180: return false;
1181: }
1182:
1183: /**
1184: * Convert the given class to its OpenJPA type override equivalent.
1185: */
1186: private static Class toOverrideType(Class cls) {
1187: return (cls == Entity.class) ? org.apache.openjpa.enhance.PersistenceCapable.class
1188: : cls;
1189: }
1190:
1191: /**
1192: * Parse @ReadOnly.
1193: */
1194: private void parseReadOnly(FieldMetaData fmd, ReadOnly ro) {
1195: if (ro.value() == UpdateAction.RESTRICT)
1196: fmd.setUpdateStrategy(UpdateStrategies.RESTRICT);
1197: else if (ro.value() == UpdateAction.IGNORE)
1198: fmd.setUpdateStrategy(UpdateStrategies.IGNORE);
1199: else
1200: throw new InternalException();
1201: }
1202:
1203: /**
1204: * Sets value generation information for the given field.
1205: */
1206: private void parseGeneratedValue(FieldMetaData fmd,
1207: GeneratedValue gen) {
1208: GenerationType strategy = gen.strategy();
1209: String generator = gen.generator();
1210: parseGeneratedValue(fmd, strategy, generator);
1211: }
1212:
1213: /**
1214: * Sets value generation information for the given field.
1215: */
1216: static void parseGeneratedValue(FieldMetaData fmd,
1217: GenerationType strategy, String generator) {
1218: int strat = getGeneratedValueStrategy(fmd, strategy, generator);
1219: if (strat != -1)
1220: fmd.setValueStrategy(strat);
1221: else {
1222: switch (strategy) {
1223: case TABLE:
1224: case SEQUENCE:
1225: // technically we should have separate system table and
1226: // sequence generators, but it's easier to just rely on
1227: // the system org.apache.openjpa.Sequence setting for both
1228: if (StringUtils.isEmpty(generator))
1229: fmd
1230: .setValueSequenceName(SequenceMetaData.NAME_SYSTEM);
1231: else
1232: fmd.setValueSequenceName(generator);
1233: break;
1234: case AUTO:
1235: fmd.setValueSequenceName(SequenceMetaData.NAME_SYSTEM);
1236: break;
1237: case IDENTITY:
1238: fmd.setValueStrategy(ValueStrategies.AUTOASSIGN);
1239: break;
1240: default:
1241: throw new UnsupportedException(strategy.toString());
1242: }
1243: }
1244: }
1245:
1246: /**
1247: * Return the value strategy for the given generator, or -1 if the
1248: * strategy depends on the <code>GenerationType</code> rather than the
1249: * generator name.
1250: */
1251: private static int getGeneratedValueStrategy(Object context,
1252: GenerationType strategy, String generator) {
1253: if (strategy != AUTO || StringUtils.isEmpty(generator))
1254: return -1;
1255:
1256: if (Generator.UUID_HEX.equals(generator))
1257: return ValueStrategies.UUID_HEX;
1258: if (Generator.UUID_STRING.equals(generator))
1259: return ValueStrategies.UUID_STRING;
1260: throw new MetaDataException(_loc.get("generator-bad-strategy",
1261: context, generator));
1262: }
1263:
1264: /**
1265: * Parse @Basic. Given annotation may be null.
1266: */
1267: private void parseBasic(FieldMetaData fmd, Basic anno, boolean lob) {
1268: Class type = fmd.getDeclaredType();
1269: if (lob && type != String.class && type != char[].class
1270: && type != Character[].class && type != byte[].class
1271: && type != Byte[].class)
1272: fmd.setSerialized(true);
1273: else if (!lob) {
1274: switch (fmd.getDeclaredTypeCode()) {
1275: case JavaTypes.OBJECT:
1276: if (Enum.class.isAssignableFrom(type))
1277: break;
1278: // else no break
1279: case JavaTypes.COLLECTION:
1280: case JavaTypes.MAP:
1281: case JavaTypes.PC:
1282: case JavaTypes.PC_UNTYPED:
1283: if (Serializable.class.isAssignableFrom(type))
1284: fmd.setSerialized(true);
1285: else
1286: throw new MetaDataException(_loc.get(
1287: "bad-meta-anno", fmd, "Basic"));
1288: break;
1289: case JavaTypes.ARRAY:
1290: if (type == char[].class || type == Character[].class
1291: || type == byte[].class || type == Byte[].class)
1292: break;
1293: if (Serializable.class.isAssignableFrom(type
1294: .getComponentType()))
1295: fmd.setSerialized(true);
1296: else
1297: throw new MetaDataException(_loc.get(
1298: "bad-meta-anno", fmd, "Basic"));
1299: break;
1300: }
1301: }
1302:
1303: if (anno == null)
1304: return;
1305: fmd.setInDefaultFetchGroup(anno.fetch() == FetchType.EAGER);
1306: if (!anno.optional())
1307: fmd.setNullValue(FieldMetaData.NULL_EXCEPTION);
1308: }
1309:
1310: /**
1311: * Parse @ManyToOne.
1312: */
1313: private void parseManyToOne(FieldMetaData fmd, ManyToOne anno) {
1314: if (!JavaTypes.maybePC(fmd.getValue()))
1315: throw new MetaDataException(_loc.get("bad-meta-anno", fmd,
1316: "ManyToOne"));
1317:
1318: // don't specifically exclude relation from DFG b/c that will prevent
1319: // us from even reading the fk when reading from the primary table,
1320: // which is not what most users will want
1321: if (anno.fetch() == FetchType.EAGER)
1322: fmd.setInDefaultFetchGroup(true);
1323: if (!anno.optional())
1324: fmd.setNullValue(FieldMetaData.NULL_EXCEPTION);
1325: if (anno.targetEntity() != void.class)
1326: fmd.setTypeOverride(anno.targetEntity());
1327: setCascades(fmd, anno.cascade());
1328: }
1329:
1330: /**
1331: * Parse @OneToOne.
1332: */
1333: private void parseOneToOne(FieldMetaData fmd, OneToOne anno) {
1334: if (!JavaTypes.maybePC(fmd.getValue()))
1335: throw new MetaDataException(_loc.get("bad-meta-anno", fmd,
1336: "OneToOne"));
1337:
1338: // don't specifically exclude relation from DFG b/c that will prevent
1339: // us from even reading the fk when reading from the primary table,
1340: // which is not what most users will want
1341: if (anno.fetch() == FetchType.EAGER)
1342: fmd.setInDefaultFetchGroup(true);
1343: if (!anno.optional())
1344: fmd.setNullValue(FieldMetaData.NULL_EXCEPTION);
1345:
1346: if (isMappingOverrideMode()
1347: && !StringUtils.isEmpty(anno.mappedBy()))
1348: fmd.setMappedBy(anno.mappedBy());
1349: if (anno.targetEntity() != void.class)
1350: fmd.setTypeOverride(anno.targetEntity());
1351: setCascades(fmd, anno.cascade());
1352: }
1353:
1354: /**
1355: * Parse @Embedded. Given annotation may be null.
1356: */
1357: private void parseEmbedded(FieldMetaData fmd, Embedded anno) {
1358: if (!JavaTypes.maybePC(fmd.getValue()))
1359: throw new MetaDataException(_loc.get("bad-meta-anno", fmd,
1360: "Embedded"));
1361:
1362: fmd.setInDefaultFetchGroup(true);
1363: fmd.setEmbedded(true);
1364: if (fmd.getEmbeddedMetaData() == null)
1365: fmd.addEmbeddedMetaData();
1366: }
1367:
1368: /**
1369: * Parse @OneToMany.
1370: */
1371: private void parseOneToMany(FieldMetaData fmd, OneToMany anno) {
1372: switch (fmd.getDeclaredTypeCode()) {
1373: case JavaTypes.ARRAY:
1374: case JavaTypes.COLLECTION:
1375: case JavaTypes.MAP:
1376: if (JavaTypes.maybePC(fmd.getElement()))
1377: break;
1378: // no break
1379: default:
1380: throw new MetaDataException(_loc.get("bad-meta-anno", fmd,
1381: "OneToMany"));
1382: }
1383:
1384: fmd.setInDefaultFetchGroup(anno.fetch() == FetchType.EAGER);
1385: if (isMappingOverrideMode()
1386: && !StringUtils.isEmpty(anno.mappedBy()))
1387: fmd.setMappedBy(anno.mappedBy());
1388: if (anno.targetEntity() != void.class)
1389: fmd.getElement().setDeclaredType(anno.targetEntity());
1390: setCascades(fmd.getElement(), anno.cascade());
1391: }
1392:
1393: /**
1394: * Parse @ManyToMany.
1395: */
1396: private void parseManyToMany(FieldMetaData fmd, ManyToMany anno) {
1397: switch (fmd.getDeclaredTypeCode()) {
1398: case JavaTypes.ARRAY:
1399: case JavaTypes.COLLECTION:
1400: case JavaTypes.MAP:
1401: if (JavaTypes.maybePC(fmd.getElement()))
1402: break;
1403: // no break
1404: default:
1405: throw new MetaDataException(_loc.get("bad-meta-anno", fmd,
1406: "OneToMany"));
1407: }
1408:
1409: fmd.setInDefaultFetchGroup(anno.fetch() == FetchType.EAGER);
1410: if (isMappingOverrideMode()
1411: && !StringUtils.isEmpty(anno.mappedBy()))
1412: fmd.setMappedBy(anno.mappedBy());
1413: if (anno.targetEntity() != void.class)
1414: fmd.getElement().setDeclaredType(anno.targetEntity());
1415: setCascades(fmd.getElement(), anno.cascade());
1416: }
1417:
1418: /**
1419: * Parse @MapKey.
1420: */
1421: private void parseMapKey(FieldMetaData fmd, MapKey anno) {
1422: String name = anno.name();
1423: if (StringUtils.isEmpty(name))
1424: fmd.getKey().setValueMappedBy(ValueMetaData.MAPPED_BY_PK);
1425: else
1426: fmd.getKey().setValueMappedBy(name);
1427: }
1428:
1429: /**
1430: * Setup the field as a LOB mapping.
1431: */
1432: protected void parseLobMapping(FieldMetaData fmd) {
1433: }
1434:
1435: /**
1436: * Parse @OrderBy.
1437: */
1438: private void parseOrderBy(FieldMetaData fmd, OrderBy anno) {
1439: String dec = anno.value();
1440: if (dec.length() == 0)
1441: dec = Order.ELEMENT + " asc";
1442: fmd.setOrderDeclaration(dec);
1443: }
1444:
1445: /**
1446: * Parse @Persistent.
1447: */
1448: private void parsePersistent(FieldMetaData fmd, Persistent anno) {
1449: switch (fmd.getDeclaredTypeCode()) {
1450: case JavaTypes.ARRAY:
1451: if (fmd.getDeclaredType() == byte[].class
1452: || fmd.getDeclaredType() == Byte[].class
1453: || fmd.getDeclaredType() == char[].class
1454: || fmd.getDeclaredType() == Character[].class)
1455: break;
1456: // no break
1457: case JavaTypes.COLLECTION:
1458: case JavaTypes.MAP:
1459: throw new MetaDataException(_loc.get("bad-meta-anno", fmd,
1460: "Persistent"));
1461: }
1462:
1463: if (!StringUtils.isEmpty(anno.mappedBy()))
1464: fmd.setMappedBy(anno.mappedBy());
1465: fmd.setInDefaultFetchGroup(anno.fetch() == FetchType.EAGER);
1466: if (!anno.optional())
1467: fmd.setNullValue(FieldMetaData.NULL_EXCEPTION);
1468: setCascades(fmd, anno.cascade());
1469: if (anno.embedded()) {
1470: if (!JavaTypes.maybePC(fmd.getValue()))
1471: throw new MetaDataException(_loc.get("bad-meta-anno",
1472: fmd, "Persistent(embedded=true)"));
1473: fmd.setEmbedded(true);
1474: if (fmd.getEmbeddedMetaData() == null)
1475: fmd.addEmbeddedMetaData();
1476: }
1477: }
1478:
1479: /**
1480: * Parse @PersistentCollection.
1481: */
1482: private void parsePersistentCollection(FieldMetaData fmd,
1483: PersistentCollection anno) {
1484: if (fmd.getDeclaredTypeCode() != JavaTypes.ARRAY
1485: && fmd.getDeclaredTypeCode() != JavaTypes.COLLECTION)
1486: throw new MetaDataException(_loc.get("bad-meta-anno", fmd,
1487: "PersistentCollection"));
1488:
1489: if (!StringUtils.isEmpty(anno.mappedBy()))
1490: fmd.setMappedBy(anno.mappedBy());
1491: fmd.setInDefaultFetchGroup(anno.fetch() == FetchType.EAGER);
1492: if (anno.elementType() != void.class)
1493: fmd.getElement().setDeclaredType(anno.elementType());
1494: setCascades(fmd.getElement(), anno.elementCascade());
1495: if (anno.elementEmbedded()) {
1496: if (!JavaTypes.maybePC(fmd.getElement()))
1497: throw new MetaDataException(_loc.get("bad-meta-anno",
1498: fmd,
1499: "PersistentCollection(embeddedElement=true)"));
1500: fmd.getElement().setEmbedded(true);
1501: if (fmd.getElement().getEmbeddedMetaData() == null)
1502: fmd.getElement().addEmbeddedMetaData();
1503: }
1504: }
1505:
1506: /**
1507: * Parse @PersistentMap.
1508: */
1509: private void parsePersistentMap(FieldMetaData fmd,
1510: PersistentMap anno) {
1511: if (fmd.getDeclaredTypeCode() != JavaTypes.MAP)
1512: throw new MetaDataException(_loc.get("bad-meta-anno", fmd,
1513: "PersistentMap"));
1514:
1515: fmd.setInDefaultFetchGroup(anno.fetch() == FetchType.EAGER);
1516: if (anno.keyType() != void.class)
1517: fmd.getKey().setDeclaredType(anno.keyType());
1518: if (anno.elementType() != void.class)
1519: fmd.getElement().setDeclaredType(anno.elementType());
1520: setCascades(fmd.getKey(), anno.keyCascade());
1521: setCascades(fmd.getElement(), anno.elementCascade());
1522: if (anno.keyEmbedded()) {
1523: if (!JavaTypes.maybePC(fmd.getKey()))
1524: throw new MetaDataException(_loc.get("bad-meta-anno",
1525: fmd, "PersistentMap(embeddedKey=true)"));
1526: fmd.getKey().setEmbedded(true);
1527: if (fmd.getKey().getEmbeddedMetaData() == null)
1528: fmd.getKey().addEmbeddedMetaData();
1529: }
1530: if (anno.elementEmbedded()) {
1531: if (!JavaTypes.maybePC(fmd.getElement()))
1532: throw new MetaDataException(_loc.get("bad-meta-anno",
1533: fmd, "PersistentMap(embeddedValue=true)"));
1534: fmd.getElement().setEmbedded(true);
1535: if (fmd.getElement().getEmbeddedMetaData() == null)
1536: fmd.getElement().addEmbeddedMetaData();
1537: }
1538: }
1539:
1540: /**
1541: * Set cascades on relation.
1542: */
1543: private void setCascades(ValueMetaData vmd, CascadeType[] cascades) {
1544: for (CascadeType cascade : cascades) {
1545: if (cascade == CascadeType.ALL
1546: || cascade == CascadeType.REMOVE)
1547: vmd.setCascadeDelete(ValueMetaData.CASCADE_IMMEDIATE);
1548: if (cascade == CascadeType.ALL
1549: || cascade == CascadeType.PERSIST)
1550: vmd.setCascadePersist(ValueMetaData.CASCADE_IMMEDIATE);
1551: if (cascade == CascadeType.ALL
1552: || cascade == CascadeType.MERGE)
1553: vmd.setCascadeAttach(ValueMetaData.CASCADE_IMMEDIATE);
1554: if (cascade == CascadeType.ALL
1555: || cascade == CascadeType.REFRESH)
1556: vmd.setCascadeRefresh(ValueMetaData.CASCADE_IMMEDIATE);
1557: }
1558: }
1559:
1560: /**
1561: * Parse @SequenceGenerator.
1562: */
1563: private void parseSequenceGenerator(AnnotatedElement el,
1564: SequenceGenerator gen) {
1565: String name = gen.name();
1566: if (StringUtils.isEmpty(name))
1567: throw new MetaDataException(_loc.get("no-seq-name", el));
1568:
1569: if (_log.isTraceEnabled())
1570: _log.trace(_loc.get("parse-sequence", name));
1571:
1572: SequenceMetaData meta = getRepository()
1573: .getCachedSequenceMetaData(name);
1574: if (meta != null) {
1575: if (_log.isWarnEnabled())
1576: _log.warn(_loc.get("dup-sequence", name, el));
1577: return;
1578: }
1579:
1580: // create new sequence
1581: meta = getRepository().addSequenceMetaData(name);
1582: String seq = gen.sequenceName();
1583: int initial = gen.initialValue();
1584: int allocate = gen.allocationSize();
1585: // don't allow initial of 0 b/c looks like def value
1586: if (initial == 0)
1587: initial = 1;
1588:
1589: // create plugin string from info
1590: String clsName, props;
1591: if (StringUtils.isEmpty(seq)) {
1592: clsName = SequenceMetaData.IMPL_NATIVE;
1593: props = null;
1594: } else if (seq.indexOf('(') != -1) // plugin
1595: {
1596: clsName = Configurations.getClassName(seq);
1597: props = Configurations.getProperties(seq);
1598: seq = null;
1599: } else {
1600: clsName = SequenceMetaData.IMPL_NATIVE;
1601: props = null;
1602: }
1603:
1604: meta
1605: .setSequencePlugin(Configurations.getPlugin(clsName,
1606: props));
1607: meta.setSequence(seq);
1608: meta.setInitialValue(initial);
1609: meta.setAllocate(allocate);
1610: meta.setSource(getSourceFile(), (el instanceof Class) ? el
1611: : null, meta.SRC_ANNOTATIONS);
1612: }
1613:
1614: /**
1615: * Parse @NamedQuery.
1616: */
1617: private void parseNamedQueries(AnnotatedElement el,
1618: NamedQuery... queries) {
1619: QueryMetaData meta;
1620: for (NamedQuery query : queries) {
1621: if (StringUtils.isEmpty(query.name()))
1622: throw new MetaDataException(_loc.get("no-query-name",
1623: el));
1624: if (StringUtils.isEmpty(query.query()))
1625: throw new MetaDataException(_loc.get("no-query-string",
1626: query.name(), el));
1627:
1628: if (_log.isTraceEnabled())
1629: _log.trace(_loc.get("parse-query", query.name()));
1630:
1631: meta = getRepository().getCachedQueryMetaData(null,
1632: query.name());
1633: if (meta != null) {
1634: if (_log.isWarnEnabled())
1635: _log.warn(_loc.get("dup-query", query.name(), el));
1636: return;
1637: }
1638:
1639: meta = getRepository().addQueryMetaData(null, query.name());
1640: meta.setQueryString(query.query());
1641: meta.setLanguage(JPQLParser.LANG_JPQL);
1642: for (QueryHint hint : query.hints())
1643: meta.addHint(hint.name(), hint.value());
1644:
1645: meta.setSource(getSourceFile(), (el instanceof Class) ? el
1646: : null, meta.SRC_ANNOTATIONS);
1647: if (isMetaDataMode())
1648: meta.setSourceMode(MODE_META);
1649: else if (isMappingMode())
1650: meta.setSourceMode(MODE_MAPPING);
1651: else
1652: meta.setSourceMode(MODE_QUERY);
1653: }
1654: }
1655:
1656: /**
1657: * Parse @NamedNativeQuery.
1658: */
1659: private void parseNamedNativeQueries(AnnotatedElement el,
1660: NamedNativeQuery... queries) {
1661: QueryMetaData meta;
1662: for (NamedNativeQuery query : queries) {
1663: if (StringUtils.isEmpty(query.name()))
1664: throw new MetaDataException(_loc.get(
1665: "no-native-query-name", el));
1666: if (StringUtils.isEmpty(query.query()))
1667: throw new MetaDataException(_loc.get(
1668: "no-native-query-string", query.name(), el));
1669:
1670: if (_log.isTraceEnabled())
1671: _log
1672: .trace(_loc.get("parse-native-query", query
1673: .name()));
1674:
1675: meta = getRepository().getCachedQueryMetaData(null,
1676: query.name());
1677: if (meta != null) {
1678: if (_log.isWarnEnabled())
1679: _log.warn(_loc.get("dup-query", query.name(), el));
1680: return;
1681: }
1682:
1683: meta = getRepository().addQueryMetaData(null, query.name());
1684: meta.setQueryString(query.query());
1685: meta.setLanguage(QueryLanguages.LANG_SQL);
1686: Class res = query.resultClass();
1687: if (ImplHelper.isManagedType(getConfiguration(), res))
1688: meta.setCandidateType(res);
1689: else if (!void.class.equals(res))
1690: meta.setResultType(res);
1691:
1692: if (!StringUtils.isEmpty(query.resultSetMapping()))
1693: meta.setResultSetMappingName(query.resultSetMapping());
1694:
1695: meta.setSource(getSourceFile(), (el instanceof Class) ? el
1696: : null, meta.SRC_ANNOTATIONS);
1697: if (isMetaDataMode())
1698: meta.setSourceMode(MODE_META);
1699: else if (isMappingMode())
1700: meta.setSourceMode(MODE_MAPPING);
1701: else
1702: meta.setSourceMode(MODE_QUERY);
1703: }
1704: }
1705:
1706: private static class MethodKey {
1707:
1708: private final Method _method;
1709:
1710: public MethodKey(Method m) {
1711: _method = m;
1712: }
1713:
1714: public int hashCode() {
1715: int code = 46 * 12 + _method.getName().hashCode();
1716: for (Class param : _method.getParameterTypes())
1717: code = 46 * code + param.hashCode();
1718: return code;
1719: }
1720:
1721: public boolean equals(Object o) {
1722: if (!(o instanceof MethodKey))
1723: return false;
1724: Method other = ((MethodKey) o)._method;
1725: if (!_method.getName().equals(other.getName()))
1726: return false;
1727: return Arrays.equals(_method.getParameterTypes(), other
1728: .getParameterTypes());
1729: }
1730: }
1731:
1732: private static class MethodComparator implements Comparator {
1733:
1734: private static MethodComparator INSTANCE = null;
1735:
1736: public static MethodComparator getInstance() {
1737: if (INSTANCE == null)
1738: INSTANCE = new MethodComparator();
1739: return INSTANCE;
1740: }
1741:
1742: public int compare(Object o1, Object o2) {
1743: Method m1 = (Method) o1;
1744: Method m2 = (Method) o2;
1745:
1746: Class c1 = m1.getDeclaringClass();
1747: Class c2 = m2.getDeclaringClass();
1748: if (!c1.equals(c2)) {
1749: if (c1.isAssignableFrom(c2))
1750: return -1;
1751: else
1752: return 1;
1753: }
1754: int compare = m1.getName().compareTo(m2.getName());
1755: if (compare == 0)
1756: return m1.hashCode() - m2.hashCode();
1757: return compare;
1758: }
1759: }
1760: }
|