blob: e0f8e4cad883419820fb46f570578094b16c5ec6 [file] [log] [blame]
/*
* 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 org.apache.calcite.util;
import org.apache.calcite.linq4j.function.Functions;
import org.apache.calcite.runtime.FlatLists;
import org.apache.calcite.util.mapping.Mappings;
import com.google.common.base.Preconditions;
import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;
import com.google.common.collect.UnmodifiableListIterator;
import java.lang.reflect.Array;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.ListIterator;
import java.util.NoSuchElementException;
/**
* An immutable list of {@link Integer} values backed by an array of
* {@code int}s.
*/
public class ImmutableIntList extends FlatLists.AbstractFlatList<Integer> {
private final int[] ints;
private static final Object[] EMPTY_ARRAY = new Object[0];
private static final ImmutableIntList EMPTY = new EmptyImmutableIntList();
// Does not copy array. Must remain private.
private ImmutableIntList(int... ints) {
this.ints = ints;
}
/**
* Returns an empty ImmutableIntList.
*/
public static ImmutableIntList of() {
return EMPTY;
}
/**
* Creates an ImmutableIntList from an array of {@code int}.
*/
public static ImmutableIntList of(int... ints) {
return new ImmutableIntList(ints.clone());
}
/**
* Creates an ImmutableIntList from an array of {@code Number}.
*/
public static ImmutableIntList copyOf(Number... numbers) {
final int[] ints = new int[numbers.length];
for (int i = 0; i < ints.length; i++) {
ints[i] = numbers[i].intValue();
}
return new ImmutableIntList(ints);
}
/**
* Creates an ImmutableIntList from an iterable of {@link Number}.
*/
public static ImmutableIntList copyOf(Iterable<? extends Number> list) {
if (list instanceof ImmutableIntList) {
return (ImmutableIntList) list;
}
@SuppressWarnings("unchecked")
final Collection<? extends Number> collection =
list instanceof Collection
? (Collection<? extends Number>) list
: Lists.newArrayList(list);
return copyFromCollection(collection);
}
/**
* Creates an ImmutableIntList from an iterator of {@link Number}.
*/
public static ImmutableIntList copyOf(Iterator<? extends Number> list) {
return copyFromCollection(Lists.newArrayList(list));
}
private static ImmutableIntList copyFromCollection(
Collection<? extends Number> list) {
final int[] ints = new int[list.size()];
int i = 0;
for (Number number : list) {
ints[i++] = number.intValue();
}
return new ImmutableIntList(ints);
}
@Override public int hashCode() {
return Arrays.hashCode(ints);
}
@Override public boolean equals(Object obj) {
return this == obj
|| obj instanceof ImmutableIntList
? Arrays.equals(ints, ((ImmutableIntList) obj).ints)
: obj instanceof List
&& obj.equals(this);
}
@Override public String toString() {
return Arrays.toString(ints);
}
@Override public boolean isEmpty() {
return ints.length == 0;
}
public int size() {
return ints.length;
}
public Object[] toArray() {
final Object[] objects = new Object[ints.length];
for (int i = 0; i < objects.length; i++) {
objects[i] = ints[i];
}
return objects;
}
public <T> T[] toArray(T[] a) {
final int size = ints.length;
if (a.length < size) {
// Make a new array of a's runtime type, but my contents:
a = a.getClass() == Object[].class
? (T[]) new Object[size]
: (T[]) Array.newInstance(
a.getClass().getComponentType(), size);
}
if ((Class) a.getClass() == Integer[].class) {
final Integer[] integers = (Integer[]) a;
for (int i = 0; i < size; i++) {
integers[i] = ints[i];
}
} else {
System.arraycopy(toArray(), 0, a, 0, size);
}
if (a.length > size) {
a[size] = null;
}
return a;
}
/** Returns an array of {@code int}s with the same contents as this list. */
public int[] toIntArray() {
return ints.clone();
}
/** Returns an List of {@code Integer}. */
public List<Integer> toIntegerList() {
ArrayList<Integer> arrayList = new ArrayList<>(size());
for (int i : ints) {
arrayList.add(i);
}
return arrayList;
}
public Integer get(int index) {
return ints[index];
}
public int getInt(int index) {
return ints[index];
}
@Override public Iterator<Integer> iterator() {
return listIterator();
}
@Override public ListIterator<Integer> listIterator() {
return listIterator(0);
}
@Override public ListIterator<Integer> listIterator(int index) {
return new AbstractIndexedListIterator<Integer>(size(), index) {
protected Integer get(int index) {
return ImmutableIntList.this.get(index);
}
};
}
public int indexOf(Object o) {
if (o instanceof Integer) {
return indexOf((int) (Integer) o);
}
return -1;
}
public int indexOf(int seek) {
for (int i = 0; i < ints.length; i++) {
if (ints[i] == seek) {
return i;
}
}
return -1;
}
public int lastIndexOf(Object o) {
if (o instanceof Integer) {
return lastIndexOf((int) (Integer) o);
}
return -1;
}
public int lastIndexOf(int seek) {
for (int i = ints.length - 1; i >= 0; --i) {
if (ints[i] == seek) {
return i;
}
}
return -1;
}
@Override public ImmutableIntList append(Integer e) {
return append((int) e);
}
/** Returns a copy of this list with one element added. */
public ImmutableIntList append(int element) {
if (ints.length == 0) {
return of(element);
}
final int[] newInts = Arrays.copyOf(this.ints, ints.length + 1);
newInts[ints.length] = element;
return new ImmutableIntList(newInts);
}
/** Returns a list that contains the values lower to upper - 1.
*
* <p>For example, {@code range(1, 3)} contains [1, 2]. */
public static List<Integer> range(final int lower, final int upper) {
return Functions.generate(upper - lower, index -> lower + index);
}
/** Returns the identity list [0, ..., count - 1].
*
* @see Mappings#isIdentity(List, int)
*/
public static ImmutableIntList identity(int count) {
final int[] integers = new int[count];
for (int i = 0; i < integers.length; i++) {
integers[i] = i;
}
return new ImmutableIntList(integers);
}
/** Returns a copy of this list with all of the given integers added. */
public ImmutableIntList appendAll(Iterable<Integer> list) {
if (list instanceof Collection && ((Collection) list).isEmpty()) {
return this;
}
return ImmutableIntList.copyOf(Iterables.concat(this, list));
}
/**
* Increments {@code offset} to each element of the list and
* returns a new int list.
*/
public ImmutableIntList incr(int offset) {
final int[] integers = new int[ints.length];
for (int i = 0; i < ints.length; i++) {
integers[i] = ints[i] + offset;
}
return new ImmutableIntList(integers);
}
/** Special sub-class of {@link ImmutableIntList} that is always
* empty and has only one instance. */
private static class EmptyImmutableIntList extends ImmutableIntList {
@Override public Object[] toArray() {
return EMPTY_ARRAY;
}
@Override public <T> T[] toArray(T[] a) {
if (a.length > 0) {
a[0] = null;
}
return a;
}
@Override public Iterator<Integer> iterator() {
return Collections.<Integer>emptyList().iterator();
}
@Override public ListIterator<Integer> listIterator() {
return Collections.<Integer>emptyList().listIterator();
}
}
/** Extension to {@link com.google.common.collect.UnmodifiableListIterator}
* that operates by index.
*
* @param <E> element type */
private abstract static class AbstractIndexedListIterator<E>
extends UnmodifiableListIterator<E> {
private final int size;
private int position;
protected abstract E get(int index);
protected AbstractIndexedListIterator(int size, int position) {
Preconditions.checkPositionIndex(position, size);
this.size = size;
this.position = position;
}
public final boolean hasNext() {
return position < size;
}
public final E next() {
if (!hasNext()) {
throw new NoSuchElementException();
}
return get(position++);
}
public final int nextIndex() {
return position;
}
public final boolean hasPrevious() {
return position > 0;
}
public final E previous() {
if (!hasPrevious()) {
throw new NoSuchElementException();
}
return get(--position);
}
public final int previousIndex() {
return position - 1;
}
}
}