Package prefuse

Source Code of prefuse.Visualization

package prefuse;

import java.awt.geom.Rectangle2D;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.Map;

import prefuse.action.Action;
import prefuse.activity.Activity;
import prefuse.activity.ActivityMap;
import prefuse.data.Graph;
import prefuse.data.Node;
import prefuse.data.Schema;
import prefuse.data.Table;
import prefuse.data.Tree;
import prefuse.data.Tuple;
import prefuse.data.expression.Expression;
import prefuse.data.expression.Predicate;
import prefuse.data.expression.parser.ExpressionParser;
import prefuse.data.tuple.CompositeTupleSet;
import prefuse.data.tuple.DefaultTupleSet;
import prefuse.data.tuple.TupleManager;
import prefuse.data.tuple.TupleSet;
import prefuse.render.DefaultRendererFactory;
import prefuse.render.Renderer;
import prefuse.render.RendererFactory;
import prefuse.util.PrefuseConfig;
import prefuse.util.PrefuseLib;
import prefuse.util.collections.CompositeIterator;
import prefuse.visual.AggregateTable;
import prefuse.visual.VisualGraph;
import prefuse.visual.VisualItem;
import prefuse.visual.VisualTable;
import prefuse.visual.VisualTree;
import prefuse.visual.VisualTupleSet;
import prefuse.visual.expression.ValidatedPredicate;
import prefuse.visual.expression.VisiblePredicate;
import prefuse.visual.tuple.TableDecoratorItem;
import prefuse.visual.tuple.TableEdgeItem;
import prefuse.visual.tuple.TableNodeItem;

/**
* <p>Central data structure representing an interactive Visualization.
* This class is responsible for
* managing the mappings between source data and onscreen VisualItems,
* maintaining a list of {@link Display} instances responsible for rendering
* of and interaction with the contents of this visualization, and
* providing a collection of named Action instances for performing
* data processing such as layout, animation, and size, shape, and color
* assignment.</p>
*
* <p>The primary responsibility of the Visualization class is the creation
* of <em>visual abstractions</em> of input data. Regardless of the data
* structure (i.e., {@link prefuse.data.Table}, {@link prefuse.data.Graph},
* or {@link prefuse.data.Tree}), this class takes source data such as that
* loaded from a file (see {@link prefuse.data.io}) or from a relational
* database (see {@link prefuse.data.io.sql}) and creates a visual
* representation of the data. These visual representations of the data are
* data sets in their own right, providing access to the underlying source
* data to be visualized while also adding addition data fields specific to a
* visualization. These fields include spatial location (x, y
* coordinates and item bounds), color (for stroke, fill, and text), size,
* shape, and font. For a given input data set of type
* {@link prefuse.data.Table}, {@link prefuse.data.Graph}, or
* or {@link prefuse.data.Tree}, a corresponding instance of
* {@link prefuse.visual.VisualTable}, {@link prefuse.visual.VisualGraph}, or
* {@link prefuse.visual.VisualTree} is created and stored in the
* visualization. These data types inherit the data values of the source
* data (and indeed, manipulate it directly) while additionally providing
* the aforementioned visual variables unique to that generated
* visual abstraction. Similarly, all {@link prefuse.data.Tuple},
* {@link prefuse.data.Node}, or {@link prefuse.data.Edge}
* instances used to represent an entry in the source data have a
* corresponding {@link prefuse.visual.VisualItem},
* {@link prefuse.visual.NodeItem}, or {@link prefuse.visual.EdgeItem}
* representing the interactive, visual realization of the backing data.</p>
*
* <p>The mapping of source data to a visual abstraction is accomplished
* using {@link #add(String, TupleSet)} and the other "add" methods. These
* methods will automatically create the visual abstraction, and store it
* in this visualization, associating it with a provided <em>data group name
* </em>. This group name allows for queries to this visualization that
* consider only VisualItem instances from that particular group. This is
* quite useful when crafting {@link prefuse.action.Action} instances that
* process only a particular group of visual data. The Visualization class
* provides mechanisms for querying any or all groups within the visualization,
* using one or both of the group name or a filtering
* {@link prefuse.data.expression.Predicate} to determine the items to
* include (see {@link #items(Predicate)} for an examples). Source data
* may be added multiple times to a Visualization under different group
* names, allowing for multiple representations of the same backing data.</p>
*
* <p>Additionally, the Visualization class supports VisualItem instances
* that are not directly grounded in backing source data. Examples include
* {@link prefuse.visual.DecoratorItem} which "decorates" another pre-existing
* VisualItem with a separate interactive visual object, and
* {@link prefuse.visual.AggregateItem} which provides an interactive visual
* representation of an aggregated of other VisualItems. Methods for adding
* data groups of these kinds include {@link #addDecorators(String, String)}
* and {@link #addAggregates(String)}.</p>
*
* <p>All of the examples discussed above are examples of <em>primary, or
* visual, data groups</em> of VisualItems. Visualizations also support
* <em>secondary, or focus data groups</em> that maintain additional
* collections of the VisualItems stored in the primary groups. Examples
* include a set of focus items (such as those that have been clicked
* by the user), selected items (items selected by a user), or search
* items (all matches to a search query). The exact semantics of these
* groups and the mechanisms by which they are populated is determined by
* application creators, but some defaults are provided. The Visualization
* class includes some default group names, namely {@link #FOCUS_ITEMS},
* {@link #SELECTED_ITEMS}, and {@link #SEARCH_ITEMS} for the above
* mentioned tasks. By default, both the {@link #FOCUS_ITEMS},
* {@link #SELECTED_ITEMS} focus groups are included in the Visualization,
* represented using {@link prefuse.data.tuple.DefaultTupleSet} instances.
* Also, some of the interactive controls provided by the
* {@link prefuse.controls} package populate these sets by default. See
* {@link prefuse.controls.FocusControl} for an example.</p>
*
* <p>Visualizations also maintain references to all the {@link Display}
* instances providing interactive views of the content of this
* visualization. {@link Display} instances registers themselves with
* the visualization either in their constructor or through
* the {@link Display#setVisualization(Visualization)} method, so they
* do not otherwise need to be added manually. Displays can be configured
* to show all or only a subset of the data in the Visualization. A
* filtering {@link prefuse.data.expression.Predicate} can be used to
* control what items are drawn by the displaying, including limiting
* the Display to particular data groups (for example, using a
* {@link prefuse.visual.expression.InGroupPredicate}). The Visualization's
* {@link #repaint()} method will trigger a repaint on all Displays
* associated with the visualization.</p>
*
* <p>Finally, the Visualization class provides a map of named
* {@link prefuse.action.Action} instances that can be invoked to perform
* processing on the VisualItems contained in the visualization.
* Using the {@link #putAction(String, Action)} will add a named Action
* to the visualization, registering the Action such that a reference
* to this Visualization will be available within the scope of the
* Action's {@link prefuse.action.Action#run(double)} method. Processing
* Actions can later be invoked by name using the {@link #run(String)}
* method and other similar methods. This functionality not only
* provides a convenient means of organizing a Visualization-specific
* collection of processing Actions, it also allows for a layer of indirection
* between an Action and its name. This allows Actions to be dynamically
* swapped at runtime. For example, an application may make a call to
* invoke an Action named "layout", but the actual layout processing maybe
* be dynamically swapped by changing the Action that corresponds to that
* name. For more information on processing Actions, see the
* {@link prefuse.action} packages and the top-level
* {@link prefuse.action.Action} class.</p>
*
* @author <a href="http://jheer.org">jeffrey heer</a>
*/
public class Visualization {
   
