/runtime/Java/src/main/java/org/antlr/runtime/tree/BaseTree.java
Java | 397 lines | 276 code | 33 blank | 88 comment | 53 complexity | d129b1a38332ccc550e53c3543b43467 MD5 | raw file
- /*
- [The "BSD license"]
- Copyright (c) 2005-2009 Terence Parr
- All rights reserved.
- Redistribution and use in source and binary forms, with or without
- modification, are permitted provided that the following conditions
- are met:
- 1. Redistributions of source code must retain the above copyright
- notice, this list of conditions and the following disclaimer.
- 2. Redistributions in binary form must reproduce the above copyright
- notice, this list of conditions and the following disclaimer in the
- documentation and/or other materials provided with the distribution.
- 3. The name of the author may not be used to endorse or promote products
- derived from this software without specific prior written permission.
- THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
- IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
- OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
- IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
- INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
- NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
- THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
- package org.antlr.runtime.tree;
- import java.util.ArrayList;
- import java.util.List;
- /** A generic tree implementation with no payload. You must subclass to
- * actually have any user data. ANTLR v3 uses a list of children approach
- * instead of the child-sibling approach in v2. A flat tree (a list) is
- * an empty node whose children represent the list. An empty, but
- * non-null node is called "nil".
- */
- public abstract class BaseTree implements Tree {
- protected List<Object> children;
- public BaseTree() {
- }
- /** Create a new node from an existing node does nothing for BaseTree
- * as there are no fields other than the children list, which cannot
- * be copied as the children are not considered part of this node.
- */
- public BaseTree(Tree node) {
- }
- @Override
- public Tree getChild(int i) {
- if ( children==null || i>=children.size() ) {
- return null;
- }
- return (Tree)children.get(i);
- }
- /** Get the children internal List; note that if you directly mess with
- * the list, do so at your own risk.
- */
- public List<? extends Object> getChildren() {
- return children;
- }
- public Tree getFirstChildWithType(int type) {
- for (int i = 0; children!=null && i < children.size(); i++) {
- Tree t = (Tree) children.get(i);
- if ( t.getType()==type ) {
- return t;
- }
- }
- return null;
- }
- @Override
- public int getChildCount() {
- if ( children==null ) {
- return 0;
- }
- return children.size();
- }
- /** Add t as child of this node.
- *
- * Warning: if t has no children, but child does
- * and child isNil then this routine moves children to t via
- * t.children = child.children; i.e., without copying the array.
- */
- @Override
- public void addChild(Tree t) {
- //System.out.println("add child "+t.toStringTree()+" "+this.toStringTree());
- //System.out.println("existing children: "+children);
- if ( t==null ) {
- return; // do nothing upon addChild(null)
- }
- BaseTree childTree = (BaseTree)t;
- if ( childTree.isNil() ) { // t is an empty node possibly with children
- if ( this.children!=null && this.children == childTree.children ) {
- throw new RuntimeException("attempt to add child list to itself");
- }
- // just add all of childTree's children to this
- if ( childTree.children!=null ) {
- if ( this.children!=null ) { // must copy, this has children already
- int n = childTree.children.size();
- for (int i = 0; i < n; i++) {
- Tree c = (Tree)childTree.children.get(i);
- this.children.add(c);
- // handle double-link stuff for each child of nil root
- c.setParent(this);
- c.setChildIndex(children.size()-1);
- }
- }
- else {
- // no children for this but t has children; just set pointer
- // call general freshener routine
- this.children = childTree.children;
- this.freshenParentAndChildIndexes();
- }
- }
- }
- else { // child is not nil (don't care about children)
- if ( children==null ) {
- children = createChildrenList(); // create children list on demand
- }
- children.add(t);
- childTree.setParent(this);
- childTree.setChildIndex(children.size()-1);
- }
- // System.out.println("now children are: "+children);
- }
- /** Add all elements of kids list as children of this node */
- public void addChildren(List<? extends Tree> kids) {
- for (int i = 0; i < kids.size(); i++) {
- Tree t = kids.get(i);
- addChild(t);
- }
- }
- @Override
- public void setChild(int i, Tree t) {
- if ( t==null ) {
- return;
- }
- if ( t.isNil() ) {
- throw new IllegalArgumentException("Can't set single child to a list");
- }
- if ( children==null ) {
- children = createChildrenList();
- }
- children.set(i, t);
- t.setParent(this);
- t.setChildIndex(i);
- }
- /** Insert child t at child position i (0..n-1) by shifting children
- i+1..n-1 to the right one position. Set parent / indexes properly
- but does NOT collapse nil-rooted t's that come in here like addChild.
- */
- public void insertChild(int i, Object t) {
- if (i < 0 || i >= getChildCount()) {
- throw new IndexOutOfBoundsException(i+" out or range");
- }
- children.add(i, t);
- // walk others to increment their child indexes
- // set index, parent of this one too
- this.freshenParentAndChildIndexes(i);
- }
- @Override
- public Object deleteChild(int i) {
- if ( children==null ) {
- return null;
- }
- Tree killed = (Tree)children.remove(i);
- // walk rest and decrement their child indexes
- this.freshenParentAndChildIndexes(i);
- return killed;
- }
- /** Delete children from start to stop and replace with t even if t is
- * a list (nil-root tree). num of children can increase or decrease.
- * For huge child lists, inserting children can force walking rest of
- * children to set their childindex; could be slow.
- */
- @Override
- public void replaceChildren(int startChildIndex, int stopChildIndex, Object t) {
- /*
- System.out.println("replaceChildren "+startChildIndex+", "+stopChildIndex+
- " with "+((BaseTree)t).toStringTree());
- System.out.println("in="+toStringTree());
- */
- if ( children==null ) {
- throw new IllegalArgumentException("indexes invalid; no children in list");
- }
- int replacingHowMany = stopChildIndex - startChildIndex + 1;
- int replacingWithHowMany;
- BaseTree newTree = (BaseTree)t;
- List<Object> newChildren;
- // normalize to a list of children to add: newChildren
- if ( newTree.isNil() ) {
- newChildren = newTree.children;
- }
- else {
- newChildren = new ArrayList<Object>(1);
- newChildren.add(newTree);
- }
- replacingWithHowMany = newChildren.size();
- int numNewChildren = newChildren.size();
- int delta = replacingHowMany - replacingWithHowMany;
- // if same number of nodes, do direct replace
- if ( delta == 0 ) {
- int j = 0; // index into new children
- for (int i=startChildIndex; i<=stopChildIndex; i++) {
- BaseTree child = (BaseTree)newChildren.get(j);
- children.set(i, child);
- child.setParent(this);
- child.setChildIndex(i);
- j++;
- }
- }
- else if ( delta > 0 ) { // fewer new nodes than there were
- // set children and then delete extra
- for (int j=0; j<numNewChildren; j++) {
- children.set(startChildIndex+j, newChildren.get(j));
- }
- int indexToDelete = startChildIndex+numNewChildren;
- for (int c=indexToDelete; c<=stopChildIndex; c++) {
- // delete same index, shifting everybody down each time
- children.remove(indexToDelete);
- }
- freshenParentAndChildIndexes(startChildIndex);
- }
- else { // more new nodes than were there before
- // fill in as many children as we can (replacingHowMany) w/o moving data
- for (int j=0; j<replacingHowMany; j++) {
- children.set(startChildIndex+j, newChildren.get(j));
- }
- int numToInsert = replacingWithHowMany-replacingHowMany;
- for (int j=replacingHowMany; j<replacingWithHowMany; j++) {
- children.add(startChildIndex+j, newChildren.get(j));
- }
- freshenParentAndChildIndexes(startChildIndex);
- }
- //System.out.println("out="+toStringTree());
- }
- /** Override in a subclass to change the impl of children list */
- protected List<Object> createChildrenList() {
- return new ArrayList<Object>();
- }
- @Override
- public boolean isNil() {
- return false;
- }
- /** Set the parent and child index values for all child of t */
- @Override
- public void freshenParentAndChildIndexes() {
- freshenParentAndChildIndexes(0);
- }
- public void freshenParentAndChildIndexes(int offset) {
- int n = getChildCount();
- for (int c = offset; c < n; c++) {
- Tree child = getChild(c);
- child.setChildIndex(c);
- child.setParent(this);
- }
- }
- public void freshenParentAndChildIndexesDeeply() {
- freshenParentAndChildIndexesDeeply(0);
- }
- public void freshenParentAndChildIndexesDeeply(int offset) {
- int n = getChildCount();
- for (int c = offset; c < n; c++) {
- BaseTree child = (BaseTree)getChild(c);
- child.setChildIndex(c);
- child.setParent(this);
- child.freshenParentAndChildIndexesDeeply();
- }
- }
- public void sanityCheckParentAndChildIndexes() {
- sanityCheckParentAndChildIndexes(null, -1);
- }
- public void sanityCheckParentAndChildIndexes(Tree parent, int i) {
- if ( parent!=this.getParent() ) {
- throw new IllegalStateException("parents don't match; expected "+parent+" found "+this.getParent());
- }
- if ( i!=this.getChildIndex() ) {
- throw new IllegalStateException("child indexes don't match; expected "+i+" found "+this.getChildIndex());
- }
- int n = this.getChildCount();
- for (int c = 0; c < n; c++) {
- CommonTree child = (CommonTree)this.getChild(c);
- child.sanityCheckParentAndChildIndexes(this, c);
- }
- }
- /** BaseTree doesn't track child indexes. */
- @Override
- public int getChildIndex() {
- return 0;
- }
- @Override
- public void setChildIndex(int index) {
- }
- /** BaseTree doesn't track parent pointers. */
- @Override
- public Tree getParent() {
- return null;
- }
- @Override
- public void setParent(Tree t) {
- }
- /** Walk upwards looking for ancestor with this token type. */
- @Override
- public boolean hasAncestor(int ttype) { return getAncestor(ttype)!=null; }
- /** Walk upwards and get first ancestor with this token type. */
- @Override
- public Tree getAncestor(int ttype) {
- Tree t = this;
- t = t.getParent();
- while ( t!=null ) {
- if ( t.getType()==ttype ) return t;
- t = t.getParent();
- }
- return null;
- }
- /** Return a list of all ancestors of this node. The first node of
- * list is the root and the last is the parent of this node.
- */
- @Override
- public List<? extends Tree> getAncestors() {
- if ( getParent()==null ) return null;
- List<Tree> ancestors = new ArrayList<Tree>();
- Tree t = this;
- t = t.getParent();
- while ( t!=null ) {
- ancestors.add(0, t); // insert at start
- t = t.getParent();
- }
- return ancestors;
- }
- /** Print out a whole tree not just a node */
- @Override
- public String toStringTree() {
- if ( children==null || children.isEmpty() ) {
- return this.toString();
- }
- StringBuilder buf = new StringBuilder();
- if ( !isNil() ) {
- buf.append("(");
- buf.append(this.toString());
- buf.append(' ');
- }
- for (int i = 0; children!=null && i < children.size(); i++) {
- Tree t = (Tree)children.get(i);
- if ( i>0 ) {
- buf.append(' ');
- }
- buf.append(t.toStringTree());
- }
- if ( !isNil() ) {
- buf.append(")");
- }
- return buf.toString();
- }
- @Override
- public int getLine() {
- return 0;
- }
- @Override
- public int getCharPositionInLine() {
- return 0;
- }
- /** Override to say how a node (not a tree) should look as text */
- @Override
- public abstract String toString();
- }