// Copyright 2017 JanusGraph Authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package org.janusgraph.util.datastructures; import com.google.common.base.Preconditions; import org.apache.commons.lang.builder.HashCodeBuilder; import java.util.*; /** * @author Matthias Broecheler (me@matthiasb.com) */ public class RangeInterval<T> implements Interval<T> { private boolean startInclusive=true; private boolean endInclusive=true; private T start = null; private T end = null; public RangeInterval() { } public RangeInterval(T start, T end) { setStart(start,true); setEnd(end,true); } public RangeInterval<T> setStart(T start, boolean inclusive) { Preconditions.checkArgument(start instanceof Comparable); this.start=start; setStartInclusive(inclusive); return this; } public RangeInterval<T> setEnd(T end, boolean inclusive) { Preconditions.checkArgument(end instanceof Comparable); this.end=end; setEndInclusive(inclusive); return this; } public RangeInterval<T> setStartInclusive(boolean inclusive) { Preconditions.checkArgument(start==null || start instanceof Comparable); this.startInclusive=inclusive; return this; } public RangeInterval<T> setEndInclusive(boolean inclusive) { Preconditions.checkArgument(end==null || end instanceof Comparable); this.endInclusive=inclusive; return this; } @Override public T getStart() { return start; } @Override public T getEnd() { return end; } @Override public boolean startInclusive() { return startInclusive; } @Override public boolean endInclusive() { return endInclusive; } @Override public boolean isPoints() { return start!=null && end!=null && start.equals(end) && startInclusive && endInclusive; } @Override public Collection<T> getPoints() { Set<T> set = new HashSet<T>(2); if (start!=null) set.add(start); if (end!=null) set.add(end); return set; } @Override public boolean isEmpty() { if (start==null || end==null) return false; if (isPoints()) return false; int cmp = ((Comparable)start).compareTo(end); return cmp>0 || (cmp==0 && (!startInclusive || !endInclusive)); } @Override public Interval<T> intersect(Interval<T> other) { Preconditions.checkArgument(other!=null); if (other instanceof PointInterval) { return other.intersect(this); } else if (other instanceof RangeInterval) { final RangeInterval<T> rint = (RangeInterval)other; Map.Entry<T,Boolean> newStart = comparePoints(start,startInclusive,rint.start,rint.startInclusive,true); Map.Entry<T,Boolean> newEnd = comparePoints(end,endInclusive,rint.end,rint.endInclusive,false); RangeInterval<T> result = new RangeInterval<T>(newStart.getKey(),newEnd.getKey()); result.setStartInclusive(newStart.getValue()); result.setEndInclusive(newEnd.getValue()); return result; } else throw new AssertionError("Unexpected interval: " + other); } private Map.Entry<T,Boolean> comparePoints(T one, boolean oneIncl, T two, boolean twoIncl, boolean chooseBigger) { if (one==null) return new AbstractMap.SimpleImmutableEntry<T, Boolean>(two,twoIncl); if (two==null) return new AbstractMap.SimpleImmutableEntry<T, Boolean>(one,oneIncl); int c = ((Comparable)one).compareTo(two); if (c==0) { return new AbstractMap.SimpleImmutableEntry<T, Boolean>(one, oneIncl & twoIncl); } else if ((c>0 && chooseBigger) || (c<0 && !chooseBigger)) { return new AbstractMap.SimpleImmutableEntry<T, Boolean>(one,oneIncl); } else { return new AbstractMap.SimpleImmutableEntry<T, Boolean>(two,twoIncl); } } public boolean containsPoint(T other) { Preconditions.checkNotNull(other); if (isPoints()) return start.equals(other); else { if (start!=null) { int cmp = ((Comparable)start).compareTo(other); if (cmp>0 || (cmp==0 && !startInclusive)) return false; } if (end!=null) { int cmp = ((Comparable)end).compareTo(other); if (cmp<0 || (cmp==0 && !endInclusive)) return false; } return true; } } @Override public int hashCode() { return new HashCodeBuilder().append(start).append(end).append(startInclusive).append(endInclusive).toHashCode(); } @Override public boolean equals(Object other) { if (this==other) return true; else if (other==null) return false; else if (!getClass().isInstance(other)) return false; RangeInterval oth = (RangeInterval)other; if ((start==null ^ oth.start==null) || (end==null ^ oth.end==null)) return false; return start.equals(oth.start) && end.equals(oth.end) && endInclusive==oth.endInclusive && startInclusive==oth.startInclusive; } @Override public String toString() { StringBuilder b = new StringBuilder(); if (startInclusive) b.append("["); else b.append("("); b.append(start).append(",").append(end); if (endInclusive) b.append("]"); else b.append(")"); return b.toString(); } }