001: package abbot.tester;
002:
003: import java.awt.*;
004: import java.util.StringTokenizer;
005:
006: import abbot.i18n.Strings;
007:
008: /** Provides encapsulation of a visible Component-relative location.
009: * "Visible" in this context means currently accessible by the pointer
010: * (possibly via scrolling). A hidden node in a collapsed tree path
011: * would <strong>not</strong> be considered visible.
012: * <p>
013: * This class the specifics of location so that {@link ComponentTester}
014: * primitives
015: * ({@link ComponentTester#actionClick(Component,ComponentLocation)},
016: * {@link ComponentTester#actionShowPopupMenu(Component,ComponentLocation)},
017: * etc) may be directed to specific elements of
018: * substructure on a <code>Component</code> (list rows, tree paths, table
019: * cells, substructure values, etc.).
020: * <p>
021: * Classes derived from <code>ComponentLocation</code> should provide
022: * constructors for each type of location indication, e.g. value, index, and
023: * {@link Point}. The {@link #toString()} method should provide an encoded
024: * {@link String} suitable for use by the {@link #parse(String)} method, which
025: * must convert the {@link String} encoding back into a proper
026: * <code>ComponentLocation</code>.
027: * <p>
028: * By convention, {@link Point} locations are specified with (x,y) notation.
029: * Indexed locations should use square brackets, e.g. [i] or [r,c] and
030: * value locations should use a quoted {@link String}, e.g.
031: * '"cuckoo for cocoa puffs"'. The specific syntax allowed will vary by
032: * specific <code>ComponentLocation</code> type. The base
033: * <code>ComponentLocation</code> implementation
034: * supports only the explicit (x,y) notation.
035: * <p>
036: * Recorders
037: * should use the {@link String} value by default for consistency. The
038: * special value {@link #CENTER} is provided to indicate the center of a
039: * {@link Component}.
040: * <p>
041: * The method {@link #badFormat(String)} should provide usage-like information
042: * indicating the acceptable forms of input for this class.
043: *
044: * @see JListLocation
045: * @see JTreeLocation
046: * @see JTableLocation
047: */
048: public class ComponentLocation {
049:
050: /** Special <code>ComponentLocation</code> encoding which represents
051: the center of the component.
052: */
053: public static final String CENTER = "(center)";
054:
055: private Point where = null;
056:
057: /** Create a simple location which represents the center of a component. */
058: public ComponentLocation() {
059: }
060:
061: /** Create a simple location. */
062: public ComponentLocation(Point where) {
063: this .where = new Point(where);
064: }
065:
066: /** Convert the abstract location into a concrete one. Returns
067: * a {@link Point} relative to the given {@link Component}.
068: */
069: public Point getPoint(Component c)
070: throws LocationUnavailableException {
071: return where != null ? new Point(where) : new Point(c
072: .getWidth() / 2, c.getHeight() / 2);
073: }
074:
075: /** Convert the abstract location into a concrete area, relative
076: * to the given <code>Component</code>. If a point has
077: * been specified, returns a 1x1 rectangle, otherwise returns the
078: * a rectangle at (0, 0) of the Component's size.
079: */
080: public Rectangle getBounds(Component c)
081: throws LocationUnavailableException {
082: if (where == null)
083: return new Rectangle(0, 0, c.getWidth(), c.getHeight());
084: return new Rectangle(where.x, where.y, 1, 1);
085: }
086:
087: /** Returns whether the given object is an equivalent
088: * <code>ComponentLocation</code>.
089: */
090: public boolean equals(Object o) {
091: if (o instanceof ComponentLocation) {
092: ComponentLocation loc = (ComponentLocation) o;
093: return (where == null && loc.where == null)
094: || (where != null && where.equals(loc.where));
095: }
096: return false;
097: }
098:
099: public String toString() {
100: if (where != null)
101: return "(" + where.x + "," + where.y + ")";
102: return CENTER;
103: }
104:
105: protected String badFormat(String encoded) {
106: return Strings.get("location.component.bad_format",
107: new Object[] { encoded });
108: }
109:
110: protected String encodeIndex(int index) {
111: return "[" + index + "]";
112: }
113:
114: /** Returns whether the given (trimmed) <code>String</code> is an encoded
115: index.
116: */
117: protected boolean isIndex(String encoded) {
118: return encoded.startsWith("[") && encoded.endsWith("]");
119: }
120:
121: /** Extract the encoded index. */
122: protected int parseIndex(String encoded) {
123: try {
124: return Integer.parseInt(encoded.substring(1,
125: encoded.length() - 1).trim());
126: } catch (NumberFormatException e) {
127: throw new IllegalArgumentException(badFormat(encoded));
128: }
129: }
130:
131: protected String encodeValue(String value) {
132: return "\"" + value + "\"";
133: }
134:
135: /** Returns whether the given (trimmed) <code>String</code> is an encoded
136: value.
137: */
138: protected boolean isValue(String encoded) {
139: return encoded.startsWith("\"") && encoded.endsWith("\"");
140: }
141:
142: /** Extract the encoded value. */
143: protected String parseValue(String encoded) {
144: return encoded.substring(1, encoded.length() - 1);
145: }
146:
147: /** Convert the given encoding into the proper location.
148: Allowed formats: (x, y)
149: <p>
150: */
151: public ComponentLocation parse(String encoded) {
152: encoded = encoded.trim();
153: if (encoded.equals(CENTER)) {
154: where = null;
155: return this ;
156: }
157: if (encoded.startsWith("(") && encoded.endsWith(")")) {
158: StringTokenizer st = new StringTokenizer(encoded.substring(
159: 1, encoded.length() - 1), ",");
160: if (st.countTokens() == 2) {
161: try {
162: int x = Integer.parseInt(st.nextToken().trim());
163: int y = Integer.parseInt(st.nextToken().trim());
164: where = new Point(x, y);
165: return this ;
166: } catch (NumberFormatException nfe) {
167: }
168: }
169: }
170: throw new IllegalArgumentException(badFormat(encoded));
171: }
172: }
|