    /** Data group name for indicating all groups */
    public static final String ALL_ITEMS
        = PrefuseConfig.get("visualization.allItems");
    /** Default data group name for focus items */
    public static final String FOCUS_ITEMS
        = PrefuseConfig.get("visualization.focusItems");
    /** Default data group name for selected items */
    public static final String SELECTED_ITEMS
        = PrefuseConfig.get("visualization.selectedItems");
    /** Default data group name for search result items */
    public static final String SEARCH_ITEMS
        = PrefuseConfig.get("visualization.searchItems");
   
    // visual abstraction
    // filtered tables and groups
    private Map m_visual;
    private Map m_source;
    private Map m_focus;
   
    // actions
    private ActivityMap m_actions;
   
    // renderers
    private RendererFactory m_renderers;
   
    // displays
    private ArrayList m_displays;
   
    // ------------------------------------------------------------------------
    // Constructor
   
    /**
     * Create a new, empty Visualization. Uses a DefaultRendererFactory.
     */
    public Visualization() {
        m_actions = new ActivityMap();
        m_renderers = new DefaultRendererFactory();
        m_visual = new LinkedHashMap();
        m_source = new HashMap();
        m_focus = new HashMap();
        m_displays = new ArrayList();
       
        addFocusGroup(Visualization.FOCUS_ITEMS,    new DefaultTupleSet());
        addFocusGroup(Visualization.SELECTED_ITEMS, new DefaultTupleSet());
    }
   
    // ------------------------------------------------------------------------
    // Data Methods
   
    /**
     * Add a data set to this visualization, using the given data group name.
     * A visual abstraction of the data will be created and registered with
     * the visualization. An exception will be thrown if the group name is
     * already in use.
     * @param group the data group name for the visualized data
     * @param data the data to visualize
     * @return a visual abstraction of the input data, a VisualTupleSet
     * instance
     */
    public synchronized VisualTupleSet add(String group, TupleSet data) {
        return add(group, data, null);
    }

    /**
     * Add a data set to this visualization, using the given data group name.
     * A visual abstraction of the data will be created and registered with
     * the visualization. An exception will be thrown if the group name is
     * already in use.
     * @param group the data group name for the visualized data
     * @param data the data to visualize
     * @param filter a filter Predicate determining which data Tuples in the
     * input data set are visualized
     * @return a visual abstraction of the input data, a VisualTupleSet
     * instance
     */
    public synchronized VisualTupleSet add(
            String group, TupleSet data, Predicate filter)
    {
        if ( data instanceof Table ) {
            return addTable(group, (Table)data, filter);
        } else if ( data instanceof Tree ) {
            return addTree(group, (Tree)data, filter);
        } else if ( data instanceof Graph ) {
            return addGraph(group, (Graph)data, filter);
        } else {
            throw new IllegalArgumentException("Unsupported TupleSet type.");
        }
    }
   
    protected void checkGroupExists(String group) {
      if ( m_visual.containsKey(group) || m_focus.containsKey(group) ) {
        throw new IllegalArgumentException(
            "Group name \'"+group+"\' already in use");
      }
    }
   
    protected void addDataGroup(String group, VisualTupleSet ts, TupleSet src) {
      checkGroupExists(group);
      m_visual.put(group, ts);
      if ( src != null )
        m_source.put(group, src);
    }
   
    // -- Tables --------------------------------------------------------------
   
    /**
     * Add an empty VisualTable to this visualization, using the given data
     * group name. This adds a group of VisualItems that do not have a
     * backing data set, useful for creating interactive visual objects
     * that do not represent data. An exception will be thrown if the group
     * name is already in use.
     * @param group the data group name for the visualized data
     * @return the added VisualTable
     */
    public synchronized VisualTable addTable(String group) {
      VisualTable vt = new VisualTable(this, group);
      addDataGroup(group, vt, null);
        return vt;
    }
   
