001/* 002 * ModeShape (http://www.modeshape.org) 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 */ 016package org.modeshape.common.collection; 017 018import java.util.Collection; 019import java.util.Collections; 020import java.util.Iterator; 021import java.util.List; 022import java.util.ListIterator; 023import java.util.NoSuchElementException; 024import org.modeshape.common.annotation.Immutable; 025import org.modeshape.common.util.CheckArg; 026 027/** 028 * An immutable {@link List} that consists of a single element appended to another existing {@link List}. The result is a list 029 * that contains all of the elements in the parent list as well as the last appended element, but while reusing the existing 030 * parent list and without having to create a copy of the parent list. 031 * 032 * @param <T> the type of element 033 */ 034@Immutable 035public class ImmutableAppendedList<T> implements List<T> { 036 037 private final List<T> parent; 038 private final T element; 039 private final int size; 040 private transient int hc; 041 042 /** 043 * Create an instance using the supplied parent list and an element to be virtually appended to the parent. Note that the 044 * parent must be immutable (though this is not checked). 045 * 046 * @param parent the parent list 047 * @param element the child element (may be null) 048 * @throws IllegalArgumentException if the reference to the parent list is null 049 */ 050 public ImmutableAppendedList( List<T> parent, 051 T element ) { 052 CheckArg.isNotNull(parent, "parent"); 053 this.parent = parent; 054 this.element = element; 055 this.size = parent.size() + 1; 056 } 057 058 @Override 059 public boolean contains( Object o ) { 060 return element == o || (element != null && element.equals(o)) || parent.contains(o); 061 } 062 063 @Override 064 public boolean containsAll( Collection<?> c ) { 065 Iterator<?> e = c.iterator(); 066 while (e.hasNext()) { 067 if (!contains(e.next())) return false; 068 } 069 return true; 070 } 071 072 @Override 073 public T get( int index ) { 074 if (index == (size - 1)) return element; 075 return parent.get(index); 076 } 077 078 @Override 079 public int indexOf( Object o ) { 080 int index = parent.indexOf(o); 081 if (index == -1) { 082 return (element == o || (element != null && element.equals(o))) ? (size - 1) : -1; 083 } 084 return -1; 085 } 086 087 @Override 088 public boolean isEmpty() { 089 return false; 090 } 091 092 @Override 093 @SuppressWarnings( "synthetic-access" ) 094 public Iterator<T> iterator() { 095 final Iterator<T> parentIterator = parent.iterator(); 096 return new Iterator<T>() { 097 boolean finished = false; 098 099 @Override 100 public boolean hasNext() { 101 return parentIterator.hasNext() || !finished; 102 } 103 104 @Override 105 public T next() { 106 if (parentIterator.hasNext()) return parentIterator.next(); 107 if (finished) throw new NoSuchElementException(); 108 finished = true; 109 return element; 110 } 111 112 @Override 113 public void remove() { 114 throw new UnsupportedOperationException(); 115 } 116 }; 117 } 118 119 @Override 120 public int lastIndexOf( Object o ) { 121 if (element == o || (element != null && element.equals(o))) return size - 1; 122 return parent.lastIndexOf(o); 123 } 124 125 @Override 126 public ListIterator<T> listIterator() { 127 return listIterator(0); 128 } 129 130 @Override 131 @SuppressWarnings( "synthetic-access" ) 132 public ListIterator<T> listIterator( final int index ) { 133 return new ListIterator<T>() { 134 int cursor = index; 135 136 @Override 137 public boolean hasNext() { 138 return cursor < size; 139 } 140 141 @Override 142 public T next() { 143 try { 144 T next = get(cursor); 145 cursor++; 146 return next; 147 } catch (IndexOutOfBoundsException e) { 148 throw new NoSuchElementException(); 149 } 150 } 151 152 @Override 153 public boolean hasPrevious() { 154 return cursor != 0; 155 } 156 157 @Override 158 public int nextIndex() { 159 return cursor; 160 } 161 162 @Override 163 public T previous() { 164 try { 165 int i = cursor - 1; 166 T previous = get(i); 167 cursor = i; 168 return previous; 169 } catch (IndexOutOfBoundsException e) { 170 throw new NoSuchElementException(); 171 } 172 } 173 174 @Override 175 public int previousIndex() { 176 return cursor - 1; 177 } 178 179 @Override 180 public void set( T o ) { 181 throw new UnsupportedOperationException(); 182 } 183 184 @Override 185 public void remove() { 186 throw new UnsupportedOperationException(); 187 } 188 189 @Override 190 public void add( T o ) { 191 throw new UnsupportedOperationException(); 192 } 193 194 }; 195 } 196 197 @Override 198 public int size() { 199 return size; 200 } 201 202 @Override 203 public List<T> subList( int fromIndex, 204 int toIndex ) { 205 if (fromIndex == 0 && toIndex == size) { 206 // The bounds are the same as this list, so just return this list ... 207 return this; 208 } 209 if (toIndex == size && fromIndex == (size - 1)) { 210 // The only list is the last element ... 211 return Collections.singletonList(element); 212 } 213 if (toIndex < size) { 214 // It is all within the range of the parent's list, so simply delegate 215 return parent.subList(fromIndex, toIndex); 216 } 217 // Otherwise, the sublist starts within the parent list and ends with the last element. 218 // So, create a sublist starting at the 'fromIndex' until the end of the parent list ... 219 List<T> sublist = parent.subList(fromIndex, toIndex - 1); // will catch out-of-bounds errors 220 // And wrap with another immutable appended list to add the last element ... 221 return new ImmutableAppendedList<T>(sublist, element); 222 } 223 224 @Override 225 public Object[] toArray() { 226 Object[] result = new Object[size]; 227 int i = 0; 228 for (T e : parent) { 229 result[i++] = e; 230 } 231 result[i] = element; 232 return result; 233 } 234 235 @Override 236 @SuppressWarnings( "unchecked" ) 237 public <X> X[] toArray( X[] a ) { 238 if (a.length < size) a = (X[])java.lang.reflect.Array.newInstance(a.getClass().getComponentType(), size); 239 a = parent.toArray(a); 240 a[size - 1] = (X)element; 241 return a; 242 } 243 244 @Override 245 public int hashCode() { 246 if (hc == 0) { 247 int hashCode = 1; 248 for (T element : this) { 249 hashCode = 31 * hashCode + (element == null ? 0 : element.hashCode()); 250 } 251 hc = hashCode; 252 } 253 return hc; 254 } 255 256 @Override 257 public boolean equals( Object obj ) { 258 if (obj == this) return true; 259 if (obj instanceof List<?>) { 260 List<?> that = (List<?>)obj; 261 if (this.size() != that.size()) return false; 262 Iterator<?> thisIter = this.iterator(); 263 Iterator<?> thatIter = that.iterator(); 264 while (thisIter.hasNext()) { 265 Object thisValue = thisIter.next(); 266 Object thatValue = thatIter.next(); 267 if (thisValue == null) { 268 if (thatValue != null) return false; 269 // assert thatValue == null; 270 } else { 271 if (!thisValue.equals(thatValue)) return false; 272 } 273 } 274 return true; 275 } 276 return super.equals(obj); 277 } 278 279 @Override 280 public String toString() { 281 StringBuilder sb = new StringBuilder(); 282 sb.append("["); 283 284 Iterator<T> i = iterator(); 285 boolean hasNext = i.hasNext(); 286 while (hasNext) { 287 T o = i.next(); 288 sb.append(o == this ? "(this Collection)" : String.valueOf(o)); 289 hasNext = i.hasNext(); 290 if (hasNext) sb.append(", "); 291 } 292 293 sb.append("]"); 294 return sb.toString(); 295 } 296 297 // ---------------------------------------------------------------------------------------------------------------- 298 // Methods that modify are not supported 299 // ---------------------------------------------------------------------------------------------------------------- 300 301 @Override 302 public void add( int index, 303 T element ) { 304 throw new UnsupportedOperationException(); 305 } 306 307 @Override 308 public boolean add( T o ) { 309 throw new UnsupportedOperationException(); 310 } 311 312 @Override 313 public boolean addAll( Collection<? extends T> c ) { 314 throw new UnsupportedOperationException(); 315 } 316 317 @Override 318 public boolean addAll( int index, 319 Collection<? extends T> c ) { 320 throw new UnsupportedOperationException(); 321 } 322 323 @Override 324 public void clear() { 325 throw new UnsupportedOperationException(); 326 } 327 328 @Override 329 public boolean remove( Object o ) { 330 throw new UnsupportedOperationException(); 331 } 332 333 @Override 334 public T remove( int index ) { 335 throw new UnsupportedOperationException(); 336 } 337 338 @Override 339 public boolean removeAll( Collection<?> c ) { 340 throw new UnsupportedOperationException(); 341 } 342 343 @Override 344 public boolean retainAll( Collection<?> c ) { 345 throw new UnsupportedOperationException(); 346 } 347 348 @Override 349 public T set( int index, 350 T element ) { 351 throw new UnsupportedOperationException(); 352 } 353 354}