All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.yaoqiang.graph.view.GraphManager Maven / Gradle / Ivy

package org.yaoqiang.graph.view;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

import org.yaoqiang.bpmn.model.elements.choreographyactivities.ChoreographyActivity;
import org.yaoqiang.bpmn.model.elements.choreographyactivities.ChoreographyTask;
import org.yaoqiang.bpmn.model.elements.choreographyactivities.SubChoreography;
import org.yaoqiang.bpmn.model.elements.collaboration.Participant;
import org.yaoqiang.bpmn.model.elements.events.BoundaryEvent;
import org.yaoqiang.graph.model.GraphModel;
import org.yaoqiang.graph.swing.GraphComponent;
import org.yaoqiang.graph.util.GraphUtils;
import org.yaoqiang.util.Constants;

import com.mxgraph.model.mxCell;
import com.mxgraph.model.mxGeometry;
import com.mxgraph.util.mxConstants;
import com.mxgraph.util.mxEvent;
import com.mxgraph.util.mxEventObject;
import com.mxgraph.util.mxEventSource;
import com.mxgraph.util.mxPoint;
import com.mxgraph.util.mxRectangle;
import com.mxgraph.util.mxUndoableEdit;
import com.mxgraph.util.mxUndoableEdit.mxUndoableChange;

/**
 * GraphManager
 * 
 * @author Shi Yaoqiang([email protected])
 */
public class GraphManager extends mxEventSource {

	protected GraphComponent graphComponent;

	protected Graph graph;

	protected GraphModel model;

	public GraphManager(GraphComponent graphComponent) {
		if (this.graphComponent != null) {
			this.graphComponent.getConnectionHandler().removeListener(handler);
			this.model.removeListener(handler);
			this.graph.removeListener(handler);
		}

		this.graphComponent = graphComponent;
		this.graph = graphComponent.getGraph();
		this.model = graphComponent.getGraph().getModel();

		if (this.graphComponent != null) {
			this.graphComponent.getConnectionHandler().addListener(mxEvent.CONNECT, handler);
			this.model.addListener(mxEvent.CHANGE, handler);
			this.graph.addListener(mxEvent.FOLD_CELLS, handler);
			this.graph.addListener(mxEvent.MOVE_CELLS, handler);
			this.graph.addListener(mxEvent.CELLS_ADDED, handler);
			this.graph.addListener(mxEvent.CELLS_RESIZED, handler);
		}
	}

	public final mxIEventListener getHandler() {
		return handler;
	}

