/*
* (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.wkf;
/*
* FlexoObservable.java
* Project WorkflowEditor
*
* Created by benoit on Mar 3, 2004
*/
import java.util.Collection;
import java.util.Enumeration;
import java.util.HashSet;
import java.util.Vector;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.openflexo.foundation.AttributeDataModification;
import org.openflexo.foundation.DataModification;
import org.openflexo.foundation.RepresentableFlexoModelObject;
import org.openflexo.foundation.rm.FlexoProject;
import org.openflexo.foundation.rm.XMLStorageResourceData;
import org.openflexo.foundation.validation.Validable;
import org.openflexo.foundation.validation.ValidationError;
import org.openflexo.foundation.validation.ValidationIssue;
import org.openflexo.foundation.validation.ValidationModel;
import org.openflexo.foundation.validation.ValidationReport;
import org.openflexo.foundation.validation.ValidationRule;
import org.openflexo.foundation.wkf.dm.WKFAttributeDataModification;
import org.openflexo.xmlcode.InvalidObjectSpecificationException;
/**
* A WKFObject instance represents an object (model in MVC paradigm) of a process (or be the process itself). This is the root class for all
* objects of WKF model.<br>
* This class implements:
* <ul>
* <li>a levelled observer/observable scheme</li>
* <li>link to parent process</li>
* <li>a method allowing to retrieve all embedded WKFObject</li>
* <li>methods used to perform validation</li>
* </ul>
*
* @author benoit, sylvain
*/
public abstract class WKFObject extends RepresentableFlexoModelObject implements Validable {
private static final Logger logger = Logger.getLogger(WKFObject.class.getPackage().getName());
private FlexoProcess _process;
// ==========================================================================
// ============================= Constructor
// ================================
// ==========================================================================
/**
* Should only be used by FlexoProcess
*/
public WKFObject(FlexoProject project) {
super(project);
}
/**
* Default constructor
*/
public WKFObject(FlexoProcess process) {
super(process != null ? process.getProject() : null);
setProcess(process);
}
// ==========================================================================
// ============================= ACCESSORS ================================
// ==========================================================================
/**
* Returns reference to the main object in which this XML-serializable object is contained relating to storing scheme: here it's the
* process itself
*
* @return the process this object is relating to
*/
@Override
public XMLStorageResourceData getXMLResourceData() {
return getProcess();
}
public FlexoWorkflow getWorkflow() {
if (getProject() != null) {
return getProject().getFlexoWorkflow();
} else if (getProcess() != null && getProcess() != this) {
return getProcess().getWorkflow();
}
return null;
}
public FlexoProcess getProcess() {
return _process;
}
public void setProcess(FlexoProcess p) {
_process = p;
}
/**
* Return a Vector of all embedded WKFObjects (Note must include itself in this vector)
*
* @return a Vector of WKFObject instances
*/
public abstract Vector<? extends WKFObject> getAllEmbeddedWKFObjects();
public Vector<WKFObject> getAllRecursivelyEmbeddedWKFObjects() {
HashSet<WKFObject> returned = new HashSet<WKFObject>();
processToAdditionOfEmbedded(this, returned);
return new Vector<WKFObject>(returned);
}
private void processToAdditionOfEmbedded(WKFObject object, Collection<WKFObject> queue) {
if (!queue.contains(object)) {
queue.add(object);
}
if (object.getAllEmbeddedWKFObjects() != null) {
Enumeration<? extends WKFObject> en = object.getAllEmbeddedWKFObjects().elements();
WKFObject candidate = null;
while (en.hasMoreElements()) {
candidate = en.nextElement();
if (candidate == null) {
if (logger.isLoggable(Level.WARNING)) {
logger.warning("Object of class " + object.getClass().getName()
+ " returned IEObjects null in its method getEmbeddedIEObjects");
}
continue;
}
if (!queue.contains(candidate)) {
queue.add(candidate);
processToAdditionOfEmbedded(candidate, queue);
}
}
}
}
@Override
public Object objectForKey(String key) {
return super.objectForKey(key);
}
/**
* Overrides the default KV-coding implementation by sending notifications about modified attribute
*
* Overrides
*
* @see org.openflexo.kvc.KeyValueCoding#setObjectForKey(java.lang.Object, java.lang.String)
* @see org.openflexo.kvc.KeyValueCoding#setObjectForKey(java.lang.Object, java.lang.String)
*/
@Override
public void setObjectForKey(Object object, String key) {
Object oldValue = objectForKey(key);
super.setObjectForKey(object, key);
if (oldValue != null && !oldValue.equals(object) || oldValue == null && object != null) {
// logger.info("Obj: "+getClass().getName()+" property: "+key+" was "+oldValue+" is now "+object);
notifyModification(key, oldValue, object);
}
}
@Override
public Class getTypeForKey(String key) {
if (logger.isLoggable(Level.FINE)) {
logger.finer("getTypeForKey for " + key);
}
try {
return super.getTypeForKey(key);
} catch (InvalidObjectSpecificationException e) {
if (logger.isLoggable(Level.FINE)) {
logger.finer("OK, let the inspector to determine type of dynamic atttribute !");
}
return null;
}
}
protected void notifyModification(String key, Object oldValue, Object newValue) {
setChanged();
notifyObservers(new AttributeDataModification(key, oldValue, newValue));
}
@Override
public void delete() {
super.delete();
}
// ==========================================================================
// ========================== Embedding implementation =====================
// ==========================================================================
// TODO: we really need to rewrite all this contains/isContained implementation, since it's really
// not well organized, and has a lot of inconsistency. One of the both method shoud be final, and all cases
// handled in the other one
public boolean isContainedIn(WKFObject obj) {
// logger.warning
// ("Contains not IMPLEMENTED for "+getClass().getName()+" and "+obj.getClass().getName()+": default implementation returns true only if supplied object is the owner process: override in sub-classes !");
return obj == getProcess();
}
public boolean contains(WKFObject obj) {
return obj.isContainedIn(this);
}
/**
* Purpose of this method is to compute a closed set embedding all supplied object using isContainedIn() / contains() methods
*
* @return
*/
public static <T extends WKFObject> Vector<T> buildEnclosingSetOfObjects(Vector<T> someObjects) {
Vector<T> returned = new Vector<T>();
if (someObjects != null) {
for (T obj : someObjects) {
boolean alreadyContained = false;
for (T temp : returned) {
if (obj.isContainedIn(temp)) {
alreadyContained = true;
}
}
if (!alreadyContained) {
// Not already contained, add it
// Before to do it, look if some other are to be removed
Vector<T> removeThose = new Vector<T>();
for (T temp : returned) {
if (temp.isContainedIn(obj)) {
removeThose.add(temp);
}
}
returned.removeAll(removeThose);
returned.add(obj);
}
}
}
return returned;
}
// ==========================================================================
// ========================== Observable implementation =====================
// ==========================================================================
public void notifyAttributeModification(String attributeName, Object oldValue, Object newValue) {
setChanged();
notifyObservers(new WKFAttributeDataModification(attributeName, oldValue, newValue));
}
public void forwardNotification(DataModification dataModification) {
setChanged();
notifyObservers(dataModification);
}
// ==========================================================================
// ========================= Validable interface
// ============================
// ==========================================================================
/**
* Return default validation model for this object
*
* @return
*/
@Override
public ValidationModel getDefaultValidationModel() {
if (getProcess() != null) {
if (getProcess().getProject() != null) {
return getProcess().getProject().getWKFValidationModel();
} else {
if (logger.isLoggable(Level.WARNING)) {
logger.warning("Could not access to project !");
}
}
} else {
if (logger.isLoggable(Level.WARNING)) {
logger.warning("Could not access to process !");
}
}
return null;
}
/**
* Returns a flag indicating if this object is valid according to default validation model
*
* @return boolean
*/
@Override
public boolean isValid() {
return isValid(getDefaultValidationModel());
}
/**
* Returns a flag indicating if this object is valid according to specified validation model
*
* @return boolean
*/
@Override
public boolean isValid(ValidationModel validationModel) {
return validationModel.isValid(this);
}
/**
* Validates this object by building new ValidationReport object Default validation model is used to perform this validation.
*/
@Override
public ValidationReport validate() {
return validate(getDefaultValidationModel());
}
/**
* Validates this object by building new ValidationReport object Supplied validation model is used to perform this validation.
*/
@Override
public ValidationReport validate(ValidationModel validationModel) {
return validationModel.validate(this);
}
/**
* Validates this object by appending eventual issues to supplied ValidationReport. Default validation model is used to perform this
* validation.
*
* @param report
* , a ValidationReport object on which found issues are appened
*/
@Override
public void validate(ValidationReport report) {
validate(report, getDefaultValidationModel());
}
/**
* Validates this object by appending eventual issues to supplied ValidationReport. Supplied validation model is used to perform this
* validation.
*
* @param report
* , a ValidationReport object on which found issues are appened
*/
@Override
public void validate(ValidationReport report, ValidationModel validationModel) {
validationModel.validate(this, report);
}
/**
* Return a vector of all embedded objects on which the validation will be performed
*
* @return a Vector of Validable objects
*/
@Override
public Vector<Validable> getAllEmbeddedValidableObjects() {
return new Vector<Validable>(getAllEmbeddedWKFObjects());
}
/**
* Returns a vector of all objects that will be deleted if you call delete on this object. If no other object are deleted, then the
* method should return EMPTY_VECTOR
*
* @return
*/
public abstract Vector<? extends WKFObject> getAllEmbeddedDeleted();
// ==========================================================================
// ============================= Validation
// =================================
// ==========================================================================
public static class WKFObjectMustReferToAProcess extends ValidationRule {
public WKFObjectMustReferToAProcess() {
super(WKFObject.class, "object_must_refer_to_a_process");
}
@Override
public ValidationIssue applyValidation(final Validable object) {
final WKFObject wkfObject = (WKFObject) object;
if (wkfObject.getProcess() == null) {
return new ValidationError(this, object, "internal_consistency_error_object_does_not_refer_to_a_process");
}
return null;
}
}
}