    /**
     * Add an empty VisualTable to this visualization, using the given data
     * group name and table schema. This adds a group of VisualItems that do
     * not have a backing data set, useful for creating interactive visual
     * objects that do not represent data. An exception will be thrown if the
     * group name is already in use.
     * @param group the data group name for the visualized data
     * @param schema the data schema to use for the VisualTable
     * @return the added VisualTable
     */
    public synchronized VisualTable addTable(String group, Schema schema) {
      VisualTable vt = new VisualTable(this, group, schema);
        addDataGroup(group, vt, null);
        return vt;
    }
   
    /**
     * Adds a data table to this visualization, using the given data group
     * name. A visual abstraction of the data will be created and registered
     * with the visualization. An exception will be thrown if the group name
     * is already in use.
     * @param group the data group name for the visualized data
     * @param table the data table to visualize
     */
    public synchronized VisualTable addTable(String group, Table table) {
        return addTable(group, table, (Predicate)null);
    }
   
    /**
     * Adds a data table to this visualization, using the given data group
     * name. A visual abstraction of the data will be created and registered
     * with the visualization. An exception will be thrown if the group name
     * is already in use.
     * @param group the data group name for the visualized data
     * @param table the data table to visualize
     * @param filter a filter Predicate determining which data Tuples in the
     * input table are visualized
     */
    public synchronized VisualTable addTable(
            String group, Table table, Predicate filter)
    {
      VisualTable vt = new VisualTable(table, this, group, filter);
      addDataGroup(group, vt, table);
        return vt;
    }

    /**
     * Adds a data table to this visualization, using the given data group
     * name. A visual abstraction of the data will be created and registered
     * with the visualization. An exception will be thrown if the group name
     * is already in use.
     * @param group the data group name for the visualized data
     * @param table the data table to visualize
     * @param schema the data schema to use for the created VisualTable
     */
    public synchronized VisualTable addTable(
            String group, Table table, Schema schema)
    {
        return addTable(group, table, null, schema);
    }
   
    /**
     * Adds a data table to this visualization, using the given data group
     * name. A visual abstraction of the data will be created and registered
     * with the visualization. An exception will be thrown if the group name
     * is already in use.
     * @param group the data group name for the visualized data
     * @param table the data table to visualize
     * @param filter a filter Predicate determining which data Tuples in the
     * input table are visualized
     * @param schema the data schema to use for the created VisualTable
     */
    public synchronized VisualTable addTable(
            String group, Table table, Predicate filter, Schema schema)
    {
        VisualTable vt = new VisualTable(table, this, group, filter, schema);
        addDataGroup(group, vt, table);
        return vt;
    }
   
    /**
     * Add a VisualTable to this visualization, using the table's
     * pre-set group name. An exception will be thrown if the group
     * name is already in use. This method allows you to insert custom
     * implementations of VisualTable into a Visualization. It is intended
     * for advanced users and should <b>NOT</b> be used if you do not know
     * what you are doing. In almost all cases, one of the other add methods
     * is preferred.
     * @param table the pre-built VisualTable to add
     * @return the added VisualTable
     */
    public synchronized VisualTable addTable(VisualTable table) {
      addDataGroup(table.getGroup(), table, table.getParentTable());
      table.setVisualization(this);
      return table;
    }
   
    // -- Graphs and Trees ----------------------------------------------------
   
    /**
     * Adds a graph to this visualization, using the given data group
     * name. A visual abstraction of the data will be created and registered
     * with the visualization. An exception will be thrown if the group name
     * is already in use.
     * @param group the data group name for the visualized graph. The nodes
     * and edges will be available in the "group.nodes" and "group.edges"
     * subgroups.
     * @param graph the graph to visualize
     */
    public synchronized VisualGraph addGraph(String group, Graph graph) {
        return addGraph(group, graph, null);
    }
   
    /**
     * Adds a graph to this visualization, using the given data group
     * name. A visual abstraction of the data will be created and registered
     * with the visualization. An exception will be thrown if the group name
     * is already in use.
     * @param group the data group name for the visualized graph. The nodes
     * and edges will be available in the "group.nodes" and "group.edges"
     * subgroups.
     * @param graph the graph to visualize
     * @param filter a filter Predicate determining which data Tuples in the
     * input graph are visualized
     */
    public synchronized VisualGraph addGraph(
            String group, Graph graph, Predicate filter)
    {
        return addGraph(group, graph, filter, VisualItem.SCHEMA, VisualItem.SCHEMA);
    }
   
    /**
     * Adds a graph to this visualization, using the given data group
     * name. A visual abstraction of the data will be created and registered
     * with the visualization. An exception will be thrown if the group name
     * is already in use.
     * @param group the data group name for the visualized graph. The nodes
     * and edges will be available in the "group.nodes" and "group.edges"
     * subgroups.
     * @param graph the graph to visualize
     * @param filter a filter Predicate determining which data Tuples in the
     * input graph are visualized
     * @param nodeSchema the data schema to use for the visual node table
     * @param edgeSchema the data schema to use for the visual edge table
     */
    public synchronized VisualGraph addGraph(String group, Graph graph,
            Predicate filter, Schema nodeSchema, Schema edgeSchema)
    {
      checkGroupExists(group); // check before adding sub-tables
        String ngroup = PrefuseLib.getGroupName(group, Graph.NODES);
        String egroup = PrefuseLib.getGroupName(group, Graph.EDGES);

        VisualTable nt, et;
        nt = addTable(ngroup, graph.getNodeTable(), filter, nodeSchema);
        et = addTable(egroup, graph.getEdgeTable(), filter, edgeSchema);
       
        VisualGraph vg = new VisualGraph(nt, et,
                graph.isDirected(), graph.getNodeKeyField(),
                graph.getEdgeSourceField(), graph.getEdgeTargetField());
        vg.setVisualization(this);
        vg.setGroup(group);
    
        addDataGroup(group, vg, graph);
       
        TupleManager ntm = new TupleManager(nt, vg, TableNodeItem.class);
        TupleManager etm = new TupleManager(et, vg, TableEdgeItem.class);
        nt.setTupleManager(ntm);
        et.setTupleManager(etm);
        vg.setTupleManagers(ntm, etm);
       
        return vg;
    }
   
