/*
* (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;
import java.awt.Color;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.Vector;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.openflexo.foundation.AttributeDataModification;
import org.openflexo.foundation.DataFlexoObserver;
import org.openflexo.foundation.DataModification;
import org.openflexo.foundation.DeletableObject;
import org.openflexo.foundation.FlexoImportableObject;
import org.openflexo.foundation.FlexoObservable;
import org.openflexo.foundation.Inspectors;
import org.openflexo.foundation.action.FlexoActionizer;
import org.openflexo.foundation.rm.FlexoProject;
import org.openflexo.foundation.rm.FlexoProjectReference;
import org.openflexo.foundation.rm.ProjectData;
import org.openflexo.foundation.utils.FlexoIndexManager;
import org.openflexo.foundation.utils.Sortable;
import org.openflexo.foundation.validation.FixProposal;
import org.openflexo.foundation.validation.Validable;
import org.openflexo.foundation.validation.ValidationIssue;
import org.openflexo.foundation.validation.ValidationRule;
import org.openflexo.foundation.validation.ValidationWarning;
import org.openflexo.foundation.wkf.action.AddRoleSpecialization;
import org.openflexo.foundation.wkf.dm.ChildrenOrderChanged;
import org.openflexo.foundation.wkf.dm.RoleColorChange;
import org.openflexo.foundation.wkf.dm.RoleNameChange;
import org.openflexo.foundation.wkf.dm.RoleRemoved;
import org.openflexo.foundation.wkf.node.AbstractActivityNode;
import org.openflexo.foundation.wkf.node.AbstractNode;
import org.openflexo.foundation.wkf.node.EventNode;
import org.openflexo.foundation.wkf.node.OperatorNode;
import org.openflexo.foundation.xml.FlexoWorkflowBuilder;
import org.openflexo.inspector.InspectableObject;
import org.openflexo.localization.FlexoLocalization;
import org.openflexo.ws.client.PPMWebService.PPMObject;
import org.openflexo.ws.client.PPMWebService.PPMRole;
/**
* Represents a role in the workflow
*
* @author sguerin
*
*/
public final class Role extends WorkflowModelObject implements FlexoImportableObject, DataFlexoObserver, Serializable, DeletableObject,
InspectableObject, Sortable {
private static final Logger logger = Logger.getLogger(Role.class.getPackage().getName());
private String roleName;
private int index = -1;
private boolean isSystemRole = false;
private boolean isAssignable = true;
private Vector<RoleSpecialization> _roleSpecializations;
public static FlexoActionizer<AddRoleSpecialization, Role, WorkflowModelObject> addParentRoleActionizer;
/**
* Constructor used during deserialization
*/
public Role(FlexoWorkflowBuilder builder) {
this(builder.getProject(), builder.workflow);
initializeDeserialization(builder);
}
/**
* Default constructor
*/
public Role(FlexoProject project, FlexoWorkflow workflow) {
super(project, workflow);
_roleSpecializations = new Vector<RoleSpecialization>();
}
public Role(FlexoWorkflow workflow, String aRoleName) {
this(workflow.getProject(), workflow);
roleName = aRoleName;
}
@Override
public String getFullyQualifiedName() {
return getProject().getFullyQualifiedName() + ".ROLE." + getName();
}
@Override
public boolean isDescriptionImportant() {
return true;
}
/**
* Default inspector name
*/
@Override
public String getInspectorName() {
if (isImported()) {
return Inspectors.WKF.IMPORTED_ROLE;
}
return Inspectors.WKF.ROLE_INSPECTOR;
}
@Override
public String getName() {
return roleName;
}
@Override
public void setName(String aName) throws DuplicateRoleException {
if (roleName == null && aName != null || roleName != null && aName == null || roleName != null && aName != null
&& !roleName.equals(aName)) {
if (getRoleList() != null && getRoleList().roleWithName(aName) != null) {
if (isDeserializing()) {
setName(aName + "-1");
}
throw new DuplicateRoleException(aName);
}
roleName = aName;
setChanged();
notifyObservers(new RoleNameChange());
}
}
public String getNameForInspector() {
if (isImported()) {
return FlexoLocalization.localizedForKey("[external]") + " " + (getName() != null ? getName() : "");
}
return getName();
}
/*
* public String getDescription() { return roleDescription; }
*
* public void setDescription(String aDescription) { roleDescription = aDescription; }
*/
public Color getColor() {
return getBgColor(DEFAULT);
}
@Override
public Color getBgColor(String context) {
return super.getBgColor(context, null);
}
public void setColor(Color aColor) {
setBgColor(aColor, DEFAULT);
setChanged();
notifyObservers(new RoleColorChange());
}
/**
* Bridging Color<->FlexoColor: used by dynamic invokation in inspector
*/
public Color getAwtColor() {
return getColor();
}
/**
* Bridging Color<->FlexoColor: used by dynamic invokation in inspector
*/
public void setAwtColor(Color aColor) {
setColor(aColor);
}
/**
* Return a Vector of all embedded WKFObjects
*
* @return a Vector of WKFObject instances
*/
@Override
public Vector<Validable> getAllEmbeddedValidableObjects() {
Vector<Validable> returned = new Vector<Validable>();
returned.add(this);
return returned;
}
public Vector<AbstractNode> getNodesUsingRole() {
Vector<AbstractNode> returned = new Vector<AbstractNode>();
for (FlexoProcess p : getProject().getAllLocalFlexoProcesses()) {
returned.addAll(getNodesUsingRole(p));
}
return returned;
}
public Vector<AbstractNode> getNodesUsingRole(FlexoProcess process) {
Vector<AbstractNode> v = new Vector<AbstractNode>();
for (AbstractNode a : process.getActivityPetriGraph().getAllEmbeddedAbstractNodes()) {
if (a instanceof AbstractActivityNode) {
if (((AbstractActivityNode) a).getRole() == this) {
v.add(a);
}
} else if (a instanceof EventNode) {
if (((EventNode) a).getRole() == this) {
v.add(a);
}
} else if (a instanceof OperatorNode) {
if (((OperatorNode) a).getRole() == this) {
v.add(a);
}
}
}
return v;
}
public boolean isUsedInProcess(FlexoProcess process) {
return isRoleUsedInProcess(process, isDefaultRole() ? null : this);
}
public boolean isUsedInPetriGraphNodes(Vector<? extends AbstractNode> nodes) {
return isRoleUsedInPetriGraphNodes(nodes, isDefaultRole() ? null : this);
}
/**
* Checks wheter the role <code>role</code> is used in the process <code>process</code> by any node defined in that process.
*
* @param process
* the process in which to look
* @param role
* the role against which to check (may be null)
* @return true if any node of the process <code>process</code> uses the role <code>role</code>, false otherwise.
*/
public static boolean isRoleUsedInProcess(FlexoProcess process, Role role) {
if (process.getActivityPetriGraph() == null) {
return false;
}
return isRoleUsedInPetriGraphNodes(process.getActivityPetriGraph().getAllEmbeddedAbstractNodes(), role);
}
public static boolean isRoleUsedInPetriGraphNodes(Vector<? extends AbstractNode> nodes, Role role) {
for (AbstractNode a : nodes) {
if (a instanceof AbstractActivityNode) {
if (((AbstractActivityNode) a).getRole() == role) {
return true;
}
} else if (a instanceof EventNode) {
if (((EventNode) a).getRole() == role) {
return true;
}
} else if (a instanceof OperatorNode) {
if (((OperatorNode) a).getRole() == role) {
return true;
}
}
}
return false;
}
private boolean isImported = false;
@Override
public boolean isImported() {
if (getRoleList() != null) {
return getRoleList().isImportedRoleList();
}
if (logger.isLoggable(Level.FINE)) {
logger.fine("No role list defined on " + this);
}
return isImported;
}
@Override
public Role getUncachedObject() {
return getRole(true);
}
public Role getRole() {
return getRole(false);
}
private Role role;
public Role getRole(boolean force) {
if (role != null) {
return role;
}
if (!getWorkflow().isCache()) {
return role = this;
}
if (force) {
ProjectData projectData = getProject().getProjectData();
if (projectData != null) {
FlexoProjectReference ref = projectData.getProjectReferenceWithURI(getWorkflow().getProjectURI());
if (ref != null) {
FlexoProject referredProject = ref.getReferredProject(true);
if (referredProject != null) {
return role = referredProject.getWorkflow().getRoleList().roleWithName(getName());
}
}
}
}
return null;
}
public static Role createImportedRoleFromRole(RoleList roleList, PPMRole role) {
Role fir = new Role(roleList.getWorkflow(), role.getName());
fir.isImported = true;
fir.updateFromObject(role);
return fir;
}
public void updateFromObject(PPMRole object) {
try {
super.updateFromObject(object);
} catch (Exception e) {
e.printStackTrace();
}
if (object.getRgb() != null) {
setColor(new Color(object.getRed(), object.getGreen(), object.getBlue()));
} else {
setColor(null);
}
}
@Override
public Class<? extends PPMObject> getEquivalentPPMClass() {
return PPMRole.class;
}
public PPMRole getEquivalentPPMRole() {
PPMRole role = new PPMRole();
copyObjectAttributesInto(role);
if (getColor() != null) {
role.setRgbColor(getColor().getRed(), getColor().getGreen(), getColor().getBlue());
}
return role;
}
public boolean isEquivalentTo(PPMRole object) {
if (!super.isEquivalentTo(object)) {
return false;
}
if (getColor() != null && object.getRgb() == null || getColor() == null && object.getRgb() != null) {
return false;
}
if (getColor() != null && object.getRgb() != null) {
if (getColor().getRed() != object.getRed()) {
return false;
}
if (getColor().getGreen() != object.getGreen()) {
return false;
}
if (getColor().getBlue() != object.getBlue()) {
return false;
}
}
return true;
}
@Override
public final void delete() {
for (RoleSpecialization rs : (Vector<RoleSpecialization>) getRoleSpecializations().clone()) {
if (logger.isLoggable(Level.FINE)) {
logger.fine("1 Delete " + rs.getFullyQualifiedName());
}
rs.delete();
}
for (RoleSpecialization rs : (Vector<RoleSpecialization>) getInverseRoleSpecializations().clone()) {
if (logger.isLoggable(Level.FINE)) {
logger.fine("2 Delete " + rs.getFullyQualifiedName());
}
rs.delete();
}
if (isImported()) {
isImported = true;// This is important, because it allows to do some tests on this deleted object
}
if (getRoleList() != null && getRoleList().getRoles().contains(this)) {
getRoleList().removeFromRoles(this);
}
super.delete();
setChanged();
notifyObservers(new RoleRemoved(this));
deleteObservers();
}
/**
* Build and return a vector of all the objects that will be deleted during deletion
*
* @param aVector
* of DeletableObject
*/
@Override
public Vector<WorkflowModelObject> getAllEmbeddedDeleted() {
Vector<WorkflowModelObject> returned = new Vector<WorkflowModelObject>();
returned.add(this);
returned.addAll(getRoleSpecializations());
returned.addAll(getInverseRoleSpecializations());
return returned;
}
public Vector<RoleSpecialization> getInverseRoleSpecializations() {
Vector<RoleSpecialization> returned = new Vector<RoleSpecialization>();
for (Role r : getRolesSpecializingMyself()) {
for (RoleSpecialization rs : r.getRoleSpecializations()) {
if (rs.getParentRole() == this) {
returned.add(rs);
}
}
}
return returned;
}
private RoleList roleList;
public RoleList getRoleList() {
return roleList;
}
public void setRoleList(RoleList roleList) {
if (this.roleList != null) {
this.roleList.removeFromRoles(this);
}
this.roleList = roleList;
}
/**
* Overrides getClassNameKey
*
* @see org.openflexo.foundation.FlexoModelObject#getClassNameKey()
*/
@Override
public String getClassNameKey() {
return "role";
}
@Override
public int getIndex() {
if (isBeingCloned()) {
return -1;
}
if (index == -1 && getCollection() != null) {
index = getCollection().length;
FlexoIndexManager.reIndexObjectOfArray(getCollection());
}
return index;
}
@Override
public void setIndex(int index) {
if (isDeserializing() || isCreatedByCloning()) {
setIndexValue(index);
return;
}
FlexoIndexManager.switchIndexForKey(this.index, index, this);
if (getIndex() != index) {
setChanged();
AttributeDataModification dm = new AttributeDataModification("index", null, getIndex());
dm.setReentrant(true);
notifyObservers(dm);
}
}
@Override
public int getIndexValue() {
return getIndex();
}
@Override
public void setIndexValue(int index) {
if (index == this.index) {
return;
}
int old = this.index;
this.index = index;
setChanged();
notifyAttributeModification("index", old, index);
if (!isDeserializing() && !isCreatedByCloning() && getProject() != null) {
getWorkflow().setChanged();
getWorkflow().notifyObservers(new ChildrenOrderChanged());
}
}
/**
* Overrides getCollection
*
* @see org.openflexo.foundation.utils.Sortable#getCollection()
*/
@Override
public Role[] getCollection() {
if (getRoleList() == null) {
return null;
}
return getRoleList().getRoles().toArray(new Role[0]);
}
// ===================================================================
// =========================== FlexoObserver =========================
// ===================================================================
@Override
public void update(FlexoObservable observable, DataModification dataModification) {
// TODO Auto-generated method stub
}
public Vector<RoleSpecialization> getRoleSpecializations() {
return _roleSpecializations;
}
public void setRoleSpecializations(Vector<RoleSpecialization> roles) {
_roleSpecializations = roles;
}
public void addToRoleSpecializations(RoleSpecialization aRoleSpecialization) {
if (isImported()) {
return;
}
if (aRoleSpecialization.getRole() == null) {
aRoleSpecialization.setRole(this);
}
if (!_roleSpecializations.contains(aRoleSpecialization) && aRoleSpecialization.getRole() == this
&& aRoleSpecialization.getParentRole() != this) {
_roleSpecializations.add(aRoleSpecialization);
notifyAttributeModification("roleSpecializations", null, aRoleSpecialization);
aRoleSpecialization.getParentRole().notifyNewRoleSpecializing(this);
}
}
public void removeFromRoleSpecializations(RoleSpecialization aRoleSpecialization) {
if (_roleSpecializations.contains(aRoleSpecialization)) {
_roleSpecializations.remove(aRoleSpecialization);
notifyAttributeModification("roleSpecializations", aRoleSpecialization, null);
}
aRoleSpecialization.getParentRole().notifyRoleNoMoreSpecializing(this);
}
public void notifyNewRoleSpecializing(Role specializingRole) {
setChanged();
notifyObservers(new RoleSpecializing(specializingRole));
}
public void notifyRoleNoMoreSpecializing(Role specializingRole) {
setChanged();
notifyObservers(new RoleNoMoreSpecializing(specializingRole));
}
public boolean isRoleSpecializationDeletable(RoleSpecialization aRole) {
return true;
}
public void performAddRoleSpecialization() {
if (addParentRoleActionizer != null) {
addParentRoleActionizer.run(this, EMPTY_VECTOR);
}
}
public void performDeleteRoleSpecialization(RoleSpecialization aRoleSpecialization) {
removeFromRoleSpecializations(aRoleSpecialization);
}
/**
* Return all roles potentially specializable for current role
*
* @return
*/
public Vector<Role> getAvailableRolesForSpecialization() {
// logger.info("What are the role that "+this+" i may specialize ?");
Vector<Role> returned = new Vector<Role>();
for (Role r : getRoleList().getRoles()) {
// logger.info("Regarding "+r+":");
// logger.info("mightSpecializeRole(r) = "+mightSpecializeRole(r));
// logger.info("!isTransitivelySpecializingRole(r) = "+!isTransitivelySpecializingRole(r));
if (r != this && mightSpecializeRole(r) && !isTransitivelySpecializingRole(r)) {
returned.add(r);
}
}
return returned;
}
/**
* Return a boolean indicating if supplied role might be specialized by this role (avoid cycles)
*
* @param aRole
* @return
*/
public boolean mightSpecializeRole(Role aRole) {
return !aRole.isTransitivelySpecializingRole(this);
}
public boolean isSpecializingRole(Role aRole) {
if (aRole == this) {
return true;
}
for (RoleSpecialization rs : getRoleSpecializations()) {
if (rs.getParentRole() == aRole) {
return true;
}
}
return false;
}
public Vector<Role> getRolesSpecializingRole(Role aRole) {
Vector<Role> v = new Vector<Role>();
if (getRoleList() != null) {
for (Role r : getRoleList().getRoles()) {
if (r.isSpecializingRole(aRole)) {
v.add(r);
}
}
}
return v;
}
public Vector<Role> getRolesSpecializingMyself() {
return getRolesSpecializingRole(this);
}
public boolean isTransitivelySpecializingRole(Role aRole) {
if (isSpecializingRole(aRole)) {
return true;
}
for (RoleSpecialization r : getRoleSpecializations()) {
if (r.getParentRole().isTransitivelySpecializingRole(aRole)) {
return true;
}
}
return false;
}
public boolean getIsSystemRole() {
return isSystemRole;
}
public void setIsSystemRole(boolean aFlag) {
if (aFlag != isSystemRole) {
this.isSystemRole = aFlag;
notifyAttributeModification("isSystemRole", !aFlag, aFlag);
}
}
public boolean getIsAssignable() {
return isAssignable;
}
public void setIsAssignable(boolean aFlag) {
if (aFlag != isAssignable) {
this.isAssignable = aFlag;
notifyAttributeModification("isAssignable", !aFlag, aFlag);
if (getWorkflow() != null && !isDeserializing()) {
getWorkflow().clearAssignableRolesCache();
}
}
}
@Override
public String toString() {
return getFullyQualifiedName();
}
public boolean isDefaultRole() {
return getWorkflow().getRoleList().getDefaultRole() == this;
}
public static class ImportedRoleShouldExistOnServer extends ValidationRule<ImportedRoleShouldExistOnServer, Role> {
public ImportedRoleShouldExistOnServer() {
super(Role.class, "imported_role_should_exist_on_server");
}
@Override
public ValidationIssue<ImportedRoleShouldExistOnServer, Role> applyValidation(Role process) {
if (process.isImported() && process.isDeletedOnServer()) {
return new ValidationWarning<ImportedRoleShouldExistOnServer, Role>(this, process,
"role_($object.name)_no_longer_exists_on_server", new DeleteRole());
}
return null;
}
public static class DeleteRole extends FixProposal<ImportedRoleShouldExistOnServer, Role> {
public DeleteRole() {
super("delete_role_($object.name)");
}
@Override
protected void fixAction() {
getObject().delete();
}
}
}
/**
* used by velocity
*
* @return sorted list of role where an arrow starting from this is going and where the arrival role is also the starting point of at
* least one arrow.
*/
public ArrayList<Role> getChildNonLeaf() {
ArrayList<Role> reply = new ArrayList<Role>();
for (RoleSpecialization rs : getRoleSpecializations()) {
Role r = rs.getParentRole();
if (r.getRoleSpecializations().size() > 0) {
reply.add(r);
}
}
sort(reply);
return reply;
}
/**
* used by velocity
*
* @return sorted list of roles where an arrow starting from this is going and where the arrival role don't have any starting arrow.
*/
public ArrayList<Role> getChildLeaf() {
ArrayList<Role> reply = new ArrayList<Role>();
for (RoleSpecialization rs : getRoleSpecializations()) {
Role r = rs.getParentRole();
if (r.getRoleSpecializations().size() == 0) {
reply.add(r);
}
}
sort(reply);
return reply;
}
public static void sort(ArrayList<Role> list) {
Collections.sort(list, new Comparator() {
@Override
public int compare(Object o1, Object o2) {
return ((Role) o1).getIndex() - ((Role) o2).getIndex();
}
});
}
}