| // 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 com.google.devtools.build.lib.syntax; |
| |
| import com.google.common.collect.ImmutableList; |
| import com.google.common.collect.Iterables; |
| import com.google.common.collect.ObjectArrays; |
| import com.google.devtools.build.lib.skylarkinterface.SkylarkModule; |
| import com.google.devtools.build.lib.skylarkinterface.SkylarkModuleCategory; |
| import java.util.AbstractCollection; |
| import java.util.AbstractList; |
| import java.util.Arrays; |
| import java.util.Iterator; |
| |
| /** |
| * A Skylark tuple, i.e. the value represented by {@code (1, 2, 3)}. Tuples are always immutable |
| * (regardless of the {@link StarlarkThread} they are created in). |
| */ |
| @SkylarkModule( |
| name = "tuple", |
| category = SkylarkModuleCategory.BUILTIN, |
| 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<E> extends AbstractList<E> implements Sequence<E> { |
| |
| 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, cast to have an arbitrary content type. */ |
| @SuppressWarnings("unchecked") |
| public static <T> Tuple<T> empty() { |
| return (Tuple<T>) EMPTY; // unchecked |
| } |
| |
| /** |
| * Returns a Tuple that wraps the specified array, which must not be subsequently modified. The |
| * caller is additionally trusted to choose an appropriate type T. |
| */ |
| static <T> Tuple<T> wrap(Object[] array) { |
| return array.length == 0 ? empty() : new Tuple<T>(array); |
| } |
| |
| /** Returns a tuple containing the given elements. */ |
| @SuppressWarnings("unchecked") |
| public static <T> Tuple<T> copyOf(Iterable<? extends T> seq) { |
| if (seq instanceof Tuple) { |
| return (Tuple<T>) seq; // unchecked |
| } |
| return wrap(Iterables.toArray(seq, Object.class)); |
| } |
| |
| /** Returns a tuple containing the given elements. */ |
| public static <T> Tuple<T> of(T... elems) { |
| if (elems.length == 0) { |
| return empty(); |
| } |
| return new Tuple<T>(Arrays.copyOf(elems, elems.length)); |
| } |
| |
| /** Returns a two-element tuple. */ |
| public static <T> Tuple<T> pair(T a, T b) { |
| // Equivalent to of(a, b) but avoids variadic array allocation. |
| return wrap(new Object[] {a, b}); |
| } |
| |
| /** Returns a three-element tuple. */ |
| public static <T> Tuple<T> triple(T a, T b, T 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 <T> Tuple<T> concat(Tuple<? extends T> x, Tuple<? extends T> y) { |
| // TODO(adonovan): opt: exploit x + () == x; y + () == y. |
| return wrap(ObjectArrays.concat(x.elems, y.elems, Object.class)); |
| } |
| |
| @Override |
| public boolean isImmutable() { |
| for (Object x : elems) { |
| if (!EvalUtils.isImmutable(x)) { |
| return false; |
| } |
| } |
| return true; |
| } |
| |
| @Override |
| public boolean isHashable() { |
| for (Object x : elems) { |
| if (!EvalUtils.isHashable(x)) { |
| return false; |
| } |
| } |
| return true; |
| } |
| |
| @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 |
| @SuppressWarnings("unchecked") |
| public E get(int i) { |
| return (E) elems[i]; // unchecked |
| } |
| |
| @Override |
| public int size() { |
| return elems.length; |
| } |
| |
| @Override |
| public Tuple<E> subList(int from, int to) { |
| return wrap(Arrays.copyOfRange(elems, from, to)); |
| } |
| |
| @Override |
| public Object[] toArray() { |
| return Arrays.copyOf(elems, elems.length); |
| } |
| |
| @Override |
| public void repr(Printer printer) { |
| // TODO(adonovan): when Printer moves into this package, |
| // inline and simplify this, the sole call with isTuple=true. |
| printer.printList(this, /*isTuple=*/ true); |
| } |
| |
| // 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<E> 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<E> 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<E> repeat(int n) { |
| if (n <= 0 || isEmpty()) { |
| return empty(); |
| } |
| // TODO(adonovan): reject unreasonably large n. |
| Object[] res = new Object[n * elems.length]; |
| for (int i = 0; i < n; i++) { |
| System.arraycopy(elems, 0, res, i * elems.length, elems.length); |
| } |
| return wrap(res); |
| } |
| } |