    /**
     * Adds a tree to this visualization, using the given data group
     * name. A visual abstraction of the data will be created and registered
     * with the visualization. An exception will be thrown if the group name
     * is already in use.
     * @param group the data group name for the visualized tree. The nodes
     * and edges will be available in the "group.nodes" and "group.edges"
     * subgroups.
     * @param tree the tree to visualize
     */
    public synchronized VisualTree addTree(String group, Tree tree) {
        return addTree(group, tree, null);
    }
   
    /**
     * Adds a tree to this visualization, using the given data group
     * name. A visual abstraction of the data will be created and registered
     * with the visualization. An exception will be thrown if the group name
     * is already in use.
     * @param group the data group name for the visualized tree. The nodes
     * and edges will be available in the "group.nodes" and "group.edges"
     * subgroups.
     * @param tree the tree to visualize
     * @param filter a filter Predicate determining which data Tuples in the
     * input graph are visualized
     */
    public synchronized VisualTree addTree(
            String group, Tree tree, Predicate filter)
    {
        return addTree(group, tree, filter, VisualItem.SCHEMA, VisualItem.SCHEMA);
    }
   
    /**
     * Adds a tree to this visualization, using the given data group
     * name. A visual abstraction of the data will be created and registered
     * with the visualization. An exception will be thrown if the group name
     * is already in use.
     * @param group the data group name for the visualized tree. The nodes
     * and edges will be available in the "group.nodes" and "group.edges"
     * subgroups.
     * @param tree the tree to visualize
     * @param filter a filter Predicate determining which data Tuples in the
     * input graph are visualized
     * @param nodeSchema the data schema to use for the visual node table
     * @param edgeSchema the data schema to use for the visual edge table
     */
    public synchronized VisualTree addTree(String group, Tree tree,
            Predicate filter, Schema nodeSchema, Schema edgeSchema)
    {
      checkGroupExists(group); // check before adding sub-tables
        String ngroup = PrefuseLib.getGroupName(group, Graph.NODES);
        String egroup = PrefuseLib.getGroupName(group, Graph.EDGES);
       
        VisualTable nt, et;
        nt = addTable(ngroup, tree.getNodeTable(), filter, nodeSchema);
        et = addTable(egroup, tree.getEdgeTable(), filter, edgeSchema);

        VisualTree vt = new VisualTree(nt, et, tree.getNodeKeyField(),
                tree.getEdgeSourceField(), tree.getEdgeTargetField());
        vt.setVisualization(this);
        vt.setGroup(group);
       
        addDataGroup(group, vt, tree);
       
        TupleManager ntm = new TupleManager(nt, vt, TableNodeItem.class);
        TupleManager etm = new TupleManager(et, vt, TableEdgeItem.class);
        nt.setTupleManager(ntm);
        et.setTupleManager(etm);
        vt.setTupleManagers(ntm, etm);
       
        return vt;
    }
   
    // -- Aggregates ----------------------------------------------------------
   
    /**
     * Add a group of aggregates to this visualization. Aggregates are
     * used to visually represent groups of VisualItems.
     * @param group the data group name for the aggregates.
     * @return the generated AggregateTable
     * @see prefuse.visual.AggregateTable
     */
    public synchronized AggregateTable addAggregates(String group) {
        return addAggregates(group, VisualItem.SCHEMA);
    }
   
    /**
     * Add a group of aggregates to this visualization. Aggregates are
     * used to visually represent groups of VisualItems.
     * @param group the data group name for the aggregates.
     * @param schema the data schema to use for the AggregateTable
     * @return the generated AggregateTable
     * @see prefuse.visual.AggregateTable
     */
    public synchronized AggregateTable addAggregates(String group,
                                                     Schema schema)
    {
        AggregateTable vat = new AggregateTable(this, group, schema);
        addDataGroup(group, vat, null);
        return vat;
    }
   
    // -- Derived Tables and Decorators ---------------------------------------
   
    /**
     * Add a derived table, a VisualTable that is cascaded from an
     * existing VisualTable. This is useful for creating VisualItems
     * that inherit a set of visual properties from another group of
     * VisualItems. This might be used, for example, in the creation
     * of small multiples where only a few visual attributes vary
     * across the multiples.
     * @param group the data group to use for the derived table
     * @param source the source data group to derive from
     * @param filter a Predicate filter indicating which tuples of the
     * source group should be inheritable by the new group
     * @param override a data schema indicating which data fields
     * should not be inherited, but managed locally by the derived group
     * @return the derived VisualTable
     */
    public synchronized VisualTable addDerivedTable(
            String group, String source, Predicate filter, Schema override)
    {
        VisualTable src = (VisualTable)getGroup(source);
        VisualTable vt = new VisualTable(src, this, group, filter, override);
    
        addDataGroup(group, vt, getSourceData(source));
        return vt;
    }
   
    /**
     * Add a group of decorators to an existing visual data group. Decorators
     * are VisualItem instances intended to "decorate" another VisualItem,
     * such as providing a label or dedicated interactive control, and are
     * realizeed as {@link prefuse.visual.DecoratorItem} instances that provide
     * access to the decorated item in addition to the standard VisualItem
     * properties. The generated table is created using the
     * {@link #addDerivedTable(String, String, Predicate, Schema)} method,
     * but with no VisualItem properties inherited from the source group.
     * @param group the data group to use for the decorators
     * @param source the source data group to decorate
     * @return the generated VisualTable of DecoratorItem instances
     */
    public synchronized VisualTable addDecorators(String group,String source) {
        return addDecorators(group, source, (Predicate)null);
    }
   
