package ns.foundation; import java.io.Serializable; import java.math.BigDecimal; import java.util.AbstractList; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.ConcurrentModificationException; import java.util.Enumeration; import java.util.Iterator; import java.util.List; import java.util.ListIterator; import java.util.NoSuchElementException; import java.util.RandomAccess; import java.util.Vector; public class NSArray<E> extends AbstractList<E> implements Cloneable, Serializable, NSKeyValueCoding, NSKeyValueCodingAdditions, _NSFoundationCollection, List<E> { public static class _AvgNumberOperator extends _Operator implements Operator { @Override public Object compute(NSArray<?> values, String keyPath) { int count = values.count(); if (count != 0) { BigDecimal sum = _sum(values, keyPath); return sum.divide(new BigDecimal(count), sum.scale() + 4, 6); } return null; } } public static class _SumNumberOperator extends _Operator implements Operator { @Override public Object compute(NSArray<?> values, String keyPath) { return _sum(values, keyPath); } } public static class _MinOperator extends _Operator implements Operator { @Override public Object compute(NSArray<?> values, String keyPath) { Object min = null; for (Object obj : values) { min = _minOrMaxValue(min, _operationValue(obj, keyPath), false); } return min; } } public static class _MaxOperator extends _Operator implements Operator { @Override public Object compute(NSArray<?> values, String keyPath) { Object max = null; for (Object obj : values) { max = _minOrMaxValue(max, _operationValue(obj, keyPath), true); } return max; } } public static class _Operator { protected Object _operationValue(Object object, String keyPath) { return keyPath == null || keyPath.length() <= 0 ? object : NSKeyValueCodingAdditions.Utility.valueForKeyPath(object, keyPath); } private BigDecimal _bigDecimalForValue(Object object) { if (object != null) { if (object instanceof Number) return new BigDecimal(object.toString()); if (object instanceof Boolean) return new BigDecimal((Boolean) object ? 1 : 0); if (object instanceof String) return new BigDecimal((String) object); throw new IllegalStateException("Can't convert " + object + " (class " + object.getClass().getName() + ") into number"); } return null; } BigDecimal _sum(NSArray<?> values, String keyPath) { BigDecimal sum = _BigDecimalZero; for (Object obj : values) { BigDecimal value = _bigDecimalForValue(_operationValue(obj, keyPath)); if (value != null) sum = sum.add(value); } return sum; } @SuppressWarnings("unchecked") Object _minOrMaxValue(Object referenceValue, Object compareValue, boolean trueForMaxAndFalseForMin) { if (referenceValue == null) return compareValue; if (compareValue == null) return referenceValue; int comparison; if (referenceValue instanceof Number || referenceValue instanceof Boolean) { Comparable<?> refValue = (Comparable<?>) referenceValue; if (referenceValue instanceof Boolean) refValue = ((Boolean) referenceValue) ? 1 : 0; Comparable<?> compValue = (Comparable<?>) compareValue; if (compareValue instanceof Boolean) compValue = ((Boolean) compareValue) ? 1 : 0; comparison = ((Comparable<Object>) refValue).compareTo(compValue); } else if (referenceValue instanceof NSTimestamp) { comparison = ((NSTimestamp) referenceValue).compare((NSTimestamp) compareValue); } else if (referenceValue instanceof Comparable) { comparison = ((Comparable<Object>) referenceValue).compareTo(compareValue); } else { throw new IllegalStateException("Cannot compare values " + referenceValue + " and " + compareValue + " (they are not instance of Comparable"); } if (trueForMaxAndFalseForMin) { if (comparison >= 0) { return referenceValue; } } else if (comparison <= 0) { return referenceValue; } return compareValue; } } public static class _CountOperator implements Operator { @Override public Object compute(NSArray<?> values, String keyPath) { return values.count(); } } public static interface Operator { public Object compute(NSArray<?> values, String keyPath); } private static final char _OperatorIndicatorChar = '@'; public static final String AverageOperatorName = "avg"; public static final String CountOperatorName = "count"; public static final String MaximumOperatorName = "max"; public static final String MinimumOperatorName = "min"; public static final String SumOperatorName = "sum"; public static final int NotFound = -1; protected static final BigDecimal _BigDecimalZero = new BigDecimal(0); static final long serialVersionUID = -3789592578296478260L; @SuppressWarnings("rawtypes") public static final NSArray EmptyArray = new NSArray(); public static final boolean CheckForNull = true; public static final boolean IgnoreNull = true; public static final boolean NoCopy = true; protected static final String NULL_NOT_ALLOWED = "Attempt to insert null into an NSArray."; protected static final String NULL_NOT_SUPPORTED = "NSArray does not support null values"; private static NSMutableDictionary<String, Operator> _operators = new NSMutableDictionary<String, Operator>(8); private List<E> _backingStore; static { try { setOperatorForKey(CountOperatorName, new _CountOperator()); setOperatorForKey(MaximumOperatorName, new _MaxOperator()); setOperatorForKey(MinimumOperatorName, new _MinOperator()); setOperatorForKey(SumOperatorName, new _SumNumberOperator()); setOperatorForKey(AverageOperatorName, new _AvgNumberOperator()); } catch (Throwable e) { NSLog.err.appendln("Exception occurred in initializer"); if (NSLog.debugLoggingAllowedForLevel(1)) { NSLog.debug.appendln(e); } throw NSForwardException._runtimeExceptionForThrowable(e); } } public NSArray() { _initializeListWithCapacity(0); } protected NSArray(int capacity) { _initializeListWithCapacity(capacity); } public NSArray(E object) { if (object == null) throw new IllegalArgumentException(NULL_NOT_ALLOWED); _initializeListWithCapacity(1).add(object); } public NSArray(E... objects) { _initializeWithObjects(objects, new NSRange(0, objects != null ? objects.length : 0), NullHandling.CheckAndSkip); } public NSArray(E[] objects, NSRange range) { _initializeWithObjects(objects, range != null ? range : NSRange.ZeroRange, NullHandling.CheckAndFail); } public NSArray(NSArray<? extends E> otherArray) { _initializeWithList(otherArray, otherArray == null ? null : new NSRange(0, otherArray.size()), NullHandling.NoCheck, !NoCopy); } public NSArray(Collection<? extends E> collection, boolean checkForNull) { if (collection == null) throw new NullPointerException("collection may not be null"); _initializeWithCollection(collection, checkForNull ? NullHandling.CheckAndFail : NullHandling.NoCheck); } public NSArray(Collection<? extends E> collection) { this(collection, true); } public NSArray(List<? extends E> list, boolean checkForNull) { if (list == null) throw new NullPointerException("list may not be null"); _initializeWithList(list, new NSRange(0, list.size()), checkForNull ? NullHandling.CheckAndFail : NullHandling.NoCheck, !NoCopy); } public NSArray(List<? extends E> list, NSRange range, boolean ignoreNull) { if (list == null) throw new IllegalArgumentException("list may not be null"); _initializeWithList(list, range != null ? range : NSRange.ZeroRange, ignoreNull ? NullHandling.CheckAndSkip : NullHandling.CheckAndFail, !NoCopy); } protected List<E> _initializeListWithCapacity(int capacity) { List<E> list = new ArrayList<E>(capacity); _setList(Collections.unmodifiableList(list)); return list; } protected void _initializeWithCapacity(int capacity) { _initializeListWithCapacity(capacity); } protected void _initializeWithObjects(E[] objects, NSRange range, NullHandling nullHandling) { if (objects == null) { if (range == null || range.length() == 0) { _initializeListWithCapacity(0); return; } throw new NullPointerException("objects cannot be null"); } _initializeWithList(Arrays.asList(objects), range, nullHandling, !NoCopy); } @SuppressWarnings("unchecked") protected void _initializeWithCollection(Collection<? extends E> collection, NullHandling nullHandling) { if (collection == null) { _initializeListWithCapacity(0); return; } if (collection instanceof List) { _initializeWithList((List<E>) collection, new NSRange(0, collection.size()), nullHandling, !NoCopy); } else { List<E> store = _initializeListWithCapacity(collection.size()); if (nullHandling == NullHandling.NoCheck || collection instanceof _NSFoundationCollection) { store.addAll(collection); return; } for (E element : collection) { if (element == null) { if (nullHandling == NullHandling.CheckAndFail) throw new IllegalArgumentException(NULL_NOT_ALLOWED); continue; } store.add(element); } } } protected void _initializeWithList(List<? extends E> list, NSRange range, NullHandling nullHandling, boolean noCopy) { if (list == null && (range == null || range.length() == 0)) { _initializeListWithCapacity(0); return; } if (list == null) throw new NullPointerException("list cannot be null"); if (list instanceof _NSFoundationCollection || nullHandling == NullHandling.NoCheck) { List<? extends E> subList = list; if (range.location() != 0 || range.length() != list.size()) { /* GWT has no java.util.List.subList so use ours */ subList = asNSArray(list, NullHandling.NoCheck).subList(range.location(), range.maxRange()); } if (noCopy) { _setList(subList); } else { _initializeListWithCapacity(subList.size()).addAll(subList); } } else { List<E> store = _initializeListWithCapacity(list.size()); for (int i = range.location(); i < range.maxRange(); i++) { E element = list.get(i); if (element == null) { if (nullHandling == NullHandling.CheckAndFail || nullHandling != NullHandling.CheckAndSkip) throw new IllegalArgumentException(NULL_NOT_ALLOWED); continue; } store.add(element); } } } protected List<E> listNoCopy() { return _backingStore; } @SuppressWarnings("unchecked") protected List<E> _setList(List<? extends E> list) { return _backingStore = (List<E>) list; } @SuppressWarnings("serial") static class RandomAccessNSArray<E> extends NSArray<E> implements RandomAccess { } public static <E> NSArray<E> asNSArray(E... objects) { return asNSArray(objects, NullHandling.CheckAndFail); } public static <E> NSArray<E> asNSArray(List<E> list) { return asNSArray(list, NullHandling.CheckAndFail); } public static <E> NSArray<E> asNSArray(E[] array, NullHandling nullHandling) { return asNSArray(Arrays.asList(array), nullHandling); } public static <E> NSArray<E> asNSArray(List<E> list, NullHandling nullHandling) { return asNSArray(list, list == null ? null : new NSRange(0, list.size()), nullHandling); } public static <E> NSArray<E> asNSArray(List<E> list, NSRange range, NullHandling nullHandling) { if (list == null || list.size() == 0) return emptyArray(); if (list.getClass() == NSArray.class && range != null) { /* GWT has no java.util.List.subList so use ours */ return (NSArray<E>) ((NSArray<E>)list).subList(range.location(), range.maxRange()); } NSArray<E> array = list instanceof RandomAccess ? new RandomAccessNSArray<E>() : new NSArray<E>(); array._initializeWithList(Collections.unmodifiableList(list), range, nullHandling, NoCopy); return array; } public NSArray<E> arrayByAddingObject(E object) { if (object == null) throw new IllegalArgumentException("object may not be null"); NSMutableArray<E> result = this.mutableClone(); result.addObject(object); return result; } public NSArray<E> arrayByAddingObjectsFromArray(NSArray<E> otherArray) { if (otherArray == null || otherArray.count() == 0) return new NSArray<E>(this); NSMutableArray<E> result = this.mutableClone(); result.addObjectsFromArray(otherArray); return result; } public ArrayList<E> arrayList() { return new ArrayList<E>(this); } @Override public NSArray<E> clone() { return this; } public String componentsJoinedByString(String separator) { StringBuffer result = new StringBuffer(); for (int i = 0; i < size(); i++) { result.append(objectAtIndex(i).toString()); if (i < size() - 1 && separator != null) result.append(separator); } return result.toString(); } public static NSArray<String> componentsSeparatedByString(String string, String separator) { NSMutableArray<String> objects; if ((string == null) || (string.length() == 0)) { return NSArray.emptyArray(); } int stringLength = string.length(); if ((separator == null) || (separator.length() == 0)) { return new NSArray<String>(string); } int separatorLength = separator.length(); int start = 0; int index = 0; int count = 0; if ((separatorLength == 1) && (stringLength < 256)) { char[] parseData = string.toCharArray(); char charSeparator = separator.charAt(0); for (int i = 0; i < stringLength; i++) { if (parseData[i] == charSeparator) { count++; } } if (count == 0) { return new NSArray<String>(string); } objects = new NSMutableArray<String>(count + 1); int end = stringLength - 1; for (index = 0; index <= end; ++index) { if (parseData[index] == charSeparator) { if (start == index) { objects.addObject(""); } else { objects.addObject(string.substring(start, index)); } start = index + 1; } } if (parseData[end] == charSeparator) { objects.addObject(""); } else { objects.addObject(string.substring(start, stringLength)); } } else { objects = new NSMutableArray<String>(4); int end = stringLength - separatorLength; while (true) { if (start >= stringLength) { return objects; } index = string.indexOf(separator, start); if (index < 0) { index = stringLength; } if (index == end) { break; } objects.addObject(string.substring(start, index)); start = index + separatorLength; } if (start <= index) { objects.addObject(string.substring(start, index)); } objects.addObject(""); } return objects; } public static NSMutableArray<String> _mutableComponentsSeparatedByString(String string, String separator) { return componentsSeparatedByString(string, separator).mutableClone(); } public boolean containsObject(Object object) { if (object == null) return false; return listNoCopy().contains(object); } public int count() { return listNoCopy().size(); } @SuppressWarnings("unchecked") public static <T> NSArray<T> emptyArray() { return EmptyArray; } public E firstObjectCommonWithArray(NSArray<? extends E> otherArray) { if (otherArray == null || otherArray.isEmpty()) return null; NSSet<E> set = new NSSet<E>(otherArray); for (E e : this) { if (set.containsObject(e)) return e; } return null; } @Override public int _shallowHashCode() { return NSArray.class.hashCode(); } @Override public int hashCode() { int hash = 1; int index = 0; Iterator<E> i = iterator(); while (i.hasNext() && index <= 16) { E element = i.next(); index++; if (element instanceof _NSFoundationCollection) { hash ^= ((_NSFoundationCollection) element)._shallowHashCode(); } else { hash ^= element.hashCode(); } } return hash; } public NSArray<E> immutableClone() { return new NSArray<E>(this); } public int indexOfIdenticalObject(Object object) { if (object == null) return NotFound; for (int i = 0; i < size(); i++) { if (objectAtIndex(i) == object) return i; } return NotFound; } public int indexOfIdenticalObject(Object object, NSRange range) { if (object == null || range == null) return NotFound; if (range.maxRange() > count()) { throw new IllegalArgumentException("Range [" + range.location() + "; " + range.length() + "] out of bounds [0, " + (count() - 1) + "]"); } NSArray<E> subArray = subarrayWithRange(range); return subArray.indexOfIdenticalObject(object) + range.location(); } public int indexOfObject(Object object) { if (object == null) { return NotFound; } return listNoCopy().indexOf(object); } public int indexOfObject(Object object, NSRange range) { if (object == null || range == null) return NotFound; if (range.maxRange() > count()) throw new IllegalArgumentException("range exceeds array dimensions"); NSArray<E> subArray = subarrayWithRange(range); return subArray.indexOfObject(object) + range.location(); } public boolean isEqualToArray(NSArray<?> otherArray) { return equals(otherArray); } public E lastObject() { int count = count(); return count != 0 ? objectAtIndex(count - 1) : null; } public void makeObjectsPerformSelector(NSSelector<?> selector, Object... parameters) { if (selector == null) { throw new IllegalArgumentException("Selector cannot be null"); } for (E element : this) { NSSelector._safeInvokeSelector(selector, element, parameters); } } public NSMutableArray<E> mutableClone() { return new NSMutableArray<E>(this); } public E objectAtIndex(int index) { if (index >= count()) throw new IndexOutOfBoundsException("Index " + index + " is out of bounds"); return listNoCopy().get(index); } public Enumeration<E> objectEnumerator() { return Collections.enumeration(this); } public Object[] objects() { return toArray(); } public Object[] objects(NSRange range) { if (range == null || range.length() == 0) return new Object[0]; return subarrayWithRange(range).toArray(); } protected Object[] objectsNoCopy() { Object[] objs = listNoCopy().toArray(); return objs != null ? objs : _NSCollectionPrimitives.EmptyArray; } public static NSArray<String> operatorNames() { NSArray<String> operatorNames; synchronized (_operators) { operatorNames = _operators.allKeys(); } return operatorNames; } public static void setOperatorForKey(String operatorName, Operator arrayOperator) { if (operatorName == null) { throw new IllegalArgumentException("Operator key cannot be null"); } if (arrayOperator == null) { throw new IllegalArgumentException("Operator cannot be null for " + operatorName); } synchronized (_operators) { _operators.setObjectForKey(arrayOperator, operatorName); } } public static Operator operatorForKey(String operatorName) { Operator arrayOperator; synchronized (_operators) { arrayOperator = _operators.objectForKey(operatorName); } return arrayOperator; } public static void removeOperatorForKey(String operatorName) { if (operatorName != null) { synchronized (_operators) { _operators.removeObjectForKey(operatorName); } } } public Enumeration<E> reverseObjectEnumerator() { return new ListReverseEnumeration(); } public NSArray<E> sortedArrayUsingComparator(NSComparator<? super E> comparator) throws NSComparator.ComparisonException { if (comparator == null) throw new IllegalArgumentException("Comparator not specified"); NSArray<E> result = mutableClone(); Collections.sort(result, comparator); return result.immutableClone(); } public NSArray<E> subarrayWithRange(NSRange range) { if (range == null || range.length() == 0) return NSArray.emptyArray(); return asNSArray(subList(range.location(), range.maxRange()), NullHandling.NoCheck); } @Override public Object valueForKey(String key) { if (key != null) { if (key.charAt(0) == _OperatorIndicatorChar) { return _valueForKeyPathWithOperator(key); } if (key.equals(CountOperatorName)) { return count(); } } NSMutableArray<Object> values = new NSMutableArray<Object>(size()); for (E element : this) { Object value = NSKeyValueCodingAdditions.Utility.valueForKeyPath(element, key); values.addObject(value == null ? ((Object) (NSKeyValueCoding.NullValue)) : value); } return values; } @Override public void takeValueForKey(Object value, String key) { for (E element : this) NSKeyValueCodingAdditions.Utility.takeValueForKeyPath(element, value, key); } @Override public Object valueForKeyPath(String keyPath) { if (keyPath != null && keyPath.charAt(0) == _OperatorIndicatorChar) return _valueForKeyPathWithOperator(keyPath); return NSKeyValueCodingAdditions.DefaultImplementation.valueForKeyPath(this, keyPath); } private Object _valueForKeyPathWithOperator(String keyPath) { int index = keyPath.indexOf('.'); String operatorName; String operatorPath; if (index < 0) { operatorName = keyPath.substring(1); operatorPath = ""; } else { operatorName = keyPath.substring(1, index); operatorPath = index >= keyPath.length() - 1 ? "" : keyPath.substring(index + 1); } Operator arrayOperator = operatorForKey(operatorName); if (arrayOperator != null) { return arrayOperator.compute(this, operatorPath); } throw new IllegalArgumentException("No key operator available to compute aggregate " + keyPath); } @Override public void takeValueForKeyPath(Object value, String keyPath) { if (keyPath == null) return; for (E element : this) { if (element instanceof NSKeyValueCodingAdditions) { ((NSKeyValueCodingAdditions) element).takeValueForKeyPath(value, keyPath); } } } public Vector<E> vector() { return new Vector<E>(this); } /* Java Collection methods */ @Override public boolean contains(Object element) { if (element == null) { throw new NullPointerException(NULL_NOT_SUPPORTED); } return containsObject(element); } @Override public boolean containsAll(Collection<?> c) { if (c == null) throw new NullPointerException(NULL_NOT_SUPPORTED); List<?> list = listNoCopy(); if (c == this || c == list) return true; if (c instanceof NSArray<?> && ((NSArray<?>) c).listNoCopy() == list) return true; return list.containsAll(c); } @Override public E get(int index) { return objectAtIndex(index); } @Override public int indexOf(Object o) { if (o == null) { throw new NullPointerException(NULL_NOT_SUPPORTED); } return listNoCopy().indexOf(o); } @Override public int lastIndexOf(Object o) { if (o == null) { throw new NullPointerException(NULL_NOT_SUPPORTED); } return listNoCopy().lastIndexOf(o); } @Override public int size() { return count(); } @Override public List<E> subList(int fromIndex, int toIndex) { if (fromIndex < 0 || toIndex > size() || fromIndex > toIndex) { throw new IndexOutOfBoundsException("Illegal index value (fromIndex < 0 || toIndex > size || fromIndex > toIndex)"); } if (fromIndex == 0 && toIndex == size()) { return this; } /* GWT doesn't implement java.util.List.subList so we can't delegate to super */ return (listNoCopy() instanceof RandomAccess ? new RandomAccessSubList<E>(this, fromIndex, toIndex) : new SubList<E>(this, fromIndex, toIndex)); } /* Because we implement our own SubList we need to redeclare removeRange() and modCount */ @Override protected void removeRange(int fromIndex, int toIndex) { super.removeRange(fromIndex, toIndex); } protected transient int modCount; private class ListReverseEnumeration implements Enumeration<E> { private int currentIndex = size(); public ListReverseEnumeration() { super(); } @Override public boolean hasMoreElements() { return currentIndex > 0; } @Override public E nextElement() { if (hasMoreElements()) { currentIndex--; return get(currentIndex); } throw new NoSuchElementException(); } } } /* Copied from java.util.AbstractList because GWT doesn't implement it */ class SubList<E> extends NSArray<E> { final NSArray<E> array; int offset; int size; int expectedModCount; SubList(NSArray<E> list, int fromIndex, int toIndex) { if (fromIndex < 0) throw new IndexOutOfBoundsException("fromIndex = " + fromIndex); if (toIndex > list.size()) throw new IndexOutOfBoundsException("toIndex = " + toIndex); if (fromIndex > toIndex) throw new IllegalArgumentException("fromIndex(" + fromIndex + ") > toIndex(" + toIndex + ")"); array = list; offset = fromIndex; size = toIndex - fromIndex; expectedModCount = list.modCount; } @Override public boolean add(E o) { add(size, o); return true; }; @Override public void add(int index, E element) { if (index < 0 || index > size) throw new IndexOutOfBoundsException(); checkForComodification(); array.add(index + offset, element); expectedModCount = array.modCount; size++; modCount++; } @Override public boolean addAll(Collection<? extends E> c) { return addAll(size, c); } @Override public boolean addAll(int index, Collection<? extends E> c) { if (index < 0 || index > size) throw new IndexOutOfBoundsException("Index: " + index + ", Size: " + size); int cSize = c.size(); if (cSize == 0) return false; checkForComodification(); array.addAll(offset + index, c); expectedModCount = array.modCount; size += cSize; modCount++; return true; } @Override public void clear() { removeRange(0, size()); } @Override public boolean contains(Object o) { for (E e : this) { if (e == o || (e.hashCode() == o.hashCode() && e.equals(0))) { return true; } } return false; } @Override public boolean containsAll(Collection<?> c) { Iterator<?> e = c.iterator(); while (e.hasNext()) { Object o = e.next(); if (o == null || !contains(o)) return false; } return true; } @Override public boolean equals(Object obj) { if (obj == this || obj == listNoCopy()) return true; if (obj instanceof NSArray<?> && listNoCopy() == ((NSArray<?>) obj).listNoCopy()) return true; return super.equals(obj); } @Override public int hashCode() { return super.hashCode(); } @Override public E get(int index) { rangeCheck(index); checkForComodification(); return array.get(index + offset); } @Override public int indexOf(Object o) { ListIterator<E> e = listIterator(); if (o == null) { return NotFound; } while (e.hasNext()) if (o.equals(e.next())) return e.previousIndex(); return NotFound; } @Override public boolean isEmpty() { return size == 0; } @Override public int lastIndexOf(Object o) { ListIterator<E> e = listIterator(size()); if (o == null) return NotFound; while (e.hasPrevious()) if (o.equals(e.previous())) return e.nextIndex(); return NotFound; } @Override public boolean removeAll(Collection<?> c) { boolean modified = false; Iterator<?> e = iterator(); while (e.hasNext()) { if (c.contains(e.next())) { e.remove(); modified = true; } } return modified; } @Override public boolean retainAll(Collection<?> c) { boolean modified = false; Iterator<E> e = iterator(); while (e.hasNext()) { if (!c.contains(e.next())) { e.remove(); modified = true; } } return modified; } @Override public E remove(int index) { rangeCheck(index); checkForComodification(); E result = array.remove(index + offset); expectedModCount = array.modCount; size--; modCount++; return result; } @Override public boolean remove(Object o) { if (o == null) throw new NullPointerException("com.webobjects.foundation.NSArray does not support null values"); Iterator<?> e = iterator(); while (e.hasNext()) { if (o.equals(e.next())) { e.remove(); return true; } } return false; } @Override public E set(int index, E element) { rangeCheck(index); checkForComodification(); return array.set(index + offset, element); } @Override public int size() { checkForComodification(); return size; } @Override protected void removeRange(int fromIndex, int toIndex) { checkForComodification(); array.removeRange(fromIndex + offset, toIndex + offset); expectedModCount = array.modCount; size -= (toIndex - fromIndex); modCount++; } @Override public Iterator<E> iterator() { return listIterator(); } @Override public ListIterator<E> listIterator(final int index) { checkForComodification(); if (index < 0 || index > size) throw new IndexOutOfBoundsException("Index: " + index + ", Size: " + size); return new ListIterator<E>() { private ListIterator<E> i = array.listIterator(index + offset); @Override public boolean hasNext() { return nextIndex() < size; } @Override public E next() { if (hasNext()) { return i.next(); } throw new NoSuchElementException(); } @Override public boolean hasPrevious() { return previousIndex() >= 0; } @Override public E previous() { if (hasPrevious()) { return i.previous(); } throw new NoSuchElementException(); } @Override public int nextIndex() { return i.nextIndex() - offset; } @Override public int previousIndex() { return i.previousIndex() - offset; } @Override public void remove() { i.remove(); expectedModCount = array.modCount; size--; modCount++; } @Override public void set(E o) { i.set(o); } @Override public void add(E o) { i.add(o); expectedModCount = array.modCount; size++; modCount++; } }; } @Override public NSArray<E> subList(int fromIndex, int toIndex) { return new SubList<E>(this, fromIndex, toIndex); } private void rangeCheck(int index) { if (index < 0 || index >= size) throw new IndexOutOfBoundsException("Index: " + index + ",Size: " + size); } private void checkForComodification() { if (array.modCount != expectedModCount) throw new ConcurrentModificationException(); } } class RandomAccessSubList<E> extends SubList<E> implements RandomAccess { RandomAccessSubList(NSArray<E> list, int fromIndex, int toIndex) { super(list, fromIndex, toIndex); } @Override public NSArray<E> subList(int fromIndex, int toIndex) { return new RandomAccessSubList<E>(this, fromIndex, toIndex); } }