/*
* (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.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.Collections;
import java.util.Comparator;
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.apache.cayenne.wocompat.PropertyListSerialization;
import org.openflexo.logging.FlexoLogger;
/**
* @author gpolet
*
*/
public class EOEntity extends EOObject {
private static final Logger logger = FlexoLogger.getLogger(EOEntity.class.getPackage().getName());
private static final String ATTRIBUTES_KEY = "attributes";
private static final String CLASS_NAME_KEY = "className";
private static final String CLASS_PROPERTIES_KEY = "classProperties";
private static final String EXTERNAL_NAME_KEY = "externalName";
private static final String PRIMARY_KEY_ATTRIBUTES_KEY = "primaryKeyAttributes";
private static final String ATTRIBUTES_USED_FOR_LOCKING_KEY = "attributesUsedForLocking";
private static final String RELATIONSHIPS_KEY = "relationships";
private static final String PARENT_KEY = "parent";
private String className;
private String externalName;
private EOEntity parentEntity;
private EOModel model;
private File file;
private List<EOAttribute> attributes;
private List<EORelationship> relationships;
private List<EOAttribute> primaryKeyAttributes;
private List<EOAttribute> attributesUsedForLocking;
private List<EOProperty> classProperties;
/**
* Incoming relationships are relationships that have this entity as their destination entity
*/
private Vector<EORelationship> incomingRelationships;
public EOEntity() {
attributes = new Vector<EOAttribute>();
relationships = new Vector<EORelationship>();
primaryKeyAttributes = new Vector<EOAttribute>();
attributesUsedForLocking = new Vector<EOAttribute>();
classProperties = new Vector<EOProperty>();
incomingRelationships = new Vector<EORelationship>();
createHashMap();
}
public List<EOAttribute> getAttributes() {
return attributes;
}
/**
* Overrides setName
*
* @see org.openflexo.foundation.dm.eo.model.EOObject#setName(java.lang.String)
*/
@Override
public void setName(String name) throws IllegalStateException {
if (getModel() != null) {
EOEntity e = getModel()._entityNamedIgnoreCase(name);
if (e != null) {
throw new IllegalStateException("duplicated entity '" + e.getName() + "'");
}
if (getName() == null && name != null || getName() != null && !getName().equals(name)) {
if (getFile() != null && getFile().exists()) {
getModel().addToFilesToDelete(getFile());
}
setFile(new File(getModel().getFile(), name + ".plist"));
}
}
super.setName(name);
}
public void setAttributes(List<EOAttribute> attributes) {
Vector<String> v = new Vector<String>();
Iterator<EOAttribute> i = attributes.iterator();
while (i.hasNext()) {
EOAttribute a = i.next();
if (a.getName() != null && !v.contains(a.getName())) {
v.add(a.getName());
} else {
if (logger.isLoggable(Level.WARNING)) {
logger.warning("Removed attribute: " + a);
}
i.remove();
}
}
this.attributes = attributes;
}
public List<EOAttribute> getAttributesUsedForLocking() {
return attributesUsedForLocking;
}
public void setAttributesUsedForLocking(List<EOAttribute> attributesUsedForLocking) {
this.attributesUsedForLocking = attributesUsedForLocking;
}
public String getClassName() {
return className;
}
public void setClassName(String className) {
this.className = className;
}
public String getExternalName() {
return externalName;
}
public void setExternalName(String externalName) {
this.externalName = externalName;
}
public EOEntity getParentEntity() {
return parentEntity;
}
public void setParentEntity(EOEntity parentEntity) throws IllegalArgumentException {
if (parentEntity != null) {
Iterator<EOAttribute> i = getAttributes().iterator();
while (i.hasNext()) {
EOAttribute a = i.next();
if (parentEntity.propertyNamed(a.getName()) != null) {
throw new IllegalArgumentException("Property " + a.getName() + " is defined in both entities " + getName() + " and "
+ parentEntity.getName());
}
}
Iterator<EORelationship> j = getRelationships().iterator();
while (i.hasNext()) {
EORelationship r = j.next();
if (parentEntity.propertyNamed(r.getName()) != null) {
throw new IllegalArgumentException("Property " + r.getName() + " is defined in both entities " + getName() + " and "
+ parentEntity.getName());
}
}
}
this.parentEntity = parentEntity;
}
public List<EOAttribute> getPrimaryKeyAttributes() {
return primaryKeyAttributes;
}
public void setPrimaryKeyAttributes(List<EOAttribute> primaryKeyAttributes) {
this.primaryKeyAttributes = primaryKeyAttributes;
}
public List<EORelationship> getRelationships() {
return relationships;
}
public void setRelationships(List<EORelationship> relationships) {
Vector<String> v = new Vector<String>();
Iterator<EORelationship> i = relationships.iterator();
while (i.hasNext()) {
EORelationship r = i.next();
if (r.getName() != null && !v.contains(r.getName())) {
v.add(r.getName());
} else {
if (logger.isLoggable(Level.WARNING)) {
logger.warning("Removed relationships: " + r);
}
i.remove();
}
}
this.relationships = relationships;
}
public List<EOProperty> getClassProperties() {
return classProperties;
}
public void setClassProperties(List<EOProperty> classProperties) {
this.classProperties = classProperties;
}
public void addAttribute(EOAttribute attribute) throws IllegalArgumentException {
if (attributes.contains(attribute)) {
throw new IllegalArgumentException("Attribute " + attribute.getName() + " is already in entity " + getName());
}
if (propertyNamed(attribute.getName()) != null) {
throw new IllegalArgumentException("An other attribute named " + attribute.getName() + " already exists in entity " + getName());
}
attributes.add(attribute);
classProperties.add(attribute);
attribute.setEntity(this);
getAttributesList().add(attribute.getOriginalMap());
}
public void removeAttribute(EOAttribute attribute) {
if (attributes.contains(attribute)) {
attributes.remove(attribute);
classProperties.remove(attribute);
attributesUsedForLocking.remove(attribute);
primaryKeyAttributes.remove(attribute);
getAttributesList().remove(attribute.getOriginalMap());
attribute.setEntity(null);
}
}
public void addRelationship(EORelationship rel) throws IllegalArgumentException {
if (relationships.contains(rel)) {
throw new IllegalArgumentException("Relationship " + rel.getName() + " is already in entity " + getName());
}
if (propertyNamed(rel.getName()) != null) {
throw new IllegalArgumentException("An other relationship named " + rel.getName() + " already exists in entity " + getName());
}
relationships.add(rel);
classProperties.add(rel);
rel.setEntity(this);
getRelationshipsList().add(rel.getOriginalMap());
}
public void removeRelationship(EORelationship rel) {
if (relationships.contains(rel)) {
relationships.remove(rel);
classProperties.remove(rel);
getRelationshipsList().remove(rel.getOriginalMap());
rel.setEntity(null);
}
}
public EOAttribute attributeNamed(String name) {
if (name == null) {
return null;
}
Iterator<EOAttribute> i = attributes.iterator();
while (i.hasNext()) {
EOAttribute a = i.next();
if (name.equals(a.getName())) {
return a;
}
}
return getParentEntity() != null ? getParentEntity().attributeNamed(name) : null;
}
/**
* Returns the first attribute found that has the same name (ignoring case) than <code>name</code>. Using this method is not intended
* directly for exploring the model but for completion and hints to the end-user
*
* @param name
* - the name to match
* @return the first attribute found that has the same name (ignoring case) than <code>name</code>
*/
public EOAttribute attributeNamedIgnoreCase(String name) {
if (name == null) {
return null;
}
Iterator<EOAttribute> i = attributes.iterator();
while (i.hasNext()) {
EOAttribute a = i.next();
if (name.equalsIgnoreCase(a.getName())) {
return a;
}
}
return getParentEntity() != null ? getParentEntity().attributeNamed(name) : null;
}
public EORelationship relationshipNamed(String name) {
if (name == null) {
return null;
}
Iterator<EORelationship> i = relationships.iterator();
while (i.hasNext()) {
EORelationship a = i.next();
if (name.equals(a.getName())) {
return a;
}
}
return getParentEntity() != null ? getParentEntity().relationshipNamed(name) : null;
}
/**
* Returns the first relationship found that has the same name (ignoring case) than <code>name</code>. Using this method is not intended
* directly for exploring the model but for completion and hints to the end-user
*
* @param name
* - the name to match
* @return the first relationship found that has the same name (ignoring case) than <code>name</code>
*/
public EORelationship relationshipNamedIgnoreCase(String name) {
if (name == null) {
return null;
}
Iterator<EORelationship> i = relationships.iterator();
while (i.hasNext()) {
EORelationship a = i.next();
if (name.equalsIgnoreCase(a.getName())) {
return a;
}
}
return getParentEntity() != null ? getParentEntity().relationshipNamed(name) : null;
}
public EOProperty propertyNamed(String name) {
EOProperty retval = null;
retval = attributeNamed(name);
if (retval == null) {
retval = relationshipNamed(name);
}
return retval;
}
/**
* Returns the first property found that has the same name (ignoring case) than <code>name</code>. It will first look in the attribute
* list then in the relationship one. Using this method is not intended directly for exploring the model but for completion and hints to
* the end-user
*
* @param name
* - the name to match
* @return the first property found that has the same name (ignoring case) than <code>name</code>
*/
public EOProperty propertyNamedIgnoreCase(String name) {
EOProperty retval = null;
retval = attributeNamedIgnoreCase(name);
if (retval == null) {
retval = relationshipNamedIgnoreCase(name);
}
return retval;
}
@SuppressWarnings("unchecked")
public static EOEntity createEntityFromFile(EOModel model, File plistFile) throws FileNotFoundException {
Map<Object, Object> map = (Map<Object, Object>) PropertyListSerialization.propertyListFromFile(plistFile);
EOEntity entity = new EOEntity();
entity.setOriginalMap(map);
entity.setName((String) map.get(NAME_KEY));
entity.setClassName((String) map.get(CLASS_NAME_KEY));
entity.setExternalName((String) map.get(EXTERNAL_NAME_KEY));
entity.setModel(model);
List<Map<Object, Object>> attributesList = (List<Map<Object, Object>>) map.get(ATTRIBUTES_KEY);
if (attributesList != null) {
Iterator<Map<Object, Object>> i = attributesList.iterator();
List<EOAttribute> attributes = new Vector<EOAttribute>();
while (i.hasNext()) {
Map<Object, Object> attributeMap = i.next();
try {
EOAttribute att = EOAttribute.createAttributeFromMap(attributeMap, entity);
attributes.add(att);
} catch (RuntimeException e) {
e.printStackTrace();
if (logger.isLoggable(Level.WARNING)) {
logger.warning("Removing this attribute from original map: " + attributeMap);
}
i.remove();
}
}
entity.setAttributes(attributes);
}
List<Map<Object, Object>> relationshipsList = (List<Map<Object, Object>>) map.get(RELATIONSHIPS_KEY);
if (relationshipsList != null) {
List<EORelationship> relationships = new Vector<EORelationship>();
Iterator<Map<Object, Object>> i = relationshipsList.iterator();
while (i.hasNext()) {
Map<Object, Object> relationshipMap = i.next();
relationships.add(EORelationship.createRelationshipFromMap(relationshipMap, entity));
}
entity.setRelationships(relationships);
}
List<String> classPropertiesList = (List<String>) map.get(CLASS_PROPERTIES_KEY);
if (classPropertiesList != null) {
List<EOProperty> classProperties = new Vector<EOProperty>();
Iterator<String> i = classPropertiesList.iterator();
while (i.hasNext()) {
String classProperty = i.next();
EOProperty p = null;
p = entity.propertyNamed(classProperty);
if (p != null) {
classProperties.add(p);
} else if (logger.isLoggable(Level.WARNING)) {
logger.warning("Could not find class property named " + classProperty + " in entity named " + entity.getName());
}
}
entity.setClassProperties(classProperties);
}
List<String> primaryKeysList = (List<String>) map.get(PRIMARY_KEY_ATTRIBUTES_KEY);
if (primaryKeysList != null) {
List<EOAttribute> primaryKeyAttributes = new Vector<EOAttribute>();
Iterator<String> i = primaryKeysList.iterator();
while (i.hasNext()) {
String attribute = i.next();
EOAttribute a = entity.attributeNamed(attribute);
if (a != null) {
primaryKeyAttributes.add(a);
} else {
if (logger.isLoggable(Level.WARNING)) {
logger.warning("Could not find primary key named " + attribute + " in entity named " + entity.getName());
}
}
}
Collections.sort(primaryKeyAttributes, new Comparator<EOAttribute>() {
@Override
public int compare(EOAttribute o1, EOAttribute o2) {
return o1.getName().compareTo(o2.getName());
}
});
entity.setPrimaryKeyAttributes(primaryKeyAttributes);
}
List<String> lockList = (List<String>) map.get(ATTRIBUTES_USED_FOR_LOCKING_KEY);
if (lockList != null) {
List<EOAttribute> attributesUsedForLocking = new Vector<EOAttribute>();
Iterator<String> i = lockList.iterator();
while (i.hasNext()) {
String attribute = i.next();
EOAttribute a = entity.attributeNamed(attribute);
if (a != null) {
attributesUsedForLocking.add(a);
} else {
if (logger.isLoggable(Level.WARNING)) {
logger.warning("Could not find attribute used for locking named " + attribute + " in entity named "
+ entity.getName());
}
}
}
entity.setAttributesUsedForLocking(attributesUsedForLocking);
}
entity.setFile(plistFile);
return entity;
}
/**
* @return
*/
public List<String> getPrimaryKeyAttributeNames() {
Vector<String> v = new Vector<String>();
Iterator<EOAttribute> i = primaryKeyAttributes.iterator();
while (i.hasNext()) {
EOAttribute a = i.next();
v.add(a.getName());
}
return v;
}
/**
* Overrides resolveObjects
*
* @see org.openflexo.foundation.dm.eo.model.EOObject#resolveObjects()
*/
@Override
protected void resolveObjects() {
Iterator<EOAttribute> i = new Vector<EOAttribute>(getAttributes()).iterator();
while (i.hasNext()) {
EOAttribute a = i.next();
a.resolveObjects();
}
Iterator<EORelationship> j = new Vector<EORelationship>(getRelationships()).iterator();
while (j.hasNext()) {
EORelationship r = j.next();
r.resolveObjects();
}
String parent = (String) getOriginalMap().get(PARENT_KEY);
if (parent != null) {
EOEntity p = getModel().entityNamed(parent);
if (p != null) {
this.parentEntity = p;
} else {
getModel().addToMissingEntities(parent);
if (logger.isLoggable(Level.WARNING)) {
logger.warning("Could not find parent entity named " + parent + " for entity " + getName());
}
}
}
}
public EOModel getModel() {
return model;
}
public void setModel(EOModel model) {
this.model = model;
}
public void addToIncomingRelationships(EORelationship relationship) {
if (!incomingRelationships.contains(relationship)) {
incomingRelationships.add(relationship);
} else if (logger.isLoggable(Level.WARNING)) {
logger.warning("Attempt to add twice the same relationship to incoming relationships of entity " + getName());
}
}
public void removeFromIncomingRelationships(EORelationship relationship) {
incomingRelationships.remove(relationship);
}
public Map<Object, Object> getMapRepresentation() {
Map<Object, Object> map = new HashMap<Object, Object>();
// Updating attributes
if (getName() != null) {
map.put(NAME_KEY, getName());
} else {
map.remove(NAME_KEY);
}
if (getClassName() != null) {
map.put(CLASS_NAME_KEY, getClassName());
} else {
map.remove(CLASS_NAME_KEY);
}
if (getExternalName() != null) {
map.put(EXTERNAL_NAME_KEY, getExternalName());
} else {
map.remove(EXTERNAL_NAME_KEY);
}
// Updating parent
if (getParentEntity() != null) {
map.put(PARENT_KEY, getParentEntity().getName());
} else {
map.remove(PARENT_KEY);
}
// Updating attributes
List<Map<Object, Object>> aList = new Vector<Map<Object, Object>>();
Iterator<EOAttribute> i = getAttributes().iterator();
while (i.hasNext()) {
EOAttribute a = i.next();
aList.add(a.getMapRepresentation());
}
map.put(ATTRIBUTES_KEY, aList);
// Updating relationships
List<Map<Object, Object>> rList = new Vector<Map<Object, Object>>();
Iterator<EORelationship> j = getRelationships().iterator();
while (j.hasNext()) {
EORelationship r = j.next();
rList.add(r.getMapRepresentation());
}
map.put(RELATIONSHIPS_KEY, rList);
// Updating class properties
Vector<String> v = new Vector<String>();
Iterator<EOProperty> k = getClassProperties().iterator();
while (k.hasNext()) {
EOProperty p = k.next();
v.add(p.getName());
}
map.put(CLASS_PROPERTIES_KEY, v);
// Updating primary keys
List<EOAttribute> pk = getPrimaryKeyAttributes();
Collections.sort(pk, new Comparator<EOAttribute>() {
@Override
public int compare(EOAttribute o1, EOAttribute o2) {
return o1.getName().compareTo(o2.getName());
}
});
v = new Vector<String>();
Iterator<EOAttribute> l = pk.iterator();
while (l.hasNext()) {
EOAttribute a = l.next();
v.add(a.getName());
}
map.put(PRIMARY_KEY_ATTRIBUTES_KEY, v);
// Updating attributes used for locking
Vector<String> locks = new Vector<String>();
Iterator<EOAttribute> m = getAttributesUsedForLocking().iterator();
while (m.hasNext()) {
EOAttribute a = m.next();
locks.add(a.getName());
}
map.put(ATTRIBUTES_USED_FOR_LOCKING_KEY, locks);
return map;
}
public String getPListRepresentation() {
return FlexoPropertyListSerialization.getPListRepresentation(getMapRepresentation());
}
/**
* @param file
* @throws IOException
*/
public void writeToFile(File file) throws IOException {
Map<Object, Object> map = getOriginalMap();
// Updating attributes
if (getName() != null) {
map.put(NAME_KEY, getName());
} else {
map.remove(NAME_KEY);
}
if (getClassName() != null) {
map.put(CLASS_NAME_KEY, getClassName());
} else {
map.remove(CLASS_NAME_KEY);
}
if (getExternalName() != null) {
map.put(EXTERNAL_NAME_KEY, getExternalName());
} else {
map.remove(EXTERNAL_NAME_KEY);
}
// Updating parent
if (getParentEntity() != null) {
map.put(PARENT_KEY, getParentEntity().getName());
} else {
map.remove(PARENT_KEY);
}
// Updating attributes
List<Map<Object, Object>> aList = new Vector<Map<Object, Object>>();
Iterator<EOAttribute> i = getAttributes().iterator();
while (i.hasNext()) {
EOAttribute a = i.next();
a.synchronizeObjectWithOriginalMap();
aList.add(a.getOriginalMap());
}
map.put(ATTRIBUTES_KEY, aList);
// Updating relationships
List<Map<Object, Object>> rList = new Vector<Map<Object, Object>>();
Iterator<EORelationship> j = getRelationships().iterator();
while (j.hasNext()) {
EORelationship r = j.next();
r.synchronizeObjectWithOriginalMap();
rList.add(r.getOriginalMap());
}
map.put(RELATIONSHIPS_KEY, rList);
// Updating class properties
Vector<String> v = new Vector<String>();
Iterator<EOProperty> k = getClassProperties().iterator();
while (k.hasNext()) {
EOProperty p = k.next();
v.add(p.getName());
}
map.put(CLASS_PROPERTIES_KEY, v);
// Updating primary keys
List<EOAttribute> pk = getPrimaryKeyAttributes();
Collections.sort(pk, new Comparator<EOAttribute>() {
@Override
public int compare(EOAttribute o1, EOAttribute o2) {
return o1.getName().compareTo(o2.getName());
}
});
v = new Vector<String>();
Iterator<EOAttribute> l = pk.iterator();
while (l.hasNext()) {
EOAttribute a = l.next();
v.add(a.getName());
}
map.put(PRIMARY_KEY_ATTRIBUTES_KEY, v);
// Updating attributes used for locking
Vector<String> locks = new Vector<String>();
Iterator<EOAttribute> m = getAttributesUsedForLocking().iterator();
while (m.hasNext()) {
EOAttribute a = m.next();
locks.add(a.getName());
}
map.put(ATTRIBUTES_USED_FOR_LOCKING_KEY, locks);
// Serialization
setFile(new File(file, getName() + ".plist"));
FlexoPropertyListSerialization.propertyListToFile(getFile(), getOriginalMap());
}
/**
* Overrides delete
*
* @see org.openflexo.foundation.dm.eo.model.EOObject#delete()
*/
@Override
public void delete() {
Iterator<EOAttribute> i = new Vector<EOAttribute>(getAttributes()).iterator();
while (i.hasNext()) {
EOAttribute a = i.next();
a.delete();
}
Iterator<EORelationship> j = new Vector<EORelationship>(getRelationships()).iterator();
while (j.hasNext()) {
EORelationship r = j.next();
r.delete();
}
Iterator<EORelationship> k = new Vector<EORelationship>(incomingRelationships).iterator();
while (k.hasNext()) {
EORelationship r = k.next();
r.setDestinationEntity(null);
}
if (model != null) {
if (getFile() != null) {
model.addToFilesToDelete(getFile());
}
model.removeEntity(this);
}
}
public File getFile() {
return file;
}
public void setFile(File file) {
this.file = file;
}
@SuppressWarnings("unchecked")
public List<Map<Object, Object>> getAttributesList() {
Map<Object, Object> map = getOriginalMap();
if (map.get(ATTRIBUTES_KEY) == null) {
map.put(ATTRIBUTES_KEY, new Vector<Map<Object, Object>>());
}
return (List<Map<Object, Object>>) map.get(ATTRIBUTES_KEY);
}
@SuppressWarnings("unchecked")
public List<Map<Object, Object>> getRelationshipsList() {
Map<Object, Object> map = getOriginalMap();
if (map.get(RELATIONSHIPS_KEY) == null) {
map.put(RELATIONSHIPS_KEY, new Vector<Map<Object, Object>>());
}
return (List<Map<Object, Object>>) map.get(RELATIONSHIPS_KEY);
}
/**
* Overrides clearObjects
*
* @see org.openflexo.foundation.dm.eo.model.EOObject#clearObjects()
*/
@Override
protected void clearObjects() {
parentEntity = null;
incomingRelationships.clear();
Iterator<EOAttribute> i = new Vector<EOAttribute>(getAttributes()).iterator();
while (i.hasNext()) {
EOAttribute a = i.next();
a.clearObjects();
}
Iterator<EORelationship> j = new Vector<EORelationship>(getRelationships()).iterator();
while (j.hasNext()) {
EORelationship r = j.next();
r.clearObjects();
}
}
/**
* @param property
*/
public void removeProperty(EOProperty property) {
if (property instanceof EOAttribute) {
removeAttribute((EOAttribute) property);
} else if (property instanceof EORelationship) {
removeRelationship((EORelationship) property);
} else if (logger.isLoggable(Level.SEVERE)) {
logger.severe("Trying to remove unknown type of EOProperty!!!");
}
}
public Vector<EORelationship> getIncomingRelationships() {
return incomingRelationships;
}
public int getPropertiesSize() {
return attributes.size() + relationships.size();
}
};