All Downloads are FREE. Search and download functionalities are using the official Maven repository.

groovy.lang.IntRange Maven / Gradle / Ivy

There is a newer version: 5.0.0-alpha-8
Show newest version
/*
 *  Licensed to the Apache Software Foundation (ASF) under one
 *  or more contributor license agreements.  See the NOTICE file
 *  distributed with this work for additional information
 *  regarding copyright ownership.  The ASF licenses this file
 *  to you under the Apache License, Version 2.0 (the
 *  "License"); you may not use this file except in compliance
 *  with the License.  You may obtain a copy of the License at
 *
 *    http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing,
 *  software distributed under the License is distributed on an
 *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 *  KIND, either express or implied.  See the License for the
 *  specific language governing permissions and limitations
 *  under the License.
 */
package groovy.lang;

import org.codehaus.groovy.runtime.IteratorClosureAdapter;
import org.codehaus.groovy.runtime.RangeInfo;

import java.io.Serializable;
import java.math.BigInteger;
import java.util.AbstractList;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.NoSuchElementException;
import java.util.Objects;

/**
 * Represents a list of Integer objects starting at a specified {@code from} value up (or down)
 * to and potentially including a given {@code to} value.
 * 

* Instances of this class may be either inclusive aware or non-inclusive aware. See the * relevant constructors for creating each type. Inclusive aware IntRange instances are * suitable for use with Groovy's range indexing - in particular if the from or to values * might be negative. This normally happens underneath the covers but is worth keeping * in mind if creating these ranges yourself explicitly. *

* Note: the design of this class might seem a little strange at first. It contains a Boolean * field, {@code inclusive}, which can be {@code true}, {@code false} or {@code null}. This * design is for backwards compatibility reasons. Groovy uses this class under the covers * to represent range indexing, e.g. {@code someList[x..y]} and {@code someString[x.. * Note: This class is a copy of {@link ObjectRange} optimized for int. If you make any * changes to this class, you might consider making parallel changes to {@link ObjectRange}. */ public class IntRange extends AbstractList implements Range, Serializable { private static final long serialVersionUID = -7827097587793510780L; /** * Iterates through each number in an IntRange. */ private class IntRangeIterator implements Iterator { /** * Counts from 0 up to size - 1. */ private int index; /** * The number of values in the range. */ private int size = size(); /** * The next value to return. */ private int value = isReverse() ? getTo() : getFrom(); @Override public boolean hasNext() { return index < size; } @Override public Integer next() { if (!hasNext()) { throw new NoSuchElementException(); } if (index++ > 0) { if (isReverse()) { --value; } else { ++value; } } return value; } /** * Not supported. * * @throws java.lang.UnsupportedOperationException always */ @Override public void remove() { IntRange.this.remove(index); } } /** * For non-inclusive aware ranges, the first number in the range; from is always less than or equal to to. * For inclusive aware ranges, the from argument supplied to the constructor. */ private final int from; /** * For non-inclusive aware ranges, the last number in the range; to is always greater than or equal to from. * For inclusive aware ranges, the from argument supplied to the constructor. */ private final int to; /** * If false, counts up from from to to. Otherwise, counts down * from to to from. Not used for inclusive-aware ranges (inclusive = true|false). */ private final boolean reverse; /** * If true or null, to is included in the range. * If false, the range stops before the to value. *

* Null for non-inclusive-aware ranges (which are inclusive). *

* If true or false, the reverse flag is discarded. */ private final Boolean inclusive; /** * Creates a new non-inclusive aware IntRange. If from is greater than * to, a reverse range is created with from and to swapped. * * @param from the first number in the range. * @param to the last number in the range. * @throws IllegalArgumentException if the range would contain more than {@link Integer#MAX_VALUE} values. */ public IntRange(int from, int to) { this.inclusive = null; if (from > to) { this.from = to; this.to = from; this.reverse = true; } else { this.from = from; this.to = to; this.reverse = false; } checkSize(); } /** * Creates a new non-inclusive aware IntRange. * * @param from the first value in the range. * @param to the last value in the range. * @param reverse true if the range should count from * to to from. * @throws IllegalArgumentException if from is greater than to. */ protected IntRange(int from, int to, boolean reverse) { this.inclusive = null; if (from > to) { throw new IllegalArgumentException("'from' must be less than or equal to 'to'"); } this.from = from; this.to = to; this.reverse = reverse; checkSize(); } /** * Creates a new inclusive aware IntRange. * * @param from the first value in the range. * @param to the last value in the range. * @param inclusive true if the to value is included in the range. */ public IntRange(boolean inclusive, int from, int to) { this.from = from; this.to = to; this.inclusive = inclusive; this.reverse = false; // range may still be reversed, this value is ignored for inclusive-aware ranges checkSize(); } /** * Creates a new NumberRange with the same from and to as this * IntRange but with a step size of stepSize. * * @param stepSize the desired step size * @return a new NumberRange * @since 2.5.0 */ public NumberRange by(T stepSize) { return new NumberRange(NumberRange.comparableNumber((Number)from), NumberRange.comparableNumber((Number)to), stepSize, inclusive); } private void checkSize() { // size() in the Collection interface returns an integer, so ranges can have no more than Integer.MAX_VALUE elements final long size = (long) to - from + 1; if (size > Integer.MAX_VALUE) { throw new IllegalArgumentException("A range must have no more than " + Integer.MAX_VALUE + " elements but attempted " + size + " elements"); } } /** * A method for determining from and to information when using this IntRange to index an aggregate object of the specified size. * Normally only used internally within Groovy but useful if adding range indexing support for your own aggregates. * * @param size the size of the aggregate being indexed * @return the calculated range information (with 1 added to the to value, ready for providing to subList */ public RangeInfo subListBorders(int size) { if (inclusive == null) { throw new IllegalStateException("Should not call subListBorders on a non-inclusive aware IntRange"); } return subListBorders(from, to, inclusive, size); } static RangeInfo subListBorders(int from, int to, boolean inclusive, int size) { int tempFrom = from; if (tempFrom < 0) { tempFrom += size; } int tempTo = to; if (tempTo < 0) { tempTo += size; } if (tempFrom > tempTo) { return new RangeInfo(inclusive ? tempTo : tempTo + 1, tempFrom + 1, true); } return new RangeInfo(tempFrom, inclusive ? tempTo + 1 : tempTo, false); } /** * Determines if this object is equal to another object. Delegates to * {@link AbstractList#equals(Object)} if that is anything * other than an {@link IntRange}. *

* It is not necessary to override hashCode, as * {@link AbstractList#hashCode()} provides a suitable hash code.

*

* Note that equals is generally handled by {@link org.codehaus.groovy.runtime.DefaultGroovyMethods#equals(List, List)} * instead of this method. * * @param that the object to compare * @return true if the objects are equal */ public boolean equals(Object that) { return that instanceof IntRange ? equals((IntRange) that) : super.equals(that); } /** * Compares an {@link IntRange} to another {@link IntRange}. * * @param that the object to compare for equality * @return true if the ranges are equal */ public boolean equals(IntRange that) { return that != null && ((inclusive == null && reverse == that.reverse && from == that.from && to == that.to) || (inclusive != null && Objects.equals(inclusive, that.inclusive) && from == that.from && to == that.to)); } @Override public Integer getFrom() { if (inclusive == null || from <= to) { return from; } return inclusive ? to : to + 1; } @Override public Integer getTo() { if (inclusive == null) { return to; } if (from <= to) { return inclusive ? to : to - 1; } return from; } /** * Returns the inclusive flag. Null for non-inclusive aware ranges or non-null for inclusive aware ranges. */ public Boolean getInclusive() { return inclusive; } /** * Gets the 'from' value as a primitive integer. * * @return the 'from' value as a primitive integer. */ public int getFromInt() { return getFrom(); } /** * Gets the 'to' value as a primitive integer. * * @return the 'to' value as a primitive integer. */ public int getToInt() { return getTo(); } @Override public boolean isReverse() { return inclusive == null ? reverse : (from > to); } @Override public boolean containsWithinBounds(Object o) { return contains(o); } @Override public Integer get(int index) { if (index < 0) { throw new IndexOutOfBoundsException("Index: " + index + " should not be negative"); } if (index >= size()) { throw new IndexOutOfBoundsException("Index: " + index + " too big for range: " + this); } return isReverse() ? getTo() - index : index + getFrom(); } @Override public int size() { return getTo() - getFrom() + 1; } @Override public Iterator iterator() { return new IntRangeIterator(); } @Override public List subList(int fromIndex, int toIndex) { if (fromIndex < 0) { throw new IndexOutOfBoundsException("fromIndex = " + fromIndex); } if (toIndex > size()) { throw new IndexOutOfBoundsException("toIndex = " + toIndex); } if (fromIndex > toIndex) { throw new IllegalArgumentException("fromIndex(" + fromIndex + ") > toIndex(" + toIndex + ")"); } if (fromIndex == toIndex) { return new EmptyRange(getFrom()); } return new IntRange(fromIndex + getFrom(), toIndex + getFrom() - 1, isReverse()); } public String toString() { return inclusive != null ? ("" + from + ".." + (inclusive ? "" : "<") + to) : (reverse ? "" + to + ".." + from : "" + from + ".." + to); } @Override public String inspect() { return toString(); } @Override public boolean contains(Object value) { if (value instanceof Integer) { return (Integer) value >= getFrom() && (Integer) value <= getTo(); } if (value instanceof BigInteger) { final BigInteger bigint = (BigInteger) value; return bigint.compareTo(BigInteger.valueOf(getFrom())) >= 0 && bigint.compareTo(BigInteger.valueOf(getTo())) <= 0; } return false; } @Override public boolean containsAll(Collection other) { if (other instanceof IntRange) { final IntRange range = (IntRange) other; return getFrom() <= range.getFrom() && range.getTo() <= getTo(); } return super.containsAll(other); } @Override public void step(int step, Closure closure) { if (step == 0) { if (!getFrom().equals(getTo())) { throw new GroovyRuntimeException("Infinite loop detected due to step size of 0"); } return; // from == to and step == 0, nothing to do, so return } if (isReverse()) { step = -step; } if (step > 0) { int value = getFrom(); while (value <= getTo()) { closure.call(value); if (((long) value + step) >= Integer.MAX_VALUE) { break; } value = value + step; } } else { int value = getTo(); while (value >= getFrom()) { closure.call(value); if (((long) value + step) <= Integer.MIN_VALUE) { break; } value = value + step; } } } @Override public List step(int step) { final IteratorClosureAdapter adapter = new IteratorClosureAdapter(this); step(step, adapter); return adapter.asList(); } @Override public int hashCode(){ int hashCode; final int from = this.getFrom(); final int to = this.getTo(); hashCode = ((from+to+1)*(from+to))/2+to; return hashCode; } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy