package er.extensions.foundation; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.util.ArrayList; import java.util.Collection; import java.util.Enumeration; import java.util.List; import java.util.Vector; import com.webobjects.foundation.NSArray; import com.webobjects.foundation.NSCoder; import com.webobjects.foundation.NSComparator; import com.webobjects.foundation.NSComparator.ComparisonException; import com.webobjects.foundation.NSData; import com.webobjects.foundation.NSForwardException; import com.webobjects.foundation.NSMutableArray; import com.webobjects.foundation.NSPropertyListSerialization; import com.webobjects.foundation.NSRange; import com.webobjects.foundation.NSSelector; /** * Custom subclass of NSMutableArray. Implements {@link java.util.List} and can * be used as a EOF custom value class because it can automatically en- and * decode an NSMutableArray as blob into a database. NOTE: As the List * implementation is based on the NSMutableArray implementation, care must be * taken when subclassing; it is best if you use only List-methods when * extending List-methods and NSArray methods in other cases. Otherwise you will * most likely get into stack overflows. NOTE: List allows for NULL values, * NSMutableArray does not. Therefore you can't use NULL objects. The * ERPrototype name is <code>mutableArray</code> */ public class ERXMutableArray<E> extends NSMutableArray<E> implements List<E> { public static final long serialVersionUID = -6581075256974648875L; public ERXMutableArray() { super(); } public ERXMutableArray(Collection<? extends E> c) { super((E[])c.toArray()); } public ERXMutableArray(NSArray<? extends E> array) { super(array); } public ERXMutableArray(int i) { super(i); } public ERXMutableArray(E obj) { super(obj); } public ERXMutableArray(E aobj[]) { super(aobj); } public ERXMutableArray(E objects[], NSRange range) { super(objects, range); } public ERXMutableArray(Vector<? extends E> vector, NSRange range, boolean flag) { super(vector, range, flag); } public static NSData toBlob(NSArray<?> d) { try (ByteArrayOutputStream bout = new ByteArrayOutputStream(); ObjectOutputStream oos = new ObjectOutputStream(bout)) { oos.writeObject(d); NSData sp = new NSData(bout.toByteArray()); return sp; } catch (IOException e) { // shouldn't ever happen, as we only write to memory throw NSForwardException._runtimeExceptionForThrowable(e); } } public static NSData toBlob(NSMutableArray<?> d) { return toBlob((NSArray<?>) d); } @SuppressWarnings("unchecked") public static NSArray fromBlob(NSData d) { try (ByteArrayInputStream bis = new ByteArrayInputStream(d.bytes()); ObjectInputStream ois = new ERXMappingObjectStream(bis)) { NSArray<?> dd = (NSArray<?>) ois.readObject(); return dd; } catch (IOException e) { // shouldn't ever happen, as we only read from memory throw NSForwardException._runtimeExceptionForThrowable(e); } catch (ClassNotFoundException e) { // might happen, but it doesn't help us much to know it throw NSForwardException._runtimeExceptionForThrowable(e); } } @SuppressWarnings("unchecked") public static NSArray fromPropertyList(String arrayAsString) { NSArray<?> a = (NSArray<?>) NSPropertyListSerialization.propertyListFromString(arrayAsString); return new ERXMutableArray<Object>(a); } public static String toPropertyList(NSArray<?> array) { return NSPropertyListSerialization.stringFromPropertyList(array); } public NSData toBlob() { return toBlob(this); } public String toPropertyList() { return toPropertyList(this); } @Override public ERXMutableArray<E> mutableClone() { return new ERXMutableArray<E>(this); } public String[] toStringArray() { return ERXArrayUtilities.toStringArray(this); } /** * Simple thread safe wrapper. May or may not be correct, but it doesn't * matter as you will never, *ever* call this directly, but call <code> * ERXMutableArray.synchronizedArray(); * </code> instead and we will fix all the bugs in due time. * @author ak * */ public static class ThreadSafeArray<V> extends ERXMutableArray<V> { /** * Do I need to update serialVersionUID? * See section 5.6 <cite>Type Changes Affecting Serialization</cite> on page 51 of the * <a href="http://java.sun.com/j2se/1.4/pdf/serial-spec.pdf">Java Object Serialization Spec</a> */ private static final long serialVersionUID = 1L; public ThreadSafeArray(NSArray<? extends V> array) { super(array); } @Override public synchronized void _moveObjectAtIndexToIndex(int sourceIndex, int destIndex) { super._moveObjectAtIndexToIndex(sourceIndex, destIndex); } @Override public synchronized void addObject(Object object) { super.addObject((V) object); } @Override public synchronized void addObjects(Object... objects) { super.addObjects((V[])objects); } @Override public synchronized void addObjectsFromArray(NSArray<? extends V> otherArray) { super.addObjectsFromArray(otherArray); } @Override public synchronized Object clone() { return new ThreadSafeArray<V>(this); } @Override public synchronized NSArray<V> immutableClone() { return super.immutableClone(); } @Override public synchronized void insertObjectAtIndex(V object, int index) { super.insertObjectAtIndex(object, index); } @Override public synchronized void removeAllObjects() { super.removeAllObjects(); } @Override public synchronized boolean removeIdenticalObject(Object object, NSRange range) { return super.removeIdenticalObject(object, range); } @Override public synchronized boolean removeIdenticalObject(Object object) { return super.removeIdenticalObject(object); } @Override public synchronized V removeLastObject() { return super.removeLastObject(); } @Override public synchronized boolean removeObject(Object object, NSRange range) { return super.removeObject(object, range); } @Override public synchronized boolean removeObject(Object object) { return super.removeObject(object); } @Override public synchronized V removeObjectAtIndex(int index) { return super.removeObjectAtIndex(index); } @Override public synchronized void removeObjects(Object... objects) { super.removeObjects(objects); } @Override public synchronized void removeObjectsInArray(NSArray<?> otherArray) { super.removeObjectsInArray(otherArray); } @Override public synchronized void removeObjectsInRange(NSRange range) { super.removeObjectsInRange(range); } @Override public synchronized V replaceObjectAtIndex(V object, int index) { return super.replaceObjectAtIndex(object, index); } @Override public synchronized void replaceObjectsInRange(NSRange range, NSArray otherArray, NSRange otherRange) { super.replaceObjectsInRange(range, otherArray, otherRange); } @Override public synchronized void setArray(NSArray<? extends V> otherArray) { super.setArray(otherArray); } @Override public synchronized void sortUsingComparator(NSComparator comparator) throws ComparisonException { super.sortUsingComparator(comparator); } @Override public synchronized int _shallowHashCode() { return super._shallowHashCode(); } @Override public synchronized NSArray<V> arrayByAddingObject(V object) { return super.arrayByAddingObject(object); } @Override public synchronized NSArray<V> arrayByAddingObjectsFromArray(NSArray<? extends V> otherArray) { return super.arrayByAddingObjectsFromArray(otherArray); } @Override public synchronized ArrayList<V> arrayList() { V[] objects = objectsNoCopy(); ArrayList<V> list = new ArrayList<V>(objects.length); for(int i = 0; i < objects.length; i++) { list.add(objects[i]); } return list; } @Override @SuppressWarnings("unchecked") public synchronized Class classForCoder() { return super.classForCoder(); } @Override public synchronized String componentsJoinedByString(String separator) { return super.componentsJoinedByString(separator); } @Override public synchronized boolean containsObject(Object object) { return super.containsObject(object); } @Override public synchronized int count() { return super.count(); } @Override public synchronized void encodeWithCoder(NSCoder coder) { super.encodeWithCoder(coder); } @Override public synchronized boolean equals(Object object) { return super.equals(object); } @Override public synchronized V firstObjectCommonWithArray(NSArray<?> otherArray) { return super.firstObjectCommonWithArray(otherArray); } @Override public synchronized int hashCode() { return super.hashCode(); } @Override public synchronized int indexOfIdenticalObject(Object object) { return super.indexOfIdenticalObject(object); } @Override public synchronized int indexOfIdenticalObject(Object object, NSRange range) { return super.indexOfIdenticalObject(object, range); } @Override public synchronized int indexOfObject(Object object) { return super.indexOfObject(object); } @Override public synchronized int indexOfObject(Object object, NSRange range) { return super.indexOfObject(object, range); } @Override public synchronized boolean isEqualToArray(NSArray otherArray) { return super.isEqualToArray(otherArray); } @Override public synchronized V lastObject() { return super.lastObject(); } @Override public synchronized void makeObjectsPerformSelector(NSSelector selector, Object... parameters) { super.makeObjectsPerformSelector(selector, parameters); } @Override public synchronized V objectAtIndex(int index) { return super.objectAtIndex(index); } @Override public synchronized Enumeration<V> objectEnumerator() { return super.objectEnumerator(); } @Override public synchronized V[] objects() { return (V[])super.objects(); } @Override public synchronized Object[] objects(NSRange range) { return super.objects(range); } @Override protected synchronized V[] objectsNoCopy() { return (V[]) super.objectsNoCopy(); } @Override public synchronized Enumeration<V> reverseObjectEnumerator() { return super.reverseObjectEnumerator(); } @Override public synchronized NSArray<V> sortedArrayUsingComparator(NSComparator comparator) throws ComparisonException { return super.sortedArrayUsingComparator(comparator); } @Override public synchronized NSArray<V> subarrayWithRange(NSRange range) { return super.subarrayWithRange(range); } @Override public synchronized void takeValueForKey(Object value, String key) { super.takeValueForKey(value, key); } @Override public synchronized void takeValueForKeyPath(Object value, String keyPath) { super.takeValueForKeyPath(value, keyPath); } @Override public synchronized String toString() { return super.toString(); } @Override public synchronized Object valueForKey(String key) { return super.valueForKey(key); } @Override public synchronized Object valueForKeyPath(String keyPath) { return super.valueForKeyPath(keyPath); } @Override public synchronized Vector<V> vector() { return super.vector(); } } public static <T> NSMutableArray<T> synchronizedArray() { return new ThreadSafeArray<T>(new ERXMutableArray<T>()); } public static <T> NSArray<T> synchronizedArray(NSArray<T> array) { if(!(array instanceof NSMutableArray)) { return array; } return new ThreadSafeArray(array); } public static <T> NSMutableArray<T> synchronizedArray(NSMutableArray<T> array) { return new ThreadSafeArray<T>(array); } }