	protected mxIEventListener handler = new mxIEventListener() {
		public void invoke(Object source, mxEventObject evt) {
			String eventName = evt.getName();

			mxCell cell = (mxCell) graph.getSelectionCell();
			if (cell == null) {
				Object[] cells = (Object[]) evt.getProperty("cells");
				if (cells != null && cells.length > 0) {
					cell = (mxCell) cells[0];
				}
			}

			model.beginUpdate();
			try {
				if (eventName.equals(mxEvent.CONNECT)) {
					cell = (mxCell) evt.getProperty("cell");
					if (model.isEdge(cell) && model.getStyle(cell).length() == 0) {
						String style = GraphUtils.setEdgeStyle(graph, cell);
						if (style.equals("messageFlow;elbow=horizontal")) {
							GraphUtils.setElementStyles(graph, cell, "elbow");
						}
					}
				} else if (eventName.equals(mxEvent.CHANGE)) {
					graphComponent.validateGraph();
				} else if (eventName.equals(mxEvent.FOLD_CELLS)) {
					Map associations = new HashMap();
					for (mxCell a : GraphUtils.getAllAssociations(graph)) {
						Object innerCell = null;
						if (model.isAnnotation(a.getTarget())) {
							innerCell = a.getSource();
							if (cell != innerCell && model.isAncestor(cell, innerCell)) {
								associations.put(a, (mxCell) a.getTarget());
							}
						} else {
							innerCell = a.getTarget();
							if (cell != innerCell && model.isAncestor(cell, innerCell)) {
								associations.put(a, (mxCell) a.getSource());
							}
						}
					}
					boolean showAnnotation = true;
					if (graph.isSubChoreography(cell)) {
						showAnnotation = !model.isCollapsedSubProcess(GraphUtils.getChoreographyActivity(graph, cell));
						GraphUtils.arrangeChoreography(graph, cell, true);
					}
					if (model.isSubProcess(cell)) {
						showAnnotation = !model.isCollapsedSubProcess(cell);
					}
					if (graph.isSwimlane(cell)) {
						showAnnotation = !graph.isCollapsedSwimlane(cell);
					}
					for (Entry e : associations.entrySet()) {
						model.setVisible(e.getKey(), showAnnotation);
						List edges = new ArrayList(Arrays.asList(graph.getConnections(e.getValue())));
						edges.remove(e.getKey());
						if (edges.isEmpty()) {
							model.setVisible(e.getValue(), showAnnotation);
						}
					}
				} else if (eventName.equals(mxEvent.MOVE_CELLS) || eventName.equals(mxEvent.CELLS_ADDED)) {
					if (model.isGroupArtifact(cell)) {
						graph.orderCells(true, new Object[] { cell });
					} else if (cell.getValue() instanceof BoundaryEvent && !cell.getGeometry().isRelative() && graph.getConnections(cell).length == 0) {
						arrangeAttachedEvent(cell);
					} else if (model.isMessage(cell) && !cell.getGeometry().isRelative() && model.isMessageFlow(model.getParent(cell))) {
						arrangeAttachedMessage(cell);
					}
					if (eventName.equals(mxEvent.CELLS_ADDED)) {
						if ((graph.isChoreography(cell) || graph.isSubChoreography(cell)) && model.getChildCount(cell) == 0) {
							insertChoreography(cell);
						}
					}
				} else if (eventName.equals(mxEvent.UNDO) || eventName.equals(mxEvent.REDO)) {
					Object edit = evt.getProperty("edit");
					if (edit != null) {
						List changes = ((mxUndoableEdit) edit).getChanges();
						graph.setSelectionCells(graph.getSelectionCellsForChanges(changes));
						cell = (mxCell) graph.getSelectionCell();
						if (graph.isChoreography(cell) || graph.isSubChoreography(cell)) {
							GraphUtils.arrangeChoreography(graph, cell, false);
						}
					}
				} else if (eventName.equals(mxEvent.CELLS_RESIZED)) {
					Object[] cells = (Object[]) evt.getProperty("cells");
					if (cells != null && cells.length > 0 && cells[0] != cell) {
						return;
					}
					if (cell != null && !graph.isImageArtifact(cell) && !graph.isSwimlane(cell)) {
						mxGeometry geo = cell.getGeometry();
						if (graph.isChoreography(cell) || graph.isSubChoreography(cell)) {
							if (geo.getWidth() <= Constants.ACTIVITY_WIDTH) {
								geo.setWidth(Constants.ACTIVITY_WIDTH);
							}
							if (geo.getHeight() <= Constants.ACTIVITY_HEIGHT + Constants.PARTICIPANT_HEIGHT * 2) {
								geo.setHeight(Constants.ACTIVITY_HEIGHT + Constants.PARTICIPANT_HEIGHT * 2);
							}
							model.setGeometry(cell, geo);
							GraphUtils.arrangeChoreography(graph, cell, false);
							if (graph.isSubChoreography(cell)) {
								Object[] selectedCells = new Object[] { GraphUtils.getChoreographyActivity(graph, cell) };
								mxRectangle alterBounds = model.getGeometry(selectedCells[0]).getAlternateBounds();
								if (model.isCollapsedSubProcess(selectedCells[0])) {
									if (alterBounds.getWidth() <= Constants.FOLDED_SUBPROCESS_WIDTH) {
										alterBounds.setWidth(Constants.FOLDED_SUBPROCESS_WIDTH + 10);
									}
									if (alterBounds.getHeight() <= Constants.ACTIVITY_HEIGHT) {
										alterBounds.setHeight(Constants.ACTIVITY_HEIGHT * 2);
									}
									graph.setCellStyles(mxConstants.STYLE_VERTICAL_ALIGN, mxConstants.ALIGN_MIDDLE, selectedCells);
								} else {
									if (alterBounds.getWidth() > Constants.FOLDED_SUBPROCESS_WIDTH) {
										alterBounds.setWidth(Constants.FOLDED_SUBPROCESS_WIDTH - 10);
									}
									if (alterBounds.getHeight() > Constants.ACTIVITY_HEIGHT) {
										alterBounds.setHeight(Constants.ACTIVITY_HEIGHT);
									}
									graph.setCellStyles(mxConstants.STYLE_VERTICAL_ALIGN, mxConstants.ALIGN_TOP, selectedCells);
								}
							}
						} else {
							if (geo.getWidth() <= Constants.ACTIVITY_WIDTH / 2) {
								geo.setWidth(Constants.ACTIVITY_WIDTH / 2);
							}
							if (geo.getHeight() <= Constants.ACTIVITY_HEIGHT / 3) {
								geo.setHeight(Constants.ACTIVITY_HEIGHT / 3);
							}
							mxRectangle alterBounds = geo.getAlternateBounds();
							if (model.isSubProcess(cell) && alterBounds != null) {
								if (model.isCollapsedSubProcess(cell)) {
									if (alterBounds.getWidth() <= Constants.FOLDED_SUBPROCESS_WIDTH) {
										alterBounds.setWidth(Constants.FOLDED_SUBPROCESS_WIDTH + 10);
									}
									if (alterBounds.getHeight() <= Constants.ACTIVITY_HEIGHT) {
										alterBounds.setHeight(Constants.ACTIVITY_HEIGHT * 2);
									}
									graph.setCellStyles(mxConstants.STYLE_VERTICAL_ALIGN, mxConstants.ALIGN_MIDDLE);
								} else {
									if (alterBounds.getWidth() > Constants.FOLDED_SUBPROCESS_WIDTH) {
										alterBounds.setWidth(Constants.FOLDED_SUBPROCESS_WIDTH - 10);
									}
									if (alterBounds.getHeight() > Constants.ACTIVITY_HEIGHT) {
										alterBounds.setHeight(Constants.ACTIVITY_HEIGHT);
									}
									graph.setCellStyles(mxConstants.STYLE_VERTICAL_ALIGN, mxConstants.ALIGN_TOP);
								}
							}
							model.setGeometry(cell, geo);
						}
					}
				}
				graph.refresh();
			} finally {
				model.endUpdate();
			}
		}
	};