    /**
     * Add a group of decorators to an existing visual data group. Decorators
     * are VisualItem instances intended to "decorate" another VisualItem,
     * such as providing a label or dedicated interactive control, and are
     * realizeed as {@link prefuse.visual.DecoratorItem} instances that provide
     * access to the decorated item in addition to the standard VisualItem
     * properties.
     * @param group the data group to use for the decorators
     * @param source the source data group to decorate
     * @param schema schema indicating which variables should <b>not</b> be
     * inherited from the source data group and instead be managed locally
     * by the generated VisualTable
     * @return the generated VisualTable of DecoratorItem instances
     */
    public synchronized VisualTable addDecorators(
            String group, String source, Schema schema)
    {
        return addDecorators(group, source, null, schema);
    }
   
    /**
     * Add a group of decorators to an existing visual data group. Decorators
     * are VisualItem instances intended to "decorate" another VisualItem,
     * such as providing a label or dedicated interactive control, and are
     * realizeed as {@link prefuse.visual.DecoratorItem} instances that provide
     * access to the decorated item in addition to the standard VisualItem
     * properties.
     * @param group the data group to use for the decorators
     * @param source the source data group to decorate
     * @param filter a Predicate filter indicating which tuples of the
     * source group should be inheritable by the new group
     * @return the generated VisualTable of DecoratorItem instances
     */
    public synchronized VisualTable addDecorators(
            String group, String source, Predicate filter)
    {
        VisualTable t = addDerivedTable(group,source,filter,VisualItem.SCHEMA);
        t.setTupleManager(new TupleManager(t, null, TableDecoratorItem.class));
        return t;
    }
   
    /**
     * Add a group of decorators to an existing visual data group. Decorators
     * are VisualItem instances intended to "decorate" another VisualItem,
     * such as providing a label or dedicated interactive control, and are
     * realizeed as {@link prefuse.visual.DecoratorItem} instances that provide
     * access to the decorated item in addition to the standard VisualItem
     * properties.
     * @param group the data group to use for the decorators
     * @param source the source data group to decorate
     * @param filter a Predicate filter indicating which tuples of the
     * source group should be inheritable by the new group
     * @param schema schema indicating which variables should <b>not</b> be
     * inherited from the source data group and instead be managed locally
     * by the generated VisualTable
     * @return the generated VisualTable of DecoratorItem instances
     */
    public synchronized VisualTable addDecorators(
            String group, String source, Predicate filter, Schema schema)
    {
        VisualTable t = addDerivedTable(group, source, filter, schema);
        t.setTupleManager(new TupleManager(t, null, TableDecoratorItem.class));
        return t;
    }
   
    // -- Data Removal --------------------------------------------------------
   
    /**
     * Removes a data group from this Visualization. If the group is a focus
     * group, the group will simply be removed, and any subsequent attempts to
     * retrieve the group will return null. If the group is a primary group, it
     * will be removed, and any members of the group will also be removed
     * from any registered focus groups.
     * @param group the data group to remove
     * @return true if the group was found and removed, false if the group
     * was not found in this visualization.
     */
    public synchronized boolean removeGroup(String group) {
        // check for focus group first
        TupleSet ts = getFocusGroup(group);
        if ( ts != null ) {
            // invalidate the item to reflect group membership change
            for ( Iterator items = ts.tuples(ValidatedPredicate.TRUE);
                  items.hasNext(); )
            {
                ((VisualItem)items.next()).setValidated(false);
            }
            ts.clear(); // trigger group removal callback
            m_focus.remove(group);
            return true;
        }
       
        // focus group not found, check for primary group
        ts = getVisualGroup(group);
        if ( ts == null ) {
            // exit with false if group not found
            return false;
        }
        // remove group members from focus sets and invalidate them
        TupleSet[] focus = new TupleSet[m_focus.size()];
        m_focus.values().toArray(focus);
        for ( Iterator items = ts.tuples(); items.hasNext(); ) {
            VisualItem item = (VisualItem)items.next();
            for ( int j=0; j<focus.length; ++j ) {
                focus[j].removeTuple(item);
            }
            item.setValidated(false);
        }
        // remove data
        if ( ts instanceof CompositeTupleSet ) {
            CompositeTupleSet cts = (CompositeTupleSet)ts;
            for ( Iterator names = cts.setNames(); names.hasNext(); ) {
                String name = (String)names.next();
                String subgroup = PrefuseLib.getGroupName(group,name);
                m_visual.remove(subgroup);
                m_source.remove(subgroup);
            }
        }
        m_visual.remove(group);
        m_source.remove(group);
        return true;
    }
   
    /**
     * Reset this visualization, clearing out all visualization tuples. All
     * data sets added using the "addXXX" methods will be removed from the
     * visualization. All registered focus groups added using the
     * addFocusGroup() methods will be retained, but will be cleared of all
     * tuples.
     */
    public synchronized void reset() {
        // first clear out all the focus groups
        Iterator iter = m_focus.entrySet().iterator();
        while ( iter.hasNext() ) {
            Map.Entry entry = (Map.Entry)iter.next();
            TupleSet ts = (TupleSet)entry.getValue();
            ts.clear();
        }
        // finally clear out all map entries
        m_visual.clear();
        m_source.clear();
    }
   
    // ------------------------------------------------------------------------
    // Groups
   
    /**
     * Get the source data TupleSet backing the given visual data group.
     * @return the backing source data set, or null if there is no such
     * data set
     */
    public TupleSet getSourceData(String group) {
        return (TupleSet)m_source.get(group);
    }
   
    /**
     * Get the source data TupleSet backing the given visual tuple set.
     * @return the backing source data set, or null if there is no such
     * data set
     */
    public TupleSet getSourceData(VisualTupleSet ts) {
        return (TupleSet)m_source.get(ts.getGroup());
    }
   
    /**
     * Get the Tuple from a backing source data set that corresponds most
     * closely to the given VisualItem.
     * @param item the VisualItem for which to retreive the source tuple
     * @return the data source tuple, or null if no such tuple could
     * be found
     */
    public Tuple getSourceTuple(VisualItem item) {
        // get the source group and tuple set, exit if none
        String group = item.getGroup();
        TupleSet source = getSourceData(group);
        if ( source == null ) return null;
       
        // first get the source table and row value
        int row = item.getRow();
        Table t = item.getTable();
        while ( t instanceof VisualTable ) {
            VisualTable vt = (VisualTable)t;
            row = vt.getParentRow(row);
            t   = vt.getParentTable();
        }
       
        // now get the appropriate source tuple
        // graphs maintain their own tuple managers so treat them specially
        String cgroup = PrefuseLib.getChildGroup(group);
        if ( cgroup != null ) {
            String pgroup = PrefuseLib.getParentGroup(group);
            Graph g = (Graph)getSourceData(pgroup);
            if ( t == g.getNodeTable() ) {
                return g.getNode(row);
            } else {
                return g.getEdge(row);
            }
        } else {
            return t.getTuple(row);
        }
    }
   
    /**
     * Get the VisualItem associated with a source data tuple, if it exists.
     * @param group the data group from which to lookup the source tuple,
     * only primary visual groups are valid, focus groups will not work
     * @param t the source data tuple
     * @return the associated VisualItem from the given data group, or
     * null if no such VisualItem exists
     */
    public VisualItem getVisualItem(String group, Tuple t) {
        TupleSet ts = getVisualGroup(group);
        VisualTable vt;
        if ( ts instanceof VisualTable ) {
            vt = (VisualTable)ts;
        } else if ( ts instanceof Graph ) {
            Graph g = (Graph)ts;
            vt = (VisualTable)(t instanceof Node ? g.getNodeTable()
                                                 : g.getEdgeTable());
        } else {
            return null;
        }
        int pr = t.getRow();
        int cr = vt.getChildRow(pr);
        return cr<0 ? null : vt.getItem(cr);
    }
   
    // ------------------------------------------------------------------------
   
    /**
     * Get the TupleSet associated with the given data group name.
     * @param group a visual data group name
     * @return the data group TupleSet
     */
    public TupleSet getGroup(String group) {
        TupleSet ts = getVisualGroup(group);
        if ( ts == null )
            ts = getFocusGroup(group);
        return ts;
    }
   
    /**
     * Indicates if a given VisualItem is contained in the given visual
     * data group.
     * @param item the VisualItem instance
     * @param group the data group to check for containment
     * @return true if the VisualItem is in the group, false otherwise
     */
    public boolean isInGroup(VisualItem item, String group) {
        if ( ALL_ITEMS.equals(group) )
            return true;
        if ( item.getGroup() == group )
            return true;
       
        TupleSet tset = getGroup(group);
        return ( tset==null ? false : tset.containsTuple(item) );
    }
   
    /**
     * Add a new secondary, or focus, group to this visualization. By
     * default the added group is an instance of
     * {@link prefuse.data.tuple.DefaultTupleSet}.
     * @param group the name of the focus group to add
     */
    public void addFocusGroup(String group) {
      checkGroupExists(group);
        m_focus.put(group, new DefaultTupleSet());
    }

    /**
     * Add a new secondary, or focus, group to this visualization.
     * @param group the name of the focus group to add
     * @param tset the TupleSet for the focus group
     */
    public void addFocusGroup(String group, TupleSet tset) {
        checkGroupExists(group);
      m_focus.put(group, tset);
    }
   
    // ------------------------------------------------------------------------
    // VisualItems
   
    /**
     * Get the size of the given visual data group.
     * @param group the visual data group
     * @return the size (number of tuples) of the group
     */
    public int size(String group) {
        TupleSet tset = getGroup(group);
        return ( tset==null ? 0 : tset.getTupleCount() );
    }
   
    /**
     * Retrieve the visual data group of the given group name. Only primary
     * visual groups will be considered.
     * @param group the visual data group
     * @return the requested data group, or null if not found
     */
    public TupleSet getVisualGroup(String group) {
        return (TupleSet)m_visual.get(group);
    }
   
    /**
     * Retrieve the focus data group of the given group name. Only secondary,
     * or focus, groups will be considered.
     * @param group the focus data group
     * @return the requested data group, or null if not found
     */
    public TupleSet getFocusGroup(String group) {
        return (TupleSet)m_focus.get(group);
    }
   
    /**
     * Invalidate the bounds of all VisualItems in the given group. This
     * will cause the bounds to be recomputed for all items upon the next
     * redraw.
     * @param group the visual data group to invalidate
     */
    public void invalidate(String group) {
        Iterator items = items(group, ValidatedPredicate.TRUE);
        while ( items.hasNext() ) {
            VisualItem item = (VisualItem)items.next();
            item.setValidated(false);
        }
    }
   
    /**
     * Invalidate the bounds of all VisualItems in this visualization. This
     * will cause the bounds to be recomputed for all items upon the next
     * redraw.
     */
    public void invalidateAll() {
        invalidate(ALL_ITEMS);
    }
   
    /**
     * Get an iterator over all visible items.
     * @return an iterator over all visible items.
     */
    public Iterator visibleItems() {
        return items(VisiblePredicate.TRUE);
    }
   
    /**
     * Get an iterator over all visible items in the specified group.
     * @param group the visual data group name
     * @return an iterator over all visible items in the specified group
     */
    public Iterator visibleItems(String group) {
        return items(group, VisiblePredicate.TRUE);
    }
   
    /**
     * Get an iterator over all items, visible or not.
     * @return an iterator over all items, visible or not.
     */
    public Iterator items() {
        return items((Predicate)null);
    }
   
    /**
     * Get an iterator over all items which match the given
     * Predicate filter.
     * @param filter a Predicate indicating which items should be included
     * in the iteration
     * @return a filtered iterator over VisualItems
     */
    public Iterator items(Predicate filter) {
        int size = m_visual.size();
        if ( size == 0 ) {
            return Collections.EMPTY_LIST.iterator();
        } else if ( size == 1 ) {
            Iterator it = m_visual.keySet().iterator();
            return items((String)it.next(), filter);
        } else {
            CompositeIterator iter = new CompositeIterator(m_visual.size());
            Iterator it = m_visual.keySet().iterator();
            for ( int i=0; it.hasNext(); ) {
                String group = (String)it.next();
                if ( !PrefuseLib.isChildGroup(group) )
                    iter.setIterator(i++, items(group, filter));
            }
            return iter;
        }
    }
   
    /**
     * Get an iterator over all items in the specified group.
     * @param group the visual data group name
     * @return an iterator over all items in the specified group.
     */
    public Iterator items(String group) {
        return items(group, (Predicate)null);
    }
   
    /**
     * Get an iterator over all items in the given group which match the given
     * filter expression.
     * @param group the visual data group to iterate over
     * @param expr an expression string that should parse to a Predicate
     * indicating which items should be included in the iteration. The input
     * string will be parsed using the
     * {@link prefuse.data.expression.parser.ExpressionParser} class. If a
     * parse error occurs, an empty iterator is returned.
     * @return a filtered iterator over VisualItems
     */
    public Iterator items(String group, String expr) {
        Expression e = ExpressionParser.parse(expr);
        if ( !(e instanceof Predicate) || ExpressionParser.getError()!=null )
            return Collections.EMPTY_LIST.iterator();
        return items(group, (Predicate)e);
    }
   
    /**
     * Get an iterator over all items in the given group which match the given
     * Predicate filter.
     * @param group the visual data group to iterate over
     * @param filter a Predicate indicating which items should be included in
     * the iteration.
     * @return a filtered iterator over VisualItems
     */
    public Iterator items(String group, Predicate filter) {
        if ( ALL_ITEMS.equals(group) )
            return items(filter);

        TupleSet t = getGroup(group);
        return ( t==null ? Collections.EMPTY_LIST.iterator()
                         : t.tuples(filter) );
    }
   
    // ------------------------------------------------------------------------
    // Batch Methods
   
    /**
     * Set a data field value for all items in a given data group matching a
     * given filter predicate.
     * @param group the visual data group name
     * @param p the filter predicate determining which items to modify
     * @param field the data field / column name to set
     * @param val the value to set
     */
    public void setValue(String group, Predicate p, String field, Object val) {
        Iterator items = items(group, p);
        while ( items.hasNext() ) {
            VisualItem item = (VisualItem)items.next();
            item.set(field, val);
        }
    }
   
    /**
     * Sets the visbility status for all items in a given data group matching
     * a given filter predicate.
     * @param group the visual data group name
     * @param p the filter predicate determining which items to modify
     * @param value the visibility value to set
     */
    public void setVisible(String group, Predicate p, boolean value) {
        Iterator items = items(group, p);
        while ( items.hasNext() ) {
            VisualItem item = (VisualItem)items.next();
            item.setVisible(value);
        }
    }

    /**
     * Sets the interactivity status for all items in a given data group
     * matching a given filter predicate.
     * @param group the visual data group name
     * @param p the filter predicate determining which items to modify
     * @param value the interactivity value to set
     */
    public void setInteractive(String group, Predicate p, boolean value) {
        Iterator items = items(group, p);
        while ( items.hasNext() ) {
            VisualItem item = (VisualItem)items.next();
            item.setInteractive(value);
        }
    }
   
    // ------------------------------------------------------------------------
    // Action Methods
   
    /**
     * Add a data processing Action to this Visualization. The Action will be
     * updated to use this Visualization in its data processing.
     * @param name the name of the Action
     * @param action the Action to add
     */
    public Action putAction(String name, Action action) {
        action.setVisualization(this);
        m_actions.put(name, action);
        return action;
    }
   
    /**
     * Get the data processing Action with the given name.
     * @param name the name of the Action
     * @return the requested Action, or null if the name was not found
     */
    public Action getAction(String name) {
        return (Action)m_actions.get(name);
    }
   
    /**
     * Remove a data processing Action registered with this visualization.
     * If the removed action is currently running, it will be canceled.
     * The visualization reference held by the removed Action will be set to
     * null.<br/>
     * <strong>NOTE:</strong> Errors may occur if the removed Action is
     * included in an "always run after" relation with another registered
     * Action that has not been removed from this visualization. It is the
     * currently the responsibility of clients to avoid this situation.
     * @param name the name of the Action
     * @return the removed Action, or null if no action was found
     */
    public Action removeAction(String name) {
        // TODO: create registry of always run after relations to automatically
        // resolve action references?
        Action a = getAction(name);
        if ( a != null ) {
            a.cancel();
            m_actions.remove(name);
            a.setVisualization(null);
        }
        return a;
    }
   
    /**
     * Schedule the Action with the given name to run immediately. The running
     * of all Actions is managed by the
     * {@link prefuse.activity.ActivityManager}, which runs in a dedicated
     * thread.
     * @param action the name of the Action to run
     * @return the Action scheduled to run
     */
    public Activity run(String action) {
        return m_actions.run(action);
    }

    /**
     * Schedule the Action with the given name to run after the specified
     * delay. The running of all Actions is managed by the
     * {@link prefuse.activity.ActivityManager}, which runs in a dedicated
     * thread.
     * @param action the name of the Action to run
     * @param delay the amount of time to wait, in milliseconds, before
     * running the Action
     * @return the Action scheduled to run
     */
    public Activity runAfter(String action, long delay) {
        return m_actions.runAt(action, System.currentTimeMillis()+delay);
    }
   
