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

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

There is a newer version: 2.2.18
Show newest version
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.graph.model.YGraphModel;
import org.yaoqiang.graph.swing.YGraphComponent;
import org.yaoqiang.graph.util.Constants;
import org.yaoqiang.graph.util.Utils;

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;

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

	protected YGraphComponent graphComponent;

	protected YGraph graph;

	protected YGraphModel model;

	public YGraphManager(YGraphComponent 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.LABEL_CHANGED, handler);
			this.graph.addListener(mxEvent.FOLD_CELLS, handler);
			this.graph.addListener(mxEvent.REMOVE_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) {
						Utils.setEdgeStyle(graph, cell);
					}
				} else if (eventName.equals(mxEvent.CHANGE)) {
					graphComponent.validateGraph();
					List changes = (List) evt.getProperty("changes");
					mxUndoableChange change = changes == null ? null : (mxUndoableChange) changes.get(0);
					if (change instanceof YGraphModel.mxStyleChange) {
						cell = (mxCell) ((YGraphModel.mxStyleChange) change).getCell();
					}
					if (graph.isChoreographyParticipant(cell)) {
						double height = 0;
						Object subprocess = null;
						Object parent = cell.getParent();
						Object[] partCells = YGraphModel.getChildVertices(model, parent);
						for (int i = 0; i < partCells.length; i++) {
							if (graph.isChoreographyParticipant(partCells[i])) {
								mxCell part = (mxCell) partCells[i];
								mxGeometry geo = model.getGeometry(part);
								if (graph.isMultiInstanceParticipant(part)) {
									geo.setHeight(Constants.PARTICIPANT_HEIGHT * 1.75);
								} else {
									geo.setHeight(Constants.PARTICIPANT_HEIGHT);
								}
								model.setGeometry(part, geo);
								height += geo.getHeight();
							} else {
								subprocess = partCells[i];
							}
						}

						mxGeometry geo = model.getGeometry(parent);
						geo.setHeight(model.getGeometry(subprocess).getHeight() + height);
						model.setGeometry(parent, geo);

						Utils.arrangeChoreography(graph, parent, false);
					}
				} else if (eventName.equals(mxEvent.LABEL_CHANGED)) {

				} else if (eventName.equals(mxEvent.FOLD_CELLS)) {
					Map associations = new HashMap();
					for (mxCell a : Utils.getAllAssociations(graph)) {
						Object innerCell = null;
						if (graph.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 = !graph.isCollapsedSubProcess(Utils.getChoreographyActivity(graph, cell));
						Utils.arrangeChoreography(graph, cell, true);
					}
					if (graph.isSubProcess(cell)) {
						showAnnotation = !graph.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.REMOVE_CELLS)) {
					if (graph.isStartEvent(cell) && graph.isEventSubProcess(model.getParent(cell))) {
						graph.setCellStyles("trigger", "", new Object[] { model.getParent(cell) });
					}
				} else if (eventName.equals(mxEvent.MOVE_CELLS) || eventName.equals(mxEvent.CELLS_ADDED)) {
					if (graph.isGroupArtifact(cell)) {
						graph.orderCells(true, new Object[] { cell });
					} else if (graph.isEvent(cell)
							&& (!graph.isThrowEvent(cell) && !graph.isLinkEvent(cell)
									&& (graph.isStartEvent(cell) && graph.isNoneEvent(cell) || !graph.isStartEvent(cell)) || graph.isEndEvent(cell) || graph
										.isNoneEvent(cell)) && !graph.isAttachedEvent(cell) && graph.getConnections(cell).length == 0) {
						arrangeAttachedEvent(cell);
					} else if (graph.isMessage(cell) && !cell.getGeometry().isRelative() && graph.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 (graph.isStartEvent(cell) && graph.isEventSubProcess(model.getParent(cell))) {
							String trigger = "Message";
							if (graph.isNoneEvent(cell)) {
								model.setStyle(cell, "startMessageEvent");
							} else {
								int endIndex = model.getStyle(cell).lastIndexOf("Event");
								if (endIndex != -1) {
									trigger = model.getStyle(cell).substring(5, endIndex);
								}
							}
							graph.setCellStyles("trigger", trigger, new Object[] { model.getParent(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)) {
							Utils.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);
							Utils.arrangeChoreography(graph, cell, false);
							if (graph.isSubChoreography(cell)) {
								Object[] selectedCells = new Object[] { Utils.getChoreographyActivity(graph, cell) };
								mxRectangle alterBounds = model.getGeometry(selectedCells[0]).getAlternateBounds();
								if (graph.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 (graph.isSubProcess(cell) && alterBounds != null) {
								if (graph.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();
		Object oldCell = null;
		List cells = new ArrayList();
		if (graph.isCallChoreography(cell)) {
			call = ";call=1;strokeWidth=3";
			name = "Call \n";
		}

		if (graph.isChoreography(cell)) {
			mxCell taskCell = new mxCell(name + "Choreography \n Task", new mxGeometry(0, Constants.PARTICIPANT_HEIGHT, Constants.ACTIVITY_WIDTH,
					Constants.ACTIVITY_HEIGHT), "choreographyTask" + call);
			id += "_CT";
			taskCell.setId(id);
			taskCell.setVertex(true);
			oldCell = model.getCell(id);
			if (oldCell != null) {
				cells.add(oldCell);
			} else {
				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.ACTIVITY_WIDTH, Constants.ACTIVITY_HEIGHT);
			geo.setAlternateBounds(new mxRectangle(0, 0, 400, 250));
			mxCell subprocessCell = new mxCell(value, geo, "choreographySubprocess" + call);
			id += "_SC";
			subprocessCell.setId(id);
			subprocessCell.setVertex(true);
			oldCell = model.getCell(id);
			if (oldCell != null) {
				cells.add(oldCell);
			} else {
				cells.add(subprocessCell);
			}
		}

		if (oldCell != null) {
			Map participants = graph.getAllChoreographyParticipants(cell);
			for (mxCell part : participants.values()) {
				cells.add(part);
			}
			graph.addCells(cells.toArray(), cell);
			graph.orderCells(true, participants.values().toArray());
		} else {
			Map participants = graph.getAllParticipants();
			String partAId = participants.get("Participant A");
			String partBId = participants.get("Participant B");
			partAId = id + "_part_" + (partAId == null ? model.createId(null) : partAId);
			partBId = id + "_part_" + (partBId == null ? model.createId(null) : partBId);

			mxCell participantBCell = new mxCell("Participant B", new mxGeometry(0, 75, Constants.ACTIVITY_WIDTH, Constants.PARTICIPANT_HEIGHT),
					"participantBottom" + call);
			participantBCell.setId(partBId);
			participantBCell.setVertex(true);
			cells.add(participantBCell);

			mxCell participantACell = new mxCell("Participant A", new mxGeometry(0, 0, Constants.ACTIVITY_WIDTH, Constants.PARTICIPANT_HEIGHT),
					"participantTop;fillColor=#E8EEF7" + call);
			participantACell.setId(partAId);
			participantACell.setVertex(true);
			cells.add(participantACell);

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

	}

	protected void arrangeAttachedEvent(mxCell cell) {
		Object parent = model.getParent(cell);
		if (graph.isTask(parent) || (graph.isCallActivityProcess(parent) || graph.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);
				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);
				model.setGeometry(parent, pgeo);
				Utils.moveLableToRight(graph, new Object[] { event });
			} else if (yOffset == 0) { // align subprocess top
				eventOffset = new mxPoint(xOffset, -eventWidth / 2);
				Utils.moveLableToTop(graph, new Object[] { event });
			} else if (xOffset == 0) { // align subprocess left
				eventOffset = new mxPoint(-eventWidth / 2, yOffset);
				Utils.moveLableToLeft(graph, new Object[] { event });
			} else if (graph.isTask(parent) || graph.isCallActivityProcess(parent) || graph.isSubProcess(parent) && graph.isCollapsedSubProcess(parent)
					|| graph.isSubProcess(parent) && !graph.isCollapsedSubProcess(parent) && Constants.STYLE_BOUNDARY_EVENT.equals(event.getStyle())) {
				if (xOffset < width / 2) {
					if (yOffset < height / 2) {
						if (xOffset < yOffset) { // align left
							eventOffset = new mxPoint(-eventWidth / 2, yOffset);
							Utils.moveLableToLeft(graph, new Object[] { event });
						} else { // align left top
							eventOffset = new mxPoint(xOffset, -eventWidth / 2);
							Utils.moveLableToTop(graph, new Object[] { event });
						}
					} else {
						if (xOffset < yOffset) { // align left
							eventOffset = new mxPoint(-eventWidth / 2, yOffset);
							Utils.moveLableToLeft(graph, new Object[] { event });
						} else { // align left bottom
							geo.setY(1);
							eventOffset = new mxPoint(xOffset, -eventWidth / 2);
						}
					}
				} else {
					if (yOffset < height / 2) {
						if (width - xOffset < yOffset) { // align right
							geo.setX(1);
							eventOffset = new mxPoint(-eventWidth / 2, yOffset);
							Utils.moveLableToRight(graph, new Object[] { event });
						} else { // align right top
							eventOffset = new mxPoint(xOffset, -eventWidth / 2);
							Utils.moveLableToTop(graph, new Object[] { event });
						}
					} else {
						if (width - xOffset < yOffset) { // align right
							geo.setX(1);
							eventOffset = new mxPoint(-eventWidth / 2, yOffset);
							Utils.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);
	}

}