/* @(#)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().

*/ public String toStringData() { StringBuilder builder = new StringBuilder(); builder.append(key); builder.append('\n'); builder.append(toString()); return builder.toString(); } /** *

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.

* * @param object the address label to compare */ public int compareTo(Object object) { KeyLabelData a = (KeyLabelData) object; int test = key.compareTo(a.key); if (test != 0) return test; int minimum = Math.min(capacity, a.capacity); for (int i = 0; i < minimum; i++) { test = label[i].compareTo(a.label[i]); if (test != 0) return test; } if (capacity == a.capacity) return 0; else if (minimum < capacity) return +1; else return -1; } /** *

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.

* * @param string the string to flatten and trim */ public static final String flattenAndTrim(String string) { if (string == null) return ""; return Strings.flatten(string).trim(); } }