/* * @(#)GraphContext.java 1.0 03-JUL-04 * * Copyright (c) 2001-2004 Gaudenz Alder * */ package org.jgraph.graph; import java.util.ArrayList; 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 org.jgraph.JGraph; /* * This object is used for interactive operations such as move and copy. It * offers two basic operations: 1. Create temporary views for a set of cells, * and 2. Create temporary views for a set of edges that are connected to a set * of cells.<p> <strong>Note:</strong>The views are consistent subgraphs. * Consequently, if an edge points to a selected and an unselected port, the * selected port is replaced by its temporary view, but the unselected port is * not. * * @version 1.0 1/1/02 @author Gaudenz Alder */ @SuppressWarnings ( "all" ) public class GraphContext implements CellMapper { /** * Switch to enable the preview of edge groups, that is, edges that 1 or * more children, as part of the context cells. */ public static boolean PREVIEW_EDGE_GROUPS = false; /** Reference to the parent graph. */ protected JGraph graph; /** Reference to the graphs GraphLayoutCache. */ protected transient GraphLayoutCache graphLayoutCache; /** Reference to the cells. */ protected Object[] cells; /** Set of all cells including all descendants. */ protected Set allCells, cellSet; /** Number of all descendants without ports. */ protected int cellCount; /** Map of (cell, view) pairs including ports. */ protected Map views = new Hashtable(); /** * Constructs a graph context for <code>cells</code> with respect to the * connections defined in the model, and the views in the view of * <code>graph</code>. */ public GraphContext(JGraph graph, Object[] cells) { GraphModel model = graph.getModel(); allCells = new HashSet(DefaultGraphModel.getDescendants(model, cells)); graphLayoutCache = graph.getGraphLayoutCache(); this.graph = graph; this.cells = cells; // Count Visible Non-Port Cells cellSet = new HashSet(); Iterator it = allCells.iterator(); while (it.hasNext()) { Object cell = it.next(); if (graphLayoutCache.isVisible(cell)) { cellSet.add(cell); if (!model.isPort(cell)) cellCount++; } } } /** * Returns <code>true</code> if this object contains no cells. */ public boolean isEmpty() { return (cells == null || cells.length == 0); } /** * Returns the number of all objects (cells and children) in this object. */ public int getDescendantCount() { return cellCount; } /** * Returns the graph that was passed to the constructor. */ public JGraph getGraph() { return graph; } /** * Returns the array that was passed to the constructor. */ public Object[] getCells() { return cells; } /** * Returns <code>true</code> if <code>node</code> or one of its * ancestors is contained in this object and visible in the original graph. */ public boolean contains(Object node) { return cellSet.contains(node); } /** * Returns an new consistent array of views for <code>cells</code>. */ public CellView[] createTemporaryCellViews() { CellView[] cellViews = new CellView[cells.length]; for (int i = 0; i < cells.length; i++) // Get View For Cell cellViews[i] = getMapping(cells[i], true); return cellViews; } /** * Returns an new consistent array of views for the ports. */ public CellView[] createTemporaryPortViews() { GraphModel model = graph.getModel(); ArrayList result = new ArrayList(); Iterator it = allCells.iterator(); while (it.hasNext()) { Object cand = it.next(); if (model.isPort(cand) && graph.getGraphLayoutCache().isVisible(cand)) result.add(getMapping(cand, true)); } // List -> CellView[] Conversion CellView[] array = new PortView[result.size()]; result.toArray(array); return array; } /** * Returns an new consistent array of views for the edges that are connected * to and not contained in <code>cells</code>. */ public CellView[] createTemporaryContextViews() { return createTemporaryContextViews(cellSet); } /** * Returns an new consistent array of views for the edges that are connected * to and not contained in <code>cellSet</code>. */ public CellView[] createTemporaryContextViews(Set cellSet) { Object[] cells = cellSet.toArray(); // Retrieve Edges From Model (recursively for edges connected // to edges connected to edges...) List result = new ArrayList(); Set delta = DefaultGraphModel.getEdges(graph.getModel(), cells); do { // Iterate over Edges Iterator it = delta.iterator(); while (it.hasNext()) { Object obj = it.next(); CellView edge = graphLayoutCache.getMapping(obj, false); // If Edge not in cellset, add its view is visible in graphview if (!cellSet.contains(obj) && graphLayoutCache.isVisible(obj) && edge != null && (PREVIEW_EDGE_GROUPS || edge.isLeaf())) { // Note: Do not use getMapping, it ignores the create flag CellView preview = createMapping(obj); result.add(preview); // Create temporary children which refer to this // preview and adopt children in the preview CellView[] children = preview.getChildViews(); for (int i=0; i<children.length; i++) { children[i] = createMapping(children[i].getCell()); } // Adopts the children preview.refresh(graph.getGraphLayoutCache(), this, false); } } delta = DefaultGraphModel.getEdges(graph.getModel(), delta .toArray()); } while (!delta.isEmpty()); // List -> CellView[] Conversion CellView[] array = new CellView[result.size()]; result.toArray(array); return array; } /** * Returns the <code>CellView</code> that is mapped to <code>cell</code> * in the graph context. New views are created based on whether cell is * contained in the context. The <code>create</code>-flag is ignored. */ public CellView getMapping(Object cell, boolean create) { if (cell != null) { CellView view = (CellView) views.get(cell); if (view != null) return view; else if (contains(cell) || (graph.getModel().isPort(cell) && create && graph .getGraphLayoutCache().isVisible(cell))) return createMapping(cell); else return graphLayoutCache.getMapping(cell, false); } return null; } public CellView createMapping(Object cell) { CellView view = graphLayoutCache.getFactory().createView( graph.getModel(), cell); putMapping(cell, view); view.refresh(graph.getGraphLayoutCache(), this, true); // Create Dependent Views // Fetch Attributes From Original View CellView src = graphLayoutCache.getMapping(cell, false); if (src != null) { view.changeAttributes(graphLayoutCache, (AttributeMap) src.getAttributes().clone()); // Inserts portviews into points list view.refresh(graph.getGraphLayoutCache(), this, false); } return view; } /** * Disconnects the edges in <code>cells</code> from the sources and * targets that are not in this context and returns a ConnectionSet that * defines the disconnection. */ public ConnectionSet disconnect(CellView[] cells) { ConnectionSet cs = new ConnectionSet(); for (int i = 0; i < cells.length; i++) { if (cells[i] instanceof EdgeView) { EdgeView view = (EdgeView) cells[i]; CellView port = view.getSource(); if (GraphConstants.isDisconnectable(view.getAllAttributes())) { if (port != null && GraphConstants.isDisconnectable(port .getParentView().getAllAttributes()) && !contains(port.getCell())) { view.setSource(null); cs.disconnect(view.getCell(), true); } port = view.getTarget(); if (port != null && GraphConstants.isDisconnectable(port .getParentView().getAllAttributes()) && !contains(port.getCell())) { view.setTarget(null); cs.disconnect(view.getCell(), false); } } } } return cs; } /** * Associates <code>cell</code> with <code>view</code> in the graph * context. */ public void putMapping(Object cell, CellView view) { views.put(cell, view); } }