001: /*
002: * @(#)ParentMap.java 1.0 03-JUL-04
003: *
004: * Copyright (c) 2001-2004 Gaudenz Alder
005: *
006: */
007: package org.jgraph.graph;
008:
009: import java.io.Serializable;
010: import java.util.ArrayList;
011: import java.util.HashSet;
012: import java.util.Hashtable;
013: import java.util.Iterator;
014: import java.util.Map;
015: import java.util.Set;
016:
017: /**
018: * An object that describes relations between childs and parents.
019: *
020: * @version 1.0 1/1/02
021: * @author Gaudenz Alder
022: */
023:
024: public class ParentMap implements Serializable {
025:
026: /** Contents of the parent map. */
027: protected ArrayList entries = new ArrayList();
028:
029: /**
030: * Set of changed changedNodes for the parent map. Includes childs and
031: * parents.
032: */
033: protected Set changedNodes = new HashSet();
034:
035: /** Maps parents to integers with the future number of childs. */
036: protected Map childCount = new Hashtable();
037:
038: /**
039: * Constructs a <code>ParentMap</code> object.
040: */
041: public ParentMap() {
042: // empty
043: }
044:
045: /**
046: * Constructs a <code>ParentMap</code> object.
047: */
048: public ParentMap(Object[] children, Object parent) {
049: addEntries(children, parent);
050: }
051:
052: /**
053: * Returns a parent map that represents the insertion or removal of
054: * <code>cells</code> in <code>model</code> based on <code>remove</code>.
055: * Unselected childs of selected nodes are moved to the first unselected
056: * parent of that node.
057: * <p>
058: * <strong>Note:</strong> Consequently, cells "move up" one level when
059: * their parent is removed. <strong>Note:</strong> onlyUsePassedInCells can
060: * be used to indicate if only cells from the passed-in cell array are
061: * allowed parents. This is only used if remove is not true.
062: */
063: public static ParentMap create(GraphModel m, Object[] c,
064: boolean remove, boolean onlyUsePassedInCells) {
065: Set cellSet = new HashSet();
066: for (int i = 0; i < c.length; i++) {
067: // Do not add null cells otherwise the while loop lower down runs
068: // in an infinite loop
069: if (c[i] != null) {
070: cellSet.add(c[i]);
071: }
072: }
073: ParentMap parentMap = new ParentMap();
074: for (int i = 0; i < c.length; i++) {
075: // Collect Parent Information
076: Object parent = m.getParent(c[i]);
077: if (parent != null
078: && (!onlyUsePassedInCells || (!remove && cellSet
079: .contains(parent))))
080: parentMap.addEntry(c[i], (remove) ? null : parent);
081: if (remove) {
082: // Move Orphans to First Unselected Parent
083: while (cellSet.contains(parent)) {
084: // Make sure there are no nulls in the cell set or the
085: // while will get stuck in a loop
086: parent = m.getParent(parent);
087: }
088: for (int j = 0; j < m.getChildCount(c[i]); j++) {
089: Object child = m.getChild(c[i], j);
090: if (!cellSet.contains(child))
091: parentMap.addEntry(child, parent);
092: }
093: }
094: }
095: return parentMap;
096: }
097:
098: /**
099: * Add a new entry for this child, parent pair to the parent map. The child
100: * and parent are added to the set of changed nodes. Note: The previous
101: * parent is changed on execution of this parent map and must be added by
102: * the GraphModel and reflected by the GraphChange.getChanged method. TODO:
103: * In general, the GraphModel should be in charge of computing the set of
104: * changed cells.
105: */
106: public void addEntry(Object child, Object parent) {
107: if (child != null) {
108: entries.add(new Entry(child, parent));
109: // Update Changed Nodes
110: changedNodes.add(child);
111: if (parent != null)
112: changedNodes.add(parent);
113: }
114: }
115:
116: /**
117: * Adds all child parent pairs using addEntry.
118: */
119: public void addEntries(Object[] children, Object parent) {
120: for (int i = 0; i < children.length; i++)
121: addEntry(children[i], parent);
122: }
123:
124: /**
125: * Returns the number of entries.
126: */
127: public int size() {
128: return entries.size();
129: }
130:
131: /**
132: * Returns an <code>Iterator</code> for the entries in the map.
133: */
134: public Iterator entries() {
135: return entries.iterator();
136: }
137:
138: /**
139: * Returns a <code>Set</code> for the nodes, childs and parents, in this
140: * parent map.
141: */
142: public Set getChangedNodes() {
143: return changedNodes;
144: }
145:
146: /**
147: * Creates a new parent map based on this parent map, where the child and
148: * parents are mapped using <code>map</code>. If one the cells is not in
149: * <code>map</code>, then the original cell is used instead.
150: * <p>
151: */
152: public ParentMap clone(Map map) {
153: ParentMap pm = new ParentMap();
154: Iterator it = entries();
155: while (it.hasNext()) {
156: Entry e = (Entry) it.next();
157: Object child = map.get(e.getChild());
158: Object parent = map.get(e.getParent());
159: if (child == null)
160: child = e.getChild();
161: if (parent == null)
162: parent = e.getParent();
163: if (child != null && parent != null)
164: pm.addEntry(child, parent);
165: }
166: return pm;
167: }
168:
169: /**
170: * Object that represents the relation between a child an a parent.
171: */
172: public class Entry implements Serializable {
173:
174: /** Child and parent of the relation this entry describes. */
175: protected Object child, parent;
176:
177: /**
178: * Constructs a new relation between <code>child</code> and
179: * <code>parent</code>.
180: */
181: public Entry(Object child, Object parent) {
182: this .child = child;
183: this .parent = parent;
184: }
185:
186: /**
187: * Returns the child of the relation.
188: */
189: public Object getChild() {
190: return child;
191: }
192:
193: /**
194: * Returns the parent of the relation.
195: */
196: public Object getParent() {
197: return parent;
198: }
199: }
200:
201: public String toString() {
202: String s = super .toString() + "\n";
203: Iterator it = entries();
204: while (it.hasNext()) {
205: Entry entry = (Entry) it.next();
206: s += " child=" + entry.getChild() + " parent="
207: + entry.getParent() + "\n";
208: }
209: return s;
210: }
211:
212: }
|