jdk/src/share/classes/javax/swing/text/html/Map.java
changeset 2 90ce3da70b43
child 1287 a04aca99c77a
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/share/classes/javax/swing/text/html/Map.java	Sat Dec 01 00:00:00 2007 +0000
@@ -0,0 +1,505 @@
+/*
+ * Copyright 1998-2006 Sun Microsystems, Inc.  All Rights Reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+package javax.swing.text.html;
+
+import java.awt.Polygon;
+import java.io.Serializable;
+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
+ */
+class Map implements Serializable {
+    /** 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);
+        }
+    }
+}