KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > prefuse > data > column > ExpressionColumn


1 package prefuse.data.column;
2
3 import java.util.BitSet JavaDoc;
4 import java.util.Iterator JavaDoc;
5 import java.util.Set JavaDoc;
6
7 import prefuse.data.DataTypeException;
8 import prefuse.data.Table;
9 import prefuse.data.event.ColumnListener;
10 import prefuse.data.event.EventConstants;
11 import prefuse.data.event.ExpressionListener;
12 import prefuse.data.expression.Expression;
13 import prefuse.data.expression.ExpressionAnalyzer;
14
15 /**
16  * <p>Column instance that stores values provided by an Expression
17  * instance. These expressions can reference other column values within the
18  * same table. Values are evaluated when first requested and then cached to
19  * increase performance. This column maintains listeners for all referenced
20  * columns discovered in the expression and for the expression itself,
21  * invalidating all cached entries when an update to either occurs.</p>
22  *
23  * <p>
24  * WARNING: Infinite recursion, eventually resulting in a StackOverflowError,
25  * could occur if an expression refers to its own column, or if two
26  * ExpressionColumns have expressions referring to each other. The
27  * responsibility for avoiding such situations is left with client programmers.
28  * Note that it is fine for one ExpressionColumn to reference another;
29  * however, the graph induced by such references must not contain any cycles.
30  * </p>
31  *
32  * @author <a HREF="http://jheer.org">jeffrey heer</a>
33  * @see prefuse.data.expression
34  */

