📄 mxgraphmodel.java
字号:
/** * $Id: mxGraphModel.java,v 1.69 2009/04/06 14:49:24 gaudenz Exp $ * Copyright (c) 2007, Gaudenz Alder */package com.mxgraph.model;import java.util.ArrayList;import java.util.Arrays;import java.util.Collection;import java.util.HashSet;import java.util.Hashtable;import java.util.Iterator;import java.util.List;import java.util.Map;import java.util.Set;import com.mxgraph.util.mxEvent;import com.mxgraph.util.mxEventObject;import com.mxgraph.util.mxEventSource;import com.mxgraph.util.mxPoint;import com.mxgraph.util.mxUndoableEdit;/** * Extends mxEventSource to implement a graph model. The graph model acts as * a wrapper around the cells which are in charge of storing the actual graph * datastructure. The model acts as a transactional wrapper with event * notification for all changes, whereas the cells contain the atomic * operations for updating the actual datastructure. * * Layers: * * The cell hierarchy in the model must have a top-level root cell which * contains the layers (typically one default layer), which in turn contain the * top-level cells of the layers. This means each cell is contained in a layer. * If no layers are required, then all new cells should be added to the default * layer. * * Layers are useful for hiding and showing groups of cells, or for placing * groups of cells on top of other cells in the display. To identify a layer, * the <isLayer> function is used. It returns true if the parent of the given * cell is the root of the model. * * See mxIGraphModel for a list of standard events. */public class mxGraphModel extends mxEventSource implements mxIGraphModel{ /** * Holds the root cell, which in turn contains the cells that represent the * layers of the diagram as child cells. That is, the actual element of the * diagram are supposed to live in the third generation of cells and below. */ protected mxICell root; /** * Maps from Ids to cells. */ protected Hashtable cells; /** * Specifies if edges should automatically be moved into the nearest common * ancestor of their terminals. Default is true. */ protected boolean maintainEdgeParent = true; /** * Specifies if the model should automatically create Ids for new cells. * Default is true. */ protected boolean createIds = true; /** * Specifies the next Id to be created. Initial value is 0. */ protected int nextId = 0; /** * Holds the changes for the current transaction. If the transaction is * closed then a new object is created for this variable using * createUndoableEdit. */ protected transient mxUndoableEdit currentEdit; /** * Counter for the depth of nested transactions. Each call to beginUpdate * increments this counter and each call to endUpdate decrements it. When * the counter reaches 0, the transaction is closed and the respective * events are fired. Initial value is 0. */ protected transient int updateLevel = 0; /** * */ protected transient boolean endingUpdate = false; /** * Constructs a new empty graph model. */ public mxGraphModel() { this(null); } /** * Constructs a new graph model. If no root is specified * then a new root mxCell with a default layer is created. * * @param root Cell that represents the root cell. */ public mxGraphModel(Object root) { currentEdit = createUndoableEdit(); if (root == null) { root = createRoot(); } setRoot(root); } /** * */ public int getUpdateLevel() { return updateLevel; } /** * Creates a new root cell with a default layer (child 0). */ public Object createRoot() { mxCell root = new mxCell(); root.insert(new mxCell()); return root; } /** * Returns the internal lookup table that is used to map from Ids to cells. */ public Hashtable getCells() { return cells; } /** * Returns the cell for the specified Id or null if no cell can be * found for the given Id. * * @param id A string representing the Id of the cell. * @return Returns the cell for the given Id. */ public Object getCell(String id) { Object result = null; if (cells != null) { result = cells.get(id); } return result; } /** * Returns true if the model automatically update parents of edges so that * the edge is contained in the nearest-common-ancestor of its terminals. * * @return Returns true if the model maintains edge parents. */ public boolean isMaintainEdgeParent() { return maintainEdgeParent; } /** * Specifies if the model automatically updates parents of edges so that * the edge is contained in the nearest-common-ancestor of its terminals. * * @param maintainEdgeParent Boolean indicating if the model should * maintain edge parents. */ public void setMaintainEdgeParent(boolean maintainEdgeParent) { this.maintainEdgeParent = maintainEdgeParent; } /** * Returns true if the model automatically creates Ids and resolves Id * collisions. * * @return Returns true if the model creates Ids. */ public boolean isCreateIds() { return createIds; } /** * Specifies if the model automatically creates Ids for new cells and * resolves Id collisions. * * @param value Boolean indicating if the model should created Ids. */ public void setCreateIds(boolean value) { createIds = value; } /* (non-Javadoc) * @see com.mxgraph.model.mxIGraphModel#getRoot() */ public Object getRoot() { return root; } /* (non-Javadoc) * @see com.mxgraph.model.mxIGraphModel#setRoot(Object) */ public Object setRoot(Object root) { execute(new mxRootChange(this, root)); return root; } /** * Inner callback to change the root of the model and update the internal * datastructures, such as cells and nextId. Returns the previous root. */ protected Object rootChanged(Object root) { Object oldRoot = this.root; this.root = (mxICell) root; // Resets counters and datastructures nextId = 0; cells = null; cellAdded(root); return oldRoot; } /** * Creates a new undoable edit. */ protected mxUndoableEdit createUndoableEdit() { return new mxUndoableEdit(this) { public void dispatch() { ((mxGraphModel) source).fireEvent(mxEvent.CHANGE, new mxEventObject(new Object[] { changes })); } }; } /* (non-Javadoc) * @see com.mxgraph.model.mxIGraphModel#cloneCells(Object[], boolean) */ public Object[] cloneCells(Object[] cells, boolean includeChildren) { Map mapping = new Hashtable(); Object[] clones = new Object[cells.length]; for (int i = 0; i < cells.length; i++) { try { clones[i] = cloneCell(cells[i], mapping, includeChildren); } catch (CloneNotSupportedException e) { // ignore } } for (int i = 0; i < cells.length; i++) { restoreClone(clones[i], cells[i], mapping); } return clones; } /** * Inner helper method for cloning cells recursively. */ protected Object cloneCell(Object cell, Map mapping, boolean includeChildren) throws CloneNotSupportedException { if (cell instanceof mxICell) { mxICell mxc = (mxICell) ((mxICell) cell).clone(); mapping.put(cell, mxc); if (includeChildren) { int childCount = getChildCount(cell); for (int i = 0; i < childCount; i++) { Object clone = cloneCell(getChildAt(cell, i), mapping, true); mxc.insert((mxICell) clone); } } return mxc; } return null; } /** * Inner helper method for restoring the connections in * a network of cloned cells. */ protected void restoreClone(Object clone, Object cell, Map mapping) { if (clone instanceof mxICell) { mxICell mxc = (mxICell) clone; Object source = getTerminal(cell, true); if (source instanceof mxICell) { mxICell tmp = (mxICell) mapping.get(source); if (tmp != null) { tmp.insertEdge(mxc, true); } } Object target = getTerminal(cell, false); if (target instanceof mxICell) { mxICell tmp = (mxICell) mapping.get(target); if (tmp != null) { tmp.insertEdge(mxc, false); } } } int childCount = getChildCount(clone); for (int i = 0; i < childCount; i++) { restoreClone(getChildAt(clone, i), getChildAt(cell, i), mapping); } } /* (non-Javadoc) * @see com.mxgraph.model.mxIGraphModel#isAncestor(Object, Object) */ public boolean isAncestor(Object parent, Object child) { while (child != null && child != parent) { child = getParent(child); } return child == parent; } /* (non-Javadoc) * @see com.mxgraph.model.mxIGraphModel#contains(Object) */ public boolean contains(Object cell) { return isAncestor(getRoot(), cell); } /* (non-Javadoc) * @see com.mxgraph.model.mxIGraphModel#getParent(Object) */ public Object getParent(Object child) { return (child instanceof mxICell) ? ((mxICell) child).getParent() : null; } /* (non-Javadoc) * @see com.mxgraph.model.mxIGraphModel#add(Object, Object, int) */ public Object add(Object parent, Object child, int index) { if (child != parent && parent != null && child != null) { boolean parentChanged = parent != getParent(child); execute(new mxChildChange(this, parent, child, index)); // Maintains the edges parents by moving the edges // into the nearest common ancestor of its // terminals if (maintainEdgeParent && parentChanged) { updateEdgeParents(child); } } return child; } /** * Invoked after a cell has been added to a parent. This recursively * creates an Id for the new cell and/or resolves Id collisions. * * @param cell Cell that has been added. */ protected void cellAdded(Object cell) { if (cell instanceof mxICell) { mxICell mxc = (mxICell) cell; if (mxc.getId() == null && isCreateIds()) { mxc.setId(createId(cell)); } if (mxc.getId() != null) { Object collision = getCell(mxc.getId()); if (collision != cell) { while (collision != null) { mxc.setId(createId(cell)); collision = getCell(mxc.getId()); } if (cells == null) { cells = new Hashtable(); } cells.put(mxc.getId(), cell); } } // Makes sure IDs of deleted cells are not reused try { int id = Integer.parseInt(mxc.getId()); nextId = Math.max(nextId, id + 1); } catch (NumberFormatException e) { // ignore } int childCount = mxc.getChildCount(); for (int i = 0; i < childCount; i++) { cellAdded(mxc.getChildAt(i)); } } } /** * Creates a new Id for the given cell and increments the global counter * for creating new Ids. * * @param cell Cell for which a new Id should be created. * @return Returns a new Id for the given cell. */ public String createId(Object cell) { String id = String.valueOf(nextId); nextId++; return id; } /* (non-Javadoc) * @see com.mxgraph.model.mxIGraphModel#remove(Object) */ public Object remove(Object cell) { if (cell == root) { setRoot(null); } else if (getParent(cell) != null) { execute(new mxChildChange(this, null, cell)); } return cell; } /** * Invoked after a cell has been removed from the model. This recursively * removes the cell from its terminals and removes the mapping from the Id * to the cell. * * @param cell Cell that has been removed. */ protected void cellRemoved(Object cell) { if (cell instanceof mxICell) { mxICell mxc = (mxICell) cell; int childCount = mxc.getChildCount(); for (int i = 0; i < childCount; i++) { cellRemoved(mxc.getChildAt(i)); } if (cells != null && mxc.getId() != null) { cells.remove(mxc.getId()); } } } /** * Inner callback to update the parent of a cell using mxCell.insert * on the parent and return the previous parent. */ protected Object parentForCellChanged(Object cell, Object parent, int index) { mxICell child = (mxICell) cell; mxICell previous = (mxICell) getParent(cell); if (parent != null) { if (parent != previous || previous.getIndex(child) != index) { ((mxICell) parent).insert(child, index); } } else if (previous != null) { int oldIndex = previous.getIndex(child); previous.remove(oldIndex); } // Checks if the previous parent was already in the // model and avoids calling cellAdded if it was. if (!contains(previous) && parent != null) { cellAdded(cell); } else if (parent == null) { cellRemoved(cell); } return previous; } /* (non-Javadoc) * @see com.mxgraph.model.mxIGraphModel#getChildCount(Object) */ public int getChildCount(Object cell) { return (cell instanceof mxICell) ? ((mxICell) cell).getChildCount() : 0; }
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -