package prefuse.data.query;
import javax.swing.JComboBox;
import javax.swing.JComponent;
import javax.swing.JList;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;
import prefuse.data.Table;
import prefuse.data.column.ColumnMetadata;
import prefuse.data.expression.BooleanLiteral;
import prefuse.data.expression.ColumnExpression;
import prefuse.data.expression.ComparisonPredicate;
import prefuse.data.expression.Expression;
import prefuse.data.expression.Literal;
import prefuse.data.expression.OrPredicate;
import prefuse.data.expression.Predicate;
import prefuse.data.tuple.TupleSet;
import prefuse.util.DataLib;
import prefuse.util.ui.JToggleGroup;
/**
* DynamicQueryBinding supporting queries based on a list of included
* data values.
* @author <a href="http://jheer.org">jeffrey heer</a>
*/
public class ListQueryBinding extends DynamicQueryBinding {
/** String used to indicate inclusion of all data values. */
private static final String ALL = "All";
private Class m_type;
private ListModel m_model;
private Listener m_lstnr;
private boolean m_includeAll;
/**
* Create a new ListQueryBinding over the given set and data field.
* @param ts the TupleSet to query
* @param field the data field (Table column) to query
*/
public ListQueryBinding(TupleSet ts, String field) {
this(ts, field, true);
}
/**
* Create a new ListQueryBinding over the given set and data field.
* @param ts the TupleSet to query
* @param field the data field (Table column) to query
* @param includeAllOption indicates if the dynamic queries should
* include an "All" option for including all data values
*/
public ListQueryBinding(TupleSet ts, String field, boolean includeAllOption) {
super(ts, field);
m_type = DataLib.inferType(ts, field);
m_lstnr = new Listener();
m_includeAll = includeAllOption;
initPredicate();
initModel();
}
private void initPredicate() {
// set up predicate
OrPredicate orP = new OrPredicate();
orP.add(BooleanLiteral.TRUE);
setPredicate(orP);
}
private void initModel() {
if ( m_model != null )
m_model.removeListSelectionListener(m_lstnr);
// set up data / selection model
Object[] o = null;
if ( m_tuples instanceof Table ) {
ColumnMetadata md = ((Table)m_tuples).getMetadata(m_field);
o = md.getOrdinalArray();
} else {
o = DataLib.ordinalArray(m_tuples.tuples(), m_field);
}
m_model = new ListModel(o);
m_model.addListSelectionListener(m_lstnr);
if ( m_includeAll ) {
m_model.insertElementAt(ALL, 0);
m_model.setSelectedItem(ALL);
}
}
// ------------------------------------------------------------------------
/**
* Returns a list model for creating custom dynamic query widgets.
* This list model acts both as a data model and a selection model,
* and so must be registered as both with any custom widgets.
* @return the dynamic query list model
*/
public ListModel getListModel() {
return m_model;
}
/**
* Creates a new group of check boxes for interacting with the query.
* @return a {@link prefuse.util.ui.JToggleGroup} of check boxes bound to
* this dynamic query.
* @see prefuse.data.query.DynamicQueryBinding#createComponent()
*/
public JComponent createComponent() {
return createCheckboxGroup();
}
/**
* Create a new interactive list for interacting with the query.
* @return a {@link javax.swing.JList} bound to this dynamic query.
*/
public JList createList() {
JList list = new JList(m_model);
list.setSelectionModel(m_model);
return list;
}
/**
* Create a new drop-down combo box for interacting with the query.
* @return a {@link javax.swing.JComboBox} bound to this dynamic query.
*/
public JComboBox createComboBox() {
return new JComboBox(m_model);
}
/**
* Creates a new group of check boxes for interacting with the query.
* @return a {@link prefuse.util.ui.JToggleGroup} of check boxes bound to
* this dynamic query.
*/
public JToggleGroup createCheckboxGroup() {
return createToggleGroup(JToggleGroup.CHECKBOX);
}
/**
* Creates a new group of radio buttons for interacting with the query.
* @return a {@link prefuse.util.ui.JToggleGroup} of radio buttons bound to
* this dynamic query.
*/
public JToggleGroup createRadioGroup() {
return createToggleGroup(JToggleGroup.RADIO);
}
private JToggleGroup createToggleGroup(int type) {
return new JToggleGroup(type, m_model, m_model);
}
// ------------------------------------------------------------------------
/**
* Create a comparison predicate fof the given data value
*/
private ComparisonPredicate getComparison(Object o) {
Expression left = new ColumnExpression(m_field);
Expression right = Literal.getLiteral(o, m_type);
return new ComparisonPredicate(ComparisonPredicate.EQ, left, right);
}
private class Listener implements ListSelectionListener {
public void valueChanged(ListSelectionEvent e) {
ListModel model = (ListModel)e.getSource();
OrPredicate orP = (OrPredicate)m_query;
if ( model.isSelectionEmpty() )
{
orP.clear();
}
else if ( m_includeAll && model.isSelectedIndex(0) )
{
orP.set(BooleanLiteral.TRUE);
}
else
{
int min = model.getMinSelectionIndex();
int max = model.getMaxSelectionIndex();
int count = 0;
for ( int i=min; i<=max; ++i ) {
if ( model.isSelectedIndex(i) )
++count;
}
if ( count == model.getSize() ) {
orP.set(BooleanLiteral.TRUE);
} else if ( count == 1 ) {
orP.set(getComparison(model.getElementAt(min)));
} else {
Predicate[] p = new Predicate[count];
for ( int i=min, j=0; i<=max; ++i ) {
if ( model.isSelectedIndex(i) )
p[j++] = getComparison(model.getElementAt(i));
}
orP.set(p);
}
}
}
}
} // end of class ListQueryBinding