001/*
002 * Copyright (C) 2009-2011 Mathias Doenitz
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 * http://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 */
016
017package org.parboiled.support;
018
019import java.util.Iterator;
020
021import static org.parboiled.common.Preconditions.checkArgument;
022
023/**
024 * An implementation of a stack of value objects providing an efficient snapshot capability and a number of convenience
025 * methods. The current state of the stack can be saved and restored in small constant time with the methods
026 * {@link #takeSnapshot()} and {@link #restoreSnapshot(Object)} ()}. The implementation also serves as an Iterable
027 * over the current stack values (the values are being provided with the last value (on top of the stack) first).
028 *
029 * @param <V> the type of the value objects
030 */
031@SuppressWarnings({"ConstantConditions"})
032public class DefaultValueStack<V> implements ValueStack<V> {
033
034    protected static class Element {
035        protected final Object value;
036        protected final Element tail;
037
038        protected Element(Object value, Element tail) {
039            this.value = value;
040            this.tail = tail;
041        }
042    }
043
044    protected Element head;
045    protected V tempValue;
046
047    /**
048     * Initializes an empty value stack.
049     */
050    public DefaultValueStack() {
051    }
052
053    /**
054     * Initializes a value stack containing the given values with the last value being at the top of the stack.
055     *
056     * @param values the initial stack values
057     */
058    public DefaultValueStack(Iterable<V> values) {
059        pushAll(values);
060    }
061
062    public boolean isEmpty() {
063        return head == null;
064    }
065
066    public int size() {
067        Element cursor = head;
068        int size = 0;
069        while (cursor != null) {
070            size++;
071            cursor = cursor.tail;
072        }
073        return size;
074    }
075
076    public void clear() {
077        head = null;
078    }
079
080    public Object takeSnapshot() {
081        return head;
082    }
083
084    public void restoreSnapshot(Object snapshot) {
085        try {
086            head = (Element) snapshot;
087        } catch (ClassCastException e) {
088            throw new IllegalArgumentException("Given argument '" + snapshot + "' is not a valid snapshot element");
089        }
090    }
091
092    public void push(V value) {
093        head = new Element(value, head);
094    }
095
096    public void push(int down, V value) {
097        head = push(down, value, head);
098    }
099
100    private static Element push(int down, Object value, Element head) {
101        if (down == 0) return new Element(value, head);
102        checkArgument(head != null, "Cannot push beyond the bottom of the stack");
103        if (down > 0) return new Element(head.value, push(down - 1, value, head.tail));
104        throw new IllegalArgumentException("Argument 'down' must not be negative");
105    }
106
107    public void pushAll(V firstValue, V... moreValues) {
108        push(firstValue);
109        for (V value : moreValues) push(value);
110    }
111
112    public void pushAll(Iterable<V> values) {
113        head = null;
114        for (V value : values) push(value);
115    }
116
117    public V pop() {
118        return pop(0);
119    }
120
121    public V pop(int down) {
122        head = pop(down, head);
123        V result = tempValue;
124        tempValue = null; // avoid memory leak
125        return result;
126    }
127
128    @SuppressWarnings("unchecked")
129    private Element pop(int down, Element head) {
130        checkArgument(head != null, "Cannot pop from beyond the bottom of the stack");
131        if (down == 0) {
132            tempValue = (V) head.value;
133            return head.tail;
134        }
135        if (down > 0) return new Element(head.value, pop(down - 1, head.tail));
136        throw new IllegalArgumentException("Argument 'down' must not be negative");
137    }
138
139    public V peek() {
140        return peek(0);
141    }
142
143    @SuppressWarnings({"unchecked"})
144    public V peek(int down) {
145        return (V) peek(down, head);
146    }
147
148    @SuppressWarnings({"ConstantConditions"})
149    private static Object peek(int down, Element head) {
150        checkArgument(head != null, "Cannot peek beyond the bottom of the stack");
151        if (down == 0) return head.value;
152        if (down > 0) return peek(down - 1, head.tail);
153        throw new IllegalArgumentException("Argument 'down' must not be negative");
154    }
155
156    public void poke(V value) {
157        poke(0, value);
158    }
159
160    public void poke(int down, V value) {
161        head = poke(down, value, head);
162    }
163
164    private static Element poke(int down, Object value, Element head) {
165        checkArgument(head != null, "Cannot poke beyond the bottom of the stack");
166        if (down == 0) return new Element(value, head.tail);
167        if (down > 0) return new Element(head.value, poke(down - 1, value, head.tail));
168        throw new IllegalArgumentException("Argument 'down' must not be negative");
169    }
170
171    public void dup() {
172        push(peek());
173    }
174
175    public void swap() {
176        Checks.ensure(isSizeGTE(2, head), "Swap not allowed on stack with less than two elements");
177        Element down1 = head.tail;
178        head = new Element(down1.value, new Element(head.value, down1.tail));
179    }
180
181    public void swap3() {
182        Checks.ensure(isSizeGTE(3, head), "Swap3 not allowed on stack with less than 3 elements");
183        Element down1 = head.tail;
184        Element down2 = down1.tail;
185        head = new Element(down2.value, new Element(down1.value, new Element(head.value, down2.tail)));
186    }
187
188    public void swap4() {
189        Checks.ensure(isSizeGTE(4, head), "Swap4 not allowed on stack with less than 4 elements");
190        Element down1 = head.tail;
191        Element down2 = down1.tail;
192        Element down3 = down2.tail;
193        head = new Element(down3.value, new Element(down2.value, new Element(down1.value, new Element(head.value,
194                down3.tail))));
195    }
196
197    public void swap5() {
198        Checks.ensure(isSizeGTE(5, head), "Swap5 not allowed on stack with less than 5 elements");
199        Element down1 = head.tail;
200        Element down2 = down1.tail;
201        Element down3 = down2.tail;
202        Element down4 = down3.tail;
203        head = new Element(down4.value, new Element(down3.value, new Element(down2.value, new Element(down1.value,
204                new Element(head.value, down4.tail)))));
205    }
206
207    public void swap6() {
208        Checks.ensure(isSizeGTE(6, head), "Swap6 not allowed on stack with less than 6 elements");
209        Element down1 = head.tail;
210        Element down2 = down1.tail;
211        Element down3 = down2.tail;
212        Element down4 = down3.tail;
213        Element down5 = down4.tail;
214        head = new Element(down5.value, new Element(down4.value, new Element(down3.value, new Element(down2.value,
215                new Element(down1.value, new Element(head.value, down5.tail))))));
216    }
217
218    private static boolean isSizeGTE(int minSize, Element head) {
219        return minSize == 1 ? head != null : isSizeGTE(minSize - 1, head.tail);
220    }
221
222    public Iterator<V> iterator() {
223        return new Iterator<V>() {
224            private Element next = head;
225            public boolean hasNext() {
226                return next != null;
227            }
228            @SuppressWarnings({"unchecked"})
229            public V next() {
230                V value = (V) next.value;
231                next = next.tail;
232                return value;
233            }
234            public void remove() {
235                throw new UnsupportedOperationException();
236            }
237        };
238    }
239}