| // Copyright 2016 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.Iterators; |
| import com.google.devtools.build.lib.events.Location; |
| import com.google.devtools.build.lib.skylarkinterface.SkylarkValue; |
| import com.google.devtools.build.lib.syntax.Mutability.Freezable; |
| import com.google.devtools.build.lib.syntax.Mutability.MutabilityException; |
| import java.util.Collection; |
| import java.util.Collections; |
| import java.util.Iterator; |
| import java.util.List; |
| import java.util.ListIterator; |
| import java.util.Map; |
| import java.util.Set; |
| import javax.annotation.Nullable; |
| |
| /** |
| * Base class for data structures that are only mutable using a proper, unfrozen {@link Mutability}. |
| */ |
| public abstract class SkylarkMutable implements Freezable, SkylarkValue { |
| |
| /** |
| * Checks whether this object is currently mutable in the given {@link Environment}, and throws |
| * an exception if it is not. |
| * |
| * @deprecated prefer {@link #checkMutable(Location, Mutability)} instead |
| */ |
| @Deprecated |
| protected void checkMutable(Location loc, Environment env) throws EvalException { |
| checkMutable(loc, env.mutability()); |
| } |
| |
| /** |
| * Checks whether this object is currently mutable using the given {@link Mutability}, and throws |
| * an exception if it is not. |
| * |
| * @throws EvalException if the object is not mutable. This may be because the object (i.e., its |
| * {@code Mutability} was frozen, or because it is temporarily locked from mutation (due to |
| * being iterated over by a loop), or because it is associated with a different {@code |
| * Mutability} than the one given. |
| */ |
| protected void checkMutable(Location loc, Mutability mutability) throws EvalException { |
| try { |
| Mutability.checkMutable(this, mutability); |
| } catch (MutabilityException ex) { |
| throw new EvalException(loc, ex); |
| } |
| } |
| |
| @Override |
| public boolean isImmutable() { |
| // By the Mutability invariants, if we're frozen and our Mutability is not SHALLOW_IMMUTABLE, |
| // then all contained values are also deeply frozen. Only tuples use SHALLOW_IMMUTABLE and they |
| // override this method anyway, so we can just do a constant-time check here. |
| return mutability().isFrozen(); |
| } |
| |
| /** |
| * Add a new lock at {@code loc}. No effect if frozen. |
| */ |
| public void lock(Location loc) { |
| mutability().lock(this, loc); |
| } |
| |
| /** |
| * Remove the lock at {@code loc}; such a lock must already exist. No effect if frozen. |
| */ |
| public void unlock(Location loc) { |
| mutability().unlock(this, loc); |
| } |
| |
| /** |
| * Base class for a {@link SkylarkMutable} that implements a Java Collections Framework interface. |
| * All of the interface's accessors should be supported, while its mutating methods must be |
| * disallowed. |
| * |
| * <p>Think of this as similar to {@link Collections#unmodifiableList}, etc., except that it's an |
| * extendable class rather than a method. |
| * |
| * <p>A subclass implements a specific data structure interface, say {@link List}, and refines the |
| * return type of {@link #getContentsUnsafe} to be that interface. The subclass implements all of |
| * the interface's accessors such that they defer to the result of {@code getContentsUnsafe}. |
| * Accessors such as {@link Collection#iterator()} must return unmodifiable views. The subclass |
| * implements final versions of all the interface's mutating methods such that they are marked |
| * {@code @Deprecated} and throw {@link UnsupportedOperationException}. |
| * |
| * <p>A concrete subclass may provide alternative mutating methods that take in a {@link |
| * Mutability} and validate that the mutation is allowed using {@link #checkMutable}. This |
| * validation must occur <em>before</em> the mutation, not after, in order to ensure that a frozen |
| * value cannot be mutated. (I.e., the fact that the check throws {@link EvalException} does not |
| * excuse us from illegally mutating a frozen value, since {@code EvalException} is not a fatal |
| * error.) |
| * |
| * <p>Subclasses need not overwrite the default methods added to some data structures in Java 8. |
| * since these are defined in terms of the non-default methods. |
| */ |
| abstract static class BaseMutableWrapper extends SkylarkMutable { |
| |
| /** |
| * The underlying contents, to which read access is forwarded. This object must not be modified |
| * without first calling {@link #checkMutable}. |
| */ |
| protected abstract Object getContentsUnsafe(); |
| |
| @Override |
| public boolean equals(Object o) { |
| return getContentsUnsafe().equals(o); |
| } |
| |
| @Override |
| public int hashCode() { |
| return getContentsUnsafe().hashCode(); |
| } |
| } |
| |
| /** Base class for a {@link SkylarkMutable} that is also a {@link Collection}. */ |
| abstract static class MutableCollection<E> extends BaseMutableWrapper implements Collection<E> { |
| |
| @Override |
| protected abstract Collection<E> getContentsUnsafe(); |
| |
| // Reading methods of Collection, in alphabetic order. |
| |
| @Override |
| public boolean contains(@Nullable Object object) { |
| return getContentsUnsafe().contains(object); |
| } |
| |
| @Override |
| public boolean containsAll(Collection<?> collection) { |
| return getContentsUnsafe().containsAll(collection); |
| } |
| |
| @Override |
| public boolean isEmpty() { |
| return getContentsUnsafe().isEmpty(); |
| } |
| |
| @Override |
| public Iterator<E> iterator() { |
| return Iterators.unmodifiableIterator(getContentsUnsafe().iterator()); |
| } |
| |
| @Override |
| public int size() { |
| return getContentsUnsafe().size(); |
| } |
| |
| // toArray() and toArray(T[]) return copies, so we don't need an unmodifiable view. |
| @Override |
| public Object[] toArray() { |
| return getContentsUnsafe().toArray(); |
| } |
| |
| @Override |
| public <T> T[] toArray(T[] other) { |
| return getContentsUnsafe().toArray(other); |
| } |
| |
| // (Disallowed) writing methods of Collection, in alphabetic order. |
| |
| @Deprecated |
| @Override |
| public final boolean add(E element) { |
| throw new UnsupportedOperationException(); |
| } |
| |
| @Deprecated |
| @Override |
| public final boolean addAll(Collection<? extends E> collection) { |
| throw new UnsupportedOperationException(); |
| } |
| |
| @Deprecated |
| @Override |
| public final void clear() { |
| throw new UnsupportedOperationException(); |
| } |
| |
| @Deprecated |
| @Override |
| public final boolean remove(Object object) { |
| throw new UnsupportedOperationException(); |
| } |
| |
| @Deprecated |
| @Override |
| public final boolean removeAll(Collection<?> collection) { |
| throw new UnsupportedOperationException(); |
| } |
| |
| @Deprecated |
| @Override |
| public final boolean retainAll(Collection<?> collection) { |
| throw new UnsupportedOperationException(); |
| } |
| } |
| |
| /** Base class for a {@link SkylarkMutable} that is also a {@link List}. */ |
| abstract static class BaseMutableList<E> extends MutableCollection<E> implements List<E> { |
| |
| @Override |
| protected abstract List<E> getContentsUnsafe(); |
| |
| // Reading methods of List, in alphabetic order. |
| |
| @Override |
| public E get(int i) { |
| return getContentsUnsafe().get(i); |
| } |
| |
| @Override |
| public int indexOf(Object element) { |
| return getContentsUnsafe().indexOf(element); |
| } |
| |
| @Override |
| public int lastIndexOf(Object element) { |
| return getContentsUnsafe().lastIndexOf(element); |
| } |
| |
| @Override |
| public ListIterator<E> listIterator() { |
| return Collections.unmodifiableList(getContentsUnsafe()).listIterator(); |
| } |
| |
| @Override |
| public ListIterator<E> listIterator(int index) { |
| return Collections.unmodifiableList(getContentsUnsafe()).listIterator(index); |
| } |
| |
| @Override |
| public List<E> subList(int fromIndex, int toIndex) { |
| return Collections.unmodifiableList(getContentsUnsafe()).subList(fromIndex, toIndex); |
| } |
| |
| // (Disallowed) writing methods of List, in alphabetic order. |
| |
| @Deprecated |
| @Override |
| public final void add(int index, E element) { |
| throw new UnsupportedOperationException(); |
| } |
| |
| @Deprecated |
| @Override |
| public final boolean addAll(int index, Collection<? extends E> elements) { |
| throw new UnsupportedOperationException(); |
| } |
| |
| @Deprecated |
| @Override |
| public final E remove(int index) { |
| throw new UnsupportedOperationException(); |
| } |
| |
| @Deprecated |
| @Override |
| public final E set(int index, E element) { |
| throw new UnsupportedOperationException(); |
| } |
| } |
| |
| /** Base class for a {@link SkylarkMutable} that is also a {@link Map}. */ |
| abstract static class MutableMap<K, V> extends BaseMutableWrapper implements Map<K, V> { |
| |
| @Override |
| protected abstract Map<K, V> getContentsUnsafe(); |
| |
| // Reading methods of Map, in alphabetic order. |
| |
| @Override |
| public boolean containsKey(Object key) { |
| return getContentsUnsafe().containsKey(key); |
| } |
| |
| @Override |
| public boolean containsValue(Object value) { |
| return getContentsUnsafe().containsValue(value); |
| } |
| |
| @Override |
| public Set<Map.Entry<K, V>> entrySet() { |
| return Collections.unmodifiableMap(getContentsUnsafe()).entrySet(); |
| } |
| |
| @Override |
| public V get(Object key) { |
| return getContentsUnsafe().get(key); |
| } |
| |
| @Override |
| public boolean isEmpty() { |
| return getContentsUnsafe().isEmpty(); |
| } |
| |
| @Override |
| public Set<K> keySet() { |
| return Collections.unmodifiableMap(getContentsUnsafe()).keySet(); |
| } |
| |
| @Override |
| public int size() { |
| return getContentsUnsafe().size(); |
| } |
| |
| @Override |
| public Collection<V> values() { |
| return Collections.unmodifiableMap(getContentsUnsafe()).values(); |
| } |
| |
| // (Disallowed) writing methods of Map, in alphabetic order. |
| |
| @Deprecated |
| @Override |
| public final void clear() { |
| throw new UnsupportedOperationException(); |
| } |
| |
| @Deprecated |
| @Override |
| public final V put(K key, V value) { |
| throw new UnsupportedOperationException(); |
| } |
| |
| @Deprecated |
| @Override |
| public final void putAll(Map<? extends K, ? extends V> map) { |
| throw new UnsupportedOperationException(); |
| } |
| |
| @Deprecated |
| @Override |
| public final V remove(Object key) { |
| throw new UnsupportedOperationException(); |
| } |
| } |
| } |