/*
* (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.Enumeration;
import java.util.Iterator;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.naming.InvalidNameException;
import org.openflexo.foundation.Inspectors;
import org.openflexo.foundation.dm.DMCardinality;
import org.openflexo.foundation.dm.DMEntity;
import org.openflexo.foundation.dm.DMModel;
import org.openflexo.foundation.dm.DMProperty;
import org.openflexo.foundation.dm.DMPropertyImplementationType;
import org.openflexo.foundation.dm.DMRegExp;
import org.openflexo.foundation.dm.DMType;
import org.openflexo.foundation.dm.DuplicateMethodSignatureException;
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.EOProperty;
import org.openflexo.foundation.dm.eo.model.EORelationship;
import org.openflexo.foundation.dm.javaparser.ParserNotInstalledException;
import org.openflexo.foundation.validation.FixProposal;
import org.openflexo.foundation.validation.Validable;
import org.openflexo.foundation.validation.ValidationError;
import org.openflexo.foundation.validation.ValidationIssue;
import org.openflexo.foundation.validation.ValidationRule;
import org.openflexo.foundation.xml.FlexoDMBuilder;
import org.openflexo.toolbox.StringUtils;
import org.openflexo.toolbox.ToolBox;
/**
* Represents an accessor as a get/set key-value pair mapping a data stored in a database, as a field in a table.
*
* @author sguerin
*
*/
public class DMEOAttribute extends DMEOProperty {
private static final Logger logger = Logger.getLogger(DMEOAttribute.class.getPackage().getName());
public static final String BOOLEAN_PROTOTYPE_NAME = "boolean";
public static final String BOOLEAN_METHOD_POSTFIX = "Boolean";
// ==========================================================================
// ============================= Instance variables
// =========================
// ==========================================================================
protected EOAttribute _eoAttribute;
// ==========================================================================
// ============================= Constructor
// ================================
// ==========================================================================
/**
* Constructor used during deserialization
*/
public DMEOAttribute(FlexoDMBuilder builder) {
this(builder.dmModel);
initializeDeserialization(builder);
}
/**
* Default constructor
*/
public DMEOAttribute(DMModel dmModel) {
super(dmModel);
}
/**
* Default constructor for dynamic creation
*/
public DMEOAttribute(DMModel dmModel, EOAttribute eoAttribute) {
this(dmModel);
_eoAttribute = eoAttribute;
if (eoAttribute != null) {
// setName(eoAttribute.name());
name = eoAttribute.getName();
}
}
/**
* Used for dynamic creation
*/
public static DMEOAttribute createsNewDMEOAttribute(DMModel dmModel, DMEOEntity dmEOEntity, String name, boolean isReadOnly,
boolean isSettable, DMPropertyImplementationType implementationType) throws EOAccessException {
EOAttribute eoAttribute = new EOAttribute();
eoAttribute.setName(name);
try {
dmEOEntity.getEOEntity().addAttribute(eoAttribute);
} catch (IllegalArgumentException e) {
if (logger.isLoggable(Level.WARNING)) {
logger.warning("EOControl management failed :" + e.getMessage());
}
throw new EOAccessException(e);
}
DMEOAttribute answer = new DMEOAttribute(dmModel, eoAttribute);
dmEOEntity.registerProperty(answer);
answer.setColumnName(ToolBox.convertJavaStringToDBName(name));
answer.setIsReadOnlyAttribute(isReadOnly);
answer.setIsSettable(isSettable);
answer.setImplementationType(implementationType);
answer.setIsUsedForLocking(false);
answer.setAllowsNull(true);
answer.setIsClassProperty(true);
answer.setEntity(dmEOEntity);
return answer;
}
@Override
protected void updateCode() {
if (getPrototype() != null) {
super.updateCode();
}
}
@Override
public void delete() {
setPrototype(null);
if (getEOAttribute() != null) {
try {
if (getDMEOEntity() != null && getDMEOEntity().getEOEntity() != null) {
getDMEOEntity().getEOEntity().removeAttribute(getEOAttribute());
} else if (getEOAttribute().getEntity() != null) {
if (logger.isLoggable(Level.WARNING)) {
logger.warning("No parent DMEOEntity or no EOEntity declared for DMEOEntity. Trying to proceed anyway.");
}
getEOAttribute().getEntity().removeAttribute(getEOAttribute());
}
} catch (IllegalArgumentException e) {
if (logger.isLoggable(Level.WARNING)) {
logger.warning("EOControl management failed :" + e.getMessage());
}
}
}
super.delete();
_eoAttribute = 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_ATTRIBUTE_INSPECTOR;
} else {
return Inspectors.DM.DM_EO_ATTRIBUTE_INSPECTOR;
}
}
@Override
public boolean isDescriptionImportant() {
return !getIsPrimaryKeyAttribute();
}
public EOAttribute getEOAttribute() {
if (_eoAttribute == null) {
if (getDMEOEntity() != null && getDMEOEntity().getEOEntity() != null) {
try {
_eoAttribute = getDMEOEntity().getEOEntity().attributeNamed(getName());
} catch (IllegalArgumentException e) {
if (logger.isLoggable(Level.WARNING)) {
logger.warning("Could not find EOAttribute named " + getName() + " : EOControl management failed");
}
}
if (_eoAttribute == null) {
if (logger.isLoggable(Level.WARNING)) {
logger.warning("Could not find EOAttribute named " + getName());
}
}
}
}
return _eoAttribute;
}
public void setEOAttribute(EOAttribute attribute) {
_eoAttribute = attribute;
}
/*
* public String getName() { if (_eoAttribute != null) { return
* _eoAttribute.name(); } else { return super.getName(); } }
*/
/**
* used by velocity (dynamic invocation: DON'T delete !)
*
* @return java class name used in EOClass
*/
public String getJavaClassName() {
try {
if (getEOAttribute().getClassName() != null) {
return getEOAttribute().getClassName().substring(getEOAttribute().getClassName().lastIndexOf(".") + 1);
} else if (getEOAttribute().getPrototype() != null) {
return getEOAttribute().getPrototype().getClassName()
.substring(getEOAttribute().getPrototype().getClassName().lastIndexOf(".") + 1);
} else {
if (logger.isLoggable(Level.WARNING)) {
logger.warning("No JavaClassName nor prototype is set on eoAttribute named \"" + getName() + "\" in entity "
+ getEntity().getName());
}
return "Object"; // Don't know what to do, let's use an
// Object.
}
} catch (Exception e) {
if (getEOAttribute() == null) {
if (logger.isLoggable(Level.FINE)) {
logger.fine("getEOAttribute() return null !!!!");
} else {
if (logger.isLoggable(Level.FINE)) {
logger.fine("Error with EOAttribute : " + getName());
}
}
}
// e.printStackTrace();
return null;
}
}
@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 attribute.");
}
DMEntity containerEntity = getEntity();
if (getEOAttribute() != null) {
boolean isPK = _eoAttribute.getIsPrimaryKey();
boolean isLock = _eoAttribute.getIsUsedForLocking();
boolean isClassProperty = _eoAttribute.getIsClassProperty();
EOEntity e = _eoAttribute.getEntity();
e.removeAttribute(_eoAttribute);
_eoAttribute.setName(newName);
try {
e.addAttribute(_eoAttribute);
} catch (IllegalArgumentException ex) {
_eoAttribute.setName(name);
e.addAttribute(_eoAttribute);
_eoAttribute.setIsPrimaryKey(isPK);
_eoAttribute.setIsUsedForLocking(isLock);
_eoAttribute.setIsClassProperty(isClassProperty);
throw ex;
}
_eoAttribute.setIsPrimaryKey(isPK);
_eoAttribute.setIsUsedForLocking(isLock);
_eoAttribute.setIsClassProperty(isClassProperty);
}
if (containerEntity != null) {
containerEntity.unregisterProperty(this, false);
}
String oldName = name;
name = newName;
if (!isDeserializing() && getEOAttribute() != null) {
Iterator<EORelationship> i = _eoAttribute.getIncomingRelationships().iterator();
while (i.hasNext()) {
EORelationship r = i.next();
DMEOEntity e = getDMModel().getDMEOEntity(r.getEntity());
e.setChanged();
}
i = _eoAttribute.getOutgoingRelationships().iterator();
while (i.hasNext()) {
EORelationship r = i.next();
if (r.getEntity() != null) {
DMEOEntity e = getDMModel().getDMEOEntity(r.getEntity());
e.setChanged();
}
}
}
if (containerEntity != null) {
containerEntity.registerProperty(this, false);
}
updateCode();
setChanged();
notifyObservers(new DMPropertyNameChanged(this, oldName, newName));
if (containerEntity != null) {
containerEntity.notifyReordering(this);
}
if (getDMModel() != null && getDMModel().getEOPrototypeRepository() != null
&& getDMModel().getEOPrototypeRepository().getEOPrototypeEntity() != null) {
if (!isDeserializing()
&& newName != null
&& newName.trim().length() > 0
&& (getColumnName() == null || getColumnName().equals(oldName) || oldName != null
&& getColumnName().equals(ToolBox.getDBTableNameFromPropertyName(oldName)))) {
setColumnName(ToolBox.getDBTableNameFromPropertyName(newName));
}
if (!isDeserializing() && newName != null && newName.trim().length() > 0 && getPrototype() == null
&& newName.toUpperCase().indexOf("DATE") > -1) {
setPrototype(getDMModel().getEOPrototypeRepository().getPrototypeNamed("date"));
}
if (!isDeserializing() && newName != null && newName.trim().length() > 0 && getPrototype() == null
&& newName.toUpperCase().endsWith("ID")) {
setPrototype(getDMModel().getEOPrototypeRepository().getPrototypeNamed("id"));
}
if (!isDeserializing() && newName != null && newName.trim().length() > 0 && getPrototype() == null
&& (newName.toUpperCase().startsWith("IS") || newName.toUpperCase().startsWith("HAS"))) {
setPrototype(getDMModel().getEOPrototypeRepository().getPrototypeNamed("boolean"));
}
}
}
}
public boolean getIsReadOnlyAttribute() {
if (getEOAttribute() != null) {
return getEOAttribute().getIsReadOnly();
}
return super.getIsReadOnly();
}
public void setIsReadOnlyAttribute(boolean aBoolean) {
if (getEOAttribute() != null) {
getEOAttribute().setIsReadOnly(aBoolean);
updateCode();
setChanged();
notifyObservers(new DMAttributeDataModification("isReadOnly", new Boolean(!aBoolean), new Boolean(aBoolean)));
}
}
public String getColumnName() {
if (getEOAttribute() != null) {
return getEOAttribute().getColumnName();
}
return null;
}
public void setColumnName(String cName) {
if (getEOAttribute() != null) {
String oldColumnName = getColumnName();
getEOAttribute().setColumnName(cName);
setChanged();
notifyObservers(new DMAttributeDataModification("columnName", oldColumnName, cName));
}
}
public boolean getIsPrimaryKeyAttribute() {
if (getDMEOEntity() != null) {
return getDMEOEntity().getPrimaryKeyAttributes().contains(this);
}
return false;
}
public void setIsPrimaryKeyAttribute(boolean aBoolean) {
if (getIsPrimaryKeyAttribute() != aBoolean) {
if (getDMEOEntity() != null && getDMEOEntity().getEOEntity() != null && getEOAttribute() != null) {
List<EOAttribute> arrayOfPrimaryKeyAttributes = getDMEOEntity().getEOEntity().getPrimaryKeyAttributes();
if (aBoolean) {
arrayOfPrimaryKeyAttributes.add(getEOAttribute());
getEOAttribute().setAllowsNull(false);
} else {
arrayOfPrimaryKeyAttributes.remove(getEOAttribute());
getEOAttribute().setAllowsNull(true);
}
getDMEOEntity().rebuildPrimaryKeyAttributes();
updateCode();
setChanged();
notifyObservers(new DMAttributeDataModification("isPrimaryKeyAttribute", new Boolean(!aBoolean), new Boolean(aBoolean)));
}
}
}
public boolean getIsUsedForLocking() {
if (getDMEOEntity() != null) {
return getDMEOEntity().getAttributesUsedForLocking().contains(this);
}
return false;
}
public void setIsUsedForLocking(boolean aBoolean) {
if (getIsUsedForLocking() != aBoolean) {
if (getDMEOEntity() != null && getDMEOEntity().getEOEntity() != null && getEOAttribute() != null) {
List<EOAttribute> arrayOfAttributesUsedForLocking = getDMEOEntity().getEOEntity().getAttributesUsedForLocking();
if (aBoolean) {
arrayOfAttributesUsedForLocking.add(getEOAttribute());
} else {
arrayOfAttributesUsedForLocking.remove(getEOAttribute());
}
getDMEOEntity().rebuildAttributesUsedForLocking();
updateCode();
setChanged();
notifyObservers(new DMAttributeDataModification("isUsedForLocking", new Boolean(!aBoolean), new Boolean(aBoolean)));
}
}
}
/**
* Implements
*
* @see org.openflexo.foundation.dm.DMEOProperty#getEOProperty()
* @see org.openflexo.foundation.dm.eo.DMEOProperty#getEOProperty()
*/
@Override
public EOProperty getEOProperty() {
return getEOAttribute();
}
@Override
public DMCardinality getCardinality() {
return DMCardinality.SINGLE;
}
@Override
public void setCardinality(DMCardinality cardinality) {
// Non relevant
}
@Override
public DMType getType() {
if (getPrototype() != null) {
// logger.info("Type for "+getPrototype().getName()+" is "+getPrototype().getType());
return getPrototype().getType();
}
return null;
}
@Override
public void setType(DMType type) {
// Non relevant
}
public int getWidth() {
if (getEOAttribute() != null) {
return getEOAttribute().getWidth();
}
return 0;
}
public void setWidth(int width) {
if (getEOAttribute() != null) {
int oldWidth = getWidth();
getEOAttribute().setWidth(width);
updateCode();
setChanged();
notifyObservers(new DMAttributeDataModification("width", new Integer(oldWidth), new Integer(width)));
}
}
public String getExternalType() {
if (getEOAttribute() != null) {
return getEOAttribute().getExternalType();
}
return null;
}
public void setExternalType(String externalType) {
if (getEOAttribute() != null) {
String oldExternalType = getExternalType();
getEOAttribute().setExternalType(externalType);
updateCode();
setChanged();
notifyObservers(new DMAttributeDataModification("externalType", oldExternalType, externalType));
}
}
public String getValueType() {
if (getEOAttribute() != null) {
return getEOAttribute().getValueType();
}
return null;
}
public void setValueType(String valueType) {
if (getEOAttribute() != null) {
String oldValueType = getValueType();
getEOAttribute().setValueType(valueType);
updateCode();
setChanged();
notifyObservers(new DMAttributeDataModification("valueType", oldValueType, valueType));
}
}
public boolean getAllowsNull() {
if (getEOAttribute() != null) {
return getEOAttribute().getAllowsNull();
}
return true;
}
public void setAllowsNull(boolean aBoolean) {
if (getEOAttribute() != null) {
getEOAttribute().setAllowsNull(aBoolean);
updateCode();
setChanged();
notifyObservers(new DMAttributeDataModification("allowsNull", new Boolean(!aBoolean), new Boolean(aBoolean)));
}
}
public DMEOPrototype getPrototype() {
if (!isSerializing()) {
ensureBooleanPropertyCreation();
}
if (getEOAttribute() != null && getEOAttribute().getPrototype() != null) {
return getDMModel().getEOPrototypeRepository().getPrototype(getEOAttribute().getPrototype());
}
return null;
}
public void setPrototype(DMEOPrototype prototype) {
if (getEOAttribute() != null) {
String oldColumnName = getColumnName();
DMEOPrototype oldPrototype = getPrototype();
if (!isDeserializing() && oldPrototype != null && oldPrototype.getName().equals(BOOLEAN_PROTOTYPE_NAME)
&& getDMEOEntity() != null) {
DMProperty p = getDMEOEntity().getDMProperty(getName() + BOOLEAN_METHOD_POSTFIX);
if (p != null) {
p.delete();
}
}
if (prototype != null) {
getEOAttribute().setPrototype(prototype.getEOAttribute());
} else {
getEOAttribute().setPrototype(null);
}
if (oldColumnName != null) {
setColumnName(oldColumnName);
}
updateCode();
setChanged();
notifyObservers(new DMAttributeDataModification("prototype", oldPrototype, prototype));
}
ensureBooleanPropertyCreation();
}
private boolean creatingBoolean = false;
/**
*
*/
private void ensureBooleanPropertyCreation() {
if (creatingBoolean) {
return;
}
creatingBoolean = true;
try {
if (!isDeserializing() && getPrototype() != null && getPrototype().getName().equals(BOOLEAN_PROTOTYPE_NAME)
&& getDMEOEntity() != null && getDMEOEntity().getDMProperty(getName() + BOOLEAN_METHOD_POSTFIX) == null) {
DMProperty p = new DMProperty(getDMModel(), getName() + BOOLEAN_METHOD_POSTFIX, DMType.makeResolvedDMType(getDMModel()
.getEntityNamed("boolean")), DMCardinality.SINGLE, true, true, DMPropertyImplementationType.PUBLIC_ACCESSORS_ONLY);
StringBuilder sb = new StringBuilder();
sb.append(" return ").append(getName()).append("()!=null && ").append(getName()).append("().equals(")
.append(getProject().getPrefix()).append("Constants.TRUE_VALUE);");
// p.setGetterCoreCode(sb.toString());
try {
p.setGetterCode(getGetterJavadoc() + StringUtils.LINE_SEPARATOR + getGetterHeader() + " { "
+ StringUtils.LINE_SEPARATOR + sb.toString() + StringUtils.LINE_SEPARATOR + "}");
} catch (ParserNotInstalledException e) {
e.printStackTrace();
} catch (DuplicateMethodSignatureException e) {
e.printStackTrace();
}
sb = new StringBuilder();
sb.append(" set").append(ToolBox.capitalize(getName(), true)).append("(value?").append(getProject().getPrefix())
.append("Constants.TRUE_VALUE:").append(getProject().getPrefix()).append("Constants.FALSE_VALUE);");
// p.setSetterCoreCode(sb.toString());
try {
p.setSetterCode(getSetterJavadoc() + StringUtils.LINE_SEPARATOR + getSetterHeader() + " { "
+ StringUtils.LINE_SEPARATOR + sb.toString() + StringUtils.LINE_SEPARATOR + "}");
} catch (ParserNotInstalledException e) {
e.printStackTrace();
} catch (DuplicateMethodSignatureException e) {
e.printStackTrace();
}
getDMEOEntity().registerProperty(p);
}
} finally {
creatingBoolean = false;
}
}
/**
* Overrides setEntity
*
* @see org.openflexo.foundation.dm.DMProperty#setEntity(org.openflexo.foundation.dm.DMEntity)
*/
@Override
public void setEntity(DMEntity entity) {
if (!isDeserializing()) {
DMEOPrototype proto = getPrototype();
setPrototype(null);// Small trick to make embedded Boolean property
// move from one entity to another
super.setEntity(entity);
setPrototype(proto);
} else {
super.setEntity(entity);
}
}
@Override
public boolean codeIsComputable() {
return getPrototype() != null;
}
// ==========================================================================
// ============================= Validation
// =================================
// ==========================================================================
public static class DMEOAttributeMustReferToAValidEORelationship extends ValidationRule {
public DMEOAttributeMustReferToAValidEORelationship() {
super(DMEOAttribute.class, "attribute_must_refer_to_a_valid_eo_attribute");
}
@Override
public ValidationIssue applyValidation(final Validable object) {
final DMEOAttribute property = (DMEOAttribute) object;
if (property.getEOAttribute() == null) {
ValidationError error = new ValidationError(this, object, "attribute_($object.name)_must_refer_to_a_valid_eo_attribute");
return error;
}
return null;
}
}
public static class DMEOAttributeMustHaveAJavaClass extends ValidationRule {
public DMEOAttributeMustHaveAJavaClass() {
super(DMEOAttribute.class, "eoattribute_must_have_a_javaclass");
}
@Override
public ValidationIssue applyValidation(final Validable object) {
final DMEOAttribute property = (DMEOAttribute) object;
if (property.getIsClassProperty() && property.getEOAttribute() != null && property.getEOAttribute().getClassName() == null
&& property.getEOAttribute().getPrototype() == null) {
ValidationError error = new ValidationError(this, object, "eoattribute_($object.name)_has_no_java_class");
for (Enumeration e = property.getDMModel().getEOPrototypeRepository().getEOPrototypeEntity().getAttributes().keys(); e
.hasMoreElements();) {
DMEOPrototype prototype = (DMEOPrototype) property.getDMModel().getEOPrototypeRepository().getEOPrototypeEntity()
.getAttribute((EOAttribute) e.nextElement());
error.addToFixProposals(new SetPrototypeAttribute(prototype));
}
return error;
}
return null;
}
}
public static class SetPrototypeAttribute extends FixProposal {
public DMEOPrototype proto;
public SetPrototypeAttribute(DMEOPrototype aPrototype) {
super("set_prototype_for_($object.name)_to_($proto.name)");
proto = aPrototype;
}
@Override
protected void fixAction() {
((DMEOAttribute) getObject()).setPrototype(proto);
}
}
}