| /* | |
| * Copyright (c) 2005, 2018, Oracle and/or its affiliates. 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. | |
| * | |
| * 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA | |
| * or visit www.oracle.com if you need additional information or have any | |
| * questions. | |
| */ | |
| /* | |
| * @test | |
| * @bug 6210287 | |
| * @summary Verifies that the various utility shapes implement | |
| * the equals(Object) and hashCode methods adequately | |
| */ | |
| import java.awt.geom.Arc2D; | |
| import java.awt.geom.Ellipse2D; | |
| import java.awt.geom.Rectangle2D; | |
| import java.awt.geom.RoundRectangle2D; | |
| import java.util.Vector; | |
| public class EqualsHashcode { | |
| public static final int NUMTESTS = 1000; | |
| public static void main(String argv[]) { | |
| new FloatRectangleTester().test(); | |
| new DoubleRectangleTester().test(); | |
| new FloatEllipseTester().test(); | |
| new DoubleEllipseTester().test(); | |
| new FloatArcTester().test(); | |
| new DoubleArcTester().test(); | |
| new FloatRoundRectTester().test(); | |
| new DoubleRoundRectTester().test(); | |
| } | |
| /** | |
| * Base utility class for random parameters for testing | |
| */ | |
| public static abstract class Val { | |
| protected String name; | |
| protected Val(String name) { | |
| this.name = name; | |
| } | |
| public abstract Object save(); | |
| public abstract void restore(Object save); | |
| public abstract void randomize(); | |
| public abstract void perturb(); | |
| } | |
| /** | |
| * Base subclass for parameters with "special" values (Infinity, NaN, etc.) | |
| */ | |
| public static abstract class SpecialVal extends Val { | |
| protected SpecialVal(String name) { | |
| super(name); | |
| } | |
| public abstract void setSpecial(); | |
| public abstract boolean isNaN(); | |
| } | |
| /** | |
| * Floating point parameter | |
| */ | |
| public static class FloatVal extends SpecialVal { | |
| private float v; | |
| public FloatVal(String name) { | |
| super(name); | |
| } | |
| public float getVal() { | |
| return v; | |
| } | |
| public String toString() { | |
| return name+" = "+v+" (flt)"; | |
| } | |
| public Object save() { | |
| return new Float(v); | |
| } | |
| public void restore(Object o) { | |
| v = ((Float) o).floatValue(); | |
| } | |
| public void randomize() { | |
| v = (float) (Math.random() * 100); | |
| } | |
| public void perturb() { | |
| v = v + 1; | |
| } | |
| public boolean hasSpecialCases() { | |
| return true; | |
| } | |
| public void setSpecial() { | |
| switch ((int) (Math.random() * 3)) { | |
| case 0: | |
| v = Float.NaN; | |
| break; | |
| case 1: | |
| v = Float.POSITIVE_INFINITY; | |
| break; | |
| case 2: | |
| v = Float.NEGATIVE_INFINITY; | |
| break; | |
| default: | |
| throw new InternalError(); | |
| } | |
| } | |
| public boolean isNaN() { | |
| return (v != v); | |
| } | |
| } | |
| /** | |
| * Double precision parameter | |
| */ | |
| public static class DoubleVal extends SpecialVal { | |
| private double v; | |
| public DoubleVal(String name) { | |
| super(name); | |
| } | |
| public double getVal() { | |
| return v; | |
| } | |
| public String toString() { | |
| return name+" = "+v+" (dbl)"; | |
| } | |
| public Object save() { | |
| return new Double(v); | |
| } | |
| public void restore(Object o) { | |
| v = ((Double) o).doubleValue(); | |
| } | |
| public void randomize() { | |
| v = Math.random() * 100; | |
| } | |
| public void perturb() { | |
| v = v + 1; | |
| } | |
| public boolean hasSpecialCases() { | |
| return true; | |
| } | |
| public void setSpecial() { | |
| switch ((int) (Math.random() * 3)) { | |
| case 0: | |
| v = Double.NaN; | |
| break; | |
| case 1: | |
| v = Double.POSITIVE_INFINITY; | |
| break; | |
| case 2: | |
| v = Double.NEGATIVE_INFINITY; | |
| break; | |
| default: | |
| throw new InternalError(); | |
| } | |
| } | |
| public boolean isNaN() { | |
| return (v != v); | |
| } | |
| } | |
| /** | |
| * Integer value with a specified min/max range. | |
| */ | |
| public static class IntRangeVal extends Val { | |
| public int v; | |
| public int min; | |
| public int max; | |
| public IntRangeVal(String name, int min, int max) { | |
| super(name); | |
| this.min = min; | |
| this.max = max; | |
| } | |
| public int getVal() { | |
| return v; | |
| } | |
| public String toString() { | |
| return name+" = "+v; | |
| } | |
| public Object save() { | |
| return new Integer(v); | |
| } | |
| public void restore(Object o) { | |
| v = ((Integer) o).intValue(); | |
| } | |
| public void randomize() { | |
| v = min + (int) (Math.random() * (max-min+1)); | |
| } | |
| public void perturb() { | |
| v = v + 1; | |
| if (v > max) { | |
| v = min; | |
| } | |
| } | |
| } | |
| /** | |
| * Base class for testing a given type of shape. | |
| * Subclasses must register all of their "parameters" which | |
| * need to be randomized, specialized, and perturbed for | |
| * testing. | |
| * Subclasses must also implement makeShape() which makes | |
| * a new shape object according to the current values of | |
| * all of their parameters. | |
| */ | |
| public static abstract class ShapeTester { | |
| public Vector params = new Vector(); | |
| public void addParam(Val v) { | |
| params.add(v); | |
| } | |
| public Val[] getParamArray() { | |
| Val ret[] = new Val[params.size()]; | |
| for (int i = 0; i < params.size(); i++) { | |
| ret[i] = (Val) params.get(i); | |
| } | |
| return ret; | |
| } | |
| public void error(String desc) { | |
| Val params[] = getParamArray(); | |
| for (int j = 0; j < params.length; j++) { | |
| System.err.println(params[j]); | |
| } | |
| throw new RuntimeException(desc); | |
| } | |
| public abstract Object makeShape(); | |
| public void test() { | |
| Val params[] = getParamArray(); | |
| for (int i = 0; i < NUMTESTS; i++) { | |
| // First, randomize all parameters | |
| for (int j = 0; j < params.length; j++) { | |
| params[j].randomize(); | |
| } | |
| // Now make 2 copies from the same params and verify equals() | |
| Object o1 = makeShape(); | |
| if (!o1.equals(o1)) { | |
| error("Shapes not equal to itself!"); | |
| } | |
| Object o2 = makeShape(); | |
| if (!o1.equals(o2)) { | |
| error("Identical shapes not equal!"); | |
| } | |
| if (o1.hashCode() != o2.hashCode()) { | |
| error("Identical hashes not equal!"); | |
| } | |
| // Now perturb the params 1 by 1 and verify !equals() | |
| for (int j = 0; j < params.length; j++) { | |
| Val param = params[j]; | |
| Object save = param.save(); | |
| param.perturb(); | |
| Object o3 = makeShape(); | |
| if (o1.equals(o3)) { | |
| error("Perturbed shape still equal!"); | |
| } | |
| // If param has "special values", test them as well | |
| if (param instanceof SpecialVal) { | |
| SpecialVal sparam = (SpecialVal) param; | |
| sparam.setSpecial(); | |
| Object o4 = makeShape(); | |
| if (o1.equals(o4)) { | |
| error("Specialized shape still equal!"); | |
| } | |
| Object o5 = makeShape(); | |
| // objects equal iff param is not a NaN | |
| if (o4.equals(o5) == sparam.isNaN()) { | |
| error("Identical specialized shapes not equal!"); | |
| } | |
| // hash codes always equal, even if NaN | |
| // (Note: equals()/hashCode() contract allows this) | |
| if (o4.hashCode() != o5.hashCode()) { | |
| error("Identical specialized hashes not equal!"); | |
| } | |
| } | |
| // Restore original value of param and make sure | |
| param.restore(save); | |
| Object o6 = makeShape(); | |
| if (!o1.equals(o6)) { | |
| error("Restored shape not equal!"); | |
| } | |
| if (o1.hashCode() != o6.hashCode()) { | |
| error("Restored hash not equal!"); | |
| } | |
| } | |
| } | |
| } | |
| } | |
| /** | |
| * Base tester class for objects with floating point xywh bounds | |
| */ | |
| public static abstract class FloatBoundedShape extends ShapeTester { | |
| public FloatVal x = new FloatVal("x"); | |
| public FloatVal y = new FloatVal("y"); | |
| public FloatVal w = new FloatVal("w"); | |
| public FloatVal h = new FloatVal("h"); | |
| public FloatBoundedShape() { | |
| addParam(x); | |
| addParam(y); | |
| addParam(w); | |
| addParam(h); | |
| } | |
| } | |
| /** | |
| * Base tester class for objects with double precision xywh bounds | |
| */ | |
| public static abstract class DoubleBoundedShape extends ShapeTester { | |
| public DoubleVal x = new DoubleVal("x"); | |
| public DoubleVal y = new DoubleVal("y"); | |
| public DoubleVal w = new DoubleVal("w"); | |
| public DoubleVal h = new DoubleVal("h"); | |
| public DoubleBoundedShape() { | |
| addParam(x); | |
| addParam(y); | |
| addParam(w); | |
| addParam(h); | |
| } | |
| } | |
| public static class FloatRectangleTester extends FloatBoundedShape { | |
| public Object makeShape() { | |
| return new Rectangle2D.Float(x.getVal(), y.getVal(), | |
| w.getVal(), h.getVal()); | |
| } | |
| } | |
| public static class DoubleRectangleTester extends DoubleBoundedShape { | |
| public Object makeShape() { | |
| return new Rectangle2D.Double(x.getVal(), y.getVal(), | |
| w.getVal(), h.getVal()); | |
| } | |
| } | |
| public static class FloatEllipseTester extends FloatBoundedShape { | |
| public Object makeShape() { | |
| return new Ellipse2D.Float(x.getVal(), y.getVal(), | |
| w.getVal(), h.getVal()); | |
| } | |
| } | |
| public static class DoubleEllipseTester extends DoubleBoundedShape { | |
| public Object makeShape() { | |
| return new Ellipse2D.Double(x.getVal(), y.getVal(), | |
| w.getVal(), h.getVal()); | |
| } | |
| } | |
| public static class FloatArcTester extends FloatBoundedShape { | |
| public FloatVal start = new FloatVal("start"); | |
| public FloatVal extent = new FloatVal("extent"); | |
| public IntRangeVal type = new IntRangeVal("type", 0, 2); | |
| public FloatArcTester() { | |
| addParam(start); | |
| addParam(extent); | |
| addParam(type); | |
| } | |
| public Object makeShape() { | |
| return new Arc2D.Float(x.getVal(), y.getVal(), | |
| w.getVal(), h.getVal(), | |
| start.getVal(), extent.getVal(), | |
| type.getVal()); | |
| } | |
| } | |
| public static class DoubleArcTester extends DoubleBoundedShape { | |
| public DoubleVal start = new DoubleVal("start"); | |
| public DoubleVal extent = new DoubleVal("extent"); | |
| public IntRangeVal type = new IntRangeVal("type", 0, 2); | |
| public DoubleArcTester() { | |
| addParam(start); | |
| addParam(extent); | |
| addParam(type); | |
| } | |
| public Object makeShape() { | |
| return new Arc2D.Double(x.getVal(), y.getVal(), | |
| w.getVal(), h.getVal(), | |
| start.getVal(), extent.getVal(), | |
| type.getVal()); | |
| } | |
| } | |
| public static class FloatRoundRectTester extends FloatBoundedShape { | |
| public FloatVal arcw = new FloatVal("arcw"); | |
| public FloatVal arch = new FloatVal("arch"); | |
| public FloatRoundRectTester() { | |
| addParam(arcw); | |
| addParam(arch); | |
| } | |
| public Object makeShape() { | |
| return new RoundRectangle2D.Float(x.getVal(), y.getVal(), | |
| w.getVal(), h.getVal(), | |
| arcw.getVal(), arch.getVal()); | |
| } | |
| } | |
| public static class DoubleRoundRectTester extends DoubleBoundedShape { | |
| public DoubleVal arcw = new DoubleVal("arcw"); | |
| public DoubleVal arch = new DoubleVal("arch"); | |
| public DoubleRoundRectTester() { | |
| addParam(arcw); | |
| addParam(arch); | |
| } | |
| public Object makeShape() { | |
| return new RoundRectangle2D.Double(x.getVal(), y.getVal(), | |
| w.getVal(), h.getVal(), | |
| arcw.getVal(), arch.getVal()); | |
| } | |
| } | |
| } |