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.AbstractList;
020import java.util.List;
021
022import static org.parboiled.common.Utils.arrayOf;
023
024/**
025 * A simple, immutable List implementation wrapping an array.
026 *
027 * @param <T> The type of the List elements.
028 */
029@SuppressWarnings( {"unchecked"})
030public abstract class ImmutableList<T> extends AbstractList<T> {
031
032    private final static ImmutableList<?> EMPTY_LIST = new ImmutableList<Object>() {
033        @Override
034        public Object get(int index) {
035            throw new IndexOutOfBoundsException("Empty list has no element with index " + index);
036        }
037
038        @Override
039        public int size() {
040            return 0;
041        }
042
043        @Override
044        public ImmutableList<Object> append(Object element) {
045            return of(element);
046        }
047    };
048
049    private static class SingleElementList<T> extends ImmutableList<T> {
050        private final T element;
051
052        public SingleElementList(T element) {
053            this.element = element;
054        }
055
056        @Override
057        public T get(int index) {
058            Preconditions.checkElementIndex(index, 1);
059            return element;
060        }
061
062        @Override
063        public int size() {
064            return 1;
065        }
066
067        @Override
068        public ImmutableList<T> append(T element) {
069            return of(this.element, element);
070        }
071    }
072    
073    private static class TwoElementList<T> extends ImmutableList<T> {
074        private final T element0;
075        private final T element1;
076
077        private TwoElementList(T element0, T element1) {
078            this.element0 = element0;
079            this.element1 = element1;
080        }
081
082        @Override
083        public T get(int index) {
084            Preconditions.checkElementIndex(index, 2);
085            return index == 0 ? element0 : element1;
086        }
087
088        @Override
089        public int size() {
090            return 2;
091        }
092
093        @Override
094        public ImmutableList<T> append(T element) {
095            return of(element0, element1, element);
096        }
097    }
098    
099    private static class RegularList extends ImmutableList<Object> {
100        private final Object[] elements;
101
102        private RegularList(Object[] elements) {
103            this.elements = elements;
104        }
105
106        @Override
107        public Object get(int index) {
108            return elements[index];
109        }
110
111        @Override
112        public int size() {
113            return elements.length;
114        }
115
116        @Override
117        public ImmutableList<Object> append(Object element) {
118            Object[] newElements = new Object[elements.length + 1];
119            System.arraycopy(elements, 0, newElements, 0, elements.length);
120            newElements[elements.length] = element;
121            return new RegularList(newElements);
122        }
123    }
124    
125    public abstract ImmutableList<T> append(T element);
126
127    public static <T> ImmutableList<T> copyOf(List<T> other) {
128        Preconditions.checkArgNotNull(other, "other");
129        return (ImmutableList<T>) (other instanceof ImmutableList ? other : new RegularList(other.toArray()));
130    }
131
132    public static <T> ImmutableList<T> of() {
133        return (ImmutableList<T>) EMPTY_LIST;
134    }
135
136    public static <T> ImmutableList<T> of(T a) {
137        return new SingleElementList<T>(a);
138    }
139
140    public static <T> ImmutableList<T> of(T a, T b) {
141        return new TwoElementList<T>(a, b);
142    }
143
144    public static <T> ImmutableList<T> of(T a, T b, T c) {
145        return (ImmutableList<T>) new RegularList(new Object[] {a, b, c});
146    }
147
148    public static <T> ImmutableList<T> of(T... elements) {
149        Preconditions.checkArgNotNull(elements, "elements");
150        return (ImmutableList<T>) new RegularList(elements.clone());
151    }
152
153    public static <T> ImmutableList<T> of(T first, T[] more) {
154        Preconditions.checkArgNotNull(more, "more");
155        return (ImmutableList<T>) new RegularList(arrayOf(first, more.clone()));
156    }
157
158    public static <T> ImmutableList<T> of(T[] first, T last) {
159        Preconditions.checkArgNotNull(first, "first");
160        return (ImmutableList<T>) new RegularList(arrayOf(first.clone(), last));
161    }
162
163    public static <T> ImmutableList<T> of(T first, ImmutableList<T> more) {
164        Preconditions.checkArgNotNull(more, "more");
165        if (more instanceof SingleElementList) {
166            return of(first, (T) ((SingleElementList) more).element);
167        } else if (more instanceof TwoElementList) {
168            TwoElementList list = (TwoElementList) more;
169            return (ImmutableList<T>) new RegularList(new Object[] {first, list.element0, list.element1});
170        } else if (more instanceof RegularList) {
171            RegularList list = (RegularList) more;
172            return (ImmutableList<T>) new RegularList(arrayOf(first, list.elements));
173        } else {
174            Preconditions.checkState(more == EMPTY_LIST);
175            return of(first);
176        }
177    }
178
179    public static <T> ImmutableList<T> of(ImmutableList<T> first, T last) {
180        Preconditions.checkArgNotNull(first, "more");
181        if (first instanceof SingleElementList) {
182            return of((T) ((SingleElementList) first).element, last);
183        } else if (first instanceof TwoElementList) {
184            TwoElementList list = (TwoElementList) first;
185            return (ImmutableList<T>) new RegularList(new Object[] {list.element0, list.element1, last});
186        } else if (first instanceof RegularList) {
187            RegularList list = (RegularList) first;
188            return (ImmutableList<T>) new RegularList(arrayOf(list.elements, last));
189        } else {
190            Preconditions.checkState(first == EMPTY_LIST);
191            return of(last);
192        }
193    }
194}