blob: d743ca0df2eef78c7d3ed6a9337c68a9b625cded [file] [log] [blame]
// 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.devtools.build.lib.skylarkinterface.SkylarkModule;
import com.google.devtools.build.lib.skylarkinterface.SkylarkModuleCategory;
import java.util.Collections;
import java.util.List;
import java.util.RandomAccess;
import javax.annotation.Nullable;
/**
* A Sequence is a finite iterable sequence of Starlark values, such as a list or tuple.
*
* <p>Sequences implement the read-only operations of the {@link List} interface, but not its update
* operations, similar to {@code ImmutableList}. The specification of {@code List} governs how such
* methods behave and in particular how they report errors. Subclasses of sequence may define ad-hoc
* mutator methods, such as {@link StarlarkList#extend}, exposed to Starlark, or Java, or both.
*
* <p>In principle, subclasses of Sequence could also define the standard update operations of List,
* but there appears to be little demand, and doing so carries some risk of obscuring unintended
* mutations to Starlark values that would currently cause the program to crash.
*/
@SkylarkModule(
name = "sequence",
documented = false,
category = SkylarkModuleCategory.BUILTIN,
doc = "common type of lists and tuples.")
public interface Sequence<E>
extends StarlarkValue, List<E>, RandomAccess, SkylarkIndexable, StarlarkIterable<E> {
@Override
default boolean truth() {
return !isEmpty();
}
/** Returns an ImmutableList object with the current underlying contents of this Sequence. */
default ImmutableList<E> getImmutableList() {
return ImmutableList.copyOf(this);
}
/** Retrieves an entry from a Sequence. */
@Override
default E getIndex(StarlarkSemantics semantics, Object key) throws EvalException {
int index = Starlark.toInt(key, "sequence index");
return get(EvalUtils.getSequenceIndex(index, size()));
}
@Override
default boolean containsKey(StarlarkSemantics semantics, Object key) throws EvalException {
return contains(key);
}
/**
* Returns the slice of this sequence, {@code this[start:stop:step]}. <br>
* For positive strides ({@code step > 0}), {@code 0 <= start <= stop <= size()}. <br>
* For negative strides ({@code step < 0}), {@code -1 <= stop <= start < size()}. <br>
* The caller must ensure that the start and stop indices are valid and that step is non-zero.
*/
Sequence<E> getSlice(Mutability mu, int start, int stop, int step);
/**
* Casts a {@code List<?>} to an unmodifiable {@code List<T>}, after checking that its contents
* all have type {@code type}.
*
* <p>The returned list may or may not be a view that is affected by updates to the original list.
*
* @param list the original list to cast
* @param type the expected type of all the list's elements
* @param description a description of the argument being converted, or null, for debugging
*/
// We could have used bounded generics to ensure that only downcasts are possible (i.e. cast
// List<S> to List<T extends S>), but this would be less convenient for some callers, and it would
// disallow casting an empty list to any type.
// TODO(adonovan): this method doesn't belong here.
@SuppressWarnings("unchecked")
public static <T> List<T> castList(List<?> list, Class<T> type, @Nullable String description)
throws EvalException {
Object desc = description == null ? null : Printer.formattable("'%s' element", description);
for (Object value : list) {
SkylarkType.checkType(value, type, desc);
}
return Collections.unmodifiableList((List<T>) list);
}
/**
* If {@code obj} is a {@code Sequence}, casts it to an unmodifiable {@code List<T>} after
* checking that each element has type {@code type}. If {@code obj} is {@code None} or null,
* treats it as an empty list. For all other values, throws an {@link EvalException}.
*
* <p>The returned list may or may not be a view that is affected by updates to the original list.
*
* @param obj the object to cast. null and None are treated as an empty list.
* @param type the expected type of all the list's elements
* @param description a description of the argument being converted, or null, for debugging
*/
// TODO(adonovan): this method doesn't belong here.
public static <T> List<T> castSkylarkListOrNoneToList(
Object obj, Class<T> type, @Nullable String description) throws EvalException {
if (EvalUtils.isNullOrNone(obj)) {
return ImmutableList.of();
}
if (obj instanceof Sequence) {
return ((Sequence<?>) obj).getContents(type, description);
}
throw Starlark.errorf(
"Illegal argument: %s is not of expected type list or NoneType",
description == null ? Starlark.repr(obj) : String.format("'%s'", description));
}
/**
* Casts this list as an unmodifiable {@code List<T>}, after checking that each element has type
* {@code type}.
*
* @param type the expected type of all the list's elements
* @param description a description of the argument being converted, or null, for debugging
*/
// TODO(adonovan): this method doesn't belong here.
default <T> List<T> getContents(Class<T> type, @Nullable String description)
throws EvalException {
return castList(this, type, description);
}
}