/* * 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.j2objc.ast; import java.util.AbstractList; import java.util.ArrayList; import java.util.List; /** * List type for lists of child nodes. Nodes added or removed from a ChildList * are reparented appropriately. */ class ChildList<T extends TreeNode> extends AbstractList<T> { private final Class<T> childType; private final TreeNode parent; private ArrayListImpl<ChildLink<T>> delegate = new ArrayListImpl<>(); public ChildList(Class<T> childType, TreeNode parent) { this.childType = childType; this.parent = parent; } public static <T extends TreeNode> ChildList<T> create(Class<T> childType, TreeNode parent) { return new ChildList<T>(childType, parent); } @Override public T get(int index) { return delegate.get(index).get(); } @Override public int size() { return delegate.size(); } @Override public T set(int index, T node) { ChildLink<T> link = delegate.get(index); T oldNode = link.get(); link.set(node); return oldNode; } @Override public void add(int index, T node) { ChildLink<T> link = new Link(childType, parent); link.set(node); modifiableDelegate().add(index, link); } @Override public T remove(int index) { ChildLink<T> link = modifiableDelegate().remove(index); T node = link.get(); link.set(null); return node; } @SuppressWarnings("unchecked") public void copyFrom(List<T> other) { for (T elem : other) { add((T) elem.copy()); } } void replaceAll(List<T> other) { clear(); addAll(other); } public void accept(TreeVisitor visitor) { if (!delegate.isEmpty()) { ArrayListImpl<ChildLink<T>> childLinks = delegate; childLinks.incrementCount(); for (ChildLink<?> link : childLinks) { link.accept(visitor); } childLinks.decrementCount(); } } @Override public String toString() { return delegate.toString(); } /** * Returns an ArrayListImpl that is safe to modify. If delegate.count does not equal to zero, * returns a copy of delegate. */ private ArrayListImpl<ChildLink<T>> modifiableDelegate() { if (delegate.getCount() != 0) { delegate = new ArrayListImpl<>(delegate); } return delegate; } private class Link extends ChildLink<T> { public Link(Class<T> childType, TreeNode parent) { super(childType, parent); } @Override public void remove() { super.remove(); modifiableDelegate().remove(this); } } /** * Mutation-safe ArrayList. Increment count before iterating through the list and decrement count * after iteration is complete. If count equals to zero, it's safe to mutate the list. */ private static class ArrayListImpl<T> extends ArrayList<T> { private int count = 0; public ArrayListImpl() { super(); } public ArrayListImpl(ArrayListImpl<T> list) { super(list); } public int getCount() { return count; } public void incrementCount() { ++count; } public void decrementCount() { --count; } } }