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}