The prefuse expression language provides a convenient way of creating statements over data stored in a prefuse data structure. For example, the expression language can be used to write Predicate instances for querying and filtering a table or graph, or to create arbitrary expressions over a data set to generate new, derived data fields that can in turn be subject to additional processing or visualization. For example, the TupleSet.tuples(Predicate) method uses a Predicate to filter the requested set of tuples, and the Table.addColumn(String,Expression) method creates a new table column whose values are generated by the provided Expression. The expression machinery is used throughout the toolkit -- it underlies the filtering and query optimization features, is a key component of dynamic query bindings, and is used to create the rule chains evaluated by the DefaultRendererFactory, ColorAction, ShapeAction, FontAction, and SizeAction classes.
The Expression interface is quite simple: given a single Tuple, compute and return a value. The returned value could be an Object, a boolean, an int, or other primitive type. Individual methods for each type are available, and which ones are legal to call for any given Expression depends on the type of Expression.
Expressions can be created directly in Java, by instantiating and chaining together the desired Expression instances. This process, however, can be somewhat tedious, so prefuse also provides a built-in parser/compiler for generating these chains of Expression instances from a textual language. The language is based on a subset of SQL, the standard language for database queries. If you have ever written a "WHERE" clause in a SQL "SELECT" query, you probably know most of the language already. To parse an expression, simply pass a text string containing an expression statement to the parse(String) method. If the string parses successfully, the parsed Expression instance will be returned.
// Create a new derived column for a Table. It is assumed that "Gross Income" // and "Expenditures" are pre-existing columns in the table with numeric values. // // Other columns can be referenced in expressions by using just the name // or by enclosing the name in brackets. Brackets are required if the column // name has a space in it. The new data field "Profits" will automatically // update if the values in either the "Gross Income" or "Expenditures" // columns change. table.addColumn("Profits", "[Gross Income] - [Expenditures]");
// Get a filtered iterator over the tuples in a Table. // Queries will be optimized, making use of underlying column Indexes // if possible. For example, if the calls // table.index("Expenditues"); // and // table.index("Profits"); // have been made before this query is issued, the resulting Indexes // will be used to optimize the query processing. Iterator tuples = table.tuples("Profits > 34000 OR Expenditures < 10000");
// Assuming a Visualization instance named vis exists, the following // line will request all visible and highlighted VisualItems that are // also members of a data or focus group named "_search_". Iterator items = vis.items("_visible AND _highlight AND ingroup('_search_')");
// This example shows how to use the expression Predicate returned by // a dynamic query instance. First create a range query and get // a refrence to the dynamic query Predicate. RangeQueryBinding rquery = new RangeQueryBinding(table, "Profits"); Predicate queryPred = rquery.getPredicate(); // ... assume any number of initializations and operations here ... // Now use the range query predicate to get a filtered iterator. Iterator inRange = table.tuples(queryPred);