/* * ParseMappingDom.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.Map; import java.util.Vector; import eu.aniketos.SecureBPMN.*; import eu.aniketos.SecureBPMN.Participant.participantType; import eu.aniketos.srcm.SRS.*; import eu.aniketos.srcm.mapping.*; import org.w3c.dom.Document; import org.w3c.dom.NamedNodeMap; import org.w3c.dom.Node; /**This class parses the Mapping XML document received as inputs, and instantiates the correspondent data structure * @author Mattia Salnitri * */ public class ParseMappingDom { //list of reference to the object that may be referenced more than one time in the SRS private static HashMap<String, Participant> BPParticipantList; //key is the name => participants name is unique private static HashMap<String, Role> SRRoleList; private static HashMap<String, Agent> SRAgentList; private static HashMap<String, Goal> SRGoalList; private static HashMap<String, Element> BPActivityList; //key is the id private static HashMap<String, eu.aniketos.srcm.SRS.Document> SRDocumentList; private static Vector<Variable> BPVariableList; //the key is the name+id //temp vector, store the mapping during the parsing private static Vector<Variable> BPVariableListTmp; //the key is the name+id private Mapping mapping; /** Constructor of the class. * This class should be instantiated after both the BPM and the SRS are already parsed. * @param domMapping the XML mapping document * @param BPParticipantList the list of the BPM participants * @param SRRoleList the list of the STS-ml roles * @param SRAgentList the list of the STS-ml agents * @param SRGoalList the list of the STS-ml goals * @param BPActivityList the list of the BPM activities * @param SRDocumentList the list of the STS-ml documents * @param BPVariableList the list of the BPM variables */ @SuppressWarnings("static-access") public ParseMappingDom(Document domMapping, HashMap<String, Participant> BPParticipantList, HashMap<String, Role> SRRoleList, HashMap<String, Agent> SRAgentList, HashMap<String, Goal> SRGoalList, HashMap<String, Element> BPActivityList, HashMap<String, eu.aniketos.srcm.SRS.Document> SRDocumentList, Vector<Variable> BPVariableList) { //instantiate the mapping mapping = new Mapping(); //set up all the hashmaps this.BPParticipantList=BPParticipantList; this.SRRoleList= SRRoleList; this.SRAgentList=SRAgentList; this.SRGoalList = SRGoalList; this.BPActivityList = BPActivityList; this.SRDocumentList = SRDocumentList; this.BPVariableList=BPVariableList; BPVariableListTmp = new Vector<Variable>(); try { mapping = (Mapping) setupMapping(domMapping, mapping, mapping); } catch (Exception e) { System.err.println("error in the Mapping class structure instantiation"); } //last step consists to match the variables //since from the mapping we collected the matching on variable and document but in the BP we have stream that are identified with (name + id) //to do in the checking algorithm //clean up the mapping mapping.clearLists(); //assign owners to activities this.assignOwners(BPActivityList); } /** * assigns to all activities an owner *rationale: *if only 1 of the candidate owners are presents in the mapping is-a list, i assign that owner *if both agent and role are present in the list, i assign the agent to the owner * * @param BPActivityList: the list of activities in the BP */ public void assignOwners(HashMap<String, Element> BPActivityList) { @SuppressWarnings("rawtypes") Iterator it = BPActivityList.entrySet().iterator(); while (it.hasNext()) { Map.Entry pairs = (Map.Entry)it.next(); if ( pairs.getValue().getClass()!=Activity.class)//i'm interested only in activities, i skip events continue; Activity act = (Activity) pairs.getValue(); Vector<Participant> candidateOwners = act.getCandidateOwners(); Iterator<Participant> itCandidateOwners = candidateOwners.iterator(); while(itCandidateOwners.hasNext())//for all candidate users they are 1 or 2 { Participant candidateOwner = itCandidateOwners.next(); //search in the is-a f exist at least a relation which map the participant to a role //for all the is-a Vector<IsA> isAList = mapping.getIsaList(); Iterator<IsA> itIsA = isAList.iterator(); while (itIsA.hasNext()) { IsA isA = itIsA.next(); if (isA.getParticipant().getId().equals(candidateOwner.getId())) { act.setOwner(candidateOwner);//found, the first, stop here break; } } //if there is still no owner i check the play relations if (act.getOwner()==null) { Vector<Plays> playsList= mapping.getPlaysList(); Iterator<Plays> itPlays = playsList.iterator(); while(itPlays.hasNext()) { Plays plays = itPlays.next(); if (plays.getParticipant().getId().equals(candidateOwner.getId())) { act.setOwner(candidateOwner); break; } } } if (act.getOwner()!=null && act.getOwner().getType()==participantType.BPAGENT)//if the participant is an agent, we finish here, otherwise we keep searching for the role break; } //at this point the activity should have the owner set if (act.getOwner()==null) System.err.println("Owner of activity " +act.getName()+" not found!"); } } //return type is not mappoing couse the return value may be also of type "Variable" @SuppressWarnings("static-access") private static Object setupMapping (Node node, Object father, Mapping mapping) throws Exception { Object newFather=null; //global couse are used in the recursive step Variable variable=null; if (node.getNodeType() == node.ELEMENT_NODE) { //node name //System.out.print("<"+node.getNodeName()); if (node.getNodeName().equalsIgnoreCase("mapping")) { //first node //new object (use in the recursive call) newFather=father; } if (node.getNodeName().equalsIgnoreCase("participant")) { //Do nothing newFather=father; } if (node.getNodeName().equalsIgnoreCase("value")) { //Do nothing newFather=father; } else if (node.getNodeName().equalsIgnoreCase("relatesTo")) { //cast the father //Mapping mapping = (Mapping) father; //set up new relation RelatesTo relatesTo = new RelatesTo(); //add relation to the mapping mapping.addRelatesToRelation(relatesTo); //new father for next recursive calls newFather=relatesTo; } else if (node.getNodeName().equalsIgnoreCase("represents")) { //cast the father //Mapping mapping = (Mapping) father; //set up the relation Represents represents = new Represents(); //add the relation mapping.addRepresentsRelation(represents); //new father for next recursive calls newFather=represents; } else if (node.getNodeName().equalsIgnoreCase("is-a")) { //cast the father //Mapping mapping = (Mapping) father; //set up the relation IsA isA = new IsA(); //add the relation //mapping.addIsaRelation(isA);//added when parsing an inner tag, to avoid duplication //new father for next recursive calls newFather=isA; } else if (node.getNodeName().equalsIgnoreCase("plays")) { //cast the father //Mapping mapping = (Mapping) father; //set up the relation Plays plays = new Plays(); //add the relation mapping.addPlaysRelation(plays); //new father for next recursive calls newFather=plays; } else if (node.getNodeName().equalsIgnoreCase("activity")) { //body activity //cast father RelatesTo relatesTo = (RelatesTo) father; String id=""; //retrieve the ID 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) { id=attr.getNodeValue(); //System.out.print(" "+attr.getNodeName() + "=\""+attr.getNodeValue()+"\""); } } if (id.compareTo("")==0) { System.err.println("invalid Activity ID"); throw new Exception(); } //search the id among all the element already collected in the BPM XML file Activity activity = (Activity) BPActivityList.get(id); //it means it is not found in the activity list, so the commitment never mentioned it, so it is useless if (activity==null) { return null; } //add element to relation relatesTo.setActivity(activity); //new object (use in the recursive call) newFather=activity; //end body } else if (node.getNodeName().equalsIgnoreCase("goal")) { //body goal //cast father RelatesTo relatesTo = (RelatesTo) father; String id=""; //retrieve the ID 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) { id=attr.getNodeValue(); //System.out.print(" "+attr.getNodeName() + "=\""+attr.getNodeValue()+"\""); } } if (id.compareTo("")==0) { System.err.println("invalid goal ID"); throw new Exception(); } //search the id among all the element already collected in the BPM XML file Goal goal = SRGoalList.get(id); //it means it is not found in the goal list, so the commitment never mentioned it, so it is useless if (goal==null) { return null; } //add element to relation relatesTo.setGoal(goal); //new object (use in the recursive call) newFather=goal; //end body } else if (node.getNodeName().equalsIgnoreCase("variable")) { //body variable if (father.getClass()== Represents.class) { //cast father Represents represents = (Represents) father; String id=""; //retrieve the ID 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) { id=attr.getNodeValue(); //System.out.print(" "+attr.getNodeName() + "=\""+attr.getNodeValue()+"\""); } } if (id.compareTo("")==0) { System.err.println("invalid variable ID"); throw new Exception(); } //create a variable, i still do not know if it's duplicate, couse i don't know the name variable = new Variable(); variable.setId(id); //add to the list anyway BPVariableListTmp.add(variable); //add element to relation represents.setVariable(variable); } //new object (use in the recursive call) newFather=variable; //end body } else if (node.getNodeName().equalsIgnoreCase("document")) { //body document //cast father Represents represents = (Represents) father; //in case the document have been already set, it means it is a duplicate, hence i have to create a new relation and add it to the mapping data structure //i use the global variable to add the relation to the mapping data structure if (represents.getDocument()!=null) { System.out.println("Duplication of document, creating new represents to relation"); Represents representsDupl = new Represents(); //add to the mapping data structure mapping.addRepresentsRelation(representsDupl); representsDupl.setVariable(represents.getVariable());//the variable should be already set because of the XSD of mapping file //substitute the relation, in order to update the correct one represents= representsDupl; } String id=""; //retrieve the ID 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) { id=attr.getNodeValue(); //System.out.print(" "+attr.getNodeName() + "=\""+attr.getNodeValue()+"\""); } } if (id.compareTo("")==0) { System.err.println("Invalid document ID"); throw new Exception(); } //search the id among all the element already collected in the BPM XML file eu.aniketos.srcm.SRS.Document document = SRDocumentList.get(id); //it means it is not found in the goal list, i.e. it is a duplicate if (document==null) { //add it to set document = new eu.aniketos.srcm.SRS.Document(); document.setId(id);//for the name i have to wait next iteration, because the name is between tag, not a property ad ID } //add element to relation represents.setDocument(document); //new object (use in the recursive call) newFather=document; //end body } else if (node.getNodeName().equalsIgnoreCase("BPPart") || node.getNodeName().equalsIgnoreCase("BPRole")) { //body Participant if (!(father.getClass()==Plays.class) || node.getNodeName().equalsIgnoreCase("BPPart"))// role of play, it links a BP participant only to a srs role: play -> bp play i.e. if the relation is a play then the tag has to be a BPRole, otherwise skip { String id=""; //retrieve the ID 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) { id=attr.getNodeValue(); //System.out.print(" "+attr.getNodeName() + "=\""+attr.getNodeValue()+"\""); } } if (id.compareTo("")==0) { System.err.println("Invalid BPPart/BProle ID"); throw new Exception(); } //search the id among all the elements already collected in the BPM XML file Participant participant = BPParticipantList.get(id); //add element to relation //cast father if (father.getClass()==IsA.class) { IsA isa = (IsA) father; if (isa.getParticipant()!=null)//if the participant is != from null, it means an isa has been already created hence i createa new one isa = new IsA(); if (participant==null)//if the participant is null it means there is no id that correspond to participants collected in BP, but i have to search also in the content of the tag, so i created a placeholder variable. { participant= new Participant(); if (node.getNodeName().equalsIgnoreCase("BPPart")) participant.setType(participantType.BPAGENT); else participant.setType(participantType.BPROLE); isa.setParticipant(participant); } else isa.setParticipant(participant); mapping.addIsaRelation(isa); newFather=isa; } else { Plays plays = (Plays) father; if (participant==null)//if the participant is null it means there is no id that correspond to participants collected in BP, but i have to search also in the content of the tag, so i created a placeholder variable. { participant= new Participant(); if (node.getNodeName().equalsIgnoreCase("BPPart")) participant.setType(participantType.BPAGENT); else participant.setType(participantType.BPROLE); plays.setParticipant(participant); } else plays.setParticipant(participant); newFather=plays; } } else { System.out.println("BPAgent skipped because of play relation in mapping"); newFather=father;//skip tag } //end body } else if (node.getNodeName().equalsIgnoreCase("SRAgent") || node.getNodeName().equalsIgnoreCase("SRRole")) { //body actor //at this point i search the isa relations that has an empty actor, they may be 2 or one, depending if only one or both the ggs BPRole or BPAgents has been used in the mapping String id=""; //retrieve the ID 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) { id=attr.getNodeValue(); //System.out.print(" "+attr.getNodeName() + "=\""+attr.getNodeValue()+"\""); } } if (id.compareTo("")==0) { System.err.println("Invalid SRAgent/SRRole ID"); throw new Exception(); } Actor actor; //search the id among all the element already collected in the BPM XML file if (node.getNodeName().equalsIgnoreCase("SRAgent")) { actor = SRAgentList.get(id); } else { actor = SRRoleList.get(id); } //add element to relation //cast father if (father.getClass()==IsA.class) { //now i have the actor/ role or agent linked to the is-a, //at this point i fetch the relations in the is-a list in the mapping that does not have associated any actors, i.e. the two is-a relation created when the parsed visited the tags BProle and BPAgent Vector<IsA> isaList = mapping.getIsaList(); Vector<IsA> isaToBeRemoved = new Vector<IsA>();//list of isa relations (it should be only one) to be removed for (IsA isa: isaList) { if (isa.getActor()==null)//candidate found { if (actor.getClass()==Agent.class && isa.getParticipant().getType()==participantType.BPAGENT) { newFather=isa; isa.setActor(actor); } else if (actor.getClass()==Role.class && isa.getParticipant().getType()==participantType.BPROLE) { newFather=isa; isa.setActor(actor); } else isaToBeRemoved.add(isa); } } isaList.removeAll(isaToBeRemoved); } else { Plays plays = (Plays) father; plays.setActor(actor); newFather=plays; } //end body } 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.getClass()==Variable.class) { //it has the same pinter to the global variable "variable", this is passed via parameter Variable variableF = (Variable) father; //i create a new variable in the new array, if it does not exists yet. Iterator<Variable> i = BPVariableListTmp.iterator(); boolean found = false; while (i.hasNext()) { Variable varTmp= (Variable) i.next(); if (varTmp.getId().compareToIgnoreCase(variableF.getId())==0 && varTmp.getName()!=null && varTmp.getName().compareToIgnoreCase(content)==0) { //is a diplicate BPVariableListTmp.remove(variableF); //return the var to substitute newFather=varTmp; found = true; break; } } //if there is no duplicate, set the name if (!found) variableF.setName(content); } else if (father.getClass()==IsA.class || father.getClass()==Plays.class) { if (father.getClass()==IsA.class)//i have to reach the 2 ( or one o no one that has a part empty { IsA isA = (IsA) father; if (isA.getParticipant()!=null && isA.getParticipant().getId()==null)//if it's a place holder variable a read and setup the participant { Participant part = BPParticipantList.get(content); part.setType(isA.getParticipant().getType()); isA.setParticipant(part); } } else if (father.getClass()==Plays.class) { Plays plays = (Plays) father; if (plays.getParticipant()!=null && plays.getParticipant().getId()==null)//if it's a place holder variable a read and setup the participant { Participant part = BPParticipantList.get(content); part.setType(plays.getParticipant().getType()); plays.setParticipant(part); } } } else if (father.getClass()==eu.aniketos.srcm.SRS.Document.class) { eu.aniketos.srcm.SRS.Document document = (eu.aniketos.srcm.SRS.Document) father; if (document.getName()==null) { System.out.println("set name duplicated document to: " + content); document.setName(content); } } } //for each the child recall the functions //it works because the text node has no child //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 Object returnValue = setupMapping(child,newFather, mapping); //if the variable has the name set, it means is has be mantained in the vector (it is not duplicated) //otherwise the var is duplicated if (returnValue!=null && returnValue.getClass()==Variable.class && variable!=null) { if (variable!=(Variable) returnValue) //if true the variable was a duplicate, so i have to substitute it in the relation ((Represents)father).setVariable(((Variable) returnValue)); } } //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()+">"); } if (father.getClass()==Mapping.class) return (Mapping) father; else if (newFather.getClass()==Variable.class) return newFather; else return null; } /**Getter of the mapping data structure * @return the mapping data structure */ public Mapping getMapping() { return mapping; } /**Setter of the mapping data structure * @param mapping the mapping data structure */ public void setMapping(Mapping mapping) { this.mapping = mapping; } }