/* This file was generated with JastAdd2 (http://jastadd.org) version jenkins-JastAdd2-430 */
package AST;

/**
 * @production ASTNode;
 * @ast node
 * 
 */
public class ASTNode<T extends ASTNode> implements Cloneable, Iterable<T> {
  /**
   * @apilevel internal
   */
  @SuppressWarnings({"unchecked", "cast"})
  public ASTNode<T> clone() throws CloneNotSupportedException {
    ASTNode node = (ASTNode) super.clone();
    node.extra_visited = -1;
    node.in$Circle(false);
    node.is$Final(false);
    return node;
  }
  /**
   * @apilevel internal
   */
  @SuppressWarnings({"unchecked", "cast"})
  public ASTNode<T> copy() {
    try {
      ASTNode node = (ASTNode) clone();
      node.parent = null;
      if(children != null) {
        node.children = (ASTNode[]) children.clone();
      }
      return node;
    } catch (CloneNotSupportedException e) {
      throw new Error("Error: clone not supported for " + getClass().getName());
    }
  }
  /**
   * Create a deep copy of the AST subtree at this node.
   * The copy is dangling, i.e. has no parent.
   * @return dangling copy of the subtree at this node
   * @apilevel low-level
   */
  @SuppressWarnings({"unchecked", "cast"})
  public ASTNode<T> fullCopy() {
    ASTNode tree = (ASTNode) copy();
    if (children != null) {
      for (int i = 0; i < children.length; ++i) {
        ASTNode child = (ASTNode) children[i];
        if(child != null) {
          child = child.fullCopy();
          tree.setChild(child, i);
        }
      }
    }
    return tree;
  }
  /**
   * 
   * @ast method
   * @aspect PrettyPrint
   * @declaredat PrettyPrint.jadd:11
   */

  public String dumpTree(int j) {
    StringBuffer s = new StringBuffer();
    for(int i = 0; i < j; i++) {
      s.append("  ");
    }
    s.append(getClass().getName() + extra() + "\n");
    for(int i = 0; i < getNumChild(); i++) {
      s.append(getChild(i).dumpTree(j + 1));
    }
    return s.toString();
  }
  /**
   * 
   * @ast method
   * @aspect Traverse
   * @declaredat Traverse.jadd:2
   */

  public void traverse() {
    for(int i = 0; i < getNumChild(); i++) {
      getChild(i).traverse();
    }
  }
  /**
   * 
   * @ast method
   * 
   */

  public ASTNode() {
    super();
    init$Children();
  }
  /**
   * 
     * Initializes the child array to the correct size.
     * Initializes List and Opt nta children.
     * @apilevel internal
     * @ast method
     
   * @ast method
   * 
   */

  public void init$Children() {
  }
  /**
   * 
     * @apilevel internal
     
   * @ast method
   * 
   */

  
  /**
   * @apilevel internal
   */
  private int childIndex;
  /**
   * 
     * @apilevel low-level
     
   * @ast method
   * 
   */

  public int getIndexOfChild(ASTNode node) {
    if (node == null) {
      return -1;
    }
    if (node.childIndex < numChildren && node == children[node.childIndex]) {
      return node.childIndex;
    }
    for(int i = 0; children != null && i < children.length; i++) {
      if(children[i] == node) {
        node.childIndex = i;
        return i;
      }
    }
    return -1;
  }
  /**
   * 
     * @apilevel internal
     
   * @ast method
   * 
   */

  

  /**
   * @apilevel internal
   */
  public static final boolean generatedWithCircularEnabled = true;
  /**
   * 
     * @apilevel internal
     
   * @ast method
   * 
   */

  
  /**
   * @apilevel internal
   */
  public static final boolean generatedWithCacheCycle = true;
  /**
   * 
     * @apilevel internal
     
   * @ast method
   * 
   */

  
  /**
   * @apilevel internal
   */
  public static final boolean generatedWithComponentCheck = false;
  /**
   * 
     * Parent pointer
     * @apilevel low-level
     
   * @ast method
   * 
   */

  

  /**
   * Parent pointer
   * @apilevel low-level
   */
  protected ASTNode parent;
  /**
   * 
     * Child array
     * @apilevel low-level
     
   * @ast method
   * 
   */

  

  /**
   * Child array
   * @apilevel low-level
   */
  protected ASTNode[] children;
  /**
   * 
     * @apilevel internal
     
   * @ast method
   * 
   */

  

  /**
   * @apilevel internal
   */
  protected static ASTNode$State state = new ASTNode$State();
  /**
   * 
     * @apilevel internal
     
   * @ast method
   * 
   */

  public final ASTNode$State state() {
    return state;
  }
  /**
   * 
     * @apilevel internal
     
   * @ast method
   * 
   */

  

