/**
* <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: OneToOneMapper.java,v 1.35 2009/03/07 21:15:19 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.EReference;
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.OneToOne;
import org.eclipse.emf.teneo.extension.ExtensionPoint;
import org.eclipse.emf.teneo.hibernate.hbmodel.HbAnnotatedEReference;
import org.eclipse.emf.teneo.simpledom.Element;
/**
* Maps a {@link OneToOne} element to its {@link MappingContext}.
* <p>
* Assumes that the given {@link PAnnotatedEStructuralFeature} is a normal
* OneToOne, i.e.
* <ul>
* <li>it is a {@link PAnnotatedEReference};
* <li>it has a {@link OneToOne} annotation;
* <li>each attribute on the {@link OneToOne} annotation is set possibly except
* for {@link OneToOne#getMappedBy()};
* <li>TODO requirements on JoinColumns/PrimaryKeyJoinColumn
* </ul>
*
* @author <a href="mailto:mtaal at elver.org">Martin Taal</a>
*/
public class OneToOneMapper extends AbstractAssociationMapper implements
ExtensionPoint {
private static final Log log = LogFactory.getLog(OneToOneMapper.class);
/** Process the one-to-one */
public void process(PAnnotatedEReference paReference) {
final PAnnotatedEReference opposite = getOtherSide(paReference);
if (opposite != null) {
// handle the case of a primary key one-to-one
if (!paReference.getPrimaryKeyJoinColumns().isEmpty()
|| (opposite != null && !opposite
.getPrimaryKeyJoinColumns().isEmpty())) {
createOneToOne(paReference);
} else {
// For a non-pk one-to-one, one side is mapped as oto, and the
// other as mto.
// the oto is the one with the mappedBy attribute
if (paReference.getOneToOne().getMappedBy() != null) {
createOneToOne(paReference);
} else {
createManyToOne(paReference);
}
}
} else { // can this case occur? A non-bidirectional one-to-one?
createManyToOne(paReference);
}
// if (opposite == null ||
// paReference.getOneToOne().eIsSet(PannotationPackage.eINSTANCE.getOneToOne_MappedBy()))
// {
// if (!paReference.getPrimaryKeyJoinColumns().isEmpty() ||
// (opposite != null && !opposite.getPrimaryKeyJoinColumns().isEmpty()))
// {
// createOneToOne(paReference);
// } else if
// (paReference.getOneToOne().eIsSet(PannotationPackage.eINSTANCE.getOneToOne_MappedBy()))
// {
// createOneToOne(paReference);
// } else {
// createManyToOne(paReference);
// }
// } else {
// createOneToOne(paReference);
// }
}
/** Create hibernate many-to-one mapping */
private void createManyToOne(PAnnotatedEReference paReference) {
log.debug("Generating many to one mapping for onetoone" + paReference);
final OneToOne oto = paReference.getOneToOne();
final EReference eref = paReference.getModelEReference();
String specifiedName = oto.getTargetEntity();
if (specifiedName == null) {
specifiedName = getHbmContext().getEntityName(
eref.getEReferenceType());
}
final HbAnnotatedEReference hbReference = (HbAnnotatedEReference) paReference;
final Element associationElement = addManyToOne(getHbmContext()
.getCurrent(), paReference, specifiedName, false);
addAccessor(associationElement);
addCascadesForSingle(associationElement, getCascades(hbReference
.getHbCascade(), oto.getCascade()));
final boolean isNullable = (oto.isOptional()
|| getHbmContext().isDoForceOptional(paReference) || getHbmContext()
.isCurrentElementFeatureMap());
associationElement.addAttribute("not-null", (isNullable ? "false"
: "true"));
if (!associationElement.getName().equals("any")) {
addLazyProxy(associationElement, oto.getFetch(), paReference);
}
if (hbReference.getHbFetch() != null) {
associationElement.addAttribute("fetch", hbReference.getHbFetch()
.getValue().getName().toLowerCase());
}
if (isEObject(specifiedName)) {
addColumnsAndFormula(associationElement, paReference,
getAnyTypeColumns(eref.getName(), true), true, false);
// foreign key is not added when the reference is to a generic
// EObject
} else {
addForeignKeyAttribute(associationElement, paReference);
addLazyProxy(associationElement, oto.getFetch(), paReference);
final List<JoinColumn> joinColumns = getJoinColumns(paReference);
addJoinColumns(paReference, associationElement, joinColumns,
isNullable);
// apparently sql server does not like a unique constraint on a
// nullable column
// null values also seem to be seen as a unique value.
if (!isNullable) {
associationElement.addAttribute("unique", "true");
}
}
}
/** Create hibernate one-to-one mapping */
private void createOneToOne(PAnnotatedEReference paReference) {
if (log.isDebugEnabled()) {
log
.debug("Generating one to one bidirectional inverse mapping for "
+ paReference);
}
final OneToOne oto = paReference.getOneToOne();
String targetName = oto.getTargetEntity();
if (targetName == null) {
targetName = getHbmContext().getEntityName(
paReference.getEReferenceType());
}
final HbAnnotatedEReference hbReference = (HbAnnotatedEReference) paReference;
final EReference eref = paReference.getModelEReference();
final EReference otherSide = eref.getEOpposite();
final Element associationElement = addOneToOne(paReference,
getHbmContext().getPropertyName(eref), targetName);
addAccessor(associationElement);
addForeignKeyAttribute(associationElement, paReference);
addCascadesForSingle(associationElement, getCascades(hbReference
.getHbCascade(), oto.getCascade()));
addLazyProxy(associationElement, oto.getFetch(), paReference);
if (hbReference.getHbFetch() != null) {
associationElement.addAttribute("fetch", hbReference.getHbFetch()
.getValue().getName().toLowerCase());
}
// add the other-side
final boolean primaryKeyJoin = !paReference.getPrimaryKeyJoinColumns()
.isEmpty()
|| (otherSide != null && !getOtherSide(paReference)
.getPrimaryKeyJoinColumns().isEmpty());
if (!primaryKeyJoin && otherSide != null && oto.getMappedBy() != null) {
associationElement.addAttribute("property-ref", getHbmContext()
.getPropertyName(otherSide));
}
// apparently is always allowed for a one-to-one, if it is bidirectional
// if (paReference.getPrimaryKeyJoinColumns().size() > 0) {
// place constrained when:
// 1) this side has a primary key join column annotation
boolean addConstrained = !paReference.getPrimaryKeyJoinColumns()
.isEmpty();
if (!addConstrained && otherSide == null) {
// 2) there is no other side and it is mandatory see here:
// http://www.hibernate.org/162.html
addConstrained = eref.isRequired();
} else if (!addConstrained && otherSide != null) {
// 3) the most difficult one, the other side must be a many-to-one
// this is the case when it does not have a pk join column and
// mappedby is
// null, or when it has a many-to-one annotation.
final PAnnotatedEReference aOpposite = paReference.getPaModel()
.getPAnnotated(otherSide);
addConstrained = aOpposite.getManyToOne() != null
&& eref.isRequired();
if (!addConstrained) {
addConstrained = eref.isRequired()
&& aOpposite.getPrimaryKeyJoinColumns().isEmpty()
&& aOpposite.getOneToOne() != null
&& aOpposite.getOneToOne().getMappedBy() == null;
}
}
if (addConstrained) {
associationElement.addAttribute("constrained", "true");
}
// }
}
}