/* * (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.node; import java.util.List; import java.util.Vector; import java.util.logging.Level; import java.util.logging.Logger; import org.openflexo.foundation.DataModification; import org.openflexo.foundation.FlexoObservable; import org.openflexo.foundation.NameChanged; import org.openflexo.foundation.help.ApplicationHelpEntryPoint; import org.openflexo.foundation.utils.FlexoModelObjectReference; import org.openflexo.foundation.utils.FlexoModelObjectReference.ReferenceOwner; import org.openflexo.foundation.validation.DeletionFixProposal; import org.openflexo.foundation.validation.FixProposal; import org.openflexo.foundation.validation.ValidationError; import org.openflexo.foundation.validation.ValidationIssue; import org.openflexo.foundation.validation.ValidationRule; import org.openflexo.foundation.wkf.FlexoProcess; import org.openflexo.foundation.wkf.FlexoProcessNode; import org.openflexo.foundation.wkf.WKFObject; import org.openflexo.foundation.wkf.dm.ObjectVisibilityChanged; import org.openflexo.foundation.wkf.dm.PortMapInserted; import org.openflexo.foundation.wkf.dm.PortMapRegisteryInserted; import org.openflexo.foundation.wkf.dm.PortMapRegisteryOrientationChanged; import org.openflexo.foundation.wkf.dm.PortMapRegisteryRemoved; import org.openflexo.foundation.wkf.dm.PortMapRemoved; import org.openflexo.foundation.wkf.dm.WKFAttributeDataModification; import org.openflexo.foundation.wkf.ws.DefaultServiceInterface; import org.openflexo.foundation.wkf.ws.FlexoPortMap; import org.openflexo.foundation.wkf.ws.PortMapRegistery; import org.openflexo.foundation.wkf.ws.ServiceInterface; /** * Please comment this class * * @author sguerin * */ public abstract class SubProcessNode extends AbstractActivityNode implements ApplicationHelpEntryPoint, ReferenceOwner { protected static final boolean forceWSCallConsistency = false; private static final Logger logger = Logger.getLogger(SubProcessNode.class.getPackage().getName()); private FlexoModelObjectReference<FlexoProcess> _subProcess; // serialized. If null, then the serviceInterface to use is the DefaultServiceInterface private ServiceInterface _serviceInterface; private PortMapRegistery _portMapRegistery; private boolean displaySubProcessImage = false; // ========================================================================== // ============================= Constructor // ================================ // ========================================================================== /** * Default constructor */ public SubProcessNode(FlexoProcess process) { super(process); } @Override public String getName() { return super.getName(); } /** * Dynamic constructor with ServiceInterface... */ public SubProcessNode(FlexoProcess process, ServiceInterface _interface) { this(process); setServiceInterface(_interface); } /** * Overrides delete * * @see org.openflexo.foundation.wkf.node.AbstractActivityNode#delete() */ @Override public void delete() { super.delete(); if (getServiceInterface() != null) { setServiceInterface(null); } if (getSubProcess() != null) { getSubProcess().removeFromSubProcessNodes(this); } if (_portMapRegistery != null) { _portMapRegistery.delete(); } } public boolean isAcceptableAsSubProcess(FlexoProcess aProcess) { if (aProcess == null) { return true; // Null value is allowed in the context of edition } return isAcceptableAsSubProcess(aProcess, aProcess.getParentProcess()); } public boolean isAcceptableAsSubProcess(FlexoProcess aProcess, FlexoProcess parentProcess) { if (parentProcess != null && parentProcess.isImported()) { return false; } if (aProcess.isTopLevelProcess()) { return true; } if (parentProcess == null) { return /* !aProcess.isRootProcess() */true;// Allowed to invoke the root process } else { return parentProcess.isAncestorOf(getProcess()); } } public boolean isAcceptableAsSubProcess(FlexoProcessNode existingProcessNode) { if (existingProcessNode == null) { return true; // Null value is allowed in the context of edition } return isAcceptableAsSubProcess(existingProcessNode, existingProcessNode.getFatherProcessNode()); } public boolean isAcceptableAsSubProcess(FlexoProcessNode aProcess, FlexoProcessNode parentProcess) { if (parentProcess != null && parentProcess.isImported()) { return false; } if (aProcess.isTopLevelProcess()) { return true; } if (parentProcess == null) { return /* !aProcess.isRootProcess() */true;// Allowed to invoke the root process } else { return parentProcess.isAncestorOf(getProcess().getProcessNode()); } } @Override public boolean isAccessible() { boolean b = super.isAccessible(); if (b) { return true; } else { if (getPortMapRegistery() != null) { for (FlexoPortMap portMap : getPortMapRegistery().getPortMaps()) { if (portMap.getIncomingPostConditions().size() > 0) { return true; } } } } return false; } // Used when serializing public FlexoModelObjectReference<FlexoProcess> getSubProcessReference() { return _subProcess; } // Used when deserializing public void setSubProcessReference(FlexoModelObjectReference<FlexoProcess> aSubProcessReference) { if (aSubProcessReference != null) { _subProcess = aSubProcessReference; _subProcess.setOwner(this); } } @Override public void objectDeleted(FlexoModelObjectReference<?> reference) { if (reference == _subProcess) { setSubProcess(null); } else { super.objectDeleted(reference); } } public boolean hasSubProcess() { return getSubProcess() != null; } public boolean hasSubProcessReference() { return _subProcess != null; } public FlexoProcess getSubProcess(boolean forceLoading) { if (_subProcess != null) { return _subProcess.getObject(forceLoading); } else { return null; } } public FlexoProcess getSubProcess() { if (_subProcess != null) { return _subProcess.getObject(); } else { return null; } } public void setSubProcess(FlexoProcess aSubProcess) { setSubProcess(aSubProcess, true); } private boolean isPortMapRegisteryVisible() { boolean defaultValue = !(this instanceof SingleInstanceSubProcessNode && this instanceof LoopSubProcessNode); if (getPortMapRegistery() != null) { defaultValue = !getPortMapRegistery().getIsHidden(); } return _booleanGraphicalPropertyForKey("isPortMapRegisteryVisible", defaultValue); } private void setIsPortMapRegisteryVisible(boolean b) { _setGraphicalPropertyForKey(b, "isPortMapRegisteryVisible"); } public void setSubProcess(FlexoProcess aSubProcess, boolean notify) { if (getSubProcess() != aSubProcess) { if (aSubProcess != null && !isAcceptableAsSubProcess(aSubProcess) && !isDeserializing()) { logger.warning("Sorry, this process is not acceptable as sub-process for this SubProcessNode"); return; } FlexoProcess oldSubProcess = _subProcess != null ? _subProcess.getObject(false) : null; if (logger.isLoggable(Level.FINE)) { logger.fine("setSubProcess() with " + aSubProcess + " for " + this); } if (aSubProcess != null) { _subProcess = new FlexoModelObjectReference<FlexoProcess>(aSubProcess, this); } else { _subProcess = null; } if (_subProcess != null && !isCreatedByCloning()) { if (getProcess() != null && getProcess() != aSubProcess) { getProcess().getFlexoResource().addToDependentResources(aSubProcess.getFlexoResource()); } aSubProcess.addToSubProcessNodes(this); } if (oldSubProcess != null && oldSubProcess != aSubProcess) { if (logger.isLoggable(Level.FINE)) { logger.fine("delete the old portmap registery"); } if (_portMapRegistery != null) { _portMapRegistery.delete(); setPortMapRegistery(null); } oldSubProcess.removeFromSubProcessNodes(this); } if (aSubProcess != null) { if (!isDeserializing()) { if (logger.isLoggable(Level.FINE)) { logger.fine("update the portmap registery"); } updatePortMapRegistery(); } } if (oldSubProcess != aSubProcess && notify) { notifyAttributeModification("subProcess", oldSubProcess, aSubProcess); } } } @Override public void setProcess(FlexoProcess p) { super.setProcess(p); if (getSubProcess() != null) { if (getProcess() != null && getProcess() != getSubProcess()) { getProcess().getFlexoResource().addToDependentResources(getSubProcess().getFlexoResource()); } getSubProcess().addToSubProcessNodes(this); } } @Override public void refreshStatistics() { super.refreshStatistics(); if (hasSubProcess()) { getSubProcess().getStatistics().refresh(); // Little hack for inspector notifyAttributeModification("subProcess.statistics.activityCount", null, null); notifyAttributeModification("subProcess.statistics.realActivityCount", null, null); notifyAttributeModification("subProcess.statistics.operationCount", null, null); notifyAttributeModification("subProcess.statistics.realOperationCount", null, null); notifyAttributeModification("subProcess.statistics.actionCount", null, null); notifyAttributeModification("subProcess.statistics.realActionCount", null, null); } } /** * call this method to get the chosen ServiceInterface. * * @return */ public ServiceInterface getActiveServiceInterface() { if (getSubProcess() == null) { return null; } if (getServiceInterface() != null) { if (logger.isLoggable(Level.FINE)) { logger.fine("Active service interface:" + getServiceInterface().getName()); } return getServiceInterface(); } if (logger.isLoggable(Level.FINE)) { logger.fine("Active service interface is the default"); } return getSubProcess().getPortRegisteryInterface(); } /** * do no use this method (for serialisation only), call getActiveServiceInterface() */ public ServiceInterface getServiceInterface() { if (logger.isLoggable(Level.FINE)) { logger.fine("getServiceInterface(): " + _serviceInterface); } if (getSubProcess() != null && serviceInterfaceName != null && _serviceInterface == null) { _serviceInterface = getSubProcess().getServiceInterfaceNamed(serviceInterfaceName); if (_serviceInterface != null) { serviceInterfaceName = null; _serviceInterface.addObserver(this); } else if (logger.isLoggable(Level.WARNING)) { logger.warning("Could not find service interface '" + serviceInterfaceName + "' in process " + getSubProcess().getName()); } } return _serviceInterface; } public void setServiceInterface(ServiceInterface anInterface) { if (_serviceInterface != null) { _serviceInterface.deleteObserver(this); } _serviceInterface = anInterface; if (_serviceInterface != null) { _serviceInterface.addObserver(this); } } private String serviceInterfaceName; public String getServiceInterfaceName() { if (getServiceInterface() != null && !(getServiceInterface() instanceof DefaultServiceInterface)) { return getServiceInterface().getName(); } return null; } public void setServiceInterfaceName(String serviceInterfaceName) { this.serviceInterfaceName = serviceInterfaceName; } @Override public abstract String getInspectorName(); @Deprecated public String getSubProcessName() { if (getSubProcess() != null) { return getSubProcess().getName(); } else { return null; } } @Deprecated public void setSubProcessName(String subProcessName) { // Not relevant anymore /* * if (getSubProcess() != null) { _subProcess = null; } _subProcessName = subProcessName; _subProcess = getSubProcess(); */ } public PortMapRegistery getPortMapRegistery() { if (_portMapRegistery == null && getSubProcess() != null && !getSubProcess().isImported()) { updatePortMapRegistery(); } return _portMapRegistery; } public void setPortMapRegistery(PortMapRegistery portMapRegistery) { PortMapRegistery oldPortMapRegistery = _portMapRegistery; _portMapRegistery = portMapRegistery; if (_portMapRegistery != null) { _portMapRegistery.setSubProcessNode(this); } if (oldPortMapRegistery != null) { if (logger.isLoggable(Level.FINE)) { logger.finer("Notify that PortMapRegistery has been removed"); } setChanged(); notifyObservers(new PortMapRegisteryRemoved(oldPortMapRegistery)); oldPortMapRegistery.deleteObserver(this); } if (portMapRegistery != oldPortMapRegistery) { if (portMapRegistery != null) { if (!isDeserializing()) { portMapRegistery.setIsVisible(isPortMapRegisteryVisible()); } portMapRegistery.addObserver(this); setChanged(); notifyObservers(new PortMapRegisteryInserted(portMapRegistery)); } } } // here we must take into account that the portMap CAN map a ServiceInterface private void updatePortMapRegistery() { if (logger.isLoggable(Level.FINE)) { logger.fine("updatePortMapRegistery()"); } if (getSubProcess() != null && !getSubProcess().isImported()) { if (_portMapRegistery == null) { // constuctor of PortMapRegistery must check the serviceInterface setPortMapRegistery(new PortMapRegistery(this)); } // update in portMapRegistery must check the ServiceInterface _portMapRegistery.updateFromServiceInterface(); } else if (_portMapRegistery != null) { _portMapRegistery.delete(); setPortMapRegistery(null); } } public boolean getIsWebService() { return this instanceof WSCallSubProcessNode; // return ((getSubProcess() != null) && (getSubProcess().getIsWebService())); } /** * Return a Vector of all embedded WKFObjects * * @return a Vector of WKFObject instances */ @Override public Vector<WKFObject> getAllEmbeddedWKFObjects() { Vector<WKFObject> returned = super.getAllEmbeddedWKFObjects(); if (_portMapRegistery != null) { returned.add(_portMapRegistery); returned.addAll(_portMapRegistery.getAllEmbeddedWKFObjects()); } return returned; } @Override public void update(FlexoObservable observable, DataModification dataModification) { super.update(observable, dataModification); if (dataModification instanceof PortMapRegisteryOrientationChanged) { forwardNotification(dataModification); } else if (dataModification instanceof PortMapInserted) { forwardNotification(dataModification); } else if (dataModification instanceof PortMapRemoved) { forwardNotification(dataModification); } else if (dataModification instanceof WKFAttributeDataModification && ((WKFAttributeDataModification) dataModification).getAttributeName().equals("isWebService")) { forwardNotification(dataModification); } else if (dataModification instanceof NameChanged) { if (observable == getSubProcess() || observable == getServiceInterface()) { forwardNotification(dataModification); } } else if (dataModification instanceof ObjectVisibilityChanged && observable == getPortMapRegistery()) { setIsPortMapRegisteryVisible(!getPortMapRegistery().getIsHidden()); } } // ========================================================================== // ============================= Validation // ================================= // ========================================================================== public static class SubProcessNodeMustReferToAProcess extends ValidationRule<SubProcessNodeMustReferToAProcess, SubProcessNode> { public SubProcessNodeMustReferToAProcess() { super(SubProcessNode.class, "sub_process_node_must_refer_to_a_process"); } @Override public ValidationIssue<SubProcessNodeMustReferToAProcess, SubProcessNode> applyValidation(SubProcessNode subProcessNode) { if (subProcessNode.getSubProcess() == null) { ValidationError<SubProcessNodeMustReferToAProcess, SubProcessNode> error = new ValidationError<SubProcessNodeMustReferToAProcess, SubProcessNode>( this, subProcessNode, "sub_process_node_($object.name)_is_not_link_to_any_sub_process"); for (FlexoProcess p : subProcessNode.getProject().getAllFlexoProcesses()) { if (subProcessNode.isAcceptableAsSubProcess(p)) { error.addToFixProposals(new SetSubProcessToExistingSubProcess(p)); } } error.addToFixProposals(new DeletionFixProposal<SubProcessNodeMustReferToAProcess, SubProcessNode>( "delete_this_sub_process_node")); return error; } return null; } public class SetSubProcessToExistingSubProcess extends FixProposal<SubProcessNodeMustReferToAProcess, SubProcessNode> { public FlexoProcess subProcess; public SetSubProcessToExistingSubProcess(FlexoProcess aSubProcess) { super("set_($object.name)_sub_process_to_($subProcess.name)"); subProcess = aSubProcess; } @Override protected void fixAction() { getObject().setSubProcess(subProcess); } } } public static class SubProcessReferenceMustBeValid extends ValidationRule<SubProcessReferenceMustBeValid, SubProcessNode> { public SubProcessReferenceMustBeValid() { super(SubProcessNode.class, "sub_process_node_must_refer_to_a_valid_process"); } @Override public ValidationIssue<SubProcessReferenceMustBeValid, SubProcessNode> applyValidation(SubProcessNode subProcessNode) { if (subProcessNode.getSubProcess() != null) { if (!subProcessNode.isAcceptableAsSubProcess(subProcessNode.getSubProcess())) { ValidationError<SubProcessReferenceMustBeValid, SubProcessNode> error = new ValidationError<SubProcessReferenceMustBeValid, SubProcessNode>( this, subProcessNode, "sub_process_node_($object.name)_is_linked_to_an_invalid_process"); for (FlexoProcess p : subProcessNode.getProject().getWorkflow().getAllFlexoProcesses()) { if (subProcessNode.isAcceptableAsSubProcess(p)) { error.addToFixProposals(new SetSubProcessToExistingSubProcess(p)); } } error.addToFixProposals(new SetSubProcessToNull()); error.addToFixProposals(new DeletionFixProposal<SubProcessReferenceMustBeValid, SubProcessNode>( "delete_this_sub_process_node")); return error; } } return null; } public class SetSubProcessToExistingSubProcess extends FixProposal<SubProcessReferenceMustBeValid, SubProcessNode> { public FlexoProcess subProcess; public SetSubProcessToExistingSubProcess(FlexoProcess aSubProcess) { super("set_($object.name)_sub_process_to_($subProcess.name)"); subProcess = aSubProcess; } @Override protected void fixAction() { getObject().setSubProcess(subProcess); } } public class SetSubProcessToNull extends FixProposal<SubProcessReferenceMustBeValid, SubProcessNode> { public SetSubProcessToNull() { super("reset_($object.name)_sub_process_reference"); } @Override protected void fixAction() { getObject().setSubProcess(null); } } } /** * Overrides getClassNameKey * * @see org.openflexo.foundation.FlexoModelObject#getClassNameKey() */ @Override public String getClassNameKey() { return "sub_process_node"; } public boolean isLoop() { return this instanceof MultipleInstanceSubProcessNode && ((MultipleInstanceSubProcessNode) this).getIsSequential(); } public boolean isFork() { return this instanceof MultipleInstanceSubProcessNode && !((MultipleInstanceSubProcessNode) this).getIsSequential(); } public boolean isSingle() { return this instanceof SingleInstanceSubProcessNode; } public boolean isLoopSingle() { return this instanceof LoopSubProcessNode; } public boolean isWSCall() { return this instanceof WSCallSubProcessNode; } @Override public ApplicationHelpEntryPoint getParentHelpEntry() { return getProcess(); } @Override public List<ApplicationHelpEntryPoint> getChildsHelpObjects() { Vector<ApplicationHelpEntryPoint> reply = new Vector<ApplicationHelpEntryPoint>(); reply.addAll(getAllOperationNodes()); return reply; } @Override public String getShortHelpLabel() { return getName(); } @Override public String getTypedHelpLabel() { return "SubProcess : " + getName(); } public void setDisplaySubProcessImage(boolean displaySubProcessImage) { if (this.displaySubProcessImage == displaySubProcessImage) { return; } this.displaySubProcessImage = displaySubProcessImage; notifyAttributeModification("displaySubProcessImage", !displaySubProcessImage, displaySubProcessImage); } public boolean getDisplaySubProcessImage() { return displaySubProcessImage; } }