1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21 package org.millscript.commons.util.list;
22
23 import org.millscript.commons.alert.alerts.Fault;
24 import org.millscript.commons.alert.alerts.IllegalArgumentAlert;
25 import org.millscript.commons.util.IList;
26 import org.millscript.commons.util.ListIterator;
27 import org.millscript.commons.util.alerts.ListIndexOutOfBoundsAlert;
28 import org.millscript.commons.util.iterator.AbstractListIterator;
29 import org.millscript.commons.util.iterator.ArrayListIterator;
30 import org.millscript.commons.util.iterator.NullListIterator;
31
32 import java.io.Serializable;
33 import java.lang.reflect.InvocationTargetException;
34 import java.lang.reflect.Method;
35 import java.util.ArrayList;
36 import java.util.Collection;
37 import java.util.Collections;
38 import java.util.Iterator;
39 import java.util.List;
40 import java.util.NoSuchElementException;
41
42 /**
43 * This class provides an immutable <code>List</code> implementation which is
44 * backed by a java.util.List instance.
45 */
46 public class IJavaUtilList< V > extends AbstractIList< V > implements Cloneable, Serializable {
47
48 /**
49 * This is the ID from the release 0.1.0 for future compatibility.
50 */
51 private static final long serialVersionUID = 4423628895238982551L;
52
53 /**
54 * This class provides a map interator implementation which iterates over
55 * the values in a {@link java.util.List}.
56 */
57 public static class JavaUtilCollectionListIterator< V > extends AbstractListIterator< V > {
58
59 /**
60 * The current object in the iteration.
61 */
62 private V currentObject;
63
64 /**
65 * The {@link Iterator} we get out values from.
66 */
67 private final Iterator< V > javaUtilIterator;
68
69 /**
70 * The number of iterations before we should stop.
71 */
72 private final int numberOfIterations;
73
74 /**
75 * Constructs a new java util collection map iterator to iterate over the
76 * values in the specified {@link Collection}.
77 *
78 * @param col the java.util.Collection whose values to iterate over
79 */
80 public JavaUtilCollectionListIterator( final Collection< V > col ) {
81 this.javaUtilIterator = col.iterator();
82 this.numberOfIterations = col.size();
83 }
84
85 /**
86 * Constructs a new java util list map iterator to iterate over the
87 * values in the specified {@link java.util.List}.
88 *
89 * @param list the java.util.List whose values to iterate over
90 * @param start the index(one based, inclusive) to start iterating from
91 * @param end the index(one based, inclusive) to stop iterating at
92 */
93 public JavaUtilCollectionListIterator( final java.util.List< V > list, final int first, final int last ) {
94 if ( first > last ) {
95 this.javaUtilIterator = null;
96 this.numberOfIterations = 0;
97 } else if ( first < 1 || first > list.size() ) {
98 throw new ListIndexOutOfBoundsAlert(
99 "First index in slice must be between 1 and the size of the list"
100 ).culprit(
101 "index",
102 first
103 ).decorate( list ).mishap();
104 } else if ( last > list.size() ) {
105 throw new ListIndexOutOfBoundsAlert(
106 "Last index in slice must not be greater than the size of the list"
107 ).culprit(
108 "index",
109 last
110 ).decorate( list ).mishap();
111 } else {
112 this.javaUtilIterator = list.listIterator( first - 1 );
113 this.numberOfIterations = last - first + 1;
114 }
115 }
116
117 /**
118 * @see org.millscript.commons.util.iterator.AbstractMapIterator#advance()
119 */
120 @Override
121 protected void advance() {
122
123 super.advance();
124
125
126 try {
127 this.currentObject = this.javaUtilIterator.next();
128 } catch ( NoSuchElementException ex ) {
129 super.position = 0;
130 } catch ( NullPointerException ex ) {
131 super.position = 0;
132 }
133 }
134
135 /**
136 * @see org.millscript.commons.util.iterator.AbstractMapIterator#getValue()
137 */
138 @Override
139 protected V getValue() {
140 return this.currentObject;
141 }
142
143 /**
144 * @see org.millscript.commons.util.MapIterator#hasNext()
145 */
146 public boolean hasNext() {
147 return this.javaUtilIterator.hasNext() && super.position < this.numberOfIterations;
148 }
149
150 /**
151 * @see org.millscript.commons.util.iterator.AbstractMapIterator#outOfBounds()
152 */
153 @Override
154 protected boolean outOfBounds() {
155 return super.position == 0 || super.position > this.numberOfIterations;
156 }
157
158 }
159
160 /**
161 * This class provides an immutable <code>List</code> implementation which is
162 * backed by a slice of an Object array. This class allows you to conveniently
163 * share store between different lists.
164 */
165 public static class ImmutableSharedJavaUtilList< V > extends AbstractIList< V > {
166
167 /**
168 * The index of the first element of this list in the backing store.
169 */
170 private final int firstIndex;
171
172 /**
173 * The index of the last element of this list in the backing store.
174 */
175 private final int lastIndex;
176
177 /**
178 * The backing store containing a fixed array of objects.
179 */
180 private final java.util.List< V > store;
181
182 /**
183 * Constructs a new immutable array list with a slice of the specified
184 * backing java.util.List.
185 *
186 * @param list the backing java.util.List
187 * @param start the index(one based, inclusive) of the first element in the
188 * slice
189 * @param end the index(one based, inclusive) of the last element in the
190 * slice. If end < start, the new list will be empty
191 */
192 public ImmutableSharedJavaUtilList( final java.util.List< V > list, final int start, final int end ) {
193 if ( start > end ) {
194 this.store = null;
195 } else if ( start < 1 || start > list.size() ) {
196 throw new ListIndexOutOfBoundsAlert(
197 "First index in slice must be between 1 and the size of the list"
198 ).culprit(
199 "index",
200 start
201 ).decorate( list ).mishap();
202 } else if ( end > list.size() ) {
203 throw new ListIndexOutOfBoundsAlert(
204 "Last index in slice must not be greater than the size of the list"
205 ).culprit(
206 "index",
207 end
208 ).decorate( list ).mishap();
209 } else {
210 this.store = list;
211 }
212 this.firstIndex = start;
213 this.lastIndex = end;
214 }
215
216 /**
217 * @see org.millscript.commons.util.list.AbstractIList#doGet(int)
218 */
219 @Override
220 protected V doGet( final int pos ) {
221
222
223 return this.store.get( this.firstIndex + pos - 2 );
224 }
225
226 /**
227 * @see org.millscript.commons.util.list.AbstractIList#doSlice(int, int, boolean)
228 */
229 @Override
230 @SuppressWarnings( "unchecked" )
231 protected IList< V > doSlice( final int first, final int last, final boolean share ) {
232
233
234 if ( share ) {
235 return new ImmutableSharedJavaUtilList< V >(
236 this.store,
237 this.firstIndex + first - 1,
238 this.firstIndex + last - 1
239 );
240 } else {
241
242
243
244 return new IArrayList< V >(
245 (V[]) this.store.subList( first - 1, last ).toArray(),
246 true
247 );
248 }
249 }
250
251 /**
252 * @see org.millscript.commons.util.IList#indexOf(java.lang.Object)
253 */
254 public int indexOf( final V value ) {
255 final int storeIndex = this.store.indexOf( value ) + 1;
256 if ( storeIndex < 1 || storeIndex > this.size() ) {
257 return 0;
258 } else {
259 return storeIndex;
260 }
261 }
262
263 /**
264 * @see org.millscript.commons.util.IMap#iterator(boolean)
265 */
266 @SuppressWarnings( "unchecked" )
267 public ListIterator< V > iterator( final boolean share ) {
268 if ( share ) {
269 return new JavaUtilCollectionListIterator< V >(
270 this.store,
271 this.firstIndex,
272 this.lastIndex
273 );
274 } else if ( this.size() == 0 ) {
275 return new NullListIterator< V >();
276 } else {
277 final V[] objects = (V[]) new Object[ this.size() ];
278 System.arraycopy( this.store, this.firstIndex - 1, objects, 0, objects.length );
279 return new ArrayListIterator< V >( objects, true );
280 }
281 }
282
283 /**
284 * @see org.millscript.commons.util.IMap#size()
285 */
286 public int size() {
287 return this.lastIndex < this.firstIndex ? 0
288 : this.lastIndex - this.firstIndex + 1;
289 }
290
291 }
292
293 /**
294 * The backing store java.util.List.
295 */
296 private final List< V > store;
297
298 /**
299 * Constructs a new empty immutable java util list.
300 */
301 @SuppressWarnings( "unchecked" )
302 public IJavaUtilList() {
303 this.store = Collections.EMPTY_LIST;
304 }
305
306 /**
307 * Constructs a new immutable array list with a copy of the objects in the
308 * specified collection as its backing store. The lists order will be the
309 * same as the order the objects are returned by the objects iterator.
310 *
311 * @param col the backing java.util.Collection to copy as a list
312 */
313 public IJavaUtilList( final Collection< V > col ) {
314 this.store = new ArrayList< V >( col.size() );
315 try {
316 for ( Iterator< V > it = col.iterator(); it.hasNext(); ) {
317 this.store.add( it.next() );
318 }
319 } catch ( Exception ex ) {
320 throw new Fault(
321 "Failed to take a copy of the specified collection"
322 ).setParentThrowable( ex ).mishap();
323 }
324 }
325
326 /**
327 * Constructs a new immutable array list with a copy of the specified
328 * backing object array, starting and ending at the specified indices.
329 *
330 * @param objects the backing object array to copy
331 * @param start the index of the first element in the slice
332 * @param end the index of the last element in the slice. If end <
333 * start, the new list will be empty
334 */
335 @SuppressWarnings( "unchecked" )
336 public IJavaUtilList( final Collection< V > col, final int start, final int end ) {
337 if ( start > end ) {
338 this.store = Collections.EMPTY_LIST;
339 } else if ( start < 1 || start > col.size() ) {
340 throw new ListIndexOutOfBoundsAlert(
341 "First index in slice must be between 1 and the length of the collection"
342 ).culprit(
343 "index",
344 start
345 ).decorate( col ).mishap();
346 } else if ( end > col.size() ) {
347 throw new ListIndexOutOfBoundsAlert(
348 "Last index in slice must not be greater than the length of the collection"
349 ).culprit(
350 "index",
351 end
352 ).decorate( col ).mishap();
353 } else {
354 int n = 1;
355 this.store = new ArrayList< V >( start - end + 1 );
356 try {
357 for ( Iterator< V > it = col.iterator(); n <= end && it.hasNext(); n++ ) {
358 if ( n >= start ) {
359 this.store.add( it.next() );
360 }
361 }
362 } catch ( Exception ex ) {
363 throw new Fault(
364 "Failed to take a copy of the specified collection slice"
365 ).setParentThrowable( ex ).mishap();
366 }
367 }
368 }
369
370 /**
371 * Constructs a new immutable array list with a the specified list as its
372 * backing store.
373 *
374 * @param list the backing java.util.List
375 * @param share if <code>true</code> the specified object array will be
376 * shared otherwise the array will be copied.
377 */
378 @SuppressWarnings( "unchecked" )
379 public IJavaUtilList( final List< V > list, final boolean share ) {
380 if ( share ) {
381 this.store = list;
382 } else if ( list instanceof Cloneable ) {
383 try {
384 Class c = list.getClass();
385 Method m = c.getMethod( "clone" );
386 this.store = (List< V >) m.invoke( list );
387 } catch ( NoSuchMethodException ex ) {
388 throw new IllegalArgumentAlert(
389 "Trying to make an immutable list from a non-cloneable java.util.List instance"
390 ).culprit( "list type", list.getClass() ).culprit( "list", list ).mishap();
391 } catch ( IllegalAccessException ex ) {
392 throw new IllegalArgumentAlert(
393 "Trying to make an immutable list from a non-cloneable java.util.List instance"
394 ).culprit( "list type", list.getClass() ).culprit( "list", list ).mishap();
395 } catch ( InvocationTargetException ex ) {
396 throw new IllegalArgumentAlert(
397 "Trying to make an immutable list from a non-cloneable java.util.List instance"
398 ).culprit( "list type", list.getClass() ).culprit( "list", list ).mishap();
399 }
400 } else {
401 this.store = new ArrayList< V >( list.size() );
402 try {
403 for ( Iterator< V > it = list.iterator(); it.hasNext(); ) {
404 this.store.add( it.next() );
405 }
406 } catch ( Exception ex ) {
407 throw new Fault(
408 "Failed to take a copy of the specified collection"
409 ).setParentThrowable( ex ).mishap();
410 }
411 }
412 }
413
414 /**
415 * @see java.lang.Object#clone()
416 */
417 @Override
418 public Object clone() throws CloneNotSupportedException {
419
420 return super.clone();
421 }
422
423 /**
424 * @see org.millscript.commons.util.list.AbstractIList#doGet(int)
425 */
426 @Override
427 protected V doGet( final int pos ) {
428 return this.store.get( pos - 1 );
429 }
430
431 /**
432 * @see org.millscript.commons.util.list.AbstractIList#doSlice(int, int, boolean)
433 */
434 @Override
435 @SuppressWarnings( "unchecked" )
436 protected IList< V > doSlice( final int first, final int last, final boolean share ) {
437 if ( share ) {
438 return new ImmutableSharedJavaUtilList< V >( this.store, first, last );
439 } else {
440
441
442
443 return new IArrayList< V >(
444 (V[]) this.store.subList( first - 1, last ).toArray(),
445 true
446 );
447 }
448 }
449
450 /**
451 * @see org.millscript.commons.util.IList#indexOf(java.lang.Object)
452 */
453 public int indexOf( final V value ) {
454 return this.store.indexOf( value ) + 1;
455 }
456
457 /**
458 * @see org.millscript.commons.util.IMap#iterator(boolean)
459 */
460 @SuppressWarnings( "unchecked" )
461 public ListIterator< V > iterator( final boolean share ) {
462 if ( this.size() == 0 ) {
463 return new NullListIterator< V >();
464 } else if ( share ) {
465 return new JavaUtilCollectionListIterator< V >( this.store );
466 } else {
467 return new ArrayListIterator< V >( (V[]) this.store.toArray(), true );
468 }
469 }
470
471 /**
472 * @see org.millscript.commons.util.IMap#size()
473 */
474 public int size() {
475 return this.store.size();
476 }
477
478 }