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}