import java.awt.Color;
import java.util.AbstractSet;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.Set;

public class TestEqualsWithSubclass {
	public static void main(String[] args) {
        final Set<Point> hashSet  = new HashSet<Point>();
		final Set<Point> arraySet = new ArraySet<Point>();

		addPoint(hashSet, arraySet, new Point(0, 0));
		addPoint(hashSet, arraySet, new Point(1, 0));
		addPoint(hashSet, arraySet, new ColoredPoint(1, 0, Color.red));
		addPoint(hashSet, arraySet, new ColoredPoint(1, 1, Color.blue));

		checkContains(hashSet, arraySet, new Point(0, 0));
		checkContains(hashSet, arraySet, new Point(1, 0));
		checkContains(hashSet, arraySet, new ColoredPoint(1, 0, Color.red));
		checkContains(hashSet, arraySet, new ColoredPoint(1, 0, Color.white));
		checkContains(hashSet, arraySet, new ColoredPoint(0, 0, Color.black));
        checkContains(hashSet, arraySet, new Point(1, 1));
	}

	private static void addPoint(Set<Point> set1, Set<Point> set2, Point p) {
		set1.add(p);
		set2.add(p);
	}

	private static void checkContains(Set<Point> set1, Set<Point> set2, Point p) {
		final boolean contains1 = set1.contains(p);
		final boolean contains2 = set2.contains(p);

		if (contains1 && contains2) {
			System.out.println(pad(p.toString(), ' ', 45) + "->     found in hashSet;     found in arraySet.");
		} else if (contains1) {
			System.out.println(pad(p.toString(), ' ', 45) + "->     found in hashSet; NOT found in arraySet.");
		} else if (contains2) {
			System.out.println(pad(p.toString(), ' ', 45) + "-> NOT found in hashSet;     found in arraySet.");
		} else {
			System.out.println(pad(p.toString(), ' ', 45) + "-> NOT found in hashSet; NOT found in arraySet.");
		}
	}

	private static String pad(String text, char c, int width) {
		final StringBuilder builder = new StringBuilder(width);

		builder.append(text);
		width -= text.length();
		while (width > 0) {
			builder.append(c);
			width--;
		}
		return builder.toString();
	}
}

class Point {
    private final int x;
    private final int y;

	public Point(int x, int y) {
	    this.x = x;
	    this.y = y;
	}

	public final int getX() {
		return this.x;
	}

	public final int getY() {
		return this.y;
	}

	@Override public boolean equals(Object o) {
		if (this == o) {
			return true;
		}
		if (!(o instanceof Point)) {
			return false;
		}

		final Point point = (Point) o;

		return (this.x == point.x && this.y == point.y);

	}

	@Override public int hashCode() {
		int result = this.x;
		result = 31 * result + this.y;
		return result;
	}

	@Override public String toString() {
		return "Point{x=" + this.x + ", y=" + this.y + '}';
	}
}

class ColoredPoint extends Point {
	private final Color color;

	public ColoredPoint(int x, int y, Color color) {
		super(x, y);
		this.color = color;
	}

	public final Color getColor() {
		return this.color;
	}

	@Override public boolean equals(Object o) {
		if (this == o) {
			return true;
		}
		if (!(o instanceof ColoredPoint && super.equals(o))) {
			return false;
		}

		final ColoredPoint point = (ColoredPoint) o;

		return (this.color == null ? (point.color == null) : this.color.equals(point.color));

	}

	@Override public int hashCode() {
		int result = super.hashCode();
		result = 31 * result + (this.color != null ? this.color.hashCode() : 0);
		return result;
	}

	@Override public String toString() {
		return "ColoredPoint{x=" + this.getX() + ", y=" + this.getY() +
		       ", color=#" + Integer.toHexString(this.color.getRGB()).substring(2) + '}';
	}
}

class ArraySet<E> extends AbstractSet<E> {
	private final ArrayList<E> list = new ArrayList<E>();

	public java.util.Iterator<E> iterator() {
		return this.list.iterator();
	}

	public int size() {
		return this.list.size();
	}

	@Override public boolean contains(Object object) {
		return this.list.contains(object);
	}

	@Override public boolean add(E e) {
		if (!this.list.contains(e)) {
			this.list.add(e);
			return true;
		}
		return false;
	}

	@Override public boolean remove(Object object) {
		return this.list.remove(object);
	}

	@Override public void clear() {
		this.list.clear();
	}
}

