| /* |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you under the Apache License, Version 2.0 (the |
| * "License"); you may not use this file except in compliance |
| * with the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, |
| * software distributed under the License is distributed on an |
| * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| * KIND, either express or implied. See the License for the |
| * specific language governing permissions and limitations |
| * under the License. |
| */ |
| package org.apache.openjpa.persistence.jdbc; |
| |
| import static org.apache.openjpa.persistence.jdbc.MappingTag.ASSOC_OVERRIDE; |
| import static org.apache.openjpa.persistence.jdbc.MappingTag.ATTR_OVERRIDE; |
| import static org.apache.openjpa.persistence.jdbc.MappingTag.COL; |
| import static org.apache.openjpa.persistence.jdbc.MappingTag.COLLECTION_TABLE; |
| import static org.apache.openjpa.persistence.jdbc.MappingTag.COLS; |
| import static org.apache.openjpa.persistence.jdbc.MappingTag.COLUMN_NAME; |
| import static org.apache.openjpa.persistence.jdbc.MappingTag.COLUMN_RESULT; |
| import static org.apache.openjpa.persistence.jdbc.MappingTag.DATASTORE_ID_COL; |
| import static org.apache.openjpa.persistence.jdbc.MappingTag.DELIMITED_IDS; |
| import static org.apache.openjpa.persistence.jdbc.MappingTag.DISCRIM_COL; |
| import static org.apache.openjpa.persistence.jdbc.MappingTag.DISCRIM_VAL; |
| import static org.apache.openjpa.persistence.jdbc.MappingTag.ENTITY_RESULT; |
| import static org.apache.openjpa.persistence.jdbc.MappingTag.ENUMERATED; |
| import static org.apache.openjpa.persistence.jdbc.MappingTag.FIELD_RESULT; |
| import static org.apache.openjpa.persistence.jdbc.MappingTag.FK; |
| import static org.apache.openjpa.persistence.jdbc.MappingTag.FK_COL_NAME; |
| import static org.apache.openjpa.persistence.jdbc.MappingTag.FK_COL_NAMES; |
| import static org.apache.openjpa.persistence.jdbc.MappingTag.INDEX; |
| import static org.apache.openjpa.persistence.jdbc.MappingTag.INHERITANCE; |
| import static org.apache.openjpa.persistence.jdbc.MappingTag.JOIN_COL; |
| import static org.apache.openjpa.persistence.jdbc.MappingTag.JOIN_TABLE; |
| import static org.apache.openjpa.persistence.jdbc.MappingTag.MAP_KEY_COL; |
| import static org.apache.openjpa.persistence.jdbc.MappingTag.MAP_KEY_ENUMERATED; |
| import static org.apache.openjpa.persistence.jdbc.MappingTag.MAP_KEY_JOIN_COL; |
| import static org.apache.openjpa.persistence.jdbc.MappingTag.MAP_KEY_TEMPORAL; |
| import static org.apache.openjpa.persistence.jdbc.MappingTag.NAME; |
| import static org.apache.openjpa.persistence.jdbc.MappingTag.ORDER_COLUMN; |
| import static org.apache.openjpa.persistence.jdbc.MappingTag.PK_JOIN_COL; |
| import static org.apache.openjpa.persistence.jdbc.MappingTag.SECONDARY_TABLE; |
| import static org.apache.openjpa.persistence.jdbc.MappingTag.SQL_RESULT_SET_MAPPING; |
| import static org.apache.openjpa.persistence.jdbc.MappingTag.TABLE; |
| import static org.apache.openjpa.persistence.jdbc.MappingTag.TABLE_GEN; |
| import static org.apache.openjpa.persistence.jdbc.MappingTag.TEMPORAL; |
| import static org.apache.openjpa.persistence.jdbc.MappingTag.UNIQUE; |
| import static org.apache.openjpa.persistence.jdbc.MappingTag.VERSION_COL; |
| import static org.apache.openjpa.persistence.jdbc.MappingTag.VERSION_COLS; |
| |
| import java.lang.reflect.Modifier; |
| import java.sql.Types; |
| import java.util.ArrayList; |
| import java.util.Arrays; |
| import java.util.EnumSet; |
| import java.util.HashMap; |
| import java.util.List; |
| import java.util.Map; |
| import java.util.Set; |
| |
| import javax.persistence.DiscriminatorType; |
| import javax.persistence.EnumType; |
| import javax.persistence.InheritanceType; |
| import javax.persistence.TemporalType; |
| |
| import org.apache.openjpa.jdbc.conf.JDBCConfiguration; |
| import org.apache.openjpa.jdbc.identifier.DBIdentifier; |
| import org.apache.openjpa.jdbc.identifier.QualifiedDBIdentifier; |
| import org.apache.openjpa.jdbc.kernel.EagerFetchModes; |
| import org.apache.openjpa.jdbc.meta.ClassMapping; |
| import org.apache.openjpa.jdbc.meta.ClassMappingInfo; |
| import org.apache.openjpa.jdbc.meta.DiscriminatorMappingInfo; |
| import org.apache.openjpa.jdbc.meta.FieldMapping; |
| import org.apache.openjpa.jdbc.meta.FieldMappingInfo; |
| import org.apache.openjpa.jdbc.meta.MappingInfo; |
| import org.apache.openjpa.jdbc.meta.MappingRepository; |
| import org.apache.openjpa.jdbc.meta.QueryResultMapping; |
| import org.apache.openjpa.jdbc.meta.QueryResultMapping.PCResult; |
| import org.apache.openjpa.jdbc.meta.SequenceMapping; |
| import org.apache.openjpa.jdbc.meta.strats.EnumValueHandler; |
| import org.apache.openjpa.jdbc.meta.strats.FlatClassStrategy; |
| import org.apache.openjpa.jdbc.meta.strats.FullClassStrategy; |
| import org.apache.openjpa.jdbc.meta.strats.NoneClassStrategy; |
| import org.apache.openjpa.jdbc.meta.strats.VerticalClassStrategy; |
| import org.apache.openjpa.jdbc.schema.Column; |
| import org.apache.openjpa.jdbc.schema.Unique; |
| import org.apache.openjpa.jdbc.sql.DBDictionary; |
| import org.apache.openjpa.lib.log.Log; |
| import org.apache.openjpa.lib.meta.SourceTracker; |
| import org.apache.openjpa.lib.util.Localizer; |
| import org.apache.openjpa.lib.util.StringUtil; |
| import org.apache.openjpa.meta.ClassMetaData; |
| import org.apache.openjpa.meta.FieldMetaData; |
| import org.apache.openjpa.meta.JavaTypes; |
| import org.apache.openjpa.persistence.XMLPersistenceMetaDataParser; |
| import org.apache.openjpa.util.InternalException; |
| import org.apache.openjpa.util.MetaDataException; |
| import org.apache.openjpa.util.UserException; |
| import org.xml.sax.Attributes; |
| import org.xml.sax.Locator; |
| import org.xml.sax.SAXException; |
| /** |
| * Custom SAX parser used by the system to parse persistence mapping files. |
| * |
| * @author Steve Kim |
| */ |
| public class XMLPersistenceMappingParser |
| extends XMLPersistenceMetaDataParser { |
| |
| private static final Map<String, MappingTag> _elems = |
| new HashMap<>(); |
| |
| static { |
| _elems.put("association-override", ASSOC_OVERRIDE); |
| _elems.put("attribute-override", ATTR_OVERRIDE); |
| _elems.put("collection-table", COLLECTION_TABLE); |
| _elems.put("column", COL); |
| _elems.put("columns", COLS); |
| _elems.put("column-name", COLUMN_NAME); |
| _elems.put("column-result", COLUMN_RESULT); |
| _elems.put("data-store-id-column", DATASTORE_ID_COL); |
| _elems.put("delimited-identifiers", DELIMITED_IDS); |
| _elems.put("discriminator-column", DISCRIM_COL); |
| _elems.put("discriminator-value", DISCRIM_VAL); |
| _elems.put("entity-result", ENTITY_RESULT); |
| _elems.put("enumerated", ENUMERATED); |
| _elems.put("field-result", FIELD_RESULT); |
| _elems.put("foreign-key", FK); |
| _elems.put("fk_column-names", FK_COL_NAMES); |
| _elems.put("fk_column_name", FK_COL_NAME); |
| _elems.put("inheritance", INHERITANCE); |
| _elems.put("index", INDEX); |
| _elems.put("join-column", JOIN_COL); |
| _elems.put("inverse-join-column", COL); |
| _elems.put("join-table", JOIN_TABLE); |
| _elems.put("map-key-enumerated", MAP_KEY_ENUMERATED); |
| _elems.put("map-key-column", MAP_KEY_COL); |
| _elems.put("map-key-join-column", MAP_KEY_JOIN_COL); |
| _elems.put("map-key-temporal", MAP_KEY_TEMPORAL); |
| _elems.put("name", NAME); |
| _elems.put("order-column", ORDER_COLUMN); |
| _elems.put("primary-key-join-column", PK_JOIN_COL); |
| _elems.put("secondary-table", SECONDARY_TABLE); |
| _elems.put("sql-result-set-mapping", SQL_RESULT_SET_MAPPING); |
| _elems.put("table", TABLE); |
| _elems.put("table-generator", TABLE_GEN); |
| _elems.put("temporal", TEMPORAL); |
| _elems.put("unique-constraint", UNIQUE); |
| _elems.put("version-columns", VERSION_COLS); |
| _elems.put("version-column", VERSION_COL); |
| } |
| |
| private static final Localizer _loc = Localizer.forPackage |
| (XMLPersistenceMappingParser.class); |
| |
| private String _override = null; |
| private String _schema = null; |
| private String _colTable = null; |
| private String _secondaryTable = null; |
| private List<Column> _cols = null; |
| private List<Column> _joinCols = null; |
| private List<Column> _supJoinCols = null; |
| private boolean _lob = false; |
| private TemporalType _temporal = null; |
| private EnumSet<UniqueFlag> _unique = EnumSet.noneOf(UniqueFlag.class); |
| private DiscriminatorType _discType; |
| private Column _discCol; |
| private int _resultIdx = 0; |
| private final DBDictionary _dict; |
| |
| // ForeignKey info |
| private Attributes _foreignKeyAttributes = null; |
| private List<String> _columnNamesList = null; |
| private String[] _columnNames = {}; |
| |
| private List<Column> _versionColumnsList = null; |
| |
| private final Map<Class<?>, ArrayList<DeferredEmbeddableOverrides>> |
| _deferredMappings = new HashMap<>(); |
| |
| /** |
| * Constructor; supply configuration. |
| */ |
| public XMLPersistenceMappingParser(JDBCConfiguration conf) { |
| super(conf); |
| _dict = conf.getDBDictionaryInstance(); |
| } |
| |
| @Override |
| protected void reset() { |
| super.reset(); |
| clearColumnInfo(); |
| clearClassInfo(); |
| clearSecondaryTableInfo(); |
| _override = null; |
| _schema = null; |
| _resultIdx = 0; |
| } |
| |
| @Override |
| protected Object startSystemMappingElement(String name, Attributes attrs) |
| throws SAXException { |
| MappingTag tag = _elems.get(name); |
| if (tag == null) { |
| if ("schema".equals(name)) |
| return name; |
| return null; |
| } |
| |
| boolean ret; |
| switch (tag) { |
| case TABLE_GEN: |
| ret = startTableGenerator(attrs); |
| break; |
| case SQL_RESULT_SET_MAPPING: |
| ret = startSQLResultSetMapping(attrs); |
| break; |
| case ENTITY_RESULT: |
| ret = startEntityResult(attrs); |
| break; |
| case FIELD_RESULT: |
| ret = startFieldResult(attrs); |
| break; |
| case COLUMN_RESULT: |
| ret = startColumnResult(attrs); |
| break; |
| default: |
| ret = false; |
| } |
| return (ret) ? tag : null; |
| } |
| |
| @Override |
| protected void endSystemMappingElement(String name) |
| throws SAXException { |
| MappingTag tag = _elems.get(name); |
| if (tag == null) { |
| if ("schema".equals(name)) { |
| _schema = currentText(); |
| getRepository().getMetaDataFactory().getDefaults().setDefaultSchema(_schema); |
| } |
| return; |
| } |
| |
| switch (tag) { |
| case SQL_RESULT_SET_MAPPING: |
| endSQLResultSetMapping(); |
| break; |
| case ENTITY_RESULT: |
| endEntityResult(); |
| break; |
| } |
| } |
| |
| @Override |
| protected Object startClassMappingElement(String name, Attributes attrs) |
| throws SAXException { |
| MappingTag tag = _elems.get(name); |
| if (tag == null) |
| return null; |
| |
| boolean ret; |
| switch (tag) { |
| case TABLE: |
| ret = startTable(attrs); |
| break; |
| case SECONDARY_TABLE: |
| ret = startSecondaryTable(attrs); |
| break; |
| case DISCRIM_COL: |
| parseDiscriminatorColumn(attrs); |
| _discCol = parseColumn(attrs); |
| ret = true; |
| break; |
| case DISCRIM_VAL: |
| ret = true; |
| break; |
| case INHERITANCE: |
| ret = startInheritance(attrs); |
| break; |
| case ASSOC_OVERRIDE: |
| case ATTR_OVERRIDE: |
| ret = startAttributeOverride(attrs); |
| break; |
| case PK_JOIN_COL: |
| ret = startPrimaryKeyJoinColumn(attrs); |
| break; |
| case COL: |
| ret = startColumn(attrs); |
| break; |
| case COLS: |
| ret = true; |
| break; |
| case JOIN_COL: |
| ret = startJoinColumn(attrs); |
| break; |
| case JOIN_TABLE: |
| ret = startJoinTable(attrs); |
| break; |
| case TABLE_GEN: |
| ret = startTableGenerator(attrs); |
| break; |
| case UNIQUE: |
| ret = startUniqueConstraint(attrs); |
| break; |
| case NAME: |
| ret = true; |
| break; |
| case TEMPORAL: |
| case ENUMERATED: |
| case MAP_KEY_ENUMERATED: |
| case MAP_KEY_TEMPORAL: |
| ret = true; |
| break; |
| case SQL_RESULT_SET_MAPPING: |
| ret = startSQLResultSetMapping(attrs); |
| break; |
| case ENTITY_RESULT: |
| ret = startEntityResult(attrs); |
| break; |
| case FIELD_RESULT: |
| ret = startFieldResult(attrs); |
| break; |
| case COLUMN_RESULT: |
| ret = startColumnResult(attrs); |
| break; |
| case COLUMN_NAME: |
| ret = true; |
| break; |
| case COLLECTION_TABLE: |
| ret = startCollectionTable(attrs); |
| break; |
| case MAP_KEY_COL: |
| ret = startMapKeyColumn(attrs); |
| break; |
| case MAP_KEY_JOIN_COL: |
| ret = startMapKeyJoinColumn(attrs); |
| break; |
| case DATASTORE_ID_COL: |
| ret = startDatastoreIdCol(attrs); |
| break; |
| case INDEX: |
| ret = startIndex(attrs); |
| break; |
| case FK: |
| ret = startForeignKey(attrs); |
| break; |
| case FK_COL_NAMES: |
| ret = startFKColumnNames(attrs); |
| break; |
| case FK_COL_NAME: |
| ret = true; |
| break; |
| case VERSION_COLS: |
| ret = startVersionColumns(attrs); |
| break; |
| case VERSION_COL: |
| ret = startVersionColumn(attrs); |
| break; |
| default: |
| ret = false; |
| } |
| return (ret) ? tag : null; |
| } |
| |
| private boolean endName() { |
| String name = this.currentText(); |
| if (StringUtil.isNotEmpty(name)) { |
| Object current = currentElement(); |
| if (current instanceof Unique) { |
| Unique unq = (Unique)current; |
| unq.setIdentifier(DBIdentifier.newConstraint(name, delimit())); |
| } |
| } |
| |
| return true; |
| } |
| |
| @Override |
| protected void endClassMappingElement(String name) |
| throws SAXException { |
| MappingTag tag = _elems.get(name); |
| if (tag == null) |
| return; |
| |
| switch (tag) { |
| case SECONDARY_TABLE: |
| endSecondaryTable(); |
| break; |
| case DISCRIM_VAL: |
| endDiscriminatorValue(); |
| break; |
| case ASSOC_OVERRIDE: |
| case ATTR_OVERRIDE: |
| endAttributeOverride(); |
| break; |
| case JOIN_TABLE: |
| endJoinTable(); |
| break; |
| case TEMPORAL: |
| endTemporal(); |
| break; |
| case MAP_KEY_TEMPORAL: |
| endMapKeyTemporal(); |
| break; |
| case ENUMERATED: |
| endEnumerated(); |
| break; |
| case MAP_KEY_ENUMERATED: |
| endMapKeyEnumerated(); |
| break; |
| case SQL_RESULT_SET_MAPPING: |
| endSQLResultSetMapping(); |
| break; |
| case ENTITY_RESULT: |
| endEntityResult(); |
| break; |
| case UNIQUE: |
| endUniqueConstraint(); |
| break; |
| case COLUMN_NAME: |
| endColumnName(); |
| break; |
| case TABLE_GEN: |
| endTableGenerator(); |
| break; |
| case NAME: |
| endName(); |
| break; |
| case FK: |
| endForeignKey(); |
| break; |
| case FK_COL_NAMES: |
| endFKColumnNames(); |
| break; |
| case FK_COL_NAME: |
| endFKColumnName(); |
| break; |
| case VERSION_COLS: |
| endVersionColumns(); |
| break; |
| } |
| } |
| |
| @Override |
| protected void startClassMapping(ClassMetaData meta, boolean mappedSuper, |
| Attributes attrs) |
| throws SAXException { |
| if (mappedSuper) |
| ((ClassMapping) meta).getMappingInfo().setStrategy |
| (NoneClassStrategy.ALIAS); |
| } |
| |
| @Override |
| protected void endClassMapping(ClassMetaData meta) |
| throws SAXException { |
| ClassMapping cm = (ClassMapping) meta; |
| if (_schema != null) |
| cm.getMappingInfo().setSchemaIdentifier(DBIdentifier.newSchema(_schema, delimit())); |
| |
| if (_supJoinCols != null) |
| cm.getMappingInfo().setColumns(_supJoinCols); |
| |
| if (_discCol != null) { |
| DiscriminatorMappingInfo dinfo = cm.getDiscriminator() |
| .getMappingInfo(); |
| switch (_discType) { |
| case CHAR: |
| _discCol.setJavaType(JavaTypes.CHAR); |
| cm.getDiscriminator().setJavaType(JavaTypes.CHAR); |
| break; |
| case INTEGER: |
| _discCol.setJavaType(JavaTypes.INT); |
| cm.getDiscriminator().setJavaType(JavaTypes.INT); |
| break; |
| default: |
| _discCol.setJavaType(JavaTypes.STRING); |
| cm.getDiscriminator().setJavaType(JavaTypes.STRING); |
| break; |
| } |
| dinfo.setColumns(Arrays.asList(new Column[]{ _discCol })); |
| } |
| clearClassInfo(); |
| } |
| |
| /** |
| * Clear cached class mapping info. |
| */ |
| private void clearClassInfo() { |
| _supJoinCols = null; |
| _discCol = null; |
| _discType = null; |
| } |
| |
| /** |
| * Start tracking secondary table information and columns |
| */ |
| private boolean startSecondaryTable(Attributes attrs) |
| throws SAXException { |
| _secondaryTable = toTableIdentifier(attrs.getValue("schema"), |
| attrs.getValue("name")).getName(); |
| ((ClassMapping)currentElement()).getMappingInfo() |
| .addSecondaryTable(DBIdentifier.newTable(_secondaryTable)); |
| return true; |
| } |
| |
| /** |
| * Set the secondary table information back to the owning class mapping. |
| */ |
| private void endSecondaryTable() { |
| ClassMapping cm = (ClassMapping) currentElement(); |
| ClassMappingInfo info = cm.getMappingInfo(); |
| info.setSecondaryTableJoinColumns(DBIdentifier.newTable(_secondaryTable, delimit()), _joinCols); |
| clearSecondaryTableInfo(); |
| } |
| |
| /** |
| * Clear cached secondary table info. |
| */ |
| private void clearSecondaryTableInfo() { |
| _joinCols = null; |
| _secondaryTable = null; |
| } |
| |
| /** |
| * Parse table-generator. |
| */ |
| private boolean startTableGenerator(Attributes attrs) { |
| String name = attrs.getValue("name"); |
| Log log = getLog(); |
| if (log.isTraceEnabled()) |
| log.trace(_loc.get("parse-gen", name)); |
| if (getRepository().getCachedSequenceMetaData(name) != null |
| && log.isWarnEnabled()) |
| log.warn(_loc.get("override-gen", name)); |
| |
| SequenceMapping seq = (SequenceMapping) getRepository(). |
| addSequenceMetaData(name); |
| seq.setSequencePlugin(SequenceMapping.IMPL_VALUE_TABLE); |
| seq.setTableIdentifier(toTableIdentifier(attrs.getValue("schema"), |
| attrs.getValue("table"))); |
| seq.setPrimaryKeyColumnIdentifier(DBIdentifier.newColumn(attrs.getValue("pk-column-name"), delimit())); |
| seq.setSequenceColumnIdentifier(DBIdentifier.newColumn(attrs.getValue("value-column-name"), delimit())); |
| seq.setPrimaryKeyValue(attrs.getValue("pk-column-value")); |
| String val = attrs.getValue("initial-value"); |
| if (val != null) |
| seq.setInitialValue(Integer.parseInt(val)); |
| val = attrs.getValue("allocation-size"); |
| if (val != null) |
| seq.setAllocate(Integer.parseInt(val)); |
| |
| Object cur = currentElement(); |
| Object scope = (cur instanceof ClassMetaData) |
| ? ((ClassMetaData) cur).getDescribedType() : null; |
| seq.setSource(getSourceFile(), scope, SourceTracker.SRC_XML); |
| Locator locator = getLocation().getLocator(); |
| if (locator != null) { |
| seq.setLineNumber(locator.getLineNumber()); |
| seq.setColNumber(locator.getColumnNumber()); |
| } |
| pushElement(seq); |
| return true; |
| } |
| |
| private void endTableGenerator() { |
| popElement(); |
| } |
| |
| /** |
| * Parse inheritance. |
| */ |
| private boolean startInheritance(Attributes attrs) { |
| String val = attrs.getValue("strategy"); |
| if (val == null) |
| return true; |
| |
| ClassMapping cm = (ClassMapping) currentElement(); |
| ClassMappingInfo info = cm.getMappingInfo(); |
| switch (Enum.valueOf(InheritanceType.class, val)) { |
| case SINGLE_TABLE: |
| info.setHierarchyStrategy(FlatClassStrategy.ALIAS); |
| break; |
| case JOINED: |
| info.setHierarchyStrategy(VerticalClassStrategy.ALIAS); |
| break; |
| case TABLE_PER_CLASS: |
| info.setHierarchyStrategy(FullClassStrategy.ALIAS); |
| break; |
| } |
| return true; |
| } |
| |
| /** |
| * Parse discriminator-value. |
| */ |
| private void endDiscriminatorValue() { |
| String val = currentText(); |
| if (StringUtil.isEmpty(val)) |
| return; |
| |
| ClassMapping cm = (ClassMapping) currentElement(); |
| cm.getDiscriminator().getMappingInfo().setValue(val); |
| |
| if (Modifier.isAbstract(cm.getDescribedType().getModifiers()) |
| && getLog().isInfoEnabled()) { |
| getLog().info( |
| _loc.get("discriminator-on-abstract-class", cm |
| .getDescribedType().getName())); |
| } |
| } |
| |
| /** |
| * Parse temporal. |
| */ |
| private void endTemporal() { |
| String temp = currentText(); |
| if (!StringUtil.isEmpty(temp)) |
| _temporal = Enum.valueOf(TemporalType.class, temp); |
| } |
| |
| /** |
| * Parse temporal. |
| */ |
| private void endMapKeyTemporal() { |
| String temp = currentText(); |
| TemporalType _mapKeyTemporal = null; |
| if (!StringUtil.isEmpty(temp)) |
| _mapKeyTemporal = Enum.valueOf(TemporalType.class, temp); |
| FieldMapping fm = (FieldMapping) currentElement(); |
| List<Column> cols = fm.getKeyMapping().getValueInfo().getColumns(); |
| if (cols.isEmpty()) { |
| cols = Arrays.asList(new Column[]{ new Column() }); |
| fm.getKeyMapping().getValueInfo().setColumns(cols); |
| } |
| |
| Column col = (Column) cols.get(0); |
| switch (_mapKeyTemporal) { |
| case DATE: |
| col.setType(Types.DATE); |
| break; |
| case TIME: |
| col.setType(Types.TIME); |
| break; |
| case TIMESTAMP: |
| col.setType(Types.TIMESTAMP); |
| break; |
| } |
| } |
| |
| /** |
| * Parse enumerated. |
| */ |
| private void endEnumerated() { |
| String text = currentText(); |
| if (StringUtil.isEmpty(text)) |
| return; |
| EnumType type = Enum.valueOf(EnumType.class, text); |
| |
| FieldMapping fm = (FieldMapping) currentElement(); |
| String strat = EnumValueHandler.class.getName() + "(StoreOrdinal=" |
| + (type == EnumType.ORDINAL) + ")"; |
| if (fm.isElementCollection()) |
| fm.getElementMapping().getValueInfo().setStrategy(strat); |
| else |
| fm.getValueInfo().setStrategy(strat); |
| } |
| |
| /** |
| * Parse map-key-enumerated. |
| */ |
| private void endMapKeyEnumerated() { |
| String text = currentText(); |
| if (StringUtil.isEmpty(text)) |
| return; |
| EnumType type = Enum.valueOf(EnumType.class, text); |
| |
| FieldMapping fm = (FieldMapping) currentElement(); |
| String strat = EnumValueHandler.class.getName() + "(StoreOrdinal=" |
| + (type == EnumType.ORDINAL) + ")"; |
| fm.getKeyMapping().getValueInfo().setStrategy(strat); |
| } |
| |
| @Override |
| protected boolean startLob(Attributes attrs) |
| throws SAXException { |
| if (super.startLob(attrs)) { |
| _lob = true; |
| return true; |
| } |
| return false; |
| } |
| |
| /** |
| * Extend to clear annotation mapping info. |
| */ |
| @Override |
| protected void startFieldMapping(FieldMetaData field, Attributes attrs) |
| throws SAXException { |
| super.startFieldMapping(field, attrs); |
| if (getAnnotationParser() != null) { |
| FieldMapping fm = (FieldMapping) field; |
| fm.getMappingInfo().clear(); |
| fm.getValueInfo().clear(); |
| fm.getElementMapping().getValueInfo().clear(); |
| fm.getKeyMapping().getValueInfo().clear(); |
| } |
| } |
| |
| /** |
| * Extend to set the columns. |
| */ |
| @Override |
| protected void endFieldMapping(FieldMetaData field) |
| throws SAXException { |
| // setup columns with cached lob and temporal info |
| FieldMapping fm = (FieldMapping) field; |
| if (_lob || _temporal != null) { |
| int typeCode = fm.isElementCollection() ? fm.getElement().getDeclaredTypeCode() : |
| fm.getDeclaredTypeCode(); |
| Class<?> type = fm.isElementCollection() ? fm.getElement().getDeclaredType() : fm.getDeclaredType(); |
| if (_cols == null) { |
| _cols = new ArrayList<>(1); |
| _cols.add(new Column()); |
| } |
| for (Column col : _cols) { |
| if (_lob && (typeCode == JavaTypes.STRING |
| || type == char[].class |
| || type == Character[].class)) { |
| col.setSize(-1); |
| col.setType(Types.CLOB); |
| } else if (_lob) |
| col.setType(Types.BLOB); |
| else { |
| switch (_temporal) { |
| case DATE: |
| col.setType(Types.DATE); |
| break; |
| case TIME: |
| col.setType(Types.TIME); |
| break; |
| case TIMESTAMP: |
| col.setType(Types.TIMESTAMP); |
| break; |
| } |
| } |
| } |
| } |
| |
| if (_cols != null) { |
| switch (fm.getDeclaredTypeCode()) { |
| case JavaTypes.ARRAY: |
| Class<?> type = fm.getDeclaredType(); |
| if (type == byte[].class || type == Byte[].class |
| || type == char[].class || type == Character[].class ) { |
| fm.getValueInfo().setColumns(_cols); |
| break; |
| } |
| // else no break |
| case JavaTypes.COLLECTION: |
| if (!fm.getValue().isSerialized()) { |
| fm.getElementMapping().getValueInfo().setColumns(_cols); |
| } else { |
| fm.getValueInfo().setColumns(_cols); |
| } |
| break; |
| case JavaTypes.MAP: |
| fm.getElementMapping().getValueInfo().setColumns(_cols); |
| break; |
| default: |
| fm.getValueInfo().setColumns(_cols); |
| } |
| if (_colTable != null) |
| fm.getMappingInfo().setTableIdentifier(DBIdentifier.newTable(_colTable, delimit())); |
| setUnique(fm); |
| } |
| clearColumnInfo(); |
| } |
| |
| /** |
| * Set unique for field. |
| */ |
| private void setUnique(FieldMapping fm) { |
| setUnique(fm, _unique); |
| } |
| |
| private void setUnique(FieldMapping fm, EnumSet<UniqueFlag> unique) { |
| if (unique.size() == 2) // i.e. TRUE & FALSE |
| getLog().warn(_loc.get("inconsist-col-attrs", fm)); |
| else if (unique.contains(UniqueFlag.TRUE)) |
| fm.getValueInfo().setUnique(new Unique()); |
| } |
| |
| /** |
| * Clear field level column information. |
| */ |
| private void clearColumnInfo() { |
| _cols = null; |
| _joinCols = null; |
| _colTable = null; |
| _lob = false; |
| _temporal = null; |
| _unique.clear(); |
| } |
| |
| /** |
| * Parse attribute-override. |
| */ |
| private boolean startAttributeOverride(Attributes attr) { |
| _override = attr.getValue("name"); |
| return true; |
| } |
| |
| /** |
| * Set attribute override into proper mapping. |
| */ |
| private void endAttributeOverride() |
| throws SAXException { |
| Object elem = currentElement(); |
| FieldMapping fm = null; |
| if (elem instanceof ClassMapping) |
| fm = getAttributeOverride((ClassMapping) elem); |
| else { |
| FieldMapping basefm = (FieldMapping) elem; |
| |
| fm = getAttributeOverrideForEmbeddable(basefm, _override, false); |
| if (fm == null) { |
| DeferredEmbeddableOverrides dfm = |
| getDeferredFieldMappingInfo( |
| AnnotationPersistenceMappingParser. |
| getEmbeddedClassType(basefm, _override), |
| basefm, _override, true); |
| dfm._defCols = _cols; |
| dfm._defTable = DBIdentifier.newTable(_colTable, delimit()); |
| dfm._attrName = _override; |
| dfm._unique = _unique; |
| } |
| } |
| if (fm != null && _cols != null) { |
| fm.getValueInfo().setColumns(_cols); |
| if (_colTable != null) |
| fm.getMappingInfo().setTableIdentifier(DBIdentifier.newTable(_colTable, delimit())); |
| setUnique(fm); |
| } |
| clearColumnInfo(); |
| _override = null; |
| } |
| |
| /** |
| * Return the proper override. |
| */ |
| private FieldMapping getAttributeOverride(ClassMapping cm) { |
| FieldMapping sup = (FieldMapping) cm.getDefinedSuperclassField |
| (_override); |
| if (sup == null) |
| sup = (FieldMapping) cm.addDefinedSuperclassField(_override, |
| Object.class, Object.class); |
| return sup; |
| } |
| |
| /** |
| * Return the proper override. |
| */ |
| private FieldMapping getAttributeOverrideForEmbeddable(FieldMapping fm, |
| String attrName, boolean mustExist) |
| throws SAXException { |
| return AnnotationPersistenceMappingParser.getEmbeddedFieldMapping(fm, |
| attrName, mustExist); |
| } |
| |
| /** |
| * Parse table. |
| */ |
| private boolean startTable(Attributes attrs) |
| throws SAXException { |
| ClassMapping mapping = (ClassMapping) currentElement(); |
| if (mapping.isAbstract()) |
| throw new UserException(_loc.get("table-not-allowed", mapping)); |
| DBIdentifier table = toTableIdentifier(attrs.getValue("schema"), |
| attrs.getValue("name")); |
| if (!DBIdentifier.isNull(table)) |
| mapping.getMappingInfo().setTableIdentifier(table); |
| return true; |
| } |
| |
| /** |
| * Parse join-table. |
| */ |
| private boolean startJoinTable(Attributes attrs) |
| throws SAXException { |
| DBIdentifier sTable = toTableIdentifier(attrs.getValue("schema"), |
| attrs.getValue("name")); |
| if (!DBIdentifier.isNull(sTable)) { |
| Object elem = currentElement(); |
| FieldMapping fm = null; |
| if (elem instanceof FieldMapping) { |
| fm = (FieldMapping) elem; |
| if (_override != null) { |
| FieldMapping basefm = (FieldMapping) elem; |
| fm = getAttributeOverrideForEmbeddable(basefm, |
| _override, false); |
| if (fm == null) { |
| DeferredEmbeddableOverrides dfm = |
| getDeferredFieldMappingInfo( |
| AnnotationPersistenceMappingParser. |
| getEmbeddedClassType(basefm, _override), |
| basefm, _override, true); |
| dfm._defTable = sTable.clone(); |
| dfm._attrName = _override; |
| } |
| } |
| } else if (elem instanceof ClassMapping) { |
| ClassMapping cm = (ClassMapping) elem; |
| fm = getAttributeOverride(cm); |
| } |
| if (fm != null) |
| fm.getMappingInfo().setTableIdentifier(sTable); |
| } |
| return true; |
| } |
| |
| /** |
| * Set the join table information back. |
| */ |
| private void endJoinTable() throws SAXException { |
| Object elem = currentElement(); |
| FieldMapping fm = null; |
| if (elem instanceof FieldMapping) { |
| fm = (FieldMapping) elem; |
| if (_override != null) { |
| FieldMapping basefm = (FieldMapping) elem; |
| fm = getAttributeOverrideForEmbeddable(basefm, _override, |
| false); |
| if (fm == null) { |
| DeferredEmbeddableOverrides dfm = |
| getDeferredFieldMappingInfo( |
| AnnotationPersistenceMappingParser. |
| getEmbeddedClassType(basefm, _override), |
| basefm, _override, true); |
| dfm._defCols = _cols; |
| dfm._defElemJoinCols = _joinCols; |
| dfm._attrName = _override; |
| } |
| } |
| } else if (elem instanceof ClassMapping){ |
| ClassMapping cm = (ClassMapping) elem; |
| fm = getAttributeOverride(cm); |
| } |
| |
| if (fm != null) { |
| if (_joinCols != null) |
| fm.getMappingInfo().setColumns(_joinCols); |
| if (_cols != null) |
| fm.getElementMapping().getValueInfo().setColumns(_cols); |
| } |
| clearColumnInfo(); |
| } |
| |
| /** |
| * Parse primary-key-join-column. |
| */ |
| private boolean startPrimaryKeyJoinColumn(Attributes attrs) |
| throws SAXException { |
| Column col = parseColumn(attrs); |
| col.setFlag(Column.FLAG_PK_JOIN, true); |
| // pk join columns on fields act as field cols |
| if (currentElement() instanceof FieldMapping) { |
| if (_cols == null) |
| _cols = new ArrayList<>(3); |
| _cols.add(col); |
| } else if (currentParent() == SECONDARY_TABLE) { |
| // pk join columns in secondary table acts as join cols |
| if (_joinCols == null) |
| _joinCols = new ArrayList<>(3); |
| _joinCols.add(col); |
| } else { |
| // must be pk join cols from this class to superclass |
| if (_supJoinCols == null) |
| _supJoinCols = new ArrayList<>(3); |
| _supJoinCols.add(col); |
| } |
| return true; |
| } |
| |
| /** |
| * Parse join-column. |
| */ |
| private boolean startJoinColumn(Attributes attrs) |
| throws SAXException { |
| // only join cols in a join table join field table to class table; |
| // others act as data fk cols |
| Object currentParent = currentParent(); |
| if (currentParent == COLLECTION_TABLE) { |
| FieldMapping fm = (FieldMapping) peekElement(); |
| Column col = parseColumn(attrs); |
| List<Column> colList = fm.getMappingInfo().getColumns(); |
| if (colList.isEmpty()) { |
| colList = new ArrayList<>(); |
| fm.getMappingInfo().setColumns(colList); |
| } |
| colList.add(col); |
| fm.getMappingInfo().setColumns(colList); |
| return true; |
| } |
| |
| if (currentParent != JOIN_TABLE) |
| return startColumn(attrs); |
| |
| if (_joinCols == null) |
| _joinCols = new ArrayList<>(3); |
| _joinCols.add(parseColumn(attrs)); |
| return true; |
| } |
| |
| /** |
| * Parse column. |
| */ |
| private boolean startColumn(Attributes attrs) |
| throws SAXException { |
| Column col = parseColumn(attrs); |
| Object obj = peekElement(); |
| if (obj instanceof FieldMapping) { |
| FieldMapping fm = (FieldMapping)obj; |
| // a collection of basic types |
| // the column is in a separate table |
| if (fm.isElementCollection() && |
| !fm.getElementMapping().isEmbedded()) { |
| List<Column> list = fm.getElementMapping().getValueInfo().getColumns(); |
| if (list.size() == 0) { |
| list = new ArrayList<>(); |
| fm.getElementMapping().getValueInfo().setColumns(list); |
| } |
| list.add(col); |
| return true; |
| } |
| } |
| if (_cols == null) |
| _cols = new ArrayList<>(3); |
| _cols.add(col); |
| return true; |
| } |
| |
| /** |
| * Parse map-key-column. |
| */ |
| private boolean startMapKeyColumn(Attributes attrs) |
| throws SAXException { |
| FieldMapping fm = (FieldMapping) peekElement(); |
| Column col = parseColumn(attrs); |
| MappingInfo info = fm.getKeyMapping().getValueInfo(); |
| List<Column> cols = new ArrayList<>(); |
| cols.add(col); |
| info.setColumns(cols); |
| return true; |
| } |
| |
| /** |
| * Parse map-key-join-column. |
| */ |
| private boolean startMapKeyJoinColumn(Attributes attrs) |
| throws SAXException { |
| boolean retVal = startMapKeyColumn(attrs); |
| // check if name is not set, set it to default: the |
| // concatenation of the name of the referencing property |
| // or field name, "-", "KEY" |
| FieldMapping fm = (FieldMapping) peekElement(); |
| MappingInfo info = fm.getKeyMapping().getValueInfo(); |
| List<Column> cols = info.getColumns(); |
| Column col = cols.get(0); |
| if (DBIdentifier.isNull(col.getIdentifier())) { |
| col.setIdentifier(DBIdentifier.newColumn(fm.getName() + "_" + "KEY", delimit())); |
| } |
| |
| return retVal; |
| } |
| |
| /** |
| * Create a column with the given attributes. |
| */ |
| private Column parseColumn(Attributes attrs) |
| throws SAXException { |
| Column col = new Column(); |
| String val = attrs.getValue("name"); |
| if (val != null) |
| col.setIdentifier(DBIdentifier.newColumn(val, delimit())); |
| val = attrs.getValue("referenced-column-name"); |
| if (val != null) { |
| setTargetIdentifier(col, val); |
| } |
| val = attrs.getValue("column-definition"); |
| if (val != null) |
| col.setTypeIdentifier(DBIdentifier.newColumnDefinition(val)); |
| val = attrs.getValue("precision"); |
| if (val != null) |
| col.setSize(Integer.parseInt(val)); |
| val = attrs.getValue("length"); |
| if (val != null) |
| col.setSize(Integer.parseInt(val)); |
| val = attrs.getValue("scale"); |
| if (val != null) |
| col.setDecimalDigits(Integer.parseInt(val)); |
| val = attrs.getValue("nullable"); |
| if (val != null) |
| col.setNotNull("false".equals(val)); |
| val = attrs.getValue("insertable"); |
| if (val != null) |
| col.setFlag(Column.FLAG_UNINSERTABLE, "false".equals(val)); |
| val = attrs.getValue("updatable"); |
| if (val != null) |
| col.setFlag(Column.FLAG_UNUPDATABLE, "false".equals(val)); |
| |
| val = attrs.getValue("unique"); |
| if (val != null) |
| _unique.add(Enum.valueOf(UniqueFlag.class, val.toUpperCase())); |
| val = attrs.getValue("table"); |
| if (val != null) { |
| if (_colTable != null && !_colTable.equals(val)) |
| throw getException(_loc.get("second-inconsist", |
| currentElement())); |
| _colTable = val; |
| } |
| return col; |
| } |
| |
| /** |
| * Sets reference column name of the given column taking into account |
| * that the given reference name that begins with a single quote represents |
| * special meaning of a constant join column and hence not to be delimited. |
| * @param col |
| * @param refColumnName |
| * @see <a href="http://issues.apache.org/jira/browse/OPENJPA-1979">OPENJPA-1979</a> |
| */ |
| private static final char SINGLE_QUOTE = '\''; |
| protected void setTargetIdentifier(Column col, String refColumnName) { |
| if (refColumnName.charAt(0) == SINGLE_QUOTE) { |
| col.setTargetIdentifier(DBIdentifier.newConstant(refColumnName)); |
| } else { |
| col.setTargetIdentifier(DBIdentifier.newColumn(refColumnName, delimit())); |
| } |
| } |
| |
| /** |
| * Parse collectionTable. |
| */ |
| private boolean startCollectionTable(Attributes attrs) |
| throws SAXException { |
| FieldMapping fm = (FieldMapping) peekElement(); |
| |
| FieldMappingInfo info = fm.getMappingInfo(); |
| DBIdentifier ctbl = parseCollectionTable(attrs); |
| info.setTableIdentifier(ctbl); |
| return true; |
| } |
| |
| private DBIdentifier parseCollectionTable(Attributes attrs) { |
| String tVal = attrs.getValue("name"); |
| String sVal = attrs.getValue("schema"); |
| return toTableIdentifier(sVal, tVal); |
| } |
| |
| /** |
| * Form a qualified table name from a schema and table name. |
| */ |
| private DBIdentifier toTableIdentifier(String schema, String table) { |
| DBIdentifier sName = DBIdentifier.newSchema(schema, delimit()); |
| DBIdentifier tName = DBIdentifier.newTable(table, delimit()); |
| if (DBIdentifier.isEmpty(tName) || DBIdentifier.isEmpty(sName)) { |
| return tName; |
| } |
| return QualifiedDBIdentifier.newPath(sName, tName); |
| } |
| |
| |
| /** |
| * Start processing <code>sql-result-set-mapping</code> node. |
| * Pushes the {@link QueryResultMapping} onto the stack as current element. |
| */ |
| private boolean startSQLResultSetMapping(Attributes attrs) { |
| String name = attrs.getValue("name"); |
| Log log = getLog(); |
| if (log.isTraceEnabled()) |
| log.trace(_loc.get("parse-sqlrsmapping", name)); |
| |
| MappingRepository repos = (MappingRepository) getRepository(); |
| QueryResultMapping result = repos.getCachedQueryResultMapping |
| (null, name); |
| if (result != null && log.isWarnEnabled()) |
| log.warn(_loc.get("override-sqlrsmapping", name, |
| currentLocation())); |
| |
| result = repos.addQueryResultMapping(null, name); |
| result.setListingIndex(_resultIdx++); |
| addComments(result); |
| |
| Object cur = currentElement(); |
| Object scope = (cur instanceof ClassMetaData) |
| ? ((ClassMetaData) cur).getDescribedType() : null; |
| result.setSource(getSourceFile(), scope, SourceTracker.SRC_XML); |
| Locator locator = getLocation().getLocator(); |
| if (locator != null) { |
| result.setLineNumber(locator.getLineNumber()); |
| result.setColNumber(locator.getColumnNumber()); |
| } |
| pushElement(result); |
| return true; |
| } |
| |
| private void endSQLResultSetMapping() |
| throws SAXException { |
| popElement(); |
| } |
| |
| /** |
| * Start processing <code>entity-result</code> node. |
| * Pushes the {@link QueryResultMapping.PCResult} |
| * onto the stack as current element. |
| */ |
| private boolean startEntityResult(Attributes attrs) |
| throws SAXException { |
| Class<?> entityClass = classForName(attrs.getValue("entity-class")); |
| String discriminator = DBIdentifier.newColumn(attrs.getValue("discriminator-column"), delimit()).getName(); |
| |
| QueryResultMapping parent = (QueryResultMapping) currentElement(); |
| QueryResultMapping.PCResult result = parent.addPCResult(entityClass); |
| if (!StringUtil.isEmpty(discriminator)) |
| result.addMapping(PCResult.DISCRIMINATOR, discriminator); |
| pushElement(result); |
| return true; |
| } |
| |
| private void endEntityResult() |
| throws SAXException { |
| popElement(); |
| } |
| |
| /** |
| * Process a <code>field-result</code> node. |
| */ |
| private boolean startFieldResult(Attributes attrs) |
| throws SAXException { |
| String fieldName = attrs.getValue("name"); |
| String columnName = DBIdentifier.newColumn(attrs.getValue("column"), delimit()).getName(); |
| |
| QueryResultMapping.PCResult parent = (QueryResultMapping.PCResult) |
| currentElement(); |
| parent.addMapping(fieldName, columnName); |
| return true; |
| } |
| |
| /** |
| * Process a <code>column-result</code> node. |
| */ |
| private boolean startColumnResult(Attributes attrs) |
| throws SAXException { |
| QueryResultMapping parent = (QueryResultMapping) currentElement(); |
| parent.addColumnResult(attrs.getValue("name")); |
| return true; |
| } |
| |
| /** |
| * Starts processing <unique-constraint> provided the tag occurs |
| * within a ClassMapping element and <em>not</em> within a secondary |
| * table. |
| * Pushes the Unique element in the stack. |
| */ |
| private boolean startUniqueConstraint(Attributes attrs) |
| throws SAXException { |
| Unique unique = new Unique(); |
| |
| DBIdentifier name = DBIdentifier.newConstraint(attrs.getValue("name"), delimit()); |
| if (!DBIdentifier.isEmpty(name)) { |
| unique.setIdentifier(name); |
| } |
| |
| pushElement(unique); |
| return true; |
| } |
| |
| /** |
| * Ends processing <unique-constraint> provided the tag occurs |
| * within a ClassMapping element and <em>not</em> within a secondary |
| * table. The stack is popped and the Unique element is added to the |
| * ClassMappingInfo. |
| */ |
| private void endUniqueConstraint() { |
| Unique unique = (Unique) popElement(); |
| Object ctx = currentElement(); |
| DBIdentifier tableName = DBIdentifier.newTable("?"); |
| if (ctx instanceof ClassMapping) { |
| ClassMappingInfo info = ((ClassMapping) ctx).getMappingInfo(); |
| tableName = (_secondaryTable == null) |
| ? info.getTableIdentifier() : DBIdentifier.newTable(_secondaryTable, delimit()); |
| info.addUnique(tableName, unique); |
| } else if (ctx instanceof FieldMapping) {// JoinTable |
| FieldMappingInfo info = ((FieldMapping)ctx).getMappingInfo(); |
| info.addJoinTableUnique(unique); |
| } else if (ctx instanceof SequenceMapping) { |
| SequenceMapping seq = (SequenceMapping)ctx; |
| unique.setTableIdentifier(seq.getTableIdentifier()); |
| Column[] uniqueColumns = unique.getColumns(); |
| DBIdentifier[] columnNames = new DBIdentifier[uniqueColumns.length]; |
| int i = 0; |
| for (Column uniqueColumn : uniqueColumns) |
| columnNames[i++] = uniqueColumn.getIdentifier().clone(); |
| seq.setUniqueColumnsIdentifier(columnNames); |
| if (!DBIdentifier.isEmpty(unique.getIdentifier())) { |
| seq.setUniqueConstraintIdentifier(unique.getIdentifier()); |
| } |
| } else { |
| throw new InternalException(); |
| } |
| } |
| |
| /** |
| * Ends processing <column-name> tag by adding the column name in |
| * the current Unique element that resides in the top of the stack. |
| */ |
| private boolean endColumnName() { |
| Object current = currentElement(); |
| if (current instanceof Unique) { |
| Unique unique = (Unique) current; |
| Column column = new Column(); |
| column.setIdentifier(DBIdentifier.newColumn(this.currentText(), delimit())); |
| unique.addColumn(column); |
| return true; |
| } |
| return false; |
| } |
| |
| /** |
| * Track unique column settings. |
| */ |
| private static enum UniqueFlag |
| { |
| TRUE, |
| FALSE |
| } |
| |
| private void parseDiscriminatorColumn(Attributes attrs) { |
| String val = attrs.getValue("discriminator-type"); |
| if (val != null) { |
| _discType = Enum.valueOf(DiscriminatorType.class, val); |
| } |
| else { |
| _discType = DiscriminatorType.STRING; |
| } |
| |
| } |
| |
| /** |
| * Process OrderColumn. |
| */ |
| @Override |
| protected boolean startOrderColumn(Attributes attrs) |
| throws SAXException { |
| Column col = parseOrderColumn(attrs); |
| Object obj = peekElement(); |
| if (obj instanceof FieldMapping) { |
| FieldMapping fm = (FieldMapping)obj; |
| fm.getMappingInfo().setOrderColumn(col); |
| |
| } |
| return true; |
| } |
| /** |
| * Create an order column with the given attributes. |
| */ |
| private Column parseOrderColumn(Attributes attrs) |
| throws SAXException { |
| |
| Column col = new Column(); |
| String val = attrs.getValue("name"); |
| if (val != null) |
| col.setIdentifier(DBIdentifier.newColumn(val, delimit())); |
| val = attrs.getValue("column-definition"); |
| if (val != null) |
| col.setTypeIdentifier(DBIdentifier.newColumnDefinition(val)); |
| val = attrs.getValue("precision"); |
| if (val != null) |
| col.setSize(Integer.parseInt(val)); |
| val = attrs.getValue("length"); |
| if (val != null) |
| col.setSize(Integer.parseInt(val)); |
| val = attrs.getValue("scale"); |
| if (val != null) |
| col.setDecimalDigits(Integer.parseInt(val)); |
| val = attrs.getValue("nullable"); |
| if (val != null) |
| col.setNotNull("false".equals(val)); |
| val = attrs.getValue("insertable"); |
| if (val != null) |
| col.setFlag(Column.FLAG_UNINSERTABLE, "false".equals(val)); |
| val = attrs.getValue("updatable"); |
| if (val != null) |
| col.setFlag(Column.FLAG_UNUPDATABLE, "false".equals(val)); |
| |
| return col; |
| } |
| |
| /** |
| * Process all deferred embeddable overrides for a given class. |
| * This should only occur after the embeddable is mapped. |
| * |
| * @param embedType embeddable class |
| * @param access class level access for embeddable |
| * @throws SAXException |
| */ |
| @Override |
| protected void applyDeferredEmbeddableOverrides(Class<?> cls) |
| throws SAXException { |
| ArrayList<DeferredEmbeddableOverrides> defMappings = |
| _deferredMappings.get(cls); |
| if (defMappings == null) |
| return; |
| |
| for (DeferredEmbeddableOverrides defMap : defMappings) { |
| FieldMapping fm = (FieldMapping)defMap._fm; |
| if (defMap == null) |
| return; |
| fm = getAttributeOverrideForEmbeddable(fm, defMap._attrName, true); |
| // Apply column, table, and unique overrides |
| if (defMap._defCols != null) { |
| fm.getValueInfo().setColumns(defMap._defCols); |
| if (!DBIdentifier.isNull(defMap._defTable)) |
| fm.getMappingInfo().setTableIdentifier(defMap._defTable); |
| setUnique(fm, defMap._unique); |
| } |
| // Apply Join column and element join columns overrides overrides |
| if (defMap._defJoinCols != null) |
| fm.getMappingInfo().setColumns(defMap._defJoinCols); |
| if (defMap._defElemJoinCols != null) |
| fm.getElementMapping().getValueInfo().setColumns( |
| defMap._defElemJoinCols); |
| } |
| // Clean up after applying mappings |
| defMappings.clear(); |
| _deferredMappings.remove(cls); |
| } |
| |
| /* |
| * Defer overrides for the specified field mapping |
| */ |
| private void deferEmbeddableOverrides( |
| Class<?> cls, DeferredEmbeddableOverrides defMap) { |
| ArrayList<DeferredEmbeddableOverrides> defMappings = |
| _deferredMappings.computeIfAbsent(cls, k -> new ArrayList<>()); |
| defMappings.add(defMap); |
| } |
| |
| /* |
| * Clean up any deferred mappings |
| */ |
| @Override |
| protected void clearDeferredMetaData() { |
| super.clearDeferredMetaData(); |
| _deferredMappings.clear(); |
| } |
| |
| /* |
| * Get embeddable overrides for the specified field mapping. If create |
| * is true, create a new override if one does not exist. |
| */ |
| private DeferredEmbeddableOverrides |
| getDeferredFieldMappingInfo(Class<?> cls, FieldMapping fm, |
| String attrName, boolean create) { |
| |
| ArrayList<DeferredEmbeddableOverrides> defMappings = |
| _deferredMappings.get(cls); |
| |
| if (defMappings == null && create) { |
| defMappings = new ArrayList<>(); |
| _deferredMappings.put(cls, defMappings); |
| } |
| DeferredEmbeddableOverrides dfm = |
| findDeferredMapping(cls, fm, attrName); |
| |
| if (dfm == null & create) { |
| dfm = new DeferredEmbeddableOverrides(fm, attrName); |
| deferEmbeddableOverrides(cls, dfm); |
| } |
| return dfm; |
| } |
| |
| /* |
| * Find deferred mappings for the given class, fm, and attr name |
| */ |
| private DeferredEmbeddableOverrides findDeferredMapping(Class<?> cls, |
| FieldMapping fm, String attrName) { |
| ArrayList<DeferredEmbeddableOverrides> defMappings = |
| _deferredMappings.get(cls); |
| if (defMappings == null) |
| return null; |
| |
| for (DeferredEmbeddableOverrides dfm : defMappings) { |
| if (dfm != null && dfm._fm == fm && |
| attrName.equals(dfm._attrName)) |
| return dfm; |
| } |
| return null; |
| } |
| |
| /** |
| * Process all deferred embeddables using an unknown access type. |
| */ |
| @Override |
| protected void addDeferredEmbeddableMetaData() { |
| super.addDeferredEmbeddableMetaData(); |
| if (_deferredMappings.size() > 0) { |
| Set<Class<?>> keys = _deferredMappings.keySet(); |
| Class<?>[] classes = keys.toArray(new Class[keys.size()]); |
| for (Class<?> aClass : classes) { |
| try { |
| applyDeferredEmbeddableOverrides(aClass); |
| } |
| catch (Exception e) { |
| throw new MetaDataException(_loc.get("no-embeddable-metadata", aClass.getName()), e); |
| } |
| } |
| } |
| |
| } |
| |
| // Inner class for storing override information |
| class DeferredEmbeddableOverrides { |
| DeferredEmbeddableOverrides(FieldMapping fm, String attrName) { |
| _fm = fm; |
| _attrName = attrName; |
| _defTable = DBIdentifier.NULL; |
| } |
| private FieldMapping _fm; |
| private List<Column> _defCols; |
| private List<Column> _defElemJoinCols; |
| private List<Column> _defJoinCols; |
| private DBIdentifier _defTable; |
| private String _attrName; |
| private EnumSet<UniqueFlag> _unique; |
| } |
| |
| @Override |
| protected boolean startDelimitedIdentifiers() { |
| JDBCConfiguration conf = (JDBCConfiguration) getConfiguration(); |
| DBDictionary dict = conf.getDBDictionaryInstance(); |
| dict.setDelimitIdentifiers(true); |
| return true; |
| } |
| |
| @Override |
| protected String normalizeSequenceName(String seqName) { |
| if (StringUtil.isEmpty(seqName)) { |
| return seqName; |
| } |
| return DBIdentifier.newSequence(seqName, delimit()).getName(); |
| } |
| |
| @Override |
| protected String normalizeSchemaName(String schName) { |
| if (StringUtil.isEmpty(schName)) { |
| return schName; |
| } |
| return DBIdentifier.newSchema(schName, delimit()).getName(); |
| } |
| |
| @Override |
| protected String normalizeCatalogName(String catName) { |
| if (StringUtil.isEmpty(catName)) { |
| return catName; |
| } |
| return DBIdentifier.newCatalog(catName, delimit()).getName(); |
| } |
| |
| private boolean delimit() { |
| return _dict.getDelimitIdentifiers(); |
| } |
| |
| /** |
| * Translate the fetch mode enum value to the internal OpenJPA constant. |
| */ |
| private static int toEagerFetchModeConstant(String mode) { |
| if(mode.equals("NONE")) |
| return EagerFetchModes.EAGER_NONE; |
| else if (mode.equals("JOIN")) |
| return EagerFetchModes.EAGER_JOIN; |
| else if (mode.equals("PARALLEL")) |
| return EagerFetchModes.EAGER_PARALLEL; |
| else |
| throw new InternalException(); |
| } |
| |
| private boolean startDatastoreIdCol(Attributes attrs) |
| throws SAXException { |
| |
| ClassMapping cm = (ClassMapping) peekElement(); |
| |
| Column col = new Column(); |
| String name = attrs.getValue("name"); |
| if (!StringUtil.isEmpty(name)); |
| col.setIdentifier(DBIdentifier.newColumn(name, delimit())); |
| String columnDefinition= attrs.getValue("column-definition"); |
| if (!StringUtil.isEmpty(columnDefinition)) |
| col.setTypeIdentifier(DBIdentifier.newColumnDefinition(columnDefinition)); |
| int precision = Integer.parseInt(attrs.getValue("precision")); |
| if (precision != 0) |
| col.setSize(precision); |
| col.setFlag(Column.FLAG_UNINSERTABLE, !Boolean.parseBoolean(attrs.getValue("insertable"))); |
| col.setFlag(Column.FLAG_UNUPDATABLE, !Boolean.parseBoolean(attrs.getValue("updatable"))); |
| cm.getMappingInfo().setColumns(Arrays.asList(new Column[]{ col })); |
| |
| return true; |
| } |
| |
| private boolean startIndex(Attributes attrs) |
| throws SAXException { |
| |
| FieldMapping fm = (FieldMapping) peekElement(); |
| |
| parseIndex(fm.getValueInfo(), |
| attrs.getValue("name"), |
| Boolean.parseBoolean(attrs.getValue("enabled")), |
| Boolean.parseBoolean(attrs.getValue("unique"))); |
| |
| return true; |
| } |
| |
| private void parseIndex(MappingInfo info, String name, |
| boolean enabled, boolean unique) { |
| if (!enabled) { |
| info.setCanIndex(false); |
| return; |
| } |
| |
| org.apache.openjpa.jdbc.schema.Index idx = |
| new org.apache.openjpa.jdbc.schema.Index(); |
| if (!StringUtil.isEmpty(name)) |
| idx.setIdentifier(DBIdentifier.newConstraint(name, delimit())); |
| idx.setUnique(unique); |
| info.setIndex(idx); |
| } |
| |
| private boolean startForeignKey(Attributes attrs) |
| throws SAXException { |
| |
| _foreignKeyAttributes = attrs; |
| |
| return true; |
| } |
| |
| private void endForeignKey() { |
| if (_foreignKeyAttributes == null) { |
| throw new InternalException(); |
| } |
| |
| boolean implicit = Boolean.parseBoolean(_foreignKeyAttributes.getValue("implicit")); |
| |
| FieldMapping fm = (FieldMapping) peekElement(); |
| MappingInfo info = fm.getValueInfo(); |
| |
| String name = _foreignKeyAttributes.getValue("name"); |
| boolean enabled = Boolean.parseBoolean(_foreignKeyAttributes.getValue("enabled")); |
| boolean deferred = Boolean.parseBoolean(_foreignKeyAttributes.getValue("deferred")); |
| boolean specified = Boolean.parseBoolean(_foreignKeyAttributes.getValue("specified")); |
| String deleteActionString = _foreignKeyAttributes.getValue("delete-action"); |
| String updateActionString = _foreignKeyAttributes.getValue("update-action"); |
| int deleteAction = toForeignKeyInt(deleteActionString); |
| int updateAction = toForeignKeyInt(updateActionString); |
| |
| if (!implicit) { |
| parseForeignKey(info, name, |
| enabled, |
| deferred, deleteAction, |
| updateAction); |
| } |
| else { |
| info.setImplicitRelation(true); |
| assertDefault(name, enabled, deferred, specified, updateAction, deleteAction); |
| } |
| |
| _columnNamesList = null; |
| } |
| |
| private void parseForeignKey(MappingInfo info, String name, boolean enabled, |
| boolean deferred, int deleteAction, int updateAction) { |
| |
| if (!enabled) { |
| info.setCanForeignKey(false); |
| return; |
| } |
| |
| org.apache.openjpa.jdbc.schema.ForeignKey fk = |
| new org.apache.openjpa.jdbc.schema.ForeignKey(); |
| if (!StringUtil.isEmpty(name)) |
| fk.setIdentifier(DBIdentifier.newForeignKey(name, delimit())); |
| fk.setDeferred(deferred); |
| fk.setDeleteAction(deleteAction); |
| fk.setUpdateAction(updateAction); |
| info.setForeignKey(fk); |
| |
| } |
| |
| |
| private int toForeignKeyInt(String action) { |
| if (action.equals("RESTRICT")) { |
| return org.apache.openjpa.jdbc.schema.ForeignKey. |
| ACTION_RESTRICT; |
| } |
| else if (action.equals("CASCADE")) { |
| return org.apache.openjpa.jdbc.schema.ForeignKey.ACTION_CASCADE; |
| } |
| else if (action.equals("NULL")) { |
| return org.apache.openjpa.jdbc.schema.ForeignKey.ACTION_NULL; |
| } |
| else if (action.equals("DEFAULT")) { |
| return org.apache.openjpa.jdbc.schema.ForeignKey.ACTION_DEFAULT; |
| } |
| else { |
| throw new InternalException(); |
| } |
| |
| } |
| |
| private void assertDefault(String name, boolean enabled, boolean deferred, boolean specified, |
| int updateAction, int deleteAction) { |
| boolean isDefault = StringUtil.isEmpty(name) |
| && enabled |
| && !deferred |
| && deleteAction == org.apache.openjpa.jdbc.schema.ForeignKey.ACTION_RESTRICT |
| && updateAction == org.apache.openjpa.jdbc.schema.ForeignKey.ACTION_RESTRICT |
| && _columnNames.length == 0 |
| && specified; |
| if (!isDefault) |
| throw new UserException(_loc.get("implicit-non-default-fk", _cls, |
| getSourceFile()).getMessage()); |
| } |
| |
| private boolean startFKColumnNames(Attributes attrs) |
| throws SAXException { |
| _columnNamesList = new ArrayList<>(); |
| return true; |
| } |
| |
| private void endFKColumnNames() { |
| if (_columnNamesList.size() > 0) { |
| _columnNames = _columnNamesList.toArray(_columnNames); |
| _columnNamesList.removeAll(_columnNamesList); |
| } |
| } |
| |
| private void endFKColumnName() { |
| _columnNamesList.add(currentText()); |
| } |
| |
| private boolean startVersionColumns(Attributes attrs) |
| throws SAXException { |
| |
| _versionColumnsList = new ArrayList<>(); |
| |
| return true; |
| } |
| |
| private void endVersionColumns() { |
| if (_versionColumnsList == null) { |
| throw new InternalException(); |
| } |
| |
| if (_versionColumnsList.size() > 0) { |
| ClassMapping cm = (ClassMapping)peekElement(); |
| cm.getVersion().getMappingInfo().setColumns(_versionColumnsList); |
| _versionColumnsList= null; |
| } |
| } |
| |
| private boolean startVersionColumn(Attributes attrs) |
| throws SAXException { |
| |
| Column col = AnnotationPersistenceMappingParser.newColumn(attrs.getValue("name"), |
| Boolean.parseBoolean(attrs.getValue("nullable")), |
| Boolean.parseBoolean(attrs.getValue("insertable")), |
| Boolean.parseBoolean(attrs.getValue("updatable")), |
| attrs.getValue("columnDefinition"), |
| Integer.parseInt(attrs.getValue("length")), |
| Integer.parseInt(attrs.getValue("precision")), |
| Integer.parseInt(attrs.getValue("scale")), |
| attrs.getValue("table"), |
| delimit()); |
| |
| _versionColumnsList.add(col); |
| |
| return true; |
| } |
| |
| @Override |
| protected void parseEagerFetchModeAttr(FieldMetaData fmd, Attributes attrs) |
| throws SAXException { |
| |
| FieldMapping fm = (FieldMapping) fmd; |
| String eagerFetchMode = attrs.getValue("eager-fetch-mode"); |
| if (!StringUtil.isEmpty(eagerFetchMode)) { |
| if (eagerFetchMode.equalsIgnoreCase("NONE")) { |
| fm.setEagerFetchMode(EagerFetchModes.EAGER_NONE); |
| } else if (eagerFetchMode.equalsIgnoreCase("JOIN")) { |
| fm.setEagerFetchMode(EagerFetchModes.EAGER_JOIN); |
| } else if (eagerFetchMode.equalsIgnoreCase("PARALLEL")) { |
| fm.setEagerFetchMode(EagerFetchModes.EAGER_PARALLEL); |
| } |
| } |
| } |
| |
| @Override |
| protected void parseElementClassCriteriaAttr(FieldMetaData fmd, Attributes attrs) |
| throws SAXException { |
| |
| String elementClassCriteriaString = attrs.getValue("element-class-criteria"); |
| if (!StringUtil.isEmpty(elementClassCriteriaString)) { |
| FieldMapping fm = (FieldMapping) fmd; |
| boolean elementClassCriteria = Boolean.parseBoolean(elementClassCriteriaString); |
| fm.getElementMapping().getValueInfo().setUseClassCriteria(elementClassCriteria); |
| } |
| } |
| |
| @Override |
| protected void parseStrategy(FieldMetaData fmd, Attributes attrs) { |
| String strategy = attrs.getValue("strategy"); |
| if (!StringUtil.isEmpty(strategy)) { |
| ((FieldMapping) fmd).getMappingInfo().setStrategy(strategy); |
| } |
| } |
| |
| @Override |
| protected boolean startExtendedClass(String elem, Attributes attrs) |
| throws SAXException { |
| ClassMapping mapping = (ClassMapping) currentElement(); |
| |
| String strategy = attrs.getValue("strategy"); |
| if (!StringUtil.isEmpty(strategy)) |
| mapping.getMappingInfo().setStrategy(strategy); |
| |
| String versionStrat = attrs.getValue("version-strategy"); |
| if (!StringUtil.isEmpty(versionStrat)) |
| mapping.getVersion().getMappingInfo().setStrategy(versionStrat); |
| |
| String discrimStrat = attrs.getValue("discriminator-strategy"); |
| if (!StringUtil.isEmpty(discrimStrat)) |
| mapping.getDiscriminator().getMappingInfo().setStrategy(discrimStrat); |
| |
| String subclassFetchMode = attrs.getValue("subclass-fetch-mode"); |
| if (!StringUtil.isEmpty(subclassFetchMode)) |
| mapping.setSubclassFetchMode(toEagerFetchModeConstant(subclassFetchMode)); |
| |
| return true; |
| } |
| } |