/* * ParseBPMDom.java * * This file is part of the SRCM module. * Copyright (c) 2012-2013 "University of Trento - DISI" All rights reserved. * * Is strictly forbidden to remove this copyright notice from this source code. * * Disclaimer of Warranty: * SRCM (this software) is provided "as-is" and without warranty of any kind, * express, implied or otherwise, including without limitation, any warranty of * merchantability or fitness for a particular purpose. * In no event shall the copyright holder or contributors be liable for any direct, * indirect, incidental, special, exemplary, or consequential damages * including, but not limited to, procurement of substitute goods or services; * loss of use, data, or profits; or business interruption) however caused and on * any theory of liability, whether in contract, strict liability, or tort (including * negligence or otherwise) arising in any way out of the use of this software, even * if advised of the possibility of such damage. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License version 3 * as published by the Free Software Foundation with the addition of the * following permission added to Section 15 as permitted in Section 7(a): * FOR ANY PART OF THE COVERED WORK IN WHICH THE COPYRIGHT IS OWNED BY * "University of Trento - DISI","University of Trento - DISI" DISCLAIMS THE * WARRANTY OF NON INFRINGEMENT OF THIRD PARTY RIGHTS. * * See the GNU Affero General Public License for more details. * You should have received a copy of the GNU Affero General Public License * along with this program; if not, see http://www.gnu.org/licenses or write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA, 02110-1301 USA. * * For more information, please contact Mattia Salnitri group at this * address: mattia.salnitri@unitn.it * */ package eu.aniketos.srcm.functional; import java.util.HashMap; import java.util.Iterator; import java.util.Vector; import java.util.regex.Matcher; import java.util.regex.Pattern; import javax.imageio.plugins.bmp.BMPImageWriteParam; import org.w3c.dom.Document; import org.w3c.dom.NamedNodeMap; import org.w3c.dom.Node; import eu.aniketos.SecureBPMN.*; import eu.aniketos.SecureBPMN.Activity.Type; import eu.aniketos.SecureBPMN.Participant.participantType; /**This class parses the Business process XML document received as inputs, and instantiates the correspondent data structure * it supposes the name of the variable is unique * @author Mattia Salnitri * */ public class ParseSecureBPMNDom { //this parser supports both activiti 5.8 and 5.9.3 private static boolean ACTIVITY59=true; //list of reference to the object that may be referenced more than one time in the EABPM private static HashMap<String, Participant> participantList;//key is the name => participants name is unique private static HashMap<String, Element> elementList; //key is the id private static Vector<Variable> variableList; //vector, because there are duplications on names and id, temporary duplication //used to store the pairs source-destination used for the control flow //private static HashMap<String, String> bpFlow; private static Vector<String[]> bpFlow; private EABPM eabpm; /**Constructor of the class * @param domBPM the XML document */ public ParseSecureBPMNDom(Document domBPM) { //instantiate hash maps participantList = new HashMap<String, Participant>(); elementList = new HashMap<String, Element>(); variableList = new Vector<Variable>(); //bpFlow = new HashMap<String, String>(); bpFlow = new Vector<String[]>(); eabpm = new EABPM(); eabpm = setupBPM(domBPM, eabpm); Iterator<String[]> Flowit = bpFlow.iterator(); //setup previous for double linked list while(Flowit.hasNext()) { String[] pair = Flowit.next(); Element source = elementList.get(pair[0]); Element target = elementList.get(pair[1]); source.addNext(target); target.addPrevious(source); } //Maintain only an object for every variable Iterator elementIt = elementList.values().iterator(); while(elementIt.hasNext()) { Object tmp = elementIt.next(); if (tmp.getClass()==Activity.class) { Activity activity = (Activity) tmp; //check if the activity has a participant, if not stop the execution. if (activity.getCandidateOwners().size()==0) { System.err.println("ERROR: the activity ID='"+activity.getId()+"' has no candidate performer(s) associated"); eabpm=null; break; } Vector<Variable> inVarList = activity.getInVariablesList(); Vector<Variable> outVarList = activity.getOutVariablesList(); //in var for (int i=0; i<inVarList.size(); i++)//while (varIt.hasNext()) { //if the object is already contained i'm ok, otherwise i have to serach the another variable object with the same name if (!variableList.contains(inVarList.get(i)))//Variable actVar= (Variable) varIt.next(); { //search through all the var to search the one with the same name Iterator varListIt = variableList.iterator(); while (varListIt.hasNext()) { Variable variable =(Variable) varListIt.next(); //i found the var who corresponds to the match i substitute and (i quit) if (variable.getName().compareToIgnoreCase(inVarList.get(i).getName())==0) { inVarList.remove(i); inVarList.add(i, variable); } } } } //out var for (int i=0; i<outVarList.size(); i++)//while (varIt.hasNext()) { if (!variableList.contains(outVarList.get(i)))//Variable actVar= (Variable) varIt.next(); { //search through all the var to search the one with the same name Iterator varListIt = variableList.iterator(); while (varListIt.hasNext()) { Variable variable =(Variable) varListIt.next(); //i found the var who corresponds to the match i substitute and (i quit) if (variable.getName().compareToIgnoreCase(outVarList.get(i).getName())==0) { outVarList.remove(i); outVarList.add(i, variable); } } } } } } if (eabpm!=null) { //setup the participant in the BP Iterator<Participant> partIT = participantList.values().iterator(); while (partIT.hasNext()) eabpm.addParticipant(partIT.next()); //setup the variables eabpm.setVariablesList(variableList); } } @SuppressWarnings("static-access") private static EABPM setupBPM (Node node, Object father) { //maintain compatibility with activiti 5.9.3 and actviti 5.8 String tagAct; if (ACTIVITY59) tagAct=""; else tagAct="bpmn2:"; Object newFather=null; if (node.getNodeType() == node.ELEMENT_NODE) { //node name //System.out.print("<"+node.getNodeName()); if (node.getNodeName().equalsIgnoreCase("definitions")) { //first node //new object (use in the recursive call) newFather=father; } else if (node.getNodeName().equalsIgnoreCase(tagAct+"process")) { //keep going on //new object (use in the recursive call) newFather=father; } else if (node.getNodeName().equalsIgnoreCase("documentation")) { //keep going on //new object (use in the recursive call) newFather=father; } else if (node.getNodeName().equalsIgnoreCase(tagAct+"startevent") || node.getNodeName().equalsIgnoreCase(tagAct+"endevent")) { //body start event //cast father EABPM eabpm = (EABPM) father; //create new item Event event = new Event(); if (node.getNodeName().equalsIgnoreCase(tagAct+"startevent")) event.setType(Event.Type.START); else event.setType(Event.Type.END); //new object (use in the recursive call) newFather=event; NamedNodeMap startAttr = node.getAttributes(); for (int i = 0; i < startAttr.getLength(); i++) { Node attr = startAttr.item(i); //node parameter if (attr.getNodeName().compareToIgnoreCase("id")==0) { event.setId(attr.getNodeValue()); //System.out.print(" "+attr.getNodeName() + "=\""+attr.getNodeValue()+"\""); } } //add element to internal hashmap, for the ordering //add after, couse i need to know the valie of the ID addElement(event); //add event to eabpm eabpm.addElement(event); //end body } else if (node.getNodeName().equalsIgnoreCase(tagAct+"userTask") || node.getNodeName().equalsIgnoreCase(tagAct+"serviceTask") ) { //body User task //cast father EABPM eabpm = (EABPM) father; //create new item Activity activity = new Activity(); //add event to eabpm eabpm.addElement(activity); if (node.getNodeName().equalsIgnoreCase(tagAct+"userTask")) activity.setType(Activity.Type.USER); else activity.setType(Activity.Type.SERVICE); //new object (use in the recursive call) //leaf tag newFather=activity; NamedNodeMap startAttr = node.getAttributes(); for (int i = 0; i < startAttr.getLength(); i++) { Node attr = startAttr.item(i); //node parameter if (attr.getNodeName().compareToIgnoreCase("id")==0) { activity.setId(attr.getNodeValue()); //System.out.print(" "+attr.getNodeName() + "=\""+attr.getNodeValue()+"\""); } else if (attr.getNodeName().compareToIgnoreCase("name")==0) { activity.setName(attr.getNodeValue()); //System.out.print(" "+attr.getNodeName() + "=\""+attr.getNodeValue()+"\""); } else if (attr.getNodeName().compareToIgnoreCase("activiti:assignee")==0) { Participant part = new Participant(); //duplicate name unique part.setName(attr.getNodeValue()); part.setId(attr.getNodeValue()); part = addUniqueParticipant(part); activity.setOwner(part); } if (attr.getNodeName().compareToIgnoreCase("activiti:securityAnnotationType")==0) { if (attr.getNodeValue().compareToIgnoreCase("ack")==0) activity.setType(Type.ACK); } } //add element to internal hash map, for the ordering //add after, because i need to know the value of the ID addElement(activity); //end body } else if (node.getNodeName().equalsIgnoreCase("extensionElements") ) { //do nothing newFather=father; } else if (node.getNodeName().equalsIgnoreCase("activiti:field") ) { if(father.getClass()==Activity.class) { Activity activity = (Activity) father; NamedNodeMap startAttr = node.getAttributes(); for (int i = 0; i < startAttr.getLength(); i++) { Node attr = startAttr.item(i); //node parameter if (attr.getNodeName().compareToIgnoreCase("name")==0) { String attrContent=attr.getNodeValue(); //if (attrContent.compareToIgnoreCase("Service Provider")==0) RequestedServiceProvider if (attrContent.compareToIgnoreCase("RequestedServiceProvider")==0) //if it's an isa relation it can only be connected to an agent, { Participant part = new Participant(); part.setType(participantType.BPAGENT); //activity.setOwner(part);//placeholder for next tag activity.pushCandidateOwner(part);//added this couse at this point an activity may have 2 executors, a role and an agent, in the mapping thena, i chose which one to keep and then i set the owner attribute newFather=part; } else if (attrContent.compareToIgnoreCase("RequestedServiceProviderRole")==0) { Participant part = new Participant(); part.setType(participantType.BPROLE); activity.pushCandidateOwner(part); newFather=part; } else if (attrContent.matches("^input[0-9]*")) { Variable variable = new Variable(); newFather=father; activity.addInVariable(variable); } else//otherwise in an output { Variable variable = new Variable(); newFather=father; activity.addOutVariable(variable); } } } } } //var name else if (node.getNodeName().equalsIgnoreCase("activiti:string") ) { newFather=father; } else if (node.getNodeName().equalsIgnoreCase("activiti:executionListener") ) { //do nothing newFather=father; } //user task input variable else if (node.getNodeName().equalsIgnoreCase("activiti:formProperty") ) { if (father.getClass()==Activity.class) { Variable variable = new Variable(); NamedNodeMap startAttr = node.getAttributes(); for (int i = 0; i < startAttr.getLength(); i++) { Node attr = startAttr.item(i); //node parameter if (attr.getNodeName().compareToIgnoreCase("name")==0) { variable.setName(attr.getNodeValue()); } else if (attr.getNodeName().compareToIgnoreCase("id")==0) { variable.setId(attr.getNodeValue()); } } ((Activity)father).addOutVariable(variable); newFather=variable; addUniqueVariable(variable); } } else if (node.getNodeName().equalsIgnoreCase(tagAct+"parallelGateway") || node.getNodeName().equalsIgnoreCase(tagAct+"exclusiveGateway") || node.getNodeName().equalsIgnoreCase(tagAct+"inclusiveGateway")) { //body gateways //cast father EABPM eabpm = (EABPM) father; //create new item Gateway gateway = new Gateway(); //add element to bp eabpm.addElement(gateway); if (node.getNodeName().equalsIgnoreCase(tagAct+"inclusiveGateway")) gateway.setType(Gateway.Type.INCLUSIVE); else if (node.getNodeName().equalsIgnoreCase(tagAct+"parallelGateway")) gateway.setType(Gateway.Type.PARALLEL); else gateway.setType(Gateway.Type.EXCLUSIVE); //new object (use in the recursive call) newFather=gateway; NamedNodeMap startAttr = node.getAttributes(); for (int i = 0; i < startAttr.getLength(); i++) { Node attr = startAttr.item(i); //node parameter if (attr.getNodeName().compareToIgnoreCase("id")==0) { gateway.setId(attr.getNodeValue()); //System.out.print(" "+attr.getNodeName() + "=\""+attr.getNodeValue()+"\""); } } //add element to internal hashmap, for the ordering //add after, couse i need to know the valid of the ID addElement(gateway); //end body } else if (node.getNodeName().equalsIgnoreCase(tagAct+"sequenceFlow")) { //body control flow String sourceRef=""; String targetRef=""; //new object (use in the recursive call) newFather=father; NamedNodeMap startAttr = node.getAttributes(); for (int i = 0; i < startAttr.getLength(); i++) { Node attr = startAttr.item(i); //node parameter if (attr.getNodeName().compareToIgnoreCase("sourceRef")==0) { sourceRef=attr.getNodeValue(); //System.out.print(" "+attr.getNodeName() + "=\""+attr.getNodeValue()+"\""); } else if (attr.getNodeName().compareToIgnoreCase("targetRef")==0) { targetRef=attr.getNodeValue(); //System.out.print(" "+attr.getNodeName() + "=\""+attr.getNodeValue()+"\""); } } //bpFlow.put(sourceRef, targetRef); String[] tmp = new String[2]; tmp[0]=sourceRef; tmp[1]=targetRef; bpFlow.add(tmp); //end body } else if (node.getNodeName().equalsIgnoreCase("aniketos:security") ) { //do nothing newFather=father; } else if (node.getNodeName().equalsIgnoreCase("aniketos:trustworthiness") ) { Activity activity= (Activity)father; NamedNodeMap startAttr = node.getAttributes(); for (int i = 0; i < startAttr.getLength(); i++) { Node attr = startAttr.item(i); //node parameter if (attr.getNodeName().compareToIgnoreCase("value")==0) { activity.setTrustworthiness(new Float(attr.getNodeValue())); } } //do nothing newFather=father; } else { //other tags, do nothing } } //***** //if the child is a piece of text, it means is the name of the father //***** else if (node.getNodeType() == node.TEXT_NODE) { //System.out.print(node.getNodeValue()); String content= node.getNodeValue(); content=content.replaceAll("[\n'\t]", ""); //if father is an instance of class Variable //I can read the content of the tag and set the name of the variable. //this is for service task variables if (father.getClass()==Activity.class) { //delete useless character ${var} ->var //content=content.replaceAll("[ ${} \t \n \r \f ]", ""); content=content.replaceAll("[ \t \n \r \f ]", ""); if(!content.equals("")) { Activity activity = (Activity) father; //if the name ogf the variable contains a $ in means it is a input variable. if(content.contains("$")) { //split string as ${V1}~${V2} on V1, V2 Pattern pattern = Pattern.compile("[\\$\\{ \\}~\\{ \\}]"); String[] inputList = pattern.split(content); Vector<Variable> varList = activity.getInVariablesList(); Iterator<Variable> varListIT = varList.iterator(); //search for the first available variable name int nextVar=0; for(int i=0; i<inputList.length; i++) { String input = inputList[i]; if (input.length()<=0) nextVar++; else break; } //search in the list of the variable of the activity, the only one which does not have a name (the one insertet in the tag just above)) while (varListIT.hasNext()) { Variable var = varListIT.next(); if (var.getId()==null) { var.setId(inputList[nextVar]); var.setName(inputList[nextVar]); addUniqueVariable(var); break; } } //incremant the variable, otherwise i'll examine twice the same var name nextVar++; //for all the other var name, i creates new variables and i insert them in the list of input var. for(int i=nextVar; i<inputList.length; i++) { String input = inputList[i]; if (input.length()>0) { Variable var = new Variable(); var.setId(input); var.setName(input); activity.addInVariable(var); addUniqueVariable(var); } } } else { Vector<Variable> varList = activity.getOutVariablesList(); Variable variable = varList.get(0);//only one output variable for every service tasks variable.setName(content); variable.setId(content); //System.out.println("Var name: "+ content); addUniqueVariable(variable); } } } //set up participant in case of service tasks if (father.getClass()==Participant.class) { Participant part = (Participant) father; content=content.replaceAll("[ ${} \t \n \r \f ]", ""); if (content.length()!=0) { //duplicate name unique part.setName(content); part.setId(content); part = addUniqueParticipant(part);//i can add participants that will not be used next, the purpose of this list is to avoid duplicatons } } } //for each the childs recall the functions //it works because the text node has no childs //placed here in order to avoid code duplication //this have to be done since the text element is always checked, even if in the xml there is no element text if (newFather==null) newFather=father; for (Node child = node.getFirstChild(); child != null; child = child.getNextSibling()) { //if there will be any child, this will be done only after the element node setupBPM(child,newFather); } //if the XML is well formed, once i have parsed all the child i have to meet the close tag if (node.getNodeType() == node.ELEMENT_NODE) { //do nothing //but i have to read it in order to go on //System.out.print("</"+node.getNodeName()+">"); } //end of the recursion? if (father.getClass()==EABPM.class) return (EABPM) father; else return null; } //EABPM getters and setters /**Getters of the instantiated Business Process data structure * @return the instantiated data structure */ public EABPM getEabpm() { return eabpm; } /**Setter of the instantiated Business Process data structure * @param eabpm the instantiated data structure */ public void setEabpm(EABPM eabpm) { this.eabpm = eabpm; } //participant getters and setters /**Getter of the participants list * @return the participant list */ public static HashMap<String, Participant> getParticipantList() { return participantList; } /**This function adds a participant to the participants list * @param participant the participant that have to be added to the list */ public static void addParticipant(Participant participant) { ParseSecureBPMNDom.participantList.put(participant.getName(), participant); } /**This function removes a given participant from the participant list * @param participant the participant that have to be removed */ public static void removeParticipant(Participant participant) { ParseSecureBPMNDom.participantList.remove(participant); } /**This function adds a given element only if there is no other element in the participant list with the same name. * @param participant the participant that have to be added * @return the same object received in input, if the is no participant, or the participant object in the list with the same name */ public static Participant addUniqueParticipant(Participant participant) { if (participantList.containsKey(participant.getName())) return participantList.get(participant.getName()); else { participantList.put(participant.getName(), participant); return participant; } } //elements getters and setters /**Getter of the element list * @return the list of the elements */ public static HashMap<String, Element> getElementList() { return elementList; } /**This function adds an element to the participants list * @param element the element that have to be added to the list */ public static void addElement(Element element) { ParseSecureBPMNDom.elementList.put(element.getId(),element); } /**This function removes a given element from the participant list * @param element the element that have to be removed */ public static void removeElement(Element element) { ParseSecureBPMNDom.elementList.remove(element); } //variable getters and setters /**Getter of the variable list * @return the list of all the variables */ public static Vector<Variable> getVariableList() { return variableList; } /**This function adds a variable to the participants list * @param variable the variable that have to be added to the list */ public static void addVariable(Variable variable) { ParseSecureBPMNDom.variableList.add(variable); } /**This function removes a given variable from the participant list * @param variable the variable that have to be removed */ public static void removeVariable(Variable variable) { ParseSecureBPMNDom.variableList.remove(variable); } //the key is name+id /**This function adds a given variable only if there is no other variable in the variable list with the same name and id. * @param variable the variable that have to be added * @return the same object received in input, if the is no variable with the same id and name, or the variable object in the list with the same name */ public static Variable addUniqueVariable(Variable variable) { //i have to search through all the variable, i cannot use "contains" method because i have to compare the names Iterator<Variable> varIT = variableList.iterator(); while (varIT.hasNext()) { Variable varComp = varIT.next(); if (variable.getName().compareToIgnoreCase(varComp.getName())==0) { return varComp; } } variableList.add(variable); return variable; } }