/** * <copyright> Copyright (c) 2005, 2006, 2007, 2008 Springsite BV (The Netherlands) and others All rights * reserved. This program and the accompanying materials are made available under the terms of the * Eclipse Public License v1.0 which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html Contributors: Martin Taal * </copyright> $Id: MappingContext.java,v 1.33 2008/12/16 20:40:29 mtaal Exp $ */ package org.eclipse.emf.teneo.hibernate.mapper; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import org.eclipse.emf.ecore.EAttribute; import org.eclipse.emf.ecore.EClass; import org.eclipse.emf.ecore.EClassifier; import org.eclipse.emf.ecore.EReference; import org.eclipse.emf.ecore.EStructuralFeature; import org.eclipse.emf.teneo.PersistenceOptions; import org.eclipse.emf.teneo.annotations.mapper.AbstractProcessingContext; import org.eclipse.emf.teneo.annotations.pamodel.PAnnotatedEAttribute; import org.eclipse.emf.teneo.annotations.pamodel.PAnnotatedEClass; import org.eclipse.emf.teneo.annotations.pamodel.PAnnotatedEReference; import org.eclipse.emf.teneo.annotations.pamodel.PAnnotatedEStructuralFeature; import org.eclipse.emf.teneo.annotations.pamodel.PAnnotatedModel; import org.eclipse.emf.teneo.annotations.pannotation.Column; import org.eclipse.emf.teneo.annotations.pannotation.JoinColumn; import org.eclipse.emf.teneo.annotations.pannotation.SecondaryTable; import org.eclipse.emf.teneo.annotations.pannotation.Table; import org.eclipse.emf.teneo.annotations.pannotation.UniqueConstraint; import org.eclipse.emf.teneo.ecore.EModelResolver; import org.eclipse.emf.teneo.extension.ExtensionInitializable; import org.eclipse.emf.teneo.extension.ExtensionManager; import org.eclipse.emf.teneo.extension.ExtensionManagerAware; import org.eclipse.emf.teneo.extension.ExtensionPoint; import org.eclipse.emf.teneo.mapping.strategy.EntityNameStrategy; import org.eclipse.emf.teneo.mapping.strategy.SQLNameStrategy; import org.eclipse.emf.teneo.simpledom.Document; import org.eclipse.emf.teneo.simpledom.Element; /** * Maps a basic attribute with many=true, e.g. list of simpletypes. * * @author <a href="mailto:mtaal@elver.org">Martin Taal</a> * @version $Revision: 1.33 $ */ public class MappingContext extends AbstractProcessingContext implements ExtensionPoint, ExtensionInitializable, ExtensionManagerAware { /** The xml document to which all elements are added */ private Document mappingDoc; /** The current element, normally a class element */ private Element currentElement; /** Mapping from eclass to entity name */ private Map<EClass, String> entityNames = null; /** * Keeps track of the list of featuremapmappers created for the current * entity */ private final List<FeatureMapMapping> featureMapMappers = new ArrayList<FeatureMapMapping>(); /** The list of eattributes for which a featuremap mapping was created */ private final List<EAttribute> handledFeatureMapEAttributes = new ArrayList<EAttribute>(); /** the mapper used for features */ private FeatureMapper featureMapper; /** * Is the current element a mixed or a feature map, in this case all * features should be not required. TODO: check, does this work with * embedded components in a feature map? */ private boolean currentElementFeatureMap = false; /** The current table element is set when an entity starts */ private Table currentTable = null; /** * The current column prefix, is used in case of multiple mixed types in one * eclass. */ private String namePrefix = ""; /** The current secondary table being processed. May be null. */ private SecondaryTable currentSecondaryTable = null; /** The current eclass */ protected EClass currentEClass = null; /** The current efeature being processed */ protected EStructuralFeature currentEFeature = null; protected PAnnotatedEStructuralFeature currentAFeature = null; /** The entity mapper */ private EntityMapper entityMapper; /** The extensionmanager used */ private ExtensionManager extensionManager; /** The option to qualify entity names */ private EntityNameStrategy entityNameStrategy = null; /** Version column name */ private String versionColumnName = null; /** ID column name */ private String idColumnName = null; /** Maximum column name */ protected int maximumSqlNameLength = -1; /** The sql case strategy */ protected SQLNameStrategy sqlNameStrategy; /** The escape character string used for escaping sql names */ protected String escapeCharacter; /** * Set force optional, force optional is used in case a subclass is stored * in the same table as its superclass, in this case all properties of the * subclass are denoted as optional. */ private boolean forceOptional = false; // Options private boolean alwaysVersion; private boolean isMapEMapAsTrueMap; private String idbagIDColumnName = "ID"; // The pa model for which this is all done, is set when generation starts private PAnnotatedModel paModel = null; // The maximum comment length allowed private int maximumCommentLength = 0; private PersistenceOptions persistenceOptions; /** Returns the entitymapper */ public EntityMapper getEntityMapper() { return entityMapper; } /** Set relevant properties */ protected void setMappingProperties(PersistenceOptions po) { versionColumnName = po.getVersionColumnName(); idColumnName = po.getIdColumnName(); maximumSqlNameLength = po.getMaximumSqlNameLength(); alwaysVersion = po.getAlwaysVersion(); isMapEMapAsTrueMap = po.isMapEMapAsTrueMap(); idbagIDColumnName = po.getIDBagIDColumnName(); maximumCommentLength = po.getMaximumCommentLength(); escapeCharacter = po.getSqlNameEscapeCharacter(); persistenceOptions = po; } /** Return the concrete impl. class */ protected String getInstanceClassName(EClass eClass) { final Class<?> clz = getInstanceClass(eClass); if (clz != null) { return clz.getName(); } log .debug("Instance class for eclass " + eClass.getName() + " is null "); return null; } /** Return the concrete impl. class, if none is found then null is returned */ protected Class<?> getInstanceClass(EClassifier eclassifier) { return EModelResolver.instance().getJavaClass(eclassifier); } /** * @return Returns the entity name for the given entity EClass. */ public String getEntityName(EClass entityEClass) { return getEntityName(entityEClass, true); } /** * @return Returns the entity name for the given entity EClass. */ public String getEntityName(EClass entityEClass, boolean throwCheckException) { String name = entityNames.get(entityEClass); if (name == null) { final Class<?> implClass = getInstanceClass(entityEClass); if (implClass != null) { name = implClass.getName(); } } if (throwCheckException && name == null) { throw new IllegalStateException( "An entity name has not been registered for " + entityEClass); } return name; } /** Set an entityname for a eclass */ public void setEntityName(EClass entityEClass, String entityName) { entityNames.put(entityEClass, entityName); } /** Start a document */ public void beginDocument(Document draft) { mappingDoc = draft; currentElement = draft.getRoot(); entityNames = new HashMap<EClass, String>(); } /** Finished creating the document */ public Document endDocument() { Document builtDocument = mappingDoc; mappingDoc = null; currentElement = null; entityNames = null; return builtDocument; } /** The current element to which new elements are added */ public Element getCurrent() { return currentElement; } /** Set the current element to which new elements are added */ public void setCurrent(Element newCurrent) { this.currentElement = newCurrent; } /** * Note this call will also clear the current list of featuremappers; * * @return the featureMapMappers gathered during the entity processing */ public List<FeatureMapMapping> getClearFeatureMapMappers() { final ArrayList<FeatureMapMapping> result = new ArrayList<FeatureMapMapping>( featureMapMappers); // clone // the // list! featureMapMappers.clear(); return result; } /** * @param Adds * a featureMapMapper to the featuremapp mapper list */ public void addFeatureMapMapper(FeatureMapMapping featureMapMapper) { if (!handledFeatureMapEAttributes.contains(featureMapMapper .getEAttribute())) { featureMapMappers.add(featureMapMapper); handledFeatureMapEAttributes.add(featureMapMapper.getEAttribute()); } } /* * (non-Javadoc) * * @see * org.eclipse.emf.teneo.extension.ExtensionInitializable#initializeExtension * () */ public void initializeExtension() { featureMapper = createFeatureMapper(); entityMapper = getExtensionManager().getExtension(EntityMapper.class); entityMapper.setHbmContext(this); } /** * @return The builder used by entity mapped that maps features to hbm. */ private FeatureMapper createFeatureMapper() { final FeatureMapper featureMapper = getExtensionManager().getExtension( FeatureMapper.class); featureMapper.setHbmContext(this); featureMapper.setBasicMapper(createMapper(BasicMapper.class)); featureMapper .setManyAttributeMapper(createMapper(ManyAttributeMapper.class)); featureMapper.setEmbeddedMapper(createMapper(EmbeddedMapper.class)); featureMapper.setIdMapper(createMapper(IdMapper.class)); featureMapper.setManyToManyMapper(createMapper(ManyToManyMapper.class)); featureMapper.setManyToOneMapper(createMapper(ManyToOneMapper.class)); featureMapper.setOneToManyMapper(createMapper(OneToManyMapper.class)); featureMapper.setOneToOneMapper(createMapper(OneToOneMapper.class)); featureMapper .setManyExternalReferenceMapper(createMapper(ManyExternalReferenceMapper.class)); return featureMapper; } protected <T> T createMapper(Class<T> clz) { final T t = getExtensionManager().getExtension(clz); ((AbstractMapper) t).setHbmContext(this); return t; } /** process the features of the annotated eclass */ protected void processFeatures(List<PAnnotatedEStructuralFeature> features) { for (PAnnotatedEStructuralFeature annotatedEStructuralFeature : features) { entityMapper.processFeature(annotatedEStructuralFeature); } } /** * @return the featureMapper */ public FeatureMapper getFeatureMapper() { return featureMapper; } /** * @return the currentElementFeatureMap */ public boolean isCurrentElementFeatureMap() { return currentElementFeatureMap; } /** * @param currentElementFeatureMap * the currentElementFeatureMap to set */ public void setCurrentElementFeatureMap(boolean currentElementFeatureMap) { this.currentElementFeatureMap = currentElementFeatureMap; } /** * @return the currentTable */ public Table getCurrentTable() { return currentTable; } /** * @param currentTable * the currentTable to set */ public void setCurrentTable(Table currentTable) { this.currentTable = currentTable; } /** * @param currentSecondaryTable */ public void setCurrentSecondaryTable(SecondaryTable currentSecondaryTable) { this.currentSecondaryTable = currentSecondaryTable; } /** Get unique constraint key. */ public String getUniqueConstraintKey(String colName) { // Obtain UniqueConstraints from secondary or primary table. List<UniqueConstraint> uniqueConstraints = null; if (currentSecondaryTable != null) { uniqueConstraints = currentSecondaryTable.getUniqueConstraints(); } else if (currentTable != null) { uniqueConstraints = currentTable.getUniqueConstraints(); } if (uniqueConstraints == null) { return null; } // NOTE: Hibernate does not support one column being part of multiple // unique constraints. for (int i = 0, n = uniqueConstraints.size(); i < n; i++) { UniqueConstraint uniqueConstraint = uniqueConstraints.get(i); if (uniqueConstraint.getColumnNames().contains(colName)) { return "c" + i; } } return null; } /** * @return the versionColumnName */ public String getVersionColumnName() { return versionColumnName; } /** * @param versionColumnName * the versionColumnName to set */ public void setVersionColumnName(String versionColumnName) { this.versionColumnName = versionColumnName; } /** * @return the maximumColumnNameLength */ public int getMaximumColumnNameLength() { return maximumSqlNameLength; } /** Default is trunc */ protected String trunc(String name) { return trunc(name, true); } /** * Utility method to truncate a column/table name. This method also repairs * the name if an efeature was inherited and really belongs to another * eclass. In this case jointables and join keys must be renamed to the new * eclass. TODO: handle the case that the jointable/columns were set * manually. This procedure will override them (only applies in case of * multiple inheritance/mappedsuperclass). This renaming is required for the * case that an ereference is inherited from a mapped superclass, in this * case the join-column of the e-reference will be placed in another table. * If one ereference is inherited by multiple subtypes then this goes wrong * because they then all share the same join column with foreign keys * relating it to different tables, and multiple foreign keys on one column * can not point to different directions. * * This method is also called for table names. * */ protected String trunc(String truncName, boolean truncPrefix) { // see bugzilla 225818 // in case of attributeoverride then do not repair the name final String useName; // method is also called for table names if (currentAFeature != null) { boolean override = false; if (currentAFeature instanceof PAnnotatedEAttribute) { override = getAttributeOverride(currentAFeature) != null; } else { override = getAssociationOverrides((PAnnotatedEReference) currentAFeature) != null; } final String otherEntityName = getEntityName(currentEFeature .getEContainingClass(), false); // if the current name starts with the name of the mapped superclass // then // change it back to the current eclass, do not do this in case of // override if (!override && currentEFeature.getEContainingClass() != currentEClass && otherEntityName != null && truncName.toUpperCase().startsWith( otherEntityName.toUpperCase())) { log.debug("Replacing name of table/joincolumn " + truncName); // get rid of the first part useName = getNamePrefix() + getEntityName(currentEClass) + truncName .substring(getEntityName( currentEFeature.getEContainingClass()) .length()); log.debug("with " + useName + " because efeature is inherited"); log .debug("This renaming does not work in case of manually specified joincolumn/table names and mappedsuperclass or multiple inheritance!"); } else { useName = getNamePrefix() + truncName; } } else { useName = getNamePrefix() + truncName; } if (escapeCharacter.length() > 0 && useName.indexOf(escapeCharacter) == 0) { return getSqlNameStrategy().convert(useName, false); } return escapeCharacter + getSqlNameStrategy().convert(useName, false) + escapeCharacter; } /** * @return the idColumnName */ public String getIdColumnName() { return idColumnName; } /** Return the alwaysversion option */ public boolean alwaysVersion() { return alwaysVersion; } /** Returns the list of eattrs, note list is updated outside of this object */ public List<EAttribute> getHandledFeatureMapEAttributes() { return handledFeatureMapEAttributes; } /** Returns the correct property name */ public String getPropertyName(EStructuralFeature ef) { return ef.getName(); } /** Return the version property handler */ public String getSyntheticVersionPropertyHandlerName() { return "org.eclipse.emf.teneo.hibernate.mapping.property.VersionPropertyHandler"; } /** Return the id property handler */ public String getSyntheticIdPropertyHandlerName() { return "org.eclipse.emf.teneo.hibernate.mapping.identifier.IdentifierPropertyHandler"; } /** Return the standard property handler */ public String getPropertyHandlerName() { return ""; } /** Return the version property handler for a normal version field */ public String getVersionPropertyHandlerName() { return ""; } /** Return the id property handler for a normal id property handler */ public String getIdPropertyHandlerName() { return ""; } /** Return the component property handler */ public String getComponentPropertyHandlerName() { return ""; } /** Is this a dynamic eclass, i.e. it has no instanceclass */ public boolean isDynamic(EClassifier eclassifier) { return !EModelResolver.instance().hasImplementationClass(eclassifier); } /** * Use the implementation name as the mapping and never use entity-mapping, * always false in this implementation */ public boolean forceUseOfInstance(PAnnotatedEClass aclass) { return false; } /** * Returns true if the instance classes have been generated by emf. For * Teneo this is always the case. Overriders can support a different * generation strategy. */ public boolean isGeneratedByEMF() { return true; } /** * There are four cases: EMF generated, EMF Dynamic, Easy EMF Generated, * Easy EMF Dynamic public boolean isEasyEMFGenerated(EClassifier * eclassifier) { return * EModelResolver.instance().hasImplementationClass(eclassifier); } public * boolean isEasyEMFDynamic(EClassifier eclassifier) { return * !isEasyEMFGenerated(eclassifier) && * EModelResolver.instance().isRegistered( eclassifier.getEPackage()); } * public boolean isEMFGenerated(EClassifier eclassifier) { return * eclassifier.getInstanceClass() != null; } */ // // public boolean isEMFDynamic(EClassifier eclassifier) { // return !isEasyEMFDynamic(eclassifier) && !isEMFGenerated(eclassifier); // } // // /** Return the impl class */ // public Class<?> getImpl(EClassifier eclassifier) { // return EModelResolver.instance().getJavaClass(eclassifier); // } // // /** Check if this is an entity (so without an impl class) */ // public boolean hasImpl(PAnnotatedEStructuralFeature af) { // return EModelResolver.instance().hasImplementationClass( // af.getAnnotatedEStructuralFeature().getEContainingClass()); // } /** Add a tuplizer element or not */ public void addTuplizerElement(Element entityElement, PAnnotatedEClass aclass) { } /** Returns the enumusertype class name */ public String getEnumUserType() { return "org.eclipse.emf.teneo.hibernate.mapping.ENumUserType"; } /** Returns the xml duration class name */ public String getDurationType() { return "org.eclipse.emf.teneo.hibernate.mapping.XSDDuration"; } /** Returns the enum user type integer name */ public String getEnumIntegerUserType() { return "org.eclipse.emf.teneo.hibernate.mapping.ENumUserIntegerType"; } /** Returns the enumusertype class name for the dynamic case */ public String getDynamicEnumUserType() { return "org.eclipse.emf.teneo.hibernate.mapping.DynamicENumUserType"; } /** Returns the user type used to persist external references */ public String getExternalUserType() { return "org.eclipse.emf.teneo.hibernate.mapping.ExternalType"; } /** Returns the enum user type integer name for the dynamic case */ public String getDynamicEnumIntegerUserType() { return "org.eclipse.emf.teneo.hibernate.mapping.DynamicENumUserIntegerType"; } /** Return the default user type */ public String getDefaultUserType() { return "org.eclipse.emf.teneo.hibernate.mapping.DefaultToStringUserType"; } /** Returns the default any type */ public String getAnytype() { return "org.eclipse.emf.teneo.hibernate.mapping.AnyEObjectType"; } /** Returns the usertype used to handle the xsd date */ public String getXSDDateUserType() { // --- JJH return persistenceOptions.getUserXSDDateType(); // return "org.eclipse.emf.teneo.hibernate.mapping.XSDDate"; // --- JJH } /** Returns the usertype used to handle the xsd datetime */ public String getXSDDateTimeUserType() { // --- JJH return persistenceOptions.getUserXSDDateTime(); // return "org.eclipse.emf.teneo.hibernate.mapping.XSDDateTime"; // --- JJH } /** * @return the eclassNameStrategy */ public EntityNameStrategy getEntityNameStrategy() { if (entityNameStrategy == null) { entityNameStrategy = getExtensionManager().getExtension( EntityNameStrategy.class); entityNameStrategy.setPaModel(getPaModel()); // this call is not // really required // but // for safety reasons } return entityNameStrategy; } /** * @return the isMapEMapAsTrueMap */ public boolean isMapEMapAsTrueMap() { return isMapEMapAsTrueMap; } /** * @return the currentEClass */ public EClass getCurrentEClass() { return currentEClass; } /** * @param currentEClass * the currentEClass to set */ public void setCurrentEClass(EClass currentEClass) { this.currentEClass = currentEClass; } /** * @return the currentEFeature */ public EStructuralFeature getCurrentEFeature() { return currentEFeature; } /** * @param currentEFeature * the currentEFeature to set */ public void setCurrentFeature(PAnnotatedEStructuralFeature currentFeature) { this.currentAFeature = currentFeature; this.currentEFeature = currentFeature == null ? null : currentFeature .getModelEStructuralFeature(); } /** * @return the idbagIDColumnName */ public String getIdbagIDColumnName() { return idbagIDColumnName; } /** * @return the forceOptional */ public boolean isDoForceOptional(PAnnotatedEStructuralFeature aFeature) { final boolean hasSecondaryTable = getSecondaryTableName(aFeature) != null; return !hasSecondaryTable && forceOptional; } /** * @param forceOptional * the forceOptional to set */ public void setForceOptional(boolean forceOptional) { this.forceOptional = forceOptional; } /** * @return the paModel */ public PAnnotatedModel getPaModel() { return paModel; } /** * @param paModel * the paModel to set */ public void setPaModel(PAnnotatedModel paModel) { this.paModel = paModel; } /** * @return the extensionManager */ public ExtensionManager getExtensionManager() { return extensionManager; } /** * @param extensionManager * the extensionManager to set */ public void setExtensionManager(ExtensionManager extensionManager) { this.extensionManager = extensionManager; } /** * @return the sqlNameStrategy */ public SQLNameStrategy getSqlNameStrategy() { if (sqlNameStrategy == null) { sqlNameStrategy = getExtensionManager().getExtension( SQLNameStrategy.class); } return sqlNameStrategy; } /** * @return the maximumCommentLength */ public int getMaximumCommentLength() { return maximumCommentLength; } /** * @return the namePrefix */ public String getNamePrefix() { return namePrefix; } /** * @param namePrefix * the namePrefix to set */ public void setNamePrefix(String namePrefix) { this.namePrefix = namePrefix; } /** * @return the escapeCharacter */ public String getEscapeCharacter() { return escapeCharacter; } /** * @param escapeCharacter * the escapeCharacter to set */ public void setEscapeCharacter(String escapeCharacter) { this.escapeCharacter = escapeCharacter; } /** * @return the persistenceOptions */ public PersistenceOptions getPersistenceOptions() { return persistenceOptions; } /** * Returns the table name from the column annotation or the joincolumn * annotation Also takes associationoverride or attributeoverride into * account */ public String getSecondaryTableName(PAnnotatedEStructuralFeature pef) { String tableName = null; if (pef instanceof PAnnotatedEAttribute) { final PAnnotatedEAttribute pea = (PAnnotatedEAttribute) pef; Column c = getAttributeOverride(pea); if (c == null) { c = pef.getColumn(); } if (c != null) { tableName = c.getTable(); } } else { final PAnnotatedEReference per = (PAnnotatedEReference) pef; pushOverrideOnStack(); addAttributeOverrides(per.getAttributeOverrides()); addAssociationOverrides(per.getAssociationOverrides()); try { if (per.getEmbedded() != null) { // check the embedded efeatures // take the first feature of the target type // assume that they are all handled in the same table final EClass eClass = per.getModelEReference() .getEReferenceType(); for (EAttribute ea : eClass.getEAllAttributes()) { final Column c = getAttributeOverride(ea.getName()); if (c != null && c.getTable() != null) { return c.getTable(); } } for (EReference er : eClass.getEAllReferences()) { final List<JoinColumn> jcs = getAssociationOverrides(er .getName()); if (jcs != null && jcs.size() > 0) { return jcs.get(0).getTable(); } } } else { List<JoinColumn> jcs = getAssociationOverrides(per); if (jcs == null || jcs.size() == 0) { jcs = per.getJoinColumns(); } if (jcs != null && jcs.size() > 0) { for (JoinColumn jc : jcs) { if (jc.getTable() != null) { tableName = jc.getTable(); break; } } } } } finally { popOverrideStack(); } } return tableName; } }