/*
* (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;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Comparator;
import java.util.Hashtable;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;
import java.util.Vector;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.openflexo.foundation.action.AddFlexoProperty;
import org.openflexo.foundation.action.DeleteFlexoProperty;
import org.openflexo.foundation.action.FlexoActionType;
import org.openflexo.foundation.action.FlexoActionizer;
import org.openflexo.foundation.action.FlexoActionnable;
import org.openflexo.foundation.action.SortFlexoProperties;
import org.openflexo.foundation.ontology.EditionPatternInstance;
import org.openflexo.foundation.ontology.EditionPatternReference;
import org.openflexo.foundation.rm.FlexoProject;
import org.openflexo.foundation.rm.ScreenshotResource;
import org.openflexo.foundation.utils.FlexoDocFormat;
import org.openflexo.foundation.utils.FlexoModelObjectReference;
import org.openflexo.foundation.validation.Validable;
import org.openflexo.foundation.viewpoint.EditionPattern;
import org.openflexo.foundation.viewpoint.PatternRole;
import org.openflexo.foundation.wkf.dm.WKFAttributeDataModification;
import org.openflexo.inspector.model.TabModel;
import org.openflexo.localization.FlexoLocalization;
import org.openflexo.localization.Language;
import org.openflexo.logging.FlexoLogger;
import org.openflexo.toolbox.HTMLUtils;
import org.openflexo.ws.client.PPMWebService.PPMObject;
import org.openflexo.xmlcode.StringEncoder;
import org.openflexo.xmlcode.XMLMapping;
/**
* Abstract class for all objects involved in FLEXO model definition
*
* @author sguerin
*
*/
public abstract class FlexoModelObject extends FlexoXMLSerializableObject implements FlexoActionnable {
public static boolean stringHasChanged(String old, String newString) {
return old == null && newString != null || old != null && !old.equals(newString);
}
private static final Logger logger = FlexoLogger.getLogger(FlexoModelObject.class.getPackage().toString());
public static final String ID_SEPARATOR = "_";
public static final Comparator<FlexoModelObject> DEFAULT_COMPARATOR = new FlexoDefaultComparator<FlexoModelObject>();
public static final Comparator<FlexoModelObject> NAME_COMPARATOR = new FlexoNameComparator<FlexoModelObject>();
private long flexoID = -2;
private transient Object _context;
private FlexoDocFormat docFormat;
private static String currentUserIdentifier;
private boolean isRegistered = false;
private Map<String, String> specificDescriptions;
private Vector<FlexoProperty> customProperties;
public static FlexoActionizer<AddFlexoProperty, FlexoModelObject, FlexoModelObject> addFlexoPropertyActionizer;
public static FlexoActionizer<DeleteFlexoProperty, FlexoProperty, FlexoProperty> deleteFlexoPropertyActionizer;
public static FlexoActionizer<SortFlexoProperties, FlexoModelObject, FlexoModelObject> sortFlexoPropertiesActionizer;
private boolean hasSpecificDescriptions = false;
private Vector<FlexoModelObjectReference<?>> referencers;
// Imported objects
private boolean isDeletedOnServer = false;
private String uri;
private String versionURI;
private String uriFromSourceObject;
public static String getCurrentUserIdentifier() {
if (currentUserIdentifier == null) {
currentUserIdentifier = "FLX".intern();
}
return currentUserIdentifier;
}
public static void setCurrentUserIdentifier(String aUserIdentifier) {
if (aUserIdentifier != null && aUserIdentifier.indexOf('#') > -1) {
aUserIdentifier = aUserIdentifier.replace('#', '-');
FlexoModelObject.currentUserIdentifier = aUserIdentifier.intern();
}
}
private String userIdentifier;
public String getUserIdentifier() {
if (userIdentifier == null) {
return getCurrentUserIdentifier();
}
return userIdentifier;
}
public void setUserIdentifier(String aUserIdentifier) {
if (aUserIdentifier != null && aUserIdentifier.indexOf('#') > -1) {
aUserIdentifier = aUserIdentifier.replace('#', '-');
}
userIdentifier = aUserIdentifier != null ? aUserIdentifier.intern() : null;
if (!isDeserializing()) {
fireSerializationIdChanged();
}
}
/**
* Constructor used by Serializable. <blockquote> The deserialization process does not use the object's constructor - the object is
* instantiated without a constructor and initialized using the serialized instance data. The only requirement on the constructor for a
* class that implements Serializable is that the first non-serializable superclass in its inheritence hierarchy must have a no-argument
* constructor. <BR>
* From <a
* href="http://www.jguru.com/faq/view.jsp?EID=251942">http://www.jguru.com/faq/view.jsp?EID=251942</A></blockquote>
*/
public FlexoModelObject() {
this(null);
}
/**
*
*/
public FlexoModelObject(FlexoProject project) {
super();
referencers = new Vector<FlexoModelObjectReference<?>>();
specificDescriptions = new TreeMap<String, String>();
customProperties = new Vector<FlexoProperty>();
_editionPatternReferences = new Vector<EditionPatternReference>();
registerObject(project);
}
protected void registerObject(FlexoProject project) {
if (project != null) {
/*
* if (project.getLastUniqueIDHasBeenSet() && flexoID < 0) { flexoID = project.getNewFlexoID();
* System.err.println("Via constructor New flexo ID: "+flexoID); }
*/
project.register(this);
isRegistered = true;
} else {
if (logger != null && logger.isLoggable(Level.FINE) && !(this instanceof TemporaryFlexoModelObject)) {
logger.fine("No project for object of type " + getClassNameKey());
}
}
}
public FlexoModelObject(FlexoProject project, String description) {
this(project);
this.description = description;
}
/**
* Overrides getStringEncoder
*
* @see org.openflexo.foundation.FlexoXMLSerializableObject#getStringEncoder()
*/
@Override
public StringEncoder getStringEncoder() {
if (getProject() != null) {
return getProject().getStringEncoder();
} else {
if (logger.isLoggable(Level.WARNING)) {
logger.warning("No project, using the default string encoder. Any elements related to a FlexoProject like BindingValues or DMType will fail to be converted");
}
return super.getStringEncoder();
}
}
public FlexoProject getProject() {
if (getXMLResourceData() != null && getXMLResourceData().getFlexoResource() != null) {
return getXMLResourceData().getFlexoResource().getProject();
}
return null;
}
@Override
public XMLMapping getXMLMapping() {
if (getXMLResourceData() != null) {
return getXMLResourceData().getXMLMapping();
}
return null;
}
public boolean isDeletedOnServer() {
return isDeletedOnServer;
}
public void setIsDeletedOnServer(boolean isDeletedOnServer) {
if (this.isDeletedOnServer == isDeletedOnServer) {
return;
}
this.isDeletedOnServer = isDeletedOnServer;
setChanged();
notifyObservers(new DataModification("isDeletedOnServer", !this.isDeletedOnServer, isDeletedOnServer));
}
public void markAsDeletedOnServer() {
setIsDeletedOnServer(true);
}
public void copyObjectAttributesInto(PPMObject object) {
object.setName(getName());
object.setUri(getURI());
object.setVersionUri(getVersionURI());
object.setGeneralDescription(getDescription());
object.setBusinessDescription(getBusinessDescription());
object.setTechnicalDescription(getTechnicalDescription());
object.setUserManualDescription(getUserManualDescription());
}
protected void updateFromObject(PPMObject object) {
setIsDeletedOnServer(false);
setURI(object.getUri());
setVersionURI(object.getVersionUri());
setDescription(object.getGeneralDescription());
setBusinessDescription(object.getBusinessDescription());
setTechnicalDescription(object.getTechnicalDescription());
setUserManualDescription(object.getUserManualDescription());
try {
setName(object.getName());
} catch (Exception e) {
if (logger.isLoggable(Level.SEVERE)) {
logger.log(Level.SEVERE, "setName threw an exception on " + this + "! This should never happen for imported objects", e);
}
}
}
/**
* Returns wheter this object is imported or not. Object implementing FlexoImportableObject should override this method
*
* @return true if this object is imported.
*/
public boolean isImported() {
return false;
}
public boolean isCache() {
return false;
}
public FlexoModelObject getUncachedObject() {
if (!isCache()) {
return this;
} else {
throw new RuntimeException("Object of type " + getClass().getName()
+ " is cached but does not properly override getUncachedObject()! (" + this + ")");
}
}
public Class<? extends PPMObject> getEquivalentPPMClass() {
return null;
}
protected boolean isEquivalentTo(PPMObject object) {
if (object == null) {
return false;
}
if (isDeletedOnServer()) {
return false;
}
if (getEquivalentPPMClass() != object.getClass()) {
return false;
}
if (stringHasChanged(getName(), object.getName())) {
return false;
}
if (stringHasChanged(getURI(), object.getUri())) {
return false;
}
if (stringHasChanged(getVersionURI(), object.getVersionUri())) {
return false;
}
if (stringHasChanged(getDescription(), object.getGeneralDescription())) {
return false;
}
if (stringHasChanged(getBusinessDescription(), object.getBusinessDescription())) {
return false;
}
if (stringHasChanged(getTechnicalDescription(), object.getTechnicalDescription())) {
return false;
}
if (stringHasChanged(getUserManualDescription(), object.getUserManualDescription())) {
return false;
}
return true;
}
public static <O extends FlexoModelObject> O getObjectWithURI(Vector<O> objects, String uri) {
for (O o : objects) {
if (o.getURI().equals(uri)) {
return o;
}
}
return null;
}
public String getURI() {
if (getProject() == null) {
throw new RuntimeException("Project is undefined for object " + getClass().getName());
}
if (isImported() || uri != null) {
return uri;
}
if (isSerializing()) {
return null; // We never serialize URI for unimported objects
}
return getProject().getURI() + "fmo/" + getClass().getSimpleName() + getUserIdentifier() + "_" + getFlexoID();
}
public void setURI(String uri) {
this.uri = uri;
}
public String getVersionURI() {
if (getProject() == null) {
throw new RuntimeException("Project is undefined for object " + getClass().getName());
}
if (isImported() || versionURI != null) {
return versionURI;
}
if (isSerializing()) {
return null; // We never serialize URI for unimported objects
}
return getProject().getProjectVersionURI() + "/fmo/version_of_" + getClass().getSimpleName() + getUserIdentifier() + "_"
+ getFlexoID();
}
public void setVersionURI(String versionURI) {
this.versionURI = versionURI;
}
public String getURIFromSourceObject() {
return uriFromSourceObject;
}
public void setURIFromSourceObject(String uri) {
this.uriFromSourceObject = uri;
}
public abstract String getFullyQualifiedName();
private String name;
public String getName() {
return name;
}
public void setName(String name) throws Exception {
String old = this.name;
this.name = name;
if (!isDeserializing()) {
setChanged();
notifyObservers(new NameChanged(old, this.name));
}
}
/**
* Returns a displayable name that is localized and readable by a user. This method is final because you should rather override
* {@link #getName()} or {@link #getClassNameKey()}
*
* @return a displayable name that is localized and readable by a user.
*/
public final String getDisplayableName() {
if (getName() != null) {
return getLocalizedClassName() + " " + getName();
} else {
return FlexoLocalization.localizedForKey("a") + " " + getLocalizedClassName();
}
}
public String getDisplayableDescription() {
return getDisplayableName();
}
/**
* @return
*/
public String getLocalizedClassName() {
return FlexoLocalization.localizedForKey(getClassNameKey());
}
/**
* @return Returns the flexoID.
*/
public long getFlexoID() {
if (isBeingCloned()) {
return -1;
}
if (getProject() != null) {
if (flexoID < 0 && getProject().getLastUniqueIDHasBeenSet() && !isDeserializing()) {
flexoID = getProject().getNewFlexoID();
// setChanged();
if (!isRegistered) {
registerObject(getProject());
isRegistered = true;
}
}
} else {
if (logger.isLoggable(Level.WARNING)) {
logger.warning("No project for object of type " + getClassNameKey());
}
}
return flexoID;
}
/**
* @param flexoID
* The flexoID to set.
*/
public void setFlexoID(long flexoID) {
if (!isCreatedByCloning() && flexoID > 0 && flexoID != this.flexoID) {
this.flexoID = flexoID;
if (!isDeserializing()) {
setChanged();
notifyObservers(new DataModification("flexoID", new Long(this.flexoID), new Long(flexoID)));
fireSerializationIdChanged();
}
}
if (flexoID < 0 && !isCreatedByCloning() && !(this instanceof FlexoProject) && getXMLResourceData() != null) {
if (logger.isLoggable(Level.WARNING)) {
logger.warning("Tried to set a negative ID on object of class " + getClass().getName());
}
}
}
private void fireSerializationIdChanged() {
for (FlexoModelObjectReference ref : new Vector<FlexoModelObjectReference>(referencers)) {
ref.notifySerializationIdHasChanged();
}
}
public String getSerializationIdentifier() {
return getSerializationIdentifier(getUserIdentifier(), String.valueOf(getFlexoID()));
}
public static String getSerializationIdentifier(String userIdentifier, String flexoId) {
return userIdentifier + ID_SEPARATOR + flexoId;
}
/**
* A map that stores the different declared actions for each class
*/
private static final Map<Class, List<FlexoActionType<?, ?, ?>>> _declaredActionsForClass = new Hashtable<Class, List<FlexoActionType<?, ?, ?>>>();
/**
* A map that stores all the actions for each class (computed with the inheritance of each class)
*/
private static final Hashtable<Class, List<FlexoActionType<?, ?, ?>>> _actionListForClass = new Hashtable<Class, List<FlexoActionType<?, ?, ?>>>();
public List<FlexoActionType<?, ?, ?>> getActionList() {
return getActionList(getClass());
}
public static <T extends FlexoModelObject> List<FlexoActionType<?, ?, ?>> getActionList(Class<T> aClass) {
if (_actionListForClass.get(aClass) == null) {
if (logger.isLoggable(Level.FINE)) {
logger.fine("COMPUTE ACTION_LIST FOR " + aClass.getName());
}
List<FlexoActionType<?, ?, ?>> returned = updateActionListFor(aClass);
if (logger.isLoggable(Level.FINE)) {
logger.fine("DONE. COMPUTE ACTION_LIST FOR " + aClass.getName() + ": " + returned.size() + " action(s) :");
for (FlexoActionType<?, ?, ?> next : returned) {
logger.fine(" " + next.getLocalizedName());
}
logger.fine(".");
}
return returned;
}
List<FlexoActionType<?, ?, ?>> returned = _actionListForClass.get(aClass);
if (logger.isLoggable(Level.FINE)) {
logger.fine("RETURN (NO COMPUTING) ACTION_LIST FOR " + aClass.getName() + ": " + returned.size() + " action(s) :");
for (FlexoActionType<?, ?, ?> next : returned) {
logger.fine(" " + next.getLocalizedName());
}
logger.fine(".");
}
return returned;
}
public static <T1 extends FlexoModelObject, T extends T1> void addActionForClass(FlexoActionType<?, T1, ?> actionType,
Class<T> objectClass) {
if (logger.isLoggable(Level.FINE)) {
logger.fine("addActionForClass: " + actionType + " for " + objectClass);
}
List<FlexoActionType<?, ?, ?>> actions = _declaredActionsForClass.get(objectClass);
if (actions == null) {
actions = new ArrayList<FlexoActionType<?, ?, ?>>();
_declaredActionsForClass.put(objectClass, actions);
}
if (actionType != null) {
if (!actions.contains(actionType)) {
actions.add(actionType);
}
} else {
logger.warning("Trying to declare null action !");
}
if (_actionListForClass != null) {
Vector<Class> entriesToRemove = new Vector<Class>();
for (Class aClass : _actionListForClass.keySet()) {
if (objectClass.isAssignableFrom(aClass)) {
entriesToRemove.add(aClass);
}
}
for (Class aClass : entriesToRemove) {
logger.info("Recompute actions list for " + aClass);
_actionListForClass.remove(aClass);
}
}
}
private static <T extends FlexoModelObject> List<FlexoActionType<?, ?, ?>> updateActionListFor(Class<T> aClass) {
List<FlexoActionType<?, ?, ?>> newActionList = new Vector<FlexoActionType<?, ?, ?>>();
for (Map.Entry<Class, List<FlexoActionType<?, ?, ?>>> e : _declaredActionsForClass.entrySet()) {
if (e.getKey().isAssignableFrom(aClass)) {
newActionList.addAll(e.getValue());
}
}
_actionListForClass.put(aClass, newActionList);
if (logger.isLoggable(Level.FINE)) {
logger.fine("updateActionListFor() class: " + aClass);
for (FlexoActionType a : newActionList) {
logger.finer(" > " + a);
}
}
return newActionList;
}
@Override
public int getActionCount() {
return getActionList().size();
}
@Override
public FlexoActionType<?, ?, ?> getActionTypeAt(int index) {
return getActionList().get(index);
}
public void delete() {
// if (logger.isLoggable(Level.FINE)) logger.fine ("Delete
// "+this.getClass().getName()+" : "+this);
if (isDeleted) {
// in some case : the delete may be called twice on a sequence (in case of deletion of last widget of the sequence)...
// and it will fail
// a good idea would be to avoid this double invocation.
// In the mean time, this little hack will do the trick.
return;
}
isDeleted = true;
if (getProject() != null) {
if (isRegistered) {
getProject().unregister(this);
}
isRegistered = false;
} else {
if (logger.isLoggable(Level.WARNING)) {
logger.warning("Project is null for " + this.getClass().getSimpleName() + "_" + this.getFlexoID());
}
}
for (EditionPatternReference ref : new ArrayList<EditionPatternReference>(getEditionPatternReferences())) {
if (ref.getEditionPatternInstance() != null) {
ref.getEditionPatternInstance().nullifyPatternActor(ref.getPatternRole());
}
ref.delete();
}
_editionPatternReferences.clear();
_editionPatternReferences = null;
for (FlexoModelObjectReference ref : new ArrayList<FlexoModelObjectReference>(referencers)) {
ref.notifyObjectDeletion();
}
referencers.clear();
referencers = null;
setChanged();
notifyObservers(new ObjectDeleted(this));
if (getProject() != null) {
getProject().notifyObjectDeleted(this);
}
}
@Override
public String getDeletedProperty() {
return DELETED_PROPERTY;
}
public void undelete() {
// if (logger.isLoggable(Level.FINE)) logger.fine ("Delete
// "+this.getClass().getName()+" : "+this);
if (getProject() != null) {
if (isRegistered) {
getProject().register(this);
}
} else {
if (logger.isLoggable(Level.WARNING)) {
logger.warning("Project is null for " + this);
}
}
isDeleted = false;
referencers = new Vector<FlexoModelObjectReference<?>>();
setChanged();
if (getProject() != null) {
getProject().notifyObjectCreated(this);
}
}
public boolean isDeleted() {
return isDeleted;
}
protected boolean isDeleted = false;
private boolean dontGenerate = false;
private String description;
public Object getContext() {
return _context;
}
public void setContext(Object context) {
_context = context;
}
/**
*
*/
public boolean getDontEscapeLatex() {
return getDocFormat() != null && getDocFormat() == FlexoDocFormat.LATEX;
}
/**
* @deprecated
* @param dontEscapeLatex
*/
@Deprecated
public void setDontEscapeLatex(boolean dontEscapeLatex) {
if (dontEscapeLatex) {
setDocFormat(FlexoDocFormat.LATEX);
}
setChanged();
notifyObservers(new DataModification("dontEscapeLatex", null, new Boolean(dontEscapeLatex)));
}
public FlexoDocFormat getDocFormat() {
return docFormat;
}
public void setDocFormat(FlexoDocFormat docFormat) {
setDocFormat(docFormat, true);
}
public void setDocFormat(FlexoDocFormat docFormat, boolean notify) {
FlexoDocFormat old = this.docFormat;
this.docFormat = docFormat;
if (notify) {
setChanged();
notifyObservers(new DataModification("docFormat", old, docFormat));
}
}
/**
* @return Returns the dontGenerate.
*/
public boolean getDontGenerate() {
return dontGenerate;
}
/**
* @param dontGenerate
* The dontGenerate to set.
*/
public void setDontGenerate(boolean dontGenerate) {
boolean old = this.dontGenerate;
if (old != dontGenerate) {
this.dontGenerate = dontGenerate;
setChanged();
notifyObservers(new WKFAttributeDataModification("dontGenerate", new Boolean(old), new Boolean(dontGenerate)));
}
}
public abstract String getClassNameKey();
public String getEnglishClassName() {
return FlexoLocalization.localizedForKeyAndLanguage(getClassNameKey(), Language.ENGLISH);
}
public String getFrenchClassName() {
return FlexoLocalization.localizedForKeyAndLanguage(getClassNameKey(), Language.FRENCH);
}
public String getDutchClassName() {
return FlexoLocalization.localizedForKeyAndLanguage(getClassNameKey(), Language.DUTCH);
}
private static final String EMPTY_STRING = "";
public boolean isDocEditable() {
return !isImported();
}
public String getDescription() {
if (description != null && description.startsWith("<html>First")) {
new Exception().printStackTrace();
}
return description;
}
public void setDescription(String description) {
if (!stringHasChanged(this.description, description)) {
return;
}
String old = this.description;
if (old == null && description == null) {
return;
}
if (old == null || !old.equals(description)) {
this.description = description;
setChanged();
notifyObservers(new DataModification("description", old, description));
}
}
public String getFullDescriptionWithOnlyBodyContent(String specificDescriptionType) {
StringBuilder sb = new StringBuilder();
if (getDescription() != null) {
String description = HTMLUtils.extractBodyContent(getDescription());
sb.append(description != null ? description : getDescription());
}
if (getHasSpecificDescriptions() && specificDescriptionType != null
&& getSpecificDescriptionForKey(specificDescriptionType) != null) {
String specifDesc = HTMLUtils.extractBodyContent(getSpecificDescriptionForKey(specificDescriptionType));
sb.append(specifDesc != null ? specifDesc : getSpecificDescriptionForKey(specificDescriptionType));
}
return sb.toString().trim();
}
public Map<String, String> getSpecificDescriptions() {
return specificDescriptions;
}
public void setSpecificDescriptions(Map<String, String> specificDescriptions) {
this.specificDescriptions = new TreeMap<String, String>(specificDescriptions);
}
public Vector<FlexoProperty> getCustomProperties() {
return customProperties;
}
public void setCustomProperties(Vector<FlexoProperty> customProperties) {
if (this.customProperties != null) {
for (FlexoProperty property : this.customProperties) {
property.setOwner(null);
}
}
this.customProperties = customProperties;
if (this.customProperties != null) {
for (FlexoProperty property : new Vector<FlexoProperty>(this.customProperties)) {
property.setOwner(this);
}
}
}
public void addToCustomProperties(FlexoProperty property) {
addToCustomProperties(property, false);
}
public void addToCustomProperties(FlexoProperty property, boolean insertSorted) {
if (insertSorted && property.getName() != null) {
int i = 0;
for (FlexoProperty p : customProperties) {
if (p.getName() != null && p.getName().compareTo(property.getName()) > 0) {
break;
}
i++;
}
customProperties.insertElementAt(property, i);
} else {
customProperties.add(property);
}
if (property != null) {
property.setOwner(this);
}
setChanged();
DataModification dm = new DataModification("customProperties", null, property);
notifyObservers(dm);
}
public void removeFromCustomProperties(FlexoProperty property) {
customProperties.remove(property);
}
public boolean hasPropertyNamed(String name) {
return getPropertyNamed(name) != null;
}
public FlexoProperty getPropertyNamed(String name) {
if (name == null) {
for (FlexoProperty p : getCustomProperties()) {
if (p.getName() == null) {
return p;
}
}
} else {
for (FlexoProperty p : getCustomProperties()) {
if (name.equals(p.getName())) {
return p;
}
}
}
return null;
}
public Vector<FlexoProperty> getProperties(String name) {
Vector<FlexoProperty> v = new Vector<FlexoProperty>();
if (name == null) {
for (FlexoProperty p : getCustomProperties()) {
if (p.getName() == null) {
v.add(p);
}
}
} else {
for (FlexoProperty p : getCustomProperties()) {
if (name.equals(p.getName())) {
v.add(p);
}
}
}
return v;
}
public void addProperty() {
if (addFlexoPropertyActionizer != null) {
addFlexoPropertyActionizer.run(this, null);
}
}
public boolean canSortProperties() {
return customProperties.size() > 1;
}
public void sortProperties() {
if (sortFlexoPropertiesActionizer != null) {
sortFlexoPropertiesActionizer.run(this, null);
}
}
public void deleteProperty(FlexoProperty property) {
if (deleteFlexoPropertyActionizer != null) {
deleteFlexoPropertyActionizer.run(property, null);
}
}
public boolean hasDescription() {
return getDescription() != null && getDescription().trim().length() > 0;
}
/**
* This property is used by the hightlightUncommentedItem mode. The decoration meaning that the description is missing will only appears
* on object for wich this method return true. So this method has to be overridden in subclass.
*
* @return
*/
public boolean isDescriptionImportant() {
return false;
}
public boolean hasSpecificHelp(String key) {
return getSpecificDescriptionForKey(key) != null && getSpecificDescriptionForKey(key).length() > 0;
}
public boolean hasSpecificDescriptionForKey(String key) {
return getSpecificDescriptionForKey(key) != null && getSpecificDescriptionForKey(key).trim().length() > 0;
}
public String getUserManualDescription() {
return getSpecificDescriptionForKey(DocType.DefaultDocType.UserManual.name());
}
public String getTechnicalDescription() {
return getSpecificDescriptionForKey(DocType.DefaultDocType.Technical.name());
}
public String getBusinessDescription() {
return getSpecificDescriptionForKey(DocType.DefaultDocType.Business.name());
}
/**
* @param businessDescription
*/
public void setBusinessDescription(String businessDescription) {
if (businessDescription != null) {
setSpecificDescriptionsForKey(businessDescription, DocType.DefaultDocType.Business.name());
} else {
removeSpecificDescriptionsWithKey(DocType.DefaultDocType.Business.name());
}
setChanged();
notifyObservers(new DataModification("businessDescription", null, businessDescription));
}
/**
* @param technicalDescription
*/
public void setTechnicalDescription(String technicalDescription) {
if (technicalDescription != null) {
setSpecificDescriptionsForKey(technicalDescription, DocType.DefaultDocType.Technical.name());
} else {
removeSpecificDescriptionsWithKey(DocType.DefaultDocType.Technical.name());
}
setChanged();
notifyObservers(new DataModification("technicalDescription", null, technicalDescription));
}
/**
* @param userManualDescription
*/
public void setUserManualDescription(String userManualDescription) {
if (userManualDescription != null) {
setSpecificDescriptionsForKey(userManualDescription, DocType.DefaultDocType.UserManual.name());
} else {
removeSpecificDescriptionsWithKey(DocType.DefaultDocType.UserManual.name());
}
setChanged();
notifyObservers(new DataModification("userManualDescription", null, userManualDescription));
}
public String getSpecificDescriptionForKey(String key) {
return specificDescriptions.get(key);
}
public void setSpecificDescriptionsForKey(String description, String key) {
specificDescriptions.put(key, description);
setChanged();
DataModification dm = new DataModification("specificDescriptions", null, description);
notifyObservers(dm);
}
public void removeSpecificDescriptionsWithKey(String key) {
specificDescriptions.remove(key);
}
@Override
public void setChanged() {
if (getProject() != null && !isDeserializing()) {
getProject().notifyObjectChanged(this);
}
super.setChanged();
}
private String expectedNotificationAttribute;
private boolean notificationHasBeenPerformed;
/**
* Override parent method by detecting unnotified setAttribute action Notify it if non-notified
*/
@Override
public void setObjectForKey(Object value, String key) {
notificationHasBeenPerformed = false;
expectedNotificationAttribute = key;
Object oldValue = objectForKey(key);
super.setObjectForKey(value, key);
if (!notificationHasBeenPerformed) {
if (logger.isLoggable(Level.WARNING)) {
logger.warning("SetAttribute for [" + key + "/" + getClass().getSimpleName() + "] (old value: " + oldValue
+ ", new value: " + value
+ ") was not notified. Please add setChanged()/notifyObservers(...) methods in required set method.");
}
setChanged();
notifyObservers(new AttributeDataModification(key, oldValue, value));
}
}
/**
*/
@Override
public void notifyObservers(DataModification dataModification) {
super.notifyObservers(dataModification);
if (expectedNotificationAttribute != null && dataModification != null
&& expectedNotificationAttribute.equals(dataModification.propertyName())) {
notificationHasBeenPerformed = true;
}
}
/**
*/
public void notifyObserversAsReentrantModification(DataModification dataModification) {
dataModification.setReentrant(true);
super.notifyObservers(dataModification);
if (expectedNotificationAttribute != null && expectedNotificationAttribute.equals(dataModification.propertyName())) {
notificationHasBeenPerformed = true;
}
}
public void addToReferencers(FlexoModelObjectReference<? extends FlexoModelObject> ref) {
if (referencers != null && !referencers.contains(ref)) {
referencers.add(ref);
}
}
public void removeFromReferencers(FlexoModelObjectReference<? extends FlexoModelObject> ref) {
if (referencers != null) {
referencers.remove(ref);
}
}
public Vector<FlexoModelObjectReference<?>> getReferencers() {
return referencers;
}
public static class FlexoDefaultComparator<E extends FlexoModelObject> implements Comparator<E> {
/**
* Overrides compare
*
* @see java.util.Comparator#compare(java.lang.Object, java.lang.Object)
*/
@Override
public int compare(E o1, E o2) {
if (o1.getFlexoID() > o2.getFlexoID()) {
return 1;
} else if (o1.getFlexoID() < o2.getFlexoID()) {
return -1;
} else {
return 0;
}
}
}
public static class FlexoNameComparator<E extends FlexoModelObject> implements Comparator<E> {
/**
* Overrides compare
*
* @see java.util.Comparator#compare(java.lang.Object, java.lang.Object)
*/
@Override
public int compare(E o1, E o2) {
if (o1.getName() == null) {
if (o2.getName() == null) {
return 0;
} else {
return -1;
}
} else if (o2.getName() == null) {
return 1;
}
return o1.getName().compareTo(o2.getName());
}
}
public final int compareTo(FlexoModelObject o2) {
if (getFlexoID() > o2.getFlexoID()) {
return 1;
} else if (getFlexoID() < o2.getFlexoID()) {
return -1;
} else {
return 0;
}
}
public final int compare(FlexoModelObject o1, FlexoModelObject o2) {
return o1.compareTo(o2);
}
// ============================================
// ============== Access to help ==============
// ============================================
private static HelpRetriever _helpRetriever = null;
public static interface HelpRetriever {
public String shortHelpForObject(FlexoModelObject object);
public String longHelpForObject(FlexoModelObject object);
}
/**
* Return help text for supplied object, as defined in DocResourceManager as long version Note: return an HTML version, with embedding
* <html>...</html> tags.
*/
public String getHelpText() {
if (_helpRetriever != null) {
return _helpRetriever.longHelpForObject(this);
}
return null;
}
/**
* Return help text for supplied object, as defined in DocResourceManager as short version Note: return an HTML version, with embedding
* <html>...</html> tags.
*/
public String getShortHelpText() {
if (_helpRetriever != null) {
return _helpRetriever.shortHelpForObject(this);
}
return null;
}
public static HelpRetriever getHelpRetriever() {
return _helpRetriever;
}
public static void setHelpRetriever(HelpRetriever retriever) {
_helpRetriever = retriever;
}
// ================================================
// ============== Dynamic properties ==============
// ================================================
private Hashtable<String, String> _dynamicProperties;
private Hashtable<String, String> _buildDynamicPropertiesWhenRequired() {
if (_dynamicProperties == null && !isSerializing()) {
// logger.info("Build _dynamicProperties for "+this);
_dynamicProperties = new Hashtable<String, String>();
}
return _dynamicProperties;
}
public Hashtable<String, String> getDynamicProperties() {
_buildDynamicPropertiesWhenRequired();
return _dynamicProperties;
}
public void setDynamicProperties(Hashtable<String, String> props) {
_dynamicProperties = props;
}
public void setDynamicPropertiesForKey(String value, String key) {
// logger.info("setDynamicPropertiesForKey: "+key+" value: "+value);
if (_dynamicProperties == null) {
_dynamicProperties = new Hashtable<String, String>();
}
_dynamicProperties.put(key, value);
}
public void removeDynamicPropertiesWithKey(String key) {
if (_dynamicProperties == null) {
_dynamicProperties = new Hashtable<String, String>();
}
_dynamicProperties.remove(key);
}
public boolean getHasSpecificDescriptions() {
return hasSpecificDescriptions;
}
public void setHasSpecificDescriptions(boolean hasSpecificDescription) {
if (this.hasSpecificDescriptions == hasSpecificDescription) {
return;
}
boolean old = this.hasSpecificDescriptions;
this.hasSpecificDescriptions = hasSpecificDescription;
setChanged();
notifyObservers(new DataModification("hasSpecificDescriptions", old, hasSpecificDescription));
}
/**
* Return a Vector of all embedded ModelObject
*
* @return a Vector of FlexoModelObject instances
*/
public Collection<FlexoModelObject> getEmbeddedObjects() {
return getXMLMapping().getEmbeddedObjectsForObject(this, FlexoModelObject.class);
}
/**
* Returns a vector of all objects that will be deleted if you call delete on this object.
*
* @return
*/
public Collection<FlexoModelObject> getAllRecursivelyEmbeddedDeletedObjects() {
return getXMLMapping().getEmbeddedObjectsForObject(this, FlexoModelObject.class, false, true);
}
public Collection<FlexoModelObject> getAllRecursivelyEmbeddedObjects() {
return getAllRecursivelyEmbeddedObjects(false);
}
public Collection<FlexoModelObject> getAllRecursivelyEmbeddedObjects(boolean maintainNaturalOrder) {
return getXMLMapping().getEmbeddedObjectsForObject(this, FlexoModelObject.class, maintainNaturalOrder, true);
}
/**
* Return a vector of all embedded objects on which the validation will be performed
*
* @return a Vector of Validable objects
*/
public Vector<Validable> getAllEmbeddedValidableObjects() {
Vector<Validable> vector = new Vector<Validable>();
for (FlexoModelObject o : getAllRecursivelyEmbeddedObjects()) {
if (o instanceof Validable) {
vector.add((Validable) o);
}
}
return vector;
}
public String getScreenshootName() {
ScreenshotResource screen = getProject().getScreenshotResource(this, false);
if (screen == null) {
return null;
}
return screen.getFileName();
}
@Override
public String toString() {
return getClass().getSimpleName() + "_" + (getName() != null ? getName() : getFlexoID());
}
protected <T> boolean requireChange(T oldValue, T newValue) {
return oldValue == null && newValue != null || oldValue != null && newValue == null || oldValue != null && newValue != null
&& !oldValue.equals(newValue);
}
public String getNextPropertyName() {
String base = FlexoLocalization.localizedForKey("property");
String attempt = base;
int i = 1;
while (getPropertyNamed(attempt) != null) {
attempt = base + "-" + i++;
}
return attempt;
}
private Vector<EditionPatternReference> _editionPatternReferences;
public Vector<EditionPatternReference> getEditionPatternReferences() {
return _editionPatternReferences;
}
public void setEditionPatternReferences(Vector<EditionPatternReference> editionPatternReferences) {
_editionPatternReferences = editionPatternReferences;
}
public boolean addToEditionPatternReferences(EditionPatternReference e) {
return _editionPatternReferences.add(e);
}
public boolean removeFromEditionPatternReferences(EditionPatternReference o) {
return _editionPatternReferences.remove(o);
}
public EditionPatternReference getEditionPatternReference(String editionPatternId, long instanceId) {
if (editionPatternId == null) {
return null;
}
if (_editionPatternReferences == null) {
return null;
}
for (EditionPatternReference r : _editionPatternReferences) {
if (r.getEditionPattern().getName().equals(editionPatternId) && r.getInstanceId() == instanceId) {
return r;
}
}
return null;
}
public EditionPatternReference getEditionPatternReference(EditionPatternInstance epInstance) {
if (_editionPatternReferences == null) {
logger.warning("Unexpected _editionPatternReferences=null !!!");
return null;
}
for (EditionPatternReference r : _editionPatternReferences) {
if (r.getEditionPatternInstance() == epInstance) {
return r;
}
}
return null;
}
// Return first one if many
public EditionPatternReference getEditionPatternReference(String editionPatternId) {
if (editionPatternId == null) {
return null;
}
for (EditionPatternReference r : _editionPatternReferences) {
if (r.getEditionPattern().getName().equals(editionPatternId)) {
return r;
}
}
return null;
}
// Return first one if many
public EditionPatternReference getEditionPatternReference(EditionPattern editionPattern) {
if (editionPattern == null) {
return null;
}
for (EditionPatternReference r : _editionPatternReferences) {
// System.out.println("1: " + r.getEditionPattern().getName() + " 2: " + editionPattern.getName());
if (r.getEditionPattern().getName().equals(editionPattern.getName())) {
return r;
}
}
return null;
}
public void registerEditionPatternReference(EditionPatternInstance editionPatternInstance, PatternRole patternRole) {
EditionPatternReference existingReference = getEditionPatternReference(editionPatternInstance);
if (existingReference == null) {
// logger.info("registerEditionPatternReference for " + editionPatternInstance.debug());
EditionPatternReference newReference = new EditionPatternReference(editionPatternInstance, patternRole);
addToEditionPatternReferences(newReference);
setChanged();
} else {
if (existingReference.getPatternRole() != patternRole) {
logger.warning("Called for register a new EditionPatternReference with an already existing EditionPatternReference with a different PatternRole");
}
}
}
public void unregisterEditionPatternReference(EditionPatternInstance editionPatternInstance, PatternRole patternRole) {
EditionPatternReference referenceToRemove = getEditionPatternReference(editionPatternInstance);
if (referenceToRemove == null) {
logger.warning("Called for unregister EditionPatternReference for unexisting reference to edition pattern instance EP="
+ editionPatternInstance.getPattern().getName() + " id=" + editionPatternInstance.getInstanceId());
for (EditionPatternReference ref : getEditionPatternReferences()) {
logger.warning("* Reference:");
logger.warning(ref.debug());
}
} else {
removeFromEditionPatternReferences(referenceToRemove);
setChanged();
}
}
@Deprecated
public Vector<TabModel> inspectionExtraTabs() {
return null;
}
/*
* private Vector<TabModel> _tabList;
*
* public Vector<TabModel> inspectionExtraTabs() { if (_tabList == null) { _tabList = new Vector<TabModel>(); if
* (getEditionPatternReferences() != null) { for (EditionPatternReference ref : getEditionPatternReferences()) { EditionPatternInspector
* inspector = ref.getEditionPattern().getInspector(); if (inspector != null) { //for (Integer i : inspector.getTabs().keySet()) { //
* _tabList.add(inspector.getTabs().get(i)); //} } } } } return _tabList; }
*/
public String getInspectorTitle() {
// By default, take default inspector name
return null;
}
public String makeReference() {
return FlexoModelObjectReference.getSerializationRepresentationForObject(this, true);
}
/**
* Return true is this object is somewhere involved as a primary representation pattern role in any of its EditionPatternReferences
*
* @return
*/
public boolean providesSupportAsPrimaryRole() {
if (getEditionPatternReferences() != null) {
if (getEditionPatternReferences().size() > 0) {
for (EditionPatternReference r : getEditionPatternReferences()) {
if (r.getPatternRole() == null) {
logger.warning("Found an EditionPatternReference with a null pattern role. Please investigate...");
} else if (r.getPatternRole().getIsPrimaryRole()) {
return true;
}
}
}
}
return false;
}
}