/*
* @(#)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);
}
}