001: /*
002: [The "BSD licence"]
003: Copyright (c) 2005-2006 Terence Parr
004: All rights reserved.
005:
006: Redistribution and use in source and binary forms, with or without
007: modification, are permitted provided that the following conditions
008: are met:
009: 1. Redistributions of source code must retain the above copyright
010: notice, this list of conditions and the following disclaimer.
011: 2. Redistributions in binary form must reproduce the above copyright
012: notice, this list of conditions and the following disclaimer in the
013: documentation and/or other materials provided with the distribution.
014: 3. The name of the author may not be used to endorse or promote products
015: derived from this software without specific prior written permission.
016:
017: THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
018: IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
019: OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
020: IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
021: INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
022: NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
023: DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
024: THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
025: (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
026: THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
027: */
028: package org.antlr.runtime.tree;
029:
030: import org.antlr.runtime.Token;
031: import org.antlr.runtime.CommonToken;
032:
033: import java.util.List;
034: import java.util.ArrayList;
035:
036: /** A generic list of elements tracked in an alternative to be used in
037: * a -> rewrite rule. We need to subclass to fill in the next() method,
038: * which returns either an AST node wrapped around a token payload or
039: * an existing subtree.
040: *
041: * Once you start next()ing, do not try to add more elements. It will
042: * break the cursor tracking I believe.
043: *
044: * @see org.antlr.runtime.tree.RewriteRuleSubtreeStream
045: * @see org.antlr.runtime.tree.RewriteRuleTokenStream
046: *
047: * TODO: add mechanism to detect/puke on modification after reading from stream
048: */
049: public abstract class RewriteRuleElementStream {
050: /** Cursor 0..n-1. If singleElement!=null, cursor is 0 until you next(),
051: * which bumps it to 1 meaning no more elements.
052: */
053: protected int cursor = 0;
054:
055: /** Track single elements w/o creating a list. Upon 2nd add, alloc list */
056: protected Object singleElement;
057:
058: /** The list of tokens or subtrees we are tracking */
059: protected List elements;
060:
061: /** Once a node / subtree has been used in a stream, it must be dup'd
062: * from then on. Streams are reset after subrules so that the streams
063: * can be reused in future subrules. So, reset must set a dirty bit.
064: * If dirty, then next() always returns a dup.
065: *
066: * I wanted to use "naughty bit" here, but couldn't think of a way
067: * to use "naughty".
068: */
069: protected boolean dirty = false;
070:
071: /** The element or stream description; usually has name of the token or
072: * rule reference that this list tracks. Can include rulename too, but
073: * the exception would track that info.
074: */
075: protected String elementDescription;
076: protected TreeAdaptor adaptor;
077:
078: public RewriteRuleElementStream(TreeAdaptor adaptor,
079: String elementDescription) {
080: this .elementDescription = elementDescription;
081: this .adaptor = adaptor;
082: }
083:
084: /** Create a stream with one element */
085: public RewriteRuleElementStream(TreeAdaptor adaptor,
086: String elementDescription, Object oneElement) {
087: this (adaptor, elementDescription);
088: add(oneElement);
089: }
090:
091: /** Create a stream, but feed off an existing list */
092: public RewriteRuleElementStream(TreeAdaptor adaptor,
093: String elementDescription, List elements) {
094: this (adaptor, elementDescription);
095: this .singleElement = null;
096: this .elements = elements;
097: }
098:
099: /** Reset the condition of this stream so that it appears we have
100: * not consumed any of its elements. Elements themselves are untouched.
101: * Once we reset the stream, any future use will need duplicates. Set
102: * the dirty bit.
103: */
104: public void reset() {
105: cursor = 0;
106: dirty = true;
107: }
108:
109: public void add(Object el) {
110: //System.out.println("add '"+elementDescription+"' is "+el);
111: if (el == null) {
112: return;
113: }
114: if (elements != null) { // if in list, just add
115: elements.add(el);
116: return;
117: }
118: if (singleElement == null) { // no elements yet, track w/o list
119: singleElement = el;
120: return;
121: }
122: // adding 2nd element, move to list
123: elements = new ArrayList(5);
124: elements.add(singleElement);
125: singleElement = null;
126: elements.add(el);
127: }
128:
129: /** Return the next element in the stream. If out of elements, throw
130: * an exception unless size()==1. If size is 1, then return elements[0].
131: * Return a duplicate node/subtree if stream is out of elements and
132: * size==1. If we've already used the element, dup (dirty bit set).
133: */
134: public Object next() {
135: int n = size();
136: if (dirty || (cursor >= n && n == 1)) {
137: // if out of elements and size is 1, dup
138: Object el = _next();
139: return dup(el);
140: }
141: // test size above then fetch
142: Object el = _next();
143: return el;
144: }
145:
146: /** do the work of getting the next element, making sure that it's
147: * a tree node or subtree. Deal with the optimization of single-
148: * element list versus list of size > 1. Throw an exception
149: * if the stream is empty or we're out of elements and size>1.
150: * protected so you can override in a subclass if necessary.
151: */
152: protected Object _next() {
153: int n = size();
154: if (n == 0) {
155: throw new RewriteEmptyStreamException(elementDescription);
156: }
157: if (cursor >= n) { // out of elements?
158: if (n == 1) { // if size is 1, it's ok; return and we'll dup
159: return toTree(singleElement);
160: }
161: // out of elements and size was not 1, so we can't dup
162: throw new RewriteCardinalityException(elementDescription);
163: }
164: // we have elements
165: if (singleElement != null) {
166: cursor++; // move cursor even for single element list
167: return toTree(singleElement);
168: }
169: // must have more than one in list, pull from elements
170: Object o = toTree(elements.get(cursor));
171: cursor++;
172: return o;
173: }
174:
175: /** When constructing trees, sometimes we need to dup a token or AST
176: * subtree. Dup'ing a token means just creating another AST node
177: * around it. For trees, you must call the adaptor.dupTree() unless
178: * the element is for a tree root; then it must be a node dup.
179: */
180: protected abstract Object dup(Object el);
181:
182: /** Ensure stream emits trees; tokens must be converted to AST nodes.
183: * AST nodes can be passed through unmolested.
184: */
185: protected Object toTree(Object el) {
186: return el;
187: }
188:
189: public boolean hasNext() {
190: return (singleElement != null && cursor < 1)
191: || (elements != null && cursor < elements.size());
192: }
193:
194: public int size() {
195: int n = 0;
196: if (singleElement != null) {
197: n = 1;
198: }
199: if (elements != null) {
200: return elements.size();
201: }
202: return n;
203: }
204:
205: public String getDescription() {
206: return elementDescription;
207: }
208: }
|