/*
 * 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 com.google.common.collect.Lists;

import java.util.AbstractList;
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 List<ChildLink<T>> delegate = Lists.newArrayList();

  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);
    delegate.add(index, link);
  }

  @Override
  public T remove(int index) {
    ChildLink<T> link = delegate.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) {
    // Copy all the children into an array to avoid a
    // ConcurrentModificationException if the visitor removes one of the nodes.
    if (!delegate.isEmpty()) {
      ChildLink<?>[] array = delegate.toArray(new ChildLink<?>[delegate.size()]);
      for (ChildLink<?> link : array) {
        link.accept(visitor);
      }
    }
  }

  @Override
  public String toString() {
    return delegate.toString();
  }

  private class Link extends ChildLink<T> {

    public Link(Class<T> childType, TreeNode parent) {
      super(childType, parent);
    }

    @Override
    public void remove() {
      super.remove();
      delegate.remove(this);
    }
  }
}

@@ -604,41 +604,31 @@
 ort 
-com.google.common.colle
+java.util.Abstra
 ct
-.
 List
-s;%0A
+;
 %0Aimp
@@ -634,39 +634,36 @@
 port java.util.A
-bstract
+rray
 List;%0Aimport jav
@@ -938,20 +938,29 @@
 private 
-List
+ArrayListImpl
 %3CChildLi
@@ -981,17 +981,12 @@
 e = 
-Lists.
 new
+ 
 Arra
@@ -990,16 +990,22 @@
 rrayList
+Impl%3C%3E
 ();%0A%0A  p
@@ -1715,24 +1715,36 @@
 e);%0A    
-d
+modifiableD
 elegate
+()
 .add(ind
@@ -1826,24 +1826,36 @@
  link = 
-d
+modifiableD
 elegate
+()
 .remove(
@@ -2192,248 +2192,124 @@
     
-// Copy all the children into an array to avoid a%0A    // ConcurrentModificationException if the visitor removes one of the nodes.%0A    if (!delegate.isEmpty()) %7B%0A      ChildLink%3C?%3E%5B%5D array = delegate.toArray(new ChildLink%3C?%3E%5Bdelegate.size()%5D
+if (!delegate.isEmpty()) %7B%0A      ArrayListImpl%3CChildLink%3CT%3E%3E childLinks = delegate;%0A      childLinks.incrementCount(
 );%0A 
@@ -2342,13 +2342,18 @@
 k : 
-array
+childLinks
 ) %7B%0A
@@ -2390,16 +2390,51 @@
       %7D%0A
+      childLinks.decrementCount();%0A
     %7D%0A  
@@ -2514,16 +2514,334 @@
 );%0A  %7D%0A%0A
+  /**%0A   * Returns an ArrayListImpl that is safe to modify. If delegate.count does not equal to zero,%0A   * returns a copy of delegate.%0A   */%0A  private ArrayListImpl%3CChildLink%3CT%3E%3E modifiableDelegate() %7B%0A    if (delegate.getCount() != 0) %7B%0A      delegate = new ArrayListImpl%3C%3E(delegate);%0A    %7D%0A    return delegate;%0A  %7D%0A%0A
   privat
@@ -3040,24 +3040,36 @@
 ;%0A      
-d
+modifiableD
 elegate
+()
 .remove(
@@ -3084,11 +3084,603 @@
    %7D%0A  %7D
+%0A%0A  /**%0A   * Mutation-safe ArrayList. Increment count before iterating through the list and decrement count%0A   * after iteration is complete. If count equals to zero, it's safe to mutate the list.%0A   */%0A  private static class ArrayListImpl%3CT%3E extends ArrayList%3CT%3E %7B%0A    private int count = 0;%0A%0A    public ArrayListImpl() %7B%0A      super();%0A    %7D%0A%0A    public ArrayListImpl(ArrayListImpl%3CT%3E list) %7B%0A      super(list);%0A    %7D%0A%0A    public int getCount() %7B%0A      return count;%0A    %7D%0A%0A    public void incrementCount() %7B%0A      ++count;%0A    %7D%0A%0A    public void decrementCount() %7B%0A      --count;%0A    %7D%0A  %7D
 %0A%7D%0A

Use customized ArrayList for ChildList