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