    /**
     * Schedule the Action with the given name to run at the specified
     * time. The running of all Actions is managed by the
     * {@link prefuse.activity.ActivityManager}, which runs in a dedicated
     * thread.
     * @param action the name of the Action to run
     * @param startTime the absolute system time, in milliseconds since the
     * epoch, at which to run the Action.
     * @return the Action scheduled to run
     */
    public Activity runAt(String action, long startTime) {
        return m_actions.runAt(action, startTime);
    }
   
    /**
     * Schedule the Action with the given name to run after another Action
     * finishes running. This relationship will only hold for one round of
     * scheduling. If the "before" Action is run a second time, the "after"
     * action will not be run a second time. The running of all Actions is
     * managed by the {@link prefuse.activity.ActivityManager}, which runs
     * in a dedicated thread.
     * @param before the name of the Action to wait for
     * @param after the name of the Action to run after the first one finishes
     * @return the Action scheduled to run after the first one finishes
     */
    public Activity runAfter(String before, String after) {
        return m_actions.runAfter(before, after);
    }
   
    /**
     * Schedule the Action with the given name to always run after another Action
     * finishes running. The running of all Actions is managed by the
     * {@link prefuse.activity.ActivityManager}, which runs in a dedicated
     * thread.
     * @param before the name of the Action to wait for
     * @param after the name of the Action to run after the first one finishes
     * @return the Action scheduled to always run after the first one finishes
     */
    public Activity alwaysRunAfter(String before, String after) {
        return m_actions.alwaysRunAfter(before, after);
    }
   
    /**
     * Cancel the Action with the given name, if it has been scheduled.
     * @param action the name of the Action to cancel
     * @return the canceled Action
     */
    public Activity cancel(String action) {
        return m_actions.cancel(action);
    }
   
    // ------------------------------------------------------------------------
    // Renderers
   
    /**
     * Set the RendererFactory used by this Visualization. The RendererFactory
     * is responsible for providing the Renderer instances used to draw
     * the VisualItems.
     * @param rf the RendererFactory to use.
     */
    public void setRendererFactory(RendererFactory rf) {
        invalidateAll();
        m_renderers = rf;
    }
   
    /**
     * Get the RendererFactory used by this Visualization.
     * @return this Visualization's RendererFactory
     */
    public RendererFactory getRendererFactory() {
        return m_renderers;
    }
   
    /**
     * Get the renderer for the given item. Consults this visualization's
     * {@link prefuse.render.RendererFactory} and returns the result.
     * @param item the item to retreive the renderer for
     * @return the {@link prefuse.render.Renderer} for drawing the
     * given item
     */
    public Renderer getRenderer(VisualItem item) {
        if ( item.getVisualization() != this ) {
            throw new IllegalArgumentException(
                    "Input item not a member of this visualization.");
        }
        return m_renderers.getRenderer(item);
    }
   
    /**
     * Issue a repaint request, causing all displays associated with this
     * visualization to be repainted.
     */
    public synchronized void repaint() {
        Iterator items = items(ValidatedPredicate.FALSE);
        while ( items.hasNext() ) {
            ((VisualItem)items.next()).validateBounds();
        }
        for ( int i=0; i<m_displays.size(); ++i ) {
            getDisplay(i).repaint();
        }
    }
   
    /**
     * Get the bounding rectangle for all items in the given group.
     * @param group the visual data group
     * @return the bounding box of the items
     */
    public Rectangle2D getBounds(String group) {
        return getBounds(group, new Rectangle2D.Double());
    }
   
    /**
     * Get the bounding rectangle for all items in the given group.
     * @param group the visual data group name
     * @param r a rectangle in which to store the computed bounding box
     * @return the input rectangle r, updated to hold the computed
     * bounding box
     */
    public Rectangle2D getBounds(String group, Rectangle2D r) {
        Iterator iter = visibleItems(group);
        if ( iter.hasNext() ) {
            VisualItem item = (VisualItem)iter.next();
            r.setRect(item.getBounds());
        }
        while ( iter.hasNext() ) {
            VisualItem item = (VisualItem)iter.next();
            Rectangle2D.union(item.getBounds(), r, r);
        }
        return r;
    }
   
    // ------------------------------------------------------------------------
    // Displays
   
    /**
     * Get the number of displays associated with this visualization.
     * @return the number of displays
     */
    public int getDisplayCount() {
        return m_displays.size();
    }
   
    /**
     * Add a display to this visualization. Called automatically by the
     * {@link prefuse.Display#setVisualization(Visualization)} method.
     * @param display the Display to add
     */
    void addDisplay(Display display) {
        m_displays.add(display);
    }
   
    /**
     * Get the display at the given list index. Displays are numbered by the
     * order in which they are added to this visualization.
     * @param idx the list index
     * @return the Display at the given index
     */
    public Display getDisplay(int idx) {
        return (Display)m_displays.get(idx);
    }
   
    /**
     * Remove a display from this visualization.
     * @param display the display to remove
     * @return true if the display was removed, false if it was not found
     */
    boolean removeDisplay(Display display) {
        return m_displays.remove(display);
    }
   
    /**
     * Report damage to associated displays, indicating a region that will need
     * to be redrawn.
     * @param item the item responsible for the damage
     * @param region the damaged region, in item-space coordinates
     */
    public void damageReport(VisualItem item, Rectangle2D region) {
        for ( int i=0; i<m_displays.size(); ++i ) {
            Display d = getDisplay(i);
            if ( d.getPredicate().getBoolean(item) ) {
                d.damageReport(region);
            }
        }
    }
   
} // end of class Visualization
TOP

Related Classes of prefuse.Visualization

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.