/*
* (c) Copyright 2010-2011 AgileBirds
*
* This file is part of OpenFlexo.
*
* OpenFlexo is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* OpenFlexo is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with OpenFlexo. If not, see <http://www.gnu.org/licenses/>.
*
*/
package org.openflexo.tm.hibernate.impl;
import java.util.Arrays;
import java.util.Vector;
import javax.naming.InvalidNameException;
import org.apache.commons.lang.StringUtils;
import org.openflexo.foundation.DataModification;
import org.openflexo.foundation.FlexoObservable;
import org.openflexo.foundation.FlexoObserver;
import org.openflexo.foundation.rm.DuplicateResourceException;
import org.openflexo.foundation.sg.implmodel.ImplementationModel;
import org.openflexo.foundation.sg.implmodel.TechnologyModelObject;
import org.openflexo.foundation.sg.implmodel.TechnologyModuleImplementation;
import org.openflexo.foundation.sg.implmodel.event.SGAttributeModification;
import org.openflexo.foundation.sg.implmodel.event.SGObjectAddedToListModification;
import org.openflexo.foundation.sg.implmodel.event.SGObjectDeletedModification;
import org.openflexo.foundation.sg.implmodel.event.SGObjectRemovedFromListModification;
import org.openflexo.foundation.xml.ImplementationModelBuilder;
import org.openflexo.tm.hibernate.impl.enums.HibernateCascade;
import org.openflexo.toolbox.JavaUtils;
/**
* This class defines relationships for the mapping relationships in a Hibernate implementation.
*
* @author Emmanuel Koch, Blue Pimento Services SPRL
*/
public class HibernateRelationship extends TechnologyModelObject implements FlexoObserver {
public static final String CLASS_NAME_KEY = "hibernate_entity";
/** Returns the cascade behaviors of this relationship. */
protected Vector<HibernateCascade> cascadeTypes = new Vector<HibernateCascade>();
/** Indicate that the relation entities must not be fetched when fetching the origin entity. */
protected boolean lazy = true;
/** Indicate that the relation cannot be <code>null</code>. */
protected boolean notNull;
/**
* Flag to indicate this relation is the inverse of another one. A two ways relation must always define which one is the original or the
* inverse one.
*/
protected boolean isInverse;
/** Indicates if this relationship is a to-many relation. */
protected boolean toMany;
/** The name of the index column, to naturally sort records in this to-many relationship (generate List instead of Set in POJOs). */
protected String indexColumnName;
/** The entity where this relationship is defined. */
protected HibernateEntity hibernateEntity;
/** The entity / entities returned by this relationship. */
protected HibernateEntity destination;
/** The inverse relation of this one. */
protected HibernateRelationship inverse;
// ================ //
// = Constructors = //
// ================ //
/**
* Build a new Hibernate relationship for the specified implementation model builder.<br/>
* This constructor is namely invoked during unserialization.
*
* @param builder
* the builder that will create this relationship
*/
public HibernateRelationship(ImplementationModelBuilder builder) {
this(builder.implementationModel);
initializeDeserialization(builder);
}
/**
* Build a new Hibernate relationship for the specified implementation model.
*
* @param implementationModel
* the implementation model where to create this Hibernate relationship
*/
public HibernateRelationship(ImplementationModel implementationModel) {
super(implementationModel);
}
// =========== //
// = Methods = //
// =========== //
/**
* @Override
*/
@Override
public String getClassNameKey() {
return CLASS_NAME_KEY;
}
/**
* {@inheritDoc}
*/
@Override
public boolean getHasInspector() {
return true;
}
/**
* @Override
*/
@Override
public String getFullyQualifiedName() {
return getHibernateEntity().getFullyQualifiedName() + "." + getName();
}
/* ===================== */
/* ====== Actions ====== */
/* ===================== */
/**
* {@inheritDoc}
*/
@Override
public void delete() {
if (getHibernateEntity() != null) {
getHibernateEntity().removeFromRelationships(this);
}
if (getInverse() != null) {
if (getInverse().getIsInverse()) {
getInverse().delete();
} else {
getInverse().setInverse(null);
}
}
setChanged();
notifyObservers(new SGObjectDeletedModification());
super.delete();
deleteObservers();
}
/**
* Used in inspector
*
* @return All HibernateCascades
*/
public Vector<HibernateCascade> getAllHibernateCascades() {
return new Vector<HibernateCascade>(Arrays.asList(HibernateCascade.values()));
}
public boolean getHasInverse() {
return getInverse() != null;
}
/**
* Set if this relationship inverse must exists. <br>
* If <code>hasInverse</code> is true and if the inverse relationship is currently null, a new relation ship will be created on the
* destination entity and will be set as inverse of this one. <br>
* If <code>hasInverse</code> is true and if the inverse relationship is currently not null, the inverse relationship will be deleted
*
* @param hasInverse
*/
public void setHasInverse(boolean hasInverse) {
if (hasInverse && !getHasInverse() && getDestination() != null) {
HibernateRelationship relationship = new HibernateRelationship(getImplementationModel());
getDestination().addToRelationships(relationship);
relationship.setDestination(this.getHibernateEntity());
relationship.setIsInverse(true);
relationship.setToMany(!getToMany());
setInverse(relationship);
setChanged();
notifyObservers(new SGAttributeModification("hasInverse", false, true));
} else if (!hasInverse && getHasInverse()) {
setIsInverse(false);
getInverse().delete();
setChanged();
notifyObservers(new SGAttributeModification("hasInverse", true, false));
}
}
/**
* Get the relationship name if any non empty one exists, otherwise build an automatic name for this relationship based on its
* destination and its cardinality.
*
* @return the relationship name if exists, the built name otherwise.
*/
public String getNameOrBuiltAutomaticOne() {
if (!StringUtils.isBlank(getName())) {
return getName();
}
if (getDestination() == null) {
return JavaUtils.getVariableName("relationship" + getHibernateEntity().getRelationships().size());
}
String builtName = getDestination().getName();
if (getToMany() && !builtName.endsWith("s") && !builtName.endsWith("x")) {
builtName = builtName + "s";
}
builtName = JavaUtils.getVariableName(builtName);
if (getHasInverse() && getIsInverse()) { // Checks that the inverse has not the same name.
if (builtName.equals(getInverse().getName())) {
builtName = builtName + "Inverse";
}
}
return builtName;
}
public void setNameOrBuiltAutomaticOne(String name) throws DuplicateResourceException, InvalidNameException {
setName(name);
}
/* ============== */
/* == Observer == */
/* ============== */
/**
* {@inheritDoc}
*/
@Override
public void update(FlexoObservable observable, DataModification dataModification) {
if (observable == getDestination() && dataModification instanceof SGObjectDeletedModification) {
setDestination(null);
}
}
/* ===================== */
/* == Getter / Setter == */
/* ===================== */
/**
* {@inheritDoc}
*/
@Override
public void setName(String name) throws DuplicateResourceException, InvalidNameException {
name = JavaUtils.getVariableName(name);
super.setName(name);
}
public Vector<HibernateCascade> getCascadeTypes() {
return cascadeTypes;
}
public void setCascadeTypes(Vector<HibernateCascade> cascadeTypes) {
if (requireChange(this.cascadeTypes, cascadeTypes)) {
Object oldValue = this.cascadeTypes;
this.cascadeTypes = cascadeTypes;
setChanged();
notifyObservers(new SGAttributeModification("cascadeTypes", oldValue, cascadeTypes));
}
}
public void addToCascadeTypes(HibernateCascade cascadeType) {
cascadeTypes.add(cascadeType);
setChanged();
notifyObservers(new SGObjectAddedToListModification("cascadeTypes", cascadeType));
}
public void removeFromCascadeTypes(HibernateCascade cascadeType) {
if (cascadeTypes.remove(cascadeType)) {
setChanged();
notifyObservers(new SGObjectRemovedFromListModification("cascadeTypes", cascadeType));
}
}
public boolean getLazy() {
return lazy;
}
public void setLazy(boolean lazy) {
if (requireChange(this.lazy, lazy)) {
Object oldValue = this.lazy;
this.lazy = lazy;
setChanged();
notifyObservers(new SGAttributeModification("lazy", oldValue, lazy));
}
}
public boolean getNotNull() {
return notNull;
}
public void setNotNull(boolean notNull) {
if (requireChange(this.notNull, notNull)) {
Object oldValue = this.notNull;
this.notNull = notNull;
setChanged();
notifyObservers(new SGAttributeModification("notNull", oldValue, notNull));
}
}
public boolean getIsInverse() {
return isInverse;
}
public void setIsInverse(boolean isInverse) {
if (requireChange(this.isInverse, isInverse)) {
Object oldValue = this.isInverse;
this.isInverse = isInverse;
setChanged();
notifyObservers(new SGAttributeModification("isInverse", oldValue, isInverse));
if (getInverse() != null) {
getInverse().setIsInverse(!isInverse);
}
}
}
public boolean getToMany() {
return toMany;
}
public void setToMany(boolean toMany) {
if (requireChange(this.toMany, toMany)) {
Object oldValue = this.toMany;
this.toMany = toMany;
setChanged();
notifyObservers(new SGAttributeModification("toMany", oldValue, toMany));
}
}
public String getIndexColumnName() {
return indexColumnName;
}
public void setIndexColumnName(String indexColumnName) {
if (requireChange(this.indexColumnName, indexColumnName)) {
Object oldValue = this.indexColumnName;
this.indexColumnName = indexColumnName;
setChanged();
notifyObservers(new SGAttributeModification("indexColumnName", oldValue, indexColumnName));
}
}
public HibernateEntity getDestination() {
return destination;
}
public boolean getHasDestination() {
return getDestination() != null;
}
public void setDestination(HibernateEntity destination) {
if (requireChange(this.destination, destination)) {
HibernateEntity oldValue = this.destination;
if (oldValue != null) {
oldValue.deleteObserver(this);
}
this.destination = destination;
if (this.destination != null) {
this.destination.addObserver(this);
}
setChanged();
notifyObservers(new SGAttributeModification("destination", oldValue, destination));
}
}
public HibernateRelationship getInverse() {
return inverse;
}
public void setInverse(HibernateRelationship inverse) {
if (requireChange(this.inverse, inverse)) {
HibernateRelationship oldValue = this.inverse;
this.inverse = inverse;
setChanged();
notifyObservers(new SGAttributeModification("inverse", oldValue, inverse));
if (this.inverse != null) {
this.inverse.setInverse(this);
}
if (oldValue != null) {
oldValue.setInverse(null);
}
}
}
public HibernateEntity getHibernateEntity() {
return hibernateEntity;
}
/**
* Called only from HibernateEntity at deserialization or at relationship creation
*
* @param entity
*/
protected void setHibernateEntity(HibernateEntity entity) {
this.hibernateEntity = entity;
}
/**
* {@inheritDoc}
*/
@Override
public TechnologyModuleImplementation getTechnologyModuleImplementation() {
return getHibernateEntity().getTechnologyModuleImplementation();
}
}