/**
* <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: EFeatureAnnotator.java,v 1.10 2008/09/21 18:36:02 mtaal Exp $
*/
package org.eclipse.emf.teneo.annotations.mapper;
import java.util.Collection;
import java.util.List;
import java.util.Set;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.eclipse.emf.ecore.EAttribute;
import org.eclipse.emf.ecore.EReference;
import org.eclipse.emf.ecore.EStructuralFeature;
import org.eclipse.emf.teneo.annotations.pamodel.PAnnotatedEAttribute;
import org.eclipse.emf.teneo.annotations.pamodel.PAnnotatedEReference;
import org.eclipse.emf.teneo.annotations.pamodel.PAnnotatedEStructuralFeature;
import org.eclipse.emf.teneo.annotations.pannotation.Transient;
import org.eclipse.emf.teneo.extension.ExtensionPoint;
import org.eclipse.emf.teneo.util.StoreUtil;
/**
* Sets the annotation on an efeature. In fact determines which efeature annotator to use
* (one-to-many, many-to-many etc.).
*
* @author <a href="mailto:mtaal@elver.org">Martin Taal</a>
* @version $Revision: 1.10 $
*/
public class EFeatureAnnotator extends AbstractAnnotator implements ExtensionPoint {
// The logger
protected static final Log log = LogFactory.getLog(EFeatureAnnotator.class);
// the annotators
protected OneToManyAttributeAnnotator otmAttributeAnnotator;
protected SingleAttributeAnnotator singleAttributeAnnotator;
protected BidirectionalManyToManyAnnotator bidirectionalManyToManyAnnotator;
protected UnidirectionalManyToManyAnnotator unidirectionalManyToManyAnnotator;
protected OneToManyReferenceAnnotator oneToManyReferenceAnnotator;
protected OneToOneReferenceAnnotator oneToOneReferenceAnnotator;
protected ManyToOneReferenceAnnotator manyToOneReferenceAnnotator;
@Override
protected void initialize() {
super.initialize();
otmAttributeAnnotator = createAnnotator(OneToManyAttributeAnnotator.class);
otmAttributeAnnotator.setEFeatureAnnotator(this);
singleAttributeAnnotator = createAnnotator(SingleAttributeAnnotator.class);
singleAttributeAnnotator.setEFeatureAnnotator(this);
bidirectionalManyToManyAnnotator = createAnnotator(BidirectionalManyToManyAnnotator.class);
bidirectionalManyToManyAnnotator.setEFeatureAnnotator(this);
unidirectionalManyToManyAnnotator = createAnnotator(UnidirectionalManyToManyAnnotator.class);
unidirectionalManyToManyAnnotator.setEFeatureAnnotator(this);
oneToManyReferenceAnnotator = createAnnotator(OneToManyReferenceAnnotator.class);
oneToManyReferenceAnnotator.setEFeatureAnnotator(this);
oneToOneReferenceAnnotator = createAnnotator(OneToOneReferenceAnnotator.class);
oneToOneReferenceAnnotator.setEFeatureAnnotator(this);
manyToOneReferenceAnnotator = createAnnotator(ManyToOneReferenceAnnotator.class);
manyToOneReferenceAnnotator.setEFeatureAnnotator(this);
}
/** Process the features of the eclass */
public void annotate(PAnnotatedEStructuralFeature aStructuralFeature) {
EStructuralFeature eStructuralFeature = aStructuralFeature.getModelEStructuralFeature();
boolean errorOccured = true;
try {
// a feature is transient if:
// - transient is true and it is an eattribute or
// - transient is true and it does not have an opposite
// - transietn is true and it's opposite is not a containment
// relation
// - it refers to an eclass which is transient
boolean isTransient =
eStructuralFeature.isTransient() &&
(eStructuralFeature instanceof EAttribute ||
((EReference) eStructuralFeature).getEOpposite() == null ||
!((EReference) eStructuralFeature).getEOpposite().isContainment() || ((EReference) eStructuralFeature)
.getEOpposite().isTransient());
// check if the refered to eclass is transient if so then this efeature is
// also transient
if (!isTransient && eStructuralFeature instanceof EReference) {
final PAnnotatedEReference aReference = (PAnnotatedEReference) aStructuralFeature;
if (aReference.getAReferenceType() != null) {
isTransient = aReference.getAReferenceType().getTransient() != null;
}
}
if (aStructuralFeature.getTransient() == null &&
((!mapVolitatileFeature() && eStructuralFeature.isVolatile()) || isTransient)) {
log.debug("Structural feature " + eStructuralFeature.getName() +
" is transient, therefore adding transient annotation");
final Transient trans = getFactory().createTransient();
trans.setEModelElement(eStructuralFeature);
aStructuralFeature.setTransient(trans);
}
// process transients further because they can be part of a
// featuremap, the specific mapper should
// handle transient
// Note that this means that transient features will still have
// additional annotations such as basic etc.
// if (aStructuralFeature.getTransient() != null) return;
if (aStructuralFeature instanceof PAnnotatedEAttribute) {
final PAnnotatedEAttribute aAttribute = (PAnnotatedEAttribute) aStructuralFeature;
if (((PAnnotatedEAttribute) aStructuralFeature).getVersion() != null) {
return;
}
final Class<?> instanceClass = eStructuralFeature.getEType().getInstanceClass();
boolean isMany = false;
// instanceClass will be null for enums
// Lob-annotated attributes must not be treated as one-to-many.
// eattributes with a hibernate type annotations should not be
// treated as a list
if (instanceClass != null && aAttribute.getLob() == null) {
isMany =
eStructuralFeature.isMany() || instanceClass.isArray() ||
Collection.class.isAssignableFrom(instanceClass) ||
Set.class.isAssignableFrom(instanceClass) ||
List.class.isAssignableFrom(instanceClass);
isMany = isMany && !StoreUtil.isElementOfAGroup(eStructuralFeature);
}
if (isMany) {
otmAttributeAnnotator.annotate(aAttribute);
} else {
singleAttributeAnnotator.annotate(aAttribute);
}
if (aAttribute.getColumn() != null && aAttribute.getColumn().getName() == null) {
aAttribute.getColumn().setName(getSqlNameStrategy().getColumnName(aAttribute, null));
}
} else if (aStructuralFeature instanceof PAnnotatedEReference) {
final PAnnotatedEReference aReference = (PAnnotatedEReference) aStructuralFeature;
// detect the type of relation
// note using the emf model it can not be checked if a relation
// is a
// uni-manytoone (2.1.8.3.2) or a uni onetoone (2.1.8.3.1)
// neither can a uni-manytomany (2.1.8.5.2) be detected
// because there is no eopposite. However this can be
// specified manually, the system as a default will choose
// uni-manytoone
final EReference eReference = (EReference) aStructuralFeature.getModelElement();
final EReference eOpposite = eReference.getEOpposite();
// elements of a group are never multi-occurence because the
// multi-occurence is
// handled by the containing featuremap
final boolean isMany = eReference.isMany() && !StoreUtil.isElementOfAGroup(eReference);
final boolean isOppositeMany =
eOpposite != null && eOpposite.isMany() && !StoreUtil.isElementOfAGroup(eOpposite);
final boolean mtmBidirectionalRelation = isMany && eOpposite != null && isOppositeMany;
final boolean mtmUnidirectionalRelation =
isMany && eOpposite == null && aReference.getManyToMany() != null;
final boolean otmBidirectionalRelation = isMany && eOpposite != null && !isOppositeMany;
final boolean otmUnidirectionalRelation = isMany && eOpposite == null;
// note as a default if the system has to choose between oto uni
// or mto uni then it will
// place a mto
final boolean otoBidirectionalRelation =
aReference.getManyToOne() == null && !isMany && eOpposite != null && !isOppositeMany;
final boolean otoUnidirectionalRelation =
aReference.getManyToOne() == null && !isMany && eOpposite == null &&
(aReference.getOneToOne() != null || !aReference.getPrimaryKeyJoinColumns().isEmpty());
final boolean mtoBidirectionalRelation = !isMany && eOpposite != null && isOppositeMany;
final boolean mtoUnidirectionalRelation = !isMany && eOpposite == null && !otoUnidirectionalRelation;
if (mtmBidirectionalRelation) {
bidirectionalManyToManyAnnotator.annotate(aReference);
} else if (mtmUnidirectionalRelation) {
unidirectionalManyToManyAnnotator.annotate(aReference);
} else if (otmBidirectionalRelation || otmUnidirectionalRelation) {
oneToManyReferenceAnnotator.annotate(aReference);
} else if (aReference.getManyToOne() == null && (otoBidirectionalRelation || otoUnidirectionalRelation)) {
oneToOneReferenceAnnotator.annotate(aReference);
} else if (mtoBidirectionalRelation) {
manyToOneReferenceAnnotator.annotate(aReference);
} else if (mtoUnidirectionalRelation) {
manyToOneReferenceAnnotator.annotate(aReference);
}
// handle column naming at this level
if (aReference.getColumn() != null && aReference.getColumn().getName() == null) {
aReference.getColumn().setName(getSqlNameStrategy().getColumnName(aReference, null));
}
} else {
throw new IllegalArgumentException("This type of StructuralFeature is not supported: " +
aStructuralFeature.getClass().getName());
}
errorOccured = false;
} finally {
// check that at least one ann was set
if (aStructuralFeature instanceof PAnnotatedEAttribute) {
PAnnotatedEAttribute pae = (PAnnotatedEAttribute) aStructuralFeature;
assert (errorOccured || pae.getBasic() != null || pae.getVersion() != null || pae.getId() != null ||
pae.getTransient() != null || pae.getOneToMany() != null);
} else {
PAnnotatedEReference par = (PAnnotatedEReference) aStructuralFeature;
assert (errorOccured || par.getTransient() != null || par.getOneToMany() != null ||
par.getManyToMany() != null || par.getManyToOne() != null || par.getOneToOne() != null);
}
}
}
/** Map the feature if it is volatile, default is false */
protected boolean mapVolitatileFeature() {
return false;
}
/**
* @return the manyToOneReferenceAnnotator
*/
public ManyToOneReferenceAnnotator getManyToOneReferenceAnnotator() {
return manyToOneReferenceAnnotator;
}
}