- /*
- * @(#)Map.java 1.8 03/12/19
- *
- * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
- * SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
- */
- package javax.swing.text.html;
-
- import java.awt.Polygon;
- import java.awt.Rectangle;
- import java.util.StringTokenizer;
- import java.util.Vector;
- import javax.swing.text.AttributeSet;
-
- /**
- * Map is used to represent a map element that is part of an HTML document.
- * Once a Map has been created, and any number of areas have been added,
- * you can test if a point falls inside the map via the contains method.
- *
- * @author Scott Violet
- * @version 1.8 12/19/03
- */
- class Map {
- /** Name of the Map. */
- private String name;
- /** An array of AttributeSets. */
- private Vector areaAttributes;
- /** An array of RegionContainments, will slowly grow to match the
- * length of areaAttributes as needed. */
- private Vector areas;
-
- public Map() {
- }
-
- public Map(String name) {
- this.name = name;
- }
-
- /**
- * Returns the name of the Map.
- */
- public String getName() {
- return name;
- }
-
- /**
- * Defines a region of the Map, based on the passed in AttributeSet.
- */
- public void addArea(AttributeSet as) {
- if (as == null) {
- return;
- }
- if (areaAttributes == null) {
- areaAttributes = new Vector(2);
- }
- areaAttributes.addElement(as.copyAttributes());
- }
-
- /**
- * Removes the previously created area.
- */
- public void removeArea(AttributeSet as) {
- if (as != null && areaAttributes != null) {
- int numAreas = (areas != null) ? areas.size() : 0;
- for (int counter = areaAttributes.size() - 1; counter >= 0;
- counter--) {
- if (((AttributeSet)areaAttributes.elementAt(counter)).
- isEqual(as)){
- areaAttributes.removeElementAt(counter);
- if (counter < numAreas) {
- areas.removeElementAt(counter);
- }
- }
- }
- }
- }
-
- /**
- * Returns the AttributeSets representing the differet areas of the Map.
- */
- public AttributeSet[] getAreas() {
- int numAttributes = (areaAttributes != null) ? areaAttributes.size() :
- 0;
- if (numAttributes != 0) {
- AttributeSet[] retValue = new AttributeSet[numAttributes];
-
- areaAttributes.copyInto(retValue);
- return retValue;
- }
- return null;
- }
-
- /**
- * Returns the AttributeSet that contains the passed in location,
- * <code>x</code>, <code>y</code>. <code>width</code>, <code>height</code>
- * gives the size of the region the map is defined over. If a matching
- * area is found, the AttribueSet for it is returned.
- */
- public AttributeSet getArea(int x, int y, int width, int height) {
- int numAttributes = (areaAttributes != null) ?
- areaAttributes.size() : 0;
-
- if (numAttributes > 0) {
- int numAreas = (areas != null) ? areas.size() : 0;
-
- if (areas == null) {
- areas = new Vector(numAttributes);
- }
- for (int counter = 0; counter < numAttributes; counter++) {
- if (counter >= numAreas) {
- areas.addElement(createRegionContainment
- ((AttributeSet)areaAttributes.elementAt(counter)));
- }
- RegionContainment rc = (RegionContainment)areas.
- elementAt(counter);
- if (rc != null && rc.contains(x, y, width, height)) {
- return (AttributeSet)areaAttributes.elementAt(counter);
- }
- }
- }
- return null;
- }
-
- /**
- * Creates and returns an instance of RegionContainment that can be
- * used to test if a particular point lies inside a region.
- */
- protected RegionContainment createRegionContainment
- (AttributeSet attributes) {
- Object shape = attributes.getAttribute(HTML.Attribute.SHAPE);
-
- if (shape == null) {
- shape = "rect";
- }
- if (shape instanceof String) {
- String shapeString = ((String)shape).toLowerCase();
- RegionContainment rc = null;
-
- try {
- if (shapeString.equals("rect")) {
- rc = new RectangleRegionContainment(attributes);
- }
- else if (shapeString.equals("circle")) {
- rc = new CircleRegionContainment(attributes);
- }
- else if (shapeString.equals("poly")) {
- rc = new PolygonRegionContainment(attributes);
- }
- else if (shapeString.equals("default")) {
- rc = DefaultRegionContainment.sharedInstance();
- }
- } catch (RuntimeException re) {
- // Something wrong with attributes.
- rc = null;
- }
- return rc;
- }
- return null;
- }
-
- /**
- * Creates and returns an array of integers from the String
- * <code>stringCoords</code>. If one of the values represents a
- * % the returned value with be negative. If a parse error results
- * from trying to parse one of the numbers null is returned.
- */
- static protected int[] extractCoords(Object stringCoords) {
- if (stringCoords == null || !(stringCoords instanceof String)) {
- return null;
- }
-
- StringTokenizer st = new StringTokenizer((String)stringCoords,
- ", \t\n\r");
- int[] retValue = null;
- int numCoords = 0;
-
- while(st.hasMoreElements()) {
- String token = st.nextToken();
- int scale;
-
- if (token.endsWith("%")) {
- scale = -1;
- token = token.substring(0, token.length() - 1);
- }
- else {
- scale = 1;
- }
- try {
- int intValue = Integer.parseInt(token);
-
- if (retValue == null) {
- retValue = new int[4];
- }
- else if(numCoords == retValue.length) {
- int[] temp = new int[retValue.length * 2];
-
- System.arraycopy(retValue, 0, temp, 0, retValue.length);
- retValue = temp;
- }
- retValue[numCoords++] = intValue * scale;
- } catch (NumberFormatException nfe) {
- return null;
- }
- }
- if (numCoords > 0 && numCoords != retValue.length) {
- int[] temp = new int[numCoords];
-
- System.arraycopy(retValue, 0, temp, 0, numCoords);
- retValue = temp;
- }
- return retValue;
- }
-
-
- /**
- * Defines the interface used for to check if a point is inside a
- * region.
- */
- interface RegionContainment {
- /**
- * Returns true if the location <code>x</code>, <code>y</code>
- * falls inside the region defined in the receiver.
- * <code>width</code>, <code>height</code> is the size of
- * the enclosing region.
- */
- public boolean contains(int x, int y, int width, int height);
- }
-
-
- /**
- * Used to test for containment in a rectangular region.
- */
- static class RectangleRegionContainment implements RegionContainment {
- /** Will be non-null if one of the values is a percent, and any value
- * that is non null indicates it is a percent
- * (order is x, y, width, height). */
- float[] percents;
- /** Last value of width passed in. */
- int lastWidth;
- /** Last value of height passed in. */
- int lastHeight;
- /** Top left. */
- int x0;
- int y0;
- /** Bottom right. */
- int x1;
- int y1;
-
- public RectangleRegionContainment(AttributeSet as) {
- int[] coords = Map.extractCoords(as.getAttribute(HTML.
- Attribute.COORDS));
-
- percents = null;
- if (coords == null || coords.length != 4) {
- throw new RuntimeException("Unable to parse rectangular area");
- }
- else {
- x0 = coords[0];
- y0 = coords[1];
- x1 = coords[2];
- y1 = coords[3];
- if (x0 < 0 || y0 < 0 || x1 < 0 || y1 < 0) {
- percents = new float[4];
- lastWidth = lastHeight = -1;
- for (int counter = 0; counter < 4; counter++) {
- if (coords[counter] < 0) {
- percents[counter] = Math.abs
- (coords[counter]) / 100.0f;
- }
- else {
- percents[counter] = -1.0f;
- }
- }
- }
- }
- }
-
- public boolean contains(int x, int y, int width, int height) {
- if (percents == null) {
- return contains(x, y);
- }
- if (lastWidth != width || lastHeight != height) {
- lastWidth = width;
- lastHeight = height;
- if (percents[0] != -1.0f) {
- x0 = (int)(percents[0] * width);
- }
- if (percents[1] != -1.0f) {
- y0 = (int)(percents[1] * height);
- }
- if (percents[2] != -1.0f) {
- x1 = (int)(percents[2] * width);
- }
- if (percents[3] != -1.0f) {
- y1 = (int)(percents[3] * height);
- }
- }
- return contains(x, y);
- }
-
- public boolean contains(int x, int y) {
- return ((x >= x0 && x <= x1) &&
- (y >= y0 && y <= y1));
- }
- }
-
-
- /**
- * Used to test for containment in a polygon region.
- */
- static class PolygonRegionContainment extends Polygon implements
- RegionContainment {
- /** If any value is a percent there will be an entry here for the
- * percent value. Use percentIndex to find out the index for it. */
- float[] percentValues;
- int[] percentIndexs;
- /** Last value of width passed in. */
- int lastWidth;
- /** Last value of height passed in. */
- int lastHeight;
-
- public PolygonRegionContainment(AttributeSet as) {
- int[] coords = Map.extractCoords(as.getAttribute(HTML.Attribute.
- COORDS));
-
- if (coords == null || coords.length == 0 ||
- coords.length % 2 != 0) {
- throw new RuntimeException("Unable to parse polygon area");
- }
- else {
- int numPercents = 0;
-
- lastWidth = lastHeight = -1;
- for (int counter = coords.length - 1; counter >= 0;
- counter--) {
- if (coords[counter] < 0) {
- numPercents++;
- }
- }
-
- if (numPercents > 0) {
- percentIndexs = new int[numPercents];
- percentValues = new float[numPercents];
- for (int counter = coords.length - 1, pCounter = 0;
- counter >= 0; counter--) {
- if (coords[counter] < 0) {
- percentValues[pCounter] = coords[counter] /
- -100.0f;
- percentIndexs[pCounter] = counter;
- pCounter++;
- }
- }
- }
- else {
- percentIndexs = null;
- percentValues = null;
- }
- npoints = coords.length / 2;
- xpoints = new int[npoints];
- ypoints = new int[npoints];
-
- for (int counter = 0; counter < npoints; counter++) {
- xpoints[counter] = coords[counter + counter];
- ypoints[counter] = coords[counter + counter + 1];
- }
- }
- }
-
- public boolean contains(int x, int y, int width, int height) {
- if (percentValues == null || (lastWidth == width &&
- lastHeight == height)) {
- return contains(x, y);
- }
- // Force the bounding box to be recalced.
- bounds = null;
- lastWidth = width;
- lastHeight = height;
- float fWidth = (float)width;
- float fHeight = (float)height;
- for (int counter = percentValues.length - 1; counter >= 0;
- counter--) {
- if (percentIndexs[counter] % 2 == 0) {
- // x
- xpoints[percentIndexs[counter] / 2] =
- (int)(percentValues[counter] * fWidth);
- }
- else {
- // y
- ypoints[percentIndexs[counter] / 2] =
- (int)(percentValues[counter] * fHeight);
- }
- }
- return contains(x, y);
- }
- }
-
-
- /**
- * Used to test for containment in a circular region.
- */
- static class CircleRegionContainment implements RegionContainment {
- /** X origin of the circle. */
- int x;
- /** Y origin of the circle. */
- int y;
- /** Radius of the circle. */
- int radiusSquared;
- /** Non-null indicates one of the values represents a percent. */
- float[] percentValues;
- /** Last value of width passed in. */
- int lastWidth;
- /** Last value of height passed in. */
- int lastHeight;
-
- public CircleRegionContainment(AttributeSet as) {
- int[] coords = Map.extractCoords(as.getAttribute(HTML.Attribute.
- COORDS));
-
- if (coords == null || coords.length != 3) {
- throw new RuntimeException("Unable to parse circular area");
- }
- x = coords[0];
- y = coords[1];
- radiusSquared = coords[2] * coords[2];
- if (coords[0] < 0 || coords[1] < 0 || coords[2] < 0) {
- lastWidth = lastHeight = -1;
- percentValues = new float[3];
- for (int counter = 0; counter < 3; counter++) {
- if (coords[counter] < 0) {
- percentValues[counter] = coords[counter] /
- -100.0f;
- }
- else {
- percentValues[counter] = -1.0f;
- }
- }
- }
- else {
- percentValues = null;
- }
- }
-
- public boolean contains(int x, int y, int width, int height) {
- if (percentValues != null && (lastWidth != width ||
- lastHeight != height)) {
- int newRad = Math.min(width, height) / 2;
-
- lastWidth = width;
- lastHeight = height;
- if (percentValues[0] != -1.0f) {
- this.x = (int)(percentValues[0] * width);
- }
- if (percentValues[1] != -1.0f) {
- this.y = (int)(percentValues[1] * height);
- }
- if (percentValues[2] != -1.0f) {
- radiusSquared = (int)(percentValues[2] *
- Math.min(width, height));
- radiusSquared *= radiusSquared;
- }
- }
- return (((x - this.x) * (x - this.x) +
- (y - this.y) * (y - this.y)) <= radiusSquared);
- }
- }
-
-
- /**
- * An implementation that will return true if the x, y location is
- * inside a rectangle defined by origin 0, 0, and width equal to
- * width passed in, and height equal to height passed in.
- */
- static class DefaultRegionContainment implements RegionContainment {
- /** A global shared instance. */
- static DefaultRegionContainment si = null;
-
- public static DefaultRegionContainment sharedInstance() {
- if (si == null) {
- si = new DefaultRegionContainment();
- }
- return si;
- }
-
- public boolean contains(int x, int y, int width, int height) {
- return (x <= width && x >= 0 && y >= 0 && y <= width);
- }
- }
- }