/* 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.camunda.bpm.model.bpmn.builder; import java.util.Collection; import java.util.Iterator; import org.camunda.bpm.model.bpmn.BpmnModelException; import org.camunda.bpm.model.bpmn.BpmnModelInstance; import org.camunda.bpm.model.bpmn.instance.Activity; import org.camunda.bpm.model.bpmn.instance.BaseElement; import org.camunda.bpm.model.bpmn.instance.BpmnModelElementInstance; import org.camunda.bpm.model.bpmn.instance.CompensateEventDefinition; import org.camunda.bpm.model.bpmn.instance.Definitions; import org.camunda.bpm.model.bpmn.instance.Error; import org.camunda.bpm.model.bpmn.instance.ErrorEventDefinition; import org.camunda.bpm.model.bpmn.instance.Escalation; import org.camunda.bpm.model.bpmn.instance.EscalationEventDefinition; import org.camunda.bpm.model.bpmn.instance.Event; import org.camunda.bpm.model.bpmn.instance.ExtensionElements; import org.camunda.bpm.model.bpmn.instance.FlowNode; import org.camunda.bpm.model.bpmn.instance.Gateway; import org.camunda.bpm.model.bpmn.instance.Message; import org.camunda.bpm.model.bpmn.instance.MessageEventDefinition; import org.camunda.bpm.model.bpmn.instance.SequenceFlow; import org.camunda.bpm.model.bpmn.instance.Signal; import org.camunda.bpm.model.bpmn.instance.SignalEventDefinition; import org.camunda.bpm.model.bpmn.instance.SubProcess; import org.camunda.bpm.model.bpmn.instance.bpmndi.BpmnEdge; import org.camunda.bpm.model.bpmn.instance.bpmndi.BpmnPlane; import org.camunda.bpm.model.bpmn.instance.bpmndi.BpmnShape; import org.camunda.bpm.model.bpmn.instance.dc.Bounds; import org.camunda.bpm.model.bpmn.instance.di.Waypoint; import org.camunda.bpm.model.xml.instance.ModelElementInstance; /** * @author Sebastian Menski */ public abstract class AbstractBaseElementBuilder<B extends AbstractBaseElementBuilder<B, E>, E extends BaseElement> extends AbstractBpmnModelElementBuilder<B, E> { public static final double SPACE = 50; protected AbstractBaseElementBuilder(BpmnModelInstance modelInstance, E element, Class<?> selfType) { super(modelInstance, element, selfType); } protected <T extends BpmnModelElementInstance> T createInstance(Class<T> typeClass) { return modelInstance.newInstance(typeClass); } protected <T extends BaseElement> T createInstance(Class<T> typeClass, String identifier) { T instance = createInstance(typeClass); if (identifier != null) { instance.setId(identifier); } return instance; } protected <T extends BpmnModelElementInstance> T createChild(Class<T> typeClass) { return createChild(element, typeClass); } protected <T extends BaseElement> T createChild(Class<T> typeClass, String identifier) { return createChild(element, typeClass, identifier); } protected <T extends BpmnModelElementInstance> T createChild(BpmnModelElementInstance parent, Class<T> typeClass) { T instance = createInstance(typeClass); parent.addChildElement(instance); return instance; } protected <T extends BaseElement> T createChild(BpmnModelElementInstance parent, Class<T> typeClass, String identifier) { T instance = createInstance(typeClass, identifier); parent.addChildElement(instance); return instance; } protected <T extends BpmnModelElementInstance> T createSibling(Class<T> typeClass) { T instance = createInstance(typeClass); element.getParentElement().addChildElement(instance); return instance; } protected <T extends BaseElement> T createSibling(Class<T> typeClass, String identifier) { T instance = createInstance(typeClass, identifier); element.getParentElement().addChildElement(instance); return instance; } protected <T extends BpmnModelElementInstance> T getCreateSingleChild(Class<T> typeClass) { return getCreateSingleChild(element, typeClass); } protected <T extends BpmnModelElementInstance> T getCreateSingleChild(BpmnModelElementInstance parent, Class<T> typeClass) { Collection<T> childrenOfType = parent.getChildElementsByType(typeClass); if (childrenOfType.isEmpty()) { return createChild(parent, typeClass); } else { if (childrenOfType.size() > 1) { throw new BpmnModelException("Element " + parent + " of type " + parent.getElementType().getTypeName() + " has more than one child element of type " + typeClass.getName()); } else { return childrenOfType.iterator().next(); } } } protected <T extends BpmnModelElementInstance> T getCreateSingleExtensionElement(Class<T> typeClass) { ExtensionElements extensionElements = getCreateSingleChild(ExtensionElements.class); return getCreateSingleChild(extensionElements, typeClass); } protected Message findMessageForName(String messageName) { Collection<Message> messages = modelInstance.getModelElementsByType(Message.class); for (Message message : messages) { if (messageName.equals(message.getName())) { // return already existing message for message name return message; } } // create new message for non existing message name Definitions definitions = modelInstance.getDefinitions(); Message message = createChild(definitions, Message.class); message.setName(messageName); return message; } protected MessageEventDefinition createMessageEventDefinition(String messageName) { Message message = findMessageForName(messageName); MessageEventDefinition messageEventDefinition = createInstance(MessageEventDefinition.class); messageEventDefinition.setMessage(message); return messageEventDefinition; } protected MessageEventDefinition createEmptyMessageEventDefinition() { return createInstance(MessageEventDefinition.class); } protected Signal findSignalForName(String signalName) { Collection<Signal> signals = modelInstance.getModelElementsByType(Signal.class); for (Signal signal : signals) { if (signalName.equals(signal.getName())) { // return already existing signal for signal name return signal; } } // create new signal for non existing signal name Definitions definitions = modelInstance.getDefinitions(); Signal signal = createChild(definitions, Signal.class); signal.setName(signalName); return signal; } protected SignalEventDefinition createSignalEventDefinition(String signalName) { Signal signal = findSignalForName(signalName); SignalEventDefinition signalEventDefinition = createInstance(SignalEventDefinition.class); signalEventDefinition.setSignal(signal); return signalEventDefinition; } protected ErrorEventDefinition findErrorDefinitionForCode(String errorCode) { Collection<ErrorEventDefinition> definitions = modelInstance.getModelElementsByType(ErrorEventDefinition.class); for(ErrorEventDefinition definition: definitions) { Error error = definition.getError(); if(error != null && error.getErrorCode().equals(errorCode)) { return definition; } } return null; } protected Error findErrorForNameAndCode(String errorCode) { Collection<Error> errors = modelInstance.getModelElementsByType(Error.class); for (Error error : errors) { if (errorCode.equals(error.getErrorCode())) { // return already existing error return error; } } // create new error Definitions definitions = modelInstance.getDefinitions(); Error error = createChild(definitions, Error.class); error.setErrorCode(errorCode); return error; } protected ErrorEventDefinition createEmptyErrorEventDefinition() { ErrorEventDefinition errorEventDefinition = createInstance(ErrorEventDefinition.class); return errorEventDefinition; } protected ErrorEventDefinition createErrorEventDefinition(String errorCode) { Error error = findErrorForNameAndCode(errorCode); ErrorEventDefinition errorEventDefinition = createInstance(ErrorEventDefinition.class); errorEventDefinition.setError(error); return errorEventDefinition; } protected Escalation findEscalationForCode(String escalationCode) { Collection<Escalation> escalations = modelInstance.getModelElementsByType(Escalation.class); for (Escalation escalation : escalations) { if (escalationCode.equals(escalation.getEscalationCode())) { // return already existing escalation return escalation; } } Definitions definitions = modelInstance.getDefinitions(); Escalation escalation = createChild(definitions, Escalation.class); escalation.setEscalationCode(escalationCode); return escalation; } protected EscalationEventDefinition createEscalationEventDefinition(String escalationCode) { Escalation escalation = findEscalationForCode(escalationCode); EscalationEventDefinition escalationEventDefinition = createInstance(EscalationEventDefinition.class); escalationEventDefinition.setEscalation(escalation); return escalationEventDefinition; } protected CompensateEventDefinition createCompensateEventDefinition() { CompensateEventDefinition compensateEventDefinition = createInstance(CompensateEventDefinition.class); return compensateEventDefinition; } /** * Sets the identifier of the element. * * @param identifier the identifier to set * @return the builder object */ public B id(String identifier) { element.setId(identifier); return myself; } /** * Add an extension element to the element. * * @param extensionElement the extension element to add * @return the builder object */ public B addExtensionElement(BpmnModelElementInstance extensionElement) { ExtensionElements extensionElements = getCreateSingleChild(ExtensionElements.class); extensionElements.addChildElement(extensionElement); return myself; } public BpmnShape createBpmnShape(FlowNode node) { BpmnPlane bpmnPlane = findBpmnPlane(); if (bpmnPlane != null) { BpmnShape bpmnShape = createInstance(BpmnShape.class); bpmnShape.setBpmnElement(node); Bounds nodeBounds = createInstance(Bounds.class); if (node instanceof SubProcess) { bpmnShape.setExpanded(true); nodeBounds.setWidth(350); nodeBounds.setHeight(200); } else if (node instanceof Activity) { nodeBounds.setWidth(100); nodeBounds.setHeight(80); } else if (node instanceof Event) { nodeBounds.setWidth(36); nodeBounds.setHeight(36); } else if (node instanceof Gateway) { nodeBounds.setWidth(50); nodeBounds.setHeight(50); } nodeBounds.setX(0); nodeBounds.setY(0); bpmnShape.addChildElement(nodeBounds); bpmnPlane.addChildElement(bpmnShape); return bpmnShape; } return null; } protected void setCoordinates(BpmnShape shape) { BpmnShape source = findBpmnShape(element); Bounds shapeBounds = shape.getBounds(); double x = 0; double y = 0; if (source != null) { Bounds sourceBounds = source.getBounds(); double sourceX = sourceBounds.getX(); double sourceWidth = sourceBounds.getWidth(); x = sourceX + sourceWidth + SPACE; if (element instanceof FlowNode) { FlowNode flowNode = (FlowNode) element; Collection<SequenceFlow> outgoing = flowNode.getOutgoing(); if (outgoing.size() == 0) { double sourceY = sourceBounds.getY(); double sourceHeight = sourceBounds.getHeight(); double targetHeight = shapeBounds.getHeight(); y = sourceY + sourceHeight / 2 - targetHeight / 2; } else { SequenceFlow[] sequenceFlows = outgoing.toArray(new SequenceFlow[outgoing.size()]); SequenceFlow last = sequenceFlows[outgoing.size() - 1]; BpmnShape targetShape = findBpmnShape(last.getTarget()); if (targetShape != null) { Bounds targetBounds = targetShape.getBounds(); double lastY = targetBounds.getY(); double lastHeight = targetBounds.getHeight(); y = lastY + lastHeight + SPACE; } } } } shapeBounds.setX(x); shapeBounds.setY(y); } public BpmnEdge createBpmnEdge(SequenceFlow sequenceFlow) { BpmnPlane bpmnPlane = findBpmnPlane(); if (bpmnPlane != null) { BpmnEdge edge = createInstance(BpmnEdge.class); edge.setBpmnElement(sequenceFlow); setWaypoints(edge); bpmnPlane.addChildElement(edge); return edge; } return null; } protected void setWaypoints(BpmnEdge edge) { SequenceFlow sequenceFlow = (SequenceFlow) edge.getBpmnElement(); FlowNode sequenceFlowSource = sequenceFlow.getSource(); FlowNode sequenceFlowTarget = sequenceFlow.getTarget(); BpmnShape source = findBpmnShape(sequenceFlowSource); BpmnShape target = findBpmnShape(sequenceFlowTarget); if (source != null && target != null) { Bounds sourceBounds = source.getBounds(); Bounds targetBounds = target.getBounds(); double sourceX = sourceBounds.getX(); double sourceY = sourceBounds.getY(); double sourceWidth = sourceBounds.getWidth(); double sourceHeight = sourceBounds.getHeight(); double targetX = targetBounds.getX(); double targetY = targetBounds.getY(); double targetHeight = targetBounds.getHeight(); Waypoint w1 = createInstance(Waypoint.class); if (sequenceFlowSource.getOutgoing().size() == 1) { w1.setX(sourceX + sourceWidth); w1.setY(sourceY + sourceHeight / 2); edge.addChildElement(w1); } else { w1.setX(sourceX + sourceWidth / 2); w1.setY(sourceY + sourceHeight); edge.addChildElement(w1); Waypoint w2 = createInstance(Waypoint.class); w2.setX(sourceX + sourceWidth / 2); w2.setY(targetY + targetHeight / 2); edge.addChildElement(w2); } Waypoint w3 = createInstance(Waypoint.class); w3.setX(targetX); w3.setY(targetY + targetHeight / 2); edge.addChildElement(w3); } } protected BpmnPlane findBpmnPlane() { Collection<BpmnPlane> planes = modelInstance.getModelElementsByType(BpmnPlane.class); return planes.iterator().next(); } protected BpmnShape findBpmnShape(BaseElement node) { Collection<BpmnShape> allShapes = modelInstance.getModelElementsByType(BpmnShape.class); Iterator<BpmnShape> iterator = allShapes.iterator(); while (iterator.hasNext()) { BpmnShape shape = iterator.next(); if (shape.getBpmnElement().equals(node)) { return shape; } } return null; } protected BpmnEdge findBpmnEdge(BaseElement sequenceFlow){ Collection<BpmnEdge> allEdges = modelInstance.getModelElementsByType(BpmnEdge.class); Iterator<BpmnEdge> iterator = allEdges.iterator(); while (iterator.hasNext()) { BpmnEdge edge = iterator.next(); if(edge.getBpmnElement().equals(sequenceFlow)) { return edge; } } return null; } protected void resizeSubProcess(BpmnShape innerShape) { BaseElement innerElement = innerShape.getBpmnElement(); Bounds innerShapeBounds = innerShape.getBounds(); ModelElementInstance parent = innerElement.getParentElement(); while (parent instanceof SubProcess) { BpmnShape subProcessShape = findBpmnShape((SubProcess) parent); if (subProcessShape != null) { Bounds subProcessBounds = subProcessShape.getBounds(); double innerX = innerShapeBounds.getX(); double innerWidth = innerShapeBounds.getWidth(); double innerY = innerShapeBounds.getY(); double innerHeight = innerShapeBounds.getHeight(); double subProcessY = subProcessBounds.getY(); double subProcessHeight = subProcessBounds.getHeight(); double subProcessX = subProcessBounds.getX(); double subProcessWidth = subProcessBounds.getWidth(); double tmpWidth = innerX + innerWidth + SPACE; double tmpHeight = innerY + innerHeight + SPACE; if (innerY == subProcessY) { subProcessBounds.setY(subProcessY - SPACE); subProcessBounds.setHeight(subProcessHeight + SPACE); } if (tmpWidth >= subProcessX + subProcessWidth) { double newWidth = tmpWidth - subProcessX; subProcessBounds.setWidth(newWidth); } if (tmpHeight >= subProcessY + subProcessHeight) { double newHeight = tmpHeight - subProcessY; subProcessBounds.setHeight(newHeight); } innerElement = (SubProcess) parent; innerShapeBounds = subProcessBounds; parent = innerElement.getParentElement(); } else { break; } } } }