001package org.unix4j.util; 002 003 004import java.util.ArrayList; 005import java.util.List; 006 007/** 008 * Range offers ways of specifying a range of numbers. 009 * <b>USAGE:</b><br/> 010 * There are no public constructors in this object. The user should use one of the static factory 011 * methods to create a Range.<br/> 012 * e.g. The following will create a range of numbers from 1 to 5<br/> 013 * <pre>Range.between(1,5);</pre> 014 * <br/> 015 * Chaining can be used to specify multiple range "elements"<br/> 016 * e.g. The following will create a range of numbers from 1 to 5, and all numbers above 10<br/> 017 * <pre>Range.between(1,5).andToEndFrom(10);</pre> 018 */ 019public class Range{ 020 final CompositeRange compositeRange = new CompositeRange(); 021 022 private Range(){} 023 private Range(final AbstractRange range){ 024 compositeRange.add(range); 025 } 026 027 /** 028 * @param index 029 * @return whether the given index is within this range 030 */ 031 public boolean isWithinRange(final int index) { 032 return compositeRange.isWithinRange(index); 033 } 034 035 /** 036 * @param indexes a list of indexes to include in the range 037 * @return a range object consisting of this range, and all 038 * previous range elements in the chain so far. 039 */ 040 public Range andOf(final int... indexes) { 041 compositeRange.add(Range.of(indexes)); 042 return this; 043 } 044 045 /** 046 * Adds a range element to include all indexes from 1 to and inclusive of the 047 * given index. 048 * @param to 049 * @return A range object consisting of this range, and all 050 * previous range elements in the chain so far. 051 */ 052 public Range andFromStartTo(final int to) { 053 compositeRange.add(Range.fromStartTo(to)); 054 return this; 055 } 056 057 /** 058 * Adds a range element to include all indexes from (and including) the given index 059 * to any greater index. 060 * @param from 061 * @return A range object consisting of this range, and all 062 * previous range elements in the chain so far. 063 */ 064 public Range andToEndFrom(final int from) { 065 compositeRange.add(Range.toEndFrom(from)); 066 return this; 067 } 068 069 /** 070 * Adds a range element to include all indexes between (and including) the given 071 * indexes. 072 * @param from 073 * @param to 074 * @return A range object consisting of this range, and all 075 * previous range elements in the chain so far. 076 */ 077 public Range andBetween(final int from, final int to) { 078 compositeRange.add(Range.between(from, to)); 079 return this; 080 } 081 082 @Override 083 public String toString() { 084 return compositeRange.toString(); 085 } 086 087 /** 088 * @param indexes a list of indexes to include in the range 089 * @return A new range object consisting of this range. 090 */ 091 public static Range of(int ... indexes){ 092 final Range range = new Range(); 093 for(int i: indexes){ 094 range.compositeRange.add(Range.includingSingleIndexOf(i)); 095 } 096 return range; 097 } 098 099 /** 100 * Creates a range element to include all indexes from 1 to (and inclusive of) the 101 * given index. 102 * @param to 103 * @return A range object consisting of this range, and all 104 * previous range elements in the chain so far. 105 */ 106 public static Range fromStartTo(final int to){ 107 Assert.assertArgGreaterThan("index must be greater than zero", to, 0); 108 return new Range(new AbstractRange(){ 109 @Override public String toString(){ return "-" + to; } 110 @Override public boolean isWithinRange(int index){return index <= to;} 111 }); 112 } 113 114 /** 115 * Creates a range element to include all elements from (and including) the given 116 * index to any greater index. 117 * @param from 118 * @return A range object consisting of this range. 119 */ 120 public static Range toEndFrom(final int from){ 121 Assert.assertArgGreaterThan("index must be greater than zero", from, 0); 122 return new Range(new AbstractRange(){ 123 private final int index = from; 124 @Override public String toString(){ return index + "-";} 125 @Override public boolean isWithinRange(int index){return index >= from;} 126 }); 127 } 128 129 /** 130 * Creates a range element to include all elements between (and including) the given 131 * indexes. 132 * @param from 133 * @param to 134 * @return A new range object consisting of this range. 135 */ 136 public static Range between(final int from, final int to){ 137 Assert.assertArgGreaterThan("index must be greater than zero", from, 0); 138 return new Range(new AbstractRange(){ 139 @Override public String toString(){ return from + "-" + to;} 140 @Override public boolean isWithinRange(int index){return from <= index && index <= to;} 141 }); 142 } 143 144 private static AbstractRange includingSingleIndexOf(final int singleIndex){ 145 Assert.assertArgGreaterThan("index must be greater than zero", singleIndex, 0); 146 return new AbstractRange(){ 147 @Override public String toString(){ return "" + singleIndex;} 148 @Override public boolean isWithinRange(int index){return index == singleIndex;} 149 }; 150 } 151} 152 153abstract class AbstractRange { 154 public abstract boolean isWithinRange(int index); 155} 156 157class CompositeRange extends AbstractRange { 158 private List<AbstractRange> rangeElements = new ArrayList<AbstractRange>(); 159 160 public void add(final AbstractRange range){ 161 rangeElements.add(range); 162 } 163 164 public void add(final Range range){ 165 rangeElements.addAll(range.compositeRange.rangeElements); 166 } 167 168 public AbstractRange first() { 169 return rangeElements.get(0); 170 } 171 172 public AbstractRange last() { 173 return rangeElements.get(rangeElements.size() - 1); 174 } 175 176 @Override 177 public boolean isWithinRange(int index){ 178 for(final AbstractRange range: rangeElements){ 179 if(range.isWithinRange(index)){ 180 return true; 181 } 182 } 183 return false; 184 } 185 186 @Override 187 public String toString() { 188 final StringBuilder sb = new StringBuilder(); 189 for (AbstractRange range : rangeElements) { 190 if (sb.length() > 0) sb.append(","); 191 sb.append(range); 192 } 193 return sb.toString(); 194 } 195} 196 197 198 199 200