/**
* <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: ManyToOneMapper.java,v 1.33 2009/03/07 21:15:20 mtaal Exp $
*/
package org.eclipse.emf.teneo.hibernate.mapper;
import java.util.List;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.eclipse.emf.ecore.EClass;
import org.eclipse.emf.teneo.annotations.pamodel.PAnnotatedEReference;
import org.eclipse.emf.teneo.annotations.pamodel.PAnnotatedEStructuralFeature;
import org.eclipse.emf.teneo.annotations.pannotation.JoinColumn;
import org.eclipse.emf.teneo.annotations.pannotation.JoinTable;
import org.eclipse.emf.teneo.annotations.pannotation.ManyToOne;
import org.eclipse.emf.teneo.extension.ExtensionPoint;
import org.eclipse.emf.teneo.hibernate.hbmodel.HbAnnotatedEReference;
import org.eclipse.emf.teneo.hibernate.hbmodel.HbAnnotatedETypeElement;
import org.eclipse.emf.teneo.simpledom.Element;
/**
* Maps a {@link ManyToOne} element to its {@link MappingContext}.
* <p>
* Assumes that the given {@link PAnnotatedEStructuralFeature} is a normal
* ManyToOne, i.e.
* <ul>
* <li>it is a {@link PAnnotatedEReference};
* <li>it has a {@link ManyToOne} annotation;
* </ul>
*
* @author <a href="mailto:mtaal at elver.org">Martin Taal</a>
*/
public class ManyToOneMapper extends AbstractAssociationMapper implements
ExtensionPoint {
/** Log it */
private static final Log log = LogFactory.getLog(ManyToOneMapper.class);
/**
* Generate the hb mapping for the given reference and annotations.
*/
public void process(PAnnotatedEReference paReference) {
log.debug("Process many-to-one " + paReference);
final List<JoinColumn> jcs = getJoinColumns(paReference);
final EClass referedTo = paReference.getModelEReference()
.getEReferenceType();
final ManyToOne mto = paReference.getManyToOne();
String targetName = mto.getTargetEntity();
if (targetName == null) {
targetName = getHbmContext().getEntityName(referedTo);
}
log.debug("Target " + targetName);
JoinTable joinTable = null;
PAnnotatedEReference aOpposite = null;
if (paReference.getModelEReference().getEOpposite() != null) {
aOpposite = paReference.getPaModel().getPAnnotated(
paReference.getModelEReference().getEOpposite());
if (aOpposite.getOneToMany() != null
&& !aOpposite.getOneToMany().isList()
&& aOpposite.getJoinTable() != null) {
joinTable = aOpposite.getJoinTable();
}
}
final Element currentElement;
if (joinTable != null) {
final boolean addInverse = aOpposite != null
&& aOpposite.getOneToMany() != null
&& aOpposite.getOneToMany().getMappedBy() == null;
currentElement = getHbmContext().getCurrent().addElement("join")
.addAttribute("table", joinTable.getName()).addAttribute(
"inverse", Boolean.toString(addInverse))
.addAttribute("optional",
Boolean.toString(mto.isOptional()));
} else {
currentElement = getHbmContext().getCurrent();
}
if (joinTable != null) {
final Element keyElement = currentElement.addElement("key");
addKeyColumns((HbAnnotatedETypeElement) paReference, keyElement,
joinTable.getInverseJoinColumns());
}
final Element associationElement = addManyToOne(currentElement,
paReference, targetName, false);
addAccessor(associationElement);
if (!associationElement.getName().equals("any")) {
addLazyProxy(associationElement, mto.getFetch(), paReference);
}
final HbAnnotatedEReference hbReference = (HbAnnotatedEReference) paReference;
final boolean isProperty = hbReference.getHbType() != null;
if (!isProperty && hbReference.getNaturalId() != null) {
associationElement.addAttribute(HbMapperConstants.NATURAL_ID_ATTR,
Boolean.toString(hbReference.getNaturalId().isMutable()));
}
if (!isProperty && joinTable != null) {
addJoinColumns(paReference, associationElement, joinTable
.getJoinColumns(), mto.isOptional()
|| getHbmContext().isDoForceOptional(paReference)
|| getHbmContext().isCurrentElementFeatureMap());
}
if (!isProperty) {
addCascadesForSingle(associationElement, getCascades(hbReference
.getHbCascade(), mto.getCascade()));
}
if (hbReference.getHbFetch() != null) {
associationElement.addAttribute("fetch", hbReference.getHbFetch()
.getValue().getName().toLowerCase());
}
final boolean nullable = getHbmContext().isDoForceOptional(paReference)
|| mto.isOptional()
|| getHbmContext().isCurrentElementFeatureMap();
if (isEObject(targetName)) {
final String erefName = paReference.getModelEReference().getName();
addColumnsAndFormula(associationElement, paReference,
getAnyTypeColumns(erefName, true), true, false);
// foreign key is not added when the reference is to a generic
// EObject
} else if (!isProperty) {
addForeignKeyAttribute(associationElement, paReference);
addLazyProxy(associationElement, mto.getFetch(), paReference);
if (joinTable == null) {
addJoinColumns(paReference, associationElement, jcs,
getHbmContext().isDoForceOptional(paReference)
|| mto.isOptional()
|| getHbmContext().isCurrentElementFeatureMap());
associationElement.addAttribute("not-null", nullable ? "false"
: "true");
}
} else if (isProperty) {
associationElement.addAttribute("not-null", nullable ? "false"
: "true");
}
// note that the reference must be required, nullable and unique columns
// are not supported
// by ms sql server
// because ms sql server also sees null as a value
if (paReference.getModelEReference().isContainment() && !nullable) {
associationElement.addAttribute("unique", "true");
}
// MT: TODO; the characteristic of the other side should be checked (if
// present), if the otherside is a onetoone
// then this
// should be set to true. But then this is then handled by a
// bidirectional onetoone (I think).
// if (joinColumns.isEmpty())
// associationElement.addAttribute("unique", "true");
}
}