1/** A generic list of elements tracked in an alternative to be used in
2 *  a -> rewrite rule.  We need to subclass to fill in the next() method,
3 *  which returns either an AST node wrapped around a token payload or
4 *  an existing subtree.
5 *
6 *  Once you start next()ing, do not try to add more elements.  It will
7 *  break the cursor tracking I believe.
8 *
9 *  @see org.antlr.runtime.tree.RewriteRuleSubtreeStream
10 *  @see org.antlr.runtime.tree.RewriteRuleTokenStream
11 *
12 *  TODO: add mechanism to detect/puke on modification after reading from stream
13 */
14org.antlr.runtime.tree.RewriteRuleElementStream = function(adaptor, elementDescription, el) {
15    /** Cursor 0..n-1.  If singleElement!=null, cursor is 0 until you next(),
16     *  which bumps it to 1 meaning no more elements.
17     */
18    this.cursor = 0;
19
20    /** Once a node / subtree has been used in a stream, it must be dup'd
21     *  from then on.  Streams are reset after subrules so that the streams
22     *  can be reused in future subrules.  So, reset must set a dirty bit.
23     *  If dirty, then next() always returns a dup.
24     *
25     *  I wanted to use "naughty bit" here, but couldn't think of a way
26     *  to use "naughty".
27     */
28    this.dirty = false;
29
30    this.elementDescription = elementDescription;
31    this.adaptor = adaptor;
32    if (el) {
33        if (org.antlr.lang.isArray(el)) {
34            this.singleElement = null;
35            this.elements = el;
36        } else {
37            this.add(el);
38        }
39    }
40};
41
42org.antlr.runtime.tree.RewriteRuleElementStream.prototype = {
43    /** Reset the condition of this stream so that it appears we have
44     *  not consumed any of its elements.  Elements themselves are untouched.
45     *  Once we reset the stream, any future use will need duplicates.  Set
46     *  the dirty bit.
47     */
48    reset: function() {
49        this.cursor = 0;
50        this.dirty = true;
51    },
52
53    add: function(el) {
54        if ( !org.antlr.lang.isValue(el) ) {
55            return;
56        }
57        if ( this.elements ) { // if in list, just add
58            this.elements.push(el);
59            return;
60        }
61        if ( !org.antlr.lang.isValue(this.singleElement) ) { // no elements yet, track w/o list
62            this.singleElement = el;
63            return;
64        }
65        // adding 2nd element, move to list
66        this.elements = [];
67        this.elements.push(this.singleElement);
68        this.singleElement = null;
69        this.elements.push(el);
70    },
71
72    /** Return the next element in the stream.  If out of elements, throw
73     *  an exception unless size()==1.  If size is 1, then return elements[0].
74     *  Return a duplicate node/subtree if stream is out of elements and
75     *  size==1.  If we've already used the element, dup (dirty bit set).
76     */
77    nextTree: function() {
78        var n = this.size(),
79            el;
80        if ( this.dirty || (this.cursor>=n && n==1) ) {
81            // if out of elements and size is 1, dup
82            el = this._next();
83            return this.dup(el);
84        }
85        // test size above then fetch
86        el = this._next();
87        return el;
88    },
89
90    /** do the work of getting the next element, making sure that it's
91     *  a tree node or subtree.  Deal with the optimization of single-
92     *  element list versus list of size > 1.  Throw an exception
93     *  if the stream is empty or we're out of elements and size>1.
94     *  protected so you can override in a subclass if necessary.
95     */
96    _next: function() {
97        var n = this.size();
98        if (n===0) {
99            throw new org.antlr.runtime.tree.RewriteEmptyStreamException(this.elementDescription);
100        }
101        if ( this.cursor>= n) { // out of elements?
102            if ( n===1 ) {  // if size is 1, it's ok; return and we'll dup
103                return this.toTree(this.singleElement);
104            }
105            // out of elements and size was not 1, so we can't dup
106            throw new org.antlr.runtime.tree.RewriteCardinalityException(this.elementDescription);
107        }
108        // we have elements
109        if ( org.antlr.lang.isValue(this.singleElement) ) {
110            this.cursor++; // move cursor even for single element list
111            return this.toTree(this.singleElement);
112        }
113        // must have more than one in list, pull from elements
114        var o = this.toTree(this.elements[this.cursor]);
115        this.cursor++;
116        return o;
117    },
118
119    /** Ensure stream emits trees; tokens must be converted to AST nodes.
120     *  AST nodes can be passed through unmolested.
121     */
122    toTree: function(el) {
123        if (el && el.getTree) {
124            return el.getTree();
125        }
126        return el;
127    },
128
129    hasNext: function() {
130         return (org.antlr.lang.isValue(this.singleElement) && this.cursor < 1) ||
131               (this.elements && this.cursor < this.elements.length);
132    },
133
134    size: function() {
135        var n = 0;
136        if ( org.antlr.lang.isValue(this.singleElement) ) {
137            n = 1;
138        }
139        if ( this.elements ) {
140            return this.elements.length;
141        }
142        return n;
143    },
144
145    getDescription: function() {
146        return this.elementDescription;
147    }
148};
149