	protected void insertChoreography(mxCell cell) {
		String call = "";
		String name = "";
		String id = cell.getId();
		ChoreographyActivity activity = null;
		List cells = new ArrayList();
		if (graph.isCallChoreography(cell)) {
			call = ";call=1;strokeWidth=3";
			name = "Call \n";
		}

		if (graph.isChoreography(cell)) {
			activity = new ChoreographyTask(name + "Choreography \n Task");
			mxCell taskCell = new mxCell(activity, new mxGeometry(0, Constants.PARTICIPANT_HEIGHT, Constants.CHOREOGRAPHY_WIDTH, Constants.ACTIVITY_HEIGHT),
					"choreographyTask" + call);
			id += "_CT";
			taskCell.setId(id);
			taskCell.setVertex(true);
			cells.add(taskCell);
		} else {
			String value = "Choreography \n Sub-Process";
			if (graph.isCallChoreography(cell)) {
				value = "Call \n Choreography";
			}
			mxGeometry geo = new mxGeometry(0, Constants.PARTICIPANT_HEIGHT, Constants.CHOREOGRAPHY_WIDTH, Constants.ACTIVITY_HEIGHT);
			geo.setAlternateBounds(new mxRectangle(0, 0, 400, 250));
			activity = new SubChoreography(value);
			mxCell subprocessCell = new mxCell(activity, geo, "choreographySubprocess" + call);
			id += "_SC";
			subprocessCell.setId(id);
			subprocessCell.setVertex(true);
			cells.add(subprocessCell);
		}

		Map participants = model.getAllParticipants();
		double ha = 1;
		Participant partA = participants.get("Participant A");
		String partAId = null;
		if (partA == null) {
			partA = new Participant("Participant A");
			partAId = model.createId(null);
		} else {
			if (partA.getMultiplicity() != 1) {
				ha = 1.75;
			}
			partAId = partA.getId();
		}
		activity.setInitiatingParticipantRef(partAId);

		double hb = 1;
		Participant partB = participants.get("Participant B");
		String partBId = null;
		if (partB == null) {
			partB = new Participant("Participant B");
			partBId = model.createId(null);
		} else {
			if (partB.getMultiplicity() != 1) {
				hb = 1.75;
			}
			partBId = partB.getId();
		}

		partAId = id + "_part_" + partAId;
		partBId = id + "_part_" + partBId;

		mxCell participantBCell = new mxCell(partB, new mxGeometry(0, 75, Constants.CHOREOGRAPHY_WIDTH, Constants.PARTICIPANT_HEIGHT * hb), "participantBottom"
				+ call);
		participantBCell.setId(partBId);
		participantBCell.setVertex(true);
		cells.add(participantBCell);

		mxCell participantACell = new mxCell(partA, new mxGeometry(0, 0, Constants.CHOREOGRAPHY_WIDTH, Constants.PARTICIPANT_HEIGHT * ha), "participantTop"
				+ call);
		participantACell.setId(partAId);
		participantACell.setVertex(true);
		cells.add(participantACell);

		graph.addCells(cells.toArray(), cell);
		graph.orderCells(true, new Object[] { participantACell, participantBCell });
		GraphUtils.arrangeChoreography(graph, cell, false);
	}