35 public class ExpressionColumn extends AbstractColumn {
36     
37     private Expression m_expr;
38     private Table m_table;
39     private Set JavaDoc m_columns;
40     
41     private BitSet JavaDoc m_valid;
42     private Column m_cache;
43     private Listener m_lstnr;
44     
45     /**
46      * Create a new ExpressionColumn.
47      * @param table the table this column is a member of
48      * @param expr the expression used to provide the column values
49      */

50     public ExpressionColumn(Table table, Expression expr) {
51         super(expr.getType(table.getSchema()));
52         m_table = table;
53         m_expr = expr;
54         m_lstnr = new Listener();
55         
56         init();
57         
58         int nrows = m_table.getRowCount();
59         m_cache = ColumnFactory.getColumn(getColumnType(), nrows);
60         m_valid = new BitSet JavaDoc(nrows);
61         m_expr.addExpressionListener(m_lstnr);
62     }
63     
64     protected void init() {
65         // first remove listeners on any current columns
66
if ( m_columns != null && m_columns.size() > 0 ) {
67             Iterator JavaDoc iter = m_columns.iterator();
68             while ( iter.hasNext() ) {
69                 String JavaDoc field = (String JavaDoc)iter.next();
70                 Column col = m_table.getColumn(field);
71                 col.removeColumnListener(m_lstnr);
72             }
73         }
74         // now get the current set of columns
75
m_columns = ExpressionAnalyzer.getReferencedColumns(m_expr);
76         
77         // sanity check table and expression
78
Iterator JavaDoc iter = m_columns.iterator();
79         while ( iter.hasNext() ) {
80             String JavaDoc name = (String JavaDoc)iter.next();
81             if ( m_table.getColumn(name) == null )
82                 throw new IllegalArgumentException JavaDoc("Table must contain all "
83                         + "columns referenced by the expression."
84                         + " Bad column name: "+name);
85             
86         }
87         
88         // passed check, so now listen to columns
89
iter = m_columns.iterator();
90         while ( iter.hasNext() ) {
91             String JavaDoc field = (String JavaDoc)iter.next();
92             Column col = m_table.getColumn(field);
93             col.addColumnListener(m_lstnr);
94         }
95     }
96     
97     // ------------------------------------------------------------------------
98
// Column Metadata
99

100     /**
101      * @see prefuse.data.column.Column#getRowCount()
102      */

103     public int getRowCount() {
104         return m_cache.getRowCount();
105     }
106
107     /**
108      * @see prefuse.data.column.Column#setMaximumRow(int)
109      */

110     public void setMaximumRow(int nrows) {
111         m_cache.setMaximumRow(nrows);
112     }
113
114     // ------------------------------------------------------------------------
115
// Cache Management
116

117     /**
118      * Check if this ExpressionColumn has a valid cached value at the given
119      * row.
120      * @param row the row to check for a valid cache entry
121      * @return true if the cache row is valid, false otherwise
122      */

123     public boolean isCacheValid(int row) {
124         return m_valid.get(row);
125     }
126     
127     /**
128      * Invalidate a range of the cache.
129      * @param start the start of the range to invalidate
130      * @param end the end of the range to invalidate, inclusive
131      */

132     public void invalidateCache(int start, int end ) {
133         m_valid.clear(start, end+1);
134     }
135     
136     // ------------------------------------------------------------------------
137
// Data Access Methods
138

139     /**
140      * Has no effect, as all values in this column are derived.
141      * @param row the row to revert
142      */

143     public void revertToDefault(int row) {
144         // do nothing, as we don't have default values.
145
}
146     
147     /**
148      * @see prefuse.data.column.AbstractColumn#canSet(java.lang.Class)
149      */

150     public boolean canSet(Class JavaDoc type) {
151         return false;
152     }
153     
154     /**
155      * @see prefuse.data.column.Column#get(int)
156      */

157     public Object JavaDoc get(int row) {
158         rangeCheck(row);
159         if ( isCacheValid(row) ) {
160             return m_cache.get(row);
161         }
162         Object JavaDoc val = m_expr.get(m_table.getTuple(row));
163         Class JavaDoc type = val==null ? Object JavaDoc.class : val.getClass();
164         if ( m_cache.canSet(type) ) {
165             m_cache.set(val, row);
166             m_valid.set(row);
167         }
168         return val;
169     }
170
171     /**
172      * @see prefuse.data.column.Column#set(java.lang.Object, int)
173      */

174     public void set(Object JavaDoc val, int row) throws DataTypeException {
175         throw new UnsupportedOperationException JavaDoc();
176     }
177     
178     private void rangeCheck(int row) {
179         if ( row < 0 || row >= getRowCount() )
180             throw new IndexOutOfBoundsException JavaDoc();
181     }
182     
183     // ------------------------------------------------------------------------
184

185     /**
186      * @see prefuse.data.column.Column#getBoolean(int)
187      */

188     public boolean getBoolean(int row) throws DataTypeException {
189         if ( !canGetBoolean() )
190             throw new DataTypeException(boolean.class);
191         rangeCheck(row);
192         
193         if ( isCacheValid(row) ) {
194             return m_cache.getBoolean(row);
195         } else {
196             boolean value = m_expr.getBoolean(m_table.getTuple(row));
197             m_cache.setBoolean(value, row);
198             m_valid.set(row);
199             return value;
200         }
201     }
202
203     private void computeNumber(int row) {
204         if ( m_columnType == int.class || m_columnType == byte.class ) {
205             m_cache.setInt(m_expr.getInt(m_table.getTuple(row)), row);
206         } else if ( m_columnType == long.class ) {
207             m_cache.setLong(m_expr.getLong(m_table.getTuple(row)), row);
208         } else if ( m_columnType == float.class ) {
209             m_cache.setFloat(m_expr.getFloat(m_table.getTuple(row)), row);
210         } else {
211             m_cache.setDouble(m_expr.getDouble(m_table.getTuple(row)), row);
212         }
213         m_valid.set(row);
214     }
215     
216     /**
217      * @see prefuse.data.column.Column#getInt(int)
218      */

219     public int getInt(int row) throws DataTypeException {
220         if ( !canGetInt() )
221             throw new DataTypeException(int.class);
222         rangeCheck(row);
223         
224         if ( !isCacheValid(row) )
225             computeNumber(row);
226         return m_cache.getInt(row);
227     }
228
229     /**
230      * @see prefuse.data.column.Column#getDouble(int)
231      */

232     public double getDouble(int row) throws DataTypeException {
233         if ( !canGetDouble() )
234             throw new DataTypeException(double.class);
235         rangeCheck(row);
236         
237         if ( !isCacheValid(row) )
238             computeNumber(row);
239         return m_cache.getDouble(row);
240     }
241
242     /**
243      * @see prefuse.data.column.Column#getFloat(int)
244      */

245     public float getFloat(int row) throws DataTypeException {
246         if ( !canGetFloat() )
247             throw new DataTypeException(float.class);
248         rangeCheck(row);
249         
250         if ( !isCacheValid(row) )
251             computeNumber(row);
252         return m_cache.getFloat(row);
253     }
254
255     /**
256      * @see prefuse.data.column.Column#getLong(int)
257      */

258     public long getLong(int row) throws DataTypeException {
259         if ( !canGetLong() )
260             throw new DataTypeException(long.class);
261         rangeCheck(row);
262         
263         if ( !isCacheValid(row) )
264             computeNumber(row);
265         return m_cache.getLong(row);
266     }
267     
268     // ------------------------------------------------------------------------
269
// Listener Methods
270

271     private class Listener implements ColumnListener, ExpressionListener {
272     
273         public void columnChanged(int start, int end) {
274             // for a single index change with a valid cache value,
275
// propagate a change event with the previous value
276
if ( start == end && isCacheValid(start) ) {
277                 if ( !m_table.isValidRow(start) ) return;
278                 
279                 // invalidate the cache index
280
invalidateCache(start, end);
281                 // fire change event including previous value
282
Class JavaDoc type = getColumnType();
283                 if ( int.class == type ) {
284                     fireColumnEvent(start, m_cache.getInt(start));
285                 } else if ( long.class == type ) {
286                     fireColumnEvent(start, m_cache.getLong(start));
287                 } else if ( float.class == type ) {
288                     fireColumnEvent(start, m_cache.getFloat(start));
289                 } else if ( double.class == type ) {
290                     fireColumnEvent(start, m_cache.getDouble(start));
291                 } else if ( boolean.class == type ) {
292                     fireColumnEvent(start, m_cache.getBoolean(start));
293                 } else {
294                     fireColumnEvent(start, m_cache.get(start));
295                 }
296                 
297             // otherwise send a generic update
298
} else {
299                 // invalidate cache indices
300
invalidateCache(start, end);
301                 // fire change event
302
fireColumnEvent(EventConstants.UPDATE, start, end);
303             }
304         }
305         
306         public void columnChanged(Column src, int idx, boolean prev) {
307             columnChanged(idx, idx);
308         }
309     
310         public void columnChanged(Column src, int idx, double prev) {
311             columnChanged(idx, idx);
312         }
313     
314         public void columnChanged(Column src, int idx, float prev) {
315             columnChanged(idx, idx);
316         }
317     
318         public void columnChanged(Column src, int type, int start, int end) {
319             columnChanged(start, end);
320         }
321     
322         public void columnChanged(Column src, int idx, int prev) {
323             columnChanged(idx, idx);
324         }
325     
326         public void columnChanged(Column src, int idx, long prev) {
327             columnChanged(idx, idx);
328         }
329     
330         public void columnChanged(Column src, int idx, Object JavaDoc prev) {
331             columnChanged(idx, idx);
332         }
333     
334         public void expressionChanged(Expression expr) {
335             // mark everything as changed
336
columnChanged(0, m_cache.getRowCount()-1);
337             // re-initialize our setup
338
init();
339         }
340     }
341     
342 } // end of class DerivedColumn
343
Popular Tags