/*
* (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.model;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Vector;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.openflexo.logging.FlexoLogger;
import org.openflexo.toolbox.PListHelper;
/**
* @author gpolet
*
*/
public class EORelationship extends EOProperty {
public static final String InnerJoin = "EOInnerJoin";
public static final String FullOuterJoin = "EOFullOuterJoin";
public static final String LeftOuterJoin = "EOLeftOuterJoin";
public static final String RightOuterJoin = "EORightOuterJoin";
private static final Logger logger = FlexoLogger.getLogger(EORelationship.class.getPackage().getName());
private static final String DELETE_RULE_KEY = "deleteRule";
private static final String DEFINITION_KEY = "definition";
private static final String DESTINATION_KEY = "destination";
private static final String IS_MANDATORY_KEY = "isMandatory";
private static final String IS_TO_MANY_KEY = "isToMany";
private static final String JOIN_SEMANTIC = "joinSemantic";
private static final String JOINS = "joins";
private static final String NR_TO_MANY_FAULTS_TO_BATCH_FETCH_KEY = "numberOfToManyFaultsToBatchFetch";
private static final String OWNS_DESTINATION_KEY = "ownsDestination";
private static final String PROPAGATES_PRIMARY_KEY = "propagatesPrimaryKey";
private String definition;
private String deleteRule;
private EOEntity destinationEntity;
private String joinSemantic;
private boolean isMandatory;
private boolean isToMany;
private boolean ownsDestination;
private boolean propagatesPrimaryKey;
private int numberOfToManyFaultsToBatchFetch;
private List<EOAttribute> destinationAttributes;
private List<EOAttribute> sourceAttributes;
private List<EOJoin> joins;
public static EORelationship createRelationshipFromMap(Map<Object, Object> map) {
return createRelationshipFromMap(map, null);
}
@SuppressWarnings("unchecked")
public static EORelationship createRelationshipFromMap(Map<Object, Object> map, EOEntity entity) {
EORelationship relationship = new EORelationship(map);
relationship.setName((String) map.get(NAME_KEY));
relationship.setDeleteRule((String) map.get(DELETE_RULE_KEY));
if (map.get(IS_MANDATORY_KEY) != null) {
relationship.setIsMandatory(PListHelper.getBoolean(map.get(IS_MANDATORY_KEY)));
} else {
relationship.setIsMandatory(false);
}
if (map.get(IS_TO_MANY_KEY) != null) {
relationship.setIsToMany(PListHelper.getBoolean(map.get(IS_TO_MANY_KEY)));
} else {
relationship.setIsToMany(false);
}
if (map.get(OWNS_DESTINATION_KEY) != null) {
relationship.setOwnsDestination(PListHelper.getBoolean(map.get(OWNS_DESTINATION_KEY)));
} else {
relationship.setOwnsDestination(false);
}
if (map.get(PROPAGATES_PRIMARY_KEY) != null) {
relationship.setPropagatesPrimaryKey(PListHelper.getBoolean(map.get(PROPAGATES_PRIMARY_KEY)));
} else {
relationship.setPropagatesPrimaryKey(false);
}
if (map.get(NR_TO_MANY_FAULTS_TO_BATCH_FETCH_KEY) != null) {
relationship.setNumberOfToManyFaultsToBatchFetch(PListHelper.getInteger(map.get(NR_TO_MANY_FAULTS_TO_BATCH_FETCH_KEY)));
} else {
relationship.setNumberOfToManyFaultsToBatchFetch(0);
}
relationship.setJoinSemantic((String) map.get(JOIN_SEMANTIC));
relationship.setEntity(entity);
relationship.setDefinition((String) map.get(DEFINITION_KEY));
List<Map<Object, Object>> list = (List<Map<Object, Object>>) map.get(JOINS);
if (list != null) {
List<EOJoin> joins = new Vector<EOJoin>();
Iterator<Map<Object, Object>> i = list.iterator();
while (i.hasNext()) {
Map<Object, Object> m = i.next();
joins.add(EOJoin.createJoinFromMap(m, relationship));
}
relationship.setJoins(joins);
}
return relationship;
}
public EORelationship() {
this(null);
}
public EORelationship(Map<Object, Object> map) {
joins = new Vector<EOJoin>();
destinationAttributes = new Vector<EOAttribute>();
sourceAttributes = new Vector<EOAttribute>();
joinSemantic = InnerJoin;
if (map == null) {
createHashMap();
} else {
setOriginalMap(map);
}
if (getOriginalMap().get(JOINS) == null) {
getOriginalMap().put(JOINS, new Vector<Map<Object, Object>>());
}
}
public EOJoin joinWithSourceAttribute(EOAttribute attribute) {
Iterator<EOJoin> i = getJoins().iterator();
while (i.hasNext()) {
EOJoin join = i.next();
if (attribute.equals(join.getSourceAttribute())) {
return join;
}
}
return null;
}
public EOJoin joinWithDestinationAttribute(EOAttribute attribute) {
Iterator<EOJoin> i = getJoins().iterator();
while (i.hasNext()) {
EOJoin join = i.next();
if (attribute.equals(join.getDestinationAttribute())) {
return join;
}
}
return null;
}
public String getDefinition() {
return definition;
}
public void setDefinition(String definition) {
this.definition = definition;
if (definition != null) {
setJoinSemantic(InnerJoin);
setJoins(new Vector<EOJoin>());
setDestinationEntity(null);
}
}
public String getDeleteRule() {
return deleteRule;
}
public void setDeleteRule(String deleteRule) {
this.deleteRule = deleteRule;
}
public List<EOAttribute> getDestinationAttributes() {
return destinationAttributes;
}
public void setDestinationAttributes(List<EOAttribute> destinationAttributes) {
this.destinationAttributes = destinationAttributes;
}
public EOEntity getDestinationEntity() {
if (getIsFlattened()) {
String[] s = getDefinition().split("\\.");
EOEntity e = getEntity();
for (int i = 0; i < s.length; i++) {
String string = s[i];
EORelationship r = e.relationshipNamed(string);
if (r != null) {
e = r.getDestinationEntity();
if (e == null) {
if (logger.isLoggable(Level.WARNING)) {
logger.warning("Destination entity for relationship '" + string + "' is null in entity "
+ r.getEntity().getName());
}
return null;
}
} else {
if (logger.isLoggable(Level.WARNING)) {
logger.warning("Could not find relationship '" + string + "' on entity " + e.getName()
+ " in relation definition: " + getDefinition());
}
return null;
}
if (i + 1 == s.length) {
return e;
}
}
return null;
}
return destinationEntity;
}
public void setDestinationEntity(EOEntity destinationEntity) {
if (this.destinationEntity != destinationEntity) {
if (this.destinationEntity != null) {
Iterator<EOJoin> i = getJoins().iterator();
while (i.hasNext()) {
EOJoin j = i.next();
if (j.getDestinationAttribute() != null) {
try {
j.setDestinationAttribute(null);
} catch (InvalidJoinException e) {
// NEVER APPEND because arg is null
}
}
}
this.destinationEntity.removeFromIncomingRelationships(this);
}
if (getDestinationAttributes().size() != 0) {
if (logger.isLoggable(Level.WARNING)) {
logger.warning("All destinations attributes have not been properly removed. I will clear them now.");
}
getDestinationAttributes().clear();
}
this.destinationEntity = destinationEntity;
if (destinationEntity != null) {
destinationEntity.addToIncomingRelationships(this);
getOriginalMap().put(DESTINATION_KEY, destinationEntity.getName());
} else {
getOriginalMap().remove(DESTINATION_KEY);
}
}
}
public boolean getIsMandatory() {
return isMandatory;
}
public void setIsMandatory(boolean isMandatory) {
this.isMandatory = isMandatory;
}
public boolean getIsToMany() {
return isToMany;
}
public void setIsToMany(boolean isToMany) {
this.isToMany = isToMany;
}
public List<EOJoin> getJoins() {
return joins;
}
public void setJoins(List<EOJoin> joins) {
if (this.joins != null) {
Iterator<EOJoin> i = this.joins.iterator();
while (i.hasNext()) {
EOJoin join = i.next();
getJoinsList().remove(join.getOriginalMap());
}
}
this.joins = joins;
}
public String getJoinSemantic() {
return joinSemantic;
}
public void setJoinSemantic(String joinSemantic) {
this.joinSemantic = joinSemantic;
}
public int getNumberOfToManyFaultsToBatchFetch() {
return numberOfToManyFaultsToBatchFetch;
}
public void setNumberOfToManyFaultsToBatchFetch(int numberOfToManyFaultsToBatchFetch) {
this.numberOfToManyFaultsToBatchFetch = numberOfToManyFaultsToBatchFetch;
}
public boolean getOwnsDestination() {
return ownsDestination;
}
public void setOwnsDestination(boolean ownsDestination) {
this.ownsDestination = ownsDestination;
}
public boolean getPropagatesPrimaryKey() {
return propagatesPrimaryKey;
}
public void setPropagatesPrimaryKey(boolean propagatesPrimaryKey) {
this.propagatesPrimaryKey = propagatesPrimaryKey;
}
public List<EOAttribute> getSourceAttributes() {
return sourceAttributes;
}
public void setSourceAttributes(List<EOAttribute> sourceAttributes) {
this.sourceAttributes = sourceAttributes;
}
public void addToSourceAttributes(EOAttribute attribute) {
if (!sourceAttributes.contains(attribute)) {
sourceAttributes.add(attribute);
attribute.addToOutgoingRelationships(this);
} else if (logger.isLoggable(Level.WARNING)) {
logger.warning("Attempt to insert twice the same attribute: " + attribute.getName()
+ " to sources attributes of relationshipd named " + getName());
}
}
public void removeFromSourceAttributes(EOAttribute attribute) {
sourceAttributes.remove(attribute);
attribute.removeFromOutgoingRelationships(this);
}
public void addToDestinationAttributes(EOAttribute attribute) {
if (!destinationAttributes.contains(attribute)) {
destinationAttributes.add(attribute);
attribute.addToIncomingRelationships(this);
} else if (logger.isLoggable(Level.WARNING)) {
logger.warning("Attempt to insert twice the same attribute: " + attribute.getName()
+ " to destination attributes of relationshipd named " + getName());
}
}
public void removeFromDestinationAttributes(EOAttribute attribute) {
destinationAttributes.remove(attribute);
attribute.removeFromIncomingRelationships(this);
}
public boolean getIsFlattened() {
return getDefinition() != null && getDefinition().indexOf('.') > -1;
}
public void addJoin(EOJoin join) {
// logger.info("addJoin() "+join);
if (joins.contains(join)) {
throw new IllegalArgumentException("The join of " + join.getSourceAttribute().getName() + " and "
+ join.getDestinationAttribute().getName() + " is already in relationship " + getName());
}
if (joinWithSourceAttribute(join.getSourceAttribute()) != null) {
throw new IllegalArgumentException("Another join with source attribute " + join.getSourceAttribute().getName()
+ " already exists in relationship " + getName());
}
if (joinWithDestinationAttribute(join.getSourceAttribute()) != null) {
throw new IllegalArgumentException("Another join with destination attribute " + join.getDestinationAttribute().getName()
+ " already exists in relationship " + getName());
}
joins.add(join);
join.setRelationship(this);
getJoinsList().add(join.getOriginalMap());
}
public void removeJoin(EOJoin join) {
// logger.info("removeJoin() "+join);
if (join.getSourceAttribute() != null) {
removeFromSourceAttributes(join.getSourceAttribute());
}
if (join.getDestinationAttribute() != null) {
removeFromDestinationAttributes(join.getDestinationAttribute());
}
joins.remove(join);
getJoinsList().remove(join.getOriginalMap());
}
/**
* Overrides resolveObjects
*
* @see org.openflexo.foundation.dm.eo.model.EOObject#resolveObjects()
*/
@Override
protected void resolveObjects() {
String dest = (String) getOriginalMap().get(DESTINATION_KEY);
if (dest != null) {
EOEntity destination = getEntity().getModel().entityNamed(dest);
if (destination != null) {
setDestinationEntity(destination);
} else {
getEntity().getModel().addToMissingEntities(dest);
if (logger.isLoggable(Level.WARNING)) {
logger.warning("Could not resolve destination entity named: " + dest);
}
}
}
Iterator<EOJoin> i = getJoins().iterator();
while (i.hasNext()) {
EOJoin j = i.next();
j.resolveObjects();
}
}
/**
* Updates the original map so that it matches the in-memory model
*/
public void synchronizeObjectWithOriginalMap() {
Map<Object, Object> map = getOriginalMap();
if (getName() != null) {
map.put(NAME_KEY, getName());
} else {
map.remove(NAME_KEY);
}
if (getDeleteRule() != null) {
map.put(DELETE_RULE_KEY, getDeleteRule());
} else {
map.remove(DELETE_RULE_KEY);
}
if (getIsMandatory()) {
map.put(IS_MANDATORY_KEY, PListHelper.getObject(getIsMandatory()));
} else {
map.remove(IS_MANDATORY_KEY);
}
map.put(IS_TO_MANY_KEY, PListHelper.getObject(getIsToMany()));
if (getOwnsDestination()) {
map.put(OWNS_DESTINATION_KEY, PListHelper.getObject(getOwnsDestination()));
} else {
map.remove(OWNS_DESTINATION_KEY);
}
if (getPropagatesPrimaryKey()) {
map.put(PROPAGATES_PRIMARY_KEY, PListHelper.getObject(getPropagatesPrimaryKey()));
} else {
map.remove(PROPAGATES_PRIMARY_KEY);
}
if (getNumberOfToManyFaultsToBatchFetch() != 0) {
map.put(NR_TO_MANY_FAULTS_TO_BATCH_FETCH_KEY, PListHelper.getObject(getNumberOfToManyFaultsToBatchFetch()));
} else {
map.remove(NR_TO_MANY_FAULTS_TO_BATCH_FETCH_KEY);
}
if (getJoinSemantic() != null) {
map.put(JOIN_SEMANTIC, getJoinSemantic());
} else {
map.remove(JOIN_SEMANTIC);
}
if (getDefinition() != null) {
map.put(DEFINITION_KEY, getDefinition());
} else {
map.remove(DEFINITION_KEY);
}
if (getDestinationEntity() != null && !getIsFlattened()) {
map.put(DESTINATION_KEY, getDestinationEntity().getName());
} else {
map.remove(DESTINATION_KEY);
}
Iterator<EOJoin> i = getJoins().iterator();
while (i.hasNext()) {
EOJoin j = i.next();
j.synchronizeObjectWithOriginalMap();
}
}
/**
* Overrides delete
*
* @see org.openflexo.foundation.dm.eo.model.EOObject#delete()
*/
@Override
public void delete() {
if (getDestinationEntity() != null) {
getDestinationEntity().removeFromIncomingRelationships(this);
}
Iterator<EOAttribute> i = getSourceAttributes().iterator();
while (i.hasNext()) {
EOAttribute att = i.next();
att.removeFromOutgoingRelationships(this);
}
Iterator<EOAttribute> j = getDestinationAttributes().iterator();
while (j.hasNext()) {
EOAttribute att = j.next();
att.removeFromIncomingRelationships(this);
}
getEntity().removeRelationship(this);
}
@SuppressWarnings("unchecked")
public List<Map<Object, Object>> getJoinsList() {
if (getOriginalMap().get(JOINS) == null) {
getOriginalMap().put(JOINS, new Vector<Map<Object, Object>>());
}
return (List<Map<Object, Object>>) getOriginalMap().get(JOINS);
}
/**
* Overrides clearObjects
*
* @see org.openflexo.foundation.dm.eo.model.EOObject#clearObjects()
*/
@Override
protected void clearObjects() {
Iterator<EOJoin> i = getJoins().iterator();
while (i.hasNext()) {
EOJoin j = i.next();
j.clearObjects();
}
sourceAttributes.clear();
destinationAttributes.clear();
destinationEntity = null;
}
/**
* Overrides toString
*
* @see java.lang.Object#toString()
*/
@Override
public String toString() {
return "EORelationship " + getName() + " " + getEntity() != null ? getEntity().getName() : "no entity";
}
public String getPListRepresentation() {
return FlexoPropertyListSerialization.getPListRepresentation(getMapRepresentation());
}
/**
* @return
*/
public Map<Object, Object> getMapRepresentation() {
Map<Object, Object> map = new HashMap<Object, Object>();
if (getName() != null) {
map.put(NAME_KEY, getName());
} else {
map.remove(NAME_KEY);
}
if (getDeleteRule() != null) {
map.put(DELETE_RULE_KEY, getDeleteRule());
} else {
map.remove(DELETE_RULE_KEY);
}
if (getIsMandatory()) {
map.put(IS_MANDATORY_KEY, PListHelper.getObject(getIsMandatory()));
} else {
map.remove(IS_MANDATORY_KEY);
}
map.put(IS_TO_MANY_KEY, PListHelper.getObject(getIsToMany()));
if (getOwnsDestination()) {
map.put(OWNS_DESTINATION_KEY, PListHelper.getObject(getOwnsDestination()));
} else {
map.remove(OWNS_DESTINATION_KEY);
}
if (getPropagatesPrimaryKey()) {
map.put(PROPAGATES_PRIMARY_KEY, PListHelper.getObject(getPropagatesPrimaryKey()));
} else {
map.remove(PROPAGATES_PRIMARY_KEY);
}
if (getNumberOfToManyFaultsToBatchFetch() != 0) {
map.put(NR_TO_MANY_FAULTS_TO_BATCH_FETCH_KEY, PListHelper.getObject(getNumberOfToManyFaultsToBatchFetch()));
} else {
map.remove(NR_TO_MANY_FAULTS_TO_BATCH_FETCH_KEY);
}
if (getJoinSemantic() != null) {
map.put(JOIN_SEMANTIC, getJoinSemantic());
} else {
map.remove(JOIN_SEMANTIC);
}
if (getDefinition() != null) {
map.put(DEFINITION_KEY, getDefinition());
} else {
map.remove(DEFINITION_KEY);
}
if (getDestinationEntity() != null && !getIsFlattened()) {
map.put(DESTINATION_KEY, getDestinationEntity().getName());
} else {
map.remove(DESTINATION_KEY);
}
List<Map<Object, Object>> jList = new Vector<Map<Object, Object>>();
Iterator<EOJoin> i = getJoins().iterator();
while (i.hasNext()) {
EOJoin j = i.next();
jList.add(j.getMapRepresentation());
}
map.put(JOINS, jList);
return map;
}
}