	protected void arrangeAttachedEvent(mxCell cell) {
		Object parent = model.getParent(cell);
		if (model.isTask(parent) || (model.isCallActivity(parent) || model.isSubProcess(parent)) && parent != graph.getCurrentRoot()) {

			mxCell event = cell;
			mxCell subflow = (mxCell) parent;
			double eventWidth = event.getGeometry().getWidth();
			double xOffset = event.getGeometry().getX();
			double yOffset = event.getGeometry().getY();
			double height = subflow.getGeometry().getHeight();
			double width = subflow.getGeometry().getWidth();
			mxGeometry geo = new mxGeometry(0, 0, eventWidth, eventWidth);
			mxPoint eventOffset = new mxPoint(-eventWidth / 2, -eventWidth / 2);

			if (yOffset + eventWidth == height) { // align subprocess bottom
				geo.setY(1);
				eventOffset = new mxPoint(xOffset, -eventWidth / 2);
				mxGeometry pgeo = new mxGeometry(subflow.getGeometry().getX(), subflow.getGeometry().getY(), width, height - eventWidth / 4);
				pgeo.setAlternateBounds(subflow.getGeometry().getAlternateBounds());
				model.setGeometry(parent, pgeo);
			} else if (xOffset + eventWidth == width) { // align subprocess right
				geo.setX(1);
				eventOffset = new mxPoint(-eventWidth / 2, yOffset);
				mxGeometry pgeo = new mxGeometry(subflow.getGeometry().getX(), subflow.getGeometry().getY(), width - eventWidth / 4, height);
				pgeo.setAlternateBounds(subflow.getGeometry().getAlternateBounds());
				model.setGeometry(parent, pgeo);
				GraphUtils.moveLableToRight(graph, new Object[] { event });
			} else if (yOffset == 0) { // align subprocess top
				eventOffset = new mxPoint(xOffset, -eventWidth / 2);
				GraphUtils.moveLableToTop(graph, new Object[] { event });
			} else if (xOffset == 0) { // align subprocess left
				eventOffset = new mxPoint(-eventWidth / 2, yOffset);
				GraphUtils.moveLableToLeft(graph, new Object[] { event });
			} else if (model.isTask(parent) || model.isCallActivity(parent) || model.isSubProcess(parent) && model.isCollapsedSubProcess(parent)
					|| model.isSubProcess(parent) && !model.isCollapsedSubProcess(parent) && event.getValue() instanceof BoundaryEvent) {
				if (xOffset + eventWidth < width / 2) {
					if (yOffset + eventWidth < height / 2) {
						if (xOffset < yOffset) { // align left
							eventOffset = new mxPoint(-eventWidth / 2, yOffset);
							GraphUtils.moveLableToLeft(graph, new Object[] { event });
						} else { // align left top
							eventOffset = new mxPoint(xOffset, -eventWidth / 2);
							GraphUtils.moveLableToTop(graph, new Object[] { event });
						}
					} else {
						if (xOffset < yOffset) { // align left
							eventOffset = new mxPoint(-eventWidth / 2, yOffset);
							GraphUtils.moveLableToLeft(graph, new Object[] { event });
						} else { // align left bottom
							geo.setY(1);
							eventOffset = new mxPoint(xOffset, -eventWidth / 2);
						}
					}
				} else {
					if (yOffset + eventWidth < height / 2) {
						if (width - xOffset < yOffset) { // align right
							geo.setX(1);
							eventOffset = new mxPoint(-eventWidth / 2, yOffset);
							GraphUtils.moveLableToRight(graph, new Object[] { event });
						} else { // align right top
							eventOffset = new mxPoint(xOffset, -eventWidth / 2);
							GraphUtils.moveLableToTop(graph, new Object[] { event });
						}
					} else {
						if (width - xOffset < yOffset) { // align right
							geo.setX(1);
							eventOffset = new mxPoint(-eventWidth / 2, yOffset);
							GraphUtils.moveLableToRight(graph, new Object[] { event });
						} else { // align right bottom
							geo.setY(1);
							eventOffset = new mxPoint(xOffset, -eventWidth / 2);
						}
					}
				}
			} else {
				eventOffset = null;
			}

			if (eventOffset != null) {
				geo.setOffset(eventOffset);
				geo.setRelative(true);
				model.setGeometry(cell, geo);
			}
		}
	}

	protected void arrangeAttachedMessage(mxCell cell) {
		cell.setConnectable(false);
		double messageWidth = cell.getGeometry().getWidth();
		double messageHeight = cell.getGeometry().getHeight();
		mxGeometry geo = new mxGeometry(0, 0, messageWidth, messageHeight);
		mxPoint messageOffset = new mxPoint(-messageHeight / 2, -messageHeight / 2);
		geo.setOffset(messageOffset);
		geo.setRelative(true);
		model.setGeometry(cell, geo);
	}

}