package abbot.tester; import java.awt.*; import java.util.StringTokenizer; import abbot.i18n.Strings; /** Provides encapsulation of a visible Component-relative location. * "Visible" in this context means currently accessible by the pointer * (possibly via scrolling). A hidden node in a collapsed tree path * would <strong>not</strong> be considered visible. * <p> * This class the specifics of location so that {@link ComponentTester} * primitives * ({@link ComponentTester#actionClick(Component,ComponentLocation)}, * {@link ComponentTester#actionShowPopupMenu(Component,ComponentLocation)}, * etc) may be directed to specific elements of * substructure on a <code>Component</code> (list rows, tree paths, table * cells, substructure values, etc.). * <p> * Classes derived from <code>ComponentLocation</code> should provide * constructors for each type of location indication, e.g. value, index, and * {@link Point}. The {@link #toString()} method should provide an encoded * {@link String} suitable for use by the {@link #parse(String)} method, which * must convert the {@link String} encoding back into a proper * <code>ComponentLocation</code>. * <p> * By convention, {@link Point} locations are specified with (x,y) notation. * Indexed locations should use square brackets, e.g. [i] or [r,c] and * value locations should use a quoted {@link String}, e.g. * '"cuckoo for cocoa puffs"'. The specific syntax allowed will vary by * specific <code>ComponentLocation</code> type. The base * <code>ComponentLocation</code> implementation * supports only the explicit (x,y) notation. * <p> * Recorders * should use the {@link String} value by default for consistency. The * special value {@link #CENTER} is provided to indicate the center of a * {@link Component}. * <p> * The method {@link #badFormat(String)} should provide usage-like information * indicating the acceptable forms of input for this class. * * @see JListLocation * @see JTreeLocation * @see JTableLocation */ public class ComponentLocation { /** Special <code>ComponentLocation</code> encoding which represents the center of the component. */ public static final String CENTER = "(center)"; private Point where = null; /** Create a simple location which represents the center of a component. */ public ComponentLocation() { } /** Create a simple location. */ public ComponentLocation(Point where) { this.where = new Point(where); } /** Convert the abstract location into a concrete one. Returns * a {@link Point} relative to the given {@link Component}. */ public Point getPoint(Component c) throws LocationUnavailableException { return where != null ? new Point(where) : new Point(c.getWidth()/2, c.getHeight()/2); } /** Convert the abstract location into a concrete area, relative * to the given <code>Component</code>. If a point has * been specified, returns a 1x1 rectangle, otherwise returns the * a rectangle at (0, 0) of the Component's size. */ public Rectangle getBounds(Component c) throws LocationUnavailableException { if (where == null) return new Rectangle(0, 0, c.getWidth(), c.getHeight()); return new Rectangle(where.x, where.y, 1, 1); } /** Returns whether the given object is an equivalent * <code>ComponentLocation</code>. */ public boolean equals(Object o) { if (o instanceof ComponentLocation) { ComponentLocation loc = (ComponentLocation)o; return (where == null && loc.where == null) || (where != null && where.equals(loc.where)); } return false; } public String toString() { if (where != null) return "(" + where.x + "," + where.y + ")"; return CENTER; } protected String badFormat(String encoded) { return Strings.get("location.component.bad_format", new Object[] { encoded }); } protected String encodeIndex(int index) { return "[" + index + "]"; } /** Returns whether the given (trimmed) <code>String</code> is an encoded index. */ protected boolean isIndex(String encoded) { return encoded.startsWith("[") && encoded.endsWith("]"); } /** Extract the encoded index. */ protected int parseIndex(String encoded) { try { return Integer.parseInt(encoded. substring(1, encoded.length()-1).trim()); } catch(NumberFormatException e) { throw new IllegalArgumentException(badFormat(encoded)); } } protected String encodeValue(String value) { return "\"" + value + "\""; } /** Returns whether the given (trimmed) <code>String</code> is an encoded value. */ protected boolean isValue(String encoded) { return encoded.startsWith("\"") && encoded.endsWith("\""); } /** Extract the encoded value. */ protected String parseValue(String encoded) { return encoded.substring(1, encoded.length()-1); } /** Convert the given encoding into the proper location. Allowed formats: (x, y) <p> */ public ComponentLocation parse(String encoded) { encoded = encoded.trim(); if (encoded.equals(CENTER)) { where = null; return this; } if (encoded.startsWith("(") && encoded.endsWith(")")) { StringTokenizer st = new StringTokenizer(encoded.substring(1, encoded.length()-1), ","); if (st.countTokens() == 2) { try { int x = Integer.parseInt(st.nextToken().trim()); int y = Integer.parseInt(st.nextToken().trim()); where = new Point(x, y); return this; } catch(NumberFormatException nfe) { } } } throw new IllegalArgumentException(badFormat(encoded)); } }