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.common; 018 019import java.util.AbstractSequentialList; 020import java.util.ListIterator; 021 022public class ImmutableLinkedList<T> extends AbstractSequentialList<T> { 023 024 private static final ImmutableLinkedList<Object> NIL = new ImmutableLinkedList<Object>() { 025 private final ListIterator<Object> iterator = new IllIterator<Object>(this); 026 027 @Override 028 public Object head() { 029 throw new UnsupportedOperationException("head of empty list"); 030 } 031 032 @Override 033 public ImmutableLinkedList<Object> tail() { 034 throw new UnsupportedOperationException("tail of empty list"); 035 } 036 037 @Override 038 public Object last() { 039 throw new UnsupportedOperationException("last of empty list"); 040 } 041 042 @Override 043 public ListIterator<Object> listIterator(int index) { 044 return iterator; 045 } 046 }; 047 048 @SuppressWarnings({"unchecked"}) 049 public static <T> ImmutableLinkedList<T> nil() { 050 return (ImmutableLinkedList<T>) NIL; 051 } 052 053 private final T head; 054 private final ImmutableLinkedList<T> tail; 055 056 // only used by NIL 057 private ImmutableLinkedList() { 058 head = null; 059 tail = null; 060 } 061 062 public ImmutableLinkedList(T head, ImmutableLinkedList<T> tail) { 063 Preconditions.checkArgNotNull(tail, "tail"); 064 this.head = head; 065 this.tail = tail; 066 } 067 068 public T head() { 069 return head; 070 } 071 072 public ImmutableLinkedList<T> tail() { 073 return tail; 074 } 075 076 public T last() { 077 ImmutableLinkedList<T> cursor = this; 078 while (!cursor.tail.isEmpty()) { 079 cursor = cursor.tail(); 080 } 081 return cursor.head(); 082 } 083 084 public ImmutableLinkedList<T> prepend(T object) { 085 return new ImmutableLinkedList<T>(object, this); 086 } 087 088 public ImmutableLinkedList<T> reverse() { 089 if (tail == NIL) return this; 090 091 ImmutableLinkedList<T> reversed = nil(); 092 ImmutableLinkedList<T> next = this; 093 while (next != NIL) { 094 reversed = reversed.prepend(next.head); 095 next = next.tail; 096 } 097 return reversed; 098 } 099 100 public static <T> boolean equal(ImmutableLinkedList<T> a, ImmutableLinkedList<T> b) { 101 Preconditions.checkArgNotNull(a, "a"); 102 Preconditions.checkArgNotNull(b, "b"); 103 return Utils.equal(a.head, b.head) && equal(a.tail, b.tail); 104 } 105 106 public static int hashCode(ImmutableLinkedList<?> list) { 107 Preconditions.checkArgNotNull(list, "list"); 108 return list.isEmpty() ? 0 : 31 * list.head.hashCode() + hashCode(list.tail); 109 } 110 111 @Override 112 public ListIterator<T> listIterator(int index) { 113 ListIterator<T> iterator = new IllIterator<T>(this); 114 while (index-- > 0) { 115 if (!iterator.hasNext()) throw new IndexOutOfBoundsException(); 116 iterator.next(); 117 } 118 return iterator; 119 } 120 121 @Override 122 public boolean isEmpty() { 123 return this == NIL; 124 } 125 126 @Override 127 public int size() { 128 ImmutableLinkedList<T> cursor = this; 129 int size = 0; 130 while (!cursor.isEmpty()) { 131 size++; 132 cursor = cursor.tail(); 133 } 134 return size; 135 } 136 137 private static class IllIterator<T> implements ListIterator<T> { 138 private final ImmutableLinkedList<T> start; 139 private ImmutableLinkedList<T> current; 140 private int nextIndex = 0; 141 142 private IllIterator(ImmutableLinkedList<T> start) { 143 this.start = start; 144 this.current = start; 145 } 146 147 public boolean hasNext() { 148 return current != NIL; 149 } 150 151 public T next() { 152 ImmutableLinkedList<T> next = current; 153 current = current.tail; 154 nextIndex++; 155 return next.head; 156 } 157 158 public boolean hasPrevious() { 159 return current != start; 160 } 161 162 public T previous() { 163 ImmutableLinkedList<T> previous = start; 164 while (previous.tail != current) previous = previous.tail; 165 nextIndex--; 166 return previous.head; 167 } 168 169 public int nextIndex() { 170 return nextIndex; 171 } 172 173 public int previousIndex() { 174 return nextIndex - 1; 175 } 176 177 public void remove() { 178 throw new UnsupportedOperationException(); 179 } 180 181 public void set(T t) { 182 throw new UnsupportedOperationException(); 183 } 184 185 public void add(T t) { 186 throw new UnsupportedOperationException(); 187 } 188 } 189 190}