001/* 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with 004 * this work for additional information regarding copyright ownership. 005 * The ASF licenses this file to You under the Apache License, Version 2.0 006 * (the "License"); you may not use this file except in compliance with 007 * the License. You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017package org.apache.commons.functor.range; 018 019import java.util.Collection; 020import java.util.Collections; 021import java.util.Iterator; 022 023import org.apache.commons.functor.BinaryFunction; 024import org.apache.commons.lang3.ObjectUtils; 025import org.apache.commons.lang3.Validate; 026import org.apache.commons.lang3.builder.EqualsBuilder; 027 028/** 029 * Abstract {@link Range}. 030 * 031 * @param <T> type of element 032 * @param <S> type of step 033 */ 034public abstract class AbstractRange<T extends Comparable<T>, S> implements Range<T, S> { 035 036 /** 037 * Left limit. 038 */ 039 protected final Endpoint<T> leftEndpoint; 040 041 /** 042 * Right limit. 043 */ 044 protected final Endpoint<T> rightEndpoint; 045 046 /** 047 * Increment step. 048 */ 049 protected final S step; 050 051 /** 052 * Function to implement the taking of a step. 053 */ 054 private final BinaryFunction<T, S, T> nextValue; 055 056 /** 057 * Create a new {@link AbstractRange}. 058 * 059 * @param leftEndpoint left endpoint 060 * @param rightEndpoint right endpoint 061 * @param step increment step 062 * @param nextValue function to implement the taking of a step 063 */ 064 protected AbstractRange(Endpoint<T> leftEndpoint, Endpoint<T> rightEndpoint, S step, 065 BinaryFunction<T, S, T> nextValue) { 066 super(); 067 this.leftEndpoint = Validate.notNull(leftEndpoint, "Left Endpoint argument must not be null"); 068 this.rightEndpoint = Validate.notNull(rightEndpoint, "Right Endpoint argument must not be null"); 069 this.step = Validate.notNull(step, "step must not be null"); 070 this.nextValue = Validate.notNull(nextValue, "next value function"); 071 } 072 073 /** 074 * {@inheritDoc} 075 */ 076 public Endpoint<T> getLeftEndpoint() { 077 return leftEndpoint; 078 } 079 080 /** 081 * {@inheritDoc} 082 */ 083 public Endpoint<T> getRightEndpoint() { 084 return rightEndpoint; 085 } 086 087 /** 088 * {@inheritDoc} 089 */ 090 public S getStep() { 091 return step; 092 } 093 094 /** 095 * {@inheritDoc} 096 */ 097 public boolean containsAll(Collection<T> coll) { 098 if (coll == null || coll.isEmpty()) { 099 return false; 100 } 101 for (T t : coll) { 102 if (!contains(t)) { 103 return false; 104 } 105 } 106 return true; 107 } 108 109 // iterable 110 // --------------------------------------------------------------- 111 /** 112 * {@inheritDoc} 113 */ 114 public final Iterator<T> iterator() { 115 // empty range -> empty iterator: 116 if (isEmpty()) { 117 return Collections.<T> emptySet().iterator(); 118 } 119 // not empty and same values -> single-value range: 120 if (ObjectUtils.equals(leftEndpoint.getValue(), rightEndpoint.getValue())) { 121 return Collections.singleton(leftEndpoint.getValue()).iterator(); 122 } 123 return createIterator(); 124 } 125 126 /** 127 * Create a non-empty iterator. 128 * 129 * @return Iterator 130 */ 131 protected abstract Iterator<T> createIterator(); 132 133 // object methods 134 // --------------------------------------------------------------- 135 /** 136 * {@inheritDoc} 137 */ 138 @Override 139 public String toString() { 140 final String pattern = "%s<%s, %s, %s>"; 141 return String.format(pattern, getClass().getSimpleName(), leftEndpoint.toLeftString(), 142 rightEndpoint.toRightString(), step); 143 } 144 145 /** 146 * {@inheritDoc} 147 */ 148 @Override 149 public boolean equals(Object obj) { 150 if (obj == this) { 151 return true; 152 } 153 if (!(obj instanceof AbstractRange<?, ?>)) { 154 return false; 155 } 156 final Range<?, ?> that = (Range<?, ?>) obj; 157 return new EqualsBuilder().append(getLeftEndpoint(), that.getLeftEndpoint()) 158 .append(getRightEndpoint(), that.getRightEndpoint()).append(getStep(), that.getStep()).build(); 159 } 160 161 /** 162 * {@inheritDoc} 163 */ 164 @Override 165 public int hashCode() { 166 int hash = getClass().getName().hashCode(); 167 hash <<= 2; 168 hash ^= this.leftEndpoint.hashCode(); 169 hash <<= 2; 170 hash ^= this.rightEndpoint.hashCode(); 171 hash <<= 2; 172 hash ^= this.step.hashCode(); 173 return hash; 174 } 175 176 /** 177 * {@inheritDoc} 178 */ 179 public final boolean isEmpty() { 180 final T leftValue = leftEndpoint.getValue(); 181 final T rightValue = rightEndpoint.getValue(); 182 183 final int cmp = ObjectUtils.compare(leftValue, rightValue); 184 final boolean closedLeft = leftEndpoint.getBoundType() == BoundType.CLOSED; 185 final boolean closedRight = rightEndpoint.getBoundType() == BoundType.CLOSED; 186 187 if (cmp == 0 && !(closedLeft && closedRight)) { 188 return true; 189 } 190 191 final T firstValue = closedLeft ? leftValue : nextValue.evaluate(leftValue, step); 192 193 final int cmpFirst = ObjectUtils.compare(firstValue, rightValue); 194 195 if (cmpFirst == 0) { 196 return !closedRight; 197 } 198 199 final boolean ascending = cmp < 0; 200 return ascending ? cmpFirst > 0 : cmpFirst < 0; 201 } 202 203}