/* @(#)KeyLabelData.java 14 September 2006 */ /* Useful imports */ import edu.neu.ccs.*; import edu.neu.ccs.gui.*; import edu.neu.ccs.codec.*; import edu.neu.ccs.console.*; import edu.neu.ccs.filter.*; import edu.neu.ccs.jpf.*; import edu.neu.ccs.parser.*; import edu.neu.ccs.pedagogy.*; import edu.neu.ccs.quick.*; import edu.neu.ccs.util.*; import java.awt.*; import java.awt.event.*; import java.awt.geom.*; import java.awt.font.*; import java.awt.image.*; import javax.swing.*; import javax.swing.border.*; import java.io.*; import java.util.*; import java.math.*; import java.beans.*; import java.lang.reflect.*; import java.net.URL; import java.util.regex.*; import java.text.ParseException; /** *
A KeyLabelData consists of a search key string
* and zero or more lines of text that constitute the label contents.
* The maximum number of lines of text is set at construction and
* defaults to 6 if not provided.
When storing string data, this class converts all non-trivial * whitespace to blanks and trims leading and trailing whitespace.
* * @author Richard Rasala */ public class KeyLabelData implements Stringable, Comparable { /** The default number of label lines to store = 6. */ public static final int CAPACITY = 6; /** The maximum number of label lines to store. */ private int capacity = CAPACITY; /** The search key string. */ private String key = null; /** The item array of label strings. */ private String[] label = null; /** * The array of sub-keys extracted from the key and * converted to lower case. */ private String[] keys = null; /** *The default constructor that * leaves all fields empty.
*/ public KeyLabelData() { this(CAPACITY, null, null); } /** *The constructor that * sets the search key and labels.
* * @param key the search key * @param label the varargs list of labels */ public KeyLabelData(String key, String... label) { this(CAPACITY, key, label); } /** *The default constructor that sets the capacity and * leaves all fields empty.
* *The capacity will be set to at least 1.
* * @param capacity the maximum number of labels */ public KeyLabelData(int capacity) { this(capacity, null, null); } /** *The constructor that sets the capacity and * sets the search key and labels.
* *The capacity will be set to at least 1.
* * @param capacity the maximum number of labels * @param key the search key * @param label the varargs list of labels */ public KeyLabelData(int capacity, String key, String... label) { if (capacity < 1) capacity = 1; this.capacity = capacity; this.label = new String[capacity]; set(key, label); } /** *Set the key label data to the given search key and the * given label strings.
* *In each string, convert all non-trivial whitespace to * blanks and trim leading and trailing whitespace.
* *Store at most the number of label strings limited by * the capacity.
* *If not enough label strings are supplied, the missing * labels are set to empty strings.
* * @param key the search key * @param label the varargs list of labels */ public void set(String key, String... label) { setKey(key); setLabels(label); } /** *Set the search key to the given key.
* *Convert all non-trivial whitespace to blanks * and trim leading and trailing whitespace.
* *For internal search purposes, this method * constructs a list of search subkeys from the * original key as follows.
* *The subkeys are those extracted by converting * the original key to lowercase and treating that * string as a blank-comma separated list that will * be split by a string tokenizer.
* *By searching against subkeys, we are likely * to find most matches desired by an end user.
* * @param key the search key */ public void setKey(String key) { this.key = flattenAndTrim(key); String lowercase = this.key.toLowerCase(); keys = Strings.tokenize(lowercase, " ,", false); } /** *Set the labels to the given label strings.
* *In each string, convert all non-trivial whitespace to * blanks and trim leading and trailing whitespace.
* *Store at most the number of label strings limited by * the capacity.
* *If not enough label strings are supplied, the missing * labels are set to empty strings.
* * @param label the varargs list of labels */ public void setLabels(String... label) { int L = (label == null) ? 0 : label.length; int M = capacity; if (L > M) L = M; for (int i = 0; i < L; i++) this.label[i] = flattenAndTrim(label[i]); for (int i = L; i < M; i++) this.label[i] = ""; } /** *Sets the i-th label to the given label if the index i * is in bounds.
* *In the string, convert all non-trivial whitespace to * blanks and trim leading and trailing whitespace.
* * @param i the label index * @param string the label to set */ public void setLabel(int i, String string) { if ((i >= 0) && (i < capacity)) label[i] = flattenAndTrim(string); } /** *Appends the given label at the index returned by
* freeSlotIndex().
Does nothing if the index equals the capacity.
* * @param string the label to append */ public void appendLabel(String string) { setLabel(freeSlotIndex(), string); } /** *Find the index of the first label slot that occurs * after all the slots for non-trivial labels have been * passed.
* *The index returned is between 0 and the capacity. * If the index equals the capacity then that means that * the last slot has a non-trivial label and therefore * no additional labels may be appended.
*/ public int freeSlotIndex() { int i = capacity - 1; while ((i >= 0) && (label[i].length() == 0)) i--; i++; return i; } /** * Returns the search key. */ public String getKey() { return key; } /** * Returns the i-th label or an empty string * if i is out of bounds. */ public String getLabel(int i) { if ((0 <= i) && (i < capacity)) return label[i]; else return ""; } /** * Returns the capacity. */ public int getCapacity() { return capacity; } /** *Returns true if the key or one of its subkeys * starts with the given string.
* *The subkeys are those extracted by converting * the original key to lowercase and treating that * string as a blank-comma separated list that will * be split by a string tokenizer.
* *This test ignores case.
* * @param string the string to test */ public boolean keyStartsWith(String string) { if (string == null) return false; string = string.toLowerCase(); int L = keys.length; for (int i = 0; i < L; i++) if (keys[i].startsWith(string)) return true; return false; } /** *Returns a string that concatenates all labels
* terminated by a newline
* starting from index zero up to but not including
* the index freeSlotIndex().
Does not include the search key in any way.
*/ public String toString() { StringBuilder builder = new StringBuilder(); int limit = freeSlotIndex(); for (int i = 0; i < limit; i++) { builder.append(label[i]); builder.append('\n'); } return builder.toString(); } /** *Returns a string that concatenates the search key
* terminated by a newline together with all labels
* terminated by a newline
* starting from index zero up to but not including
* the index freeSlotIndex().
Splits the given data into an array of strings using * the newline character as a separator; * uses the first string in the array to set the key and * the remaining strings in the array to set the labels.
* *If key or label data is missing then such data will be * set to an empty string.
* * @param data the data for this key label data as a string */ public void fromStringData(String data) { if (data == null) { set(null, null); return; } fromStringArrayData(Strings.exactSplitNewlineList(data)); } /** *Uses the first string in the array to set the key and * the remaining strings in the array to set the labels.
* *If key or label data is missing then such data will be * set to an empty string.
* * @param list the data for this key label data as a string array */ public void fromStringArrayData(String[] list) { if (list == null) { set(null, null); return; } int K = list.length; if (K == 0) { set(null, null); return; } setKey(list[0]); int L = K - 1; int M = capacity; if (L > M) L = M; for (int i = 0; i < L; i++) label[i] = flattenAndTrim(list[i+1]); for (int i = L; i < M; i++) label[i] = ""; } /** *Returns true if the given object is a key data label, * the capacity of the object is the same as this capacity, * and all of the object's fields are equal to the corresponding * fields of this key data label; returns false otherwise.
*/ public boolean equals(Object object) { if (! (object instanceof KeyLabelData)) return false; KeyLabelData a = (KeyLabelData) object; if (! (capacity == a.capacity)) return false; if (! key.equals(a.key)) return false; for (int i = 0; i < capacity; i++) if (! label[i].equals(a.label[i])) return false; return true; } /** *Returns the sum of the hash codes of its constituent * string data fields.
*/ public int hashCode() { int sum = key.hashCode(); for (int i = 0; i < capacity; i++) sum += label[i].hashCode(); return sum; } /** *Compares each of the corresponding string data fields * and returns the first comparison that is non-zero; if * all corresponding fields are equal then returns 0.
* *In this implementation, the comparison looks beyond * the search key to the label strings if two keys are * equal. This implementation means that compareTo is * compatible with equals.
* *Accounts for the fact that the two objects may have * different capacity and provides a sensible ordering.
* *Throws ClassCastException if the given
* object is not an address label.
Return the given string with all non-trivial whitespace * converted to blanks and leading and trailing whitespace * trimmed.
* *If the given string data is null,
* return an empty string.