Java tutorial
/* * Copyright (c) 2004-2012 The YAWL Foundation. All rights reserved. * The YAWL Foundation is a collaboration of individuals and * organisations who are committed to improving workflow technology. * * This file is part of YAWL. YAWL is free software: you can * redistribute it and/or modify it under the terms of the GNU Lesser * General Public License as published by the Free Software Foundation. * * YAWL 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 Lesser General * Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with YAWL. If not, see <http://www.gnu.org/licenses/>. */ package org.yawlfoundation.yawl.elements; import org.jdom2.Document; import org.jdom2.Element; import org.jdom2.output.Format; import org.jdom2.output.XMLOutputter; import org.yawlfoundation.yawl.elements.data.YParameter; import org.yawlfoundation.yawl.elements.data.YVariable; import org.yawlfoundation.yawl.elements.data.external.AbstractExternalDBGateway; import org.yawlfoundation.yawl.elements.data.external.ExternalDBGatewayFactory; import org.yawlfoundation.yawl.elements.e2wfoj.E2WFOJNet; import org.yawlfoundation.yawl.elements.state.YIdentifier; import org.yawlfoundation.yawl.elements.state.YMarking; import org.yawlfoundation.yawl.engine.YPersistenceManager; import org.yawlfoundation.yawl.exceptions.YDataStateException; import org.yawlfoundation.yawl.exceptions.YPersistenceException; import org.yawlfoundation.yawl.util.*; import java.util.*; /** * * The implementation of a net in the YAWL semantics - A container for tasks and conditions. * @author Lachlan Aldred * */ public final class YNet extends YDecomposition { private YInputCondition _inputCondition; private YOutputCondition _outputCondition; private Map<String, YExternalNetElement> _netElements = new HashMap<String, YExternalNetElement>(); private Map<String, YVariable> _localVariables = new HashMap<String, YVariable>(); private String _externalDataGateway; private YNet _clone; public YNet(String id, YSpecification specification) { super(id, specification); } public void setInputCondition(YInputCondition inputCondition) { _inputCondition = inputCondition; _netElements.put(inputCondition.getID(), inputCondition); } public void setOutputCondition(YOutputCondition outputCondition) { _outputCondition = outputCondition; _netElements.put(outputCondition.getID(), outputCondition); } /** This method removes a net element together with preSet, postSet, reset, * cancelledBy sets. */ public boolean removeNetElement(YExternalNetElement netElement) { for (YExternalNetElement preset : netElement.getPresetElements()) { YFlow flow = new YFlow(preset, netElement); preset.removePostsetFlow(flow); } for (YExternalNetElement postset : netElement.getPostsetElements()) { YFlow flow = new YFlow(netElement, postset); postset.removePresetFlow(flow); } // need to remove from removeSet and cancelledBySet as well if (netElement instanceof YTask) { YTask task = (YTask) netElement; Set<YExternalNetElement> removeSet = task.getRemoveSet(); if (removeSet != null) { for (YExternalNetElement element : removeSet) { element.removeFromCancelledBySet(task); } } } // check if a place or condition is part of any cancellation sets Set<YExternalNetElement> cancelledBy = netElement.getCancelledBySet(); if (cancelledBy != null) { for (YExternalNetElement element : cancelledBy) { ((YTask) element).removeFromRemoveSet(netElement); } } return _netElements.remove(netElement.getID()) != null; } public void addNetElement(YExternalNetElement netElement) { _netElements.put(netElement.getID(), netElement); } // only have to update the map when an element's id changes public void refreshNetElementIdentifier(String oldIdentifier) { YExternalNetElement element = _netElements.remove(oldIdentifier); if (element != null) { _netElements.put(element.getID(), element); } } public Map<String, YExternalNetElement> getNetElements() { return new HashMap<String, YExternalNetElement>(_netElements); } public List<YTask> getNetTasks() { List<YTask> result = new ArrayList<YTask>(); for (YNetElement element : _netElements.values()) { if (element instanceof YTask) result.add((YTask) element); } return result; } /** * Method getInputCondition. * @return YConditionInterface */ public YInputCondition getInputCondition() { return this._inputCondition; } /** * Method getOutputCondition. * @return YCondition */ public YOutputCondition getOutputCondition() { return _outputCondition; } /** * * @param id * @return YExternalNetElement */ public YExternalNetElement getNetElement(String id) { return _netElements.get(id); } public void setExternalDataGateway(String gateway) { _externalDataGateway = gateway; } public String getExternalDataGateway() { return _externalDataGateway; } /** * Used to verify that the net conforms to syntax of YAWL. */ public void verify(YVerificationHandler handler) { super.verify(handler); if (_inputCondition == null) { handler.error(this, this + " must contain input condition."); } if (_outputCondition == null) { handler.error(this, this + " must contain output condition."); } for (YExternalNetElement element : _netElements.values()) { if (element instanceof YInputCondition && !element.equals(_inputCondition)) { handler.error(this, "Only one Input Condition allowed per net."); } if (element instanceof YOutputCondition && !element.equals(_outputCondition)) { handler.error(this, "Only one Output Condition allowed per net."); } element.verify(handler); } for (YVariable var : _localVariables.values()) { var.verify(handler); } //check that all elements in the net are on a directed path from 'i' to 'o'. verifyDirectedPath(handler); new YNetLocalVarVerifier(this).verify(handler); } private void verifyDirectedPath(YVerificationHandler handler) { /* Function isValid(YConditionInterface i, YConditionInterface o, Tasks T, Conditions C): Boolean BEGIN: #Initalize variables: visitedFw := {i}; visitingFw := postset(visitedFw); visitedBk := {o}; visitingBk := preset(visitedBk); */ Set<YExternalNetElement> visitedFw = new HashSet<YExternalNetElement>(); Set<YExternalNetElement> visitingFw = new HashSet<YExternalNetElement>(); visitingFw.add(_inputCondition); Set<YExternalNetElement> visitedBk = new HashSet<YExternalNetElement>(); Set<YExternalNetElement> visitingBk = new HashSet<YExternalNetElement>(); visitingBk.add(_outputCondition); /* Begin Loop: visitedFw := visitedFw Union visitingFw; visitingFw := postset(visitingFw) - visitedFw; Until: visitingFw = {} */ do { visitedFw.addAll(visitingFw); visitingFw = getPostset(visitingFw); visitingFw.removeAll(visitedFw); } while (visitingFw.size() > 0); /* Begin Loop: visitedBk := visitedBk Union visitingBk; visitingBk := preset(visitingBk) - visitedBk; Until: visitingBk = {} */ do { visitedBk.addAll(visitingBk); visitingBk = getPreset(visitingBk); visitingBk.removeAll(visitedBk); } while (visitingBk.size() > 0); /* return visitedFw = T U C ^ visitedBk = T U C; // returns true iff all t in T are on a directed path from i to o END */ int numElements = _netElements.size(); Set<YExternalNetElement> allElements = new HashSet<YExternalNetElement>(_netElements.values()); allElements.add(_inputCondition); allElements.add(_outputCondition); Set<YExternalNetElement> elementsNotInPath; if (visitedFw.size() != numElements) { elementsNotInPath = new HashSet<YExternalNetElement>(allElements); elementsNotInPath.removeAll(visitedFw); for (YExternalNetElement element : elementsNotInPath) { handler.error(this, element + " is not on a forward directed path from i to o."); } } if (visitedBk.size() != numElements) { elementsNotInPath = new HashSet<YExternalNetElement>(allElements); elementsNotInPath.removeAll(visitedBk); for (YExternalNetElement element : elementsNotInPath) { handler.error(this, element + " is not on a backward directed path from i to o."); } } } public static Set<YExternalNetElement> getPostset(Set<YExternalNetElement> elements) { Set<YExternalNetElement> postset = new HashSet<YExternalNetElement>(); for (YExternalNetElement element : elements) { if (!(element instanceof YOutputCondition)) { postset.addAll(element.getPostsetElements()); } } return postset; } public static Set<YExternalNetElement> getPreset(Set<YExternalNetElement> elements) { Set<YExternalNetElement> preset = new HashSet<YExternalNetElement>(); for (YExternalNetElement element : elements) { if (element != null && !(element instanceof YInputCondition)) { preset.addAll(element.getPresetElements()); } } return preset; } public Object clone() { try { _clone = (YNet) super.clone(); _clone._netElements = new HashMap<String, YExternalNetElement>(); Set<YExternalNetElement> visited = new HashSet<YExternalNetElement>(); Set<YExternalNetElement> visiting = new HashSet<YExternalNetElement>(); visiting.add(_inputCondition); do { for (YExternalNetElement element : visiting) { element.clone(); } //ensure traversal of each element only occurs once visited.addAll(visiting); visiting = getPostset(visiting); visiting.removeAll(visited); } while (visiting.size() > 0); _clone._localVariables = new HashMap<String, YVariable>(); for (YVariable variable : _localVariables.values()) { YVariable copyVar = (YVariable) variable.clone(); _clone.setLocalVariable(copyVar); } _clone._externalDataGateway = _externalDataGateway; _clone._data = (Document) this._data.clone(); //do cleanup of class variable _clone before returning. Object temp = _clone; _clone = null; return temp; } catch (CloneNotSupportedException e) { e.printStackTrace(); } return null; } protected YNet getCloneContainer() { return _clone; } public boolean orJoinEnabled(YTask orJoin, YIdentifier caseID) { if (orJoin == null || caseID == null) { throw new RuntimeException( "Irrelevant to check the enabledness of an orjoin if " + "this is called with null params."); } if (orJoin.getJoinType() != YTask._OR) { throw new RuntimeException(orJoin + " is not an OR-Join."); } YMarking actualMarking = new YMarking(caseID); List locations = new Vector(actualMarking.getLocations()); Set preSet = orJoin.getPresetElements(); if (locations.containsAll(preSet)) { return true; } boolean callORjoin = false; for (Iterator locIter = locations.iterator(); locIter.hasNext();) { YNetElement element = (YNetElement) locIter.next(); if (preSet.contains(element)) { callORjoin = true; } } if (callORjoin) { try { E2WFOJNet e2Net = new E2WFOJNet(this, orJoin); e2Net.restrictNet(actualMarking); e2Net.restrictNet(orJoin); return e2Net.orJoinEnabled(actualMarking, orJoin); } catch (Exception e) { throw new RuntimeException("Exception in OR-join call:" + e); } } // don't waste time on an orjoin with no tokens in preset return false; } public void setLocalVariable(YVariable variable) { if (null != variable.getName()) { _localVariables.put(variable.getName(), variable); } else if (null != variable.getElementName()) { _localVariables.put(variable.getElementName(), variable); } } public Map<String, YVariable> getLocalVariables() { return _localVariables; } public YVariable removeLocalVariable(String name) { return _localVariables.remove(name); } public YVariable getLocalOrInputVariable(String name) { return _localVariables.containsKey(name) ? _localVariables.get(name) : getInputParameters().get(name); } public String toXML() { StringBuilder xml = new StringBuilder(); xml.append(super.toXML()); for (YVariable variable : getLocalVarsSorted()) { xml.append(variable.toXML()); } xml.append("<processControlElements>"); xml.append(_inputCondition.toXML()); Set<YExternalNetElement> visitedFw = new HashSet<YExternalNetElement>(); Set<YExternalNetElement> visitingFw = new HashSet<YExternalNetElement>(); visitingFw.add(_inputCondition); do { visitedFw.addAll(visitingFw); visitingFw = getPostset(visitingFw); visitingFw.removeAll(visitedFw); xml.append(produceXMLStringForSet(visitingFw)); } while (visitingFw.size() > 0); Set<YExternalNetElement> remainingElements = new HashSet<YExternalNetElement>(_netElements.values()); remainingElements.removeAll(visitedFw); xml.append(produceXMLStringForSet(remainingElements)); xml.append(_outputCondition.toXML()); xml.append("</processControlElements>"); if (_externalDataGateway != null) { xml.append(StringUtil.wrap(_externalDataGateway, "externalDataGateway")); } return xml.toString(); } private String produceXMLStringForSet(Set<YExternalNetElement> elements) { List<YExternalNetElement> elementList = new ArrayList<YExternalNetElement>(elements); Collections.sort(elementList, new Comparator<YExternalNetElement>() { public int compare(YExternalNetElement e1, YExternalNetElement e2) { if ((e1 == null) || (e1.getID() == null)) return -1; if ((e2 == null) || (e2.getID() == null)) return 1; return e1.getID().compareTo(e2.getID()); } }); StringBuilder xml = new StringBuilder(); for (YExternalNetElement element : elementList) { if (element instanceof YTask) { xml.append(element.toXML()); } else { YCondition condition = (YCondition) element; if (!(condition instanceof YInputCondition || condition instanceof YOutputCondition || condition.isImplicit())) { xml.append(condition.toXML()); } } } return xml.toString(); } private List<YVariable> getLocalVarsSorted() { List<YVariable> variables = new ArrayList<YVariable>(_localVariables.values()); Collections.sort(variables, new Comparator<YVariable>() { public int compare(YVariable var1, YVariable var2) { return var1.getOrdering() - var2.getOrdering(); } }); return variables; } /** * Initialises the variable/parameter declarations so that the net may execute. */ public void initialise(YPersistenceManager pmgr) throws YPersistenceException { super.initialise(pmgr); for (YVariable variable : _localVariables.values()) { String varElementName = variable.getPreferredName(); if (variable.getInitialValue() != null) { addData(pmgr, new XNode(varElementName, variable.getInitialValue()).toElement()); } else { addData(pmgr, new Element(varElementName)); } } } public void setIncomingData(YPersistenceManager pmgr, Element incomingData) throws YDataStateException, YPersistenceException { for (YParameter parameter : getInputParameters().values()) { Element actualParam = incomingData.getChild(parameter.getName()); if (parameter.isMandatory() && actualParam == null) { throw new IllegalArgumentException("The input data for Net:" + getID() + " is missing mandatory input data for a parameter (" + parameter.getName() + "). " + " Alternatively the data is there but the query in the super net produced data with" + " the wrong name (Check your specification). " + new XMLOutputter(Format.getPrettyFormat()).outputString(incomingData).trim()); } // remove any attributes - not required and cause validation errors if left if ((actualParam != null) && !actualParam.getAttributes().isEmpty()) { JDOMUtil.stripAttributes(actualParam); } } // validate against schema getSpecification().getDataValidator().validate(getInputParameters().values(), incomingData, getID()); for (Element element : incomingData.getChildren()) { if (getInputParameters().containsKey(element.getName())) { addData(pmgr, element.clone()); } else { throw new IllegalArgumentException( "Element " + element + " is not a valid input parameter of " + this); } } } public Set<YTask> getBusyTasks() { return getActiveTasks(null, "busy"); } public Set<YTask> getEnabledTasks(YIdentifier id) { return getActiveTasks(id, "enabled"); } public Set<YTask> getActiveTasks(YIdentifier id, String taskType) { Set<YTask> activeTasks = new HashSet<YTask>(); for (YExternalNetElement element : _netElements.values()) { if (element instanceof YTask) { YTask task = (YTask) element; if ((taskType.equals("enabled") && task.t_enabled(id)) || (taskType.equals("busy") && task.t_isBusy())) { activeTasks.add(task); } } } return activeTasks; } // only called when a case successfully completes public void postCaseDataToExternal(String caseID) { AbstractExternalDBGateway gateway = getInstantiatedExternalDataGateway(); if (gateway != null) { gateway.updateFromCaseData(getSpecification().getSpecificationID(), caseID, new ArrayList<YParameter>(getOutputParameters().values()), _data.getRootElement()); } } // called when a case begins public Element getCaseDataFromExternal(String caseID) { AbstractExternalDBGateway gateway = getInstantiatedExternalDataGateway(); if (gateway != null) { return gateway.populateCaseData(getSpecification().getSpecificationID(), caseID, new ArrayList<YParameter>(getInputParameters().values()), new ArrayList<YVariable>(getLocalVariables().values()), _data.getRootElement()); } else return null; } private AbstractExternalDBGateway getInstantiatedExternalDataGateway() { if (_externalDataGateway != null) { return ExternalDBGatewayFactory.getInstance(_externalDataGateway); } else return null; } public boolean usesSimpleRootData() { return getRootDataElementName().equals("data"); } }