/* * 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()); } } }