Package prefuse.data.util

Source Code of prefuse.data.util.Sort

/**
*
*/
package prefuse.data.util;

import java.util.Arrays;
import java.util.Comparator;

import prefuse.data.Schema;
import prefuse.data.Table;
import prefuse.data.Tuple;
import prefuse.data.expression.Predicate;
import prefuse.data.tuple.TupleSet;
import prefuse.util.collections.CompositeComparator;
import prefuse.util.collections.NullComparator;

/**
* <p>Utility class representing sorting criteria, this can be given as
* input to the {@link TupleSet#tuples(Predicate, Sort)} method to
* get a sorted iteration of tuples.</p>
*
* <p>Sort criteria consists of an ordered list of data field names to
* sort by, along with an indication to sort tuples in either ascending
* or descending order. These criteria can be passed in to the
* constructor or added incrementally using the
* {@link #add(String, boolean)} method.</p>
*
* <p>Alternatively, one can also specify the sorting criteria using a
* single string, which is parsed using the {@link #parse(String)} method.
* This string should consist
* of a comma-delimited list of field names, which optional "ASC" or
* "DESC" modifiers to specify ascending or descending sorts. If no
* modifier is given, ascending order is assumed. Field
* names which include spaces or other non-standard characters should
* be written in brackets ([]), just as is done in
* {@link prefuse.data.expression.parser.ExpressionParser expression
* language statements}. For example, the
* following string</p>
*
* <pre>"Profit DESC, [Product Type]"</pre>  
*
* <p>sorts first by the data field "Profit" in descending order,
* additionally sorting in ascending order by the data field
* "Product Type" for tuples which have identical values in the
* "Profit" field.</p>
*
* @author <a href="http://jheer.org">jeffrey heer</a>
*/
public class Sort {

    private static final String ASC  = " ASC";
    private static final String DESC = " DESC";
    private static final String asc  = ASC.toLowerCase();
    private static final String desc = DESC.toLowerCase();
   
    private String[]  m_fields;
    private boolean[] m_ascend;
   
    /**
     * Creates a new, empty Sort specification.
     */
    public Sort() {
      this(new String[0], new boolean[0]);
    }
   
    /**
     * Creates a new Sort specification that sorts on the
     * given fields, all in ascending order.
     * @param fields the fields to sort on, in order of precedence
     */
    public Sort(String[] fields) {
      this(fields, new boolean[fields.length]);
      Arrays.fill(m_ascend, true);
    }
   
    /**
     * Creates a new Sort specification that sorts on the
     * given fields in the given orders.
     * @param fields the fields to sort on, in order of precedence
     * @param ascend for each field, indicates if the field should
     * be sorted in ascending (true) or descending (false) order
     */
    public Sort(String[] fields, boolean[] ascend) {
        m_fields = fields;
        m_ascend = ascend;
    }
   
    /**
     * Adds a new field to this Sort specification.
     * @param field the additional field to sort on
     * @param ascend indicates if the field should
     * be sorted in ascending (true) or descending (false) order
     */
    public void add(String field, boolean ascend) {
        String[] f = new String[m_fields.length+1];
        System.arraycopy(m_fields, 0, f, 0, m_fields.length);
        f[m_fields.length] = field;
        m_fields = f;
       
        boolean[] b = new boolean[m_fields.length+1];
        System.arraycopy(m_ascend, 0, b, 0, m_ascend.length);
        b[m_ascend.length] = ascend;
        m_ascend = b;
    }
   
    /**
     * Returns the number of fields in this Sort specification.
     * @return the number of fields to sort on
     */
    public int size() {
        return m_fields.length;
    }
   
    /**
     * Returns the sort field at the given index.
     * @param i the index to look up
     * @return the sort field at the given index
     */
    public String getField(int i) {
        return m_fields[i];
    }
   
    /**
     * Returns the ascending modifier as the given index.
     * @param i the index to look up
     * @return true if the field at the given index is to be sorted
     * in ascending order, false for descending order
     */
    public boolean isAscending(int i) {
        return m_ascend[i];
    }
   
    /**
     * Generates a Comparator to be used for sorting tuples drawn from
     * the given tuple set.
     * @param ts the TupleSet whose Tuples are to be sorted
     * @return a Comparator instance for sorting tuples from the given
     * set using the sorting criteria given in this specification
     */
    public Comparator getComparator(TupleSet ts) {
        // get the schema, so we can lookup column value types       
        Schema s = null;
        if ( ts instanceof Table ) {
            // for Tables, we can get this directly
          s = ((Table)ts).getSchema();
        } else {
          // if non-table tuple set is empty, we punt
          if ( ts.getTupleCount() == 0 )
            return new NullComparator();
          // otherwise, use the schema of the first tuple in the set
            s = ((Tuple)ts.tuples().next()).getSchema();
        }
        // create the comparator
        CompositeComparator cc = new CompositeComparator(m_fields.length);
        for ( int i=0; i<m_fields.length; ++i ) {
            cc.add(new TupleComparator(m_fields[i],
                       s.getColumnType(m_fields[i]), m_ascend[i]));
        }
        return cc;
    }
   
    // ------------------------------------------------------------------------
   
    private static void subparse(String s, Object[] res) {
        s = s.trim();
       
        // extract ascending modifier first
        res[1] = Boolean.TRUE;
        if ( s.endsWith(DESC) || s.endsWith(desc) ) {
            res[1] = Boolean.FALSE;
            s = s.substring(0, s.length()-DESC.length()).trim();
        } else if ( s.endsWith(ASC) || s.endsWith(asc) ) {
            s = s.substring(0, s.length()-ASC.length()).trim();
        }
       
        if ( s.startsWith("[") ) {
            if ( s.lastIndexOf("[") == 0 &&
                 s.endsWith("]") && s.indexOf("]") == s.length() ) {
                res[0] = s.substring(1, s.length()-1);
            } else {
                throw new RuntimeException();
            }
        } else {
            if ( s.indexOf(" ") < 0 && s.indexOf("\t") < 0 ) {
                res[0] = s;
            } else {
                throw new RuntimeException();
            }
        }
    }
   
    /**
     * Parse a comma-delimited String of data fields to sort on, along
     * with optional ASC or DESC modifiers, to generate a new Sort
     * specification. This string should consist
   * of a comma-delimited list of field names, which optional "ASC" or
   * "DESC" modifiers to specify ascending or descending sorts. If no
   * modifier is given, ascending order is assumed. Field
   * names which include spaces or other non-standard characters should
   * be written in brackets ([]), just as is done in
   * {@link prefuse.data.expression.parser.ExpressionParser expression
   * language statements}. For example, the
   * following string</p>
   *
   * <pre>"Profit DESC, [Product Type]"</pre>  
   *
   * <p>sorts first by the data field "Profit" in descending order,
   * additionally sorting in ascending order by the data field
   * "Product Type" for tuples which have identical values in the
   * "Profit" field.</p>
     * @param s the sort specification String
     * @return a new Sort specification
     */
    public static Sort parse(String s) {
        Sort sort = new Sort();
        Object[] res = new Object[2];
        int idx = 0, len = s.length();
        int comma = s.indexOf(',');
        int quote = s.indexOf('[');
        while ( idx < len ) {
            if ( comma < 0 ) {
                subparse(s.substring(idx), res);
                sort.add((String)res[0], ((Boolean)res[1]).booleanValue());
                break;
            } else if ( quote < 0 || comma < quote ) {
                subparse(s.substring(idx, comma), res);
                sort.add((String)res[0], ((Boolean)res[1]).booleanValue());
                idx = comma + 1;
                comma = s.indexOf(idx, ',');
            } else {
                int q2 = s.indexOf(quote, ']');
                if ( q2 < 0 ) {
                    throw new RuntimeException();
                } else {
                    comma = s.indexOf(q2, ',');
                    subparse(s.substring(idx, comma), res);
                    sort.add((String)res[0], ((Boolean)res[1]).booleanValue());
                    idx = comma + 1;
                    comma = s.indexOf(idx, ',');
                }
            }
        }
        return sort;
    }
   
    /**
     * @see java.lang.Object#toString()
     */
    public String toString() {
      StringBuffer sbuf = new StringBuffer();
      for ( int i=0; i<m_fields.length; ++i ) {
        if ( i > 0 ) sbuf.append(", ");
        sbuf.append('[').append(m_fields[i]).append(']');
        sbuf.append((m_ascend[i]) ? ASC : DESC);
      }
      return sbuf.toString();
    }
   
} // end of class Sort
TOP

Related Classes of prefuse.data.util.Sort

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.