package jadex.bpmn; import jadex.bpmn.model.MActivity; import jadex.bpmn.model.MAnnotation; import jadex.bpmn.model.MAnnotationDetail; import jadex.bpmn.model.MArtifact; import jadex.bpmn.model.MAssociation; import jadex.bpmn.model.MAssociationTarget; import jadex.bpmn.model.MBpmnModel; import jadex.bpmn.model.MLane; import jadex.bpmn.model.MMessagingEdge; import jadex.bpmn.model.MNamedIdElement; import jadex.bpmn.model.MParameter; import jadex.bpmn.model.MPool; import jadex.bpmn.model.MSequenceEdge; import jadex.bpmn.model.MSubProcess; import jadex.bridge.Argument; import jadex.bridge.IArgument; import jadex.commons.IFilter; import jadex.commons.ResourceInfo; import jadex.commons.SReflect; import jadex.javaparser.IParsedExpression; import jadex.javaparser.javaccimpl.JavaCCExpressionParser; import jadex.xml.AccessInfo; import jadex.xml.AttributeInfo; import jadex.xml.IContext; import jadex.xml.IPostProcessor; import jadex.xml.MappingInfo; import jadex.xml.ObjectInfo; import jadex.xml.SubobjectInfo; import jadex.xml.TypeInfo; import jadex.xml.XMLInfo; import jadex.xml.bean.BeanAccessInfo; import jadex.xml.bean.BeanObjectReaderHandler; import jadex.xml.reader.Reader; import java.io.File; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Set; import java.util.StringTokenizer; import javax.xml.namespace.QName; /** * Reader for loading Bpmn XML models into a Java representation states. */ public class BpmnXMLReader { //-------- constants -------- // Copied from jadex.tools.bpmn.editor.properties.AbstractJadexPropertySection /** * String delimiter for list elements <p> * <p><code>0x241F</code> (9247) SYMBOL FOR UNIT SEPARATOR</p> */ public static final String LIST_ELEMENT_DELIMITER = "\u241F"; // "<*>"; /** * String delimiter for element attributes <p> * <p><code>0x240B</code> (9227) SYMBOL FOR VERTICAL TABULATION</p> */ public static final String LIST_ELEMENT_ATTRIBUTE_DELIMITER = "\u240B"; //"#|#"; //-------- attributes -------- /** The singleton reader instance. */ protected static Reader reader; //-------- methods -------- // Initialize reader instance. static { reader = new Reader(new BeanObjectReaderHandler(getXMLMapping())); } /** * Get the reader instance. * / public static Reader getReader() { return reader; }*/ /** * Read properties from xml. * @param info The resource info. * @param classloader The classloader. */ public static MBpmnModel read(ResourceInfo rinfo, ClassLoader classloader) throws Exception { MBpmnModel ret = (MBpmnModel)reader.read(rinfo.getInputStream(), classloader, null); ret.setFilename(rinfo.getFilename()); ret.setLastModified(rinfo.getLastModified()); ret.setClassloader(classloader); String name = new File(rinfo.getFilename()).getName(); name = name.substring(0, name.length()-5); ret.setName(name); ret.initModelInfo(); rinfo.getInputStream().close(); return ret; } /** * Get the XML mapping. */ public static Set getXMLMapping() { Set types = new HashSet(); String uri = "http://stp.eclipse.org/bpmn"; String xmiuri = "http://www.omg.org/XMI"; types.add(new TypeInfo(new XMLInfo(new QName[]{new QName(uri, "BpmnDiagram")}), new ObjectInfo(MBpmnModel.class, new BpmnModelPostProcessor()), new MappingInfo(null, new AttributeInfo[]{ new AttributeInfo(new AccessInfo(new QName("http://www.w3.org/2001/XMLSchema-instance", "schemaLocation"), AccessInfo.IGNORE_READWRITE)), new AttributeInfo(new AccessInfo(new QName("http://www.omg.org/XMI", "version"), null, AccessInfo.IGNORE_READWRITE)), new AttributeInfo(new AccessInfo("iD", null, AccessInfo.IGNORE_READWRITE)) }, new SubobjectInfo[]{ new SubobjectInfo(new AccessInfo("pools", "pool")), new SubobjectInfo(new AccessInfo("artifacts", "artifact")), new SubobjectInfo(new AccessInfo("messages", "messagingEdge")), new SubobjectInfo(new AccessInfo("eAnnotations", "annotation")) }))); types.add(new TypeInfo(new XMLInfo("eAnnotations"), new ObjectInfo(MAnnotation.class), new MappingInfo(null, new BeanAccessInfo[]{ }, new SubobjectInfo[]{ new SubobjectInfo(new AccessInfo("details", "detail")), }))); types.add(new TypeInfo(new XMLInfo("details"), new ObjectInfo(MAnnotationDetail.class))); types.add(new TypeInfo(new XMLInfo("pools"), new ObjectInfo(MPool.class, new PoolPostProcessor()), new MappingInfo(null, new AttributeInfo[]{ new AttributeInfo(new AccessInfo("name", "description")), new AttributeInfo(new AccessInfo("associations", "associationsDescription")), new AttributeInfo(new AccessInfo("iD", null, AccessInfo.IGNORE_READWRITE)) }, new SubobjectInfo[]{ new SubobjectInfo(new AccessInfo("vertices", "activity")), new SubobjectInfo(new AccessInfo("sequenceEdges", "sequenceEdge")), new SubobjectInfo(new AccessInfo("lanes", "lane")), new SubobjectInfo(new AccessInfo("eAnnotations", "annotation")) }))); types.add(new TypeInfo(new XMLInfo("artifacts"), new ObjectInfo(MArtifact.class), new MappingInfo(null, new AttributeInfo[]{ new AttributeInfo(new AccessInfo("name", "description")), new AttributeInfo(new AccessInfo("iD", null, AccessInfo.IGNORE_READWRITE)) }, new SubobjectInfo[]{ new SubobjectInfo(new AccessInfo("associations", "association")), new SubobjectInfo(new AccessInfo("eAnnotations", "annotation")) }))); types.add(new TypeInfo(new XMLInfo("associations"), new ObjectInfo(MAssociation.class, new AssociationPostProcessor()), new MappingInfo(null, new AttributeInfo[]{ new AttributeInfo(new AccessInfo("iD", null, AccessInfo.IGNORE_READWRITE)) }, new SubobjectInfo[]{ new SubobjectInfo(new XMLInfo("eAnnotations"), new AccessInfo("eAnnotations", "annotation")) }))); types.add(new TypeInfo(new XMLInfo("lanes"), new ObjectInfo(MLane.class, new LanePostProcessor()), new MappingInfo(null, new AttributeInfo[]{ new AttributeInfo(new AccessInfo("name", "description")), new AttributeInfo(new AccessInfo("activities", "activitiesDescription")), new AttributeInfo(new AccessInfo("iD", null, AccessInfo.IGNORE_READWRITE)) }, new SubobjectInfo[]{ new SubobjectInfo(new XMLInfo("eAnnotations"), new AccessInfo("eAnnotations", "annotation")) }))); types.add(new TypeInfo(new XMLInfo("eventHandlers"), new ObjectInfo(MActivity.class, new EventHandlerPostProcessor()), new MappingInfo(null, new AttributeInfo[]{ new AttributeInfo(new AccessInfo("name", "description")), new AttributeInfo(new AccessInfo("outgoingEdges", "outgoingSequenceEdgesDescription")), new AttributeInfo(new AccessInfo("incomingEdges", "incomingSequenceEdgesDescription")), new AttributeInfo(new AccessInfo("iD", null, AccessInfo.IGNORE_READWRITE)), }, new SubobjectInfo[]{ new SubobjectInfo(new XMLInfo("eAnnotations"), new AccessInfo("eAnnotations", "annotation")) }))); types.add(new TypeInfo(new XMLInfo("vertices", new IFilter() { public boolean filter(Object obj) { String type = (String)((Map)obj).get("type"); return type.endsWith("Activity"); } }), new ObjectInfo(MActivity.class, new ActivityPostProcessor()), new MappingInfo(null, new AttributeInfo[]{ new AttributeInfo(new AccessInfo("name", "description")), new AttributeInfo(new AccessInfo("outgoingEdges", "outgoingSequenceEdgesDescription")), new AttributeInfo(new AccessInfo("incomingEdges", "incomingSequenceEdgesDescription")), new AttributeInfo(new AccessInfo("lanes", "laneDescription")), new AttributeInfo(new AccessInfo("associations", "associationsDescription")), new AttributeInfo(new AccessInfo("activityType", "activityType", null, MBpmnModel.TASK)), new AttributeInfo(new AccessInfo("iD", null, AccessInfo.IGNORE_READWRITE)) }, new SubobjectInfo[]{ new SubobjectInfo(new AccessInfo("incomingMessages", "incomingMessageDescription")), new SubobjectInfo(new AccessInfo("outgoingMessages", "outgoingMessageDescription")), new SubobjectInfo(new AccessInfo("eAnnotations", "annotation")) }))); types.add(new TypeInfo(new XMLInfo("vertices", new IFilter() { public boolean filter(Object obj) { String type = (String)((Map)obj).get("type"); return type.endsWith("SubProcess"); } }), new ObjectInfo(MSubProcess.class, new ActivityPostProcessor()), new MappingInfo(null, new AttributeInfo[]{ new AttributeInfo(new AccessInfo("name", "description")), new AttributeInfo(new AccessInfo("outgoingEdges", "outgoingSequenceEdgesDescription")), new AttributeInfo(new AccessInfo("incomingEdges", "incomingSequenceEdgesDescription")), new AttributeInfo(new AccessInfo("lanes", "laneDescription")), new AttributeInfo(new AccessInfo("associations", "associationsDescription")), new AttributeInfo(new AccessInfo("activityType", "activityType", null, MBpmnModel.SUBPROCESS)), new AttributeInfo(new AccessInfo("iD", null, AccessInfo.IGNORE_READWRITE)) }, new SubobjectInfo[]{ new SubobjectInfo(new AccessInfo("incomingMessages", "incomingMessageDescription")), new SubobjectInfo(new AccessInfo("outgoingMessages", "outgoingMessageDescription")), new SubobjectInfo(new AccessInfo("eventHandlers", "eventHandler")), new SubobjectInfo(new AccessInfo("vertices", "Activity")), new SubobjectInfo(new AccessInfo("sequenceEdges", "sequenceEdge")), new SubobjectInfo(new AccessInfo("eAnnotations", "annotation")) }))); types.add(new TypeInfo(new XMLInfo("sequenceEdges"), new ObjectInfo(MSequenceEdge.class, new SequenceEdgePostProcessor()), new MappingInfo(null, new AttributeInfo[]{ new AttributeInfo(new AccessInfo("name", "description")), new AttributeInfo(new AccessInfo("associations", "associationsDescription")), new AttributeInfo(new AccessInfo("iD", null, AccessInfo.IGNORE_READWRITE)), new AttributeInfo(new AccessInfo("conditionType", null, AccessInfo.IGNORE_READWRITE)), new AttributeInfo(new AccessInfo("isDefault", "default")) }, new SubobjectInfo[]{ new SubobjectInfo(new XMLInfo("eAnnotations"), new AccessInfo("eAnnotations", "annotation")) }))); types.add(new TypeInfo(new XMLInfo("messagingEdges"), new ObjectInfo(MMessagingEdge.class), new MappingInfo(null, new AttributeInfo[]{ new AttributeInfo(new AccessInfo("name", "description")), new AttributeInfo(new AccessInfo("associations", "associationsDescription")), new AttributeInfo(new AccessInfo("iD", null, AccessInfo.IGNORE_READWRITE)), }, new SubobjectInfo[]{ new SubobjectInfo(new AccessInfo("eAnnotations", "annotation")) }))); types.add(new TypeInfo(new XMLInfo("incomingMessages"), new ObjectInfo(HashMap.class), new MappingInfo(null, new AttributeInfo[]{ new AttributeInfo(new AccessInfo(new QName(xmiuri, "type"), null, null, null, new BeanAccessInfo(AccessInfo.THIS))), new AttributeInfo(new AccessInfo("href", null, null, null, new BeanAccessInfo(AccessInfo.THIS))), new AttributeInfo(new AccessInfo("iD", null, AccessInfo.IGNORE_READWRITE)), }, new SubobjectInfo[]{ new SubobjectInfo(new AccessInfo("eAnnotations", "annotation")) }))); types.add(new TypeInfo(new XMLInfo("outgoingMessages"), new ObjectInfo(HashMap.class), new MappingInfo(null, new AttributeInfo[]{ new AttributeInfo(new AccessInfo(new QName(xmiuri, "type"), null, null, null, new BeanAccessInfo(AccessInfo.THIS))), new AttributeInfo(new AccessInfo("href", null, null, null, new BeanAccessInfo(AccessInfo.THIS))), new AttributeInfo(new AccessInfo("iD", null, AccessInfo.IGNORE_READWRITE)), }, new SubobjectInfo[]{ new SubobjectInfo(new AccessInfo("eAnnotations", "annotation")) }))); types.add(new TypeInfo(new XMLInfo("messages"), new ObjectInfo(MMessagingEdge.class), new MappingInfo(null, new AttributeInfo[]{ new AttributeInfo(new AccessInfo("source", "sourceDescription")), new AttributeInfo(new AccessInfo("target", "targetDescription")), new AttributeInfo(new AccessInfo("iD", null, AccessInfo.IGNORE_READWRITE)), }, new SubobjectInfo[]{ new SubobjectInfo(new AccessInfo("eAnnotations", "annotation")) }))); return types; } /** * Activity post processor. */ static class ActivityPostProcessor implements IPostProcessor { //-------- IPostProcessor interface -------- /** * Establish element connections. */ public Object postProcess(IContext context, Object object) { MBpmnModel dia = (MBpmnModel)context.getRootObject(); MActivity act = (MActivity)object; // System.out.println("Act: "+act.getName()+" "+act.getDescription()); // Make edge connections. Map edges = dia.getAllSequenceEdges(); String indesc = act.getIncomingSequenceEdgesDescription(); if(indesc!=null) { StringTokenizer stok = new StringTokenizer(indesc); while(stok.hasMoreElements()) { String edgeid = stok.nextToken(); MSequenceEdge edge = (MSequenceEdge)edges.get(edgeid); act.addIncomingSequenceEdge(edge); edge.setTarget(act); } } String outdesc = act.getOutgoingSequenceEdgesDescription(); if(outdesc!=null) { StringTokenizer stok = new StringTokenizer(outdesc); while(stok.hasMoreElements()) { String edgeid = stok.nextToken(); MSequenceEdge edge = (MSequenceEdge)edges.get(edgeid); act.addOutgoingSequenceEdge(edge); edge.setSource(act); } } // Make message connections. // todo: message - pool connections Map allmessages = dia.getAllMessagingEdges(); List inmsgs = act.getIncomingMessagesDescriptions(); if(inmsgs!=null) { for(int i=0; i<inmsgs.size(); i++) { Map msgdesc = (Map)inmsgs.get(i); String id = ((String)msgdesc.get("href")).substring(1); MMessagingEdge msg = (MMessagingEdge)allmessages.get(id); if(msg==null) throw new RuntimeException("Could not find message: "+id); act.addIncomingMessagingEdge(msg); msg.setTarget(act); } } List outmsgs = act.getOutgoingMessagesDescriptions(); if(outmsgs!=null) { for(int i=0; i<outmsgs.size(); i++) { Map msgdesc = (Map)outmsgs.get(i); String id = ((String)msgdesc.get("href")).substring(1); MMessagingEdge msg = (MMessagingEdge)allmessages.get(id); if(msg==null) throw new RuntimeException("Could not find message: "+id); act.addOutgoingMessagingEdge(msg); msg.setTarget(act); // Set mode to throwing. Hack!!! BPMN editor does not set isThrowing property when using message edges. act.setPropertyValue("isThrowing", Boolean.TRUE); } } if(act.getDescription()!=null) { // first line: name // lines with = in it: properties or class // lines starting with in/out/inout: parameters StringTokenizer stok = new StringTokenizer(act.getDescription(), "\r\n"); JavaCCExpressionParser parser = new JavaCCExpressionParser(); while(stok.hasMoreTokens()) { String prop = stok.nextToken().trim(); // System.out.println("prop: "+prop); int idx = prop.indexOf("="); if(prop.startsWith("in") || prop.startsWith("out")) { // parameter StringTokenizer stok2 = new StringTokenizer(prop, " \t="); String paramdir = stok2.nextToken(); String paramclazzname = stok2.nextToken(); Class paramclazz = SReflect.findClass0(paramclazzname, dia.getAllImports(), context.getClassLoader()); if(paramclazz==null) throw new RuntimeException("Parameter class not found in imports: "+dia+", "+act+", "+paramclazzname);//+", "+SUtil.arrayToString(dia.getAllImports())); String paramname = stok2.nextToken(); IParsedExpression paramexp = null; if(stok2.hasMoreTokens()) { String proptext = prop.substring(idx+1).trim(); paramexp = parser.parseExpression(proptext, dia.getAllImports(), null, context.getClassLoader()); } MParameter param = new MParameter(paramdir, paramclazz, paramname, paramexp); act.addParameter(param); } else if(idx!=-1) { // property or class String propname = prop.substring(0, idx).trim(); String proptext = prop.substring(idx+1).trim(); if(propname.equals("class")) { // Compatibility hack: strip ".class" from value, if present. if(proptext.endsWith(".class")) { proptext = proptext.substring(0, proptext.length()-6); } try { Class clazz = SReflect.findClass(proptext, dia.getAllImports(), context.getClassLoader()); act.setClazz(clazz); } catch(ClassNotFoundException cnfe) { throw new RuntimeException(cnfe); } } else if(act instanceof MSubProcess && propname.equals("parallel")) { IParsedExpression propval = parser.parseExpression(proptext, dia.getAllImports(), null, context.getClassLoader()); ((MSubProcess)act).setSubprocessType(((Boolean)propval.getValue(null)).booleanValue() ? MSubProcess.SUBPROCESSTYPE_PARALLEL : MSubProcess.SUBPROCESSTYPE_NONE); } else { IParsedExpression propval = parser.parseExpression(proptext, dia.getAllImports(), null, context.getClassLoader()); act.setPropertyValue(propname, propval); } } else { // line without "=" is name act.setName(prop); } } } // Read annotations from Jadex bpmn tool. List annos = act.getAnnotations(); if(annos!=null) { JavaCCExpressionParser parser = new JavaCCExpressionParser(); for(int i=0; i<annos.size(); i++) { MAnnotation anno = (MAnnotation)annos.get(i); // new jadex parameter handling - we accept ALL "_parameters_table" if(anno.getSource().toLowerCase().endsWith("_parameters_table")) { BpmnMultiColumTable table = parseBpmnMultiColumTable(anno .getDetails()); for (int row = 0; row < table.dimension[0]; row++) { // normal activity parameter has 4 values if (table.data[row].length == 4) { String dir = table.data[row][0]; // direction String name = table.data[row][1]; // name String clazzname = table.data[row][2]; // class String val = table.data[row][3] != "" ? table.data[row][3] : null; // value try { Class clazz = SReflect.findClass(clazzname, dia.getAllImports(), context.getClassLoader()); IParsedExpression exp = null; if(val!=null && val.length()>0) { exp = parser.parseExpression(val, dia.getAllImports(), null, context.getClassLoader()); } MParameter param = new MParameter(dir, clazz, name, exp); act.addParameter(param); // System.out.println("Parameter: "+param); } catch(ClassNotFoundException cnfe) { throw new RuntimeException(cnfe); } } // Parameters of event handlers have 2 elements = are treated as properties?! // TODO: rename parameters to properties in editor and write converter? else if (table.data[row].length == 2) { String name = table.data[row][0]; String val = table.data[row][1] != "" ? table.data[row][1] : null; // context variable IParsedExpression exp = null; if(val!=null && val.length()>0) { exp = parser.parseExpression(val, dia.getAllImports(), null, context.getClassLoader()); } act.setPropertyValue(name, exp); } else { throw new RuntimeException("Parameter specification error: "+table.data[row].length+" "+Arrays.toString(table.data[row])); } } // next annotation continue; } // new jadex properties handling // we accept ALL "_properties_table" else if (anno.getSource().toLowerCase().endsWith("_properties_table")) { BpmnMultiColumTable table = parseBpmnMultiColumTable(anno .getDetails()); for (int row = 0; row < table.dimension[0]; row++) { // normal property has 2 values assert table.data[row].length == 2; String name = table.data[row][0]; String val = table.data[row][1] != "" ? table.data[row][1] : null; // context variable IParsedExpression exp = null; if(val!=null && val.length()>0) { exp = parser.parseExpression(val, dia.getAllImports(), null, context.getClassLoader()); } act.setPropertyValue(name, exp); // System.out.println("Parameter/property: "+name+" "+exp); } // next annotation continue; } if (!anno.getSource().toLowerCase().endsWith("table")) { List details = anno.getDetails(); if (details != null) { for (int j = 0; j < details.size(); j++) { MAnnotationDetail detail = (MAnnotationDetail) details .get(j); String key = detail.getKey(); String value = detail.getValue(); // TODO: remove old parameter handling if ("parameters".equals(key)) { StringTokenizer stok = new StringTokenizer( value, LIST_ELEMENT_DELIMITER); while (stok.hasMoreTokens()) { String paramtext = stok.nextToken(); StringTokenizer stok2 = new StringTokenizer( paramtext, LIST_ELEMENT_ATTRIBUTE_DELIMITER); // Parameters of normal activities have 4 elements int tokcnt = stok2.countTokens(); if (tokcnt == 3 || tokcnt == 4) { String dir = stok2.nextToken(); String name = stok2.nextToken(); String clazzname = stok2 .nextToken(); String val = stok2.hasMoreTokens() ? stok2 .nextToken() : null; try { Class clazz = SReflect .findClass( clazzname, dia.getAllImports(), context.getClassLoader()); IParsedExpression exp = null; if (val != null && val.length() > 0) { exp = parser .parseExpression( val, dia.getAllImports(), null, context.getClassLoader()); } MParameter param = new MParameter( dir, clazz, name, exp); act.addParameter(param); // System.out.println("Parameter: "+param); } catch (ClassNotFoundException cnfe) { throw new RuntimeException(cnfe); } } // Parameters of event handlers have 2 elements = are treated as properties?! else if (tokcnt == 2) { String name = stok2.nextToken(); String val = stok2.nextToken(); // context variable IParsedExpression exp = null; if (val != null && val.length() > 0) { exp = parser .parseExpression( val, dia.getAllImports(), null, context.getClassLoader()); } act.setPropertyValue(name, exp); // System.out.println("Parameter/property: "+name+" "+exp); } else { throw new RuntimeException( "Parameter specification error: " + stok2.countTokens() + " " + paramtext); } } } else // property { // Skip empty string (cannot be parsed to anything), for parsing empty string "" need to be used if (!"".equals(value)) { if (key.equals("class")) { // Compatibility hack: strip ".class" from value, if present. if (value.endsWith(".class")) { value = value.substring(0, value.length() - 6); } try { Class clazz = SReflect .findClass( value, dia.getAllImports(), context.getClassLoader()); act.setClazz(clazz); } catch (ClassNotFoundException cnfe) { throw new RuntimeException(cnfe); } } else if (act instanceof MSubProcess && "parallel".equals(key.toLowerCase())) { IParsedExpression propval = parser .parseExpression( value, dia.getAllImports(), null, context.getClassLoader()); ((MSubProcess) act) .setSubprocessType(((Boolean) propval .getValue(null)) .booleanValue() ? MSubProcess.SUBPROCESSTYPE_PARALLEL : MSubProcess.SUBPROCESSTYPE_NONE); } else { IParsedExpression propval = parser .parseExpression( value, dia.getAllImports(), null, context.getClassLoader()); act.setPropertyValue(key, propval); } } } } } } } } return null; } /** * Get the pass number. * @return The pass number. */ public int getPass() { return 2; } } /** * Event handler post processor. */ static class EventHandlerPostProcessor extends ActivityPostProcessor { //-------- IPostProcessor interface -------- /** * Establish element connections. */ public Object postProcess(IContext context, Object object) { Object ret = super.postProcess(context, object); if(ret==null) { ((MActivity)object).setEventHandler(true); } else { ((MActivity)ret).setEventHandler(true); } return ret; } } /** * Pool post processor. */ static class PoolPostProcessor extends NamePropertyPostProcessor { //-------- IPostProcessor interface -------- /** * Establish element connections. */ public Object postProcess(IContext context, Object object) { super.postProcess(context, object); // Set pool of activities. MPool pool = (MPool)object; setSubActivities(pool, pool); return null; } /** * Associate also subactivities with outer pool. */ protected void setSubActivities(MAssociationTarget parent, MPool pool) { List activities = parent instanceof MSubProcess? getAllActivities((MSubProcess)parent): ((MPool)parent).getActivities(); if(activities != null) { for(Iterator it = activities.iterator(); it.hasNext(); ) { MActivity activity = (MActivity)it.next(); activity.setPool(pool); if(activity instanceof MSubProcess) setSubActivities((MSubProcess)activity, pool); } } } /** * Get all activities of a subprocess. */ public List getAllActivities(MSubProcess proc) { List ret = new ArrayList(); if(proc.getActivities()!=null) ret.addAll(proc.getActivities()); if(proc.getEventHandlers()!=null) ret.addAll(proc.getEventHandlers()); return ret; } } /** * Lane post processor. */ static class LanePostProcessor extends NamePropertyPostProcessor { //-------- IPostProcessor interface -------- /** * Establish element connections. */ public Object postProcess(IContext context, Object object) { super.postProcess(context, object); // Resolve activities MLane lane = (MLane)object; String actdesc = lane.getActivitiesDescription(); if(actdesc!=null) { MBpmnModel dia = (MBpmnModel)context.getRootObject(); Map activities = dia.getAllActivities(); StringTokenizer stok = new StringTokenizer(actdesc); while(stok.hasMoreElements()) { String actid = stok.nextToken(); MActivity activity = (MActivity)activities.get(actid); lane.addActivity(activity); activity.setLane(lane); } } return null; } } /** * Association post processor. */ static class AssociationPostProcessor implements IPostProcessor { //-------- IPostProcessor interface -------- /** * Set source and target of association. */ public Object postProcess(IContext context, Object object) { MBpmnModel dia = (MBpmnModel)context.getRootObject(); MAssociation asso = (MAssociation)object; MArtifact source = (MArtifact)dia.getAllAssociationSources().get(asso.getId()); MAssociationTarget target = (MAssociationTarget)dia.getAllAssociationTargets().get(asso.getId()); if(source==null) throw new RuntimeException("Could not find association source: "+source); if(target==null) throw new RuntimeException("Could not find association target: "+target); asso.setSource(source); asso.setTarget(target); source.addAssociation(asso); target.addAssociation(asso); return null; } /** * Get the pass number. * @return The pass number. */ public int getPass() { return 2; } } /** * Sequence edge post processor. */ static class SequenceEdgePostProcessor implements IPostProcessor { //-------- IPostProcessor interface -------- /** * Establish element connections. */ public Object postProcess(IContext context, Object object) { MBpmnModel dia = (MBpmnModel)context.getRootObject(); MSequenceEdge edge = (MSequenceEdge)object; JavaCCExpressionParser parser = new JavaCCExpressionParser(); // Read annotations from Jadex bpmn tool. List annos = edge.getAnnotations(); if(annos!=null) { for(int i=0; i<annos.size(); i++) { MAnnotation anno = (MAnnotation)annos.get(i); // new mappings handling - we accept ALL "_mappings_table since a mapping is a mapping :-) // todo: enhance mappings with index? if(anno.getSource().toLowerCase().endsWith("_mappings_table")) { BpmnMultiColumTable table = parseBpmnMultiColumTable(anno .getDetails()); for (int row = 0; row < table.dimension[0]; row++) { try { // normal mapping has 2 values assert table.data[row].length == 2; } catch (AssertionError e) { // TODO Auto-generated catch block e.printStackTrace(); } String propname = table.data[row][0]; String proptext = table.data[row][1]; IParsedExpression exp = parser.parseExpression(proptext, dia.getAllImports(), null, context.getClassLoader()); IParsedExpression iexp = null; if(propname.endsWith("]") && propname.indexOf("[")!=-1) { String itext = propname.substring(propname.indexOf("[")+1, propname.length()-1); propname = propname.substring(0, propname.indexOf("[")); iexp = parser.parseExpression(itext, dia.getAllImports(), null, context.getClassLoader()); } edge.addParameterMapping(propname, exp, iexp); } // next annotation continue; } if (!anno.getSource().toLowerCase().endsWith("table")) { List details = anno.getDetails(); if (details != null) { for (int j = 0; j < details.size(); j++) { MAnnotationDetail detail = (MAnnotationDetail) details .get(j); String key = detail.getKey(); String value = detail.getValue(); // TODO: remove old mappings handling if ("mappings".equals(key)) { StringTokenizer stok = new StringTokenizer( value, LIST_ELEMENT_DELIMITER); while (stok.hasMoreTokens()) { String maptext = stok.nextToken(); StringTokenizer stok2 = new StringTokenizer( maptext, LIST_ELEMENT_ATTRIBUTE_DELIMITER); if (stok2.countTokens() == 2) { String propname = stok2.nextToken(); String proptext = stok2.nextToken(); IParsedExpression exp = parser .parseExpression( proptext, dia.getAllImports(), null, context.getClassLoader()); IParsedExpression iexp = null; if (propname.endsWith("]") && propname.indexOf("[") != -1) { String itext = propname .substring( propname.indexOf("[") + 1, propname.length() - 1); propname = propname.substring( 0, propname.indexOf("[")); iexp = parser .parseExpression( itext, dia.getAllImports(), null, context.getClassLoader()); } edge.addParameterMapping(propname, exp, iexp); } // System.out.println("Mapping: "+propname+" "+exp); } } else // todo: remove old mappings handling until here if ("condition".equals(key) && value != null && value.length() > 0) { IParsedExpression cond = parser .parseExpression(value, dia.getAllImports(), null, context.getClassLoader()); edge.setCondition(cond); // System.out.println("Condition: "+key+" "+value); } } } } } } // Only interpret description when no annotations were found. else if(edge.getDescription()!=null) { // first line: name // second line: condition // lines with = in it: parameters StringTokenizer stok = new StringTokenizer(edge.getDescription(), "\r\n"); String lineone = null; String linetwo = null; while(stok.hasMoreTokens()) { String prop = stok.nextToken(); int idx = prop.indexOf("="); boolean comp = idx>0 && (prop.charAt(idx-1)=='!' || prop.charAt(idx-1)=='<' || prop.charAt(idx-1)=='>'); boolean eq = idx!=-1 && idx<prop.length()-1 && prop.charAt(idx+1)=='='; boolean assignment = idx!=-1 && !comp && !eq; if(assignment) { String propname = prop.substring(0, idx).trim(); String proptext = prop.substring(idx+1).trim(); IParsedExpression exp = parser.parseExpression(proptext, dia.getAllImports(), null, context.getClassLoader()); IParsedExpression iexp = null; if(propname.endsWith("]") && propname.indexOf("[")!=-1) { String itext = propname.substring(propname.indexOf("[")+1, propname.length()-1); propname = propname.substring(0, propname.indexOf("[")); iexp = parser.parseExpression(itext, dia.getAllImports(), null, context.getClassLoader()); } edge.addParameterMapping(propname, exp, iexp); } else { // last line without "=" is assumed to be condition if(lineone==null) lineone = prop; else linetwo = prop; } } if(lineone!=null && linetwo!=null) { edge.setName(lineone); IParsedExpression cond = parser.parseExpression(linetwo, dia.getAllImports(), null, context.getClassLoader()); edge.setCondition(cond); } else if(lineone!=null) { IParsedExpression cond = parser.parseExpression(lineone, dia.getAllImports(), null, context.getClassLoader()); edge.setCondition(cond); } } return null; } /** * Get the pass number. * @return The pass number. */ public int getPass() { return 2; } } /** * Named element post processor. * Can parse the name and an aribitrary number of properties. */ static class NamePropertyPostProcessor implements IPostProcessor { //-------- IPostProcessor interface -------- /** * Establish element connections. */ public Object postProcess(IContext context, Object object) { MBpmnModel dia = (MBpmnModel)context.getRootObject(); MNamedIdElement namedelem = (MNamedIdElement)object; JavaCCExpressionParser parser = new JavaCCExpressionParser(); if(namedelem.getDescription()!=null) { // first line: name // lines with = in it: properties StringTokenizer stok = new StringTokenizer(namedelem.getDescription(), "\r\n"); while(stok.hasMoreTokens()) { String prop = stok.nextToken(); int idx = prop.indexOf("="); if(idx!=-1) { String propname = prop.substring(0, idx).trim(); String proptext = prop.substring(idx+1).trim(); try { Object propval = parser.parseExpression(proptext, dia.getAllImports(), null, context.getClassLoader()).getValue(null); namedelem.setPropertyValue(propname, propval); } catch(RuntimeException e) { throw new RuntimeException("Error parsing property: "+dia+", "+propname+", "+proptext, e); } } else { // line without "=" is name namedelem.setName(prop); } } } return null; } /** * Get the pass number. * @return The pass number. */ public int getPass() { return 2; } } /** * Bpmn Model post processor. */ static class BpmnModelPostProcessor implements IPostProcessor { //-------- IPostProcessor interface -------- /** * Establish element connections. */ public Object postProcess(IContext context, Object object) { MBpmnModel model = (MBpmnModel)context.getRootObject(); JavaCCExpressionParser parser = new JavaCCExpressionParser(); // Handle the annotations of the model (Jadex BPMN Editor). List annos = model.getAnnotations(); if(annos!=null) { // new jadex import handling // Parse imports/package first (editor saves annotations in arbitrary order, grrr.) for(int i=0; i<annos.size(); i++) { MAnnotation anno = (MAnnotation)annos.get(i); if (anno.getSource().toLowerCase().endsWith("_imports_table")) { BpmnMultiColumTable table = parseBpmnMultiColumTable(anno .getDetails()); for (int row = 0; row < table.dimension[0]; row++) { // normal import has 1 value assert table.data[row].length == 1; String imp = table.data[row][0]; if (imp.length() > 0) model.addImport(imp); } // we have found the imports break; } } // Parse imports/package first (editor saves annotations in arbitrary order, grrr.) for(int i=0; i<annos.size(); i++) { MAnnotation anno = (MAnnotation)annos.get(i); if (!anno.getSource().toLowerCase().endsWith("table")) { List details = anno.getDetails(); if (details != null) { for (int j = 0; j < details.size(); j++) { MAnnotationDetail detail = (MAnnotationDetail) details .get(j); String key = detail.getKey().toLowerCase(); String value = detail.getValue(); // TODO: remove old imports handling if ("imports".equals(key)) { StringTokenizer stok = new StringTokenizer( value, LIST_ELEMENT_DELIMITER); for (int k = 0; stok.hasMoreElements(); k++) { String imp = stok.nextToken().trim(); if (imp.length() > 0) model.addImport(imp); } // System.out.println("Imports: "+SUtil.arrayToString(imps)); } else // todo: remove old imports until here if ("package".equals(key)) { model.setPackage(value); // System.out.println("Package: "+value); } } } } } for(int i=0; i<annos.size(); i++) { MAnnotation anno = (MAnnotation)annos.get(i); // new jadex arguments handling if(anno.getSource().toLowerCase().endsWith("_arguments_table")) { BpmnMultiColumTable table = parseBpmnMultiColumTable(anno .getDetails()); for (int row = 0; row < table.dimension[0]; row++) { // normal argument has 6 values assert table.data[row].length == 6; String name = table.data[row][0]; String isarg = table.data[row][1]; String isres = table.data[row][2]; String desc = table.data[row][3]; String typename = table.data[row][4]; String val = table.data[row][5] != "" ? table.data[row][5] : null; IParsedExpression exp = null; // context variable Class clazz = SReflect.findClass0(typename, model.getAllImports(), context.getClassLoader()); if(clazz!=null) { if(val!=null && val.length()>0) { exp = parser.parseExpression(val, model.getAllImports(), null, context.getClassLoader()); } model.addContextVariable(name, clazz, exp); // System.out.println("Context variable: "+name); } IArgument arg = null; if(isarg!=null && Boolean.parseBoolean(isarg)) { Object argval = null; try { argval = exp!=null ? exp.getValue(null) : null; } catch(RuntimeException e) { // Hack!!! initial value for context variable might not be accessible statically. } arg = new Argument(name, desc, typename, argval); model.addArgument(arg); } if(isres!=null && Boolean.parseBoolean(isres)) { if(arg==null) { Object argval = null; try { argval = exp!=null ? exp.getValue(null) : null; } catch(RuntimeException e) { // Hack!!! initial value for context variable might not be accessible statically. } arg = new Argument(name, desc, typename, argval); } model.addResult(arg); } // System.out.println("Argument: "+arg); } // next annotation //continue; } // end new arguments handling else if (!anno.getSource().toLowerCase().endsWith("table")) { // handle other annotation details here List details = anno.getDetails(); if(details!=null) { for(int j=0; j<details.size(); j++) { MAnnotationDetail detail = (MAnnotationDetail)details.get(j); String key = detail.getKey().toLowerCase(); String value = detail.getValue(); if("editor_version".equals(key)) { // currently ignored // todo: maybe add a version attribute to the jadex model? } else if("description".equals(key)) { model.setDescription(value); } else if("parameters".equals(key)) { throw new RuntimeException("parameters no longer separately"); } // TODO: remove old arguments handling else if("arguments".equals(key)) { StringTokenizer stok = new StringTokenizer(value, LIST_ELEMENT_DELIMITER); while(stok.hasMoreTokens()) { String argtext = stok.nextToken(); StringTokenizer stok2 = new StringTokenizer(argtext, LIST_ELEMENT_ATTRIBUTE_DELIMITER); String name = stok2.nextToken(); String isarg = stok2.nextToken(); String isres = stok2.nextToken(); String desc = stok2.nextToken(); String typename = stok2.nextToken(); String val = stok2.hasMoreTokens()? stok2.nextToken(): null; IParsedExpression exp = null; // context variable Class clazz = SReflect.findClass0(typename, model.getAllImports(), context.getClassLoader()); if(clazz!=null) { if(val!=null && val.length()>0) { exp = parser.parseExpression(val, model.getAllImports(), null, context.getClassLoader()); } model.addContextVariable(name, clazz, exp); // System.out.println("Context variable: "+name); } IArgument arg = null; if(isarg!=null && Boolean.parseBoolean(isarg)) { Object argval = null; try { argval = exp!=null ? exp.getValue(null) : null; } catch(RuntimeException e) { // Hack!!! initial value for context variable might not be accessible statically. } arg = new Argument(name, desc, typename, argval); model.addArgument(arg); } if(isres!=null && Boolean.parseBoolean(isres)) { if(arg==null) { Object argval = null; try { argval = exp!=null ? exp.getValue(null) : null; } catch(RuntimeException e) { // Hack!!! initial value for context variable might not be accessible statically. } arg = new Argument(name, desc, typename, argval); } model.addResult(arg); } // System.out.println("Argument: "+arg); } } else if("results".equals(key)) { throw new RuntimeException("results no longer separately"); } // TODO: remove old properties handling else if("properties".equals(key)) { StringTokenizer stok = new StringTokenizer(value, LIST_ELEMENT_DELIMITER); while(stok.hasMoreTokens()) { String proptext = stok.nextToken(); StringTokenizer stok2 = new StringTokenizer(proptext, LIST_ELEMENT_ATTRIBUTE_DELIMITER); String name = stok2.nextToken(); String val = stok2.hasMoreTokens()? stok2.nextToken(): null; IParsedExpression exp = null; if(val!=null && val.length()>0) { exp = parser.parseExpression(val, model.getAllImports(), null, context.getClassLoader()); try { Object propval = exp!=null ? exp.getValue(null) : null; model.addProperty(name, propval); } catch(RuntimeException e) { e.printStackTrace(); } } } } else if(!"package".equals(key) && !"imports".equals(key)) { throw new RuntimeException("Error parsing annotation: "+key+", "+value); } } } } } } // Read information from artifact text (normal stp modeller) List arts = model.getArtifacts(); if(arts!=null) { if(arts.size()>1) throw new RuntimeException("Diagram must have one artifact for imports/package"); String desc = ((MArtifact)arts.get(0)).getDescription(); StringTokenizer stok = new StringTokenizer(desc, "\r\n"); while(stok.hasMoreTokens()) { String prop = stok.nextToken().trim(); if(prop.endsWith(";")) prop = prop.substring(0, prop.length()-1); if(prop.startsWith("package")) { String packagename = prop.substring(prop.indexOf("package")+8).trim(); model.setPackage(packagename); } else if(prop.startsWith("import")) { String imp = prop.substring(prop.indexOf("imports")+7).trim(); model.addImport(imp); } else if(prop.startsWith("argument")) { String argstr = prop.substring(prop.indexOf("argument")+8).trim(); try { IArgument arg = (IArgument)parser.parseExpression(argstr, model.getAllImports(), null, context.getClassLoader()).getValue(null); model.addArgument(arg); // Hack!!! Add context variable for argument too. model.addContextVariable(arg.getName(), SReflect.findClass(arg.getTypename(), model.getAllImports(), context.getClassLoader()), null); } catch(Exception e) { throw new RuntimeException("Error parsing argument: "+model+", "+argstr, e); } } else if(prop.startsWith("result")) { String resstr = prop.substring(prop.indexOf("result")+6).trim(); try { IArgument res = (IArgument)parser.parseExpression(resstr, model.getAllImports(), null, context.getClassLoader()).getValue(null); model.addResult(res); // Hack!!! Add context variable for result too. model.addContextVariable(res.getName(), SReflect.findClass(res.getTypename(), model.getAllImports(), context.getClassLoader()), null); } catch(Exception e) { throw new RuntimeException("Error parsing result: "+model+", "+resstr, e); } } else { // context variable String init = null; int idx = prop.indexOf("="); if(idx!=-1) { init = prop.substring(idx+1); prop = prop.substring(0, idx); } StringTokenizer stok2 = new StringTokenizer(prop, " \t"); if(stok2.countTokens()==2) { String clazzname = stok2.nextToken(); Class clazz = SReflect.findClass0(clazzname, model.getAllImports(), context.getClassLoader()); if(clazz!=null) { String name = stok2.nextToken(); IParsedExpression exp = null; if(init!=null) { exp = parser.parseExpression(init, model.getAllImports(), null, context.getClassLoader()); } model.addContextVariable(name, clazz, exp); } } } } } // Create configurations for each pool. // todo: currently each pool is a config // one could allow arbitrary definitions of configs // Argument[] args = (Argument[])model.getModelInfo().getArguments(); // List pools = model.getPools(); // for(int i=0; i<pools.size(); i++) // { // MPool pool = (MPool)pools.get(i); // for(int j=0; j<args.length; ) // MConfiguration config = new MConfiguration(); // config.addPool(pool); // config.setName(pool.getName()); // model.addC // } return null; } /** * Get the pass number. * @return The pass number. */ public int getPass() { return 1; } } // ---- Helper method for various post IPostProcessor ---- /** * Parse a list of annotation details into an BpmnMultiColumnTable * @param details * @return BpmnMultiColumTable from details */ public static BpmnMultiColumTable parseBpmnMultiColumTable(List details) { BpmnMultiColumTable table = null; // initialize the table for(int j=0; j<details.size() && table == null; j++) { MAnnotationDetail detail = (MAnnotationDetail)details.get(j); String key = detail.getKey().toLowerCase(); String value = detail.getValue(); if("dimension".equals(key)) { table = new BpmnMultiColumTable(value); } } assert table != null; for (int j = 0; j < details.size(); j++) { MAnnotationDetail detail = (MAnnotationDetail) details .get(j); String key = detail.getKey().toLowerCase(); String value = detail.getValue(); if ("dimension".equals(key) || "uniquecolumnindex".equals(key)) { continue; } table.setCellValue(key, value); } return table; } } //---- BpmnMultiColumTable ---- class BpmnMultiColumTable { /** The table dimension [x, y] */ int[] dimension; /** The table data */ String[][] data; /** * Create a BpmnMultiColumnTable with given dimension * @param tableDimension */ public BpmnMultiColumTable(String tableDimension) { super(); this.dimension = parseCellIndex(tableDimension); this.data = new String[dimension[0]][dimension[1]]; } /** * Parse a cell index string to an int[] for 2-dimensional table array * @param index * @return int[]{row, column} */ private int[] parseCellIndex(String index) { assert index != null; String[] xy = index.split(":"); if (xy.length == 2) { try { return new int[] { Integer.parseInt(xy[0]), Integer.parseInt(xy[1]) }; } catch (NumberFormatException nfe) { /*ignore*/ } } return new int[]{-1, -1}; } /** * Set the value at given index in data[][] * @param cellIndex * @param value */ public void setCellValue(String cellIndex, String value) { int[] index = parseCellIndex(cellIndex); assert index[0] != -1 && index[1] != -1; assert index[0] < data.length; assert index[1] < data[index[0]].length; data[index[0]][index[1]] = value; } /** * Get the value at given index * @param cellIndex * @return the value at index from data[][] or null */ public String getCellValue(String cellIndex) { int[] index = parseCellIndex(cellIndex); assert index[0] != -1 && index[1] != -1; assert index[0] < data.length; assert index[1] < data[index[0]].length; return data[index[0]][index[1]]; } }