blob: 10f018fdffd02ba049e4861023d3153cfaee773f [file] [log] [blame]
/*
* 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());
}
}
}