/*
* (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.io.File;
import java.util.Enumeration;
import java.util.Vector;
import java.util.logging.Logger;
import org.openflexo.foundation.AttributeDataModification;
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.Validable;
import org.openflexo.foundation.wkf.dm.ChildrenOrderChanged;
import org.openflexo.foundation.wkf.dm.ProcessNodeInserted;
import org.openflexo.foundation.wkf.dm.ProcessNodeRemoved;
import org.openflexo.foundation.xml.FlexoWorkflowBuilder;
import org.openflexo.toolbox.ToolBox;
/**
* A FlexoProcessNode is an high-level representation of a FlexoProcess in the global workflow. Those FlexoProcessNode are embedded.
*
* @author benoit,sylvain
*/
public class FlexoProcessNode extends FlexoFolderContainerNode implements Sortable {
private int index = -1;
@SuppressWarnings("unused")
private static final Logger logger = Logger.getLogger(FlexoProcessNode.class.getPackage().getName());
// ==========================================================================
// ============================= Variables
// ==================================
// ==========================================================================
/**
* Father of this FlexoProcessNode
*/
protected FlexoProcessNode _father;
private FlexoProcess process;
/**
* Childs of this FlexoProcessNode as Vector of FlexoProcessNode
*/
public Vector<FlexoProcessNode> _childs;
protected String name = null;
private String processResourceName;
public FlexoProcessNode(FlexoWorkflowBuilder builder) {
this(builder.workflow);
initializeDeserialization(builder);
}
public FlexoProcessNode(FlexoWorkflow workflow) {
super(workflow.getProject(), workflow);
_childs = new Vector<FlexoProcessNode>();
}
public FlexoProcessNode(String aName, FlexoProcess aProcess, FlexoWorkflow workflow) {
this(workflow);
name = aName;
if (aProcess != null) {
setProcess(aProcess);
}
}
@Override
public String getFullyQualifiedName() {
return getProcess().getFullyQualifiedName() + ".PROCESS_NODE";
}
public String getProcessResourceName() {
if (getProcess() != null) {
return getProcess().getFlexoResource().getName();
}
if (processResourceName == null) {
return getName();// Kept for backward compatibility
}
return processResourceName;
}
public void setProcessResourceName(String processResourceName) {
this.processResourceName = processResourceName;
}
@Override
public boolean isImported() {
if (getFatherProcessNode() != null) {
return getFatherProcessNode().isImported();
} else {
return getWorkflow().getImportedRootNodeProcesses().contains(this);
}
}
@Override
public FlexoProcessNode getProcessNode() {
return this;
}
public FlexoProcessNode getFatherProcessNode() {
return _father;
}
public void setFatherProcessNode(FlexoProcessNode fatherProcessNode) {
if (_father == fatherProcessNode) {
return;
}
boolean isImported = isImported();
// 1. remove from current father
if (_father != null) {
_father.removeFromSubProcesses(this);
} else {
if (isImported) {
getWorkflow().removeFromImportedRootNodeProcesses(this);
} else {
getWorkflow().removeFromTopLevelNodeProcesses(this);
}
}
// 2. We set the father
_father = fatherProcessNode;
}
public Vector<FlexoProcessNode> getSubProcesses() {
return _childs;
}
public void setSubProcesses(Vector<FlexoProcessNode> aVector) {
_childs = aVector;
}
public void addToSubProcesses(FlexoProcessNode aProcessNode) {
if (!_childs.contains(aProcessNode)) {
_childs.add(aProcessNode);
clearOrphanProcesses();
aProcessNode.setFatherProcessNode(this);
if (getProcess() != null) {
getProcess().rebuildSubProcesses();
}
if (!isDeserializing()) {
setChanged();
notifyObservers(new ProcessNodeInserted(aProcessNode));
aProcessNode.setIndexValue(aProcessNode.getCollection().length);
FlexoIndexManager.reIndexObjectOfArray(aProcessNode.getCollection());
}
}
}
// Ugly little hack to prevent a node from being removed from is folder because it is currently moving;
private boolean isMoving = false;
public void startMoving() {
isMoving = true;
}
public void stopMoving() {
isMoving = false;
}
public void removeFromSubProcesses(FlexoProcessNode aProcessNode) {
if (_childs.contains(aProcessNode)) {
ProcessFolder folder = aProcessNode.getParentFolder();
if (folder != null && !aProcessNode.isMoving) {
folder.removeFromProcesses(aProcessNode);
}
_childs.remove(aProcessNode);
clearOrphanProcesses();
aProcessNode.setFatherProcessNode(null);
if (getProcess() != null) {
getProcess().rebuildSubProcesses();
}
FlexoProcessNode[] coll;
if (folder != null) {
coll = folder.getProcesses().toArray(new FlexoProcessNode[0]);
} else {
coll = getOrphanProcesses().toArray(new FlexoProcessNode[0]);
}
FlexoIndexManager.reIndexObjectOfArray(coll);
setChanged();
notifyObservers(new ProcessNodeRemoved(aProcessNode));
}
}
private Vector<FlexoProcessNode> orphanProcesses;
public Vector<FlexoProcessNode> getOrphanProcesses() {
if (orphanProcesses == null) {
orphanProcesses = new Vector<FlexoProcessNode>();
for (Enumeration<FlexoProcessNode> en = getSortedSubprocesses(); en.hasMoreElements();) {
FlexoProcessNode node = en.nextElement();
if (node.getParentFolder() == null) {
node.setIndex(orphanProcesses.size() + 1);
orphanProcesses.add(node);
}
}
}
return orphanProcesses;
}
public void clearOrphanProcesses() {
orphanProcesses = null;
getPropertyChangeSupport().firePropertyChange("sortedOrphanSubprocesses", this.orphanProcesses, null);
}
@Override
public void delete() {
boolean isImported = isImported();
if (getParentFolder() != null) {
getParentFolder().removeFromProcesses(this);
}
if (getFatherProcessNode() != null) {
getFatherProcessNode().removeFromSubProcesses(this);
_father = null;
setProcess(null);
} else if (getWorkflow() != null) {
if (isImported) {
getWorkflow().removeFromImportedRootNodeProcesses(this);
} else {
getWorkflow().removeFromTopLevelNodeProcesses(this);
}
}
super.delete();
}
// Not serialized
private Vector<ProcessFolder> parentFolders = new Vector<ProcessFolder>();
public Vector<ProcessFolder> getParentFolders() {
return parentFolders;
}
public void addParentFolder(ProcessFolder parent) {
if (!parentFolders.contains(parent)) {
parentFolders.add(parent);
if (getFatherProcessNode() != null) {
getFatherProcessNode().clearOrphanProcesses();
} else {
getWorkflow().clearOrphanProcesses();
}
}
}
public void removeParentFolder(ProcessFolder parent) {
if (parentFolders.contains(parent)) {
parentFolders.remove(parent);
if (getFatherProcessNode() != null) {
getFatherProcessNode().clearOrphanProcesses();
getFatherProcessNode().setChanged();
getFatherProcessNode().notifyObservers(new ChildrenOrderChanged());
} else {
getWorkflow().clearOrphanProcesses();
getWorkflow().setChanged();
getWorkflow().notifyObservers(new ChildrenOrderChanged());
}
}
}
public ProcessFolder getParentFolder() {
if (parentFolders.size() > 0) {
return parentFolders.firstElement();
} else {
return null;
}
}
@Override
public String getName() {
if (getProcess() != null) {
name = getProcess().getName();
}
return name;
}
/**
* @deprecated - redundant information, only FlexoProcess.getName() and FlexoProcess.setName() are relevant now.
*/
@Override
@Deprecated
public void setName(String aName) {
name = aName;
setChanged();
}
public File getFile() {
if (getProcess() != null && getProcess().getFlexoResource() != null) {
return getProcess().getFlexoResource().getFile();
}
return null;
}
/**
* Overrides getClassNameKey
*
* @see org.openflexo.foundation.FlexoModelObject#getClassNameKey()
*/
@Override
public String getClassNameKey() {
return "flexo_process_node";
}
@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 index;
}
@Override
public void setIndexValue(int index) {
if (this.index == index) {
return;
}
int old = this.index;
this.index = index;
setChanged();
notifyAttributeModification("index", old, index);
if (!isDeserializing() && !isCreatedByCloning()) {
if (getFatherProcessNode() == null) {
if (getWorkflow() != null) {
getWorkflow().setChanged();
getWorkflow().notifyObservers(new ChildrenOrderChanged());
}
} else {
getFatherProcessNode().setChanged();
getFatherProcessNode().notifyObservers(new ChildrenOrderChanged());
}
}
}
/**
* Overrides getCollection
*
* @see org.openflexo.foundation.utils.Sortable#getCollection()
*/
@Override
public FlexoProcessNode[] getCollection() {
if (getFatherProcessNode() != null) {
return getFatherProcessNode().getSubProcesses().toArray(new FlexoProcessNode[0]);
}
return getWorkflow()._getTopLevelNodeProcesses().toArray(new FlexoProcessNode[0]);
}
public FlexoProcess getProcess() {
return getProcess(false);
}
public FlexoProcess getProcess(boolean force) {
if (process != null || !force) {
return process;
} else {
if (getWorkflow().isCache()) {
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 process = referredProject.getWorkflow().getLocalFlexoProcessNodeWithFlexoID(getFlexoID()).getProcess();
}
}
}
}
}
return null;
}
public void setProcess(FlexoProcess process) {
if (this.process == null) {
this.process = process;
if (process != null) {
process.setProcessNode(this);
}
}
}
@Override
public Vector<Validable> getAllEmbeddedValidableObjects() {
Vector<Validable> v = new Vector<Validable>();
v.add(this);
return v;
}
public Enumeration<FlexoProcessNode> getSortedSubprocesses() {
disableObserving();
FlexoProcessNode[] o = FlexoIndexManager.sortArray(getSubProcesses().toArray(new FlexoProcessNode[0]));
enableObserving();
return ToolBox.getEnumeration(o);
}
public Enumeration<FlexoProcessNode> getSortedOrphanSubprocesses() {
disableObserving();
FlexoProcessNode[] o = FlexoIndexManager.sortArray(getOrphanProcesses().toArray(new FlexoProcessNode[0]));
enableObserving();
return ToolBox.getEnumeration(o);
}
public int getVectorIndex() {
if (getParentFolder() != null) {
return getParentFolder().getProcesses().indexOf(this) + 1;
} else {
if (getFatherProcessNode() != null) {
return getFatherProcessNode().getOrphanProcesses().indexOf(this) + 1;
} else if (isImported()) {
return getWorkflow().getImportedRootNodeProcesses().indexOf(this) + 1;
} else {
return getWorkflow().getTopLevelFlexoProcesses().indexOf(this) + 1;
}
}
}
public void setVectorIndex(int index) {
index--;
if (getParentFolder() != null) {
getParentFolder().setIndexForProcess(index, this);
setChanged();
notifyAttributeModification("vectorIndex", -1, index + 1);
}/* else {
if (getFatherProcessNode()!=null)
return getFatherProcessNode().getOrphanProcesses().indexOf(this);
else if (isImported())
return getWorkflow().getImportedRootNodeProcesses().indexOf(this);
else
return getWorkflow().getTopLevelFlexoProcesses().indexOf(this);
}*/
}
public boolean isAncestorOf(FlexoProcessNode processNode) {
FlexoProcessNode current = processNode;
while (current != null) {
if (current == this) {
return true;
}
current = current.getFatherProcessNode();
}
return false;
}
public boolean isTopLevelProcess() {
return getFatherProcessNode() == null;
}
}