// Copyright (c) 2003 Dustin Sallings <dustin@spy.net> package net.spy.util; /** * A range of Comparable objects. */ public class Range<T extends Comparable<T>> extends Object implements Comparable<Range<T>> { public enum MatchType { INCLUSIVE, EXCLUSIVE } private final T low; private final T high; private MatchType lowMatch=MatchType.INCLUSIVE; private MatchType highMatch=MatchType.INCLUSIVE; /** * Get an instance of Range. */ public Range(T lowObject, T highObject) { super(); low=lowObject; high=highObject; invariant(); } // Class invariant private void invariant() { if (low==null && high==null) { throw new IllegalArgumentException( "At least one of the low or high object must be set."); } if((low != null) && (high != null)) { if(low.compareTo(high) > 0) { throw new IllegalArgumentException( "Low object must not be greater than the high object"); } } } /** * Get the low match type. */ public MatchType getLowMatch() { return(lowMatch); } /** * Set the low match type. * * @param lm either INCLUSIVE or EXCLUSIVE */ public void setLowMatch(MatchType lm) { lowMatch=lm; } /** * Get the high match type. */ public MatchType getHighMatch() { return(highMatch); } /** * Set the high match type. * * @param hm either INCLUSIVE or EXCLUSIVE */ public void setHighMatch(MatchType hm) { this.highMatch=hm; } /** * Get the low object. */ public Comparable<T> getLow() { return(low); } /** * Get the high object. */ public Comparable<T> getHigh() { return(high); } /** * Describe this object in Set notation. */ @Override public String toString() { StringBuilder sb=new StringBuilder(256); sb.append("{Range "); if(lowMatch == MatchType.INCLUSIVE) { sb.append('['); } else { sb.append('('); } sb.append(low); sb.append(", "); sb.append(high); if(highMatch == MatchType.INCLUSIVE) { sb.append(']'); } else { sb.append(')'); } sb.append("}"); return(sb.toString()); } /** * True if the given object lies within this range. */ public boolean contains(T c) { // False if we prove it's not there boolean rv=true; int lowCompare=0; int highCompare=0; if(lowMatch == MatchType.EXCLUSIVE) { lowCompare=1; } if(highMatch == MatchType.EXCLUSIVE) { highCompare=-1; } if(c != null) { // If low and high are the same, this range represents an exact // match. if((low != null) && (high != null) && low.equals(high)) { if(!(c.compareTo(low) == 0)) { rv=false; } } else { // Make sure it's not below the low if((low != null) && (c.compareTo(low) < lowCompare)) { rv=false; } // Make sure it's not above the high if((high != null) && (c.compareTo(high) > highCompare)) { rv=false; } } } else { // Null isn't there rv=false; } return(rv); } /** * @see Comparable */ public int compareTo(Range<T> r) { int rv=compareLow(this, r); if(rv == 0) { rv=compareHigh(this, r); } return(rv); } /** * True if the given object is a Range object that represents the same * range. */ @Override // Unchecked Range cast. @SuppressWarnings("unchecked") public boolean equals(Object o) { boolean rv=false; if(o instanceof Range) { rv= (compareTo((Range<T>)o)==0); } return(rv); } /** * Get a predictable hash code. * @return the low object's hash code if it's available, else the high * object's hash code */ @Override public int hashCode() { return(low != null ? low.hashCode() : high.hashCode()); } // Compare two ranges based on their low value private int compareLow(Range<T> a, Range<T> b) { int rv=0; if(!(a.low==null && b.low == null)) { // At least one is not null if(a.low != null && b.low != null) { // neither is null, plain compare rv=a.low.compareTo(b.low); } else { // One is null, null is first if(a.low == null) { // First one is null rv=-1; } else { // second is null rv=1; } } } return(rv); } // Compare two ranges based on their high value private int compareHigh(Range<T> a, Range<T> b) { int rv=0; if(!(a.high==null && b.high == null)) { // At least one is not null if(a.high != null && b.high != null) { // neither is null, plain compare rv=a.high.compareTo(b.high); } else { // One is null, null is last if(a.high == null) { // First one is null rv=1; } else { // second is null rv=-1; } } } return(rv); } }