  /**
   * @apilevel internal
   */
  public boolean in$Circle = false;
  /**
   * 
     * @apilevel internal
     
   * @ast method
   * 
   */

  public boolean in$Circle() {
    return in$Circle;
  }
  /**
   * 
     * @apilevel internal
     
   * @ast method
   * 
   */

  public void in$Circle(boolean b) {
    in$Circle = b;
  }
  /**
   * 
     * @apilevel internal
     
   * @ast method
   * 
   */

  

  /**
   * @apilevel internal
   */
  public boolean is$Final = false;
  /**
   * 
     * @apilevel internal
     
   * @ast method
   * 
   */

  public boolean is$Final() { return is$Final; }
  /**
   * 
     * @apilevel internal
     
   * @ast method
   * 
   */

  public void is$Final(boolean b) { is$Final = b; }
  /**
   * 
      * @apilevel low-level
      
   * @ast method
   * 
   */

  @SuppressWarnings("cast") 
  public T getChild(int i) {

    ASTNode node = this.getChildNoTransform(i);
    if(node == null) {
      return null;
    }
    if(node.is$Final()) {
      return (T) node;
    }
    if(!node.mayHaveRewrite()) {
      node.is$Final(this.is$Final());
        return (T) node;
    }
    if(!node.in$Circle()) {
      int rewriteState;
      int num = this.state().boundariesCrossed;
      do {
        this.state().push(ASTNode$State.REWRITE_CHANGE);
        ASTNode oldNode = node;
        oldNode.in$Circle(true);
        node = node.rewriteTo();
        if(node != oldNode) {
          this.setChild(node, i);
        }
        oldNode.in$Circle(false);
        rewriteState = this.state().pop();
      } while(rewriteState == ASTNode$State.REWRITE_CHANGE);
      if(rewriteState == ASTNode$State.REWRITE_NOCHANGE && this.is$Final() && num == this.state().boundariesCrossed) {
        node.is$Final(true);
          this.state().boundariesCrossed = num;
      }
    } else if(this.is$Final() != node.is$Final()) {
      this.state().boundariesCrossed++;
    }
    return (T) node;


  }
  /**
   * 
      * @apilevel low-level
      
   * @ast method
   * 
   */

  public void addChild(T node) {
    setChild(node, getNumChildNoTransform());
  }
  /**
   * 
      * <p><em>This method does not invoke AST transformations.</em></p>
      * @apilevel low-level
      
   * @ast method
   * 
   */

  @SuppressWarnings("cast")
  public final T getChildNoTransform(int i) {
      if (children == null) {
      return null;
    }
    T child = (T)children[i];
    return child;        
  }
  /**
   * 
     * @apilevel low-level
     
   * @ast method
   * 
   */

  
  /**
   * @apilevel low-level
   */
   protected int numChildren;
  /**
   * 
     * @apilevel low-level
     
   * @ast method
   * 
   */

  protected int numChildren() {
    return numChildren;
  }
  /**
   * 
      * @apilevel low-level
      
   * @ast method
   * 
   */

  public int getNumChild() {
    return numChildren();
  }
  /**
   * 
      * <p><em>This method does not invoke AST transformations.</em></p>
      * @apilevel low-level
      
   * @ast method
   * 
   */

  public final int getNumChildNoTransform() {
    return numChildren();
  }
  /**
   * 
      * @apilevel low-level
      
   * @ast method
   * 
   */

  public void setChild(ASTNode node, int i) {
    if(children == null) {
      children = new ASTNode[(i+1>4 || !(this instanceof List))?i+1:4];
    } else if (i >= children.length) {
      ASTNode c[] = new ASTNode[i << 1];
      System.arraycopy(children, 0, c, 0, children.length);
      children = c;
    }
    children[i] = node;
    if(i >= numChildren) {
      numChildren = i+1;
    }
    if(node != null) { 
      node.setParent(this); 
      node.childIndex = i; 
    }
  }
  /**
   * 
      * @apilevel low-level
      
   * @ast method
   * 
   */

  public void insertChild(ASTNode node, int i) {
    if(children == null) {
      children = new ASTNode[(i+1>4 || !(this instanceof List))?i+1:4];
      children[i] = node;
    } else {
      ASTNode c[] = new ASTNode[children.length + 1];
      System.arraycopy(children, 0, c, 0, i);
      c[i] = node;
      if(i < children.length) {
        System.arraycopy(children, i, c, i+1, children.length-i);
        for(int j = i+1; j < c.length; ++j) {
          if(c[j] != null) {
            c[j].childIndex = j;
          }
        }
      }
      children = c;
    }
    numChildren++;
    if(node != null) { 
      node.setParent(this); 
      node.childIndex = i; 
    }
  }
  /**
   * 
      * @apilevel low-level
      
   * @ast method
   * 
   */

