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.Iterator;
020
021import org.apache.commons.functor.BinaryFunction;
022import org.apache.commons.lang3.Validate;
023
024/**
025 * A range of integers.
026 *
027 * @since 1.0
028 * @version $Revision: 1385335 $ $Date: 2012-09-16 15:08:31 -0300 (Sun, 16 Sep 2012) $
029 */
030public class IntegerRange extends NumericRange<Integer> {
031
032    // attributes
033    // ---------------------------------------------------------------
034
035    /**
036     * Calculate default step.
037     */
038    public static final BinaryFunction<Integer, Integer, Integer> DEFAULT_STEP =
039        new BinaryFunction<Integer, Integer, Integer>() {
040
041            public Integer evaluate(Integer left, Integer right) {
042                return left > right ? -1 : 1;
043            }
044        };
045
046    // constructors
047    // ---------------------------------------------------------------
048    /**
049     * Create a new IntegerRange.
050     *
051     * @param from
052     *            start
053     * @param to
054     *            end
055     */
056    public IntegerRange(Number from, Number to) {
057        this(from.intValue(), to.intValue());
058    }
059
060    /**
061     * Create a new IntegerRange.
062     *
063     * @param from
064     *            start
065     * @param to
066     *            end
067     * @param step
068     *            increment
069     */
070    public IntegerRange(Number from, Number to, Number step) {
071        this(from.intValue(), to.intValue(), step.intValue());
072    }
073
074    /**
075     * Create a new IntegerRange.
076     *
077     * @param from
078     *            start
079     * @param to
080     *            end
081     */
082    public IntegerRange(int from, int to) {
083        this(from, to, DEFAULT_STEP.evaluate(from, to).intValue());
084    }
085
086    /**
087     * Create a new IntegerRange.
088     *
089     * @param from
090     *            start
091     * @param to
092     *            end
093     * @param step
094     *            increment
095     */
096    public IntegerRange(int from, int to, int step) {
097        this(from, DEFAULT_LEFT_BOUND_TYPE, to, DEFAULT_RIGHT_BOUND_TYPE, step);
098    }
099
100    /**
101     * Create a new IntegerRange.
102     *
103     * @param from
104     *            start
105     * @param to
106     *            end
107     * @throws NullPointerException
108     *             if either {@link Endpoint} is {@code null}
109     */
110    public IntegerRange(Endpoint<Integer> from, Endpoint<Integer> to) {
111        this(from, to, DEFAULT_STEP.evaluate(from.getValue(), to.getValue()));
112    }
113
114    /**
115     * Create a new IntegerRange.
116     *
117     * @param from
118     *            start
119     * @param leftBoundType
120     *            type of left bound
121     * @param to
122     *            end
123     * @param rightBoundType
124     *            type of right bound
125     * @throws NullPointerException
126     *             if either {@link BoundType} is {@code null}
127     */
128    public IntegerRange(int from, BoundType leftBoundType, int to, BoundType rightBoundType) {
129        this(from, leftBoundType, to, rightBoundType, DEFAULT_STEP.evaluate(from, to));
130    }
131
132    /**
133     * Create a new IntegerRange.
134     *
135     * @param from
136     *            start
137     * @param to
138     *            end
139     * @param step
140     *            increment
141     * @throws NullPointerException
142     *             if either {@link Endpoint} is {@code null}
143     */
144    public IntegerRange(Endpoint<Integer> from, Endpoint<Integer> to, int step) {
145        super(from, to, Integer.valueOf(step), new BinaryFunction<Integer, Integer, Integer>() {
146
147            public Integer evaluate(Integer left, Integer right) {
148                return Integer.valueOf(left.intValue() + right.intValue());
149            }
150        });
151
152        final int f = from.getValue();
153        final int t = to.getValue();
154
155        Validate.isTrue(f == t || Integer.signum(step) == Integer.signum(t - f),
156            "Will never reach '%s' from '%s' using step %s", t, f, step);
157    }
158
159    /**
160     * Create a new IntegerRange.
161     *
162     * @param from
163     *            start
164     * @param leftBoundType
165     *            type of left bound
166     * @param to
167     *            end
168     * @param rightBoundType
169     *            type of right bound
170     * @param step
171     *            increment
172     * @throws NullPointerException
173     *             if either {@link BoundType} is {@code null}
174     */
175    public IntegerRange(int from, BoundType leftBoundType, int to, BoundType rightBoundType, int step) {
176        this(new Endpoint<Integer>(from, leftBoundType), new Endpoint<Integer>(to, rightBoundType), step);
177    }
178
179    /**
180     * {@inheritDoc}
181     */
182    protected Iterator<Integer> createIterator() {
183        return new Iterator<Integer>() {
184            private int currentValue;
185
186            {
187                currentValue = leftEndpoint.getValue();
188
189                if (leftEndpoint.getBoundType() == BoundType.OPEN) {
190                    this.currentValue += step;
191                }
192            }
193
194            public void remove() {
195                throw new UnsupportedOperationException();
196            }
197
198            public Integer next() {
199                final int step = getStep();
200                final int r = currentValue;
201                currentValue += step;
202                return Integer.valueOf(r);
203            }
204
205            public boolean hasNext() {
206                final int cmp = Integer.valueOf(currentValue).compareTo(rightEndpoint.getValue());
207
208                if (cmp == 0) {
209                    return rightEndpoint.getBoundType() == BoundType.CLOSED;
210                }
211                if (step > 0) {
212                    return cmp < 0;
213                }
214                return cmp > 0;
215            }
216        };
217    }
218
219}