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