/*
* (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.foundation.dm.eo;
import java.util.Iterator;
import java.util.List;
import java.util.Vector;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.naming.InvalidNameException;
import org.openflexo.foundation.CodeType;
import org.openflexo.foundation.Inspectors;
import org.openflexo.foundation.TargetType;
import org.openflexo.foundation.bindings.Bindable;
import org.openflexo.foundation.bindings.BindingModel;
import org.openflexo.foundation.bindings.BindingVariable;
import org.openflexo.foundation.dm.DMCardinality;
import org.openflexo.foundation.dm.DMEntity;
import org.openflexo.foundation.dm.DMModel;
import org.openflexo.foundation.dm.DMObject;
import org.openflexo.foundation.dm.DMPropertyImplementationType;
import org.openflexo.foundation.dm.DMRegExp;
import org.openflexo.foundation.dm.DMType;
import org.openflexo.foundation.dm.dm.DMAttributeDataModification;
import org.openflexo.foundation.dm.dm.DMPropertyNameChanged;
import org.openflexo.foundation.dm.eo.model.EOAttribute;
import org.openflexo.foundation.dm.eo.model.EOEntity;
import org.openflexo.foundation.dm.eo.model.EOJoin;
import org.openflexo.foundation.dm.eo.model.EOProperty;
import org.openflexo.foundation.dm.eo.model.EORelationship;
import org.openflexo.foundation.dm.eo.model.InvalidJoinException;
import org.openflexo.foundation.validation.FixProposal;
import org.openflexo.foundation.validation.ValidationError;
import org.openflexo.foundation.validation.ValidationIssue;
import org.openflexo.foundation.validation.ValidationRule;
import org.openflexo.foundation.validation.ValidationWarning;
import org.openflexo.foundation.xml.FlexoDMBuilder;
import org.openflexo.localization.FlexoLocalization;
import org.openflexo.toolbox.EmptyVector;
/**
* Represents an accessor as a get/set key-value pair mapping a data stored in a database, implemented in DB as a relationship to an other
* table.
*
* @author sguerin
*
*/
public class DMEORelationship extends DMEOProperty implements Bindable {
private static final Logger logger = Logger.getLogger(DMEORelationship.class.getPackage().getName());
// ==========================================================================
// ============================= Instance variables
// =========================
// ==========================================================================
protected EORelationship _eoRelationship;
private boolean _isFlattenRelationship = false;
private String _lastKnownFlattenRelationshipDefinition = null;
private FlattenRelationshipDefinition _flattenRelationshipDefinition = null;
private FlattenRelationshipDefinitionBindingModel _flattenRelationshipDefinitionBindingModel;
// ==========================================================================
// ============================= Constructor
// ================================
// ==========================================================================
/**
* Constructor used during deserialization
*/
public DMEORelationship(FlexoDMBuilder builder) {
this(builder.dmModel);
initializeDeserialization(builder);
}
/**
* Default constructor
*/
public DMEORelationship(DMModel dmModel) {
super(dmModel);
_dmEOJoins = new Vector<DMEOJoin>();
_dmEOJoinsNeedToBeRecomputed = true;
_destinationEntity = null;
_destinationType = null;
}
/**
* Default constructor for dynamic creation
*/
public DMEORelationship(DMModel dmModel, EORelationship eoRelationship) {
this(dmModel);
_eoRelationship = eoRelationship;
if (eoRelationship != null) {
// setName(eoRelationship.name());
name = eoRelationship.getName();
if (eoRelationship.getIsFlattened()) {
_isFlattenRelationship = true;
}
}
}
/**
* Used for dynamic creation of normal relationship
*/
public static DMEORelationship createsNewDMEORelationship(DMModel dmModel, DMEOEntity dmEOEntity, String name, boolean isReadOnly,
boolean isSettable, DMPropertyImplementationType implementationType) throws EOAccessException {
EORelationship eoRelationship = new EORelationship();
eoRelationship.setName(name);
try {
dmEOEntity.getEOEntity().addRelationship(eoRelationship);
} catch (IllegalArgumentException e) {
if (logger.isLoggable(Level.WARNING)) {
logger.warning("EOControl management failed :" + e.getMessage());
}
throw new EOAccessException(e);
}
DMEORelationship answer = new DMEORelationship(dmModel, eoRelationship);
dmEOEntity.registerProperty(answer);
answer.setIsReadOnly(isReadOnly);
answer.setIsClassProperty(true);
answer.setIsSettable(isSettable);
answer.setImplementationType(implementationType);
answer.setPropagatesPrimaryKey(false);
answer.setOwnsDestination(false);
return answer;
}
/**
* Used for dynamic creation of flatten relationship
*/
public static DMEORelationship createsNewFlattenDMEORelationship(DMModel dmModel, DMEOEntity dmEOEntity, String name,
String definition, boolean isReadOnly, boolean isSettable, DMPropertyImplementationType implementationType)
throws EOAccessException {
EORelationship eoRelationship = new EORelationship();
eoRelationship.setName(name);
eoRelationship.setDefinition(definition);
try {
dmEOEntity.getEOEntity().addRelationship(eoRelationship);
} catch (IllegalArgumentException e) {
if (logger.isLoggable(Level.WARNING)) {
logger.warning("EOControl management failed :" + e.getMessage());
}
throw new EOAccessException(e);
}
DMEORelationship answer = new DMEORelationship(dmModel, eoRelationship);
dmEOEntity.registerProperty(answer);
answer._isFlattenRelationship = true;
answer.setIsReadOnly(isReadOnly);
answer.setIsClassProperty(true);
answer.setIsSettable(isSettable);
answer.setImplementationType(implementationType);
answer.setPropagatesPrimaryKey(false);
answer.setOwnsDestination(false);
return answer;
}
/**
* Overrides getEmbeddedDMObjects
*
* @see org.openflexo.foundation.dm.DMProperty#getEmbeddedDMObjects()
*/
@Override
public Vector<DMObject> getEmbeddedDMObjects() {
Vector<DMObject> v = super.getEmbeddedDMObjects();
if (v instanceof EmptyVector) {
v = new Vector<DMObject>();
}
v.addAll(getDMEOJoins());
return v;
}
@Override
public void delete() {
if (getEORelationship() != null) {
resetJoins();
try {
if (getDMEOEntity() != null && getDMEOEntity().getEOEntity() != null) {
getDMEOEntity().getEOEntity().removeRelationship(getEORelationship());
} else if (getEORelationship().getEntity() != null) {
if (logger.isLoggable(Level.WARNING)) {
logger.warning("No parent DMEOEntity or no EOEntity declared for DMEOEntity. Trying to proceed anyway.");
}
getEORelationship().getEntity().removeRelationship(getEORelationship());
}
} catch (IllegalArgumentException e) {
if (logger.isLoggable(Level.WARNING)) {
logger.warning("EOControl management failed :" + e.getMessage());
}
}
}
super.delete();
_eoRelationship = null;
}
/**
* Return String uniquely identifying inspector template which must be applied when trying to inspect this object
*
* @return a String value
*/
@Override
public String getInspectorName() {
if (getDMRepository() != null && getDMRepository().isReadOnly()) {
return Inspectors.DM.DM_RO_EO_RELATIONSHIP_INSPECTOR;
} else {
if (getIsFlattenRelationship()) {
return Inspectors.DM.DM_EO_FLATTEN_RELATIONSHIP_INSPECTOR;
} else {
return Inspectors.DM.DM_EO_RELATIONSHIP_INSPECTOR;
}
}
}
public EORelationship getEORelationship() {
// logger.info("getEORelationship(), _eoRelationship="+_eoRelationship+" getDMEOEntity()="+getDMEOEntity());
if (_eoRelationship == null) {
if (getDMEOEntity() != null && getDMEOEntity().getEOEntity() != null) {
try {
// logger.info("Build EORelationship");
_eoRelationship = getDMEOEntity().getEOEntity().relationshipNamed(getName());
} catch (IllegalArgumentException e) {
if (logger.isLoggable(Level.WARNING)) {
logger.warning("Could not find EORelationship named " + getName() + " : EOControl management failed");
}
}
if (_eoRelationship == null) {
if (logger.isLoggable(Level.WARNING)) {
logger.warning("Could not find EORelationship named " + getName());
}
}
}
}
return _eoRelationship;
}
public void setEORelationship(EORelationship attribute) {
_eoRelationship = attribute;
}
@Override
public void setName(String newName) throws IllegalArgumentException, InvalidNameException {
if (name == null || !name.equals(newName)) {
if (!isDeserializing() && (newName == null || !DMRegExp.ENTITY_NAME_PATTERN.matcher(newName).matches())) {
throw new InvalidNameException("'" + newName + "' is not a valid name for relationship.");
}
DMEntity containerEntity = getEntity();
if (getEORelationship() != null) {
boolean isClassProperty = _eoRelationship.getIsClassProperty();
EOEntity entity = _eoRelationship.getEntity();
if (entity != null) {
entity.removeRelationship(_eoRelationship);
}
_eoRelationship.setName(newName);
if (entity != null) {
try {
entity.addRelationship(_eoRelationship);
} catch (IllegalArgumentException e) {
_eoRelationship.setName(name);
entity.addRelationship(_eoRelationship);
throw e;
}
_eoRelationship.setIsClassProperty(isClassProperty);
}
}
if (containerEntity != null) {
containerEntity.unregisterProperty(this, false);
}
String oldName = name;
name = newName;
if (containerEntity != null) {
containerEntity.registerProperty(this, false);
}
updateCode();
setChanged();
notifyObservers(new DMPropertyNameChanged(this, oldName, newName));
if (containerEntity != null) {
containerEntity.notifyReordering(this);
}
}
}
public boolean getIsToMany() {
if (getIsFlattenRelationship()) {
if (getFlattenRelationshipDefinition() != null && getFlattenRelationshipDefinition().getBindingPathLastElement() != null) {
// isToMany iff at least one of path element is a toMany
for (int i = 0; i < getFlattenRelationshipDefinition().getBindingPathElementCount(); i++) {
DMEORelationship r = getFlattenRelationshipDefinition().getBindingPathElementAtIndex(i);
if (!r.getIsFlattenRelationship() && r.getIsToMany()) {
return true;
}
}
}
return false;
} else {
if (getEORelationship() != null) {
return getEORelationship().getIsToMany();
}
return true;
}
}
public boolean getIsManyTo() {
DMEORelationship inv = getInverse();
if (inv != null) {
return inv.getIsToMany();
}
return false;
}
public void setIsToMany(boolean aBoolean) {
if (getEORelationship() != null) {
boolean old = getEORelationship().getIsToMany();
if (old && aBoolean || !old && !aBoolean) {
return;
}
getEORelationship().setIsToMany(aBoolean);
updateCode();
setChanged();
notifyObservers(new DMAttributeDataModification("isToMany", new Boolean(!aBoolean), new Boolean(aBoolean)));
setChanged();
notifyObservers(new DMAttributeDataModification("cardinality", aBoolean ? DMCardinality.SINGLE : DMCardinality.VECTOR,
!aBoolean ? DMCardinality.SINGLE : DMCardinality.VECTOR));
}
}
public boolean getIsFlattenRelationship() {
return _isFlattenRelationship;
}
// Normally immutable: never call this except during deserialization
public void _setIsFlattenRelationship(boolean isFlattenRelationship) {
if (isDeserializing()) {
_isFlattenRelationship = isFlattenRelationship;
}
}
// private String _lastKnownFlattenRelationshipDefinition = null;
public FlattenRelationshipDefinition getFlattenRelationshipDefinition() {
if (getEORelationship() != null) {
if (_flattenRelationshipDefinition != null && _lastKnownFlattenRelationshipDefinition != null
&& _lastKnownFlattenRelationshipDefinition.equals(getEORelationship().getDefinition())) {
return _flattenRelationshipDefinition;
} else {
// Rebuild it
_flattenRelationshipDefinition = new FlattenRelationshipDefinition(this, getEORelationship().getDefinition());
_lastKnownFlattenRelationshipDefinition = _flattenRelationshipDefinition.getStringRepresentation(); // Avoid recreation
}
}
return null;
}
public void setFlattenRelationshipDefinition(FlattenRelationshipDefinition flattenRelationshipDefinition) {
if (getEORelationship() != null) {
getEORelationship().setDefinition(flattenRelationshipDefinition.getStringRepresentation());
}
_flattenRelationshipDefinition = flattenRelationshipDefinition;
_lastKnownFlattenRelationshipDefinition = _flattenRelationshipDefinition.getStringRepresentation(); // Avoid recreation
}
@Override
public DMEOEntity getEntity() {
return (DMEOEntity) super.getEntity();
}
public FlattenRelationshipDefinitionBindingModel getFlattenRelationshipDefinitionBindingModel() {
if (_flattenRelationshipDefinitionBindingModel == null && getEntity() != null) {
_flattenRelationshipDefinitionBindingModel = new FlattenRelationshipDefinitionBindingModel();
}
return _flattenRelationshipDefinitionBindingModel;
}
@Override
public BindingModel getBindingModel() {
return getFlattenRelationshipDefinitionBindingModel();
}
protected class FlattenRelationshipDefinitionBindingModel extends BindingModel {
private BindingVariable _bindingVariable;
FlattenRelationshipDefinitionBindingModel() {
_bindingVariable = new BindingVariable(null, getEntity().getDMModel(), "");
_bindingVariable.setVariableName("definition");
_bindingVariable.setType(DMType.makeResolvedDMType(getEntity()));
}
@Override
public int getBindingVariablesCount() {
return 1;
}
@Override
public BindingVariable getBindingVariableAt(int index) {
return _bindingVariable;
}
@Override
public boolean allowsNewBindingVariableCreation() {
return false;
}
}
/**
* Implements
*
* @see org.openflexo.foundation.dm.DMEOProperty#getEOProperty()
* @see org.openflexo.foundation.dm.eo.DMEOProperty#getEOProperty()
*/
@Override
public EOProperty getEOProperty() {
return getEORelationship();
}
@Override
public DMCardinality getCardinality() {
if (getIsToMany()) {
return DMCardinality.VECTOR;
}
return DMCardinality.SINGLE;
}
@Override
public void setCardinality(DMCardinality cardinality) {
if (cardinality == DMCardinality.SINGLE) {
setIsToMany(false);
} else if (cardinality == DMCardinality.VECTOR) {
setIsToMany(true);
}
}
@Override
public DMType getType() {
if (getIsFlattenRelationship()) {
if (getFlattenRelationshipDefinition() != null && getFlattenRelationshipDefinition().getBindingPathLastElement() != this) {
return getFlattenRelationshipDefinition().getBindingPathLastElementType();
} else {
return null;
}
}
if (_destinationType == null && getDestinationEntity() != null) {
_destinationType = DMType.makeResolvedDMType(getDestinationEntity());
}
return _destinationType;
}
@Override
public void setType(DMType type) {
// Non relevant
}
public DeleteRuleType getDeleteRule() {
if (getEORelationship() != null) {
return DeleteRuleType.getDeleteRule(getEORelationship().getDeleteRule());
}
if (!isDeserializing() && logger.isLoggable(Level.SEVERE)) {
logger.severe("DMEORelationship: " + getName() + " has no associated EORelationship.");
}
return DeleteRuleType.NULLIFY;
}
public boolean isNullify() {
return getDeleteRule() == null || getDeleteRule().equals(DeleteRuleType.NULLIFY);
}
public boolean isCascade() {
return getDeleteRule() != null && getDeleteRule().equals(DeleteRuleType.CASCADE);
}
public boolean isDeny() {
return getDeleteRule() != null && getDeleteRule().equals(DeleteRuleType.DENY);
}
public boolean isNoAction() {
return getDeleteRule() != null && getDeleteRule().equals(DeleteRuleType.NO_ACTION);
}
public void setDeleteRule(DeleteRuleType deleteRule) {
if (getEORelationship() != null) {
getEORelationship().setDeleteRule(deleteRule.getEOCode());
setChanged();
}
}
public JoinSemanticType getJoinSemantic() {
if (getEORelationship() != null) {
return JoinSemanticType.getJoinSemanticType(getEORelationship().getJoinSemantic());
}
return isDeserializing() ? null : JoinSemanticType.INNER;
}
public void setJoinSemantic(JoinSemanticType joinSemantic) {
if (getEORelationship() != null) {
getEORelationship().setJoinSemantic(joinSemantic.getEOCode());
updateCode();
setChanged();
}
}
public boolean getIsMandatory() {
if (getEORelationship() != null) {
return getEORelationship().getIsMandatory();
}
return false;
}
public void setIsMandatory(boolean isMandatory) {
if (getEORelationship() != null) {
getEORelationship().setIsMandatory(isMandatory);
updateCode();
setChanged();
}
}
public boolean getOwnsDestination() {
if (getEORelationship() != null) {
return getEORelationship().getOwnsDestination();
}
return false;
}
public void setOwnsDestination(boolean ownsDestination) {
if (getEORelationship() != null) {
getEORelationship().setOwnsDestination(ownsDestination);
updateCode();
setChanged();
}
}
public boolean getPropagatesPrimaryKey() {
if (getEORelationship() != null) {
return getEORelationship().getPropagatesPrimaryKey();
}
return false;
}
public void setPropagatesPrimaryKey(boolean propagatesPrimaryKey) {
if (getEORelationship() != null) {
getEORelationship().setPropagatesPrimaryKey(propagatesPrimaryKey);
updateCode();
setChanged();
}
}
public int getNumberOfToManyFaultsToBatchFetch() {
if (getEORelationship() != null) {
return getEORelationship().getNumberOfToManyFaultsToBatchFetch();
}
return 0;
}
public void setNumberOfToManyFaultsToBatchFetch(int numberOfToManyFaultsToBatchFetch) {
if (getEORelationship() != null) {
getEORelationship().setNumberOfToManyFaultsToBatchFetch(numberOfToManyFaultsToBatchFetch);
setChanged();
}
}
public DMEORelationship getInverse() {
try {
EOEntity dest = getDestinationEntity().getEOEntity();
for (EORelationship rel : dest.getRelationships()) {
DMEORelationship inverseCandidate = getDestinationEntity().getDMEORelationship(rel);
if (isInverseOf(inverseCandidate)) {
return inverseCandidate;
}
}
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
private boolean isInverseOf(DMEORelationship inverseCandidate) {
if (_eoRelationship.getEntity() == inverseCandidate.getDestinationEntity().getEOEntity()) {
if (attributesMatches(_eoRelationship.getSourceAttributes(), inverseCandidate.getEORelationship().getDestinationAttributes())) {
if (attributesMatches(_eoRelationship.getDestinationAttributes(), inverseCandidate.getEORelationship()
.getSourceAttributes())) {
return true;
}
}
}
return false;
}
private boolean attributesMatches(List<EOAttribute> att1List, List<EOAttribute> att2List) {
if (att1List.size() == att2List.size()) {
List<EOAttribute> arr = new Vector<EOAttribute>(att2List);
Iterator<EOAttribute> i = att1List.iterator();
while (i.hasNext()) {
arr.remove(i.next());
}
return arr.size() == 0;
}
return false;
}
// =========================================================
// =================== Joins management ====================
// =========================================================
private Vector<DMEOJoin> _dmEOJoins = new Vector<DMEOJoin>();
private boolean _dmEOJoinsNeedToBeRecomputed = true;
private DMEOEntity _destinationEntity = null;
private DMType _destinationType = null;
public Vector<DMEOJoin> getDMEOJoins() {
if (_dmEOJoinsNeedToBeRecomputed) {
rebuildDMEOJoins();
}
return _dmEOJoins;
}
public void addToDMEOJoins(DMEOJoin join) {
_dmEOJoins.add(join);
updateCode();
}
public void removeFromDMEOJoins(DMEOJoin join) {
_dmEOJoins.remove(join);
updateCode();
}
private void rebuildDMEOJoins() {
if (logger.isLoggable(Level.FINE)) {
logger.fine("rebuildDMEOJoins()");
}
resetJoins();
if (getEORelationship() != null) {
// for (EOJoin j : getEORelationship().getJoins()) logger.info("Join: "+j);
for (Iterator<EOJoin> i = getEORelationship().getJoins().iterator(); i.hasNext();) {
EOJoin next = i.next();
addToDMEOJoins(new DMEOJoin(this, next));
}
_dmEOJoinsNeedToBeRecomputed = false;
}
}
public void updateJoins() {
_dmEOJoinsNeedToBeRecomputed = true;
getDMEOJoins();
}
private void resetJoins() {
Vector<DMEOJoin> toDelete = new Vector<DMEOJoin>();
toDelete.addAll(_dmEOJoins);
for (DMEOJoin next : toDelete) {
next.delete(false);
}
_dmEOJoins.clear();
}
/**
* Overrides parent's method by adressing an NSArray instead of a Vector<Type> in case of a EOF toMany relationship
*/
@Override
public DMType getResultingType() {
if (getIsToMany() && !isDeserializing()) {
DMEntity NSArrayEntity = getDMModel().getEntityNamed("com.webobjects.foundation.NSArray");
if (NSArrayEntity == null) {
logger.warning("Could not access DMEntity com.webobjects.foundation.NSArray");
return DMType.makeUnresolvedDMType("com.webobjects.foundation.NSArray");
} else {
return DMType.makeResolvedDMType(NSArrayEntity);
}
}
return super.getResultingType();
}
/**
* Overrides parent's method by using a NSMutableArray parameter for a toMany relationship
*/
@Override
protected String[] getSetterSignatureCandidates() {
if (getIsToMany()) {
String[] candidates = { getSetterName() + "(NSMutableArray)" };
return candidates;
} else {
return super.getSetterSignatureCandidates();
}
}
/**
* Overrides parent's method by using a NSMutableArray parameter for a toMany relationship
*/
@Override
protected String getSetterHeader() {
if (getIsToMany()) {
StringBuffer methodHeader = new StringBuffer();
methodHeader.append(getSetterModifier());
methodHeader.append("void ");
methodHeader.append(getSetterName());
methodHeader.append("(");
methodHeader.append("NSMutableArray");
methodHeader.append(" ");
methodHeader.append(getSetterParamName());
methodHeader.append(")");
return methodHeader.toString();
} else {
return super.getSetterHeader();
}
}
public DMEOEntity getDestinationEntity() {
if (getIsFlattenRelationship()) {
if (getFlattenRelationshipDefinition() != null && getFlattenRelationshipDefinition().getBindingPathLastElementType() != null) {
return (DMEOEntity) getFlattenRelationshipDefinition().getBindingPathLastElementType().getBaseEntity();
}
return null;
} else {
if (getEORelationship() != null) {
EOEntity destinationEOEntity = getEORelationship().getDestinationEntity();
if (destinationEOEntity != null) {
_destinationEntity = getDMModel().getDMEOEntity(destinationEOEntity);
if (_destinationEntity == null) {
if (logger.isLoggable(Level.WARNING)) {
logger.warning("Could not find DMEOEntity named " + destinationEOEntity.getName());
}
} else {
_destinationType = DMType.makeResolvedDMType(_destinationEntity);
}
} else {
if (_destinationEntity != null) {
DMEOEntity old = _destinationEntity;
_destinationEntity = null;
_destinationType = null;
setChanged();
notifyObservers(new DMAttributeDataModification("destinationEntity", old, null));
}
}
}
}
return _destinationEntity;
}
public void setDestinationEntity(DMEOEntity destinationEntity, boolean performAutoJoin) {
DMEOEntity oldDestinationEntity = getDestinationEntity();
if (destinationEntity != oldDestinationEntity) {
resetJoins();
_destinationEntity = destinationEntity;
if (_destinationEntity == null) {
_destinationType = null;
} else {
_destinationType = DMType.makeResolvedDMType(_destinationEntity);
}
updateCode();
if (getEORelationship() != null) {
if (destinationEntity != null) {
getEORelationship().setDestinationEntity(destinationEntity.getEOEntity());
} else {
getEORelationship().setDestinationEntity(null);
}
}
// Warning: notifications must be done after udating completely the model, otherwise, observers will be notified too soon and
// will get de-synchronized with the model.
setChanged();
notifyObservers(new DMAttributeDataModification("destinationEntity", oldDestinationEntity, destinationEntity));
setChanged();
notifyObservers(new DMAttributeDataModification("type", oldDestinationEntity, destinationEntity));
setChanged();
notifyObservers(new DMAttributeDataModification("DMEOJoins", null, null));
if (destinationEntity != null && performAutoJoin) {
// Automatic name if it was not set yet
if (getName() != null && getName().startsWith(FlexoLocalization.localizedForKey("default_new_relationship_name"))
&& destinationEntity.getName() != null && destinationEntity.getName().trim().length() > 2) {
try {
setName(Character.toLowerCase(destinationEntity.getName().charAt(0)) + destinationEntity.getName().substring(1)
+ (getIsToMany() ? "s" : ""));
} catch (IllegalArgumentException e) {
e.printStackTrace();
} catch (InvalidNameException e) {
e.printStackTrace();
}
}
// Automatic join if possible
boolean throwException = joinAutomatically(destinationEntity, getIsToMany());
if (!throwException) {
throwException = joinAutomatically(destinationEntity, !getIsToMany());
if (throwException) {
setIsToMany(!getIsToMany());
if (getIsToMany() && getName() != null && !getName().endsWith("s")) {
try {
setName(getName() + "s");
} catch (IllegalArgumentException e) {
e.printStackTrace();
} catch (InvalidNameException e) {
e.printStackTrace();
}
} else if (!getIsToMany() && getName() != null && getName().endsWith("s")) {
try {
setName(getName().substring(0, getName().length() - 1));
} catch (IllegalArgumentException e) {
e.printStackTrace();
} catch (InvalidNameException e) {
e.printStackTrace();
}
}
}
}
}
}
}
// Info only: description of what is this relation (joins or definition when flattened)
public String getRelationshipDefinition() {
if (getIsFlattenRelationship()) {
if (getFlattenRelationshipDefinition() != null) {
return getFlattenRelationshipDefinition().getStringRepresentation();
} else {
return "-";
}
} else {
StringBuffer sb = new StringBuffer();
boolean isFirst = true;
sb.append("(");
for (DMEOJoin join : getDMEOJoins()) {
sb.append((isFirst ? "" : ",")
+ (join.getSourceEntity() != null && join.getSourceAttribute() != null ? join.getSourceEntity().getName() + "."
+ join.getSourceAttribute().getName() : join.getSourceEntity() != null ? FlexoLocalization
.localizedForKey("unresolved_attribute") : FlexoLocalization.localizedForKey("unresolved_entity"))
+ "-"
+ (join.getDestinationEntity() != null && join.getDestinationAttribute() != null ? join.getDestinationEntity()
.getName() + "." + join.getDestinationAttribute().getName()
: join.getDestinationEntity() != null ? FlexoLocalization.localizedForKey("unresolved_attribute")
: FlexoLocalization.localizedForKey("unresolved_entity")));
isFirst = false;
}
sb.append(")");
return sb.toString();
}
}
public void setDestinationEntity(DMEOEntity destinationEntity) {
setDestinationEntity(destinationEntity, true);
}
/**
* Creates joins automatically if attributes with the same name (if exact case is not found, an attempt ignoring the case will be made
* but it won't work all the time) and same type can be found
*
* @param destinationEntity
* @param isToMany
* @return
*/
private boolean joinAutomatically(DMEOEntity destinationEntity, boolean isToMany) {
boolean throwException = false;
if (!isToMany) {
for (DMEOAttribute a : destinationEntity.getPrimaryKeyAttributes()) {
DMEOAttribute source = getDMEOEntity().getAttributeNamed(a.getName());
if (source == null) {
source = getDMEOEntity().getAttributeNamedIgnoreCase(a.getName());
}
if (source != null && (source.getType() == null || source.getType().equals(a.getType()))) {
DMEOJoin join = new DMEOJoin(this);
addToDMEOJoins(join);
try {
join.setSourceAttribute(source);
} catch (EOAccessException e) {
e.printStackTrace();
} catch (InvalidJoinException e) {
// should not append
e.printStackTrace();
}
try {
join.setDestinationAttribute(a);
} catch (EOAccessException e) {
e.printStackTrace();
} catch (InvalidJoinException e) {
// should not append
e.printStackTrace();
}
}
}
} else {
for (DMEOAttribute a : getDMEOEntity().getPrimaryKeyAttributes()) {
DMEOAttribute dest = destinationEntity.getAttributeNamed(a.getName());
if (dest == null) {
dest = destinationEntity.getAttributeNamedIgnoreCase(a.getName());
}
if (dest != null && (dest.getType() == null || dest.getType().equals(a.getType()))) {
DMEOJoin join = new DMEOJoin(this);
addToDMEOJoins(join);
try {
join.setSourceAttribute(a);
} catch (EOAccessException e) {
e.printStackTrace();
} catch (InvalidJoinException e) {
// should not append
e.printStackTrace();
}
try {
join.setDestinationAttribute(dest);
} catch (EOAccessException e) {
e.printStackTrace();
} catch (InvalidJoinException e) {
// should not append
e.printStackTrace();
}
}
}
}
return throwException;
}
public DMEOAttribute getPrimarySourceAttribute() {
if (getEORelationship() != null) {
List<EOAttribute> sourceAttributes = getEORelationship().getSourceAttributes();
if (sourceAttributes.size() >= 1) {
EOAttribute sourceAttribute = sourceAttributes.get(0);
return getDMEOEntity().getAttribute(sourceAttribute);
}
}
return null;
}
public DMEOAttribute getPrimaryDestinationAttribute() {
if (getEORelationship() != null) {
List<EOAttribute> destinationAttributes = getEORelationship().getDestinationAttributes();
if (destinationAttributes.size() >= 1) {
EOAttribute destinationAttribute = destinationAttributes.get(0);
if (getDestinationEntity() != null) {
return getDestinationEntity().getAttribute(destinationAttribute);
}
}
}
return null;
}
public DMEOJoin createNewJoin() {
DMEOJoin newJoin = new DMEOJoin(this);
addToDMEOJoins(newJoin);
return newJoin;
}
public void deleteJoin(DMEOJoin join) {
join.delete();
}
public boolean isJoinDeletable(DMEOJoin join) {
return true;
}
@Override
public DMPropertyImplementationType getImplementationType() {
return DMPropertyImplementationType.PUBLIC_ACCESSORS_ONLY;
}
// ==========================================================================
// ============================= Validation
// =================================
// ==========================================================================
public static class DMEORelationshipMustReferToAValidEORelationship extends
ValidationRule<DMEORelationshipMustReferToAValidEORelationship, DMEORelationship> {
public DMEORelationshipMustReferToAValidEORelationship() {
super(DMEORelationship.class, "relationship_must_refer_to_a_valid_eo_relationship");
}
@Override
public ValidationIssue<DMEORelationshipMustReferToAValidEORelationship, DMEORelationship> applyValidation(
final DMEORelationship object) {
if (object.getEORelationship() == null) {
ValidationError<DMEORelationshipMustReferToAValidEORelationship, DMEORelationship> error = new ValidationError<DMEORelationshipMustReferToAValidEORelationship, DMEORelationship>(
this, object, "relationship_($object.name)_must_refer_to_a_valid_eo_relationship");
return error;
}
return null;
}
}
public static class DMEORelationshipToManyIsRarellyMandatory extends
ValidationRule<DMEORelationshipToManyIsRarellyMandatory, DMEORelationship> {
public DMEORelationshipToManyIsRarellyMandatory() {
super(DMEORelationship.class, "to_many_relation_ship_is_rarelly_mandatory");
}
@Override
public ValidationIssue<DMEORelationshipToManyIsRarellyMandatory, DMEORelationship> applyValidation(final DMEORelationship object) {
if (object.getIsToMany() && object.getEORelationship() != null && object.getEORelationship().getIsMandatory()) {
ValidationWarning<DMEORelationshipToManyIsRarellyMandatory, DMEORelationship> error = new ValidationWarning(this, object,
"relationship_($object.name)_is_tomany_and_mandatory");
error.addToFixProposals(new SetRelationNotMandatory());
return error;
}
return null;
}
}
public static class SetRelationNotMandatory extends FixProposal<DMEORelationshipToManyIsRarellyMandatory, DMEORelationship> {
public SetRelationNotMandatory() {
super("make_relationship_($object.name)_not_mandatory");
}
@Override
protected void fixAction() {
getObject().getEORelationship().setIsMandatory(false);
}
}
public static class ForeignKeyTypeMustMatchPrimaryKey extends ValidationRule<ForeignKeyTypeMustMatchPrimaryKey, DMEORelationship> {
public ForeignKeyTypeMustMatchPrimaryKey() {
super(DMEORelationship.class, "foreign_key_type_must_match_primary_key_type");
}
@Override
public ValidationIssue<ForeignKeyTypeMustMatchPrimaryKey, DMEORelationship> applyValidation(DMEORelationship rel) {
Iterator<DMEOJoin> i = rel.getDMEOJoins().iterator();
while (i.hasNext()) {
DMEOJoin j = i.next();
if (j.getSourceAttribute() != null && j.getDestinationAttribute() != null) {
if (j.getSourceAttribute().getPrototype() != j.getDestinationAttribute().getPrototype()) {
Vector<FixProposal<ForeignKeyTypeMustMatchPrimaryKey, DMEORelationship>> v = new Vector<FixProposal<ForeignKeyTypeMustMatchPrimaryKey, DMEORelationship>>();
if (rel.getIsToMany()) {
v.add(new SetPrototype(j.getDestinationAttribute(), j.getSourceAttribute().getPrototype()));
v.add(new SetPrototype(j.getSourceAttribute(), j.getDestinationAttribute().getPrototype()));
return new ValidationError(
this,
j,
"foreign_key_type_($object.sourceAttribute.prototype.name)_must_match_primary_key_type_($object.destinationAttribute.prototype.name)",
v);
} else {
v.add(new SetPrototype(j.getSourceAttribute(), j.getDestinationAttribute().getPrototype()));
v.add(new SetPrototype(j.getDestinationAttribute(), j.getDestinationAttribute().getPrototype()));
return new ValidationError(this, j,
"foreign_key_type_($object.destinationAttribute.prototype.name)_must_match_primary_key_type_($object.sourceAttribute.prototype.name)");
}
}
}
}
return null;
}
public static class SetPrototype extends FixProposal<ForeignKeyTypeMustMatchPrimaryKey, DMEORelationship> {
private DMEOAttribute source;
private DMEOPrototype prototype;
public SetPrototype(DMEOAttribute att, DMEOPrototype prototype) {
super("set_($object.source.name)_to_prototype_($object.prototype.name)");
this.source = att;
this.prototype = prototype;
}
@Override
protected void fixAction() {
source.setPrototype(prototype);
}
public DMEOAttribute getSource() {
return source;
}
public DMEOPrototype getPrototype() {
return prototype;
}
}
@Override
public boolean isValidForTarget(TargetType targetType) {
return targetType != CodeType.PROTOTYPE;
}
}
/*
* private boolean _isJoinCurrentlyEdited = false; private DMEOEntity
* _editedDestinationEntity = null; private DMEOAttribute
* _editedSourceAttribute = null; private DMEOAttribute
* _editedDestinationAttribute = null;
*
* public DMEOEntity getDestinationEntity() { if (_isJoinCurrentlyEdited) {
* return _editedDestinationEntity; } if (getEORelationship() != null) {
* EOEntity destinationEOEntity = getEORelationship().destinationEntity();
* if (destinationEOEntity != null) { DMEOEntity destinationEntity =
* getDMModel().getDMEOEntity(destinationEOEntity); if (destinationEntity ==
* null) { if (logger.isLoggable(Level.WARNING)) logger.warning("Could not
* find DMEOEntity named "+destinationEOEntity.name()); } else { return
* destinationEntity; } } } return null; }
*
* public DMEOAttribute getSourceAttribute() { if (_isJoinCurrentlyEdited) {
* return _editedSourceAttribute; } if (getEORelationship() != null) {
* NSArray sourceAttributes = getEORelationship().sourceAttributes(); if
* (sourceAttributes.count() > 1) { if (logger.isLoggable(Level.WARNING))
* logger.warning("Could not handle multi-join for relationship
* "+getEORelationship().name()); } else if (sourceAttributes.count() == 1) {
* EOAttribute sourceAttribute =
* (EOAttribute)sourceAttributes.objectAtIndex(0); return
* getDMEOEntity().getAttribute(sourceAttribute); } } return null; }
*
* public DMEOAttribute getDestinationAttribute() { if
* (_isJoinCurrentlyEdited) { return _editedDestinationAttribute; } if
* (getEORelationship() != null) { NSArray destinationAttributes =
* getEORelationship().destinationAttributes(); if
* (destinationAttributes.count() > 1) { if
* (logger.isLoggable(Level.WARNING)) logger.warning("Could not handle
* multi-join for relationship "+getEORelationship().name()); } else if
* (destinationAttributes.count() == 1) { EOAttribute destinationAttribute =
* (EOAttribute)destinationAttributes.objectAtIndex(0); if
* (getDestinationEntity() != null) { return
* getDestinationEntity().getAttribute(destinationAttribute); } } } return
* null; }
*
* private void resetJoin() { if (logger.isLoggable(Level.INFO)) logger.info
* ("Reset EOJoin"); _editedDestinationEntity = getDestinationEntity();
* _editedDestinationAttribute = getDestinationAttribute();
* _editedSourceAttribute = getSourceAttribute(); if (getEORelationship() !=
* null) { for (Enumeration en = new
* NSArray(getEORelationship().joins()).objectEnumerator();
* en.hasMoreElements();) { EOJoin join = (EOJoin)en.nextElement();
* getEORelationship().removeJoin(join); } } _isJoinCurrentlyEdited = true; }
*
* private void tryToCreateJoin() { if ((_isJoinCurrentlyEdited) &&
* ((getEORelationship() != null)) && (_editedDestinationEntity != null) &&
* (_editedSourceAttribute != null) && (_editedDestinationAttribute != null) &&
* (_editedDestinationAttribute.getDMEOEntity() == _editedDestinationEntity) &&
* (_editedSourceAttribute.getEOAttribute() != null) &&
* (_editedDestinationAttribute.getEOAttribute() != null)) { if
* (logger.isLoggable(Level.INFO)) logger.info ("Make new EOJoin"); EOJoin
* join = new
* EOJoin(_editedSourceAttribute.getEOAttribute(),_editedDestinationAttribute.getEOAttribute());
* getEORelationship().addJoin(join); _isJoinCurrentlyEdited = false; //
* TODO support a faire pour DMEntity.getPropertiesWithThisType throw new
* RelationshipJoinSuccessfullyUpdated(this); } }
*
* public void setDestinationEntity (DMEOEntity destinationEntity) {
* DMEOEntity oldDestinationEntity = getDestinationEntity(); if
* (destinationEntity != oldDestinationEntity) { if
* (!_isJoinCurrentlyEdited) { resetJoin(); } _editedDestinationEntity =
* destinationEntity; setDestinationAttribute(null); tryToCreateJoin();
* setChanged(); notifyObservers(new
* DMAttributeDataModification("destinationEntity",oldDestinationEntity,destinationEntity)); } }
*
* public void setSourceAttribute (DMEOAttribute sourceAttribute) {
* DMEOAttribute oldSourceAttribute = getSourceAttribute(); if
* (sourceAttribute != oldSourceAttribute) { if (!_isJoinCurrentlyEdited) {
* resetJoin(); } _editedSourceAttribute = sourceAttribute;
* tryToCreateJoin(); setChanged(); notifyObservers(new
* DMAttributeDataModification("sourceAttribute",oldSourceAttribute,sourceAttribute)); } }
*
* public void setDestinationAttribute (DMEOAttribute destinationAttribute) {
* DMEOAttribute oldDestinationAttribute = getDestinationAttribute(); if
* (destinationAttribute != oldDestinationAttribute) { if
* (!_isJoinCurrentlyEdited) { resetJoin(); } _editedDestinationAttribute =
* destinationAttribute; tryToCreateJoin(); setChanged();
* notifyObservers(new
* DMAttributeDataModification("destinationAttribute",oldDestinationAttribute,destinationAttribute)); } }
*/
}