/** * Copyright 2010 JBoss Inc * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.drools.bpmn2.legacy.beta1; import java.util.ArrayList; import java.util.Collection; import java.util.List; import org.drools.bpmn2.xpath.XPathDialect; import org.drools.compiler.xml.XmlDumper; import org.drools.definition.process.Connection; import org.drools.definition.process.Node; import org.drools.definition.process.NodeContainer; import org.drools.definition.process.WorkflowProcess; import org.drools.process.core.ContextContainer; import org.drools.process.core.Work; import org.drools.process.core.context.swimlane.Swimlane; import org.drools.process.core.context.swimlane.SwimlaneContext; import org.drools.process.core.context.variable.Variable; import org.drools.process.core.context.variable.VariableScope; import org.drools.process.core.datatype.impl.type.ObjectDataType; import org.drools.process.core.event.EventFilter; import org.drools.process.core.event.EventTypeFilter; import org.drools.rule.builder.dialect.java.JavaDialect; import org.drools.workflow.core.Constraint; import org.drools.workflow.core.node.ActionNode; import org.drools.workflow.core.node.CompositeNode; import org.drools.workflow.core.node.EndNode; import org.drools.workflow.core.node.EventNode; import org.drools.workflow.core.node.EventTrigger; import org.drools.workflow.core.node.FaultNode; import org.drools.workflow.core.node.ForEachNode; import org.drools.workflow.core.node.HumanTaskNode; import org.drools.workflow.core.node.Join; import org.drools.workflow.core.node.RuleSetNode; import org.drools.workflow.core.node.Split; import org.drools.workflow.core.node.StartNode; import org.drools.workflow.core.node.SubProcessNode; import org.drools.workflow.core.node.TimerNode; import org.drools.workflow.core.node.Trigger; import org.drools.workflow.core.node.WorkItemNode; import org.drools.xml.Handler; import org.drools.xml.SemanticModule; public class XmlBPMNProcessDumper { public static final String JAVA_LANGUAGE = "http://www.java.com/java"; public static final String RULE_LANGUAGE = "http://www.jboss.org/drools/rule"; public static final String XPATH_LANGUAGE = "http://www.w3.org/1999/XPath"; public static XmlBPMNProcessDumper INSTANCE = new XmlBPMNProcessDumper(); private final static String EOL = System.getProperty( "line.separator" ); private SemanticModule semanticModule; private XmlBPMNProcessDumper() { semanticModule = new BPMNSemanticModule(); } public String dump(WorkflowProcess process) { return dump(process, true); } public String dump(WorkflowProcess process, boolean includeMeta) { StringBuilder xmlDump = new StringBuilder(); visitProcess(process, xmlDump, includeMeta); return xmlDump.toString(); } protected void visitProcess(WorkflowProcess process, StringBuilder xmlDump, boolean includeMeta) { String targetNamespace = (String) process.getMetaData().get("TargetNamespace"); if (targetNamespace == null) { targetNamespace = "http://www.jboss.org/drools"; } xmlDump.append( "<?xml version=\"1.0\" encoding=\"UTF-8\"?> " + EOL + "<definitions id=\"Definition\"" + EOL + " targetNamespace=\"" + targetNamespace + "\"" + EOL + " typeLanguage=\"http://www.java.com/javaTypes\"" + EOL + " expressionLanguage=\"http://www.mvel.org/2.0\"" + EOL + " xmlns=\"http://schema.omg.org/spec/BPMN/2.0\"" + EOL + " xmlns:xs=\"http://www.w3.org/2001/XMLSchema-instance\"" + EOL + " xs:schemaLocation=\"http://schema.omg.org/spec/BPMN/2.0 BPMN20.xsd\"" + EOL + " xmlns:di=\"http://bpmndi.org\"" + EOL + " xmlns:tns=\"http://www.jboss.org/drools\">" + EOL + EOL); // item definitions VariableScope variableScope = (VariableScope) ((org.drools.process.core.Process) process).getDefaultContext(VariableScope.VARIABLE_SCOPE); visitVariableScope(variableScope, "_", xmlDump); visitSubVariableScopes(process.getNodes(), xmlDump); xmlDump.append( " <resource id=\"Actor\" name=\"Human Actor\" />" + EOL + EOL); visitInterfaces(process, xmlDump); // the process itself xmlDump.append(" <process processType=\"executable\" "); if (process.getId() != null) { xmlDump.append("id=\"" + XmlDumper.replaceIllegalChars(process.getId()) + "\" "); } if (process.getName() != null) { xmlDump.append("name=\"" + XmlDumper.replaceIllegalChars(process.getName()) + "\" "); } String packageName = process.getPackageName(); if (packageName != null && !"org.drools.bpmn2".equals(packageName)) { xmlDump.append("tns:packageName=\"" + XmlDumper.replaceIllegalChars(packageName) + "\" "); } // TODO: package, version xmlDump.append(">" + EOL + EOL); visitLanes(process, xmlDump); visitHeader(process, xmlDump, includeMeta); visitNodes(process, xmlDump, includeMeta); visitConnections(process.getNodes(), xmlDump, includeMeta); xmlDump.append(" </process>" + EOL + EOL); if (includeMeta) { xmlDump.append( " <di:processDiagram processRef=\"" + XmlDumper.replaceIllegalChars(process.getId()) + "\" >" + EOL + " <di:laneCompartment isVisible=\"false\" >" + EOL); for (Node node: process.getNodes()) { String nodeType = null; Integer x = (Integer) node.getMetaData().get("x"); Integer y = (Integer) node.getMetaData().get("y"); Integer width = (Integer) node.getMetaData().get("width"); Integer height = (Integer) node.getMetaData().get("height"); if (node instanceof StartNode || node instanceof EndNode || node instanceof EventNode || node instanceof FaultNode) { nodeType = "event"; int offsetX = (int) ((width - 30) / 2); width = 30; x = x + offsetX; int offsetY = (int) ((height - 30) / 2); y = y + offsetY; height = 30; } else if (node instanceof ActionNode || node instanceof RuleSetNode || node instanceof TimerNode) { nodeType = "activity"; } else if (node instanceof Join || node instanceof Split) { nodeType = "gateway"; int offsetX = (int) ((width - 40) / 2); width = 40; x = x + offsetX; int offsetY = (int) ((height - 40) / 2); y = y + offsetY; height = 40; } else if (node instanceof SubProcessNode) { nodeType = "calledSubprocess"; } else if (node instanceof CompositeNode) { nodeType = "subprocess"; } else if (node instanceof WorkItemNode) { nodeType = "activity"; } xmlDump.append( " <di:" + nodeType + "Shape id=\"" + getUniqueNodeId(node) + "_gui\" " + nodeType + "Ref=\"" + getUniqueNodeId(node) + "\" " + "x=\"" + x + "\" " + "y=\"" + y + "\" " + "width=\"" + width + "\" " + "height=\"" + height + "\" " + " />" + EOL); } xmlDump.append( " </di:laneCompartment>" + EOL); visitConnectionsDi(process.getNodes(), xmlDump); xmlDump.append( " </di:processDiagram>" + EOL + EOL); } xmlDump.append("</definitions>"); } private void visitVariableScope(VariableScope variableScope, String prefix, StringBuilder xmlDump) { if (variableScope != null && !variableScope.getVariables().isEmpty()) { for (Variable variable: variableScope.getVariables()) { xmlDump.append( " <itemDefinition id=\"" + XmlDumper.replaceIllegalChars(prefix + variable.getName()) + "Item\" "); if (variable.getType() != null) { xmlDump.append("structureRef=\"" + XmlDumper.replaceIllegalChars(variable.getType().getStringType()) + "\" "); } xmlDump.append("/>" + EOL); } xmlDump.append(EOL); } } private void visitSubVariableScopes(Node[] nodes, StringBuilder xmlDump) { for (Node node: nodes) { if (node instanceof ContextContainer) { VariableScope variableScope = (VariableScope) ((ContextContainer) node).getDefaultContext(VariableScope.VARIABLE_SCOPE); if (variableScope != null) { visitVariableScope(variableScope, XmlBPMNProcessDumper.getUniqueNodeId(node) + "-", xmlDump); } } if (node instanceof NodeContainer) { visitSubVariableScopes(((NodeContainer) node).getNodes(), xmlDump); } } } private void visitLanes(WorkflowProcess process, StringBuilder xmlDump) { // lanes Collection<Swimlane> swimlanes = ((SwimlaneContext) ((org.drools.workflow.core.WorkflowProcess) process) .getDefaultContext(SwimlaneContext.SWIMLANE_SCOPE)).getSwimlanes(); if (!swimlanes.isEmpty()) { xmlDump.append(" <laneSet>" + EOL); for (Swimlane swimlane: swimlanes) { xmlDump.append(" <lane name=\"" + XmlDumper.replaceIllegalChars(swimlane.getName()) + "\" >" + EOL); visitLane(process, swimlane.getName(), xmlDump); xmlDump.append(" </lane>" + EOL); } xmlDump.append(" </laneSet>" + EOL); } } private void visitLane(NodeContainer container, String lane, StringBuilder xmlDump) { for (Node node: container.getNodes()) { if (node instanceof HumanTaskNode) { String swimlane = ((HumanTaskNode) node).getSwimlane(); if (lane.equals(swimlane)) { xmlDump.append(" <flowElementRef>" + XmlBPMNProcessDumper.getUniqueNodeId(node) + "</flowElementRef>" + EOL); } } else { String swimlane = (String) node.getMetaData().get("Lane"); if (lane.equals(swimlane)) { xmlDump.append(" <flowElementRef>" + XmlBPMNProcessDumper.getUniqueNodeId(node) + "</flowElementRef>" + EOL); } } if (node instanceof NodeContainer) { visitLane((NodeContainer) node, lane, xmlDump); } } } protected void visitHeader(WorkflowProcess process, StringBuilder xmlDump, boolean includeMeta) { // TODO: imports, function imports // TODO: globals // TODO: swimlanes // TODO: exception handlers VariableScope variableScope = (VariableScope) ((org.drools.process.core.Process) process).getDefaultContext(VariableScope.VARIABLE_SCOPE); if (variableScope != null) { visitVariables(variableScope.getVariables(), xmlDump); } } public static void visitVariables(List<Variable> variables, StringBuilder xmlDump) { if (!variables.isEmpty()) { xmlDump.append(" <!-- process variables -->" + EOL); for (Variable variable: variables) { if (variable.getMetaData("DataObject") == null) { xmlDump.append(" <property id=\"" + XmlDumper.replaceIllegalChars(variable.getName()) + "\" "); if (variable.getType() != null) { xmlDump.append("itemSubjectRef=\"_" + XmlDumper.replaceIllegalChars(variable.getName()) + "Item\"" ); } // TODO: value? xmlDump.append("/>" + EOL); } } for (Variable variable: variables) { if (variable.getMetaData("DataObject") != null) { xmlDump.append(" <dataObject id=\"" + XmlDumper.replaceIllegalChars(variable.getName()) + "\" "); if (variable.getType() != null) { xmlDump.append("itemSubjectRef=\"_" + XmlDumper.replaceIllegalChars(variable.getName()) + "Item\"" ); } // TODO: value? xmlDump.append("/>" + EOL); } } xmlDump.append(EOL); } } protected void visitInterfaces(WorkflowProcess process, StringBuilder xmlDump) { for (Node node: process.getNodes()) { if (node instanceof WorkItemNode) { Work work = ((WorkItemNode) node).getWork(); if (work != null) { if ("Service Task".equals(work.getName())) { String interfaceName = (String) work.getParameter("Interface"); if (interfaceName == null) { interfaceName = ""; } String operationName = (String) work.getParameter("Operation"); if (operationName == null) { operationName = ""; } String parameterType = (String) work.getParameter("ParameterType"); if (parameterType == null) { parameterType = ""; } xmlDump.append( " <itemDefinition id=\"" + getUniqueNodeId(node) + "_InMessageType\" structureRef=\"" + parameterType + "\"/>" + EOL + " <message id=\"" + getUniqueNodeId(node) + "_InMessage\" structureRef=\"" + getUniqueNodeId(node) + "_InMessageType\" />" + EOL + " <interface id=\"" + getUniqueNodeId(node) + "_ServiceInterface\" name=\"" + interfaceName + "\">" + EOL + " <operation id=\"" + getUniqueNodeId(node) + "_ServiceOperation\" name=\"" + operationName + "\">" + EOL + " <inMessageRef>" + getUniqueNodeId(node) + "_InMessage</inMessageRef>" + EOL + " </operation>" + EOL + " </interface>" + EOL + EOL); } else if ("Send Task".equals(work.getName())) { String messageType = (String) work.getParameter("MessageType"); if (messageType == null) { messageType = ""; } xmlDump.append( " <itemDefinition id=\"" + getUniqueNodeId(node) + "_MessageType\" structureRef=\"" + XmlDumper.replaceIllegalChars(messageType) + "\"/>" + EOL + " <message id=\"" + getUniqueNodeId(node) + "_Message\" structureRef=\"" + getUniqueNodeId(node) + "_MessageType\" />" + EOL + EOL); } else if ("Receive Task".equals(work.getName())) { String messageType = (String) work.getParameter("MessageType"); if (messageType == null) { messageType = ""; } xmlDump.append( " <itemDefinition id=\"" + getUniqueNodeId(node) + "_MessageType\" structureRef=\"" + XmlDumper.replaceIllegalChars(messageType) + "\"/>" + EOL + " <message id=\"" + getUniqueNodeId(node) + "_Message\" structureRef=\"" + getUniqueNodeId(node) + "_MessageType\" />" + EOL + EOL); } } } else if (node instanceof EndNode) { String messageType = (String) node.getMetaData().get("MessageType"); if (messageType != null) { xmlDump.append( " <itemDefinition id=\"" + getUniqueNodeId(node) + "_MessageType\" structureRef=\"" + XmlDumper.replaceIllegalChars(messageType) + "\"/>" + EOL + " <message id=\"" + getUniqueNodeId(node) + "_Message\" structureRef=\"" + getUniqueNodeId(node) + "_MessageType\" />" + EOL + EOL); } } else if (node instanceof ActionNode) { String messageType = (String) node.getMetaData().get("MessageType"); if (messageType != null) { xmlDump.append( " <itemDefinition id=\"" + getUniqueNodeId(node) + "_MessageType\" structureRef=\"" + XmlDumper.replaceIllegalChars(messageType) + "\"/>" + EOL + " <message id=\"" + getUniqueNodeId(node) + "_Message\" structureRef=\"" + getUniqueNodeId(node) + "_MessageType\" />" + EOL + EOL); } } else if (node instanceof EventNode) { List<EventFilter> filters = ((EventNode) node).getEventFilters(); if (filters.size() > 0) { String messageRef = ((EventTypeFilter) filters.get(0)).getType(); messageRef = messageRef.substring(8); String messageType = (String) node.getMetaData().get("MessageType"); xmlDump.append( " <itemDefinition id=\"" + XmlDumper.replaceIllegalChars(messageRef) + "Type\" structureRef=\"" + XmlDumper.replaceIllegalChars(messageType) + "\"/>" + EOL + " <message id=\"" + XmlDumper.replaceIllegalChars(messageRef) + "\" structureRef=\"" + XmlDumper.replaceIllegalChars(messageRef) + "Type\" />" + EOL + EOL); } } else if (node instanceof StartNode) { StartNode startNode = (StartNode) node; if (startNode.getTriggers() != null && !startNode.getTriggers().isEmpty()) { Trigger trigger = startNode.getTriggers().get(0); if (trigger instanceof EventTrigger) { String eventType = ((EventTypeFilter) ((EventTrigger) trigger).getEventFilters().get(0)).getType(); if (eventType.startsWith("Message-")) { eventType = eventType.substring(8); String messageType = (String) node.getMetaData().get("MessageType"); xmlDump.append( " <itemDefinition id=\"" + XmlDumper.replaceIllegalChars(eventType) + "Type\" structureRef=\"" + XmlDumper.replaceIllegalChars(messageType) + "\"/>" + EOL + " <message id=\"" + XmlDumper.replaceIllegalChars(eventType) + "\" structureRef=\"" + XmlDumper.replaceIllegalChars(eventType) + "Type\" />" + EOL + EOL); } } } } else if (node instanceof ForEachNode) { ForEachNode forEachNode = (ForEachNode) node; xmlDump.append( " <itemDefinition id=\"" + XmlBPMNProcessDumper.getUniqueNodeId(forEachNode) + "_multiInstanceItemType\" structureRef=\"" + XmlDumper.replaceIllegalChars(((ObjectDataType) forEachNode.getVariableType()).getClassName()) + "\"/>" + EOL + EOL); } } } private void visitNodes(WorkflowProcess process, StringBuilder xmlDump, boolean includeMeta) { xmlDump.append(" <!-- nodes -->" + EOL); for (Node node: process.getNodes()) { visitNode(node, xmlDump, includeMeta); } xmlDump.append(EOL); } public void visitNode(Node node, StringBuilder xmlDump, boolean includeMeta) { Handler handler = semanticModule.getHandlerByClass(node.getClass()); if (handler != null) { ((AbstractNodeHandler) handler).writeNode((org.drools.workflow.core.Node) node, xmlDump, includeMeta); } else { throw new IllegalArgumentException( "Unknown node type: " + node); } } private void visitConnections(Node[] nodes, StringBuilder xmlDump, boolean includeMeta) { xmlDump.append(" <!-- connections -->" + EOL); List<Connection> connections = new ArrayList<Connection>(); for (Node node: nodes) { for (List<Connection> connectionList: node.getIncomingConnections().values()) { connections.addAll(connectionList); } } for (Connection connection: connections) { visitConnection(connection, xmlDump, includeMeta); } xmlDump.append(EOL); } public void visitConnection(Connection connection, StringBuilder xmlDump, boolean includeMeta) { xmlDump.append(" <sequenceFlow id=\"" + getUniqueNodeId(connection.getFrom()) + "-" + getUniqueNodeId(connection.getTo()) + "\" sourceRef=\"" + getUniqueNodeId(connection.getFrom()) + "\" "); // TODO fromType, toType xmlDump.append("targetRef=\"" + getUniqueNodeId(connection.getTo()) + "\" "); // if (includeMeta) { // String bendpoints = (String) connection.getMetaData("bendpoints"); // if (bendpoints != null) { // xmlDump.append("g:bendpoints=\"" + bendpoints + "\" "); // } // } if (connection.getFrom() instanceof Split) { Split split = (Split) connection.getFrom(); if (split.getType() == Split.TYPE_XOR || split.getType() == Split.TYPE_OR) { Constraint constraint = split.getConstraint(connection); if (constraint == null) { xmlDump.append(">" + EOL + " <conditionExpression xs:type=\"tFormalExpression\" />"); } else { if (constraint.getName() != null && constraint.getName().trim().length() > 0) { xmlDump.append("name=\"" + XmlDumper.replaceIllegalChars(constraint.getName()) + "\" "); } xmlDump.append(">" + EOL + " <conditionExpression xs:type=\"tFormalExpression\" "); if ("code".equals(constraint.getType())) { if (JavaDialect.ID.equals(constraint.getDialect())) { xmlDump.append("language=\"" + JAVA_LANGUAGE + "\" "); } else if (XPathDialect.ID.equals(constraint.getDialect())) { xmlDump.append("language=\"" + XPATH_LANGUAGE + "\" "); } } else { xmlDump.append("language=\"" + RULE_LANGUAGE + "\" "); } String constraintString = constraint.getConstraint(); if (constraintString == null) { constraintString = ""; } xmlDump.append(">" + XmlDumper.replaceIllegalChars(constraintString) + "</conditionExpression>"); } xmlDump.append(EOL + " </sequenceFlow>" + EOL); } else { xmlDump.append("/>" + EOL); } } else { xmlDump.append("/>" + EOL); } } public static String getUniqueNodeId(Node node) { String result = (String) node.getMetaData().get("UniqueId"); if (result != null) { return result; } result = node.getId() + ""; NodeContainer nodeContainer = node.getNodeContainer(); while (nodeContainer instanceof CompositeNode) { CompositeNode composite = (CompositeNode) nodeContainer; result = composite.getId() + "-" + result; nodeContainer = composite.getNodeContainer(); } return "_" + result; } private void visitConnectionsDi(Node[] nodes, StringBuilder xmlDump) { List<Connection> connections = new ArrayList<Connection>(); for (Node node: nodes) { for (List<Connection> connectionList: node.getIncomingConnections().values()) { connections.addAll(connectionList); } if (node instanceof CompositeNode) { visitConnectionsDi(((CompositeNode) node).getNodes(), xmlDump); } } for (Connection connection: connections) { String bendpoints = (String) connection.getMetaData().get("bendpoints"); xmlDump.append( " <di:sequenceFlowConnector sequenceFlowRef=\"" + getUniqueNodeId(connection.getFrom()) + "-" + getUniqueNodeId(connection.getTo()) + "\" " + "sourceRef=\"" + getUniqueNodeId(connection.getFrom()) + "_gui\" " + "targetRef=\"" + getUniqueNodeId(connection.getTo()) + "_gui\" "); if (bendpoints == null) { xmlDump.append("/>" + EOL); } else { xmlDump.append(">" + EOL); bendpoints = bendpoints.substring(1, bendpoints.length() - 1); String[] points = bendpoints.split(";"); for (String point: points) { String[] coords = point.split(","); if (coords.length == 2) { xmlDump.append( " <di:bendpoint x=\"" + coords[0] + "\" y=\"" + coords[1] + "\" />" + EOL); } } xmlDump.append( " </di:sequenceFlowConnector>" + EOL); } } } }