/**
* <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: AbstractProcessingContext.java,v 1.8 2008/12/16 20:40:34 mtaal Exp $
*/
package org.eclipse.emf.teneo.annotations.mapper;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Stack;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.eclipse.emf.common.util.EList;
import org.eclipse.emf.ecore.EClass;
import org.eclipse.emf.ecore.EStructuralFeature;
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.AssociationOverride;
import org.eclipse.emf.teneo.annotations.pannotation.AttributeOverride;
import org.eclipse.emf.teneo.annotations.pannotation.Column;
import org.eclipse.emf.teneo.annotations.pannotation.JoinColumn;
/**
* ProcessingContext which handles attributes overrides.
*
* @author <a href="mailto:mtaal@elver.org">Martin Taal</a>
* @version $Revision: 1.8 $
*/
public class AbstractProcessingContext {
/** The logger for all these exceptions */
protected static final Log log = LogFactory
.getLog(AbstractProcessingContext.class);
/** The current list of overrides */
private Map<String, Object> currentOverrides = new HashMap<String, Object>();
/**
* Pushes the current overrides on the stack, to be popped later, this is to
* handle nested components
*/
private Stack<Map<String, Object>> overrideStack = new Stack<Map<String, Object>>();
/**
* Pushes the current embedding feature on the stack, to be popped later,
* this is to handle nested components and automatic renaming of props
*/
private Stack<PAnnotatedEStructuralFeature> embeddingFeatureStack = new Stack<PAnnotatedEStructuralFeature>();
/**
* Add attribute overrides, happens for each mapped superclass and each
* embedded component
*/
public void addAttributeOverrides(EList<AttributeOverride> aos) {
if (aos != null) {
for (AttributeOverride override : aos) {
currentOverrides.put(override.getName(), override.getColumn());
}
}
}
/** Add association overrides, for each mapped subclass */
public void addAssociationOverrides(EList<AssociationOverride> overrides) {
if (overrides != null) {
for (AssociationOverride override : overrides) {
currentOverrides.put(override.getName(), override
.getJoinColumns());
}
}
}
/**
* Pushes the current overrides on the stack, to be popped later, this is to
* handle nested components
*/
public void pushOverrideOnStack() {
overrideStack.push(new HashMap<String, Object>(currentOverrides));
}
/** Pop the current overrides on the stack */
public void popOverrideStack() {
currentOverrides = overrideStack.pop();
}
/** Pushes the current embedding feature on the stack */
public void pushEmbeddingFeature(PAnnotatedEStructuralFeature er) {
embeddingFeatureStack.push(er);
}
/** Pops the current embedding feature from the stack */
public void popEmbeddingFeature() {
embeddingFeatureStack.pop();
}
/** Peeks for the current embedding feature */
public PAnnotatedEStructuralFeature getEmbeddingFeature() {
if (embeddingFeatureStack.isEmpty()) {
return null;
}
return embeddingFeatureStack.peek();
}
/** Clear the override is done before an entity is processed */
public void clearOverrides() {
currentOverrides.clear();
}
/** Return the overridden column for the passed attribute */
public Column getAttributeOverride(PAnnotatedEStructuralFeature paFeature) {
return getAttributeOverride(paFeature.getModelElement().getName());
}
/** Return the overridden Joincolumns for the indicated featureName */
public Column getAttributeOverride(String featureName) {
final Column c = (Column) currentOverrides.get(featureName);
if (c == null) {
final Object o = getFromStack(featureName);
if (o != null && o instanceof Column) {
return (Column) o;
}
}
return c;
}
/** Return the overridden JoinColumns for this reference */
public List<JoinColumn> getAssociationOverrides(
PAnnotatedEReference paReference) {
return getAssociationOverrides(paReference.getModelEReference()
.getName());
}
@SuppressWarnings("unchecked")
public List<JoinColumn> getAssociationOverrides(String featureName) {
final List<JoinColumn> jcs = (List<JoinColumn>) currentOverrides
.get(featureName);
if (jcs == null) {
final Object o = getFromStack(featureName);
if (o instanceof List<?>) {
return (List<JoinColumn>) o;
}
}
return jcs;
}
private Object getFromStack(String name) {
for (int i = (overrideStack.size() - 1); i >= 0; i--) {
final Map<String, Object> checkOverride = overrideStack.get(i);
final Object o = checkOverride.get(name);
if (o != null) {
return o;
}
}
return null;
}
/**
* This method returns all inherited features which need to be added to the
* mapping of the aclass itself. The method makes a distinction makes a
* distinction between the first supertype (the first one in the list) and
* later ones. The features of the first type are only added to the mapping
* if the first type is a mappedsuperclass, in all other cases the features
* of the first type are not mapped in the aclass itself because they are
* inherited (the mapping describes the inheritance relation). For the other
* supertypes (located at index 1 and up in getESuperTypes) the features are
* mapped as properties in the class itself. The superEntity is the super
* aclass denoted as the real supertype extended by teneo.
*/
public List<PAnnotatedEStructuralFeature> getInheritedFeatures(
PAnnotatedEClass aClass) {
// if no supertypes then there are no inherited features
final EClass eclass = aClass.getModelEClass();
if (eclass.getESuperTypes().size() == 0) {
return new ArrayList<PAnnotatedEStructuralFeature>();
}
log
.debug("Determining inherited features which are mapped locally for "
+ aClass.getModelEClass().getName());
final List<EStructuralFeature> inheritedFeatures = new ArrayList<EStructuralFeature>(
eclass.getEAllStructuralFeatures());
// remove all the features of the eclass itself
inheritedFeatures.removeAll(eclass.getEStructuralFeatures());
// check if the type has a supertype (a non-transient,
// non-mappedsuperclass, if so then
// remove all features inherited from the first supertype
// as this inheritance is done in the mapping file
if (aClass.getPaSuperEntity() != null) {
inheritedFeatures.removeAll(aClass.getPaSuperEntity()
.getModelEClass().getEAllStructuralFeatures());
}
// get all efeatures from direct mappedsuperclasses
// the id feature inherited from a direct mappedsuperclass should be
// maintained in other cases the id features are not mapped locally.
// The system can also ignore this and let the user be more carefull not
// to
// add id features here and there in the inheritance structure but this
// is
// more robust
removeIdFeatures(aClass, inheritedFeatures);
// convert the result
final PAnnotatedModel paModel = aClass.getPaModel();
final ArrayList<PAnnotatedEStructuralFeature> result = new ArrayList<PAnnotatedEStructuralFeature>();
for (EStructuralFeature esf : inheritedFeatures) {
result.add(paModel.getPAnnotated(esf));
}
return result;
}
/**
* Remove all id-features not inherited from a direct mapped superclass, and
* add the features from the mapped superclass
*/
private void removeIdFeatures(PAnnotatedEClass aClass,
List<EStructuralFeature> inheritedFeatures) {
// first get all the mapped superclasses
final ArrayList<EClass> mappedSuperEClasses = new ArrayList<EClass>();
for (EClass superEClass : aClass.getModelEClass().getESuperTypes()) {
final PAnnotatedEClass superPAClass = aClass.getPaModel()
.getPAnnotated(superEClass);
if (superPAClass != null
&& superPAClass.getMappedSuperclass() != null) {
mappedSuperEClasses.add(superPAClass.getModelEClass());
}
}
// now get all the efeatures of the mappedsuperclasses to prevent any id
// features from them being removed, only do that when the aclass does
// not
// have a real super type, in that case the id can be inherited from the
// mappedsuperclass
final ArrayList<EStructuralFeature> mappedSuperFeatures = new ArrayList<EStructuralFeature>();
if (aClass.getPaSuperEntity() == null
|| aClass.getPaSuperEntity().getMappedSuperclass() != null) {
for (EClass mappedSuperEClass : mappedSuperEClasses) {
mappedSuperFeatures.removeAll(mappedSuperEClass
.getEAllStructuralFeatures());
mappedSuperFeatures.addAll(mappedSuperEClass
.getEAllStructuralFeatures());
}
}
// now remove all id features not coming from a direct mapped superclass
final ArrayList<EStructuralFeature> toRemove = new ArrayList<EStructuralFeature>();
for (EStructuralFeature esf : inheritedFeatures) {
final PAnnotatedEStructuralFeature pef = aClass.getPaModel()
.getPAnnotated(esf);
if (pef instanceof PAnnotatedEAttribute
&& ((PAnnotatedEAttribute) pef).getId() != null
&& !mappedSuperFeatures.contains(esf)) {
toRemove.add(esf);
}
}
inheritedFeatures.removeAll(toRemove);
}
//
// /** Returns all mapped super classes */
// public List<PAnnotatedEClass> getMappedSuperClasses(PAnnotatedEClass
// entity) {
// final List<PAnnotatedEClass> result = new ArrayList<PAnnotatedEClass>();
// for (EClass superEClass : entity.getAnnotatedEClass().getESuperTypes()) {
// final PAnnotatedEClass superPAClass = entity.getPaModel()
// .getPAnnotated(superEClass);
// if (superPAClass != null
// && superPAClass.getMappedSuperclass() != null) {
// result.add(superPAClass);
// // and add the mapped super classes of the mapped superclass
// // note that only the unbroken chain of mappedsuperclasses is
// // added to the result, if there
// // is a non-mappedsuperclass in the inheritance then it stops
// // there
// // issue also identified by Douglas Bitting
// result.addAll(getMappedSuperClasses(superPAClass));
// }
// }
//
// return result;
// }
/**
* Returns true if the eclass only has mappedsuperclasses without id
* annotated property
*/
public boolean mustAddSyntheticID(PAnnotatedEClass entity) {
if (entity.hasIdAnnotatedFeature()) {
return false;
}
for (EClass superEClass : entity.getModelEClass().getEAllSuperTypes()) {
final PAnnotatedEClass superPAClass = entity.getPaModel()
.getPAnnotated(superEClass);
if (superPAClass != null
&& superPAClass.getMappedSuperclass() == null) {
return false;
} else if (superPAClass != null
&& superPAClass.getMappedSuperclass() != null) {
if (superPAClass.hasIdAnnotatedFeature()) {
return false;
}
}
}
return true;
}
}