/* 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.activiti.editor.language.json.converter; import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.logging.Level; import java.util.logging.Logger; import math.geom2d.Point2D; import math.geom2d.conic.Circle2D; import math.geom2d.line.Line2D; import math.geom2d.polygon.Polyline2D; import org.activiti.bpmn.model.Activity; import org.activiti.bpmn.model.BaseElement; import org.activiti.bpmn.model.BoundaryEvent; import org.activiti.bpmn.model.BpmnModel; import org.activiti.bpmn.model.FlowElement; import org.activiti.bpmn.model.GraphicInfo; import org.activiti.bpmn.model.Process; import org.activiti.bpmn.model.SequenceFlow; import org.activiti.bpmn.model.SubProcess; import org.activiti.editor.constants.EditorJsonConstants; import org.activiti.editor.constants.StencilConstants; import org.apache.commons.lang.StringUtils; import org.codehaus.jackson.JsonNode; import org.codehaus.jackson.map.ObjectMapper; import org.codehaus.jackson.node.ArrayNode; import org.codehaus.jackson.node.ObjectNode; /** * @author Tijs Rademakers */ public class BpmnJsonConverter implements EditorJsonConstants, StencilConstants, ActivityProcessor { protected static final Logger LOGGER = Logger.getLogger(BpmnJsonConverter.class.getName()); private ObjectMapper objectMapper = new ObjectMapper(); private static Map<Class<? extends BaseElement>, Class<? extends BaseBpmnJsonConverter>> convertersToJsonMap = new HashMap<Class<? extends BaseElement>, Class<? extends BaseBpmnJsonConverter>>(); private static Map<String, Class<? extends BaseBpmnJsonConverter>> convertersToBpmnMap = new HashMap<String, Class<? extends BaseBpmnJsonConverter>>(); static { // start and end events StartEventJsonConverter.fillTypes(convertersToBpmnMap, convertersToJsonMap); EndEventJsonConverter.fillTypes(convertersToBpmnMap, convertersToJsonMap); // connectors SequenceFlowJsonConverter.fillTypes(convertersToBpmnMap, convertersToJsonMap); // task types BusinessRuleTaskJsonConverter.fillTypes(convertersToBpmnMap, convertersToJsonMap); MailTaskJsonConverter.fillTypes(convertersToBpmnMap, convertersToJsonMap); ManualTaskJsonConverter.fillTypes(convertersToBpmnMap, convertersToJsonMap); ReceiveTaskJsonConverter.fillTypes(convertersToBpmnMap, convertersToJsonMap); ScriptTaskJsonConverter.fillTypes(convertersToBpmnMap, convertersToJsonMap); ServiceTaskJsonConverter.fillTypes(convertersToBpmnMap, convertersToJsonMap); UserTaskJsonConverter.fillTypes(convertersToBpmnMap, convertersToJsonMap); CallActivityJsonConverter.fillTypes(convertersToBpmnMap, convertersToJsonMap); // gateways ExclusiveGatewayJsonConverter.fillTypes(convertersToBpmnMap, convertersToJsonMap); InclusiveGatewayJsonConverter.fillTypes(convertersToBpmnMap, convertersToJsonMap); ParallelGatewayJsonConverter.fillTypes(convertersToBpmnMap, convertersToJsonMap); EventGatewayJsonConverter.fillTypes(convertersToBpmnMap, convertersToJsonMap); // scope constructs SubProcessJsonConverter.fillTypes(convertersToBpmnMap, convertersToJsonMap); EventSubProcessJsonConverter.fillTypes(convertersToBpmnMap, convertersToJsonMap); // catch events CatchEventJsonConverter.fillTypes(convertersToBpmnMap, convertersToJsonMap); // throw events ThrowEventJsonConverter.fillTypes(convertersToBpmnMap, convertersToJsonMap); // boundary events BoundaryEventJsonConverter.fillTypes(convertersToBpmnMap, convertersToJsonMap); // Jenkins task JenkinsTaskJsonConverter.fillTypes(convertersToBpmnMap, convertersToJsonMap); } private static final List<String> DI_CIRCLES = new ArrayList<String>(); private static final List<String> DI_RECTANGLES = new ArrayList<String>(); private static final List<String> DI_GATEWAY = new ArrayList<String>(); static { DI_CIRCLES.add(STENCIL_EVENT_START_ERROR); DI_CIRCLES.add(STENCIL_EVENT_START_MESSAGE); DI_CIRCLES.add(STENCIL_EVENT_START_NONE); DI_CIRCLES.add(STENCIL_EVENT_START_TIMER); DI_CIRCLES.add(STENCIL_EVENT_BOUNDARY_ERROR); DI_CIRCLES.add(STENCIL_EVENT_BOUNDARY_SIGNAL); DI_CIRCLES.add(STENCIL_EVENT_BOUNDARY_TIMER); DI_CIRCLES.add(STENCIL_EVENT_CATCH_MESSAGE); DI_CIRCLES.add(STENCIL_EVENT_CATCH_SIGNAL); DI_CIRCLES.add(STENCIL_EVENT_CATCH_TIMER); DI_CIRCLES.add(STENCIL_EVENT_THROW_NONE); DI_CIRCLES.add(STENCIL_EVENT_THROW_SIGNAL); DI_CIRCLES.add(STENCIL_EVENT_END_NONE); DI_CIRCLES.add(STENCIL_EVENT_END_ERROR); DI_RECTANGLES.add(STENCIL_CALL_ACTIVITY); DI_RECTANGLES.add(STENCIL_SUB_PROCESS); DI_RECTANGLES.add(STENCIL_EVENT_SUB_PROCESS); DI_RECTANGLES.add(STENCIL_TASK_BUSINESS_RULE); DI_RECTANGLES.add(STENCIL_TASK_MAIL); DI_RECTANGLES.add(STENCIL_TASK_MANUAL); DI_RECTANGLES.add(STENCIL_TASK_RECEIVE); DI_RECTANGLES.add(STENCIL_TASK_SCRIPT); DI_RECTANGLES.add(STENCIL_TASK_SERVICE); DI_RECTANGLES.add(STENCIL_TASK_USER); DI_RECTANGLES.add(STENCIL_TASK_JENKINS); DI_GATEWAY.add(STENCIL_GATEWAY_EVENT); DI_GATEWAY.add(STENCIL_GATEWAY_EXCLUSIVE); DI_GATEWAY.add(STENCIL_GATEWAY_INCLUSIVE); DI_GATEWAY.add(STENCIL_GATEWAY_PARALLEL); } public ObjectNode convertToJson(BpmnModel model) { ObjectNode modelNode = objectMapper.createObjectNode(); modelNode.put("bounds", BpmnJsonConverterUtil.createBoundsNode(1485, 1050, 0, 0)); modelNode.put("resourceId", "canvas"); ObjectNode stencilNode = objectMapper.createObjectNode(); stencilNode.put("id", "BPMNDiagram"); modelNode.put("stencil", stencilNode); ObjectNode stencilsetNode = objectMapper.createObjectNode(); stencilsetNode.put("namespace", "http://b3mn.org/stencilset/bpmn2.0#"); stencilsetNode.put("url", "../editor/stencilsets/bpmn2.0/bpmn2.0.json"); modelNode.put("stencilset", stencilsetNode); ArrayNode shapesArrayNode = objectMapper.createArrayNode(); Process process = model.getMainProcess(); ObjectNode propertiesNode = objectMapper.createObjectNode(); if (StringUtils.isNotEmpty(process.getId())) { propertiesNode.put(PROPERTY_PROCESS_ID, process.getId()); } if (StringUtils.isNotEmpty(process.getName())) { propertiesNode.put(PROPERTY_NAME, process.getName()); } if (StringUtils.isNotEmpty(process.getDocumentation())) { propertiesNode.put(PROPERTY_DOCUMENTATION, process.getDocumentation()); } modelNode.put(EDITOR_SHAPE_PROPERTIES, propertiesNode); processFlowElements(process.getFlowElements(), model, shapesArrayNode, 0.0, 0.0); modelNode.put(EDITOR_CHILD_SHAPES, shapesArrayNode); return modelNode; } public void processFlowElements(Collection<FlowElement> flowElements, BpmnModel model, ArrayNode shapesArrayNode, double subProcessX, double subProcessY) { for (FlowElement flowElement : flowElements) { Class<? extends BaseBpmnJsonConverter> converter = convertersToJsonMap.get(flowElement.getClass()); if (converter != null) { try { converter.newInstance().convertToJson(flowElement, this, model, shapesArrayNode, subProcessX, subProcessY); } catch (Exception e) { LOGGER.log(Level.SEVERE, "Error converting " + flowElement, e); } } } } public BpmnModel convertToBpmnModel(JsonNode modelNode) { BpmnModel bpmnModel = new BpmnModel(); Map<String, JsonNode> shapeMap = new HashMap<String, JsonNode>(); Map<String, JsonNode> sourceRefMap = new HashMap<String, JsonNode>(); Map<String, JsonNode> edgeMap = new HashMap<String, JsonNode>(); Map<String, List<JsonNode>> sourceAndTargetMap = new HashMap<String, List<JsonNode>>(); readShapeDI(modelNode, 0, 0, shapeMap, sourceRefMap, bpmnModel); filterAllEdges(modelNode, edgeMap, sourceAndTargetMap, shapeMap, sourceRefMap); readEdgeDI(edgeMap, sourceAndTargetMap, bpmnModel); JsonNode processIdNode = modelNode.get(EDITOR_SHAPE_PROPERTIES).get(PROPERTY_PROCESS_ID); if (processIdNode != null && StringUtils.isNotEmpty(processIdNode.asText())) { bpmnModel.getMainProcess().setId(processIdNode.asText()); } JsonNode processNameNode = modelNode.get(EDITOR_SHAPE_PROPERTIES).get(PROPERTY_NAME); if (processNameNode != null && StringUtils.isNotEmpty(processNameNode.asText())) { bpmnModel.getMainProcess().setName(processNameNode.asText()); } ArrayNode shapesArrayNode = (ArrayNode) modelNode.get(EDITOR_CHILD_SHAPES); processJsonElements(shapesArrayNode, modelNode, bpmnModel.getMainProcess(), shapeMap); // sequence flows are now all on root level Map<String, SubProcess> subShapesMap = new HashMap<String, SubProcess>(); for (FlowElement flowElement : bpmnModel.getMainProcess().getFlowElements()) { if (flowElement instanceof SubProcess) { SubProcess subProcess = (SubProcess) flowElement; fillSubShapes(subShapesMap, subProcess); } } if (subShapesMap.size() > 0) { List<String> removeSubFlowsList = new ArrayList<String>(); for (FlowElement flowElement : bpmnModel.getMainProcess().getFlowElements()) { if (flowElement instanceof SequenceFlow) { SequenceFlow sequenceFlow = (SequenceFlow) flowElement; if (subShapesMap.containsKey(sequenceFlow.getSourceRef())) { SubProcess subProcess = subShapesMap.get(sequenceFlow.getSourceRef()); subProcess.addFlowElement(sequenceFlow); removeSubFlowsList.add(sequenceFlow.getId()); } } } for (String flowId : removeSubFlowsList) { bpmnModel.getMainProcess().removeFlowElement(flowId); } } // boundary events only contain attached ref id fillAttachedToRef(bpmnModel, bpmnModel.getMainProcess().getFlowElements()); return bpmnModel; } public void processJsonElements(JsonNode shapesArrayNode, JsonNode modelNode, BaseElement parentElement, Map<String, JsonNode> shapeMap) { for (JsonNode shapeNode : shapesArrayNode) { Class<? extends BaseBpmnJsonConverter> converter = convertersToBpmnMap.get(BpmnJsonConverterUtil.getStencilId(shapeNode)); if (converter != null) { try { converter.newInstance().convertToBpmnModel(shapeNode, modelNode, this, parentElement, shapeMap); } catch (Exception e) { LOGGER.log(Level.SEVERE, "Error converting " + BpmnJsonConverterUtil.getStencilId(shapeNode), e); } } } } private void fillSubShapes(Map<String, SubProcess> subShapesMap, SubProcess subProcess) { for (FlowElement flowElement : subProcess.getFlowElements()) { if (flowElement instanceof SubProcess) { SubProcess childSubProcess = (SubProcess) flowElement; fillSubShapes(subShapesMap, childSubProcess); } else { subShapesMap.put(flowElement.getId(), subProcess); } } } private void fillAttachedToRef(BpmnModel model, Collection<FlowElement> flowElementList) { for (FlowElement flowElement : flowElementList) { if (flowElement instanceof BoundaryEvent) { BoundaryEvent boundaryEvent = (BoundaryEvent) flowElement; Activity activity = retrieveAttachedRefObject(boundaryEvent.getAttachedToRefId(), model.getMainProcess().getFlowElements()); boundaryEvent.setAttachedToRef(activity); activity.getBoundaryEvents().add(boundaryEvent); } if (flowElement instanceof SubProcess) { SubProcess subProcess = (SubProcess) flowElement; fillAttachedToRef(model, subProcess.getFlowElements()); } } } private Activity retrieveAttachedRefObject(String attachedToRefId, Collection<FlowElement> flowElementList) { for (FlowElement flowElement : flowElementList) { if (attachedToRefId.equals(flowElement.getId())) { return (Activity) flowElement; } else if (flowElement instanceof SubProcess) { SubProcess subProcess = (SubProcess) flowElement; Activity activity = retrieveAttachedRefObject(attachedToRefId, subProcess.getFlowElements()); if (activity != null) { return activity; } } } return null; } private void readShapeDI(JsonNode objectNode, double parentX, double parentY, Map<String, JsonNode> shapeMap, Map<String, JsonNode> sourceRefMap, BpmnModel bpmnModel) { if (objectNode.get(EDITOR_CHILD_SHAPES) != null) { for (JsonNode jsonChildNode : objectNode.get(EDITOR_CHILD_SHAPES)) { String stencilId = BpmnJsonConverterUtil.getStencilId(jsonChildNode); if (STENCIL_SEQUENCE_FLOW.equals(stencilId) == false) { GraphicInfo graphicInfo = new GraphicInfo(); JsonNode boundsNode = jsonChildNode.get(EDITOR_BOUNDS); ObjectNode upperLeftNode = (ObjectNode) boundsNode.get(EDITOR_BOUNDS_UPPER_LEFT); graphicInfo.x = upperLeftNode.get(EDITOR_BOUNDS_X).asDouble() + parentX; graphicInfo.y = upperLeftNode.get(EDITOR_BOUNDS_Y).asDouble() + parentY; ObjectNode lowerRightNode = (ObjectNode) boundsNode.get(EDITOR_BOUNDS_LOWER_RIGHT); graphicInfo.width = lowerRightNode.get(EDITOR_BOUNDS_X).asDouble() - graphicInfo.x + parentX; graphicInfo.height = lowerRightNode.get(EDITOR_BOUNDS_Y).asDouble() - graphicInfo.y + parentY; String childShapeId = jsonChildNode.get(EDITOR_SHAPE_ID).asText(); bpmnModel.addGraphicInfo(BpmnJsonConverterUtil.getElementId(jsonChildNode), graphicInfo); shapeMap.put(childShapeId, jsonChildNode); ArrayNode outgoingNode = (ArrayNode) jsonChildNode.get("outgoing"); if (outgoingNode != null && outgoingNode.size() > 0) { for (JsonNode outgoingChildNode : outgoingNode) { JsonNode resourceNode = outgoingChildNode.get(EDITOR_SHAPE_ID); if (resourceNode != null) { sourceRefMap.put(resourceNode.asText(), jsonChildNode); } } } readShapeDI(jsonChildNode, graphicInfo.x, graphicInfo.y, shapeMap, sourceRefMap, bpmnModel); } } } } private void filterAllEdges(JsonNode objectNode, Map<String, JsonNode> edgeMap, Map<String, List<JsonNode>> sourceAndTargetMap, Map<String, JsonNode> shapeMap, Map<String, JsonNode> sourceRefMap) { if (objectNode.get(EDITOR_CHILD_SHAPES) != null) { for (JsonNode jsonChildNode : objectNode.get(EDITOR_CHILD_SHAPES)) { ObjectNode childNode = (ObjectNode) jsonChildNode; String stencilId = BpmnJsonConverterUtil.getStencilId(childNode); if (STENCIL_SUB_PROCESS.equals(stencilId)) { filterAllEdges(childNode, edgeMap, sourceAndTargetMap, shapeMap, sourceRefMap); } else if (STENCIL_SEQUENCE_FLOW.equals(stencilId)) { String childEdgeId = BpmnJsonConverterUtil.getElementId(childNode); String targetRefId = childNode.get("target").get(EDITOR_SHAPE_ID).asText(); List<JsonNode> sourceAndTargetList = new ArrayList<JsonNode>(); sourceAndTargetList.add(sourceRefMap.get(childNode.get(EDITOR_SHAPE_ID).asText())); sourceAndTargetList.add(shapeMap.get(targetRefId)); edgeMap.put(childEdgeId, childNode); sourceAndTargetMap.put(childEdgeId, sourceAndTargetList); } } } } private void readEdgeDI(Map<String, JsonNode> edgeMap, Map<String, List<JsonNode>> sourceAndTargetMap, BpmnModel bpmnModel) { for (String edgeId : edgeMap.keySet()) { JsonNode edgeNode = edgeMap.get(edgeId); List<JsonNode> sourceAndTargetList = sourceAndTargetMap.get(edgeId); JsonNode sourceRefNode = sourceAndTargetList.get(0); JsonNode targetRefNode = sourceAndTargetList.get(1); if (sourceRefNode == null) { LOGGER.log(Level.INFO, "Skipping edge " + edgeId + " because source ref is null"); continue; } if (targetRefNode == null) { LOGGER.log(Level.INFO, "Skipping edge " + edgeId + " because target ref is null"); continue; } JsonNode dockersNode = edgeNode.get(EDITOR_DOCKERS); double sourceDockersX = dockersNode.get(0).get(EDITOR_BOUNDS_X).getDoubleValue(); double sourceDockersY = dockersNode.get(0).get(EDITOR_BOUNDS_Y).getDoubleValue(); GraphicInfo sourceInfo = bpmnModel.getGraphicInfo(BpmnJsonConverterUtil.getElementId(sourceRefNode)); GraphicInfo targetInfo = bpmnModel.getGraphicInfo(BpmnJsonConverterUtil.getElementId(targetRefNode)); /*JsonNode sourceRefBoundsNode = sourceRefNode.get(EDITOR_BOUNDS); BoundsLocation sourceRefUpperLeftLocation = getLocation(EDITOR_BOUNDS_UPPER_LEFT, sourceRefBoundsNode); BoundsLocation sourceRefLowerRightLocation = getLocation(EDITOR_BOUNDS_LOWER_RIGHT, sourceRefBoundsNode); JsonNode targetRefBoundsNode = targetRefNode.get(EDITOR_BOUNDS); BoundsLocation targetRefUpperLeftLocation = getLocation(EDITOR_BOUNDS_UPPER_LEFT, targetRefBoundsNode); BoundsLocation targetRefLowerRightLocation = getLocation(EDITOR_BOUNDS_LOWER_RIGHT, targetRefBoundsNode);*/ double sourceRefLineX = sourceInfo.x + sourceDockersX; double sourceRefLineY = sourceInfo.y + sourceDockersY; double nextPointInLineX; double nextPointInLineY; nextPointInLineX = dockersNode.get(1).get(EDITOR_BOUNDS_X).getDoubleValue(); nextPointInLineY = dockersNode.get(1).get(EDITOR_BOUNDS_Y).getDoubleValue(); if (dockersNode.size() == 2) { nextPointInLineX += targetInfo.x; nextPointInLineY += targetInfo.y; } Line2D firstLine = new Line2D(sourceRefLineX, sourceRefLineY, nextPointInLineX, nextPointInLineY); String sourceRefStencilId = BpmnJsonConverterUtil.getStencilId(sourceRefNode); String targetRefStencilId = BpmnJsonConverterUtil.getStencilId(targetRefNode); List<GraphicInfo> graphicInfoList = new ArrayList<GraphicInfo>(); if (DI_CIRCLES.contains(sourceRefStencilId)) { Circle2D eventCircle = new Circle2D(sourceInfo.x + sourceDockersX, sourceInfo.y + sourceDockersY, sourceDockersX); Collection<Point2D> intersections = eventCircle.intersections(firstLine); Point2D intersection = intersections.iterator().next(); graphicInfoList.add(createGraphicInfo(intersection.getX(), intersection.getY())); } else if (DI_RECTANGLES.contains(sourceRefStencilId)) { Polyline2D rectangle = createRectangle(sourceInfo); Collection<Point2D> intersections = rectangle.intersections(firstLine); Point2D intersection = intersections.iterator().next(); graphicInfoList.add(createGraphicInfo(intersection.getX(), intersection.getY())); } else if (DI_GATEWAY.contains(sourceRefStencilId)) { Polyline2D gatewayRectangle = createGateway(sourceInfo); Collection<Point2D> intersections = gatewayRectangle.intersections(firstLine); Point2D intersection = intersections.iterator().next(); graphicInfoList.add(createGraphicInfo(intersection.getX(), intersection.getY())); } Line2D lastLine = null; if (dockersNode.size() > 2) { for(int i = 1; i < dockersNode.size() - 1; i++) { double x = dockersNode.get(i).get(EDITOR_BOUNDS_X).getDoubleValue(); double y = dockersNode.get(i).get(EDITOR_BOUNDS_Y).getDoubleValue(); graphicInfoList.add(createGraphicInfo(x, y)); } double startLastLineX = dockersNode.get(dockersNode.size() - 2).get(EDITOR_BOUNDS_X).getDoubleValue(); double startLastLineY = dockersNode.get(dockersNode.size() - 2).get(EDITOR_BOUNDS_Y).getDoubleValue(); double endLastLineX = dockersNode.get(dockersNode.size() - 1).get(EDITOR_BOUNDS_X).getDoubleValue(); double endLastLineY = dockersNode.get(dockersNode.size() - 1).get(EDITOR_BOUNDS_Y).getDoubleValue(); endLastLineX += targetInfo.x; endLastLineY += targetInfo.y; lastLine = new Line2D(startLastLineX, startLastLineY, endLastLineX, endLastLineY); } else { lastLine = firstLine; } if (DI_RECTANGLES.contains(targetRefStencilId)) { Polyline2D rectangle = createRectangle(targetInfo); Collection<Point2D> intersections = rectangle.intersections(lastLine); Point2D intersection = intersections.iterator().next(); graphicInfoList.add(createGraphicInfo(intersection.getX(), intersection.getY())); } else if (DI_CIRCLES.contains(targetRefStencilId)) { double targetDockersX = dockersNode.get(dockersNode.size() - 1).get(EDITOR_BOUNDS_X).getDoubleValue(); double targetDockersY = dockersNode.get(dockersNode.size() - 1).get(EDITOR_BOUNDS_Y).getDoubleValue(); Circle2D eventCircle = new Circle2D(targetInfo.x + targetDockersX, targetInfo.y + targetDockersY, targetDockersX); Collection<Point2D> intersections = eventCircle.intersections(lastLine); Point2D intersection = intersections.iterator().next(); graphicInfoList.add(createGraphicInfo(intersection.getX(), intersection.getY())); } else if (DI_GATEWAY.contains(targetRefStencilId)) { Polyline2D gatewayRectangle = createGateway(targetInfo); Collection<Point2D> intersections = gatewayRectangle.intersections(lastLine); Point2D intersection = intersections.iterator().next(); graphicInfoList.add(createGraphicInfo(intersection.getX(), intersection.getY())); } bpmnModel.addFlowGraphicInfoList(edgeId, graphicInfoList); } } private Polyline2D createRectangle(GraphicInfo graphicInfo) { Polyline2D rectangle = new Polyline2D(new Point2D(graphicInfo.x, graphicInfo.y), new Point2D(graphicInfo.x + graphicInfo.width, graphicInfo.y), new Point2D(graphicInfo.x + graphicInfo.width, graphicInfo.y + graphicInfo.height), new Point2D(graphicInfo.x, graphicInfo.y + graphicInfo.height), new Point2D(graphicInfo.x, graphicInfo.y)); return rectangle; } private Polyline2D createGateway(GraphicInfo graphicInfo) { double middleX = graphicInfo.x + (graphicInfo.width / 2); double middleY = graphicInfo.y + (graphicInfo.height / 2); Polyline2D gatewayRectangle = new Polyline2D(new Point2D(graphicInfo.x, middleY), new Point2D(middleX, graphicInfo.y), new Point2D(graphicInfo.x + graphicInfo.width, middleY), new Point2D(middleX, graphicInfo.y + graphicInfo.height), new Point2D(graphicInfo.x, middleY)); return gatewayRectangle; } private GraphicInfo createGraphicInfo(double x, double y) { GraphicInfo graphicInfo = new GraphicInfo(); graphicInfo.x = x; graphicInfo.y = y; return graphicInfo; } }