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.jdbc;
0020:
0021: import java.lang.annotation.Annotation;
0022: import java.lang.reflect.AnnotatedElement;
0023: import java.lang.reflect.Modifier;
0024: import java.security.AccessController;
0025: import java.sql.Types;
0026: import java.util.ArrayList;
0027: import java.util.Arrays;
0028: import java.util.HashMap;
0029: import java.util.List;
0030: import java.util.Map;
0031: import javax.persistence.AssociationOverride;
0032: import javax.persistence.AssociationOverrides;
0033: import javax.persistence.AttributeOverride;
0034: import javax.persistence.AttributeOverrides;
0035: import javax.persistence.ColumnResult;
0036: import javax.persistence.DiscriminatorColumn;
0037: import javax.persistence.DiscriminatorValue;
0038: import javax.persistence.EntityResult;
0039: import javax.persistence.EnumType;
0040: import javax.persistence.Enumerated;
0041: import javax.persistence.FieldResult;
0042: import javax.persistence.Inheritance;
0043: import javax.persistence.JoinColumn;
0044: import javax.persistence.JoinColumns;
0045: import javax.persistence.JoinTable;
0046: import javax.persistence.PrimaryKeyJoinColumn;
0047: import javax.persistence.PrimaryKeyJoinColumns;
0048: import javax.persistence.SecondaryTable;
0049: import javax.persistence.SecondaryTables;
0050: import javax.persistence.SqlResultSetMapping;
0051: import javax.persistence.SqlResultSetMappings;
0052: import javax.persistence.Table;
0053: import javax.persistence.TableGenerator;
0054: import javax.persistence.Temporal;
0055: import javax.persistence.UniqueConstraint;
0056:
0057: import org.apache.commons.lang.StringUtils;
0058: import org.apache.openjpa.jdbc.conf.JDBCConfiguration;
0059: import org.apache.openjpa.jdbc.kernel.EagerFetchModes;
0060: import org.apache.openjpa.jdbc.meta.ClassMapping;
0061: import org.apache.openjpa.jdbc.meta.ClassMappingInfo;
0062: import org.apache.openjpa.jdbc.meta.Discriminator;
0063: import org.apache.openjpa.jdbc.meta.FieldMapping;
0064: import org.apache.openjpa.jdbc.meta.MappingInfo;
0065: import org.apache.openjpa.jdbc.meta.MappingRepository;
0066: import org.apache.openjpa.jdbc.meta.QueryResultMapping;
0067: import org.apache.openjpa.jdbc.meta.SequenceMapping;
0068: import org.apache.openjpa.jdbc.meta.ValueMapping;
0069: import org.apache.openjpa.jdbc.meta.ValueMappingInfo;
0070: import org.apache.openjpa.jdbc.meta.strats.EnumValueHandler;
0071: import org.apache.openjpa.jdbc.meta.strats.FlatClassStrategy;
0072: import org.apache.openjpa.jdbc.meta.strats.FullClassStrategy;
0073: import org.apache.openjpa.jdbc.meta.strats.VerticalClassStrategy;
0074: import org.apache.openjpa.jdbc.schema.Column;
0075: import org.apache.openjpa.jdbc.schema.Unique;
0076: import org.apache.openjpa.jdbc.sql.DBDictionary;
0077: import org.apache.openjpa.lib.log.Log;
0078: import org.apache.openjpa.lib.util.J2DoPriv5Helper;
0079: import org.apache.openjpa.lib.util.Localizer;
0080: import org.apache.openjpa.meta.ClassMetaData;
0081: import org.apache.openjpa.meta.FieldMetaData;
0082: import org.apache.openjpa.meta.JavaTypes;
0083: import org.apache.openjpa.persistence.AnnotationPersistenceMetaDataParser;
0084: import static org.apache.openjpa.persistence.jdbc.MappingTag.*;
0085: import org.apache.openjpa.util.InternalException;
0086: import org.apache.openjpa.util.MetaDataException;
0087: import org.apache.openjpa.util.UnsupportedException;
0088: import org.apache.openjpa.util.UserException;
0089:
0090: /**
0091: * Persistence annotation mapping parser.
0092: *
0093: * @author Pinaki Poddar
0094: * @author Steve Kim
0095: * @author Abe White
0096: * @nojavadoc
0097: */
0098: public class AnnotationPersistenceMappingParser extends
0099: AnnotationPersistenceMetaDataParser {
0100:
0101: protected static final int TRUE = 1;
0102: protected static final int FALSE = 2;
0103:
0104: private static final Localizer _loc = Localizer
0105: .forPackage(AnnotationPersistenceMappingParser.class);
0106:
0107: private static final Map<Class, MappingTag> _tags = new HashMap<Class, MappingTag>();
0108:
0109: static {
0110: _tags.put(AssociationOverride.class, ASSOC_OVERRIDE);
0111: _tags.put(AssociationOverrides.class, ASSOC_OVERRIDES);
0112: _tags.put(AttributeOverride.class, ATTR_OVERRIDE);
0113: _tags.put(AttributeOverrides.class, ATTR_OVERRIDES);
0114: _tags.put(javax.persistence.Column.class, COL);
0115: _tags.put(ColumnResult.class, COLUMN_RESULT);
0116: _tags.put(DiscriminatorColumn.class, DISCRIM_COL);
0117: _tags.put(DiscriminatorValue.class, DISCRIM_VAL);
0118: _tags.put(ElementColumn.class, ELEM_COL);
0119: _tags.put(ElementColumns.class, ELEM_COLS);
0120: _tags.put(ElementEmbeddedMapping.class, ELEM_EMBEDDED_MAPPING);
0121: _tags.put(ElementStrategy.class, ELEM_STRAT);
0122: _tags.put(EntityResult.class, ENTITY_RESULT);
0123: _tags.put(Enumerated.class, ENUMERATED);
0124: _tags.put(FieldResult.class, FIELD_RESULT);
0125: _tags.put(Inheritance.class, INHERITANCE);
0126: _tags.put(JoinColumn.class, JOIN_COL);
0127: _tags.put(JoinColumns.class, JOIN_COLS);
0128: _tags.put(JoinTable.class, JOIN_TABLE);
0129: _tags.put(KeyColumn.class, KEY_COL);
0130: _tags.put(KeyColumns.class, KEY_COLS);
0131: _tags.put(KeyClassCriteria.class, KEY_CLASS_CRIT);
0132: _tags.put(KeyEmbeddedMapping.class, KEY_EMBEDDED_MAPPING);
0133: _tags.put(KeyForeignKey.class, KEY_FK);
0134: _tags.put(KeyIndex.class, KEY_INDEX);
0135: _tags.put(KeyJoinColumn.class, KEY_JOIN_COL);
0136: _tags.put(KeyJoinColumns.class, KEY_JOIN_COLS);
0137: _tags.put(KeyNonpolymorphic.class, KEY_NONPOLY);
0138: _tags.put(KeyStrategy.class, KEY_STRAT);
0139: _tags.put(PrimaryKeyJoinColumn.class, PK_JOIN_COL);
0140: _tags.put(PrimaryKeyJoinColumns.class, PK_JOIN_COLS);
0141: _tags.put(SecondaryTable.class, SECONDARY_TABLE);
0142: _tags.put(SecondaryTables.class, SECONDARY_TABLES);
0143: _tags.put(SqlResultSetMapping.class, SQL_RESULT_SET_MAPPING);
0144: _tags.put(SqlResultSetMappings.class, SQL_RESULT_SET_MAPPINGS);
0145: _tags.put(Table.class, TABLE);
0146: _tags.put(Temporal.class, TEMPORAL);
0147: _tags.put(TableGenerator.class, TABLE_GEN);
0148: _tags.put(ClassCriteria.class, CLASS_CRIT);
0149: _tags.put(Columns.class, COLS);
0150: _tags.put(ContainerTable.class, CONTAINER_TABLE);
0151: _tags.put(DataStoreIdColumn.class, DATASTORE_ID_COL);
0152: _tags.put(DiscriminatorStrategy.class, DISCRIM_STRAT);
0153: _tags.put(EagerFetchMode.class, EAGER_FETCH_MODE);
0154: _tags.put(ElementClassCriteria.class, ELEM_CLASS_CRIT);
0155: _tags.put(ElementForeignKey.class, ELEM_FK);
0156: _tags.put(ElementIndex.class, ELEM_INDEX);
0157: _tags.put(ElementJoinColumn.class, ELEM_JOIN_COL);
0158: _tags.put(ElementJoinColumns.class, ELEM_JOIN_COLS);
0159: _tags.put(ElementNonpolymorphic.class, ELEM_NONPOLY);
0160: _tags.put(EmbeddedMapping.class, EMBEDDED_MAPPING);
0161: _tags.put(ForeignKey.class, FK);
0162: _tags.put(Index.class, INDEX);
0163: _tags.put(MappingOverride.class, MAPPING_OVERRIDE);
0164: _tags.put(MappingOverrides.class, MAPPING_OVERRIDES);
0165: _tags.put(Nonpolymorphic.class, NONPOLY);
0166: _tags.put(OrderColumn.class, ORDER_COL);
0167: _tags.put(Strategy.class, STRAT);
0168: _tags.put(SubclassFetchMode.class, SUBCLASS_FETCH_MODE);
0169: _tags.put(Unique.class, UNIQUE);
0170: _tags.put(VersionColumn.class, VERSION_COL);
0171: _tags.put(VersionColumns.class, VERSION_COLS);
0172: _tags.put(VersionStrategy.class, VERSION_STRAT);
0173: _tags.put(XEmbeddedMapping.class, X_EMBEDDED_MAPPING);
0174: _tags.put(XJoinColumn.class, X_JOIN_COL);
0175: _tags.put(XJoinColumns.class, X_JOIN_COLS);
0176: _tags.put(XMappingOverride.class, X_MAPPING_OVERRIDE);
0177: _tags.put(XMappingOverrides.class, X_MAPPING_OVERRIDES);
0178: _tags.put(XSecondaryTable.class, X_SECONDARY_TABLE);
0179: _tags.put(XSecondaryTables.class, X_SECONDARY_TABLES);
0180: _tags.put(XTable.class, X_TABLE);
0181: }
0182:
0183: public AnnotationPersistenceMappingParser(JDBCConfiguration conf) {
0184: super (conf);
0185: }
0186:
0187: @Override
0188: protected void parsePackageMappingAnnotations(Package pkg) {
0189: MappingTag tag;
0190: for (Annotation anno : pkg.getDeclaredAnnotations()) {
0191: tag = _tags.get(anno.annotationType());
0192: if (tag == null) {
0193: handleUnknownPackageMappingAnnotation(pkg, anno);
0194: continue;
0195: }
0196:
0197: switch (tag) {
0198: case TABLE_GEN:
0199: parseTableGenerator(pkg, (TableGenerator) anno);
0200: break;
0201: default:
0202: throw new UnsupportedException(_loc.get("unsupported",
0203: pkg, anno.toString()));
0204: }
0205: }
0206: }
0207:
0208: /**
0209: * Allow subclasses to handle unknown annotations.
0210: */
0211: protected boolean handleUnknownPackageMappingAnnotation(
0212: Package pkg, Annotation anno) {
0213: return false;
0214: }
0215:
0216: /**
0217: * Parse @TableGenerator.
0218: */
0219: private void parseTableGenerator(AnnotatedElement el,
0220: TableGenerator gen) {
0221: String name = gen.name();
0222: if (StringUtils.isEmpty(name))
0223: throw new MetaDataException(_loc.get("no-gen-name", el));
0224:
0225: Log log = getLog();
0226: if (log.isTraceEnabled())
0227: log.trace(_loc.get("parse-gen", name));
0228:
0229: SequenceMapping meta = (SequenceMapping) getRepository()
0230: .getCachedSequenceMetaData(name);
0231: if (meta != null) {
0232: if (log.isWarnEnabled())
0233: log.warn(_loc.get("dup-gen", name, el));
0234: return;
0235: }
0236:
0237: meta = (SequenceMapping) getRepository().addSequenceMetaData(
0238: name);
0239: meta.setSequencePlugin(SequenceMapping.IMPL_VALUE_TABLE);
0240: meta.setTable(toTableName(gen.schema(), gen.table()));
0241: meta.setPrimaryKeyColumn(gen.pkColumnName());
0242: meta.setSequenceColumn(gen.valueColumnName());
0243: meta.setPrimaryKeyValue(gen.pkColumnValue());
0244: meta.setInitialValue(gen.initialValue());
0245: meta.setAllocate(gen.allocationSize());
0246: meta.setSource(getSourceFile(), (el instanceof Class) ? el
0247: : null, meta.SRC_ANNOTATIONS);
0248:
0249: //### EJB3
0250: if (gen.uniqueConstraints().length > 0 && log.isWarnEnabled())
0251: log.warn(_loc.get("unique-constraints", name));
0252: }
0253:
0254: @Override
0255: protected void parseClassMappingAnnotations(ClassMetaData meta) {
0256: ClassMapping cm = (ClassMapping) meta;
0257: Class cls = cm.getDescribedType();
0258:
0259: MappingTag tag;
0260: for (Annotation anno : cls.getDeclaredAnnotations()) {
0261: tag = _tags.get(anno.annotationType());
0262: if (tag == null) {
0263: handleUnknownClassMappingAnnotation(cm, anno);
0264: continue;
0265: }
0266:
0267: switch (tag) {
0268: case ASSOC_OVERRIDE:
0269: parseAssociationOverrides(cm,
0270: (AssociationOverride) anno);
0271: break;
0272: case ASSOC_OVERRIDES:
0273: parseAssociationOverrides(cm,
0274: ((AssociationOverrides) anno).value());
0275: break;
0276: case ATTR_OVERRIDE:
0277: parseAttributeOverrides(cm, (AttributeOverride) anno);
0278: break;
0279: case ATTR_OVERRIDES:
0280: parseAttributeOverrides(cm, ((AttributeOverrides) anno)
0281: .value());
0282: break;
0283: case DISCRIM_COL:
0284: parseDiscriminatorColumn(cm, (DiscriminatorColumn) anno);
0285: break;
0286: case DISCRIM_VAL:
0287: cm.getDiscriminator().getMappingInfo().setValue(
0288: ((DiscriminatorValue) anno).value());
0289: if (Modifier.isAbstract(cm.getDescribedType()
0290: .getModifiers())
0291: && getLog().isInfoEnabled()) {
0292: getLog().info(
0293: _loc.get("discriminator-on-abstract-class",
0294: cm.getDescribedType().getName()));
0295: }
0296: break;
0297: case INHERITANCE:
0298: parseInheritance(cm, (Inheritance) anno);
0299: break;
0300: case PK_JOIN_COL:
0301: parsePrimaryKeyJoinColumns(cm,
0302: (PrimaryKeyJoinColumn) anno);
0303: break;
0304: case PK_JOIN_COLS:
0305: parsePrimaryKeyJoinColumns(cm,
0306: ((PrimaryKeyJoinColumns) anno).value());
0307: break;
0308: case SECONDARY_TABLE:
0309: parseSecondaryTables(cm, (SecondaryTable) anno);
0310: break;
0311: case SECONDARY_TABLES:
0312: parseSecondaryTables(cm, ((SecondaryTables) anno)
0313: .value());
0314: break;
0315: case SQL_RESULT_SET_MAPPING:
0316: parseSQLResultSetMappings(cm,
0317: (SqlResultSetMapping) anno);
0318: break;
0319: case SQL_RESULT_SET_MAPPINGS:
0320: parseSQLResultSetMappings(cm,
0321: ((SqlResultSetMappings) anno).value());
0322: break;
0323: case TABLE:
0324: parseTable(cm, (Table) anno);
0325: break;
0326: case TABLE_GEN:
0327: parseTableGenerator(cls, (TableGenerator) anno);
0328: break;
0329: case DATASTORE_ID_COL:
0330: parseDataStoreIdColumn(cm, (DataStoreIdColumn) anno);
0331: break;
0332: case DISCRIM_STRAT:
0333: cm.getDiscriminator().getMappingInfo().setStrategy(
0334: ((DiscriminatorStrategy) anno).value());
0335: break;
0336: case FK:
0337: parseForeignKey(cm.getMappingInfo(), (ForeignKey) anno);
0338: break;
0339: case MAPPING_OVERRIDE:
0340: parseMappingOverrides(cm, (MappingOverride) anno);
0341: break;
0342: case MAPPING_OVERRIDES:
0343: parseMappingOverrides(cm, ((MappingOverrides) anno)
0344: .value());
0345: break;
0346: case STRAT:
0347: cm.getMappingInfo().setStrategy(
0348: ((Strategy) anno).value());
0349: break;
0350: case SUBCLASS_FETCH_MODE:
0351: cm
0352: .setSubclassFetchMode(toEagerFetchModeConstant(((SubclassFetchMode) anno)
0353: .value()));
0354: break;
0355: case VERSION_COL:
0356: parseVersionColumns(cm, (VersionColumn) anno);
0357: break;
0358: case VERSION_COLS:
0359: parseVersionColumns(cm, ((VersionColumns) anno).value());
0360: break;
0361: case VERSION_STRAT:
0362: cm.getVersion().getMappingInfo().setStrategy(
0363: ((VersionStrategy) anno).value());
0364: break;
0365: case X_MAPPING_OVERRIDE:
0366: parseMappingOverrides(cm, (XMappingOverride) anno);
0367: break;
0368: case X_MAPPING_OVERRIDES:
0369: parseMappingOverrides(cm, ((XMappingOverrides) anno)
0370: .value());
0371: break;
0372: case X_TABLE:
0373: case X_SECONDARY_TABLE:
0374: case X_SECONDARY_TABLES:
0375: // no break; not supported yet
0376: default:
0377: throw new UnsupportedException(_loc.get("unsupported",
0378: cm, anno));
0379: }
0380: }
0381: }
0382:
0383: /**
0384: * Allow subclasses to handle unknown annotations.
0385: */
0386: protected boolean handleUnknownClassMappingAnnotation(
0387: ClassMapping cls, Annotation anno) {
0388: return false;
0389: }
0390:
0391: /**
0392: * Parse @AssociationOverride(s).
0393: */
0394: private void parseAssociationOverrides(ClassMapping cm,
0395: AssociationOverride... assocs) {
0396: FieldMapping sup;
0397: JoinColumn[] scols;
0398: int unique;
0399: List<Column> jcols;
0400: for (AssociationOverride assoc : assocs) {
0401: if (StringUtils.isEmpty(assoc.name()))
0402: throw new MetaDataException(_loc.get(
0403: "no-override-name", cm));
0404: sup = (FieldMapping) cm.getDefinedSuperclassField(assoc
0405: .name());
0406: if (sup == null)
0407: sup = (FieldMapping) cm.addDefinedSuperclassField(assoc
0408: .name(), Object.class, Object.class);
0409: scols = assoc.joinColumns();
0410: if (scols == null || scols.length == 0)
0411: continue;
0412:
0413: jcols = new ArrayList<Column>(scols.length);
0414: unique = 0;
0415: for (JoinColumn scol : scols) {
0416: unique |= (scol.unique()) ? TRUE : FALSE;
0417: jcols.add(newColumn(scol));
0418: }
0419: setColumns(sup, sup.getValueInfo(), jcols, unique);
0420: }
0421: }
0422:
0423: /**
0424: * Parse @AttributeOverride(s).
0425: */
0426: private void parseAttributeOverrides(ClassMapping cm,
0427: AttributeOverride... attrs) {
0428: FieldMapping sup;
0429: for (AttributeOverride attr : attrs) {
0430: if (StringUtils.isEmpty(attr.name()))
0431: throw new MetaDataException(_loc.get(
0432: "no-override-name", cm));
0433: sup = (FieldMapping) cm.getDefinedSuperclassField(attr
0434: .name());
0435: if (sup == null)
0436: sup = (FieldMapping) cm.addDefinedSuperclassField(attr
0437: .name(), Object.class, Object.class);
0438: if (attr.column() != null)
0439: parseColumns(sup, attr.column());
0440: }
0441: }
0442:
0443: /**
0444: * Parse inheritance @PrimaryKeyJoinColumn(s).
0445: */
0446: private void parsePrimaryKeyJoinColumns(ClassMapping cm,
0447: PrimaryKeyJoinColumn... joins) {
0448: List<Column> cols = new ArrayList<Column>(joins.length);
0449: for (PrimaryKeyJoinColumn join : joins)
0450: cols.add(newColumn(join));
0451: cm.getMappingInfo().setColumns(cols);
0452: }
0453:
0454: /**
0455: * Create a new schema column with information from the given annotation.
0456: */
0457: private static Column newColumn(PrimaryKeyJoinColumn join) {
0458: Column col = new Column();
0459: col.setFlag(Column.FLAG_PK_JOIN, true);
0460: if (!StringUtils.isEmpty(join.name()))
0461: col.setName(join.name());
0462: if (!StringUtils.isEmpty(join.columnDefinition()))
0463: col.setTypeName(join.columnDefinition());
0464: if (!StringUtils.isEmpty(join.referencedColumnName()))
0465: col.setTarget(join.referencedColumnName());
0466: return col;
0467: }
0468:
0469: /**
0470: * Parse @SecondaryTable(s).
0471: */
0472: private void parseSecondaryTables(ClassMapping cm,
0473: SecondaryTable... tables) {
0474: ClassMappingInfo info = cm.getMappingInfo();
0475: Log log = getLog();
0476:
0477: String name;
0478: List<Column> joins;
0479: boolean warnUnique = false;
0480: for (SecondaryTable table : tables) {
0481: name = table.name();
0482: if (StringUtils.isEmpty(name))
0483: throw new MetaDataException(_loc.get("second-name", cm));
0484: if (!StringUtils.isEmpty(table.schema()))
0485: name = table.schema() + "." + name;
0486: if (table.pkJoinColumns().length > 0) {
0487: joins = new ArrayList<Column>(
0488: table.pkJoinColumns().length);
0489: for (PrimaryKeyJoinColumn join : table.pkJoinColumns())
0490: joins.add(newColumn(join));
0491: info.setSecondaryTableJoinColumns(name, joins);
0492: }
0493: warnUnique |= table.uniqueConstraints().length > 0;
0494: }
0495:
0496: //### EJB3
0497: if (warnUnique && log.isWarnEnabled())
0498: log.warn(_loc.get("unique-constraints", cm));
0499: }
0500:
0501: /**
0502: * Set class table.
0503: */
0504: private void parseTable(ClassMapping cm, Table table) {
0505: String tableName = toTableName(table.schema(), table.name());
0506: if (tableName != null)
0507: cm.getMappingInfo().setTableName(tableName);
0508:
0509: for (UniqueConstraint uniqueConstraint : table
0510: .uniqueConstraints()) {
0511: Unique unique = newUnique(cm, null, uniqueConstraint
0512: .columnNames());
0513: cm.getMappingInfo().addUnique(unique);
0514: }
0515: }
0516:
0517: /**
0518: * Form a qualified table name from a schema and table name.
0519: */
0520: private static String toTableName(String schema, String table) {
0521: if (StringUtils.isEmpty(table))
0522: return null;
0523: if (StringUtils.isEmpty(schema))
0524: return table;
0525: return schema + "." + table;
0526: }
0527:
0528: /**
0529: * Parses the given annotation to create and cache a
0530: * {@link SQLResultSetMappingMetaData}.
0531: */
0532: private void parseSQLResultSetMappings(ClassMapping cm,
0533: SqlResultSetMapping... annos) {
0534: MappingRepository repos = (MappingRepository) getRepository();
0535: Log log = getLog();
0536: for (SqlResultSetMapping anno : annos) {
0537: if (log.isTraceEnabled())
0538: log.trace(_loc.get("parse-sqlrsmapping", anno.name()));
0539:
0540: QueryResultMapping result = repos
0541: .getCachedQueryResultMapping(null, anno.name());
0542: if (result != null) {
0543: if (log.isWarnEnabled())
0544: log.warn(_loc.get("dup-sqlrsmapping", anno.name(),
0545: cm));
0546: continue;
0547: }
0548:
0549: result = repos.addQueryResultMapping(null, anno.name());
0550: result.setSource(getSourceFile(), cm.getDescribedType(),
0551: result.SRC_ANNOTATIONS);
0552:
0553: for (EntityResult entity : anno.entities()) {
0554: QueryResultMapping.PCResult entityResult = result
0555: .addPCResult(entity.entityClass());
0556: if (!StringUtils.isEmpty(entity.discriminatorColumn()))
0557: entityResult.addMapping(entityResult.DISCRIMINATOR,
0558: entity.discriminatorColumn());
0559:
0560: for (FieldResult field : entity.fields())
0561: entityResult.addMapping(field.name(), field
0562: .column());
0563: }
0564: for (ColumnResult column : anno.columns())
0565: result.addColumnResult(column.name());
0566: }
0567: }
0568:
0569: /**
0570: * Parse @DiscriminatorColumn.
0571: */
0572: private void parseDiscriminatorColumn(ClassMapping cm,
0573: DiscriminatorColumn dcol) {
0574: Column col = new Column();
0575: if (!StringUtils.isEmpty(dcol.name()))
0576: col.setName(dcol.name());
0577: if (!StringUtils.isEmpty(dcol.columnDefinition()))
0578: col.setTypeName(dcol.columnDefinition());
0579: Discriminator discrim = cm.getDiscriminator();
0580: switch (dcol.discriminatorType()) {
0581: case CHAR:
0582: col.setJavaType(JavaTypes.CHAR);
0583: discrim.setJavaType(JavaTypes.CHAR);
0584: break;
0585: case INTEGER:
0586: col.setJavaType(JavaTypes.INT);
0587: if (dcol.length() != 31)
0588: col.setSize(dcol.length());
0589: discrim.setJavaType(JavaTypes.INT);
0590: break;
0591: default:
0592: col.setJavaType(JavaTypes.STRING);
0593: col.setSize(dcol.length());
0594: discrim.setJavaType(JavaTypes.STRING);
0595: }
0596: cm.getDiscriminator().getMappingInfo().setColumns(
0597: Arrays.asList(new Column[] { col }));
0598: }
0599:
0600: /**
0601: * Parse @Inheritance.
0602: */
0603: private void parseInheritance(ClassMapping cm, Inheritance inherit) {
0604: ClassMappingInfo info = cm.getMappingInfo();
0605: switch (inherit.strategy()) {
0606: case SINGLE_TABLE:
0607: info.setHierarchyStrategy(FlatClassStrategy.ALIAS);
0608: break;
0609: case JOINED:
0610: info.setHierarchyStrategy(VerticalClassStrategy.ALIAS);
0611: break;
0612: case TABLE_PER_CLASS:
0613: info.setHierarchyStrategy(FullClassStrategy.ALIAS);
0614: break;
0615: default:
0616: throw new InternalException();
0617: }
0618: }
0619:
0620: /**
0621: * Parse class-level @MappingOverride(s).
0622: */
0623: private void parseMappingOverrides(ClassMapping cm,
0624: MappingOverride... overs) {
0625: FieldMapping sup;
0626: for (MappingOverride over : overs) {
0627: if (StringUtils.isEmpty(over.name()))
0628: throw new MetaDataException(_loc.get(
0629: "no-override-name", cm));
0630: sup = (FieldMapping) cm.getDefinedSuperclassField(over
0631: .name());
0632: if (sup == null)
0633: sup = (FieldMapping) cm.addDefinedSuperclassField(over
0634: .name(), Object.class, Object.class);
0635: populate(sup, over);
0636: }
0637: }
0638:
0639: /**
0640: * Populate the given field from override data.
0641: */
0642: private void populate(FieldMapping fm, MappingOverride over) {
0643: if (over.containerTable().specified())
0644: parseContainerTable(fm, over.containerTable());
0645: parseColumns(fm, over.columns());
0646: parseXJoinColumns(fm, fm.getValueInfo(), true, over
0647: .joinColumns());
0648: parseElementJoinColumns(fm, over.elementJoinColumns());
0649: }
0650:
0651: /**
0652: * Parse datastore identity information in @DataStoreIdColumn.
0653: */
0654: private void parseDataStoreIdColumn(ClassMapping cm,
0655: DataStoreIdColumn id) {
0656: Column col = new Column();
0657: if (!StringUtils.isEmpty(id.name()))
0658: col.setName(id.name());
0659: if (!StringUtils.isEmpty(id.columnDefinition()))
0660: col.setTypeName(id.columnDefinition());
0661: if (id.precision() != 0)
0662: col.setSize(id.precision());
0663: col.setFlag(Column.FLAG_UNINSERTABLE, !id.insertable());
0664: col.setFlag(Column.FLAG_UNUPDATABLE, !id.updatable());
0665: cm.getMappingInfo().setColumns(
0666: Arrays.asList(new Column[] { col }));
0667: }
0668:
0669: /**
0670: * Parse the given foreign key.
0671: */
0672: private void parseForeignKey(MappingInfo info, ForeignKey fk) {
0673: parseForeignKey(info, fk.name(), fk.enabled(), fk.deferred(),
0674: fk.deleteAction(), fk.updateAction());
0675: }
0676:
0677: /**
0678: * Set foreign key data on the given mapping info.
0679: */
0680: protected void parseForeignKey(MappingInfo info, String name,
0681: boolean enabled, boolean deferred,
0682: ForeignKeyAction deleteAction, ForeignKeyAction updateAction) {
0683: if (!enabled) {
0684: info.setCanForeignKey(false);
0685: return;
0686: }
0687:
0688: org.apache.openjpa.jdbc.schema.ForeignKey fk = new org.apache.openjpa.jdbc.schema.ForeignKey();
0689: if (!StringUtils.isEmpty(name))
0690: fk.setName(name);
0691: fk.setDeferred(deferred);
0692: fk.setDeleteAction(toForeignKeyAction(deleteAction));
0693: fk.setUpdateAction(toForeignKeyAction(updateAction));
0694: info.setForeignKey(fk);
0695: }
0696:
0697: /**
0698: * Convert our FK action enum to an internal OpenJPA action.
0699: */
0700: private int toForeignKeyAction(ForeignKeyAction action) {
0701: switch (action) {
0702: case RESTRICT:
0703: return org.apache.openjpa.jdbc.schema.ForeignKey.ACTION_RESTRICT;
0704: case CASCADE:
0705: return org.apache.openjpa.jdbc.schema.ForeignKey.ACTION_CASCADE;
0706: case NULL:
0707: return org.apache.openjpa.jdbc.schema.ForeignKey.ACTION_NULL;
0708: case DEFAULT:
0709: return org.apache.openjpa.jdbc.schema.ForeignKey.ACTION_DEFAULT;
0710: default:
0711: throw new InternalException();
0712: }
0713: }
0714:
0715: /**
0716: * Parse the given index.
0717: */
0718: private void parseIndex(MappingInfo info, Index idx) {
0719: parseIndex(info, idx.name(), idx.enabled(), idx.unique());
0720: }
0721:
0722: /**
0723: * Set index data on the given mapping info.
0724: */
0725: protected void parseIndex(MappingInfo info, String name,
0726: boolean enabled, boolean unique) {
0727: if (!enabled) {
0728: info.setCanIndex(false);
0729: return;
0730: }
0731:
0732: org.apache.openjpa.jdbc.schema.Index idx = new org.apache.openjpa.jdbc.schema.Index();
0733: if (!StringUtils.isEmpty(name))
0734: idx.setName(name);
0735: idx.setUnique(unique);
0736: info.setIndex(idx);
0737: }
0738:
0739: /**
0740: * Set unique data on the given mapping info.
0741: */
0742: private void parseUnique(FieldMapping fm,
0743: org.apache.openjpa.persistence.jdbc.Unique anno) {
0744: ValueMappingInfo info = fm.getValueInfo();
0745: if (!anno.enabled()) {
0746: info.setCanUnique(false);
0747: return;
0748: }
0749:
0750: org.apache.openjpa.jdbc.schema.Unique unq = new org.apache.openjpa.jdbc.schema.Unique();
0751: if (!StringUtils.isEmpty(anno.name()))
0752: unq.setName(anno.name());
0753: unq.setDeferred(anno.deferred());
0754: info.setUnique(unq);
0755: }
0756:
0757: /**
0758: * Parse @VersionColumn(s).
0759: */
0760: private void parseVersionColumns(ClassMapping cm,
0761: VersionColumn... vcols) {
0762: if (vcols.length == 0)
0763: return;
0764:
0765: List<Column> cols = new ArrayList<Column>(vcols.length);
0766: for (VersionColumn vcol : vcols)
0767: cols.add(newColumn(vcol));
0768: cm.getVersion().getMappingInfo().setColumns(cols);
0769: }
0770:
0771: /**
0772: * Create a new schema column with information from the given annotation.
0773: */
0774: private static Column newColumn(VersionColumn anno) {
0775: Column col = new Column();
0776: if (!StringUtils.isEmpty(anno.name()))
0777: col.setName(anno.name());
0778: if (!StringUtils.isEmpty(anno.columnDefinition()))
0779: col.setTypeName(anno.columnDefinition());
0780: if (anno.precision() != 0)
0781: col.setSize(anno.precision());
0782: else if (anno.length() != 255)
0783: col.setSize(anno.length());
0784: col.setNotNull(!anno.nullable());
0785: col.setDecimalDigits(anno.scale());
0786: col.setFlag(Column.FLAG_UNINSERTABLE, !anno.insertable());
0787: col.setFlag(Column.FLAG_UNUPDATABLE, !anno.updatable());
0788: return col;
0789: }
0790:
0791: /**
0792: * Parse class-level @XMappingOverride(s).
0793: */
0794: private void parseMappingOverrides(ClassMapping cm,
0795: XMappingOverride... overs) {
0796: FieldMapping sup;
0797: for (XMappingOverride over : overs) {
0798: if (StringUtils.isEmpty(over.name()))
0799: throw new MetaDataException(_loc.get(
0800: "no-override-name", cm));
0801: sup = (FieldMapping) cm.getDefinedSuperclassField(over
0802: .name());
0803: if (sup == null)
0804: sup = (FieldMapping) cm.addDefinedSuperclassField(over
0805: .name(), Object.class, Object.class);
0806: populate(sup, over);
0807: }
0808: }
0809:
0810: /**
0811: * Populate the given field from override data.
0812: */
0813: private void populate(FieldMapping fm, XMappingOverride over) {
0814: if (over.containerTable().specified())
0815: parseContainerTable(fm, over.containerTable());
0816: parseColumns(fm, over.columns());
0817: parseXJoinColumns(fm, fm.getValueInfo(), true, over
0818: .joinColumns());
0819: parseElementColumns(fm, over.elementColumns());
0820: parseElementJoinColumns(fm, over.elementJoinColumns());
0821: parseKeyColumns(fm, over.keyColumns());
0822: parseKeyJoinColumns(fm, over.keyJoinColumns());
0823: }
0824:
0825: /**
0826: * Parse @ElementColumn(s).
0827: */
0828: private void parseElementColumns(FieldMapping fm,
0829: ElementColumn... pcols) {
0830: if (pcols.length == 0)
0831: return;
0832:
0833: List<Column> cols = new ArrayList<Column>(pcols.length);
0834: int unique = 0;
0835: for (int i = 0; i < pcols.length; i++) {
0836: cols.add(newColumn(pcols[i]));
0837: unique |= (pcols[i].unique()) ? TRUE : FALSE;
0838: }
0839: setColumns(fm, fm.getElementMapping().getValueInfo(), cols,
0840: unique);
0841: }
0842:
0843: /**
0844: * Create a new schema column with information from the given annotation.
0845: */
0846: private static Column newColumn(ElementColumn anno) {
0847: Column col = new Column();
0848: if (!StringUtils.isEmpty(anno.name()))
0849: col.setName(anno.name());
0850: if (!StringUtils.isEmpty(anno.columnDefinition()))
0851: col.setTypeName(anno.columnDefinition());
0852: if (anno.precision() != 0)
0853: col.setSize(anno.precision());
0854: else if (anno.length() != 255)
0855: col.setSize(anno.length());
0856: col.setNotNull(!anno.nullable());
0857: col.setDecimalDigits(anno.scale());
0858: col.setFlag(Column.FLAG_UNINSERTABLE, !anno.insertable());
0859: col.setFlag(Column.FLAG_UNUPDATABLE, !anno.updatable());
0860: return col;
0861: }
0862:
0863: /**
0864: * Parse @KeyJoinColumn(s).
0865: */
0866: private void parseKeyJoinColumns(FieldMapping fm,
0867: KeyJoinColumn... joins) {
0868: if (joins.length == 0)
0869: return;
0870:
0871: List<Column> cols = new ArrayList<Column>(joins.length);
0872: int unique = 0;
0873: for (int i = 0; i < joins.length; i++) {
0874: cols.add(newColumn(joins[i]));
0875: unique |= (joins[i].unique()) ? TRUE : FALSE;
0876: }
0877: setColumns(fm, fm.getKeyMapping().getValueInfo(), cols, unique);
0878: }
0879:
0880: /**
0881: * Create a new schema column with information from the given annotation.
0882: */
0883: private static Column newColumn(KeyJoinColumn join) {
0884: Column col = new Column();
0885: if (!StringUtils.isEmpty(join.name()))
0886: col.setName(join.name());
0887: if (!StringUtils.isEmpty(join.columnDefinition()))
0888: col.setName(join.columnDefinition());
0889: if (!StringUtils.isEmpty(join.referencedColumnName()))
0890: col.setTarget(join.referencedColumnName());
0891: if (!StringUtils.isEmpty(join.referencedAttributeName()))
0892: col.setTargetField(join.referencedAttributeName());
0893: col.setNotNull(!join.nullable());
0894: col.setFlag(Column.FLAG_UNINSERTABLE, !join.insertable());
0895: col.setFlag(Column.FLAG_UNUPDATABLE, !join.updatable());
0896: return col;
0897: }
0898:
0899: /**
0900: * Translate the fetch mode enum value to the internal OpenJPA constant.
0901: */
0902: private static int toEagerFetchModeConstant(FetchMode mode) {
0903: switch (mode) {
0904: case NONE:
0905: return EagerFetchModes.EAGER_NONE;
0906: case JOIN:
0907: return EagerFetchModes.EAGER_JOIN;
0908: case PARALLEL:
0909: return EagerFetchModes.EAGER_PARALLEL;
0910: default:
0911: throw new InternalException();
0912: }
0913: }
0914:
0915: @Override
0916: protected void parseLobMapping(FieldMetaData fmd) {
0917: Column col = new Column();
0918: if (fmd.getDeclaredTypeCode() == JavaTypes.STRING
0919: || fmd.getDeclaredType() == char[].class
0920: || fmd.getDeclaredType() == Character[].class)
0921: col.setType(Types.CLOB);
0922: else
0923: col.setType(Types.BLOB);
0924: ((FieldMapping) fmd).getValueInfo().setColumns(
0925: Arrays.asList(new Column[] { col }));
0926: }
0927:
0928: @Override
0929: protected void parseMemberMappingAnnotations(FieldMetaData fmd) {
0930: FieldMapping fm = (FieldMapping) fmd;
0931: AnnotatedElement el = (AnnotatedElement) getRepository()
0932: .getMetaDataFactory().getDefaults().getBackingMember(
0933: fmd);
0934:
0935: MappingTag tag;
0936: for (Annotation anno : el.getDeclaredAnnotations()) {
0937: tag = _tags.get(anno.annotationType());
0938: if (tag == null) {
0939: handleUnknownMemberMappingAnnotation(fm, anno);
0940: continue;
0941: }
0942:
0943: switch (tag) {
0944: case ASSOC_OVERRIDE:
0945: parseAssociationOverrides(fm,
0946: (AssociationOverride) anno);
0947: break;
0948: case ASSOC_OVERRIDES:
0949: parseAssociationOverrides(fm,
0950: ((AssociationOverrides) anno).value());
0951: break;
0952: case ATTR_OVERRIDE:
0953: parseAttributeOverrides(fm, (AttributeOverride) anno);
0954: break;
0955: case ATTR_OVERRIDES:
0956: parseAttributeOverrides(fm, ((AttributeOverrides) anno)
0957: .value());
0958: break;
0959: case COL:
0960: parseColumns(fm, (javax.persistence.Column) anno);
0961: break;
0962: case COLS:
0963: parseColumns(fm, ((Columns) anno).value());
0964: break;
0965: case ENUMERATED:
0966: parseEnumerated(fm, (Enumerated) anno);
0967: break;
0968: case JOIN_COL:
0969: parseJoinColumns(fm, fm.getValueInfo(), true,
0970: (JoinColumn) anno);
0971: break;
0972: case JOIN_COLS:
0973: parseJoinColumns(fm, fm.getValueInfo(), true,
0974: ((JoinColumns) anno).value());
0975: break;
0976: case JOIN_TABLE:
0977: parseJoinTable(fm, (JoinTable) anno);
0978: break;
0979: case KEY_CLASS_CRIT:
0980: fm.getKeyMapping().getValueInfo().setUseClassCriteria(
0981: ((KeyClassCriteria) anno).value());
0982: break;
0983: case KEY_COL:
0984: parseKeyColumns(fm, (KeyColumn) anno);
0985: break;
0986: case KEY_COLS:
0987: parseKeyColumns(fm, ((KeyColumns) anno).value());
0988: break;
0989: case KEY_EMBEDDED_MAPPING:
0990: KeyEmbeddedMapping kembed = (KeyEmbeddedMapping) anno;
0991: parseEmbeddedMapping(fm.getKeyMapping(), kembed
0992: .nullIndicatorColumnName(), kembed
0993: .nullIndicatorAttributeName(), kembed
0994: .overrides());
0995: break;
0996: case KEY_FK:
0997: KeyForeignKey kfk = (KeyForeignKey) anno;
0998: parseForeignKey(fm.getKeyMapping().getValueInfo(), kfk
0999: .name(), kfk.enabled(), kfk.deferred(), kfk
1000: .deleteAction(), kfk.updateAction());
1001: break;
1002: case KEY_INDEX:
1003: KeyIndex kidx = (KeyIndex) anno;
1004: parseIndex(fm.getKeyMapping().getValueInfo(), kidx
1005: .name(), kidx.enabled(), kidx.unique());
1006: break;
1007: case KEY_JOIN_COL:
1008: parseKeyJoinColumns(fm, (KeyJoinColumn) anno);
1009: break;
1010: case KEY_JOIN_COLS:
1011: parseKeyJoinColumns(fm, ((KeyJoinColumns) anno).value());
1012: break;
1013: case KEY_NONPOLY:
1014: fm
1015: .getKeyMapping()
1016: .setPolymorphic(
1017: toPolymorphicConstant(((KeyNonpolymorphic) anno)
1018: .value()));
1019: break;
1020: case KEY_STRAT:
1021: fm.getKeyMapping().getValueInfo().setStrategy(
1022: ((KeyStrategy) anno).value());
1023: break;
1024: case PK_JOIN_COL:
1025: parsePrimaryKeyJoinColumns(fm,
1026: (PrimaryKeyJoinColumn) anno);
1027: break;
1028: case PK_JOIN_COLS:
1029: parsePrimaryKeyJoinColumns(fm,
1030: ((PrimaryKeyJoinColumns) anno).value());
1031: break;
1032: case TABLE_GEN:
1033: parseTableGenerator(el, (TableGenerator) anno);
1034: break;
1035: case TEMPORAL:
1036: parseTemporal(fm, (Temporal) anno);
1037: break;
1038: case CLASS_CRIT:
1039: fm.getValueInfo().setUseClassCriteria(
1040: ((ClassCriteria) anno).value());
1041: break;
1042: case CONTAINER_TABLE:
1043: parseContainerTable(fm, (ContainerTable) anno);
1044: break;
1045: case EAGER_FETCH_MODE:
1046: fm
1047: .setEagerFetchMode(toEagerFetchModeConstant(((EagerFetchMode) anno)
1048: .value()));
1049: break;
1050: case ELEM_CLASS_CRIT:
1051: fm.getElementMapping().getValueInfo()
1052: .setUseClassCriteria(
1053: ((ElementClassCriteria) anno).value());
1054: break;
1055: case ELEM_COL:
1056: parseElementColumns(fm, (ElementColumn) anno);
1057: break;
1058: case ELEM_COLS:
1059: parseElementColumns(fm, ((ElementColumns) anno).value());
1060: break;
1061: case ELEM_EMBEDDED_MAPPING:
1062: ElementEmbeddedMapping ee = (ElementEmbeddedMapping) anno;
1063: parseEmbeddedMapping(fm.getElementMapping(), ee
1064: .nullIndicatorAttributeName(), ee
1065: .nullIndicatorColumnName(), ee.overrides());
1066: break;
1067: case ELEM_FK:
1068: ElementForeignKey efk = (ElementForeignKey) anno;
1069: parseForeignKey(fm.getElementMapping().getValueInfo(),
1070: efk.name(), efk.enabled(), efk.deferred(), efk
1071: .deleteAction(), efk.updateAction());
1072: break;
1073: case ELEM_INDEX:
1074: ElementIndex eidx = (ElementIndex) anno;
1075: parseIndex(fm.getElementMapping().getValueInfo(), eidx
1076: .name(), eidx.enabled(), eidx.unique());
1077: break;
1078: case ELEM_JOIN_COL:
1079: parseElementJoinColumns(fm, (ElementJoinColumn) anno);
1080: break;
1081: case ELEM_JOIN_COLS:
1082: parseElementJoinColumns(fm, ((ElementJoinColumns) anno)
1083: .value());
1084: break;
1085: case ELEM_NONPOLY:
1086: fm
1087: .getElementMapping()
1088: .setPolymorphic(
1089: toPolymorphicConstant(((ElementNonpolymorphic) anno)
1090: .value()));
1091: break;
1092: case ELEM_STRAT:
1093: fm.getElementMapping().getValueInfo().setStrategy(
1094: ((ElementStrategy) anno).value());
1095: break;
1096: case EMBEDDED_MAPPING:
1097: parseEmbeddedMapping(fm, (EmbeddedMapping) anno);
1098: break;
1099: case FK:
1100: parseForeignKey(fm.getValueInfo(), (ForeignKey) anno);
1101: break;
1102: case INDEX:
1103: parseIndex(fm.getValueInfo(), (Index) anno);
1104: break;
1105: case NONPOLY:
1106: fm
1107: .setPolymorphic(toPolymorphicConstant(((Nonpolymorphic) anno)
1108: .value()));
1109: break;
1110: case ORDER_COL:
1111: parseOrderColumn(fm, (OrderColumn) anno);
1112: break;
1113: case STRAT:
1114: fm.getValueInfo()
1115: .setStrategy(((Strategy) anno).value());
1116: break;
1117: case UNIQUE:
1118: parseUnique(
1119: fm,
1120: (org.apache.openjpa.persistence.jdbc.Unique) anno);
1121: break;
1122: case X_EMBEDDED_MAPPING:
1123: XEmbeddedMapping embed = (XEmbeddedMapping) anno;
1124: parseEmbeddedMapping(fm, embed
1125: .nullIndicatorColumnName(), embed
1126: .nullIndicatorAttributeName(), embed
1127: .overrides());
1128: break;
1129: case X_JOIN_COL:
1130: parseXJoinColumns(fm, fm.getValueInfo(), true,
1131: (XJoinColumn) anno);
1132: break;
1133: case X_JOIN_COLS:
1134: parseXJoinColumns(fm, fm.getValueInfo(), true,
1135: ((XJoinColumns) anno).value());
1136: break;
1137: default:
1138: throw new UnsupportedException(_loc.get("unsupported",
1139: fm, anno.toString()));
1140: }
1141: }
1142: }
1143:
1144: /**
1145: * Allow subclasses to handle unknown annotations.
1146: */
1147: protected boolean handleUnknownMemberMappingAnnotation(
1148: FieldMapping fm, Annotation anno) {
1149: return false;
1150: }
1151:
1152: /**
1153: * Return the {@link ValueMapping} <code>POLY_*</code> constant for
1154: * the given enum value.
1155: */
1156: protected static int toPolymorphicConstant(NonpolymorphicType val) {
1157: switch (val) {
1158: case EXACT:
1159: return ValueMapping.POLY_FALSE;
1160: case JOINABLE:
1161: return ValueMapping.POLY_JOINABLE;
1162: case FALSE:
1163: return ValueMapping.POLY_TRUE;
1164: default:
1165: throw new InternalException();
1166: }
1167: }
1168:
1169: /**
1170: * Parse given @AssociationOverride annotations on an embedded mapping.
1171: */
1172: private void parseAssociationOverrides(FieldMapping fm,
1173: AssociationOverride... assocs) {
1174: ClassMapping embed = fm.getEmbeddedMapping();
1175: if (embed == null)
1176: throw new MetaDataException(_loc.get("not-embedded", fm));
1177:
1178: FieldMapping efm;
1179: JoinColumn[] ecols;
1180: int unique;
1181: List<Column> jcols;
1182: for (AssociationOverride assoc : assocs) {
1183: efm = embed.getFieldMapping(assoc.name());
1184: if (efm == null)
1185: throw new MetaDataException(_loc.get(
1186: "embed-override-name", fm, assoc.name()));
1187: ecols = assoc.joinColumns();
1188: if (ecols == null || ecols.length == 0)
1189: continue;
1190:
1191: unique = 0;
1192: jcols = new ArrayList<Column>(ecols.length);
1193: for (JoinColumn ecol : ecols) {
1194: unique |= (ecol.unique()) ? TRUE : FALSE;
1195: jcols.add(newColumn(ecol));
1196: }
1197: setColumns(efm, efm.getValueInfo(), jcols, unique);
1198: }
1199: }
1200:
1201: /**
1202: * Parse given @AttributeOverride annotations on an embedded mapping.
1203: */
1204: private void parseAttributeOverrides(FieldMapping fm,
1205: AttributeOverride... attrs) {
1206: ClassMapping embed = fm.getEmbeddedMapping();
1207: if (embed == null)
1208: throw new MetaDataException(_loc.get("not-embedded", fm));
1209:
1210: FieldMapping efm;
1211: for (AttributeOverride attr : attrs) {
1212: efm = embed.getFieldMapping(attr.name());
1213: if (efm == null)
1214: throw new MetaDataException(_loc.get(
1215: "embed-override-name", fm, attr.name()));
1216: if (attr.column() != null)
1217: parseColumns(efm, attr.column());
1218: }
1219: }
1220:
1221: /**
1222: * Parse @Enumerated.
1223: */
1224: private void parseEnumerated(FieldMapping fm, Enumerated anno) {
1225: String strat = EnumValueHandler.class.getName()
1226: + "(StoreOrdinal="
1227: + String.valueOf(anno.value() == EnumType.ORDINAL)
1228: + ")";
1229: fm.getValueInfo().setStrategy(strat);
1230: }
1231:
1232: /**
1233: * Parse @Temporal.
1234: */
1235: private void parseTemporal(FieldMapping fm, Temporal anno) {
1236: List cols = fm.getValueInfo().getColumns();
1237: if (!cols.isEmpty() && cols.size() != 1)
1238: throw new MetaDataException(_loc.get("num-cols-mismatch",
1239: fm, String.valueOf(cols.size()), "1"));
1240: if (cols.isEmpty()) {
1241: cols = Arrays.asList(new Column[] { new Column() });
1242: fm.getValueInfo().setColumns(cols);
1243: }
1244:
1245: Column col = (Column) cols.get(0);
1246: switch (anno.value()) {
1247: case DATE:
1248: col.setType(Types.DATE);
1249: break;
1250: case TIME:
1251: col.setType(Types.TIME);
1252: break;
1253: case TIMESTAMP:
1254: col.setType(Types.TIMESTAMP);
1255: break;
1256: }
1257: }
1258:
1259: /**
1260: * Parse @Column(s).
1261: */
1262: protected void parseColumns(FieldMapping fm,
1263: javax.persistence.Column... pcols) {
1264: if (pcols.length == 0)
1265: return;
1266:
1267: // might already have some column information from mapping annotation
1268: List cols = fm.getValueInfo().getColumns();
1269: if (!cols.isEmpty() && cols.size() != pcols.length)
1270: throw new MetaDataException(_loc.get("num-cols-mismatch",
1271: fm, String.valueOf(cols.size()), String
1272: .valueOf(pcols.length)));
1273:
1274: // cache the JAXB XmlType class if it is present so we do not
1275: // have a hard-wired dependency on JAXB here
1276: Class xmlTypeClass = null;
1277: try {
1278: xmlTypeClass = Class
1279: .forName("javax.xml.bind.annotation.XmlType");
1280: } catch (Exception e) {
1281: }
1282:
1283: int unique = 0;
1284: String secondary = null;
1285: for (int i = 0; i < pcols.length; i++) {
1286: if (cols.size() > i)
1287: setupColumn((Column) cols.get(i), pcols[i]);
1288: else {
1289: if (cols.isEmpty())
1290: cols = new ArrayList<Column>(pcols.length);
1291: cols.add(newColumn(pcols[i]));
1292: }
1293:
1294: if (xmlTypeClass != null
1295: && StringUtils.isEmpty(pcols[i].columnDefinition())
1296: && ((Boolean) AccessController
1297: .doPrivileged(J2DoPriv5Helper
1298: .isAnnotationPresentAction(fm
1299: .getDeclaredType(),
1300: xmlTypeClass)))
1301: .booleanValue()) {
1302: DBDictionary dict = ((MappingRepository) getRepository())
1303: .getDBDictionary();
1304: if (dict.supportsXMLColumn)
1305: // column maps to xml type
1306: ((Column) cols.get(i))
1307: .setTypeName(dict.xmlTypeName);
1308: }
1309:
1310: unique |= (pcols[i].unique()) ? TRUE : FALSE;
1311: secondary = trackSecondaryTable(fm, secondary, pcols[i]
1312: .table(), i);
1313: }
1314:
1315: setColumns(fm, fm.getValueInfo(), cols, unique);
1316: if (secondary != null)
1317: fm.getMappingInfo().setTableName(secondary);
1318: }
1319:
1320: /**
1321: * Create a new schema column with information from the given annotation.
1322: */
1323: private static Column newColumn(javax.persistence.Column anno) {
1324: Column col = new Column();
1325: setupColumn(col, anno);
1326: return col;
1327: }
1328:
1329: /**
1330: * Setup the given column with information from the given annotation.
1331: */
1332: private static void setupColumn(Column col,
1333: javax.persistence.Column anno) {
1334: if (!StringUtils.isEmpty(anno.name()))
1335: col.setName(anno.name());
1336: if (!StringUtils.isEmpty(anno.columnDefinition()))
1337: col.setTypeName(anno.columnDefinition());
1338: if (anno.precision() != 0)
1339: col.setSize(anno.precision());
1340: else if (anno.length() != 255)
1341: col.setSize(anno.length());
1342: col.setNotNull(!anno.nullable());
1343: col.setDecimalDigits(anno.scale());
1344: col.setFlag(Column.FLAG_UNINSERTABLE, !anno.insertable());
1345: col.setFlag(Column.FLAG_UNUPDATABLE, !anno.updatable());
1346: }
1347:
1348: /**
1349: * Set the given columns as the columns for <code>fm</code>.
1350: *
1351: * @param unique bitwise combination of TRUE and FALSE for the
1352: * unique attribute of each column
1353: */
1354: protected void setColumns(FieldMapping fm, MappingInfo info,
1355: List<Column> cols, int unique) {
1356: info.setColumns(cols);
1357: if (unique == TRUE)
1358: info.setUnique(new org.apache.openjpa.jdbc.schema.Unique());
1359:
1360: //### EJB3
1361: Log log = getLog();
1362: if (log.isWarnEnabled() && unique == (TRUE | FALSE))
1363: log.warn(_loc.get("inconsist-col-attrs", fm));
1364: }
1365:
1366: /**
1367: * Helper to track the secondary table for a set of columns.
1368: *
1369: * @param secondary secondary table for last column
1370: * @param colSecondary secondary table for current column
1371: * @return secondary table for field
1372: */
1373: private String trackSecondaryTable(FieldMapping fm,
1374: String secondary, String colSecondary, int col) {
1375: if (StringUtils.isEmpty(colSecondary))
1376: colSecondary = null;
1377: if (col == 0)
1378: return colSecondary;
1379: if (!StringUtils.equalsIgnoreCase(secondary, colSecondary))
1380: throw new MetaDataException(_loc
1381: .get("second-inconsist", fm));
1382: return secondary;
1383: }
1384:
1385: /**
1386: * Parse @JoinTable.
1387: */
1388: private void parseJoinTable(FieldMapping fm, JoinTable join) {
1389: fm.getMappingInfo().setTableName(
1390: toTableName(join.schema(), join.name()));
1391: parseJoinColumns(fm, fm.getMappingInfo(), false, join
1392: .joinColumns());
1393: parseJoinColumns(fm, fm.getElementMapping().getValueInfo(),
1394: false, join.inverseJoinColumns());
1395: }
1396:
1397: /**
1398: * Parse given @JoinColumn annotations.
1399: */
1400: private void parseJoinColumns(FieldMapping fm, MappingInfo info,
1401: boolean secondaryAllowed, JoinColumn... joins) {
1402: if (joins.length == 0)
1403: return;
1404:
1405: List<Column> cols = new ArrayList<Column>(joins.length);
1406: int unique = 0;
1407: String secondary = null;
1408: for (int i = 0; i < joins.length; i++) {
1409: cols.add(newColumn(joins[i]));
1410: unique |= (joins[i].unique()) ? TRUE : FALSE;
1411: secondary = trackSecondaryTable(fm, secondary, joins[i]
1412: .table(), i);
1413: if (!secondaryAllowed && secondary != null)
1414: throw new MetaDataException(_loc.get("bad-second", fm));
1415: }
1416:
1417: setColumns(fm, info, cols, unique);
1418: if (secondary != null)
1419: fm.getMappingInfo().setTableName(secondary);
1420: }
1421:
1422: /**
1423: * Create a new schema column with information from the given annotation.
1424: */
1425: private static Column newColumn(JoinColumn join) {
1426: Column col = new Column();
1427: if (!StringUtils.isEmpty(join.name()))
1428: col.setName(join.name());
1429: if (!StringUtils.isEmpty(join.columnDefinition()))
1430: col.setTypeName(join.columnDefinition());
1431: if (!StringUtils.isEmpty(join.referencedColumnName()))
1432: col.setTarget(join.referencedColumnName());
1433: col.setNotNull(!join.nullable());
1434: col.setFlag(Column.FLAG_UNINSERTABLE, !join.insertable());
1435: col.setFlag(Column.FLAG_UNUPDATABLE, !join.updatable());
1436: return col;
1437: }
1438:
1439: /**
1440: * Parse @KeyColumn(s).
1441: */
1442: private void parseKeyColumns(FieldMapping fm, KeyColumn... pcols) {
1443: if (pcols.length == 0)
1444: return;
1445:
1446: List<Column> cols = new ArrayList<Column>(pcols.length);
1447: int unique = 0;
1448: for (int i = 0; i < pcols.length; i++) {
1449: cols.add(newColumn(pcols[i]));
1450: unique |= (pcols[i].unique()) ? TRUE : FALSE;
1451: }
1452: setColumns(fm, fm.getKeyMapping().getValueInfo(), cols, unique);
1453: }
1454:
1455: /**
1456: * Create a new schema column with information from the given annotation.
1457: */
1458: private static Column newColumn(KeyColumn anno) {
1459: Column col = new Column();
1460: if (!StringUtils.isEmpty(anno.name()))
1461: col.setName(anno.name());
1462: if (!StringUtils.isEmpty(anno.columnDefinition()))
1463: col.setTypeName(anno.columnDefinition());
1464: if (anno.precision() != 0)
1465: col.setSize(anno.precision());
1466: else if (anno.length() != 255)
1467: col.setSize(anno.length());
1468: col.setNotNull(!anno.nullable());
1469: col.setDecimalDigits(anno.scale());
1470: col.setFlag(Column.FLAG_UNINSERTABLE, !anno.insertable());
1471: col.setFlag(Column.FLAG_UNUPDATABLE, !anno.updatable());
1472: return col;
1473: }
1474:
1475: /**
1476: * Parse given @PrimaryKeyJoinColumn annotations.
1477: */
1478: private void parsePrimaryKeyJoinColumns(FieldMapping fm,
1479: PrimaryKeyJoinColumn... joins) {
1480: List<Column> cols = new ArrayList<Column>(joins.length);
1481: for (PrimaryKeyJoinColumn join : joins)
1482: cols.add(newColumn(join));
1483: setColumns(fm, fm.getValueInfo(), cols, 0);
1484: }
1485:
1486: /**
1487: * Parse given @XJoinColumn annotations.
1488: */
1489: protected void parseXJoinColumns(FieldMapping fm, MappingInfo info,
1490: boolean secondaryAllowed, XJoinColumn... joins) {
1491: if (joins.length == 0)
1492: return;
1493:
1494: List<Column> cols = new ArrayList<Column>(joins.length);
1495: int unique = 0;
1496: String secondary = null;
1497: for (int i = 0; i < joins.length; i++) {
1498: cols.add(newColumn(joins[i]));
1499: unique |= (joins[i].unique()) ? TRUE : FALSE;
1500: secondary = trackSecondaryTable(fm, secondary, joins[i]
1501: .table(), i);
1502: if (!secondaryAllowed && secondary != null)
1503: throw new MetaDataException(_loc.get("bad-second", fm));
1504: }
1505:
1506: setColumns(fm, info, cols, unique);
1507: if (secondary != null)
1508: fm.getMappingInfo().setTableName(secondary);
1509: }
1510:
1511: /**
1512: * Create a new schema column with information from the given annotation.
1513: */
1514: private static Column newColumn(XJoinColumn join) {
1515: Column col = new Column();
1516: if (!StringUtils.isEmpty(join.name()))
1517: col.setName(join.name());
1518: if (!StringUtils.isEmpty(join.columnDefinition()))
1519: col.setTypeName(join.columnDefinition());
1520: if (!StringUtils.isEmpty(join.referencedColumnName()))
1521: col.setTarget(join.referencedColumnName());
1522: if (!StringUtils.isEmpty(join.referencedAttributeName()))
1523: col.setTargetField(join.referencedAttributeName());
1524: col.setNotNull(!join.nullable());
1525: col.setFlag(Column.FLAG_UNINSERTABLE, !join.insertable());
1526: col.setFlag(Column.FLAG_UNUPDATABLE, !join.updatable());
1527: return col;
1528: }
1529:
1530: /**
1531: * Parse embedded info for the given mapping.
1532: */
1533: private void parseEmbeddedMapping(FieldMapping fm,
1534: EmbeddedMapping anno) {
1535: ClassMapping embed = fm.getEmbeddedMapping();
1536: if (embed == null)
1537: throw new MetaDataException(_loc.get("not-embedded", fm));
1538:
1539: FieldMapping efm;
1540: for (MappingOverride over : anno.overrides()) {
1541: efm = embed.getFieldMapping(over.name());
1542: if (efm == null)
1543: throw new MetaDataException(_loc.get(
1544: "embed-override-name", fm, over.name()));
1545: populate(efm, over);
1546: }
1547:
1548: String nullInd = null;
1549: if (!StringUtils.isEmpty(anno.nullIndicatorAttributeName()))
1550: nullInd = anno.nullIndicatorAttributeName();
1551: else if (!StringUtils.isEmpty(anno.nullIndicatorColumnName()))
1552: nullInd = anno.nullIndicatorColumnName();
1553: if (nullInd == null)
1554: return;
1555:
1556: ValueMappingInfo info = fm.getValueInfo();
1557: populateNullIndicator(nullInd, info);
1558: }
1559:
1560: /**
1561: * Parse embedded info for the given mapping.
1562: */
1563: private void parseEmbeddedMapping(ValueMapping vm,
1564: String nullIndicatorAttribute, String nullIndicatorColumn,
1565: XMappingOverride[] overrides) {
1566: ClassMapping embed = vm.getEmbeddedMapping();
1567: if (embed == null)
1568: throw new MetaDataException(_loc.get("not-embedded", vm));
1569:
1570: FieldMapping efm;
1571: for (XMappingOverride over : overrides) {
1572: efm = embed.getFieldMapping(over.name());
1573: if (efm == null)
1574: throw new MetaDataException(_loc.get(
1575: "embed-override-name", vm, over.name()));
1576: populate(efm, over);
1577: }
1578:
1579: String nullInd = null;
1580: if (!StringUtils.isEmpty(nullIndicatorAttribute))
1581: nullInd = nullIndicatorAttribute;
1582: else if (!StringUtils.isEmpty(nullIndicatorColumn))
1583: nullInd = nullIndicatorColumn;
1584: if (nullInd == null)
1585: return;
1586:
1587: ValueMappingInfo info = vm.getValueInfo();
1588: populateNullIndicator(nullInd, info);
1589: }
1590:
1591: private void populateNullIndicator(String nullInd,
1592: ValueMappingInfo info) {
1593: if ("false".equals(nullInd))
1594: info.setCanIndicateNull(false);
1595: else {
1596: Column col = new Column();
1597: if (!"true".equals(nullInd))
1598: col.setName(nullInd);
1599: info.setColumns(Arrays.asList(new Column[] { col }));
1600: }
1601: }
1602:
1603: /**
1604: * Parse @ContainerTable.
1605: */
1606: protected void parseContainerTable(FieldMapping fm,
1607: ContainerTable ctbl) {
1608: fm.getMappingInfo().setTableName(
1609: toTableName(ctbl.schema(), ctbl.name()));
1610: parseXJoinColumns(fm, fm.getMappingInfo(), false, ctbl
1611: .joinColumns());
1612: if (ctbl.joinForeignKey().specified())
1613: parseForeignKey(fm.getMappingInfo(), ctbl.joinForeignKey());
1614: if (ctbl.joinIndex().specified())
1615: parseIndex(fm.getMappingInfo(), ctbl.joinIndex());
1616: }
1617:
1618: /**
1619: * Parse @OrderColumn.
1620: */
1621: private void parseOrderColumn(FieldMapping fm, OrderColumn order) {
1622: if (!order.enabled()) {
1623: fm.getMappingInfo().setCanOrderColumn(false);
1624: return;
1625: }
1626:
1627: Column col = new Column();
1628: if (!StringUtils.isEmpty(order.name()))
1629: col.setName(order.name());
1630: if (!StringUtils.isEmpty(order.columnDefinition()))
1631: col.setTypeName(order.columnDefinition());
1632: if (order.precision() != 0)
1633: col.setSize(order.precision());
1634: col.setFlag(Column.FLAG_UNINSERTABLE, !order.insertable());
1635: col.setFlag(Column.FLAG_UNUPDATABLE, !order.updatable());
1636: fm.getMappingInfo().setOrderColumn(col);
1637: }
1638:
1639: /**
1640: * Parse @ElementJoinColumn(s).
1641: */
1642: protected void parseElementJoinColumns(FieldMapping fm,
1643: ElementJoinColumn... joins) {
1644: if (joins.length == 0)
1645: return;
1646:
1647: List<Column> cols = new ArrayList<Column>(joins.length);
1648: int unique = 0;
1649: for (int i = 0; i < joins.length; i++) {
1650: cols.add(newColumn(joins[i]));
1651: unique |= (joins[i].unique()) ? TRUE : FALSE;
1652: }
1653: setColumns(fm, fm.getElementMapping().getValueInfo(), cols,
1654: unique);
1655: }
1656:
1657: /**
1658: * Create a new schema column with information from the given annotation.
1659: */
1660: private static Column newColumn(ElementJoinColumn join) {
1661: Column col = new Column();
1662: if (!StringUtils.isEmpty(join.name()))
1663: col.setName(join.name());
1664: if (!StringUtils.isEmpty(join.columnDefinition()))
1665: col.setTypeName(join.columnDefinition());
1666: if (!StringUtils.isEmpty(join.referencedColumnName()))
1667: col.setTarget(join.referencedColumnName());
1668: if (!StringUtils.isEmpty(join.referencedAttributeName()))
1669: col.setTargetField(join.referencedAttributeName());
1670: col.setNotNull(!join.nullable());
1671: col.setFlag(Column.FLAG_UNINSERTABLE, !join.insertable());
1672: col.setFlag(Column.FLAG_UNUPDATABLE, !join.updatable());
1673: return col;
1674: }
1675:
1676: private static Unique newUnique(ClassMapping cm, String name,
1677: String[] columnNames) {
1678: if (columnNames == null || columnNames.length == 0)
1679: return null;
1680: Unique uniqueConstraint = new Unique();
1681: uniqueConstraint.setName(name);
1682: for (int i = 0; i < columnNames.length; i++) {
1683: if (StringUtils.isEmpty(columnNames[i]))
1684: throw new UserException(_loc.get("empty-unique-column",
1685: Arrays.toString(columnNames), cm));
1686: Column column = new Column();
1687: column.setName(columnNames[i]);
1688: uniqueConstraint.addColumn(column);
1689: }
1690: return uniqueConstraint;
1691: }
1692: }
|