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}