| // Copyright 2014 The Bazel Authors. All rights reserved. |
| // |
| // Licensed 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 net.starlark.java.eval; |
| |
| import com.google.common.collect.ImmutableList; |
| import com.google.common.collect.Iterables; |
| import com.google.common.collect.ObjectArrays; |
| import java.util.AbstractCollection; |
| import java.util.AbstractList; |
| import java.util.Arrays; |
| import java.util.Iterator; |
| import net.starlark.java.annot.StarlarkBuiltin; |
| |
| /** A Tuple is an immutable finite sequence of values. */ |
| @StarlarkBuiltin( |
| name = "tuple", |
| category = "core", |
| doc = |
| "The built-in tuple type. Example tuple expressions:<br>" |
| + "<pre class=language-python>x = (1, 2, 3)</pre>" |
| + "Accessing elements is possible using indexing (starts from <code>0</code>):<br>" |
| + "<pre class=language-python>e = x[1] # e == 2</pre>" |
| + "Lists support the <code>+</code> operator to concatenate two tuples. Example:<br>" |
| + "<pre class=language-python>x = (1, 2) + (3, 4) # x == (1, 2, 3, 4)\n" |
| + "x = (\"a\", \"b\")\n" |
| + "x += (\"c\",) # x == (\"a\", \"b\", \"c\")</pre>" |
| + "Similar to lists, tuples support slice operations:" |
| + "<pre class=language-python>('a', 'b', 'c', 'd')[1:3] # ('b', 'c')\n" |
| + "('a', 'b', 'c', 'd')[::2] # ('a', 'c')\n" |
| + "('a', 'b', 'c', 'd')[3:0:-1] # ('d', 'c', 'b')</pre>" |
| + "Tuples are immutable, therefore <code>x[1] = \"a\"</code> is not supported.") |
| public final class Tuple extends AbstractList<Object> |
| implements Sequence<Object>, Comparable<Tuple> { |
| |
| private final Object[] elems; |
| |
| private Tuple(Object[] elems) { |
| this.elems = elems; |
| } |
| |
| // The shared (sole) empty tuple. |
| private static final Tuple EMPTY = new Tuple(new Object[0]); |
| |
| /** Returns the empty tuple. */ |
| public static Tuple empty() { |
| return EMPTY; |
| } |
| |
| /** Returns a Tuple that wraps the specified array, which must not be subsequently modified. */ |
| static Tuple wrap(Object[] array) { |
| return array.length == 0 ? empty() : new Tuple(array); |
| } |
| |
| /** Returns a tuple containing the given elements. */ |
| public static Tuple copyOf(Iterable<?> seq) { |
| if (seq instanceof Tuple) { |
| return (Tuple) seq; |
| } |
| return wrap(Iterables.toArray(seq, Object.class)); |
| } |
| |
| /** Returns a tuple containing the given elements. */ |
| public static Tuple of(Object... elems) { |
| if (elems.length == 0) { |
| return empty(); |
| } |
| return new Tuple(Arrays.copyOf(elems, elems.length)); |
| } |
| |
| /** Returns a two-element tuple. */ |
| public static Tuple pair(Object a, Object b) { |
| // Equivalent to of(a, b) but avoids variadic array allocation. |
| return wrap(new Object[] {a, b}); |
| } |
| |
| /** Returns a three-element tuple. */ |
| public static Tuple triple(Object a, Object b, Object c) { |
| // Equivalent to of(a, b, c) but avoids variadic array allocation. |
| return wrap(new Object[] {a, b, c}); |
| } |
| |
| /** Returns a tuple that is the concatenation of two tuples. */ |
| public static Tuple concat(Tuple x, Tuple y) { |
| if (x.isEmpty()) { |
| return y; |
| } else if (y.isEmpty()) { |
| return x; |
| } else { |
| return wrap(ObjectArrays.concat(x.elems, y.elems, Object.class)); |
| } |
| } |
| |
| @Override |
| public boolean isImmutable() { |
| for (Object x : elems) { |
| if (!Starlark.isImmutable(x)) { |
| return false; |
| } |
| } |
| return true; |
| } |
| |
| @Override |
| public void checkHashable() throws EvalException { |
| for (Object x : elems) { |
| Starlark.checkHashable(x); |
| } |
| } |
| |
| @Override |
| public int hashCode() { |
| return 9857 + 8167 * Arrays.hashCode(elems); |
| } |
| |
| @Override |
| public boolean equals(Object that) { |
| // This slightly violates the java.util.List equivalence contract |
| // because it considers the class, not just the elements. |
| return this == that |
| || (that instanceof Tuple && Arrays.equals(this.elems, ((Tuple) that).elems)); |
| } |
| |
| @Override |
| public int compareTo(Tuple that) { |
| return Sequence.compare(this, that); |
| } |
| |
| @Override |
| public Object get(int i) { |
| return elems[i]; |
| } |
| |
| @Override |
| public int size() { |
| return elems.length; |
| } |
| |
| @Override |
| public Tuple subList(int from, int to) { |
| return wrap(Arrays.copyOfRange(elems, from, to)); |
| } |
| |
| /** Returns a new array of class Object[] containing the tuple elements. */ |
| @Override |
| public Object[] toArray() { |
| return elems.length != 0 ? Arrays.copyOf(elems, elems.length, Object[].class) : elems; |
| } |
| |
| @SuppressWarnings("unchecked") |
| @Override |
| public <T> T[] toArray(T[] a) { |
| if (a.length < elems.length) { |
| return (T[]) Arrays.copyOf(elems, elems.length, a.getClass()); |
| } else { |
| System.arraycopy(elems, 0, a, 0, elems.length); |
| Arrays.fill(a, elems.length, a.length, null); |
| return a; |
| } |
| } |
| |
| @Override |
| public void repr(Printer printer) { |
| printer.append('('); |
| String sep = ""; |
| for (Object elem : elems) { |
| printer.append(sep); |
| sep = ", "; |
| printer.repr(elem); |
| } |
| if (elems.length == 1) { |
| printer.append(','); |
| } |
| printer.append(')'); |
| } |
| |
| // TODO(adonovan): StarlarkValue has 3 String methods yet still we need this fourth. Why? |
| @Override |
| public String toString() { |
| return Starlark.repr(this); |
| } |
| |
| @Override |
| public ImmutableList<Object> getImmutableList() { |
| // Share the array with this (immutable) Tuple. |
| return wrapImmutable(elems); |
| } |
| |
| /** |
| * Returns a new ImmutableList<T> backed by {@code array}, which must not be subsequently |
| * modified. |
| */ |
| // TODO(adonovan): move this somewhere more appropriate. |
| static <T> ImmutableList<T> wrapImmutable(Object[] array) { |
| // Construct an ImmutableList that shares the array. |
| // ImmutableList relies on the implementation of Collection.toArray |
| // not subsequently modifying the returned array. |
| return ImmutableList.copyOf( |
| new AbstractCollection<T>() { |
| @Override |
| public Object[] toArray() { |
| return array; |
| } |
| |
| @Override |
| public int size() { |
| return array.length; |
| } |
| |
| @Override |
| public Iterator<T> iterator() { |
| throw new UnsupportedOperationException(); |
| } |
| }); |
| } |
| |
| @Override |
| public Tuple getSlice(Mutability mu, int start, int stop, int step) { |
| RangeList indices = new RangeList(start, stop, step); |
| int n = indices.size(); |
| if (step == 1) { // common case |
| return subList(indices.at(0), indices.at(n)); |
| } |
| Object[] res = new Object[n]; |
| for (int i = 0; i < n; ++i) { |
| res[i] = elems[indices.at(i)]; |
| } |
| return wrap(res); |
| } |
| |
| /** Returns a Tuple containing n consecutive repeats of this tuple. */ |
| Tuple repeat(StarlarkInt n) throws EvalException { |
| if (n.signum() <= 0 || isEmpty()) { |
| return empty(); |
| } |
| |
| // TODO(adonovan): reject unreasonably large n. |
| int ni = n.toInt("repeat"); |
| Object[] res = new Object[ni * elems.length]; |
| for (int i = 0; i < ni; i++) { |
| System.arraycopy(elems, 0, res, i * elems.length, elems.length); |
| } |
| return wrap(res); |
| } |
| } |