  public void removeChild(int i) {
    if(children != null) {
      ASTNode child = (ASTNode)children[i];
      if(child != null) {
        child.parent = null;
        child.childIndex = -1;
      }
      // Adding a check of this instance to make sure its a List, a move of children doesn't make
      // any sense for a node unless its a list. Also, there is a problem if a child of a non-List node is removed
      // and siblings are moved one step to the right, with null at the end.
      if (this instanceof List || this instanceof Opt) {
        System.arraycopy(children, i+1, children, i, children.length-i-1);
        children[children.length-1] = null;
        numChildren--;
        // fix child indices
        for(int j = i; j < numChildren; ++j) {
          if(children[j] != null) {
            child = (ASTNode) children[j];
            child.childIndex = j;
          }
        }
      } else {
        children[i] = null;
      }
    }
  }
  /**
   * 
     * @apilevel low-level
     
   * @ast method
   * 
   */

  public ASTNode getParent() {
    if(parent != null && ((ASTNode)parent).is$Final() != is$Final()) {
      state().boundariesCrossed++;
    }
    ;
    return (ASTNode)parent;
  }
  /**
   * 
     * @apilevel low-level
     
   * @ast method
   * 
   */

  public void setParent(ASTNode node) {
    parent = node;
  }
  /**
   * 
   * @ast method
   * 
   */

  protected boolean duringJavaDemoNames() {
  if(state().duringJavaDemoNames == 0) {
    return false;
  } else {
    state().pop();
    state().push(ASTNode$State.REWRITE_INTERRUPT);
    return true;
  }
}
  /**
   * 
     * @apilevel low-level
     
   * @ast method
   * 
   */

  public java.util.Iterator<T> iterator() {
    return new java.util.Iterator<T>() {
      private int counter = 0;
      public boolean hasNext() {
        return counter < getNumChild();
      }
      @SuppressWarnings("unchecked") public T next() {
        if(hasNext())
          return (T)getChild(counter++);
        else
          return null;
      }
      public void remove() {
        throw new UnsupportedOperationException();
      }
    };
  }
  /**
   * 
     * @apilevel internal
     
   * @ast method
   * 
   */

  public boolean mayHaveRewrite() {
    return false;
  }
  /**
   * 
      * @apilevel low-level
      
   * @ast method
   * 
   */

  public void flushCache() {
    extra_visited = -1;
  }
  /**
   * 
      * @apilevel internal
      
   * @ast method
   * 
   */

  public void flushCollectionCache() {
  }
  /**
   * @apilevel internal
   */
  protected int extra_visited = -1;
    /**
     * @attribute syn
     * @aspect PrettyPrint
     * @declaredat PrettyPrint.jadd:23
     */
  public String extra() {
    ASTNode$State state = state();
    if (extra_visited == state().boundariesCrossed) {
      throw new RuntimeException("Circular definition of attr: extra in class: ast.AST.SynDecl");
    }
          
    extra_visited = state().boundariesCrossed;
      
    try {  return "";  }
    finally {
      extra_visited = -1;
    }
  }
  /**
   * @apilevel internal
   */
  public ASTNode rewriteTo() {    if(state().peek() == ASTNode$State.REWRITE_CHANGE) {
      state().pop();
      state().push(ASTNode$State.REWRITE_NOCHANGE);
    }
    return this;
  }  /**
   * @apilevel internal
   */
  public ClassDecl Define_ClassDecl_unknownType(ASTNode caller, ASTNode child) {
    return getParent().Define_ClassDecl_unknownType(this, caller);
  }
  /**
   * @apilevel internal
   */
  public Variable Define_Variable_lookupVariable(ASTNode caller, ASTNode child, String name) {
    return getParent().Define_Variable_lookupVariable(this, caller, name);
  }
  /**
   * @apilevel internal
   */
  public ClassDecl Define_ClassDecl_lookupCanonical(ASTNode caller, ASTNode child, String pack, String type) {
    return getParent().Define_ClassDecl_lookupCanonical(this, caller, pack, type);
  }
  /**
   * @apilevel internal
   */
  public ClassDecl Define_ClassDecl_lookupType(ASTNode caller, ASTNode child, String name) {
    return getParent().Define_ClassDecl_lookupType(this, caller, name);
  }
  /**
   * @apilevel internal
   */
  public Kind Define_Kind_kind(ASTNode caller, ASTNode child) {
    return getParent().Define_Kind_kind(this, caller);
  }
  /**
   * @apilevel internal
   */
  public boolean Define_boolean_hasPackage(ASTNode caller, ASTNode child, String name) {
    return getParent().Define_boolean_hasPackage(this, caller, name);
  }
}
