Home | History | Annotate | Download | only in tree
      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 list of elements tracked in an alternative to be used in
     34  *  a -> rewrite rule.  We need to subclass to fill in the next() method,
     35  *  which returns either an AST node wrapped around a token payload or
     36  *  an existing subtree.
     37  *
     38  *  Once you start next()ing, do not try to add more elements.  It will
     39  *  break the cursor tracking I believe.
     40  *
     41  *  @see org.antlr.runtime.tree.RewriteRuleSubtreeStream
     42  *  @see org.antlr.runtime.tree.RewriteRuleTokenStream
     43  *
     44  *  TODO: add mechanism to detect/puke on modification after reading from stream
     45  */
     46 public abstract class RewriteRuleElementStream {
     47 	/** Cursor 0..n-1.  If singleElement!=null, cursor is 0 until you next(),
     48 	 *  which bumps it to 1 meaning no more elements.
     49 	 */
     50 	protected int cursor = 0;
     51 
     52 	/** Track single elements w/o creating a list.  Upon 2nd add, alloc list */
     53 	protected Object singleElement;
     54 
     55 	/** The list of tokens or subtrees we are tracking */
     56 	protected List elements;
     57 
     58 	/** Once a node / subtree has been used in a stream, it must be dup'd
     59 	 *  from then on.  Streams are reset after subrules so that the streams
     60 	 *  can be reused in future subrules.  So, reset must set a dirty bit.
     61 	 *  If dirty, then next() always returns a dup.
     62 	 *
     63 	 *  I wanted to use "naughty bit" here, but couldn't think of a way
     64 	 *  to use "naughty".
     65 	 *
     66 	 *  TODO: unused?
     67 	 */
     68 	protected boolean dirty = false;
     69 
     70 	/** The element or stream description; usually has name of the token or
     71 	 *  rule reference that this list tracks.  Can include rulename too, but
     72 	 *  the exception would track that info.
     73 	 */
     74 	protected String elementDescription;
     75 	protected TreeAdaptor adaptor;
     76 
     77 	public RewriteRuleElementStream(TreeAdaptor adaptor, String elementDescription) {
     78 		this.elementDescription = elementDescription;
     79 		this.adaptor = adaptor;
     80 	}
     81 
     82 	/** Create a stream with one element */
     83 	public RewriteRuleElementStream(TreeAdaptor adaptor,
     84 									String elementDescription,
     85 									Object oneElement)
     86 	{
     87 		this(adaptor, elementDescription);
     88 		add(oneElement);
     89 	}
     90 
     91 	/** Create a stream, but feed off an existing list */
     92 	public RewriteRuleElementStream(TreeAdaptor adaptor,
     93 									String elementDescription,
     94 									List elements)
     95 	{
     96 		this(adaptor, elementDescription);
     97 		this.singleElement = null;
     98 		this.elements = elements;
     99 	}
    100 
    101 	/** Reset the condition of this stream so that it appears we have
    102 	 *  not consumed any of its elements.  Elements themselves are untouched.
    103 	 *  Once we reset the stream, any future use will need duplicates.  Set
    104 	 *  the dirty bit.
    105 	 */
    106 	public void reset() {
    107 		cursor = 0;
    108 		dirty = true;
    109 	}
    110 
    111 	public void add(Object el) {
    112 		//System.out.println("add '"+elementDescription+"' is "+el);
    113 		if ( el==null ) {
    114 			return;
    115 		}
    116 		if ( elements!=null ) { // if in list, just add
    117 			elements.add(el);
    118 			return;
    119 		}
    120 		if ( singleElement == null ) { // no elements yet, track w/o list
    121 			singleElement = el;
    122 			return;
    123 		}
    124 		// adding 2nd element, move to list
    125 		elements = new ArrayList(5);
    126 		elements.add(singleElement);
    127 		singleElement = null;
    128 		elements.add(el);
    129 	}
    130 
    131 	/** Return the next element in the stream.  If out of elements, throw
    132 	 *  an exception unless size()==1.  If size is 1, then return elements[0].
    133 	 *  Return a duplicate node/subtree if stream is out of elements and
    134 	 *  size==1.  If we've already used the element, dup (dirty bit set).
    135 	 */
    136 	public Object nextTree() {
    137 		int n = size();
    138 		if ( dirty || (cursor>=n && n==1) ) {
    139 			// if out of elements and size is 1, dup
    140 			Object el = _next();
    141 			return dup(el);
    142 		}
    143 		// test size above then fetch
    144 		Object el = _next();
    145 		return el;
    146 	}
    147 
    148 	/** do the work of getting the next element, making sure that it's
    149 	 *  a tree node or subtree.  Deal with the optimization of single-
    150 	 *  element list versus list of size > 1.  Throw an exception
    151 	 *  if the stream is empty or we're out of elements and size>1.
    152 	 *  protected so you can override in a subclass if necessary.
    153 	 */
    154 	protected Object _next() {
    155 		int n = size();
    156 		if ( n ==0 ) {
    157 			throw new RewriteEmptyStreamException(elementDescription);
    158 		}
    159 		if ( cursor>= n) { // out of elements?
    160 			if ( n ==1 ) {  // if size is 1, it's ok; return and we'll dup
    161 				return toTree(singleElement);
    162 			}
    163 			// out of elements and size was not 1, so we can't dup
    164 			throw new RewriteCardinalityException(elementDescription);
    165 		}
    166 		// we have elements
    167 		if ( singleElement!=null ) {
    168 			cursor++; // move cursor even for single element list
    169 			return toTree(singleElement);
    170 		}
    171 		// must have more than one in list, pull from elements
    172 		Object o = toTree(elements.get(cursor));
    173 		cursor++;
    174 		return o;
    175 	}
    176 
    177 	/** When constructing trees, sometimes we need to dup a token or AST
    178 	 * 	subtree.  Dup'ing a token means just creating another AST node
    179 	 *  around it.  For trees, you must call the adaptor.dupTree() unless
    180 	 *  the element is for a tree root; then it must be a node dup.
    181 	 */
    182 	protected abstract Object dup(Object el);
    183 
    184 	/** Ensure stream emits trees; tokens must be converted to AST nodes.
    185 	 *  AST nodes can be passed through unmolested.
    186 	 */
    187 	protected Object toTree(Object el) {
    188 		return el;
    189 	}
    190 
    191 	public boolean hasNext() {
    192 		 return (singleElement != null && cursor < 1) ||
    193 			   (elements!=null && cursor < elements.size());
    194 	}
    195 
    196 	public int size() {
    197 		int n = 0;
    198 		if ( singleElement != null ) {
    199 			n = 1;
    200 		}
    201 		if ( elements!=null ) {
    202 			return elements.size();
    203 		}
    204 		return n;
    205 	}
    206 
    207 	public String getDescription() {
    208 		return elementDescription;
    209 	}
    210 }
    211