001: package prefuse.data.tuple;
002:
003: import java.util.HashSet;
004: import java.util.Iterator;
005: import java.util.LinkedHashMap;
006: import java.util.Map;
007: import java.util.Set;
008: import java.util.logging.Logger;
009:
010: import prefuse.data.Table;
011: import prefuse.data.Tuple;
012: import prefuse.data.event.TupleSetListener;
013: import prefuse.data.expression.Expression;
014: import prefuse.data.expression.Predicate;
015: import prefuse.data.expression.parser.ExpressionParser;
016: import prefuse.util.collections.CompositeIterator;
017:
018: /**
019: * <p>TupleSet implementation for treating a collection of tuple sets
020: * as a single, composite tuple set. This composite does not take
021: * overlap between contained TupleSets into account.</p>
022: *
023: * <p>The {@link TupleSet#addTuple(Tuple)} and {@link #setTuple(Tuple)}
024: * methods are not supported by this class, and calling these methods will
025: * result in a UnsupportedOperationException. Instead, use the add or set
026: * methods on the desired non-composite tuple set.</p>
027: *
028: * @author <a href="http://jheer.org">jeffrey heer</a>
029: */
030: public class CompositeTupleSet extends AbstractTupleSet {
031:
032: private static final Logger s_logger = Logger
033: .getLogger(CompositeTupleSet.class.getName());
034:
035: private Map m_map; // map names to tuple sets
036: private Set m_sets; // support quick reverse lookup
037: private int m_count; // count of total tuples
038: private Listener m_lstnr;
039:
040: /**
041: * Create a new, empty CompositeTupleSet
042: */
043: public CompositeTupleSet() {
044: this (true);
045: }
046:
047: protected CompositeTupleSet(boolean listen) {
048: m_map = new LinkedHashMap();
049: m_sets = new HashSet();
050: m_count = 0;
051: m_lstnr = listen ? new Listener() : null;
052: }
053:
054: /**
055: * Add a TupleSet to this composite.
056: * @param name the name of the TupleSet to add
057: * @param set the set to add
058: */
059: public void addSet(String name, TupleSet set) {
060: if (hasSet(name)) {
061: throw new IllegalArgumentException("Name already in use: "
062: + name);
063: }
064: m_map.put(name, set);
065: m_sets.add(set);
066: m_count += set.getTupleCount();
067: if (m_lstnr != null)
068: set.addTupleSetListener(m_lstnr);
069: }
070:
071: /**
072: * Indicates if this composite contains a TupleSet with the given name.
073: * @param name the name to look for
074: * @return true if a TupleSet with the given name is found, false otherwise
075: */
076: public boolean hasSet(String name) {
077: return m_map.containsKey(name);
078: }
079:
080: /**
081: * Indicates if this composite contains the given TupleSet.
082: * @param set the TupleSet to check for containment
083: * @return true if the TupleSet is contained in this composite,
084: * false otherwise
085: */
086: public boolean containsSet(TupleSet set) {
087: return m_sets.contains(set);
088: }
089:
090: /**
091: * Get the TupleSet associated with the given name.
092: * @param name the name of the TupleSet to get
093: * @return the associated TupleSet, or null if not found
094: */
095: public TupleSet getSet(String name) {
096: return (TupleSet) m_map.get(name);
097: }
098:
099: /**
100: * Get an iterator over the names of all the TupleSets in this composite.
101: * @return the iterator over contained set names.
102: */
103: public Iterator setNames() {
104: return m_map.keySet().iterator();
105: }
106:
107: /**
108: * Get an iterator over all the TupleSets in this composite.
109: * @return the iterator contained sets.
110: */
111: public Iterator sets() {
112: return m_map.values().iterator();
113: }
114:
115: /**
116: * Remove the TupleSet with the given name from this composite.
117: * @param name the name of the TupleSet to remove
118: * @return the removed TupleSet, or null if not found
119: */
120: public TupleSet removeSet(String name) {
121: TupleSet ts = (TupleSet) m_map.remove(name);
122: if (ts != null) {
123: m_sets.remove(ts);
124: if (m_lstnr != null)
125: ts.removeTupleSetListener(m_lstnr);
126: }
127: return ts;
128: }
129:
130: /**
131: * Remove all contained TupleSets from this composite.
132: */
133: public void removeAllSets() {
134: Iterator sets = m_map.entrySet().iterator();
135: while (sets.hasNext()) {
136: Map.Entry entry = (Map.Entry) sets.next();
137: TupleSet ts = (TupleSet) entry.getValue();
138: sets.remove();
139: m_sets.remove(ts);
140: if (m_lstnr != null)
141: ts.removeTupleSetListener(m_lstnr);
142: }
143: m_count = 0;
144: }
145:
146: /**
147: * Clear this TupleSet, calling clear on all contained TupleSet
148: * instances. All contained TupleSets remain members of this
149: * composite, but they have their data cleared.
150: * @see prefuse.data.tuple.TupleSet#clear()
151: */
152: public void clear() {
153: Iterator sets = m_map.entrySet().iterator();
154: while (sets.hasNext()) {
155: Map.Entry entry = (Map.Entry) sets.next();
156: ((TupleSet) entry.getValue()).clear();
157: }
158: m_count = 0;
159: }
160:
161: // ------------------------------------------------------------------------
162: // TupleSet Interface
163:
164: /**
165: * Not supported.
166: * @see prefuse.data.tuple.TupleSet#addTuple(prefuse.data.Tuple)
167: */
168: public Tuple addTuple(Tuple t) {
169: throw new UnsupportedOperationException();
170: }
171:
172: /**
173: * Not supported.
174: * @see prefuse.data.tuple.TupleSet#setTuple(prefuse.data.Tuple)
175: */
176: public Tuple setTuple(Tuple t) {
177: throw new UnsupportedOperationException();
178: }
179:
180: /**
181: * Removes the tuple from its source set if that source set is contained
182: * within this composite.
183: * @see prefuse.data.tuple.TupleSet#removeTuple(prefuse.data.Tuple)
184: */
185: public boolean removeTuple(Tuple t) {
186: Table table = t.getTable();
187: if (m_sets.contains(table)) {
188: return table.removeTuple(t);
189: } else {
190: return false;
191: }
192: }
193:
194: /**
195: * @see prefuse.data.tuple.TupleSet#containsTuple(prefuse.data.Tuple)
196: */
197: public boolean containsTuple(Tuple t) {
198: Iterator it = m_map.entrySet().iterator();
199: while (it.hasNext()) {
200: Map.Entry entry = (Map.Entry) it.next();
201: TupleSet ts = (TupleSet) entry.getValue();
202: if (ts.containsTuple(t))
203: return true;
204: }
205: return false;
206: }
207:
208: /**
209: * @see prefuse.data.tuple.TupleSet#getTupleCount()
210: */
211: public int getTupleCount() {
212: if (m_lstnr != null) {
213: return m_count;
214: } else {
215: int count = 0;
216: Iterator it = m_map.entrySet().iterator();
217: for (int i = 0; it.hasNext(); ++i) {
218: Map.Entry entry = (Map.Entry) it.next();
219: TupleSet ts = (TupleSet) entry.getValue();
220: count += ts.getTupleCount();
221: }
222: return count;
223: }
224: }
225:
226: /**
227: * @see prefuse.data.tuple.TupleSet#tuples()
228: */
229: public Iterator tuples() {
230: CompositeIterator ci = new CompositeIterator(m_map.size());
231: Iterator it = m_map.entrySet().iterator();
232: for (int i = 0; it.hasNext(); ++i) {
233: Map.Entry entry = (Map.Entry) it.next();
234: TupleSet ts = (TupleSet) entry.getValue();
235: ci.setIterator(i, ts.tuples());
236: }
237: return ci;
238: }
239:
240: /**
241: * @see prefuse.data.tuple.TupleSet#tuples(prefuse.data.expression.Predicate)
242: */
243: public Iterator tuples(Predicate filter) {
244: CompositeIterator ci = new CompositeIterator(m_map.size());
245: Iterator it = m_map.entrySet().iterator();
246: for (int i = 0; it.hasNext(); ++i) {
247: Map.Entry entry = (Map.Entry) it.next();
248: TupleSet ts = (TupleSet) entry.getValue();
249: ci.setIterator(i, ts.tuples(filter));
250: }
251: return ci;
252: }
253:
254: // -- Data Field Methods --------------------------------------------------
255:
256: /**
257: * Returns true.
258: * @see prefuse.data.tuple.TupleSet#isAddColumnSupported()
259: */
260: public boolean isAddColumnSupported() {
261: return true;
262: }
263:
264: /**
265: * Adds the value to all contained TupleSets that return a true value for
266: * {@link TupleSet#isAddColumnSupported()}.
267: * @see prefuse.data.tuple.TupleSet#addColumn(java.lang.String, java.lang.Class, java.lang.Object)
268: */
269: public void addColumn(String name, Class type, Object defaultValue) {
270: Iterator it = m_map.entrySet().iterator();
271: while (it.hasNext()) {
272: Map.Entry entry = (Map.Entry) it.next();
273: TupleSet ts = (TupleSet) entry.getValue();
274: if (ts.isAddColumnSupported()) {
275: try {
276: ts.addColumn(name, type, defaultValue);
277: } catch (IllegalArgumentException iae) {
278: // already exists
279: }
280: } else {
281: s_logger
282: .fine("Skipped addColumn for " + entry.getKey());
283: }
284: }
285: }
286:
287: /**
288: * Adds the value to all contained TupleSets that return a true value for
289: * {@link TupleSet#isAddColumnSupported()}.
290: * @see prefuse.data.tuple.TupleSet#addColumn(java.lang.String, java.lang.Class)
291: */
292: public void addColumn(String name, Class type) {
293: Iterator it = m_map.entrySet().iterator();
294: while (it.hasNext()) {
295: Map.Entry entry = (Map.Entry) it.next();
296: TupleSet ts = (TupleSet) entry.getValue();
297: if (ts.isAddColumnSupported()) {
298: try {
299: ts.addColumn(name, type);
300: } catch (IllegalArgumentException iae) {
301: // already exists
302: }
303: } else {
304: s_logger
305: .fine("Skipped addColumn for " + entry.getKey());
306: }
307: }
308: }
309:
310: /**
311: * Adds the value to all contained TupleSets that return a true value for
312: * {@link TupleSet#isAddColumnSupported()}.
313: * @see prefuse.data.tuple.TupleSet#addColumn(java.lang.String, prefuse.data.expression.Expression)
314: */
315: public void addColumn(String name, Expression expr) {
316: Iterator it = m_map.entrySet().iterator();
317: while (it.hasNext()) {
318: Map.Entry entry = (Map.Entry) it.next();
319: TupleSet ts = (TupleSet) entry.getValue();
320: if (ts.isAddColumnSupported()) {
321: try {
322: ts.addColumn(name, expr);
323: } catch (IllegalArgumentException iae) {
324: // already exists
325: }
326: } else {
327: s_logger
328: .fine("Skipped addColumn for " + entry.getKey());
329: }
330: }
331: }
332:
333: /**
334: * Adds the value to all contained TupleSets that return a true value for
335: * {@link TupleSet#isAddColumnSupported()}.
336: * @see prefuse.data.tuple.TupleSet#addColumn(java.lang.String, java.lang.String)
337: */
338: public void addColumn(String name, String expr) {
339: Expression ex = ExpressionParser.parse(expr);
340: Throwable t = ExpressionParser.getError();
341: if (t != null) {
342: throw new RuntimeException(t);
343: } else {
344: addColumn(name, ex);
345: }
346: }
347:
348: // ------------------------------------------------------------------------
349: // Internal TupleSet Listener
350:
351: /**
352: * Listener that relays tuple set change events as they occur and updates
353: * the total tuple count appropriately.
354: */
355: private class Listener implements TupleSetListener {
356: public void tupleSetChanged(TupleSet tset, Tuple[] add,
357: Tuple[] rem) {
358: m_count += add.length - rem.length;
359: fireTupleEvent(add, rem);
360: }
361: }
362:
363: } // end of class CompositeTupleSet
|