• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  [The "BSD license"]
3  Copyright (c) 2005-2009 Terence Parr
4  All rights reserved.
5 
6  Redistribution and use in source and binary forms, with or without
7  modification, are permitted provided that the following conditions
8  are met:
9  1. Redistributions of source code must retain the above copyright
10      notice, this list of conditions and the following disclaimer.
11  2. Redistributions in binary form must reproduce the above copyright
12      notice, this list of conditions and the following disclaimer in the
13      documentation and/or other materials provided with the distribution.
14  3. The name of the author may not be used to endorse or promote products
15      derived from this software without specific prior written permission.
16 
17  THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18  IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19  OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21  INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22  NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23  DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24  THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25  (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26  THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27  */
28 package org.antlr.runtime.tree;
29 
30 import java.util.ArrayList;
31 import java.util.List;
32 
33 /** A generic tree implementation with no payload.  You must subclass to
34  *  actually have any user data.  ANTLR v3 uses a list of children approach
35  *  instead of the child-sibling approach in v2.  A flat tree (a list) is
36  *  an empty node whose children represent the list.  An empty, but
37  *  non-null node is called "nil".
38  */
39 public abstract class BaseTree implements Tree {
40 	protected List children;
41 
BaseTree()42 	public BaseTree() {
43 	}
44 
45 	/** Create a new node from an existing node does nothing for BaseTree
46 	 *  as there are no fields other than the children list, which cannot
47 	 *  be copied as the children are not considered part of this node.
48 	 */
BaseTree(Tree node)49 	public BaseTree(Tree node) {
50 	}
51 
getChild(int i)52 	public Tree getChild(int i) {
53 		if ( children==null || i>=children.size() ) {
54 			return null;
55 		}
56 		return (Tree)children.get(i);
57 	}
58 
59 	/** Get the children internal List; note that if you directly mess with
60 	 *  the list, do so at your own risk.
61 	 */
getChildren()62 	public List getChildren() {
63 		return children;
64 	}
65 
getFirstChildWithType(int type)66 	public Tree getFirstChildWithType(int type) {
67 		for (int i = 0; children!=null && i < children.size(); i++) {
68 			Tree t = (Tree) children.get(i);
69 			if ( t.getType()==type ) {
70 				return t;
71 			}
72 		}
73 		return null;
74 	}
75 
getChildCount()76 	public int getChildCount() {
77 		if ( children==null ) {
78 			return 0;
79 		}
80 		return children.size();
81 	}
82 
83 	/** Add t as child of this node.
84 	 *
85 	 *  Warning: if t has no children, but child does
86 	 *  and child isNil then this routine moves children to t via
87 	 *  t.children = child.children; i.e., without copying the array.
88 	 */
addChild(Tree t)89 	public void addChild(Tree t) {
90 		//System.out.println("add child "+t.toStringTree()+" "+this.toStringTree());
91 		//System.out.println("existing children: "+children);
92 		if ( t==null ) {
93 			return; // do nothing upon addChild(null)
94 		}
95 		BaseTree childTree = (BaseTree)t;
96 		if ( childTree.isNil() ) { // t is an empty node possibly with children
97 			if ( this.children!=null && this.children == childTree.children ) {
98 				throw new RuntimeException("attempt to add child list to itself");
99 			}
100 			// just add all of childTree's children to this
101 			if ( childTree.children!=null ) {
102 				if ( this.children!=null ) { // must copy, this has children already
103 					int n = childTree.children.size();
104 					for (int i = 0; i < n; i++) {
105 						Tree c = (Tree)childTree.children.get(i);
106 						this.children.add(c);
107 						// handle double-link stuff for each child of nil root
108 						c.setParent(this);
109 						c.setChildIndex(children.size()-1);
110 					}
111 				}
112 				else {
113 					// no children for this but t has children; just set pointer
114 					// call general freshener routine
115 					this.children = childTree.children;
116 					this.freshenParentAndChildIndexes();
117 				}
118 			}
119 		}
120 		else { // child is not nil (don't care about children)
121 			if ( children==null ) {
122 				children = createChildrenList(); // create children list on demand
123 			}
124 			children.add(t);
125 			childTree.setParent(this);
126 			childTree.setChildIndex(children.size()-1);
127 		}
128 		// System.out.println("now children are: "+children);
129 	}
130 
131 	/** Add all elements of kids list as children of this node */
addChildren(List kids)132 	public void addChildren(List kids) {
133 		for (int i = 0; i < kids.size(); i++) {
134 			Tree t = (Tree) kids.get(i);
135 			addChild(t);
136 		}
137 	}
138 
setChild(int i, Tree t)139 	public void setChild(int i, Tree t) {
140 		if ( t==null ) {
141 			return;
142 		}
143 		if ( t.isNil() ) {
144 			throw new IllegalArgumentException("Can't set single child to a list");
145 		}
146 		if ( children==null ) {
147 			children = createChildrenList();
148 		}
149 		children.set(i, t);
150 		t.setParent(this);
151 		t.setChildIndex(i);
152 	}
153 
154 	/** Insert child t at child position i (0..n-1) by shifting children
155 		i+1..n-1 to the right one position. Set parent / indexes properly
156 	 	but does NOT collapse nil-rooted t's that come in here like addChild.
157 	 */
insertChild(int i, Object t)158 	public void insertChild(int i, Object t) {
159 		if ( children==null ) return;
160 		children.add(i, t);
161 		// walk others to increment their child indexes
162 		// set index, parent of this one too
163 		this.freshenParentAndChildIndexes(i);
164 	}
165 
deleteChild(int i)166 	public Object deleteChild(int i) {
167 		if ( children==null ) {
168 			return null;
169 		}
170 		Tree killed = (Tree)children.remove(i);
171 		// walk rest and decrement their child indexes
172 		this.freshenParentAndChildIndexes(i);
173 		return killed;
174 	}
175 
176 	/** Delete children from start to stop and replace with t even if t is
177 	 *  a list (nil-root tree).  num of children can increase or decrease.
178 	 *  For huge child lists, inserting children can force walking rest of
179 	 *  children to set their childindex; could be slow.
180 	 */
replaceChildren(int startChildIndex, int stopChildIndex, Object t)181 	public void replaceChildren(int startChildIndex, int stopChildIndex, Object t) {
182 		/*
183 		System.out.println("replaceChildren "+startChildIndex+", "+stopChildIndex+
184 						   " with "+((BaseTree)t).toStringTree());
185 		System.out.println("in="+toStringTree());
186 		*/
187 		if ( children==null ) {
188 			throw new IllegalArgumentException("indexes invalid; no children in list");
189 		}
190 		int replacingHowMany = stopChildIndex - startChildIndex + 1;
191 		int replacingWithHowMany;
192 		BaseTree newTree = (BaseTree)t;
193 		List newChildren = null;
194 		// normalize to a list of children to add: newChildren
195 		if ( newTree.isNil() ) {
196 			newChildren = newTree.children;
197 		}
198 		else {
199 			newChildren = new ArrayList(1);
200 			newChildren.add(newTree);
201 		}
202 		replacingWithHowMany = newChildren.size();
203 		int numNewChildren = newChildren.size();
204 		int delta = replacingHowMany - replacingWithHowMany;
205 		// if same number of nodes, do direct replace
206 		if ( delta == 0 ) {
207 			int j = 0; // index into new children
208 			for (int i=startChildIndex; i<=stopChildIndex; i++) {
209 				BaseTree child = (BaseTree)newChildren.get(j);
210 				children.set(i, child);
211 				child.setParent(this);
212 				child.setChildIndex(i);
213                 j++;
214             }
215 		}
216 		else if ( delta > 0 ) { // fewer new nodes than there were
217 			// set children and then delete extra
218 			for (int j=0; j<numNewChildren; j++) {
219 				children.set(startChildIndex+j, newChildren.get(j));
220 			}
221 			int indexToDelete = startChildIndex+numNewChildren;
222 			for (int c=indexToDelete; c<=stopChildIndex; c++) {
223 				// delete same index, shifting everybody down each time
224 				children.remove(indexToDelete);
225 			}
226 			freshenParentAndChildIndexes(startChildIndex);
227 		}
228 		else { // more new nodes than were there before
229 			// fill in as many children as we can (replacingHowMany) w/o moving data
230 			for (int j=0; j<replacingHowMany; j++) {
231 				children.set(startChildIndex+j, newChildren.get(j));
232 			}
233 			int numToInsert = replacingWithHowMany-replacingHowMany;
234 			for (int j=replacingHowMany; j<replacingWithHowMany; j++) {
235 				children.add(startChildIndex+j, newChildren.get(j));
236 			}
237 			freshenParentAndChildIndexes(startChildIndex);
238 		}
239 		//System.out.println("out="+toStringTree());
240 	}
241 
242 	/** Override in a subclass to change the impl of children list */
createChildrenList()243 	protected List createChildrenList() {
244 		return new ArrayList();
245 	}
246 
isNil()247 	public boolean isNil() {
248 		return false;
249 	}
250 
251 	/** Set the parent and child index values for all child of t */
freshenParentAndChildIndexes()252 	public void freshenParentAndChildIndexes() {
253 		freshenParentAndChildIndexes(0);
254 	}
255 
freshenParentAndChildIndexes(int offset)256 	public void freshenParentAndChildIndexes(int offset) {
257 		int n = getChildCount();
258 		for (int c = offset; c < n; c++) {
259 			Tree child = (Tree)getChild(c);
260 			child.setChildIndex(c);
261 			child.setParent(this);
262 		}
263 	}
264 
freshenParentAndChildIndexesDeeply()265 	public void freshenParentAndChildIndexesDeeply() {
266 		freshenParentAndChildIndexesDeeply(0);
267 	}
268 
freshenParentAndChildIndexesDeeply(int offset)269 	public void freshenParentAndChildIndexesDeeply(int offset) {
270 		int n = getChildCount();
271 		for (int c = offset; c < n; c++) {
272 			BaseTree child = (BaseTree)getChild(c);
273 			child.setChildIndex(c);
274 			child.setParent(this);
275 			child.freshenParentAndChildIndexesDeeply();
276 		}
277 	}
278 
sanityCheckParentAndChildIndexes()279 	public void sanityCheckParentAndChildIndexes() {
280 		sanityCheckParentAndChildIndexes(null, -1);
281 	}
282 
sanityCheckParentAndChildIndexes(Tree parent, int i)283 	public void sanityCheckParentAndChildIndexes(Tree parent, int i) {
284 		if ( parent!=this.getParent() ) {
285 			throw new IllegalStateException("parents don't match; expected "+parent+" found "+this.getParent());
286 		}
287 		if ( i!=this.getChildIndex() ) {
288 			throw new IllegalStateException("child indexes don't match; expected "+i+" found "+this.getChildIndex());
289 		}
290 		int n = this.getChildCount();
291 		for (int c = 0; c < n; c++) {
292 			CommonTree child = (CommonTree)this.getChild(c);
293 			child.sanityCheckParentAndChildIndexes(this, c);
294 		}
295 	}
296 
297 	/** BaseTree doesn't track child indexes. */
getChildIndex()298 	public int getChildIndex() {
299 		return 0;
300 	}
setChildIndex(int index)301 	public void setChildIndex(int index) {
302 	}
303 
304 	/** BaseTree doesn't track parent pointers. */
getParent()305 	public Tree getParent() {
306 		return null;
307 	}
308 
setParent(Tree t)309     public void setParent(Tree t) {
310 	}
311 
312     /** Walk upwards looking for ancestor with this token type. */
hasAncestor(int ttype)313     public boolean hasAncestor(int ttype) { return getAncestor(ttype)!=null; }
314 
315     /** Walk upwards and get first ancestor with this token type. */
getAncestor(int ttype)316     public Tree getAncestor(int ttype) {
317         Tree t = this;
318         t = t.getParent();
319         while ( t!=null ) {
320             if ( t.getType()==ttype ) return t;
321             t = t.getParent();
322         }
323         return null;
324     }
325 
326     /** Return a list of all ancestors of this node.  The first node of
327      *  list is the root and the last is the parent of this node.
328      */
getAncestors()329     public List getAncestors() {
330         if ( getParent()==null ) return null;
331         List ancestors = new ArrayList();
332         Tree t = this;
333         t = t.getParent();
334         while ( t!=null ) {
335             ancestors.add(0, t); // insert at start
336             t = t.getParent();
337         }
338         return ancestors;
339     }
340 
341     /** Print out a whole tree not just a node */
toStringTree()342     public String toStringTree() {
343 		if ( children==null || children.size()==0 ) {
344 			return this.toString();
345 		}
346 		StringBuffer buf = new StringBuffer();
347 		if ( !isNil() ) {
348 			buf.append("(");
349 			buf.append(this.toString());
350 			buf.append(' ');
351 		}
352 		for (int i = 0; children!=null && i < children.size(); i++) {
353 			Tree t = (Tree)children.get(i);
354 			if ( i>0 ) {
355 				buf.append(' ');
356 			}
357 			buf.append(t.toStringTree());
358 		}
359 		if ( !isNil() ) {
360 			buf.append(")");
361 		}
362 		return buf.toString();
363 	}
364 
getLine()365     public int getLine() {
366 		return 0;
367 	}
368 
getCharPositionInLine()369 	public int getCharPositionInLine() {
370 		return 0;
371 	}
372 
373 	/** Override to say how a node (not a tree) should look as text */
toString()374 	public abstract String toString();
375 }
376