summaryrefslogtreecommitdiff
path: root/projects/net.wotonomy.foundation/src/main
diff options
context:
space:
mode:
authorBenjamin Culkin <scorpress@gmail.com>2024-05-19 17:56:33 -0400
committerBenjamin Culkin <scorpress@gmail.com>2024-05-19 17:56:33 -0400
commitaedc34d55462a75e329bbf342251ff6504cd117e (patch)
treebcc8f1f2352582717b484df302aeea6696b8f000 /projects/net.wotonomy.foundation/src/main
Initial import from SVN
Diffstat (limited to 'projects/net.wotonomy.foundation/src/main')
-rw-r--r--projects/net.wotonomy.foundation/src/main/java/net/wotonomy/foundation/NSArray.java661
-rw-r--r--projects/net.wotonomy.foundation/src/main/java/net/wotonomy/foundation/NSBundle.java418
-rw-r--r--projects/net.wotonomy.foundation/src/main/java/net/wotonomy/foundation/NSCoder.java113
-rw-r--r--projects/net.wotonomy.foundation/src/main/java/net/wotonomy/foundation/NSCoding.java371
-rw-r--r--projects/net.wotonomy.foundation/src/main/java/net/wotonomy/foundation/NSComparator.java104
-rw-r--r--projects/net.wotonomy.foundation/src/main/java/net/wotonomy/foundation/NSData.java263
-rw-r--r--projects/net.wotonomy.foundation/src/main/java/net/wotonomy/foundation/NSDate.java217
-rw-r--r--projects/net.wotonomy.foundation/src/main/java/net/wotonomy/foundation/NSDictionary.java332
-rw-r--r--projects/net.wotonomy.foundation/src/main/java/net/wotonomy/foundation/NSDisposable.java35
-rw-r--r--projects/net.wotonomy.foundation/src/main/java/net/wotonomy/foundation/NSForwardException.java157
-rw-r--r--projects/net.wotonomy.foundation/src/main/java/net/wotonomy/foundation/NSKeyValueCoding.java431
-rw-r--r--projects/net.wotonomy.foundation/src/main/java/net/wotonomy/foundation/NSKeyValueCodingAdditions.java213
-rw-r--r--projects/net.wotonomy.foundation/src/main/java/net/wotonomy/foundation/NSKeyValueCodingSupport.java228
-rw-r--r--projects/net.wotonomy.foundation/src/main/java/net/wotonomy/foundation/NSLock.java108
-rw-r--r--projects/net.wotonomy.foundation/src/main/java/net/wotonomy/foundation/NSLocking.java59
-rw-r--r--projects/net.wotonomy.foundation/src/main/java/net/wotonomy/foundation/NSLog.java489
-rw-r--r--projects/net.wotonomy.foundation/src/main/java/net/wotonomy/foundation/NSMultiReaderLock.java159
-rw-r--r--projects/net.wotonomy.foundation/src/main/java/net/wotonomy/foundation/NSMutableArray.java429
-rw-r--r--projects/net.wotonomy.foundation/src/main/java/net/wotonomy/foundation/NSMutableData.java167
-rw-r--r--projects/net.wotonomy.foundation/src/main/java/net/wotonomy/foundation/NSMutableDictionary.java166
-rw-r--r--projects/net.wotonomy.foundation/src/main/java/net/wotonomy/foundation/NSMutableRange.java116
-rw-r--r--projects/net.wotonomy.foundation/src/main/java/net/wotonomy/foundation/NSNotification.java164
-rw-r--r--projects/net.wotonomy.foundation/src/main/java/net/wotonomy/foundation/NSNotificationCenter.java671
-rw-r--r--projects/net.wotonomy.foundation/src/main/java/net/wotonomy/foundation/NSNotificationQueue.java345
-rw-r--r--projects/net.wotonomy.foundation/src/main/java/net/wotonomy/foundation/NSNull.java104
-rw-r--r--projects/net.wotonomy.foundation/src/main/java/net/wotonomy/foundation/NSNumberFormatter.java57
-rw-r--r--projects/net.wotonomy.foundation/src/main/java/net/wotonomy/foundation/NSPropertyListSerialization.java275
-rw-r--r--projects/net.wotonomy.foundation/src/main/java/net/wotonomy/foundation/NSRange.java351
-rw-r--r--projects/net.wotonomy.foundation/src/main/java/net/wotonomy/foundation/NSRecursiveLock.java148
-rw-r--r--projects/net.wotonomy.foundation/src/main/java/net/wotonomy/foundation/NSRunLoop.java522
-rw-r--r--projects/net.wotonomy.foundation/src/main/java/net/wotonomy/foundation/NSSelector.java391
-rw-r--r--projects/net.wotonomy.foundation/src/main/java/net/wotonomy/foundation/NSSet.java212
-rw-r--r--projects/net.wotonomy.foundation/src/main/java/net/wotonomy/foundation/NSTimeZone.java272
-rw-r--r--projects/net.wotonomy.foundation/src/main/java/net/wotonomy/foundation/NSTimestamp.java285
-rw-r--r--projects/net.wotonomy.foundation/src/main/java/net/wotonomy/foundation/NSTimestampFormatter.java65
-rw-r--r--projects/net.wotonomy.foundation/src/main/java/net/wotonomy/foundation/internal/Duplicator.java299
-rw-r--r--projects/net.wotonomy.foundation/src/main/java/net/wotonomy/foundation/internal/Introspector.java941
-rw-r--r--projects/net.wotonomy.foundation/src/main/java/net/wotonomy/foundation/internal/IntrospectorException.java32
-rw-r--r--projects/net.wotonomy.foundation/src/main/java/net/wotonomy/foundation/internal/MissingPropertyException.java32
-rw-r--r--projects/net.wotonomy.foundation/src/main/java/net/wotonomy/foundation/internal/NetworkClassLoader.java368
-rw-r--r--projects/net.wotonomy.foundation/src/main/java/net/wotonomy/foundation/internal/NullPrimitiveException.java32
-rw-r--r--projects/net.wotonomy.foundation/src/main/java/net/wotonomy/foundation/internal/PropertyComparator.java100
-rw-r--r--projects/net.wotonomy.foundation/src/main/java/net/wotonomy/foundation/internal/PropertyListParser.java546
-rw-r--r--projects/net.wotonomy.foundation/src/main/java/net/wotonomy/foundation/internal/QueueMap.java538
-rw-r--r--projects/net.wotonomy.foundation/src/main/java/net/wotonomy/foundation/internal/URLResourceReader.java207
-rw-r--r--projects/net.wotonomy.foundation/src/main/java/net/wotonomy/foundation/internal/ValueConverter.java718
-rw-r--r--projects/net.wotonomy.foundation/src/main/java/net/wotonomy/foundation/internal/WotonomyException.java134
-rw-r--r--projects/net.wotonomy.foundation/src/main/java/net/wotonomy/foundation/package.html18
-rw-r--r--projects/net.wotonomy.foundation/src/main/java/net/wotonomy/foundation/xml/XMLDecoder.java68
-rw-r--r--projects/net.wotonomy.foundation/src/main/java/net/wotonomy/foundation/xml/XMLEncoder.java61
50 files changed, 13192 insertions, 0 deletions
diff --git a/projects/net.wotonomy.foundation/src/main/java/net/wotonomy/foundation/NSArray.java b/projects/net.wotonomy.foundation/src/main/java/net/wotonomy/foundation/NSArray.java
new file mode 100644
index 0000000..491722c
--- /dev/null
+++ b/projects/net.wotonomy.foundation/src/main/java/net/wotonomy/foundation/NSArray.java
@@ -0,0 +1,661 @@
+/*
+Wotonomy: OpenStep design patterns for pure Java applications.
+Copyright (C) 2000 Blacksmith, Inc.
+
+This library is free software; you can redistribute it and/or
+modify it under the terms of the GNU Lesser General Public
+License as published by the Free Software Foundation; either
+version 2.1 of the License, or (at your option) any later version.
+
+This library is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+Lesser General Public License for more details.
+
+You should have received a copy of the GNU Lesser General Public
+License along with this library; if not, see http://www.gnu.org
+*/
+
+package net.wotonomy.foundation;
+
+import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Enumeration;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.ListIterator;
+
+/**
+* NSArray is an unmodifiable List.
+* Calling the mutator methods of the List interface (add, addAll,
+* set, etc.) on an instance of NSArray will throw an Unsupported
+* Operation exception: use a NSMutableArray instead. This is to
+* simulate Objective-C's pattern of exposing mutator methods only
+* on mutable subinterface, which is wonderful for communicating
+* via interface the contract on returned collections (whether you
+* may modify return values) as well as implementing array faults.
+*
+* @author michael@mpowers.net
+* @author $Author: cgruber $
+* @version $Revision: 929 $
+*/
+public class NSArray implements List, Serializable
+{
+ /**
+ * Actual list that backs this instance.
+ */
+ List list;
+
+ /**
+ * Return value when array index is not found.
+ */
+ public static final int NotFound = -1;
+
+ /**
+ * A constant representing an empty array.
+ */
+ public static final NSArray EmptyArray = new NSArray();
+
+ /**
+ * Returns an NSArray backed by the specified List.
+ * This is useful to "protect" an internal representation
+ * that is returned by a method of return type NSArray.
+ */
+ public static NSArray arrayBackedByList( List aList )
+ {
+ return new NSArray( aList, null );
+ }
+
+ /**
+ * A constructor that uses the provided list as the backing
+ * list object. This is unlike ArrayList and other
+ * java.util.Collection types insofar as that API requires
+ * that the provided Collection be copied into the newly constructed
+ * Collection.
+ *
+ * TODO: See if this signature can be reasonably changed, as having a no-op parameter is a little counter-intuitive.
+ * @param aList A list that the caller wishes to become the backing list for the NSArray.
+ * @param ignored This parameter is entirely ignored, and is only there to distinguish the API.
+ */
+ NSArray( List aList, Object ignored ) // differentiates
+ {
+ list = aList;
+ }
+
+ /**
+ * Constructor with a size hint, used by NSMutableArray.
+ */
+ NSArray( int aSize )
+ {
+ list = new ArrayList( aSize );
+ }
+
+ /**
+ * Default constructor returns an empty array.
+ */
+ public NSArray ()
+ {
+ list = new ArrayList();
+ }
+
+ /**
+ * Produces an array containing only the specified object.
+ */
+ public NSArray (Object anObject)
+ {
+ this();
+ list.add( anObject );
+ }
+
+ /**
+ * Produces an array containing the specified objects.
+ */
+ public NSArray (Object[] anArray)
+ {
+ this();
+ for ( int i = 0; i < anArray.length; i++ )
+ {
+ list.add( anArray[i] );
+ }
+ }
+
+ /**
+ * Produces an array containing the objects in the specified collection.
+ */
+ public NSArray (Collection aCollection)
+ {
+ this();
+ Iterator i = aCollection.iterator();
+ while ( i.hasNext() ) list.add( i.next() );
+ }
+
+ /**
+ * Returns the number of items in this array.
+ */
+ public int count ()
+ {
+ return list.size();
+ }
+
+ /**
+ * Returns an array containing all objects in this array
+ * plus the specified object.
+ */
+ public NSArray arrayByAddingObject (Object anObject)
+ {
+ NSArray result = new NSArray( this );
+ result.protectedAdd( anObject );
+ return result;
+ }
+
+ /**
+ * Returns an array containing all objects in this array
+ * plus all objects in the specified list.
+ */
+ public NSArray arrayByAddingObjectsFromArray (Collection aCollection)
+ {
+ NSArray result = new NSArray( this );
+ result.protectedAddAll( aCollection );
+ return result;
+ }
+
+ /**
+ * Returns a string containing the string representations of
+ * each element in this array, with each element separated from
+ * each neighboring element by the specified string.
+ */
+ public String componentsJoinedByString (String separator)
+ {
+ StringBuffer buf = new StringBuffer();
+ Iterator it = list.iterator();
+ if ( it.hasNext() )
+ {
+ buf.append( it.next().toString() );
+ }
+ while ( it.hasNext() )
+ {
+ buf.append( separator );
+ buf.append( String.valueOf(it.next()) );
+ }
+ return buf.toString();
+ }
+
+ /**
+ * Returns whether an equivalent object is contained in this array.
+ */
+ public boolean containsObject (Object anObject)
+ {
+ return list.contains( anObject );
+ }
+
+ /**
+ * Returns the first object in this array that is equivalent to
+ * an object in the specified list, or null if no objects are
+ * in common.
+ */
+ public Object firstObjectCommonWithArray (Collection aCollection)
+ {
+ if ( aCollection == null ) return null;
+
+ Object o;
+ Iterator it = list.iterator();
+ while ( it.hasNext() )
+ {
+ o = it.next();
+ if ( aCollection.contains( o ) ) return o;
+ }
+ return null;
+ }
+
+ /**
+ * Returns whether the specified list contains elements equivalent
+ * to those in this array in the same order.
+ */
+ public boolean isEqualToArray (List aList)
+ {
+ return list.equals( aList );
+ }
+
+ /**
+ * Returns the last object in this array, or null if the array is empty.
+ */
+ public Object lastObject ()
+ {
+ int i;
+ if ( (i = list.size()) == 0 ) return null;
+ return list.get( i - 1 );
+ }
+
+ /**
+ *
+ */
+/*
+ public NSArray sortedArrayUsingSelector (NSSelector);
+*/
+
+ /**
+ * Returns an array comprised of only those elements whose
+ * indices fall within the specified range.
+ */
+ public NSArray subarrayWithRange (NSRange aRange)
+ {
+ //TODO: Test this logic.
+ NSArray result = new NSArray();
+ if ( aRange == null ) return result;
+
+ int loc = aRange.location();
+ int max = aRange.maxRange();
+ int count = count();
+ for ( int i = loc; i <= max && i < count; i++ )
+ {
+ result.protectedAdd( list.get( i ) );
+ }
+ return result;
+ }
+
+ /**
+ * Returns an enumeration over the the elements of the array.
+ */
+ public Enumeration objectEnumerator ()
+ {
+ //TODO: Test this logic.
+ return new Enumeration()
+ {
+ Iterator it = NSArray.this.iterator();
+ public boolean hasMoreElements()
+ {
+ return it.hasNext();
+ }
+ public Object nextElement()
+ {
+ return it.next();
+ }
+ };
+ }
+
+ /**
+ * Returns an enumeration over the elements of the array in reverse order.
+ */
+ public java.util.Enumeration reverseObjectEnumerator ()
+ {
+ return new java.util.Enumeration()
+ {
+ ListIterator it = null;
+ public ListIterator getIterator()
+ {
+ if ( it == null )
+ {
+ it = NSArray.this.listIterator();
+ // zoom to end
+ while ( it.hasNext() ) it.next();
+ }
+ return it;
+ }
+ public boolean hasMoreElements()
+ {
+ return getIterator().hasPrevious();
+ }
+ public Object nextElement()
+ {
+ return getIterator().previous();
+ }
+ };
+ }
+
+ /**
+ * Copies the elements of this array into the specified object array
+ * as the array's capacity permits.
+ */
+ public void getObjects (Object[] anArray)
+ {
+ getObjects(anArray,null);
+ }
+
+ /**
+ * Copies the elements of this array that fall within the specified range
+ * into the specified object array as the array's capacity permits. This
+ * method must not overflow, even in the face of a null range, an over or
+ * under-sized array, or a bad range. It may underflow and fail to
+ * entirely populate the array, if the array is larger than the data to
+ * be copied.
+ *
+ * TODO: Check whether in WebObjects the range supposed to be measured against the parameter or the NSArray itself??? -ceg
+ *
+ * @param anArray An object array to be filled by this method.
+ * @param range An NSRange object representing the range of data in the NSArray to be copied.
+ */
+ public void getObjects (Object[] array, NSRange range)
+ {
+ if ( array == null ) return;
+ if ( range == null) range = new NSRange(0,array.length);
+ int limit = Math.min(Math.min(array.length,range.length()),(count()-range.location()));
+ for ( int i = 0; i < limit ; i++ ) {
+ //anArray[ i-aRange.location() ] = objectAtIndex( i );
+ array[ i ] = objectAtIndex( range.location() + i );
+ }
+ }
+
+ /**
+ * Returns the index of the first object in the array equivalent
+ * to the specified object. Returns NotFound if the item is not found.
+ */
+ public int indexOfObject (Object anObject)
+ {
+ int result = list.indexOf( anObject );
+ if ( result == -1 ) return NotFound; // in case this changes
+ return result;
+ }
+
+ /**
+ * Returns the index of the first object in the array
+ * within the specified range equivalent to the specified object.
+ * Returns NotFound if the item is not found.
+ */
+ public int indexOfObject (Object anObject, NSRange aRange)
+ {
+ if ( ( anObject == null ) || ( aRange == null ) ) return NotFound;
+
+ int loc = aRange.location();
+ int max = aRange.maxRange();
+ for ( int i = loc; i < max; i++ )
+ {
+ if ( anObject.equals( list.get(i) ) )
+ {
+ return i;
+ }
+ }
+ return NotFound;
+ }
+
+ /**
+ * Returns the index of the specified object if it exists
+ * in the array, comparing by reference.
+ * Returns NotFound if the item is not found.
+ */
+ public int indexOfIdenticalObject (Object anObject)
+ {
+ int size = list.size();
+ for ( int i = 0; i < size; i++ )
+ {
+ if ( anObject == list.get(i) )
+ {
+ return i;
+ }
+ }
+ return NotFound;
+ }
+
+ /**
+ * Returns the index of the first object in the array
+ * within the specified range equivalent to the specified object.
+ */
+ public int indexOfIdenticalObject (Object anObject, NSRange aRange)
+ {
+ if ( aRange == null ) return NotFound;
+
+ int loc = aRange.location();
+ int max = aRange.maxRange();
+ for ( int i = loc; i < max; i++ )
+ {
+ if ( anObject == list.get(i) )
+ {
+ return i;
+ }
+ }
+ return NotFound;
+ }
+
+ /**
+ * Returns the object at the specified index. Throws an
+ * IndexOutOfRange exception if the index is out of range.
+ */
+ public Object objectAtIndex (int anIndex)
+ {
+ return list.get( anIndex );
+ }
+
+ /**
+ * Returns an array consisting of strings within the specified string
+ * as delimited by the specified separator characters.
+ */
+ public static NSArray componentsSeparatedByString
+ (String aString, String aSeparator)
+ {
+ NSArray result = new NSArray();
+ if ( aString == null ) return result;
+ if ( aSeparator == null ) return new NSArray( aString );
+
+ //FIXME: The spec probably considers the whole
+ // string as a separator, unlike string tokenizer.
+ java.util.StringTokenizer tokens =
+ new java.util.StringTokenizer( aString, aSeparator );
+ while ( tokens.hasMoreTokens() )
+ {
+ result.protectedAdd( tokens.nextToken() );
+ }
+
+ return result;
+ }
+
+ public Object clone()
+ {
+ return new NSArray( list );
+ }
+
+ public NSArray immutableClone()
+ {
+ return this;
+ }
+
+ public NSMutableArray mutableClone()
+ {
+ return new NSMutableArray( this );
+ }
+
+ public String toString() {
+ StringBuffer buf = new StringBuffer();
+ buf.append(NSPropertyListSerialization.TOKEN_BEGIN[NSPropertyListSerialization.PLIST_ARRAY]);
+ for (int i = 0; i < count(); i++) {
+ Object x = objectAtIndex(i);
+ buf.append(NSPropertyListSerialization.stringForPropertyList(x));
+ if (i < count() - 1)
+ buf.append(", ");
+ }
+ buf.append(NSPropertyListSerialization.TOKEN_END[NSPropertyListSerialization.PLIST_ARRAY]);
+ return buf.toString();
+ }
+
+ // interface List: accessors
+
+ public boolean contains(Object o) { return list.contains(o); }
+ public boolean containsAll(Collection c) { return list.containsAll(c); }
+ public boolean equals(Object o) { return list.equals(o); }
+ public Object get(int index) { return list.get(index); }
+ public int hashCode() {
+ int code = 19;
+ code *= getClass().hashCode();
+ code *= list.hashCode();
+ return code;
+ }
+ public int indexOf(Object o) { return list.indexOf(o); }
+ public boolean isEmpty() { return list.isEmpty(); }
+ public int lastIndexOf(Object o) { return list.lastIndexOf(o); }
+ public int size() { return list.size(); }
+ public Object[] toArray() { return list.toArray(); }
+ public Object[] toArray(Object[] a) { return list.toArray(a); }
+
+ // interface List: mutators
+
+
+ public void add(int index, Object element)
+ {
+ this.list.add(index,element);
+ }
+
+ public boolean add(Object o)
+ {
+ return this.list.add(o);
+ }
+
+ public boolean addAll(Collection coll)
+ {
+ return this.list.addAll(coll);
+ }
+
+ public boolean addAll(int index, Collection c)
+ {
+ return this.list.addAll(index,c);
+ }
+
+ public void clear()
+ {
+ this.list.clear();
+ }
+
+ public Iterator iterator()
+ {
+ // make a copy to avoid ConcurrentModificationExceptions
+ final Iterator i = new LinkedList( list ).iterator();
+ return new Iterator()
+ {
+ public boolean hasNext() {return i.hasNext();}
+ public Object next() {return i.next();}
+ public void remove() { throw new UnsupportedOperationException(); }
+ };
+ }
+
+ public ListIterator listIterator() { return listIterator(0); }
+
+ public ListIterator listIterator(final int index)
+ {
+ // make a copy to avoid ConcurrentModificationExceptions
+ final ListIterator i = new LinkedList( list ).listIterator(index);
+ return new ListIterator()
+ {
+ public boolean hasNext() {return i.hasNext();}
+ public Object next() {return i.next();}
+ public boolean hasPrevious() {return i.hasPrevious();}
+ public Object previous() {return i.previous();}
+ public int nextIndex() {return i.nextIndex();}
+ public int previousIndex() {return i.previousIndex();}
+ public void remove() { throw new UnsupportedOperationException(); }
+ public void set(Object o) { throw new UnsupportedOperationException(); }
+ public void add(Object o) { throw new UnsupportedOperationException(); }
+ };
+ }
+
+ public Object remove(int index)
+ {
+ return this.list.remove(index);
+ }
+
+ public boolean remove(Object o)
+ {
+ return this.list.remove(o);
+ }
+
+ public boolean removeAll(Collection coll)
+ {
+ return this.list.removeAll(coll);
+ }
+
+ public boolean retainAll(Collection coll)
+ {
+ return this.list.retainAll(coll);
+ }
+
+ public Object set(int index, Object element)
+ {
+ return this.list.set(index,element);
+ }
+
+ public List subList(int fromIndex, int toIndex)
+ {
+ return Collections.unmodifiableList(list.subList(fromIndex, toIndex));
+ }
+
+ /**
+ * Provided for the use of subclasses like ArrayFault.
+ */
+ protected boolean protectedAdd( Object o )
+ {
+ return list.add( o );
+ }
+
+ /**
+ * Provided for the use of subclasses like ArrayFault.
+ */
+ protected boolean protectedAddAll( Collection coll )
+ {
+ return list.addAll( coll );
+ }
+}
+
+/*
+ * $Log$
+ * Revision 1.2 2006/03/10 00:52:27 cgruber
+ * Add tests for NSArray and fix some problems that became obvious as a result.
+ *
+ * Revision 1.1 2006/02/16 12:47:16 cgruber
+ * Check in all sources in eclipse-friendly maven-enabled packages.
+ *
+ * Revision 1.16 2005/07/13 14:12:44 cgruber
+ * Add mutableClone() and immutableClone() per. WebObjects 5.3 conformance.
+ *
+ * Revision 1.15 2003/08/06 23:07:52 chochos
+ * general code cleanup (mostly, removing unused imports)
+ *
+ * Revision 1.14 2003/08/05 00:48:56 chochos
+ * use NSPropertyListSerialization to get the opening and closing tokens for the string representation
+ *
+ * Revision 1.13 2003/08/04 20:26:10 chochos
+ * use NSPropertyListSerialization inside toString()
+ *
+ * Revision 1.12 2003/08/04 18:18:43 chochos
+ * toString() yields strings in the same format as Apple's NSArray
+ *
+ * Revision 1.11 2003/01/28 19:44:20 mpowers
+ * Fixed reverse enumerator.
+ *
+ * Revision 1.10 2003/01/18 23:49:55 mpowers
+ * Added mutableClone().
+ *
+ * Revision 1.9 2003/01/18 23:30:42 mpowers
+ * WODisplayGroup now compiles.
+ *
+ * Revision 1.8 2003/01/16 22:47:30 mpowers
+ * Compatibility changes to support compiling woextensions source.
+ * (34 out of 56 classes compile!)
+ *
+ * Revision 1.7 2003/01/10 19:16:40 mpowers
+ * Implemented support for page caching.
+ *
+ * Revision 1.6 2002/10/24 21:15:36 mpowers
+ * New implementations of NSArray and subclasses.
+ *
+ * Revision 1.5 2002/10/24 18:16:30 mpowers
+ * Now enforcing NSArray's immutable nature.
+ *
+ * Revision 1.4 2002/03/08 19:02:54 mpowers
+ * Long-overdue speed optimization of indexOfIdenticalObject.
+ *
+ * Revision 1.3 2002/02/13 22:02:56 mpowers
+ * Fixed: bug in componentsSeparatedByString when separator is null
+ * (thanks to Cedrik LIME).
+ *
+ * Revision 1.2 2001/01/11 20:34:26 mpowers
+ * Implemented EOSortOrdering and added support in framework.
+ * Added header-click to sort table columns.
+ *
+ * Revision 1.1.1.1 2000/12/21 15:47:26 mpowers
+ * Contributing wotonomy.
+ *
+ * Revision 1.3 2000/12/20 16:25:37 michael
+ * Added log to all files.
+ *
+ *
+ */
+
diff --git a/projects/net.wotonomy.foundation/src/main/java/net/wotonomy/foundation/NSBundle.java b/projects/net.wotonomy.foundation/src/main/java/net/wotonomy/foundation/NSBundle.java
new file mode 100644
index 0000000..b735404
--- /dev/null
+++ b/projects/net.wotonomy.foundation/src/main/java/net/wotonomy/foundation/NSBundle.java
@@ -0,0 +1,418 @@
+/*
+ Wotonomy: OpenStep design patterns for pure Java applications.
+ Copyright (C) 2005 Israfil Consulting Services Corporation
+ Copyright (C) 2005 Christian Gruber
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, see http://www.gnu.org
+ */
+package net.wotonomy.foundation;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.util.Locale;
+import java.util.Properties;
+import java.util.jar.JarEntry;
+import java.util.jar.JarFile;
+import java.util.jar.JarInputStream;
+
+import net.wotonomy.foundation.internal.NetworkClassLoader;
+
+/**
+ * An implementation of NSBundle. Unlike the standard WebObjects NSBundle, this
+ * implementation loads bundles dynamically. This means that all bundles do not
+ * need to exist in the classpath at startup time. Practically, this means that
+ * NSBundle has a custom classloader.
+ *
+ * This behaviour is not supported in Apple's WebObjects, and should be used
+ * only if compatibility is not desired. It is largely intended for internal use
+ * within this framework, but is exposed through bundleForURL()
+ *
+ * Another difference between Wotonomy's NSBundle and Apple's implementation is
+ * the ability to initialize the application with a custom resource lookup
+ * "path".
+ *
+ * @author cgruber@israfil.net
+ * @author $Author: cgruber $
+ * @version $Revision: 892 $
+ *
+ */
+public class NSBundle {
+
+ /* Class variables */
+
+ public static final String BundleDidLoadNotification = "NSBundleDidLoadNotification";
+
+ public static final String LoadedClassesNotification = "NSLoadedClassesNotification";
+
+ private static final NSMutableArray _allBundles = new NSMutableArray();
+
+ private static final NSMutableArray _allFrameworks = new NSMutableArray();
+
+ private static NSMutableDictionary _languageCodes = new NSMutableDictionary();
+
+ private static NSBundle _mainBundle = null;
+
+ protected static NetworkClassLoader _classLoader = new NetworkClassLoader(ClassLoader.getSystemClassLoader());
+
+ /* Instance variables */
+
+ protected String name;
+
+ protected NSMutableDictionary info = null;
+
+ protected String path;
+
+ protected NSMutableArray classNames = new NSMutableArray();
+
+ protected NSMutableArray packages = new NSMutableArray();
+
+ protected Properties properties;
+
+ protected boolean isFramework = false;
+
+ protected boolean loaded = false;
+
+ protected Class principalClass;
+
+ /* Constructors */
+
+ /**
+ * The default constructor, which is only public to support other framework
+ * functionality, and to be API compatible with Apple's WebObjects.
+ * Generally, framework users should use bundleForXXXX() methods.
+ */
+ public NSBundle() {
+ }
+
+ /* Methods */
+
+ /**
+ * @deprecated use mainBundle() to access the application bundle and
+ * frameworkBundles() to access any frameworks.
+ */
+ public static synchronized NSArray allBundles() {
+ return _allBundles.immutableClone();
+ }
+
+ /**
+ * @deprecated use frameworkBundles() to access any frameworks.
+ */
+ public static NSArray allFrameworks() {
+ return frameworkBundles();
+ }
+
+ /**
+ * Returns the bundle that contains the provided class, if any. Otherwise,
+ * it returns null. Because NSBundles have a specialized class-loader, if
+ * any two bundles contain duiplicates of the same class, the second will
+ * fail to load. TODO: Determine if class-load scoping of duplicate classes
+ * is appropriate.
+ *
+ * @param class1
+ * @return NSBundle
+ */
+ public static synchronized NSBundle bundleForClass(Class class1) {
+ throw new UnsupportedOperationException("Method not yet implemented.");
+ // TODO: Implement.
+ }
+
+ /**
+ * @deprecated Apple's WebObjects says you should not load from arbitrary
+ * path.
+ * @param path
+ * @return
+ */
+ public static synchronized NSBundle bundleWithPath(String path) {
+ try {
+ return bundleWithURL(new File(path).toURI().toURL());
+ } catch (MalformedURLException e) {
+ NSLog.err.appendln("Bundle path is invalid: " + path);
+ return null;
+ }
+ }
+
+ /**
+ * <strong>Note:</strong>This method is only in Wotonomy.
+ *
+ * This method returns a bundle at a given URL, registering that bundle as
+ * well. If the bundle has already been loaded/registered, it is simply
+ * returned from the cache.
+ *
+ * @param url
+ * @return
+ */
+ public static synchronized NSBundle bundleWithURL(URL url) {
+ NSBundle result = null;
+ String sep = System.getProperty("file.separator");
+ String protocol = url.getProtocol();
+ if (protocol.equals("file")) {
+ File f = new File(url.getPath());
+ if (!f.exists()) {
+ NSLog.err.appendln("Bundle not found: " + url);
+ return null;
+ }
+ StringBuffer filename = new StringBuffer(f.getName());
+ int extensionIndex = filename.lastIndexOf(".");
+ if (extensionIndex == -1) {
+ NSLog.err
+ .appendln("Named URL does not point to a bundle with an extension: "
+ + url);
+ return null;
+ }
+ String basename = filename.substring(0, extensionIndex);
+ String extension = filename.substring(extensionIndex + 1, filename
+ .length());
+ System.out.println("basename: " + basename);
+ System.out.println("extension: " + extension);
+ result = new NSBundle();
+ result.name = basename;
+ result.isFramework = extension.equals("framework");
+ if (f.isDirectory()) {
+ try {
+ File javadir = new File(f.getCanonicalPath() + sep + "Contents"
+ + sep + "Resources" + sep + "Java");
+ System.out.println(javadir);
+ System.out.println(javadir.exists());
+ File[] jars = javadir.listFiles();
+
+ } catch (IOException e) { }
+ } else {
+ throw new RuntimeException(
+ "Compressed bundle files not currently supported.");
+ }
+ throw new RuntimeException("Method not finished.");
+ } else {
+ try {
+ JarInputStream j = new JarInputStream(url.openStream());
+ JarEntry entry1 = j.getNextJarEntry();
+
+ JarFile f;
+ throw new RuntimeException("Method not finished.");
+ } catch (IOException e) {
+ NSLog.err
+ .appendln("IOException loading framework jar from URL "
+ + url + " - message: "
+ + e.getLocalizedMessage());
+ StringWriter stacktrace = new StringWriter();
+ e.printStackTrace(new PrintWriter(stacktrace));
+ NSLog.err.appendln(stacktrace);
+ return null;
+ }
+ }
+ }
+
+ /**
+ * This method returns a bundle, either from cache, or if it doesn't exist
+ * yet, it attempts to look it up - first from the classpath, then from the
+ * resource path. TODO: Determine if the lookup order is the desired
+ * semantic.
+ *
+ * @param name
+ * @return
+ */
+ public static synchronized NSBundle bundleForName(String name) {
+ throw new UnsupportedOperationException("Method not yet implemented.");
+ // TODO: Implement.
+ }
+
+ public static synchronized NSArray frameworkBundles() {
+ return _allFrameworks.immutableClone();
+ }
+
+ /**
+ * Used to set the "Main" application bundle, in which primary resources are
+ * loaded for GUI applications. This is mostly only relevant for
+ * XXApplication objects. This should therefore not be generally used by
+ * consumers of the framework.
+ *
+ * @param aBundle
+ */
+ public static void setMainBundle(NSBundle aBundle) {
+ _mainBundle = aBundle;
+ }
+
+ public static NSBundle mainBundle() {
+ return _mainBundle;
+ }
+
+ /**
+ * Get the default prefix for locale. TODO: This really needs to be made
+ * dynamic somehow.
+ *
+ * @return
+ */
+ protected static String defaultLocalePrefix() {
+ String language = (String) _languageCodes.objectForKey(Locale
+ .getDefault().getLanguage());
+ return language + ".lproj";
+ }
+
+ protected static synchronized NSBundle findOrCreateBundleWithPath(String s) {
+ throw new UnsupportedOperationException("Method not yet implemented.");
+ // TODO: Implement.
+ }
+
+ /**
+ * TODO: figure out what this does.
+ *
+ * @return
+ */
+ public NSArray bundleClassPackageNames() {
+ throw new UnsupportedOperationException("Method not yet implemented.");
+ // TODO: Implement.
+ }
+
+ public String bundlePath() {
+ return this.path;
+ }
+
+ /**
+ * Returns a byte array for the given resource path. TODO: Lookup semantics
+ * in WebObjects javadocs.
+ *
+ * @param path
+ * @return
+ */
+ public byte[] bytesForResourcePath(String path) {
+ throw new UnsupportedOperationException("Method not yet implemented.");
+ // TODO: Implement.
+ }
+
+ public NSArray bundleClassNames() {
+ return classNames.immutableClone();
+ }
+
+ public NSDictionary infoDictionary() {
+ return info;
+ }
+
+ /**
+ * Returns an input stream for a given resource path. TODO: Lookup semantics
+ * in WebObjects javadocs.
+ *
+ * @param path
+ * @return
+ */
+ public InputStream inputStreamForResourcePath(String path) {
+ throw new UnsupportedOperationException("Method not yet implemented.");
+ // TODO: Implement.
+ }
+
+ public boolean isFramework() {
+ return isFramework;
+ }
+
+ public boolean load() {
+ return loaded;
+ }
+
+ public String name() {
+ return name;
+ }
+
+ /**
+ * @deprecated Don't use this method, use
+ * resourcePathForLocalizedResourceNamed() instead.
+ */
+
+ public String pathForResource(String aName, String anExtension) {
+ return this.resourcePathForLocalizedResourceNamed(aName, null);
+ }
+
+ /**
+ * @deprecated Don't use this method, use
+ * resourcePathForLocalizedResourceNamed() instead.
+ */
+ public String pathForResource(String aName, String anExtension,
+ String subDir) {
+ return this.resourcePathForLocalizedResourceNamed(aName, subDir);
+ }
+
+ /**
+ * @deprecated Don't use this method, use resourcePathsForResources()
+ * instead.
+ */
+ public NSArray pathsForResources(String aName, String anExtension) {
+ throw new UnsupportedOperationException("Method not yet implemented.");
+ // TODO: Implement.
+ }
+
+ public Class principalClass() {
+ return principalClass;
+ }
+
+ public Properties properties() {
+ return properties;
+ }
+
+ /**
+ * @deprecated Resources are now accessed using the bytesForResourcePath()
+ * and inputStreamForResourcePath() methods.
+ */
+ public String resourcePath() {
+ throw new UnsupportedOperationException("Method not yet implemented.");
+ // TODO: Implement.
+ }
+
+ public String resourcePathForLocalizedResourceNamed(String aName,
+ String subDir) {
+ throw new UnsupportedOperationException("Method not yet implemented.");
+ // TODO: Implement.
+ }
+
+ public NSArray resourcePathsForDirectories(String extension,
+ String subdirPath) {
+ throw new UnsupportedOperationException("Method not yet implemented.");
+ }
+
+ public NSArray resourcePathsForLocalizedResources(String extension,
+ String subdirPath) {
+ throw new UnsupportedOperationException("Method not yet implemented.");
+ }
+
+ public NSArray resourcePathsForResources(String extension, String subdirPath) {
+ throw new UnsupportedOperationException("Method not yet implemented.");
+
+ }
+
+ public String toString() {
+ int i = 0;
+ if (classNames != null)
+ i = classNames.count();
+ return "<" + getClass().getName() + " name:'" + name + "' bundlePath:'"
+ + path + "' packages:'" + packages + "' " + i + " classes >";
+ }
+
+ /* Static initialization code */
+
+ private static void _initLanguages() {
+ _languageCodes.setObjectForKey("de", "German");
+ _languageCodes.setObjectForKey("en", "English");
+ _languageCodes.setObjectForKey("eo", "Esperanto");
+ _languageCodes.setObjectForKey("es", "Spanish");
+ _languageCodes.setObjectForKey("fr", "French");
+ _languageCodes.setObjectForKey("ja", "Japanese");
+ }
+
+ static {
+ NSBundle._initLanguages();
+
+ }
+
+}
diff --git a/projects/net.wotonomy.foundation/src/main/java/net/wotonomy/foundation/NSCoder.java b/projects/net.wotonomy.foundation/src/main/java/net/wotonomy/foundation/NSCoder.java
new file mode 100644
index 0000000..ca32dd5
--- /dev/null
+++ b/projects/net.wotonomy.foundation/src/main/java/net/wotonomy/foundation/NSCoder.java
@@ -0,0 +1,113 @@
+/*
+Wotonomy: OpenStep design patterns for pure Java applications.
+Copyright (C) 2002 Israfil consulting Services Corporation
+
+This library is free software; you can redistribute it and/or
+modify it under the terms of the GNU Lesser General Public
+License as published by the Free Software Foundation; either
+version 2.1 of the License, or (at your option) any later version.
+
+This library is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+Lesser General Public License for more details.
+
+You should have received a copy of the GNU Lesser General Public
+License along with this library; if not, see http://www.gnu.org
+
+$Id: NSCoder.java 893 2006-02-16 13:22:23Z cgruber $
+
+*/
+
+package net.wotonomy.foundation;
+
+import java.io.InputStream;
+import java.io.OutputStream;
+
+/**
+* A class that defines a simple encode/decode paradigm. Subclasses
+* would handle the target format.
+*
+* @author cgruber@israfil.net
+* @author $Author: cgruber $
+* @version $Revision: 893 $
+*/
+
+public abstract class NSCoder {
+
+ public NSCoder() {
+ }
+
+ public abstract void encodeBoolean(boolean flag);
+
+ public abstract void encodeByte(byte byte0);
+
+ public abstract void encodeBytes(byte abyte0[]);
+
+ public abstract void encodeChar(char c);
+
+ public abstract void encodeShort(short word0);
+
+ public abstract void encodeInt(int i);
+
+ public abstract void encodeLong(long l);
+
+ public abstract void encodeFloat(float f);
+
+ public abstract void encodeDouble(double d);
+
+ public abstract void encodeObject(Object obj);
+
+ public abstract void encodeClass(Class class1);
+
+ public abstract void encodeObjects(Object aobj[]);
+
+ public abstract boolean decodeBoolean();
+
+ public abstract byte decodeByte();
+
+ public abstract byte[] decodeBytes();
+
+ public abstract char decodeChar();
+
+ public abstract short decodeShort();
+
+ public abstract int decodeInt();
+
+ public abstract long decodeLong();
+
+ public abstract float decodeFloat();
+
+ public abstract double decodeDouble();
+
+ public abstract Object decodeObject();
+
+ public abstract Class decodeClass();
+
+ public abstract Object[] decodeObjects();
+
+ public void prepareForWriting(OutputStream outputstream) {
+ }
+
+ public void prepareForReading(InputStream inputstream) {
+ }
+
+ public void finishCoding() {
+ }
+
+}
+/*
+ * $Log$
+ * Revision 1.2 2006/02/16 13:15:00 cgruber
+ * Check in all sources in eclipse-friendly maven-enabled packages.
+ *
+ * Revision 1.1 2002/07/14 21:56:16 mpowers
+ * Contributions from cgruber.
+ *
+ * Revision 1.2 2002/06/25 19:03:02 cgruber
+ * Internal documentation fixes.
+ *
+ * Revision 1.1 2002/06/25 07:52:57 cgruber
+ * Add quite a few abstract classes, interfaces, and classes. All API consistent with WebObjects, but with no implementation, nor any private or package access members from the original.
+ *
+ */
diff --git a/projects/net.wotonomy.foundation/src/main/java/net/wotonomy/foundation/NSCoding.java b/projects/net.wotonomy.foundation/src/main/java/net/wotonomy/foundation/NSCoding.java
new file mode 100644
index 0000000..56e4124
--- /dev/null
+++ b/projects/net.wotonomy.foundation/src/main/java/net/wotonomy/foundation/NSCoding.java
@@ -0,0 +1,371 @@
+/*
+Wotonomy: OpenStep design patterns for pure Java applications.
+Copyright (C) 2002 Israfil consulting Services Corporation
+
+This library is free software; you can redistribute it and/or
+modify it under the terms of the GNU Lesser General Public
+License as published by the Free Software Foundation; either
+version 2.1 of the License, or (at your option) any later version.
+
+This library is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+Lesser General Public License for more details.
+
+You should have received a copy of the GNU Lesser General Public
+License along with this library; if not, see http://www.gnu.org
+
+$Id: NSCoding.java 892 2006-02-16 12:47:16Z cgruber $
+
+*/
+
+package net.wotonomy.foundation;
+
+import java.math.BigInteger;
+
+/**
+* A helper interface supporting the NSCoder APIs. At present it
+* very confusing to me how this even works from a structural
+* perspective, but to be consistent with WebObjects APIs, this will
+* have to be properly implemented.
+*
+* @author cgruber@israfil.net
+* @author $Author: cgruber $
+* @version $Revision: 892 $
+*/
+
+public interface NSCoding {
+
+ /** Not yet implemented */
+ public static class _BigDecimalSupport extends _BigIntegerSupport {
+
+ /** Not yet implemented */
+ public void encodeWithCoder(Object obj, NSCoder nscoder) {
+ throw new UnsupportedOperationException("Not Yet Implemented");
+ }
+
+ /** Not yet implemented */
+ public Object decodeObject(NSCoder nscoder) {
+ throw new UnsupportedOperationException("Not Yet Implemented");
+ }
+
+ /** Not yet implemented */
+ public _BigDecimalSupport() {
+ throw new UnsupportedOperationException("Not Yet Implemented");
+ }
+ }
+
+ /** Not yet implemented */
+ public static class _BigIntegerSupport extends Support {
+
+ /** Not yet implemented */
+ public void encodeWithCoder(Object obj, NSCoder nscoder) {
+ throw new UnsupportedOperationException("Not Yet Implemented");
+ }
+
+ /** Not yet implemented */
+ public Object decodeObject(NSCoder nscoder) {
+ throw new UnsupportedOperationException("Not Yet Implemented");
+ }
+
+ /** Not yet implemented */
+ protected static void _encodeBigInteger(
+ NSCoder nscoder,
+ BigInteger biginteger) {
+ throw new UnsupportedOperationException("Not Yet Implemented");
+ }
+
+ /** Not yet implemented */
+ protected static BigInteger _decodeBigInteger(NSCoder nscoder, int i) {
+ throw new UnsupportedOperationException("Not Yet Implemented");
+ }
+
+ /** Not yet implemented */
+ public _BigIntegerSupport() {
+ throw new UnsupportedOperationException("Not Yet Implemented");
+ }
+ }
+
+ /** Not yet implemented */
+ public static class _DoubleSupport extends _NumberSupport {
+
+ /** Not yet implemented */
+ public void encodeWithCoder(Object obj, NSCoder nscoder) {
+ throw new UnsupportedOperationException("Not Yet Implemented");
+ }
+
+ /** Not yet implemented */
+ public _DoubleSupport() {
+ throw new UnsupportedOperationException("Not Yet Implemented");
+ }
+ }
+
+ /** Not yet implemented */
+ public static class _FloatSupport extends _NumberSupport {
+
+ /** Not yet implemented */
+ public void encodeWithCoder(Object obj, NSCoder nscoder) {
+ throw new UnsupportedOperationException("Not Yet Implemented");
+ }
+
+ /** Not yet implemented */
+ public _FloatSupport() {
+ throw new UnsupportedOperationException("Not Yet Implemented");
+ }
+ }
+
+ /** Not yet implemented */
+ public static class _LongSupport extends _NumberSupport {
+
+ /** Not yet implemented */
+ public void encodeWithCoder(Object obj, NSCoder nscoder) {
+ throw new UnsupportedOperationException("Not Yet Implemented");
+ }
+
+ /** Not yet implemented */
+ public _LongSupport() {
+ throw new UnsupportedOperationException("Not Yet Implemented");
+ }
+ }
+
+ /** Not yet implemented */
+ public static class _IntegerSupport extends _NumberSupport {
+
+ /** Not yet implemented */
+ public void encodeWithCoder(Object obj, NSCoder nscoder) {
+ throw new UnsupportedOperationException("Not Yet Implemented");
+ }
+
+ /** Not yet implemented */
+ public _IntegerSupport() {
+ throw new UnsupportedOperationException("Not Yet Implemented");
+ }
+ }
+
+ /** Not yet implemented */
+ public static class _ShortSupport extends _NumberSupport {
+
+ /** Not yet implemented */
+ public void encodeWithCoder(Object obj, NSCoder nscoder) {
+ throw new UnsupportedOperationException("Not Yet Implemented");
+ }
+
+ /** Not yet implemented */
+ public _ShortSupport() {
+ throw new UnsupportedOperationException("Not Yet Implemented");
+ }
+ }
+
+ /** Not yet implemented */
+ public static class _ByteSupport extends _NumberSupport {
+
+ /** Not yet implemented */
+ public void encodeWithCoder(Object obj, NSCoder nscoder) {
+ throw new UnsupportedOperationException("Not Yet Implemented");
+ }
+
+ /** Not yet implemented */
+ public _ByteSupport() {
+ throw new UnsupportedOperationException("Not Yet Implemented");
+ }
+ }
+
+ /** Not yet implemented */
+ public static class _NumberSupport extends Support {
+
+ /** Not yet implemented */
+ public Class classForCoder(Object obj) {
+ throw new UnsupportedOperationException("Not Yet Implemented");
+ }
+
+ /** Not yet implemented */
+ public void encodeWithCoder(Object obj, NSCoder nscoder) {
+ throw new UnsupportedOperationException("Not Yet Implemented");
+ }
+
+ /** Not yet implemented */
+ public Object decodeObject(NSCoder nscoder) {
+ throw new UnsupportedOperationException("Not Yet Implemented");
+ }
+
+ /** Not yet implemented */
+ public _NumberSupport() {
+ throw new UnsupportedOperationException("Not Yet Implemented");
+ }
+ }
+
+ /** Not yet implemented */
+ public static class _CharacterSupport extends Support {
+
+ /** Not yet implemented */
+ public void encodeWithCoder(Object obj, NSCoder nscoder) {
+ throw new UnsupportedOperationException("Not Yet Implemented");
+ }
+
+ /** Not yet implemented */
+ public Object decodeObject(NSCoder nscoder) {
+ throw new UnsupportedOperationException("Not Yet Implemented");
+ }
+
+ /** Not yet implemented */
+ public _CharacterSupport() {
+ throw new UnsupportedOperationException("Not Yet Implemented");
+ }
+ }
+
+ /** Not yet implemented */
+ public static class _DateSupport extends Support {
+
+ /** Not yet implemented */
+ public void encodeWithCoder(Object obj, NSCoder nscoder) {
+ throw new UnsupportedOperationException("Not Yet Implemented");
+ }
+
+ /** Not yet implemented */
+ public Object decodeObject(NSCoder nscoder) {
+ throw new UnsupportedOperationException("Not Yet Implemented");
+ }
+
+ /** Not yet implemented */
+ public _DateSupport() {
+ throw new UnsupportedOperationException("Not Yet Implemented");
+ }
+ }
+
+ /** Not yet implemented */
+ public static class _StringSupport extends Support {
+
+ /** Not yet implemented */
+ public void encodeWithCoder(Object obj, NSCoder nscoder) {
+ throw new UnsupportedOperationException("Not Yet Implemented");
+ }
+
+ /** Not yet implemented */
+ public Object decodeObject(NSCoder nscoder) {
+ throw new UnsupportedOperationException("Not Yet Implemented");
+ }
+
+ /** Not yet implemented */
+ public _StringSupport() {
+ throw new UnsupportedOperationException("Not Yet Implemented");
+ }
+ }
+
+ /** Not yet implemented */
+ public static class _BooleanSupport extends Support {
+
+ /** Not yet implemented */
+ public void encodeWithCoder(Object obj, NSCoder nscoder) {
+ throw new UnsupportedOperationException("Not Yet Implemented");
+ }
+
+ /** Not yet implemented */
+ public Object decodeObject(NSCoder nscoder) {
+ throw new UnsupportedOperationException("Not Yet Implemented");
+ }
+
+ /** Not yet implemented */
+ public _BooleanSupport() {
+ throw new UnsupportedOperationException("Not Yet Implemented");
+ }
+ }
+
+ /** Helper class for NSCoding. */
+ public static abstract class Support {
+
+ private static NSMutableDictionary classSupportMap =
+ new NSMutableDictionary(16);
+
+ public static Support supportForClass(Class aClass) {
+ Support support = null;
+ Class realClass = aClass;
+ while (support == null && realClass != null) {
+ support = (Support) classSupportMap.objectForKey(realClass);
+ if (support == null)
+ realClass.getSuperclass();
+ // Cache if we had to look to a superclass.
+ else if (aClass != realClass)
+ classSupportMap.setObjectForKey(support, aClass);
+ }
+ return support;
+ }
+
+ public static void setSupportForClass(Support support, Class class1) {
+ classSupportMap.setObjectForKey(support, class1);
+ }
+
+ /** Return the class of a given object. It boggles the mind
+ * as to why this is not a static, but in the original, it's
+ * not. &lt;sigh&gt;
+ */
+ public Class classForCoder(Object obj) {
+ return obj.getClass();
+ }
+
+ public abstract void encodeWithCoder(Object obj, NSCoder nscoder);
+
+ public abstract Object decodeObject(NSCoder nscoder);
+
+ protected static void _encodeUTF8(String s, NSCoder nscoder) {
+ throw new UnsupportedOperationException("Not Yet Implemented");
+ }
+
+ protected static String _decodeUTF8(NSCoder nscoder) {
+ throw new UnsupportedOperationException("Not Yet Implemented");
+ }
+
+ static {
+ setSupportForClass(new _StringSupport(), java.lang.String.class);
+ setSupportForClass(new _BooleanSupport(), java.lang.Boolean.class);
+ setSupportForClass(new _NumberSupport(), null);
+ setSupportForClass(new _ByteSupport(), java.lang.Byte.class);
+ setSupportForClass(new _ShortSupport(), java.lang.Short.class);
+ setSupportForClass(new _IntegerSupport(), java.lang.Integer.class);
+ setSupportForClass(new _LongSupport(), java.lang.Long.class);
+ setSupportForClass(new _FloatSupport(), java.lang.Float.class);
+ setSupportForClass(new _DoubleSupport(), java.lang.Double.class);
+ setSupportForClass(
+ new _BigIntegerSupport(),
+ java.math.BigInteger.class);
+ setSupportForClass(
+ new _BigDecimalSupport(),
+ java.math.BigDecimal.class);
+ setSupportForClass(new _DateSupport(), java.util.Date.class);
+ setSupportForClass(
+ new _CharacterSupport(),
+ java.lang.Character.class);
+ }
+
+ public Support() {
+ }
+ }
+
+ //CEG: I'm not sure why these are here, since NSCoding is an interface.
+ // It doesn't seem to even be used anywhere.
+ //CEG: I'm not even sure why this compiles!!
+ //public abstract Class classForCoder();
+
+ //CEG: I'm not sure why these are here, since NSCoding is an interface.
+ // It doesn't seem to even be used anywhere.
+ //CEG: I'm not even sure why this compiles!!
+ //public abstract void encodeWithCoder(NSCoder nscoder);
+
+}
+/*
+ * $Log$
+ * Revision 1.1 2006/02/16 12:47:16 cgruber
+ * Check in all sources in eclipse-friendly maven-enabled packages.
+ *
+ * Revision 1.2 2003/08/06 23:07:52 chochos
+ * general code cleanup (mostly, removing unused imports)
+ *
+ * Revision 1.1 2002/07/14 21:56:16 mpowers
+ * Contributions from cgruber.
+ *
+ * Revision 1.2 2002/06/25 19:03:02 cgruber
+ * Internal documentation fixes.
+ *
+ * Revision 1.1 2002/06/25 07:52:56 cgruber
+ * Add quite a few abstract classes, interfaces, and classes. All API consistent with WebObjects, but with no implementation, nor any private or package access members from the original.
+ *
+ */
diff --git a/projects/net.wotonomy.foundation/src/main/java/net/wotonomy/foundation/NSComparator.java b/projects/net.wotonomy.foundation/src/main/java/net/wotonomy/foundation/NSComparator.java
new file mode 100644
index 0000000..287a59b
--- /dev/null
+++ b/projects/net.wotonomy.foundation/src/main/java/net/wotonomy/foundation/NSComparator.java
@@ -0,0 +1,104 @@
+/*
+Wotonomy: OpenStep design patterns for pure Java applications.
+Copyright (C) 2002 Israfil consulting Services Corporation
+
+This library is free software; you can redistribute it and/or
+modify it under the terms of the GNU Lesser General Public
+License as published by the Free Software Foundation; either
+version 2.1 of the License, or (at your option) any later version.
+
+This library is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+Lesser General Public License for more details.
+
+You should have received a copy of the GNU Lesser General Public
+License along with this library; if not, see http://www.gnu.org
+
+$Id: NSComparator.java 913 2006-03-10 00:52:27Z cgruber $
+
+*/
+
+package net.wotonomy.foundation;
+
+import java.util.Comparator;
+
+
+/**
+* An object that compares two other objects. As a convenience, it
+* also implements java.util.Comparator.
+*
+* @author cgruber@israfil.net
+* @author $Author: cgruber $
+* @version $Revision: 913 $
+*/
+
+public abstract class NSComparator implements Comparator {
+ protected static class _NSSelectorComparator extends NSComparator {
+
+ public int compare(Object obj, Object obj1) throws ComparisonException {
+ throw new UnsupportedOperationException("Not Yet Implemented");
+ }
+
+ public _NSSelectorComparator(NSSelector nsselector) {
+ throw new UnsupportedOperationException("Not Yet Implemented");
+ }
+ }
+
+ private static class StandInComparator extends NSComparator {
+
+ public StandInComparator() {
+ throw new UnsupportedOperationException("Not Yet Implemented");
+ }
+
+ public int compare(Object obj, Object obj1) throws ComparisonException {
+ throw new UnsupportedOperationException("Not Yet Implemented");
+ }
+
+ }
+
+ public static class ComparisonException extends ClassCastException {
+
+ public ComparisonException(String s) {
+ super(s);
+ }
+ }
+
+
+ public static final NSComparator AscendingStringComparator = new StandInComparator();
+ public static final NSComparator DescendingStringComparator = new StandInComparator();
+ public static final NSComparator AscendingCaseInsensitiveStringComparator = new StandInComparator();
+ public static final NSComparator DescendingCaseInsensitiveStringComparator = new StandInComparator();
+ public static final NSComparator AscendingNumberComparator = new StandInComparator();
+ public static final NSComparator DescendingNumberComparator = new StandInComparator();
+ public static final NSComparator AscendingTimestampComparator = new StandInComparator();
+ public static final NSComparator DescendingTimestampComparator = new StandInComparator();
+ public static final int OrderedAscending = -1;
+ public static final int OrderedSame = 0;
+ public static final int OrderedDescending = 1;
+
+ public NSComparator() {
+ }
+
+ public abstract int compare(Object obj, Object obj1) throws ClassCastException;
+
+ public static int _compareObjects(Comparable comparable, Comparable comparable1) {
+ throw new UnsupportedOperationException("Not Yet Implemented");
+ }
+
+}
+/*
+ * $Log$
+ * Revision 1.2 2006/03/10 00:52:27 cgruber
+ * Add tests for NSArray and fix some problems that became obvious as a result.
+ *
+ * Revision 1.1 2006/02/16 12:47:16 cgruber
+ * Check in all sources in eclipse-friendly maven-enabled packages.
+ *
+ * Revision 1.1 2002/07/14 21:56:16 mpowers
+ * Contributions from cgruber.
+ *
+ * Revision 1.1 2002/06/25 07:52:56 cgruber
+ * Add quite a few abstract classes, interfaces, and classes. All API consistent with WebObjects, but with no implementation, nor any private or package access members from the original.
+ *
+ */
diff --git a/projects/net.wotonomy.foundation/src/main/java/net/wotonomy/foundation/NSData.java b/projects/net.wotonomy.foundation/src/main/java/net/wotonomy/foundation/NSData.java
new file mode 100644
index 0000000..36c527c
--- /dev/null
+++ b/projects/net.wotonomy.foundation/src/main/java/net/wotonomy/foundation/NSData.java
@@ -0,0 +1,263 @@
+/*
+Wotonomy: OpenStep design patterns for pure Java applications.
+Copyright (C) 2000 Blacksmith, Inc.
+
+This library is free software; you can redistribute it and/or
+modify it under the terms of the GNU Lesser General Public
+License as published by the Free Software Foundation; either
+version 2.1 of the License, or (at your option) any later version.
+
+This library is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+Lesser General Public License for more details.
+
+You should have received a copy of the GNU Lesser General Public
+License along with this library; if not, see http://www.gnu.org
+*/
+
+package net.wotonomy.foundation;
+
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+
+/**
+* A pure java implementation of NSData, which
+* is basically a wrapper on a byte array.
+*
+* @author michael@mpowers.net
+* @author $Author: cgruber $
+* @version $Revision: 893 $
+*/
+public class NSData
+{
+ public static final NSData EmptyData = new NSData();
+
+ protected byte[] bytes;
+
+ /**
+ * Default constructor creates a zero-data object.
+ */
+ public NSData ()
+ {
+ bytes = new byte[0];
+ }
+
+ /**
+ * Creates an object containing a copy of the specified bytes.
+ */
+ public NSData (byte[] data)
+ {
+ this( data, 0, data.length );
+ }
+
+ /**
+ * Creates an object containing a copy of the bytes from the specified
+ * array within the specified range.
+ */
+ public NSData (byte[] data, int start, int length)
+ {
+ bytes = new byte[ length ];
+ for ( int i = 0; i < length; i++ )
+ {
+ bytes[i] = data[ start+i ];
+ }
+ }
+
+ /**
+ * Creates an object containing the bytes of the specified string.
+ */
+ public NSData (String aString)
+ {
+ this( aString.getBytes() );
+ }
+
+ /**
+ * Creates an object containing the contents of the specified file.
+ * Errors reading the file will produce an empty or partially blank array.
+ */
+ public NSData (File aFile)
+ {
+ int len = (int) aFile.length();
+ byte[] data = new byte[ len ];
+ try
+ {
+ new java.io.FileInputStream( aFile ).read( data );
+ }
+ catch ( Exception exc )
+ {
+ // produce an empty or partially blank array
+ }
+ bytes = data;
+ }
+
+ /**
+ * Creates an object containing the contents of the specified URL.
+ */
+ public NSData (java.net.URL aURL)
+ {
+ throw new RuntimeException( "Not Implemented" );
+ }
+
+ /**
+ * Creates an object containing a copy of the contents of the
+ * specified NSData object.
+ */
+ public NSData (NSData aData)
+ {
+ this( aData.bytes() );
+ }
+
+ /**
+ * Creates a new NSData object from the bytes in the input stream.
+ * The input stream is read fully and is not closed.
+ * @param stream The stream to read from.
+ * @param chunkSize The buffer size used to read from the stream.
+ * @throws IOException if the stream cannot be read from.
+ */
+ public NSData(InputStream stream, int chunkSize) throws IOException {
+ super();
+ byte[] b = new byte[chunkSize];
+ ByteArrayOutputStream bout = new ByteArrayOutputStream();
+ int read = 0;
+ do {
+ read = stream.read(b);
+ if (read > 0)
+ bout.write(b, 0, read);
+ } while (read > 0);
+ bytes = bout.toByteArray();
+ }
+
+ /**
+ * Returns the length of the contained data.
+ */
+ public int length ()
+ {
+ return bytes.length;
+ }
+
+ /**
+ * Returns whether the specified data is equivalent to these data.
+ */
+ public boolean isEqualToData (NSData aData)
+ {
+ if (length() != aData.length())
+ return false;
+ byte[] a = bytes();
+ byte[] b = aData.bytes();
+
+ for ( int i = 0; i < a.length; i++ ) {
+ if ( a[i] != b[i] )
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * Return the bytes within the data that fall within the specified range.
+ */
+ public NSData subdataWithRange (NSRange aRange)
+ {
+ int loc = aRange.location();
+ byte[] src = bytes();
+ byte[] data = new byte[ aRange.length() ];
+ System.arraycopy(src, loc, data, 0, data.length);
+ return new NSData( data );
+ }
+
+ /**
+ * Writes the contents of this data to the specified URL.
+ * If atomically is true, then the data is written to a temporary
+ * file and then renamed to the name specified by the URL when
+ * the data transfer is complete.
+ */
+ public boolean writeToURL (java.net.URL aURL, boolean atomically)
+ {
+ throw new RuntimeException( "Not Implemented" );
+ }
+
+ /**
+ * Convenience to return the contents of the specified file.
+ */
+ public static NSData dataWithContentsOfMappedFile (java.io.File aFile)
+ {
+ return new NSData( aFile );
+ }
+
+ /**
+ * Returns a copy of the bytes starting at the specified location
+ * and ranging for the specified length.
+ */
+ public byte[] bytes (int location, int length)
+ {
+ byte[] data = new byte[ length ];
+ for ( int i = 0; i < length; i++ )
+ {
+ data[i] = bytes[ location + i ];
+ }
+ return data;
+ }
+
+ /**
+ * Returns a copy of the bytes backing this data object.
+ * NOTE: This method is not in the NSData spec and is
+ * included for convenience only.
+ */
+ public byte[] bytes()
+ {
+ return bytes( 0, length() );
+ }
+
+ public String toString() {
+ String hex = "0123456789ABCDEF";
+ StringBuffer buf = new StringBuffer();
+ buf.append(NSPropertyListSerialization.TOKEN_BEGIN[NSPropertyListSerialization.PLIST_DATA]);
+ for (int i = 0; i < bytes.length; i++) {
+ byte b = bytes[i];
+ buf.append(hex.charAt((b & 0xf0) >> 4));
+ buf.append(hex.charAt(b & 0x0f));
+ if (i % 5 == 4)
+ buf.append(' ');
+ }
+ buf.append(NSPropertyListSerialization.TOKEN_END[NSPropertyListSerialization.PLIST_DATA]);
+ return buf.toString();
+ }
+
+ public boolean isEqual(Object obj) {
+ if (obj == this)
+ return true;
+ if (obj instanceof NSData)
+ return isEqualToData((NSData)obj);
+ return false;
+ }
+
+}
+
+/*
+ * $Log$
+ * Revision 1.2 2006/02/16 13:15:00 cgruber
+ * Check in all sources in eclipse-friendly maven-enabled packages.
+ *
+ * Revision 1.5 2003/08/19 01:53:52 chochos
+ * added constructor with an InputStream
+ *
+ * Revision 1.4 2003/08/05 00:51:31 chochos
+ * get the enclosing tokens from NSPropertyListSerialization
+ *
+ * Revision 1.3 2003/08/04 22:45:47 chochos
+ * toString() prints out the bytes in hex (in property list format)
+ *
+ * Revision 1.2 2003/08/02 01:52:00 chochos
+ * added EmptyData
+ *
+ * Revision 1.1.1.1 2000/12/21 15:47:26 mpowers
+ * Contributing wotonomy.
+ *
+ * Revision 1.3 2000/12/20 16:25:38 michael
+ * Added log to all files.
+ *
+ *
+ */
+
diff --git a/projects/net.wotonomy.foundation/src/main/java/net/wotonomy/foundation/NSDate.java b/projects/net.wotonomy.foundation/src/main/java/net/wotonomy/foundation/NSDate.java
new file mode 100644
index 0000000..d5b6f61
--- /dev/null
+++ b/projects/net.wotonomy.foundation/src/main/java/net/wotonomy/foundation/NSDate.java
@@ -0,0 +1,217 @@
+/*
+Wotonomy: OpenStep design patterns for pure Java applications.
+Copyright (C) 2000 Blacksmith, Inc.
+
+This library is free software; you can redistribute it and/or
+modify it under the terms of the GNU Lesser General Public
+License as published by the Free Software Foundation; either
+version 2.1 of the License, or (at your option) any later version.
+
+This library is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+Lesser General Public License for more details.
+
+You should have received a copy of the GNU Lesser General Public
+License along with this library; if not, see http://www.gnu.org
+*/
+
+package net.wotonomy.foundation;
+
+import java.util.Date;
+import java.util.GregorianCalendar;
+import java.util.TimeZone;
+
+/**
+* A pure java implementation of NSDate that extends
+* java.util.Date for greater java compatibility.
+*
+* @author michael@mpowers.net
+* @author $Author: cgruber $
+* @version $Revision: 892 $
+*/
+public class NSDate extends Date
+{
+ // NSComparisonResult compatibility
+
+ public static final int NSOrderedAscending = -1;
+ public static final int NSOrderedSame = 0;
+ public static final int NSOrderedDescending = 1;
+
+// public static final double TimeIntervalSince1970;
+// public static final NSDate DateFor1970;
+
+ /**
+ * Default constructor represents the current date.
+ */
+ public NSDate ()
+ {
+ super();
+ }
+
+ /**
+ * Represents the specified number of seconds from the current date.
+ */
+ public NSDate (double seconds)
+ {
+ super( (long) new NSDate().getTime() +
+ timeIntervalToMilliseconds(seconds) );
+ }
+
+ /**
+ * Represents the specified number of seconds from the specified date.
+ */
+ public NSDate (double seconds, Date sinceDate)
+ {
+ super( (long) sinceDate.getTime() +
+ timeIntervalToMilliseconds(seconds) );
+ }
+
+ /**
+ * Returns the interval between this date and 1 January 2001 GMT.
+ */
+ public double timeIntervalSinceReferenceDate ()
+ {
+ GregorianCalendar referenceDate =
+ new GregorianCalendar( TimeZone.getTimeZone( "GMT" ) );
+ referenceDate.set( 2001, 0, 0, 0, 0, 0 );
+ return timeIntervalSinceDate( referenceDate.getTime() );
+ }
+
+ /**
+ * Returns the interval between this date and the specified date
+ * in seconds.
+ */
+ public double timeIntervalSinceDate (Date aDate)
+ {
+ return millisecondsToTimeInterval(
+ this.getTime() - aDate.getTime() );
+ }
+
+ /**
+ * Returns the interval between this date and the current date
+ * in seconds.
+ */
+ public double timeIntervalSinceNow ()
+ {
+ return timeIntervalSinceDate( new NSDate() );
+ }
+
+ /**
+ * Compares this date to the specified date and returns the
+ * earlier date. Unspecified which is returned if both are equal.
+ */
+ public NSDate earlierDate (NSDate aDate)
+ {
+ if ( aDate == null ) return this;
+ if ( after( aDate ) ) return aDate;
+ return this;
+ }
+
+ /**
+ * Compares this date to the specified date and returns the
+ * later date. Unspecified which is returned if both are equal.
+ */
+ public NSDate laterDate (NSDate aDate)
+ {
+ if ( aDate == null ) return this;
+ if ( before( aDate ) ) return aDate;
+ return this;
+ }
+
+ /**
+ * Returns a negative value if the specified date is later than
+ * this date, a positive value if the specified date is earlier
+ * than this date, or zero if the dates are equal. The return
+ * values are compatible with type NSComparisonResult.
+ */
+ public int compare (Date aDate)
+ {
+ if ( before( aDate ) ) return NSOrderedAscending;
+ if ( after( aDate ) ) return NSOrderedDescending;
+ return NSOrderedSame;
+ }
+
+ /**
+ * Returns whether the this date is equal to the specified date,
+ * per the result of equals().
+ */
+ public boolean isEqualToDate (Date aDate)
+ {
+ return equals( aDate );
+ }
+
+ /**
+ * Returns a date that differs from this date by the specified
+ * number of seconds.
+ */
+ public NSDate dateByAddingTimeInterval (double seconds)
+ {
+ return new NSDate( seconds, this );
+ }
+
+ /**
+ * Returns the number of seconds between now and the reference date.
+ */
+ public static double currentTimeIntervalSinceReferenceDate ()
+ {
+ return new NSDate().timeIntervalSinceReferenceDate();
+ }
+
+ /**
+ * Converts seconds to milliseconds. Included for compatibility.
+ */
+ public static long timeIntervalToMilliseconds (double seconds)
+ {
+ return (long) seconds*1000;
+ }
+
+ /**
+ * Converts milliseconds to seconds. Included for compatibility.
+ */
+ public static double millisecondsToTimeInterval (long millis)
+ {
+ return millis/1000.0;
+ }
+
+ /**
+ * Returns a date that is greater than all representable dates.
+ */
+ public static NSDate distantFuture ()
+ {
+ NSDate result = new NSDate();
+ result.setTime( Long.MAX_VALUE );
+ return result;
+ }
+
+ /**
+ * Returns a date that is less than all representable dates.
+ */
+ public static NSDate distantPast ()
+ {
+ NSDate result = new NSDate();
+ result.setTime( Long.MIN_VALUE );
+ return result;
+ }
+
+// inherited from java.util.Date
+// public java.lang.String toString ();
+// public boolean equals (java.lang.Object);
+// public int hashCode ();
+
+}
+
+/*
+ * $Log$
+ * Revision 1.1 2006/02/16 12:47:16 cgruber
+ * Check in all sources in eclipse-friendly maven-enabled packages.
+ *
+ * Revision 1.1.1.1 2000/12/21 15:47:28 mpowers
+ * Contributing wotonomy.
+ *
+ * Revision 1.3 2000/12/20 16:25:38 michael
+ * Added log to all files.
+ *
+ *
+ */
+
diff --git a/projects/net.wotonomy.foundation/src/main/java/net/wotonomy/foundation/NSDictionary.java b/projects/net.wotonomy.foundation/src/main/java/net/wotonomy/foundation/NSDictionary.java
new file mode 100644
index 0000000..235a7bb
--- /dev/null
+++ b/projects/net.wotonomy.foundation/src/main/java/net/wotonomy/foundation/NSDictionary.java
@@ -0,0 +1,332 @@
+/*
+Wotonomy: OpenStep design patterns for pure Java applications.
+Copyright (C) 2000 Blacksmith, Inc.
+
+This library is free software; you can redistribute it and/or
+modify it under the terms of the GNU Lesser General Public
+License as published by the Free Software Foundation; either
+version 2.1 of the License, or (at your option) any later version.
+
+This library is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+Lesser General Public License for more details.
+
+You should have received a copy of the GNU Lesser General Public
+License along with this library; if not, see http://www.gnu.org
+*/
+
+package net.wotonomy.foundation;
+
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+
+/**
+* A pure java implementation of NSDictionary that
+* implements Map for greater java interoperability.
+*
+* @author michael@mpowers.net
+* @author cgruber@israfil.net
+* @author $Author: cgruber $
+* @version $Revision: 893 $
+*/
+public class NSDictionary extends HashMap implements NSKeyValueCoding
+{
+ public static final NSDictionary EmptyDictionary = new NSDictionary();
+
+ /**
+ * Default constructor produces an empty dictionary.
+ */
+ public NSDictionary ()
+ {
+ super();
+ }
+
+ /**
+ * Constructor produces an empty dictionary with an initial capacity.
+ */
+ public NSDictionary (int initialCapacity)
+ {
+ super(initialCapacity);
+ }
+
+ /**
+ * Produces a dictionary that contains one key referencing one value.
+ */
+ public NSDictionary (Object key, Object value)
+ {
+ super();
+ put( key, value );
+ }
+
+ /**
+ * Produces a dictionary containing the specified keys and values.
+ * An IllegalArgumentException is thrown if the arrays are not
+ * of the same length.
+ */
+ public NSDictionary (Object[] objects, Object[] keys)
+ {
+ super();
+ if ( keys.length != objects.length )
+ {
+ throw new IllegalArgumentException( "Array lengths do not match." );
+ }
+
+ for ( int i = 0; i < keys.length; i++ )
+ {
+ put( keys[i], objects[i] );
+ }
+ }
+
+ /**
+ * Produces a dictionary that is a copy of the specified map (or dictionary).
+ */
+ public NSDictionary (Map aMap)
+ {
+ super( aMap );
+ }
+
+ /**
+ * Returns a count of the key-value pairs in this dictionary.
+ */
+ public int count ()
+ {
+ return size();
+ }
+
+ /**
+ * Returns an NSArray containing all keys in this dictionary.
+ */
+ public NSArray allKeys ()
+ {
+ return new NSArray( keySet() );
+
+ }
+
+ /**
+ * Returns an NSArray containing all keys that reference the
+ * specified value.
+ */
+ public NSArray allKeysForObject (Object value)
+ {
+ NSMutableArray result = new NSMutableArray();
+ Map.Entry entry;
+ Iterator it = entrySet().iterator();
+
+ while ( it.hasNext() )
+ {
+ entry = (Map.Entry) it.next();
+
+ // handle null values
+ if ( ( value == null ) && ( entry.getValue() == null )
+ || ( value.equals( entry.getValue() ) ) )
+ {
+ // if match, add to result set
+ result.addObject( entry.getKey() );
+ }
+ }
+
+ return result;
+ }
+
+ /**
+ * Returns an NSArray containing all values in this dictionary.
+ */
+ public NSArray allValues ()
+ {
+ return new NSArray( values() );
+ }
+
+ /**
+ * Returns whether the specified dictionary has the same or
+ * equivalent key-value pairs as this dictionary.
+ */
+ public boolean isEqualToDictionary (NSDictionary aDictionary)
+ {
+ return equals( aDictionary );
+ }
+
+ /**
+ * Returns an array of objects for the specified array of keys.
+ * If a key isn't found, the marker parameter will be placed
+ * in the corresponding index(es) in the returned array.
+ */
+ public NSArray objectsForKeys (NSArray anArray, Object aMarker)
+ {
+ NSMutableArray result = new NSMutableArray();
+ if ( anArray == null ) return result;
+
+ Object value;
+ Enumeration enumeration = anArray.objectEnumerator();
+ while ( enumeration.hasMoreElements() )
+ {
+ value = objectForKey( enumeration.nextElement() );
+ if ( value == null )
+ {
+ value = aMarker;
+ }
+ result.addObject( value );
+ }
+ return result;
+ }
+
+ /**
+ * Returns an enumeration over the keys in this dictionary.
+ */
+ public java.util.Enumeration keyEnumerator ()
+ {
+ return new java.util.Enumeration()
+ {
+ Iterator it = NSDictionary.this.keySet().iterator();
+ public boolean hasMoreElements()
+ {
+ return it.hasNext();
+ }
+ public Object nextElement()
+ {
+ return it.next();
+ }
+ };
+ }
+
+ /**
+ * Returns an enumeration over the values in this dictionary.
+ */
+ public java.util.Enumeration objectEnumerator ()
+ {
+ return new java.util.Enumeration()
+ {
+ Iterator it = NSDictionary.this.values().iterator();
+ public boolean hasMoreElements()
+ {
+ return it.hasNext();
+ }
+ public Object nextElement()
+ {
+ return it.next();
+ }
+ };
+ }
+
+ /**
+ * Returns the value for the specified key, or null
+ * if the key is not found.
+ */
+ public Object objectForKey (Object aKey)
+ {
+ return get( aKey );
+ }
+
+ // interface NSKeyValueCoding
+
+ public Object valueForKey (String aKey)
+ { // System.out.println( "valueForKey: " + aKey + "->" + this );
+ Object result = objectForKey( aKey );
+ if ( result == null )
+ result = NSKeyValueCodingSupport.valueForKey( this, aKey );
+ return result;
+ }
+
+ public void takeValueForKey (Object aValue, String aKey)
+ { // System.out.println( "takeValueForKey: " + aKey + " : " + aValue + "->" + this );
+ put( aKey, aValue ); //FIXME: technically cheating since this is a read-only class
+ }
+
+ public Object storedValueForKey (String aKey)
+ {
+ Object result = objectForKey( aKey );
+ if ( result == null )
+ result = NSKeyValueCodingSupport.storedValueForKey( this, aKey );
+ return result;
+ }
+
+ public void takeStoredValueForKey (Object aValue, String aKey)
+ {
+ put( aKey, aValue ); //FIXME: technically cheating since this is a read-only class
+ }
+
+ public Object handleQueryWithUnboundKey (String aKey)
+ {
+ return NSKeyValueCodingSupport.handleQueryWithUnboundKey( this, aKey );
+ }
+
+ public void handleTakeValueForUnboundKey (Object aValue, String aKey)
+ {
+ NSKeyValueCodingSupport.handleTakeValueForUnboundKey( this, aValue, aKey );
+ }
+
+ public void unableToSetNullForKey (String aKey)
+ {
+ NSKeyValueCodingSupport.unableToSetNullForKey( this, aKey );
+ }
+
+ public Object validateTakeValueForKeyPath (Object aValue, String aKey)
+ {
+ throw new RuntimeException( "Not implemented yet." );
+ }
+
+ public String toString() {
+ StringBuffer buf = new StringBuffer();
+ Enumeration enumeration = keyEnumerator();
+ boolean quote = false;
+ buf.append(NSPropertyListSerialization.TOKEN_BEGIN[NSPropertyListSerialization.PLIST_DICTIONARY]);
+ while (enumeration.hasMoreElements()) {
+ if (buf.length() == 1)
+ buf.append(' ');
+ Object k = enumeration.nextElement();
+ buf.append(NSPropertyListSerialization.stringForPropertyList(k));
+ buf.append(" = ");
+ k = objectForKey(k);
+ buf.append(NSPropertyListSerialization.stringForPropertyList(k));
+ buf.append("; ");
+ }
+ buf.append(NSPropertyListSerialization.TOKEN_END[NSPropertyListSerialization.PLIST_DICTIONARY]);
+ return buf.toString();
+ }
+
+}
+
+/*
+ * $Log$
+ * Revision 1.2 2006/02/16 13:15:00 cgruber
+ * Check in all sources in eclipse-friendly maven-enabled packages.
+ *
+ * Revision 1.9 2005/05/11 15:21:53 cgruber
+ * Change enum to enumeration, since enum is now a keyword as of Java 5.0
+ *
+ * A few other comments in the code.
+ *
+ * Revision 1.8 2003/08/05 00:50:14 chochos
+ * use NSPropertyListSerialization to get the tokens to enclose the string description
+ *
+ * Revision 1.7 2003/08/04 20:26:10 chochos
+ * use NSPropertyListSerialization inside toString()
+ *
+ * Revision 1.6 2003/08/04 18:49:38 chochos
+ * NSDictionary(Object[], Object[]) was taking the parameters in the wrong order; for compatibility with Apple's NSDictionary, objects comes first, then keys.
+ *
+ * Revision 1.5 2003/08/04 18:26:19 chochos
+ * fixed opening '{'
+ *
+ * Revision 1.4 2003/01/28 22:11:30 mpowers
+ * Now implements NSKeyValueCoding.
+ *
+ * Revision 1.3 2002/06/30 17:58:06 mpowers
+ * Add a capacity constructor and static empty dictionary: thanks cgruber.
+ *
+ * Revision 1.2 2001/02/23 23:43:41 mpowers
+ * Removed ill-advised this.
+ *
+ * Revision 1.1.1.1 2000/12/21 15:47:31 mpowers
+ * Contributing wotonomy.
+ *
+ * Revision 1.3 2000/12/20 16:25:38 michael
+ * Added log to all files.
+ *
+ *
+ */
+
+
+
diff --git a/projects/net.wotonomy.foundation/src/main/java/net/wotonomy/foundation/NSDisposable.java b/projects/net.wotonomy.foundation/src/main/java/net/wotonomy/foundation/NSDisposable.java
new file mode 100644
index 0000000..46df7d2
--- /dev/null
+++ b/projects/net.wotonomy.foundation/src/main/java/net/wotonomy/foundation/NSDisposable.java
@@ -0,0 +1,35 @@
+/*
+Wotonomy: OpenStep design patterns for pure Java applications.
+Copyright (C) 2002 Israfil consulting Services Corporation
+
+This library is free software; you can redistribute it and/or
+modify it under the terms of the GNU Lesser General Public
+License as published by the Free Software Foundation; either
+version 2.1 of the License, or (at your option) any later version.
+
+This library is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+Lesser General Public License for more details.
+
+You should have received a copy of the GNU Lesser General Public
+License along with this library; if not, see http://www.gnu.org
+
+$Id: NSDisposable.java 893 2006-02-16 13:22:23Z cgruber $
+
+*/
+
+package net.wotonomy.foundation;
+
+/**
+* Interface for objects that respond to dispose();
+*
+* @author cgruber@israfil.net
+* @author $Author: cgruber $
+* @version $Revision: 893 $
+*/
+public interface NSDisposable {
+
+ public abstract void dispose();
+
+}
diff --git a/projects/net.wotonomy.foundation/src/main/java/net/wotonomy/foundation/NSForwardException.java b/projects/net.wotonomy.foundation/src/main/java/net/wotonomy/foundation/NSForwardException.java
new file mode 100644
index 0000000..1db0ad2
--- /dev/null
+++ b/projects/net.wotonomy.foundation/src/main/java/net/wotonomy/foundation/NSForwardException.java
@@ -0,0 +1,157 @@
+/*
+Wotonomy: OpenStep design patterns for pure Java applications.
+Copyright (C) 2000 Michael Powers
+
+This library is free software; you can redistribute it and/or
+modify it under the terms of the GNU Lesser General Public
+License as published by the Free Software Foundation; either
+version 2.1 of the License, or (at your option) any later version.
+
+This library is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+Lesser General Public License for more details.
+
+You should have received a copy of the GNU Lesser General Public
+License along with this library; if not, see http://www.gnu.org
+*/
+
+package net.wotonomy.foundation;
+
+import java.io.PrintStream;
+import java.io.PrintWriter;
+import java.io.StringWriter;
+
+/**
+* Serves to wrap an exception inside of a RuntimeException,
+* which is not required to be declared in a throws statement.
+*
+* @author michael@mpowers.net
+* @author $Author: cgruber $
+* @version $Revision: 893 $
+*/
+
+public class NSForwardException extends RuntimeException
+{
+ protected String message;
+ protected Throwable wrappedThrowable;
+
+ /**
+ * Default constructor.
+ */
+ public NSForwardException()
+ {
+ super();
+ message = null;
+ wrappedThrowable = null;
+ }
+
+ /**
+ * Standard constructor with message.
+ */
+ public NSForwardException( String aMessage )
+ {
+ super( aMessage );
+ message = aMessage;
+ wrappedThrowable = null;
+ }
+
+ /**
+ * Specifies a throwable to wrap.
+ */
+ public NSForwardException( Throwable aThrowable )
+ {
+ super();
+ message = null;
+ wrappedThrowable = aThrowable;
+ }
+
+ /**
+ * Specifies a message and a throwable to wrap.
+ */
+ public NSForwardException( Throwable aThrowable, String aMessage )
+ {
+ super( aMessage );
+ message = aMessage;
+ wrappedThrowable = aThrowable;
+ }
+
+ /**
+ * Returns the wrapped throwable.
+ */
+ public Throwable originalException()
+ {
+ return wrappedThrowable;
+ }
+
+ public void printStackTrace(PrintWriter s)
+ {
+ if ( message != null )
+ {
+ s.println( toString() );
+ }
+ if ( wrappedThrowable != null )
+ {
+ wrappedThrowable.printStackTrace( s );
+ return;
+ }
+ super.printStackTrace( s );
+ }
+
+ public void printStackTrace(PrintStream s)
+ {
+ if ( message != null )
+ {
+ s.println( toString() );
+ }
+ if ( wrappedThrowable != null )
+ {
+ wrappedThrowable.printStackTrace( s );
+ return;
+ }
+ super.printStackTrace( s );
+ }
+
+ public void printStackTrace()
+ {
+ if ( message != null )
+ {
+ System.err.println( toString() );
+ }
+ if ( wrappedThrowable != null )
+ {
+ wrappedThrowable.printStackTrace();
+ return;
+ }
+ super.printStackTrace();
+ }
+
+ public String stackTrace()
+ {
+ StringWriter writer = new StringWriter();
+ PrintWriter printWriter = new PrintWriter( writer );
+ if ( wrappedThrowable != null )
+ {
+ wrappedThrowable.printStackTrace( printWriter );
+ }
+ else
+ {
+ super.printStackTrace( printWriter );
+ }
+ printWriter.flush();
+ printWriter.close();
+ return writer.toString();
+ }
+
+ public String toString()
+ {
+ String result = message;
+ if ( result == null ) result = "";
+ if ( wrappedThrowable != null )
+ {
+ result = wrappedThrowable.toString() + " : " + result;
+ }
+ return result;
+ }
+
+}
diff --git a/projects/net.wotonomy.foundation/src/main/java/net/wotonomy/foundation/NSKeyValueCoding.java b/projects/net.wotonomy.foundation/src/main/java/net/wotonomy/foundation/NSKeyValueCoding.java
new file mode 100644
index 0000000..5792303
--- /dev/null
+++ b/projects/net.wotonomy.foundation/src/main/java/net/wotonomy/foundation/NSKeyValueCoding.java
@@ -0,0 +1,431 @@
+/*
+Wotonomy: OpenStep design patterns for pure Java applications.
+Copyright (C) 2001 Intersect Software Corporation
+
+This library is free software; you can redistribute it and/or
+modify it under the terms of the GNU Lesser General Public
+License as published by the Free Software Foundation; either
+version 2.1 of the License, or (at your option) any later version.
+
+This library is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+Lesser General Public License for more details.
+
+You should have received a copy of the GNU Lesser General Public
+License along with this library; if not, see http://www.gnu.org
+*/
+
+package net.wotonomy.foundation;
+
+import net.wotonomy.foundation.internal.Introspector;
+import net.wotonomy.foundation.internal.IntrospectorException;
+import net.wotonomy.foundation.internal.MissingPropertyException;
+import net.wotonomy.foundation.internal.NullPrimitiveException;
+import net.wotonomy.foundation.internal.WotonomyException;
+
+/**
+* NSKeyValueCoding defines an interface for classes that
+* need to have more control over the wotonomy's property
+* introspection facilities. <br><br>
+*
+* On an object that implements this interface, wotonomy
+* will call these methods, and otherwise use the static
+* methods on NSKeyValueCodingSupport. <br><br>
+*
+* NSKeyValueCodingSupport implements the default behaviors
+* for each of these methods, so classes implementing this
+* interface can call those methods to acheive the same
+* behavior. <br><br>
+*
+* valueForKey and takeValueForKey are called in response
+* to user actions, like viewing an object or updating its
+* value in a user interface. These should call the public
+* getter and setter methods on the object itself and the
+* operations should be subject to validation. <br><br>
+*
+* storedValueForKey and takeStoredValueForKey are called
+* in response to wotonomy actions, like snapshotting,
+* faulting, commits, and reverts. These operations should
+* bypass the public methods and directly modify the internal
+* state of the object without validation.
+*
+* @author michael@mpowers.net
+* @author $Author: cgruber $
+* @version $Revision: 893 $
+*/
+public interface NSKeyValueCoding
+{
+
+ public static final Null NullValue = new Null();
+
+ /**
+ * Returns the value for the specified property.
+ * If the property does not exist, this method should
+ * call handleQueryWithUnboundKey.
+ */
+ Object valueForKey( String aKey );
+
+ /**
+ * Sets the property to the specified value.
+ * If the property does not exist, this method should
+ * call handleTakeValueForUnboundKey.
+ * If the property is of a type that cannot allow
+ * null (e.g. primitive types) and aValue is null,
+ * this method should call unableToSetNullForKey.
+ */
+ void takeValueForKey( Object aValue, String aKey );
+
+ /**
+ * Returns the value for the private field that
+ * corresponds to the specified property.
+ */
+ Object storedValueForKey( String aKey );
+
+ /**
+ * Sets the the private field that corresponds to the
+ * specified property to the specified value.
+ */
+ void takeStoredValueForKey( Object aValue, String aKey );
+
+ /**
+ * Called by valueForKey when the specified key is
+ * not found on this object. Implementing classes
+ * should handle the specified value or otherwise
+ * throw an exception.
+ */
+ Object handleQueryWithUnboundKey( String aKey );
+
+ /**
+ * Called by takeValueForKey when the specified key
+ * is not found on this object. Implementing classes
+ * should handle the specified value or otherwise
+ * throw an exception.
+ */
+ void handleTakeValueForUnboundKey( Object aValue, String aKey );
+
+ /**
+ * Called by takeValueForKey when the type of the
+ * specified key is not allowed to be null, as is
+ * the case with primitive types. Implementing
+ * classes should handle this case appropriately
+ * or otherwise throw an exception.
+ */
+ void unableToSetNullForKey( String aKey );
+
+ /**
+ * Static utility methods that
+ * call the appropriate method if the object implements
+ * NSKeyValueCoding, otherwise calls the method
+ * on DefaultImplementation.
+ */
+ public class Utility
+ {
+ /**
+ * Calls the appropriate method if the object implements
+ * NSKeyValueCoding, otherwise calls the method
+ * on DefaultImplementation.
+ */
+ static public Object valueForKey(
+ Object anObject, String aKey )
+ {
+ if ( anObject instanceof NSKeyValueCoding )
+ {
+ return ((NSKeyValueCoding)anObject).valueForKey( aKey );
+ }
+ return DefaultImplementation.valueForKey( anObject, aKey );
+ }
+
+ /**
+ * Calls the appropriate method if the object implements
+ * NSKeyValueCoding, otherwise calls the method
+ * on DefaultImplementation.
+ */
+ static public void takeValueForKey(
+ Object anObject, Object aValue, String aKey )
+ {
+ if ( anObject instanceof NSKeyValueCoding )
+ {
+ ((NSKeyValueCoding)anObject).takeValueForKey( aValue, aKey );
+ }
+ DefaultImplementation.takeValueForKey( anObject, aValue, aKey );
+ }
+
+ /**
+ * Calls the appropriate method if the object implements
+ * NSKeyValueCoding, otherwise calls the method
+ * on DefaultImplementation.
+ */
+ static public Object storedValueForKey(
+ Object anObject, String aKey )
+ {
+ if ( anObject instanceof NSKeyValueCoding )
+ {
+ return ((NSKeyValueCoding)anObject).storedValueForKey( aKey );
+ }
+ return DefaultImplementation.storedValueForKey( anObject, aKey );
+ }
+
+ /**
+ * Calls the appropriate method if the object implements
+ * NSKeyValueCoding, otherwise calls the method
+ * on DefaultImplementation.
+ */
+ static public void takeStoredValueForKey(
+ Object anObject, Object aValue, String aKey )
+ {
+ if ( anObject instanceof NSKeyValueCoding )
+ {
+ ((NSKeyValueCoding)anObject).takeStoredValueForKey( aValue, aKey );
+ }
+ DefaultImplementation.takeStoredValueForKey( anObject, aValue, aKey );
+ }
+
+ /**
+ * Calls the appropriate method if the object implements
+ * NSKeyValueCoding, otherwise calls the method
+ * on DefaultImplementation.
+ */
+ static public Object handleQueryWithUnboundKey(
+ Object anObject, String aKey )
+ {
+ if ( anObject instanceof NSKeyValueCoding )
+ {
+ return ((NSKeyValueCoding)anObject).handleQueryWithUnboundKey( aKey );
+ }
+ return DefaultImplementation.handleQueryWithUnboundKey( anObject, aKey );
+ }
+
+ /**
+ * Calls the appropriate method if the object implements
+ * NSKeyValueCoding, otherwise calls the method
+ * on DefaultImplementation.
+ */
+ static public void handleTakeValueForUnboundKey(
+ Object anObject, Object aValue, String aKey )
+ {
+ if ( anObject instanceof NSKeyValueCoding )
+ {
+ ((NSKeyValueCoding)anObject).handleTakeValueForUnboundKey( aValue, aKey );
+ }
+ DefaultImplementation.handleTakeValueForUnboundKey( anObject, aValue, aKey );
+ }
+
+ /**
+ * Calls the appropriate method if the object implements
+ * NSKeyValueCoding, otherwise calls the method
+ * on DefaultImplementation.
+ */
+ static public void unableToSetNullForKey(
+ Object anObject, String aKey )
+ {
+ if ( anObject instanceof NSKeyValueCoding )
+ {
+ ((NSKeyValueCoding)anObject).unableToSetNullForKey( aKey );
+ }
+ DefaultImplementation.unableToSetNullForKey( anObject, aKey );
+ }
+ }
+
+ public class DefaultImplementation
+ {
+ /**
+ * Returns the value for the specified property key
+ * on the specified object. <br><br>
+ *
+ * If the property does not exist, this method calls
+ * handleQueryWithUnboundKey on the object if it
+ * implements NSKeyValueCoding, otherwise calls
+ * handleQueryWithUnboundKey on this class. <br><br>
+ */
+ static public Object valueForKey(
+ Object anObject, String aKey )
+ {
+ if ( anObject == null ) return null;
+ //TODO: may need to handle "." nesting here so
+ // that handleQueryWithUnboundKey gets called for
+ // for the nested object, not the parent object
+
+ //Correction: need to handle key paths in
+ // KeyValueCodingAdditionsSupport.
+
+ try
+ {
+ return Introspector.get( anObject, aKey );
+ }
+ catch ( IntrospectorException exc )
+ {
+ if ( anObject instanceof NSKeyValueCoding )
+ {
+ return ((NSKeyValueCoding)anObject).handleQueryWithUnboundKey( aKey );
+ }
+ return handleQueryWithUnboundKey( anObject, aKey );
+ }
+ }
+
+ /**
+ * Sets the property to the specified value on
+ * the specified object.
+ *
+ * If the property does not exist, this method calls
+ * handleTakeValueForUnboundKey on the object if it
+ * implements NSKeyValueCoding, otherwise calls
+ * handleTakeValueForUnboundKey on this class.
+ *
+ * If the property is of a type that cannot allow
+ * null (e.g. primitive types) and aValue is null,
+ * this method should call unableToSetNullForKey
+ * on the object if it implements NSKeyValueCoding,
+ * otherwise calls unableToSetNullForKey on this class.
+ */
+ static public void takeValueForKey(
+ Object anObject, Object aValue, String aKey )
+ {
+ if ( anObject == null ) return;
+ //TODO: may need to handle "." nesting here so
+ // that handleTakeValueForUnboundKey gets called for
+ // for the nested object, not the parent object
+ try
+ {
+ Introspector.set( anObject, aKey, aValue );
+ }
+ catch ( NullPrimitiveException exc )
+ {
+ if ( anObject instanceof NSKeyValueCoding )
+ {
+ ((NSKeyValueCoding)anObject).unableToSetNullForKey( aKey );
+ }
+ else
+ {
+ unableToSetNullForKey( anObject, aKey );
+ }
+ }
+ catch ( MissingPropertyException exc )
+ {
+ if ( anObject instanceof NSKeyValueCoding )
+ {
+ ((NSKeyValueCoding)anObject).handleTakeValueForUnboundKey(
+ aValue, aKey );
+ }
+ else
+ {
+ handleTakeValueForUnboundKey( anObject, aValue, aKey );
+ }
+ }
+
+ }
+
+ /**
+ * Returns the value for the private field that
+ * corresponds to the specified property on
+ * the specified object.
+ *
+ * This implementation currently calls valueForKey,
+ * because java security currently prevents us from
+ * accessing the fields of another object.
+ */
+ static public Object storedValueForKey(
+ Object anObject, String aKey )
+ {
+ //TODO: this currently just calls valueForKey
+ return valueForKey( anObject, aKey );
+ }
+
+ /**
+ * Sets the the private field that corresponds to the
+ * specified property to the specified value on the
+ * specified object.
+ *
+ * This implementation currently calls takeValueForKey,
+ * because java security currently prevents us from
+ * accessing the fields of another object.
+ */
+ static public void takeStoredValueForKey(
+ Object anObject, Object aValue, String aKey )
+ {
+ //TODO: this currently just calls takeValueForKey
+ takeValueForKey( anObject, aValue, aKey );
+ }
+
+ /**
+ * Called by valueForKey when the specified key is
+ * not found on the specified object, if that object
+ * does not implement NSKeyValueCoding.
+ *
+ * This implementation throws a WotonomyException.
+ */
+ static public Object handleQueryWithUnboundKey(
+ Object anObject, String aKey )
+ {
+ throw new WotonomyException(
+ "Key not found for object: "
+ + aKey + " : " + anObject );
+ }
+
+ /**
+ * Called by takeValueForKey when the specified key
+ * is not found on the specified object, if that object
+ * does not implement NSKeyValueCoding.
+ *
+ * This implementation throws a WotonomyException.
+ */
+ static public void handleTakeValueForUnboundKey(
+ Object anObject, Object aValue, String aKey )
+ {
+ throw new WotonomyException(
+ "Key not found for object while setting value: "
+ + aKey + " : " + anObject + " : " + aValue );
+ }
+
+ /**
+ * Called by takeValueForKey when the type of the
+ * specified key is not allowed to be null, as is
+ * the case with primitive types, if the specified
+ * object does not implement NSKeyValueCoding.
+ *
+ * This implementation throws a WotonomyException.
+ */
+ static public void unableToSetNullForKey(
+ Object anObject, String aKey )
+ {
+ throw new WotonomyException(
+ "Tried to key on object to null: "
+ + aKey + " : " + anObject );
+ }
+ }
+
+ public class Null {
+ public Null() {
+ super();
+ }
+
+ public String toString() {
+ return "<null>";
+ }
+ }
+}
+
+/*
+ * $Log$
+ * Revision 1.2 2006/02/16 13:15:00 cgruber
+ * Check in all sources in eclipse-friendly maven-enabled packages.
+ *
+ * Revision 1.3 2003/08/07 02:43:56 chochos
+ * added NullValue, which points to a Null instance.
+ *
+ * Revision 1.2 2003/01/18 23:30:42 mpowers
+ * WODisplayGroup now compiles.
+ *
+ * Revision 1.1 2003/01/17 14:40:49 mpowers
+ * Adding files to fix build.
+ *
+ * Revision 1.2 2001/03/28 16:12:30 mpowers
+ * Documented interface.
+ *
+ * Revision 1.1 2001/03/27 23:25:05 mpowers
+ * Contributing interface, no docs yet.
+ *
+ *
+ */
+
+
diff --git a/projects/net.wotonomy.foundation/src/main/java/net/wotonomy/foundation/NSKeyValueCodingAdditions.java b/projects/net.wotonomy.foundation/src/main/java/net/wotonomy/foundation/NSKeyValueCodingAdditions.java
new file mode 100644
index 0000000..785facd
--- /dev/null
+++ b/projects/net.wotonomy.foundation/src/main/java/net/wotonomy/foundation/NSKeyValueCodingAdditions.java
@@ -0,0 +1,213 @@
+/*
+Wotonomy: OpenStep design patterns for pure Java applications.
+Copyright (C) 2001 Intersect Software Corporation
+
+This library is free software; you can redistribute it and/or
+modify it under the terms of the GNU Lesser General Public
+License as published by the Free Software Foundation; either
+version 2.1 of the License, or (at your option) any later version.
+
+This library is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+Lesser General Public License for more details.
+
+You should have received a copy of the GNU Lesser General Public
+License along with this library; if not, see http://www.gnu.org
+*/
+
+package net.wotonomy.foundation;
+
+import java.util.List;
+import java.util.Map;
+
+/**
+* NSKeyValueCodingAdditions defines an interface for classes
+* that need to have more control over the wotonomy's bulk
+* property copying and cloning facilities. <br><br>
+*
+* @author michael@mpowers.net
+* @author $Author: cgruber $
+* @version $Revision: 893 $
+*/
+public interface NSKeyValueCodingAdditions extends NSKeyValueCoding
+{
+ /**
+ * Returns the value for the specified key path, which is
+ * a series of keys delimited by ".", for example:
+ * "createTime.year.length".
+ */
+ Object valueForKeyPath( String aKeyPath );
+
+ /**
+ * Sets the value for the specified key path, which is
+ * a series of keys delimited by ".", for example:
+ * "createTime.year.length".
+ * The value is set for the last object referenced by
+ * the key path.
+ */
+ void takeValueForKeyPath( Object aValue, String aKeyPath );
+
+ /**
+ * Returns a Map of the specified keys to their values,
+ * each of which might be obtained by calling valueForKey.
+ */
+ NSDictionary valuesForKeys( List aKeyList );
+
+ /**
+ * Takes the keys from the specified map as properties
+ * and applies the corresponding values, each of which
+ * might be set by calling takeValueForKey.
+ */
+ void takeValuesFromDictionary( Map aMap );
+
+
+ /**
+ * Static utility methods that
+ * call the appropriate method if the object implements
+ * NSKeyValueCodingAdditions, otherwise calls the method
+ * on DefaultImplementation.
+ */
+ public class Utility
+ {
+ /**
+ * Calls the appropriate method if the object implements
+ * NSKeyValueCodingAdditions, otherwise calls the method
+ * on DefaultImplementation.
+ */
+ public static void takeValuesFromDictionary(
+ Object object, Map dictionary)
+ {
+ if (object instanceof NSKeyValueCodingAdditions) {
+ ((NSKeyValueCodingAdditions)object).takeValuesFromDictionary(dictionary);
+ } else {
+ DefaultImplementation.takeValuesFromDictionary(object, dictionary);
+ }
+ }
+
+ /**
+ * Calls the appropriate method if the object implements
+ * NSKeyValueCodingAdditions, otherwise calls the method
+ * on DefaultImplementation.
+ */
+ public static void takeValueForKeyPath(
+ Object object, Object aValue, String aKeyPath)
+ {
+ if (object instanceof NSKeyValueCodingAdditions) {
+ ((NSKeyValueCodingAdditions)object).takeValueForKeyPath(aValue, aKeyPath);
+ } else {
+ DefaultImplementation.takeValueForKeyPath(object, aValue, aKeyPath);
+ }
+ }
+
+ /**
+ * Calls the appropriate method if the object implements
+ * NSKeyValueCodingAdditions, otherwise calls the method
+ * on DefaultImplementation.
+ */
+ public static NSDictionary valuesForKeys(
+ Object object, List keys)
+ {
+ if (object instanceof NSKeyValueCodingAdditions) {
+ return ((NSKeyValueCodingAdditions)object).valuesForKeys(keys);
+ } else {
+ return DefaultImplementation.valuesForKeys(object, keys);
+ }
+ }
+
+ /**
+ * Calls the appropriate method if the object implements
+ * NSKeyValueCodingAdditions, otherwise calls the method
+ * on DefaultImplementation.
+ */
+ public static Object valueForKeyPath(
+ Object object, String aKeyPath)
+ {
+ if (object instanceof NSKeyValueCodingAdditions) {
+ return ((NSKeyValueCodingAdditions)object).valueForKeyPath(aKeyPath);
+ } else {
+ return DefaultImplementation.valueForKeyPath(object, aKeyPath);
+ }
+ }
+ }
+
+ /**
+ * Provides a reflection-based implementation for classes that
+ * don't implement NSKeyValueCodingAdditions.
+ */
+ public class DefaultImplementation
+ {
+ /**
+ * Provides a reflection-based implementation for classes that
+ * don't implement NSKeyValueCodingAdditions.
+ */
+ public static void takeValuesFromDictionary(
+ Object object, Map dictionary)
+ {
+ throw new RuntimeException( "Not implemented yet." );
+ }
+
+ /**
+ * Provides a reflection-based implementation for classes that
+ * don't implement NSKeyValueCodingAdditions.
+ */
+ public static void takeValueForKeyPath(
+ Object object, Object aValue, String aKeyPath)
+ {
+ // currently, NSKeyValueCoding.takeValueForKey accepts paths
+ NSKeyValueCoding.DefaultImplementation.takeValueForKey( object, aValue, aKeyPath );
+ }
+
+ /**
+ * Provides a reflection-based implementation for classes that
+ * don't implement NSKeyValueCodingAdditions.
+ */
+ public static NSDictionary valuesForKeys(
+ Object object, List keys)
+ {
+ throw new RuntimeException( "Not implemented yet." );
+ }
+
+ /**
+ * Provides a reflection-based implementation for classes that
+ * don't implement NSKeyValueCodingAdditions.
+ */
+ public static Object valueForKeyPath(
+ Object object, String aKeyPath)
+ {
+ // currently, NSKeyValueCoding.valueForKey accepts paths
+ return NSKeyValueCoding.DefaultImplementation.valueForKey( object, aKeyPath );
+ }
+ }
+}
+
+/*
+ * $Log$
+ * Revision 1.2 2006/02/16 13:15:00 cgruber
+ * Check in all sources in eclipse-friendly maven-enabled packages.
+ *
+ * Revision 1.2 2003/01/18 23:30:42 mpowers
+ * WODisplayGroup now compiles.
+ *
+ * Revision 1.1 2003/01/17 14:40:49 mpowers
+ * Adding files to fix build.
+ *
+ * Revision 1.3 2001/12/10 15:25:11 mpowers
+ * Now properly extending NSKeyValueCoding.
+ *
+ * Revision 1.2 2001/04/28 14:12:23 mpowers
+ * Refactored cloning/copying into KeyValueCodingUtilities.
+ *
+ * Revision 1.1 2001/03/29 03:29:49 mpowers
+ * Now using KeyValueCoding and Support instead of Introspector.
+ *
+ * Revision 1.2 2001/03/28 16:12:30 mpowers
+ * Documented interface.
+ *
+ * Revision 1.1 2001/03/27 23:25:05 mpowers
+ * Contributing interface, no docs yet.
+ *
+ *
+ */
+
+
diff --git a/projects/net.wotonomy.foundation/src/main/java/net/wotonomy/foundation/NSKeyValueCodingSupport.java b/projects/net.wotonomy.foundation/src/main/java/net/wotonomy/foundation/NSKeyValueCodingSupport.java
new file mode 100644
index 0000000..a947896
--- /dev/null
+++ b/projects/net.wotonomy.foundation/src/main/java/net/wotonomy/foundation/NSKeyValueCodingSupport.java
@@ -0,0 +1,228 @@
+/*
+Wotonomy: OpenStep design patterns for pure Java applications.
+Copyright (C) 2001 Intersect Software Corporation
+
+This library is free software; you can redistribute it and/or
+modify it under the terms of the GNU Lesser General Public
+License as published by the Free Software Foundation; either
+version 2.1 of the License, or (at your option) any later version.
+
+This library is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+Lesser General Public License for more details.
+
+You should have received a copy of the GNU Lesser General Public
+License along with this library; if not, see http://www.gnu.org
+*/
+
+package net.wotonomy.foundation;
+
+import net.wotonomy.foundation.internal.Introspector;
+import net.wotonomy.foundation.internal.IntrospectorException;
+import net.wotonomy.foundation.internal.MissingPropertyException;
+import net.wotonomy.foundation.internal.NullPrimitiveException;
+import net.wotonomy.foundation.internal.WotonomyException;
+
+/**
+* NSKeyValueCodingSupport defines default behavior for
+* classes implementing NSKeyValueSupport. <br><br>
+*
+* On an object that does not implement NSKeyValueCoding,
+* wotonomy will call the methods on this class directly.
+*
+* @author michael@mpowers.net
+* @author $Author: cgruber $
+* @version $Revision: 892 $
+*/
+public class NSKeyValueCodingSupport
+{
+ /**
+ * Returns the value for the specified property key
+ * on the specified object. <br><br>
+ *
+ * If the property does not exist, this method calls
+ * handleQueryWithUnboundKey on the object if it
+ * implements NSKeyValueCoding, otherwise calls
+ * handleQueryWithUnboundKey on this class. <br><br>
+ */
+ static public Object valueForKey(
+ Object anObject, String aKey )
+ {
+ //TODO: may need to handle "." nesting here so
+ // that handleQueryWithUnboundKey gets called for
+ // for the nested object, not the parent object
+
+ //Correction: need to handle key paths in
+ // KeyValueCodingAdditionsSupport.
+
+ try
+ {
+ return Introspector.get( anObject, aKey );
+ }
+ catch ( IntrospectorException exc )
+ {
+ if ( anObject instanceof NSKeyValueCoding )
+ {
+ return ((NSKeyValueCoding)anObject).handleQueryWithUnboundKey( aKey );
+ }
+ return handleQueryWithUnboundKey( anObject, aKey );
+ }
+ }
+
+ /**
+ * Sets the property to the specified value on
+ * the specified object.
+ *
+ * If the property does not exist, this method calls
+ * handleTakeValueForUnboundKey on the object if it
+ * implements NSKeyValueCoding, otherwise calls
+ * handleTakeValueForUnboundKey on this class.
+ *
+ * If the property is of a type that cannot allow
+ * null (e.g. primitive types) and aValue is null,
+ * this method should call unableToSetNullForKey
+ * on the object if it implements NSKeyValueCoding,
+ * otherwise calls unableToSetNullForKey on this class.
+ */
+ static public void takeValueForKey(
+ Object anObject, Object aValue, String aKey )
+ {
+ //TODO: may need to handle "." nesting here so
+ // that handleTakeValueForUnboundKey gets called for
+ // for the nested object, not the parent object
+
+ try
+ {
+ Introspector.set( anObject, aKey, aValue );
+ }
+ catch ( NullPrimitiveException exc )
+ {
+ if ( anObject instanceof NSKeyValueCoding )
+ {
+ ((NSKeyValueCoding)anObject).unableToSetNullForKey( aKey );
+ }
+ else
+ {
+ unableToSetNullForKey( anObject, aKey );
+ }
+ }
+ catch ( MissingPropertyException exc )
+ {
+ if ( anObject instanceof NSKeyValueCoding )
+ {
+ ((NSKeyValueCoding)anObject).handleTakeValueForUnboundKey(
+ aValue, aKey );
+ }
+ else
+ {
+ handleTakeValueForUnboundKey( anObject, aValue, aKey );
+ }
+ }
+
+ }
+
+ /**
+ * Returns the value for the private field that
+ * corresponds to the specified property on
+ * the specified object.
+ *
+ * This implementation currently calls valueForKey,
+ * because java security currently prevents us from
+ * accessing the fields of another object.
+ */
+ static public Object storedValueForKey(
+ Object anObject, String aKey )
+ {
+ //TODO: this currently just calls valueForKey
+ return valueForKey( anObject, aKey );
+ }
+
+ /**
+ * Sets the the private field that corresponds to the
+ * specified property to the specified value on the
+ * specified object.
+ *
+ * This implementation currently calls takeValueForKey,
+ * because java security currently prevents us from
+ * accessing the fields of another object.
+ */
+ static public void takeStoredValueForKey(
+ Object anObject, Object aValue, String aKey )
+ {
+ //TODO: this currently just calls takeValueForKey
+ takeValueForKey( anObject, aValue, aKey );
+ }
+
+ /**
+ * Called by valueForKey when the specified key is
+ * not found on the specified object, if that object
+ * does not implement NSKeyValueCoding.
+ *
+ * This implementation throws a WotonomyException.
+ */
+ static public Object handleQueryWithUnboundKey(
+ Object anObject, String aKey )
+ {
+ throw new WotonomyException(
+ "Key not found for object: "
+ + aKey + " : " + anObject );
+ }
+
+ /**
+ * Called by takeValueForKey when the specified key
+ * is not found on the specified object, if that object
+ * does not implement NSKeyValueCoding.
+ *
+ * This implementation throws a WotonomyException.
+ */
+ static public void handleTakeValueForUnboundKey(
+ Object anObject, Object aValue, String aKey )
+ {
+ throw new WotonomyException(
+ "Key not found for object while setting value: "
+ + aKey + " : " + anObject + " : " + aValue );
+ }
+
+ /**
+ * Called by takeValueForKey when the type of the
+ * specified key is not allowed to be null, as is
+ * the case with primitive types, if the specified
+ * object does not implement NSKeyValueCoding.
+ *
+ * This implementation throws a WotonomyException.
+ */
+ static public void unableToSetNullForKey(
+ Object anObject, String aKey )
+ {
+ throw new WotonomyException(
+ "Tried to key on object to null: "
+ + aKey + " : " + anObject );
+ }
+
+}
+
+/*
+ * $Log$
+ * Revision 1.1 2006/02/16 12:47:16 cgruber
+ * Check in all sources in eclipse-friendly maven-enabled packages.
+ *
+ * Revision 1.1 2003/01/17 14:40:50 mpowers
+ * Adding files to fix build.
+ *
+ * Revision 1.4 2001/05/18 21:04:33 mpowers
+ * Reimplemented EditingContext.initializeObject.
+ *
+ * Revision 1.3 2001/04/27 00:28:29 mpowers
+ * Fixed a return value.
+ *
+ * Revision 1.2 2001/04/03 20:36:01 mpowers
+ * Fixed refaulting/reverting/invalidating to be self-consistent.
+ *
+ * Revision 1.1 2001/03/28 17:49:33 mpowers
+ * Implemented NSKeyValueCodingSupport.
+ *
+ *
+ */
+
+
diff --git a/projects/net.wotonomy.foundation/src/main/java/net/wotonomy/foundation/NSLock.java b/projects/net.wotonomy.foundation/src/main/java/net/wotonomy/foundation/NSLock.java
new file mode 100644
index 0000000..e6a5147
--- /dev/null
+++ b/projects/net.wotonomy.foundation/src/main/java/net/wotonomy/foundation/NSLock.java
@@ -0,0 +1,108 @@
+/*
+Wotonomy: OpenStep design patterns for pure Java applications.
+Copyright (C) 2002 Israfil consulting Services Corporation
+
+This library is free software; you can redistribute it and/or
+modify it under the terms of the GNU Lesser General Public
+License as published by the Free Software Foundation; either
+version 2.1 of the License, or (at your option) any later version.
+
+This library is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+Lesser General Public License for more details.
+
+You should have received a copy of the GNU Lesser General Public
+License along with this library; if not, see http://www.gnu.org
+
+$Id: NSLock.java 893 2006-02-16 13:22:23Z cgruber $
+
+*/
+
+package net.wotonomy.foundation;
+
+import EDU.oswego.cs.dl.util.concurrent.Mutex;
+
+/**
+* A simple mutually-exclusive lock. Currently an API-compliance
+* subclass of an external Mutex class, conforming to the API and
+* behavior of com.webobjects.foundation.NSLock. This class implements
+* the NSLocking protocol (interface), and is implemented using
+* Doug Lea's concurrent programming package.
+*
+* @author cgruber@israfil.net
+* @author $Author: cgruber $
+* @version $Revision: 893 $
+*
+*/
+
+public class NSLock extends Mutex implements NSLocking {
+
+
+ public NSLock() {
+ }
+
+ public synchronized void lock() {
+ try {
+ acquire();
+ } catch (InterruptedException interruptedexception) {
+ notify();
+ }
+ }
+
+ public synchronized void unlock() {
+ release();
+ }
+
+ public synchronized boolean tryLock() {
+ return tryLock(0);
+ }
+
+ public synchronized boolean tryLock(long l) {
+ try {
+ return attempt(l);
+ } catch (InterruptedException interruptedexception) {
+ notify();
+ return false;
+ }
+ }
+
+ public boolean tryLock(NSTimestamp nstimestamp) {
+ return tryLock(nstimestamp.getTime() - System.currentTimeMillis());
+ }
+
+ public String toString() {
+ return getClass().getName() + " <" + (inuse_ ? "Locked" : "Unlocked") + ">";
+ }
+
+ public synchronized boolean isLocked() {
+ return inuse_;
+ }
+
+}
+
+/*
+ * $Log$
+ * Revision 1.2 2006/02/16 13:15:00 cgruber
+ * Check in all sources in eclipse-friendly maven-enabled packages.
+ *
+ * Revision 1.1 2002/07/14 21:56:16 mpowers
+ * Contributions from cgruber.
+ *
+ * Revision 1.4 2002/06/25 19:03:02 cgruber
+ * Internal documentation fixes.
+ *
+ * Revision 1.3 2002/06/25 18:52:56 cgruber
+ * Fix javadocs that resulted from bad cut-and-paste of the
+ * boilerplate.
+ *
+ * Revision 1.2 2002/06/25 17:45:52 cgruber
+ * Add implementation of NSLock using Doug Lea's concurrent
+ * programming APIs.
+ *
+ * Revision 1.1 2002/06/25 07:52:57 cgruber
+ * Add quite a few abstract classes, interfaces, and classes. All
+ * API consistent with WebObjects, but with no implementation, nor
+ * any private or package access members from the original.
+ *
+ */
diff --git a/projects/net.wotonomy.foundation/src/main/java/net/wotonomy/foundation/NSLocking.java b/projects/net.wotonomy.foundation/src/main/java/net/wotonomy/foundation/NSLocking.java
new file mode 100644
index 0000000..f8c8ff4
--- /dev/null
+++ b/projects/net.wotonomy.foundation/src/main/java/net/wotonomy/foundation/NSLocking.java
@@ -0,0 +1,59 @@
+/*
+Wotonomy: OpenStep design patterns for pure Java applications.
+Copyright (C) 2002 Israfil consulting Services Corporation
+
+This library is free software; you can redistribute it and/or
+modify it under the terms of the GNU Lesser General Public
+License as published by the Free Software Foundation; either
+version 2.1 of the License, or (at your option) any later version.
+
+This library is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+Lesser General Public License for more details.
+
+You should have received a copy of the GNU Lesser General Public
+License along with this library; if not, see http://www.gnu.org
+
+$Id: NSLocking.java 892 2006-02-16 12:47:16Z cgruber $
+
+*/
+
+package net.wotonomy.foundation;
+
+/**
+* Defines a simple locking protocol. Very course-grain locking.
+*
+* @author cgruber@israfil.net
+* @author $Author: cgruber $
+* @version $Revision: 892 $
+*/
+
+public interface NSLocking {
+
+ public static final long OneSecond = 1000L;
+ public static final long OneMinute = 60000L;
+ public static final long OneHour = 0x36ee80L;
+ public static final long OneDay = 0x5265c00L;
+ public static final long OneWeek = 0x240c8400L;
+ public static final long OneYear = 0x758f0dfc0L;
+ public static final long OneCentury = 0x2debe176700L;
+
+ public abstract void lock();
+
+ public abstract void unlock();
+
+}
+
+/*
+ * $Log$
+ * Revision 1.1 2006/02/16 12:47:16 cgruber
+ * Check in all sources in eclipse-friendly maven-enabled packages.
+ *
+ * Revision 1.1 2002/07/14 21:56:16 mpowers
+ * Contributions from cgruber.
+ *
+ * Revision 1.2 2002/06/21 22:11:19 cgruber
+ * Add a log trail
+ *
+ */
diff --git a/projects/net.wotonomy.foundation/src/main/java/net/wotonomy/foundation/NSLog.java b/projects/net.wotonomy.foundation/src/main/java/net/wotonomy/foundation/NSLog.java
new file mode 100644
index 0000000..52e4090
--- /dev/null
+++ b/projects/net.wotonomy.foundation/src/main/java/net/wotonomy/foundation/NSLog.java
@@ -0,0 +1,489 @@
+/*
+Wotonomy: OpenStep design patterns for pure Java applications.
+Copyright (C) 2001 Intersect Software Corporation
+
+This library is free software; you can redistribute it and/or
+modify it under the terms of the GNU Lesser General Public
+License as published by the Free Software Foundation; either
+version 2.1 of the License, or (at your option) any later version.
+
+This library is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+Lesser General Public License for more details.
+
+You should have received a copy of the GNU Lesser General Public
+License along with this library; if not, see http://www.gnu.org
+*/
+
+package net.wotonomy.foundation;
+
+import java.io.ByteArrayOutputStream;
+import java.io.FileOutputStream;
+import java.io.PrintStream;
+import java.util.Date;
+
+/**
+* NSLog is foundation's built-in logging facility: IMPLEMENTED, BUT NOT TESTED.
+* By default, all groups are enabled, and debug level is DebugLevelOff.
+*
+* @author michael@mpowers.net
+* @author $Author: cgruber $
+* @version $Revision: 893 $
+*/
+public class NSLog
+{
+ public static long DebugGroupApplicationGeneration = 1L << 3;
+ public static long DebugGroupArchiving = 1L << 6;
+ public static long DebugGroupAssociations = 1L << 19;
+ public static long DebugGroupComponentBindings = 1L << 9;
+ public static long DebugGroupControllers = 1L << 20;
+ public static long DebugGroupComponents = 1L << 26;
+ public static long DebugGroupDatabaseAccess = 1L << 16;
+ public static long DebugGroupDeployment = 1L << 22;
+ public static long DebugGroupEnterpriseObjects = 1L << 1;
+ public static long DebugGroupFormatting = 1L << 10;
+ public static long DebugGroupIO = 1L << 13;
+ public static long DebugGroupJSPServlets = 1L << 27;
+ public static long DebugGroupKeyValueCoding = 1L << 8;
+ public static long DebugGroupModel = 1L << 15;
+ public static long DebugGroupMultithreading = 1L << 4;
+ public static long DebugGroupParsing = 1L << 23;
+ public static long DebugGroupQualifiers = 1L << 11;
+ public static long DebugGroupReflection = 1L << 24;
+ public static long DebugGroupRequestHandling = 1L << 25;
+ public static long DebugGroupResources = 1L << 5;
+ public static long DebugGroupRules = 1L << 21;
+ public static long DebugGroupSQLGeneration = 1L << 17;
+ public static long DebugGroupTiming = 1L << 14;
+ public static long DebugGroupUserInterface = 1L << 18;
+ public static long DebugGroupValidation = 1L << 7;
+ public static long DebugGroupWebObjects = 1L << 2;
+ public static long DebugGroupWebServices = 1L << 2;
+
+ public static int DebugLevelOff = 0;
+ public static int DebugLevelCritical = 1;
+ public static int DebugLevelInformational = 2;
+ public static int DebugLevelDetailed = 3;
+
+ /**
+ * The logger to which debug statements should be
+ * conditionally written. By default, these messages
+ * appear on the standard error stream.
+ */
+ public static Logger debug;
+
+ /**
+ * The logger to which error messages should be written,
+ * which may not always be user-visible. By default,
+ * these messages appear on the standard error stream.
+ */
+ public static Logger err;
+
+ /**
+ * The logger to which user-visible messages should be written.
+ * By default, these messages appear on the standard output stream.
+ */
+ public static Logger out;
+
+ private static long allowedGroups;
+ private static int allowedLevel;
+
+ static
+ {
+ debug = new PrintStreamLogger( System.err );
+ err = new PrintStreamLogger( System.err );
+ out = new PrintStreamLogger( System.out );
+
+ //TODO: need to initialize the debug level and groups based
+ // on the value of the NSDebugLevel and NSDebugGroup properties
+ allowedGroups = Long.MAX_VALUE;
+ allowedLevel = 0;
+ }
+
+ /**
+ * Adds the specified group masks to those allowed for logging.
+ */
+ public static void allowDebugLoggingForGroups(long aDebugGroups)
+ {
+ allowedGroups = allowedGroups | aDebugGroups;
+ }
+
+ /**
+ * Returns the current logging debug level.
+ */
+ public static int allowedDebugLevel()
+ {
+ return allowedLevel;
+ }
+
+ /**
+ * Returns whether logging is allowed for the specified groups masks.
+ */
+ public static boolean debugLoggingAllowedForGroups(long aDebugGroups)
+ {
+ return ( allowedGroups == ( allowedGroups | aDebugGroups ) );
+ }
+
+ /**
+ * Returns whether logging is allowed for the specified level.
+ */
+ public static boolean debugLoggingAllowedForLevel(int aDebugLevel)
+ {
+ return ( allowedLevel >= aDebugLevel );
+ }
+
+ /**
+ * Returns whether logging allowed for the specified groups masks
+ * at the specified level. Convenience method.
+ */
+ public static boolean debugLoggingAllowedForLevelAndGroups(int aDebugLevel,
+ long aDebugGroups)
+ {
+ return ( ( allowedLevel >= aDebugLevel )
+ && ( allowedGroups == ( allowedGroups | aDebugGroups ) ) );
+ }
+
+ /**
+ * Convenience to obtain a java PrintStream for the specified file path.
+ * Returns null if the stream could not be created.
+ */
+ public static PrintStream printStreamForPath(String aPath)
+ {
+ try
+ {
+ return new PrintStream( new FileOutputStream( aPath ) );
+ }
+ catch ( Throwable t )
+ {
+ }
+ return null;
+ }
+
+ /**
+ * Removes the specified group masks from those allowed for logging.
+ */
+ public static void refuseDebugLoggingForGroups(long aDebugGroups)
+ {
+ allowedGroups = ( allowedGroups | aDebugGroups ) ^ aDebugGroups;
+ }
+
+ /**
+ * Sets the allowed groups to only those specified by the mask.
+ */
+ public static void setAllowedDebugGroups(long aDebugGroups)
+ {
+ allowedGroups = aDebugGroups;
+ }
+
+ /**
+ * Sets the current debug level.
+ */
+ public static void setAllowedDebugLevel(int aDebugLevel)
+ {
+ allowedLevel = aDebugLevel;
+ }
+
+ /**
+ * Sets the current debug logger.
+ * Does nothing if logger is null.
+ */
+ public static void setDebug(NSLog.Logger logger)
+ {
+ if ( logger != null )
+ {
+ debug = logger;
+ }
+ }
+
+ /**
+ * Sets the current error logger.
+ * Does nothing if logger is null.
+ */
+ public static void setErr(NSLog.Logger logger)
+ {
+ if ( logger != null )
+ {
+ err = logger;
+ }
+ }
+
+ /**
+ * Sets the current output logger.
+ * Does nothing if logger is null.
+ */
+ public static void setOut(NSLog.Logger logger)
+ {
+ if ( logger != null )
+ {
+ out = logger;
+ }
+ }
+
+ /**
+ * Convenience to write the throwable's stack trace
+ * to a string.
+ */
+ public static String throwableAsString(Throwable t)
+ {
+ ByteArrayOutputStream os = new ByteArrayOutputStream();
+ PrintStream ps = new PrintStream( os );
+ t.printStackTrace( ps );
+ return os.toString();
+ }
+
+ /**
+ * The abstract superclass of all Logger implementations.
+ */
+ static abstract public class Logger
+ {
+ private boolean enabled;
+ private boolean verbose;
+
+ /**
+ * Default constructor sets enabled
+ * and verbose to true.
+ */
+ public Logger()
+ {
+ enabled = true;
+ verbose = true;
+ }
+
+ /**
+ * Convenience to append a Boolean.
+ */
+ public void appendln(boolean aValue)
+ {
+ appendln( new Boolean( aValue ) );
+ }
+
+ /**
+ * Convenience to append a Byte.
+ */
+ public void appendln(byte aValue)
+ {
+ appendln( new Byte( aValue ) );
+ }
+
+ /**
+ * Convenience to write a String
+ * comprised of the byte array using
+ * the default encoding.
+ */
+ public void appendln(byte[] aValue)
+ {
+ appendln( new String( aValue ) );
+ }
+
+ /**
+ * Convenience to append a Character.
+ */
+ public void appendln(char aValue)
+ {
+ appendln( new Character( aValue ) );
+ }
+
+ /**
+ * Convenience to append a String
+ * comprised of the character array.
+ */
+ public void appendln(char[] aValue)
+ {
+ appendln( new String( aValue ) );
+ }
+
+ /**
+ * Convenience to append a Double.
+ */
+ public void appendln(double aValue)
+ {
+ appendln( new Double( aValue ) );
+ }
+
+ /**
+ * Convenience to append a Float.
+ */
+ public void appendln(float aValue)
+ {
+ appendln( new Float( aValue ) );
+ }
+
+ /**
+ * Convenience to append a Integer.
+ */
+ public void appendln(int aValue)
+ {
+ appendln( new Integer( aValue ) );
+ }
+
+ /**
+ * Convenience to append a Long.
+ */
+ public void appendln(long aValue)
+ {
+ appendln( new Long( aValue ) );
+ }
+
+ /**
+ * Convenience to append a Short
+ */
+ public void appendln(short aValue)
+ {
+ appendln( new Short( aValue ) );
+ }
+
+ /**
+ * Convenience to append a Throwable.
+ */
+ public void appendln(Throwable aValue)
+ {
+ appendln( NSLog.throwableAsString( aValue ) );
+ }
+
+ /**
+ * Writes the object to the log.
+ */
+ public abstract void appendln(Object aValue);
+
+ /**
+ * Appends a line to the log.
+ */
+ public abstract void appendln();
+
+ /**
+ * Flushes any buffered output to the log.
+ */
+ public abstract void flush();
+
+ /**
+ * Returns whether the logger is enabled,
+ * the meaning of which is defined by the
+ * implementing class.
+ */
+ public boolean isEnabled()
+ {
+ return enabled;
+ }
+
+ /**
+ * Returns whether the logger is verbose,
+ * the meaning of which is defined by the
+ * implementing class.
+ */
+ public boolean isVerbose()
+ {
+ return verbose;
+ }
+
+ /**
+ * Sets whether the logger is enabled,
+ * the meaning of which is defined by the
+ * implementing class.
+ */
+ public void setIsEnabled(boolean aBool)
+ {
+ enabled = aBool;
+ }
+
+ /**
+ * Sets whether the logger is verbose,
+ * the meaning of which is defined by the
+ * implementing class.
+ */
+ public void setIsVerbose(boolean aBool)
+ {
+ verbose = aBool;
+ }
+ }
+
+ /**
+ * The default implementation of Logger that writes to a Java
+ * PrintStream. If not enabled, no output is written.
+ * If verbose, output is in format: "[time] <thread name> message".
+ */
+ static public class PrintStreamLogger extends Logger
+ {
+ private PrintStream thePrintStream;
+
+ /**
+ * Constructor takes a PrintStream.
+ */
+ public PrintStreamLogger(PrintStream ps)
+ {
+ thePrintStream = ps;
+ }
+
+ /**
+ * Sends a newline to the print stream.
+ */
+ public void appendln()
+ {
+ if ( isEnabled() )
+ {
+ thePrintStream.println();
+ }
+ }
+
+ /**
+ * Writes the throwable to the print stream.
+ */
+ public void appendln(Throwable aValue)
+ {
+ appendln( NSLog.throwableAsString( aValue ) );
+ }
+
+ /**
+ * Writes aValue.toString to the print stream.
+ */
+ public void appendln(Object aValue)
+ {
+ if ( isEnabled() )
+ {
+ if ( isVerbose() )
+ {
+ thePrintStream.print( '[' + new Date().toString() + "] <" +
+ Thread.currentThread().getName() + "> " );
+ }
+ if ( aValue == null ) aValue = "null";
+ thePrintStream.println( aValue.toString() );
+ }
+ }
+
+ /**
+ * Flushes the print stream.
+ */
+ public void flush()
+ {
+ thePrintStream.flush();
+ }
+
+ /**
+ * Returns the current print stream.
+ */
+ public PrintStream printStream()
+ {
+ return thePrintStream;
+ }
+
+ /**
+ * Replaces the current print stream.
+ */
+ public void setPrintStream(PrintStream aStream)
+ {
+ thePrintStream = aStream;
+ }
+ }
+}
+
+/*
+ * $Log$
+ * Revision 1.2 2006/02/16 13:15:00 cgruber
+ * Check in all sources in eclipse-friendly maven-enabled packages.
+ *
+ * Revision 1.1 2003/01/31 22:33:00 mpowers
+ * Contributing NSLog.
+ *
+ *
+ */
+
diff --git a/projects/net.wotonomy.foundation/src/main/java/net/wotonomy/foundation/NSMultiReaderLock.java b/projects/net.wotonomy.foundation/src/main/java/net/wotonomy/foundation/NSMultiReaderLock.java
new file mode 100644
index 0000000..abed576
--- /dev/null
+++ b/projects/net.wotonomy.foundation/src/main/java/net/wotonomy/foundation/NSMultiReaderLock.java
@@ -0,0 +1,159 @@
+/*
+Wotonomy: OpenStep design patterns for pure Java applications.
+Copyright (C) 2002 Israfil consulting Services Corporation
+
+This library is free software; you can redistribute it and/or
+modify it under the terms of the GNU Lesser General Public
+License as published by the Free Software Foundation; either
+version 2.1 of the License, or (at your option) any later version.
+
+This library is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+Lesser General Public License for more details.
+
+You should have received a copy of the GNU Lesser General Public
+License along with this library; if not, see http://www.gnu.org
+
+$Id: NSMultiReaderLock.java 893 2006-02-16 13:22:23Z cgruber $
+
+*/
+
+package net.wotonomy.foundation;
+
+import EDU.oswego.cs.dl.util.concurrent.ReentrantWriterPreferenceReadWriteLock;
+
+/**
+* A Read-Write lock that allows unlimited number of calling threads to
+* acquire read locks, but only one thread to acquire a write lock. It
+* is also reentrant, allowing each thread to re-acquire it's lock
+* recursively. For that reason it is somewhat slower, as there is a
+* hash lookup when attempting to acquire and release reader locks. Of
+* course a writer lock is quite a bit slower than a reader lock. A
+* write lock is mutally exclusive with read locks, though a thread
+* that has obtained a read-lock may be promoted to a write lock and
+* vice versa when conditions permit.
+*
+* @author cgruber@israfil.net
+* @author $Author: cgruber $
+* @version $Revision: 893 $
+*/
+public class NSMultiReaderLock extends ReentrantWriterPreferenceReadWriteLock implements NSLocking {
+
+ NSMutableDictionary _readerSuspended = new NSMutableDictionary();
+
+ public NSMultiReaderLock() {
+ }
+
+ public void lockForReading() {
+ try {
+ readerLock_.acquire();
+ } catch (InterruptedException interruptedexception) {
+ // Null behavior, as notify() is already called
+ // by acquire();
+ // We may want to log here.
+ }
+ }
+
+ public void unlockForReading() {
+ readerLock_.release();
+ }
+
+ public void lock() {
+ lockForWriting();
+ }
+
+ public void lockForWriting() {
+ try {
+ writerLock_.acquire();
+ } catch (InterruptedException interruptedexception) {
+ // Null behavior, as notify() is already called
+ // by acquire();
+ // We may want to log here.
+ }
+ }
+
+ public void unlock() {
+ unlockForWriting();
+ }
+
+ public void unlockForWriting() {
+ writerLock_.release();
+ }
+
+ /** @see com.webobjects.foundation.NSMultiReaderLock#suspendReaderLock() */
+ public void suspendReaderLocks() {
+ Thread thisThread = Thread.currentThread();
+ Integer suspendedReaders = (Integer)_readerSuspended.get(thisThread);
+ if (suspendedReaders != null && suspendedReaders.intValue() > 0) return;
+ // logic is to override startRead / endRead and ensure that the suspension
+ // isn't improperly stopped.
+ throw new UnsupportedOperationException("Not Yet Implemented");
+ }
+
+ /** @see com.webobjects.foundation.NSMultiReaderLock#retrieveReaderLock() */
+ public void retrieveReaderLocks() {
+ Thread thisThread = Thread.currentThread();
+ Integer suspendedReaders = (Integer)_readerSuspended.get(thisThread);
+ if (suspendedReaders != null && suspendedReaders.intValue() > 0) return;
+ // logic is to override startRead / endRead and ensure that the suspension
+ // isn't improperly stopped.
+ throw new UnsupportedOperationException("Not Yet Implemented");
+ }
+
+ public boolean tryLockForWriting() {
+ try {
+ return writerLock_.attempt(0);
+ } catch (InterruptedException interruptedexception) {
+ // notify() is already called by attempt();
+ // We may want to log here.
+ return false;
+ }
+ }
+
+ public boolean tryLockForReading() {
+ try {
+ return readerLock_.attempt(0);
+ } catch (InterruptedException interruptedexception) {
+ // notify() is already called by attempt();
+ // We may want to log here.
+ return false;
+ }
+ }
+
+ public String toString() {
+ throw new UnsupportedOperationException("Not Yet Implemented");
+ }
+
+ protected String _padString(long l, int i) {
+ throw new UnsupportedOperationException("Not Yet Implemented");
+ }
+
+ protected String _padString(String s, int i, boolean flag) {
+ throw new UnsupportedOperationException("Not Yet Implemented");
+ }
+
+
+}
+/*
+ * $Log$
+ * Revision 1.2 2006/02/16 13:15:00 cgruber
+ * Check in all sources in eclipse-friendly maven-enabled packages.
+ *
+ * Revision 1.2 2003/08/06 23:07:52 chochos
+ * general code cleanup (mostly, removing unused imports)
+ *
+ * Revision 1.1 2002/07/14 21:56:16 mpowers
+ * Contributions from cgruber.
+ *
+ * Revision 1.2 2002/06/26 00:40:22 cgruber
+ * Add implementation, using ReentrantWriterPreferenceReadWriteLock
+ * as a base.
+ *
+ * suspendReaderLocks and retreiveReaderLocks is the one
+ * that's likeliest to be a pain.
+ *
+ * Revision 1.1 2002/06/25 07:52:57 cgruber
+ * Add quite a few abstract classes, interfaces, and classes. All API consistent with WebObjects, but with no implementation, nor any private or package access members from the original.
+ *
+ */
diff --git a/projects/net.wotonomy.foundation/src/main/java/net/wotonomy/foundation/NSMutableArray.java b/projects/net.wotonomy.foundation/src/main/java/net/wotonomy/foundation/NSMutableArray.java
new file mode 100644
index 0000000..f705000
--- /dev/null
+++ b/projects/net.wotonomy.foundation/src/main/java/net/wotonomy/foundation/NSMutableArray.java
@@ -0,0 +1,429 @@
+/*
+Wotonomy: OpenStep design patterns for pure Java applications.
+Copyright (C) 2000 Blacksmith, Inc.
+
+This library is free software; you can redistribute it and/or
+modify it under the terms of the GNU Lesser General Public
+License as published by the Free Software Foundation; either
+version 2.1 of the License, or (at your option) any later version.
+
+This library is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+Lesser General Public License for more details.
+
+You should have received a copy of the GNU Lesser General Public
+License along with this library; if not, see http://www.gnu.org
+*/
+
+package net.wotonomy.foundation;
+
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.List;
+import java.util.ListIterator;
+
+/**
+* NSMutableArray extends NSArray to allow modification.
+*
+* @author michael@mpowers.net
+* @author $Author: cgruber $
+* @version $Revision: 893 $
+*/
+public class NSMutableArray extends NSArray
+{
+ /**
+ * Returns an NSArray backed by the specified List.
+ * This is useful to "protect" an internal representation
+ * that is returned by a method of return type NSArray.
+ */
+ public static NSMutableArray mutableArrayBackedByList( List aList )
+ {
+ return new NSMutableArray( aList, null );
+ }
+
+ NSMutableArray( List aList, Object ignored ) // differentiates
+ {
+ super( aList, ignored );
+ }
+
+ /**
+ * Default constructor returns an empty array.
+ */
+ public NSMutableArray ()
+ {
+ super();
+//System.out.println( "NSMutableArray: " + net.wotonomy.ui.swing.util.StackTraceInspector.getMyCaller() );
+ }
+
+ /**
+ * Constructor with a size hint.
+ */
+ public NSMutableArray ( int aSize )
+ {
+ super();
+//System.out.println( "NSMutableArray: " + net.wotonomy.ui.swing.util.StackTraceInspector.getMyCaller() );
+ }
+
+ /**
+ * Produces an array containing only the specified object.
+ */
+ public NSMutableArray (Object anObject)
+ {
+ super( anObject );
+//System.out.println( "NSMutableArray: " + net.wotonomy.ui.swing.util.StackTraceInspector.getMyCaller() );
+ }
+
+ /**
+ * Produces an array containing the specified objects.
+ */
+ public NSMutableArray (Object[] anArray)
+ {
+ super( anArray );
+//System.out.println( "NSMutableArray: " + net.wotonomy.ui.swing.util.StackTraceInspector.getMyCaller() );
+ }
+
+ /**
+ * Produces an array containing the objects in the specified collection.
+ */
+ public NSMutableArray (Collection aCollection)
+ {
+ super( aCollection );
+//System.out.println( "NSMutableArray: " + net.wotonomy.ui.swing.util.StackTraceInspector.getMyCaller() );
+ }
+
+ /**
+ * Removes the last object from the array.
+ */
+ public void removeLastObject ()
+ {
+ list.remove( count() - 1 );
+ }
+
+ /**
+ * Removes the object at the specified index.
+ */
+ public void removeObjectAtIndex (int index)
+ {
+ list.remove( index );
+ }
+
+ /**
+ * Adds all objects in the specified collection.
+ */
+ public void addObjectsFromArray (Collection aCollection)
+ {
+ list.addAll( aCollection );
+ }
+
+ /**
+ * Removes all objects from the array.
+ */
+ public void removeAllObjects ()
+ {
+ list.clear();
+ }
+
+ /**
+ * Removes all objects equivalent to the specified object
+ * within the range of specified indices.
+ */
+ public void removeObject (Object anObject, NSRange aRange)
+ {
+ if ( ( anObject == null ) || ( aRange == null ) ) return;
+
+ int loc = aRange.location();
+ int max = aRange.maxRange();
+ for ( int i = loc; i < max; i++ )
+ {
+ if ( anObject.equals( list.get( i ) ) )
+ {
+ list.remove( i );
+ i = i - 1;
+ max = max - 1;
+ }
+ }
+ }
+
+ /**
+ * Removes all instances of the specified object within the
+ * range of specified indices, comparing by reference.
+ */
+ public void removeIdenticalObject (Object anObject, NSRange aRange)
+ {
+ if ( ( anObject == null ) || ( aRange == null ) ) return;
+
+ int loc = aRange.location();
+ int max = aRange.maxRange();
+ for ( int i = loc; i < max; i++ )
+ {
+ if ( anObject == list.get( i ) )
+ {
+ list.remove( i );
+ i = i - 1;
+ max = max - 1;
+ }
+ }
+ }
+
+ /**
+ * Removes all objects in the specified collection from the array.
+ */
+ public void removeObjectsInArray (Collection aCollection)
+ {
+ list.removeAll( aCollection );
+ }
+
+ /**
+ * Removes all objects in the indices within the specified range
+ * from the array.
+ */
+ public void removeObjectsInRange (NSRange aRange)
+ {
+ if ( aRange == null ) return;
+
+ for ( int i = 0; i < aRange.length(); i++ )
+ {
+ list.remove( aRange.location() );
+ }
+ }
+
+ /**
+ * Replaces objects in the current range with objects from
+ * the specified range of the specified array. If currentRange
+ * is larger than otherRange, the extra objects are removed.
+ * If otherRange is larger than currentRange, the extra objects
+ * are added.
+ */
+ public void replaceObjectsInRange (NSRange currentRange,
+ List otherArray, NSRange otherRange)
+ {
+ if ( ( currentRange == null ) || ( otherArray == null ) ||
+ ( otherRange == null ) ) return;
+
+ // transform otherRange if out of bounds for array
+ if ( otherRange.maxRange() > otherArray.size() )
+ {
+ // TODO: Test this logic.
+ int loc = Math.min( otherRange.location(), otherArray.size() - 1 );
+ otherRange = new NSRange( loc, otherArray.size() - loc );
+ }
+
+ Object o;
+ List subList = list.subList(
+ currentRange.location(), currentRange.maxRange() );
+ int otherIndex = otherRange.location();
+ // TODO: Test this logic.
+ for ( int i = 0; i < subList.size(); i++ )
+ {
+ if ( otherIndex < otherRange.maxRange() )
+ { // set object
+ subList.set( i, otherArray.get( otherIndex ) );
+ }
+ else
+ { // remove extra elements from currentRange
+ subList.remove( i );
+ i--;
+ }
+ otherIndex++;
+ }
+ // TODO: Test this logic.
+ for ( int i = otherIndex; i < otherRange.maxRange(); i++ )
+ {
+ list.add( otherArray.get( i ) );
+ }
+ }
+
+ /**
+ * Clears the current array and then populates it with the
+ * contents of the specified collection.
+ */
+ public void setArray (Collection aCollection)
+ {
+ list.clear();
+ list.addAll( aCollection );
+ }
+
+ /**
+ * Sorts this array using the values from the specified selector.
+ */
+ public void sortUsingSelector (NSSelector aSelector)
+ {
+ //TODO: implement
+ throw new UnsupportedOperationException( "Not implemented yet." );
+ }
+
+ /**
+ * Removes all objects equivalent to the specified object.
+ */
+ public void removeObject (Object anObject)
+ {
+ list.remove( anObject );
+ }
+
+ /**
+ * Removes all occurences of the specified object,
+ * comparing by reference.
+ */
+ public void removeIdenticalObject (Object anObject)
+ {
+ Iterator it = list.iterator();
+ while ( it.hasNext() )
+ {
+ if ( it.next() == anObject )
+ {
+ it.remove();
+ }
+ }
+ }
+
+ /**
+ * Inserts the specified object into this array at the
+ * specified index.
+ */
+ public void insertObjectAtIndex (Object anObject, int anIndex)
+ {
+ list.add( anIndex, anObject );
+ }
+
+ /**
+ * Replaces the object at the specified index with the
+ * specified object.
+ */
+ public void replaceObjectAtIndex (int anIndex, Object anObject)
+ {
+ list.set( anIndex, anObject );
+ }
+
+ /**
+ * Adds the specified object to the end of this array.
+ */
+ public void addObject (Object anObject)
+ {
+ list.add( anObject );
+ }
+
+ public Object clone()
+ {
+ return new NSMutableArray( list );
+ }
+
+ public NSArray immutableClone() {
+ return new NSArray(this);
+ }
+
+ public NSMutableArray mutableClone() {
+ return new NSMutableArray(this);
+ }
+
+ // interface List: mutators
+
+ public void add(int index, Object element)
+ {
+ list.add( index, element );
+ }
+
+ public boolean add(Object o)
+ {
+ return list.add(o);
+ }
+
+ public boolean addAll(Collection coll)
+ {
+ return list.addAll(coll);
+ }
+
+ public boolean addAll(int index, Collection c)
+ {
+ return list.addAll( index, c );
+ }
+
+ public void clear()
+ {
+ list.clear();
+ }
+
+ public Iterator iterator()
+ {
+ return list.iterator();
+ }
+
+ public ListIterator listIterator()
+ {
+ return list.listIterator();
+ }
+
+ public ListIterator listIterator(int index)
+ {
+ return list.listIterator();
+ }
+
+ public Object remove(int index)
+ {
+ return list.remove( index );
+ }
+
+ public boolean remove(Object o)
+ {
+ return list.remove(o);
+ }
+
+ public boolean removeAll(Collection coll)
+ {
+ return list.removeAll(coll);
+ }
+
+ public boolean retainAll(Collection coll)
+ {
+ return list.retainAll(coll);
+ }
+
+ public Object set(int index, Object element)
+ {
+ return list.set( index, element );
+ }
+
+ public List subList(int fromIndex, int toIndex)
+ {
+ return list.subList( fromIndex, toIndex );
+ }
+
+}
+
+/*
+ * $Log$
+ * Revision 1.2 2006/02/16 13:15:00 cgruber
+ * Check in all sources in eclipse-friendly maven-enabled packages.
+ *
+ * Revision 1.8 2005/07/13 14:12:44 cgruber
+ * Add mutableClone() and immutableClone() per. WebObjects 5.3 conformance.
+ *
+ * Revision 1.7 2003/08/06 23:07:52 chochos
+ * general code cleanup (mostly, removing unused imports)
+ *
+ * Revision 1.6 2003/01/16 22:47:30 mpowers
+ * Compatibility changes to support compiling woextensions source.
+ * (34 out of 56 classes compile!)
+ *
+ * Revision 1.5 2003/01/10 19:16:40 mpowers
+ * Implemented support for page caching.
+ *
+ * Revision 1.4 2002/10/24 21:15:36 mpowers
+ * New implementations of NSArray and subclasses.
+ *
+ * Revision 1.3 2002/10/24 18:16:30 mpowers
+ * Now enforcing NSArray's immutable nature.
+ *
+ * Revision 1.2 2001/01/11 20:34:26 mpowers
+ * Implemented EOSortOrdering and added support in framework.
+ * Added header-click to sort table columns.
+ *
+ * Revision 1.1.1.1 2000/12/21 15:47:31 mpowers
+ * Contributing wotonomy.
+ *
+ * Revision 1.3 2000/12/20 16:25:38 michael
+ * Added log to all files.
+ *
+ *
+ */
+
diff --git a/projects/net.wotonomy.foundation/src/main/java/net/wotonomy/foundation/NSMutableData.java b/projects/net.wotonomy.foundation/src/main/java/net/wotonomy/foundation/NSMutableData.java
new file mode 100644
index 0000000..c677ea7
--- /dev/null
+++ b/projects/net.wotonomy.foundation/src/main/java/net/wotonomy/foundation/NSMutableData.java
@@ -0,0 +1,167 @@
+/*
+Wotonomy: OpenStep design patterns for pure Java applications.
+Copyright (C) 2000 Blacksmith, Inc.
+
+This library is free software; you can redistribute it and/or
+modify it under the terms of the GNU Lesser General Public
+License as published by the Free Software Foundation; either
+version 2.1 of the License, or (at your option) any later version.
+
+This library is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+Lesser General Public License for more details.
+
+You should have received a copy of the GNU Lesser General Public
+License along with this library; if not, see http://www.gnu.org
+*/
+
+package net.wotonomy.foundation;
+
+/**
+* A pure java implementation of NSMutableData, which
+* is basically an editable wrapper for a byte array.
+*
+* @author michael@mpowers.net
+* @author $Author: cgruber $
+* @version $Revision: 892 $
+*/
+public class NSMutableData
+ extends NSData
+{
+ /**
+ * Default constructor creates a zero-data object.
+ */
+ public NSMutableData ()
+ {
+ super();
+ }
+
+ /**
+ * Creates an object containing the contents of the specified URL.
+ */
+ public NSMutableData (java.net.URL aURL)
+ {
+ super( aURL );
+ }
+
+ /**
+ * Creates an object containing a copy of the contents of the
+ * specified NSData object.
+ */
+ public NSMutableData (NSData aData)
+ {
+ super( aData );
+ }
+
+ /**
+ * Creates an object containing the specified number of bytes
+ * initialized to all zeroes.
+ */
+ public NSMutableData (int size)
+ {
+ super( new byte[size] ); // inits to zeroes
+ }
+
+
+ /**
+ * Sets the length of the data to the specified length.
+ * If shorter, the data is truncated. If longer, the extra
+ * bytes are initialized to zeroes.
+ */
+ public void setLength (int length)
+ {
+ byte[] data = new byte[ length ]; // inits to zeroes
+ int limit = length;
+ if (limit > bytes.length)
+ limit = bytes.length;
+ for ( int i = 0; i < limit; i++ )
+ {
+ data[i] = this.bytes[ i ];
+ }
+ this.bytes = data;
+ }
+
+ /**
+ * Appends the specified data to the end of this data.
+ */
+ public void appendData (NSData aData)
+ {
+ int len = aData.length();
+ byte[] data = new byte[ bytes.length + len ];
+
+ int i;
+ for ( i = 0; i < bytes.length; i++ )
+ {
+ data[i] = bytes[i];
+ }
+
+ byte[] src = aData.bytes( 0, len );
+ for ( int j = 0; j < len; j++ )
+ {
+ data[i+j] = src[j];
+ }
+
+ bytes = data;
+ }
+
+ public void appendByte(byte b) {
+ setLength(bytes.length + 1);
+ bytes[bytes.length-1] = b;
+ }
+
+ public void appendBytes(byte[] b) {
+ int origLen = bytes.length;
+ setLength(origLen + b.length);
+ for (int i = 0; i < b.length; i++)
+ bytes[i + origLen] = b[i];
+ }
+
+ /**
+ * Increases the size of the byte array by the specified amount.
+ */
+ public void increaseLengthBy (int increment)
+ {
+ setLength( length() + increment );
+ }
+
+ /**
+ * Sets the bytes in the array within the specified range to zero.
+ */
+ public void resetBytesInRange (NSRange aRange)
+ {
+ int loc = aRange.location();
+ int max = aRange.maxRange();
+ for ( int i = loc; i < max; i++ )
+ {
+ bytes[i] = 0;
+ }
+ }
+
+ /**
+ * Copies the data in the specified object to this object,
+ * completely replacing the previous contents.
+ */
+ public void setData (NSData aData)
+ {
+ bytes = aData.bytes( 0, aData.length() );
+ }
+}
+
+/*
+ * $Log$
+ * Revision 1.1 2006/02/16 12:47:16 cgruber
+ * Check in all sources in eclipse-friendly maven-enabled packages.
+ *
+ * Revision 1.2 2003/08/06 23:57:13 chochos
+ * appendByte(), appendBytes()
+ *
+ * Revision 1.1.1.1 2000/12/21 15:47:34 mpowers
+ * Contributing wotonomy.
+ *
+ * Revision 1.3 2000/12/20 16:25:38 michael
+ * Added log to all files.
+ *
+ *
+ */
+
diff --git a/projects/net.wotonomy.foundation/src/main/java/net/wotonomy/foundation/NSMutableDictionary.java b/projects/net.wotonomy.foundation/src/main/java/net/wotonomy/foundation/NSMutableDictionary.java
new file mode 100644
index 0000000..0b291a7
--- /dev/null
+++ b/projects/net.wotonomy.foundation/src/main/java/net/wotonomy/foundation/NSMutableDictionary.java
@@ -0,0 +1,166 @@
+/*
+Wotonomy: OpenStep design patterns for pure Java applications.
+Copyright (C) 2000 Blacksmith, Inc.
+
+This library is free software; you can redistribute it and/or
+modify it under the terms of the GNU Lesser General Public
+License as published by the Free Software Foundation; either
+version 2.1 of the License, or (at your option) any later version.
+
+This library is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+Lesser General Public License for more details.
+
+You should have received a copy of the GNU Lesser General Public
+License along with this library; if not, see http://www.gnu.org
+*/
+
+package net.wotonomy.foundation;
+
+import java.util.Enumeration;
+import java.util.Map;
+
+/**
+* A pure java implementation of NSMutableDictionary that
+* implements Map for greater java interoperability.
+*
+* @author michael@mpowers.net
+* @author $Author: cgruber $
+* @version $Revision: 893 $
+*/
+public class NSMutableDictionary
+ extends NSDictionary
+{
+ /**
+ * Default constructor produces an empty dictionary.
+ */
+ public NSMutableDictionary ()
+ {
+ super();
+ }
+
+ /**
+ * Default constructor produces an empty dictionary.
+ */
+ public NSMutableDictionary (int initialSize)
+ {
+ super(initialSize);
+ }
+
+ /**
+ * Produces a dictionary that contains one key referencing one value.
+ */
+ public NSMutableDictionary (Object key, Object value)
+ {
+ super( key, value );
+ }
+
+ /**
+ * Produces a dictionary containing the specified keys and values.
+ * An IllegalArgumentException is thrown if the arrays are not
+ * of the same length.
+ */
+ public NSMutableDictionary (Object[] keys, Object[] values)
+ {
+ super( keys, values );
+ }
+
+ /**
+ * Produces a dictionary that is a copy of the specified map (or dictionary).
+ */
+ public NSMutableDictionary (Map aMap)
+ {
+ super( aMap );
+ }
+
+ /**
+ * Removes the key-value pair for the specified key.
+ */
+ public void removeObjectForKey (Object aKey)
+ {
+ remove( aKey );
+ }
+
+ /**
+ * Copies all mappings from the specified dictionary to this dictionary,
+ * replacing any mappings this map had for any keys in the specified map.
+ */
+ public void addEntriesFromDictionary (Map aMap)
+ {
+ putAll( aMap );
+ }
+
+ /**
+ * Removes all mappings from this dictionary.
+ */
+ public void removeAllObjects ()
+ {
+ clear();
+ }
+
+ /**
+ * Removes all keys in the specified array from this dictionary.
+ */
+ public void removeObjectsForKeys (NSArray anArray)
+ {
+ Enumeration enumeration = anArray.objectEnumerator();
+ while ( enumeration.hasMoreElements() )
+ {
+ removeObjectForKey( enumeration.nextElement() );
+ }
+ }
+
+ /**
+ * Clears all mappings in this dictionary and then adds all entries
+ * in the specified dictionary.
+ */
+ public void setDictionary (Map aMap)
+ {
+ removeAllObjects();
+ addEntriesFromDictionary( aMap );
+ }
+
+ /**
+ * Sets the value for the specified key. If the key currently
+ * exists to the dictionary, the old value is replaced with the
+ * specified value. An IllegalArgumentException is thrown if
+ * either the key or value is null.
+ */
+ public void setObjectForKey (Object aValue, Object aKey)
+ {
+ if ( ( aKey == null ) || ( aValue == null ) )
+ {
+ throw new IllegalArgumentException(
+ "Cannot use null objects with an NSMutableDictionary." );
+ }
+ put( aKey, aValue );
+ }
+}
+
+/*
+ * $Log$
+ * Revision 1.2 2006/02/16 13:15:00 cgruber
+ * Check in all sources in eclipse-friendly maven-enabled packages.
+ *
+ * Revision 1.4 2005/05/11 15:21:53 cgruber
+ * Change enum to enumeration, since enum is now a keyword as of Java 5.0
+ *
+ * A few other comments in the code.
+ *
+ * Revision 1.3 2002/06/30 17:16:26 mpowers
+ * Added new constructor taking an int: thanks cgruber.
+ *
+ *
+ * Revision 1.2 2001/02/23 23:43:41 mpowers
+ * Removed ill-advised this.
+ *
+ * Revision 1.1.1.1 2000/12/21 15:47:34 mpowers
+ * Contributing wotonomy.
+ *
+ * Revision 1.3 2000/12/20 16:25:38 michael
+ * Added log to all files.
+ *
+ *
+ */
+
diff --git a/projects/net.wotonomy.foundation/src/main/java/net/wotonomy/foundation/NSMutableRange.java b/projects/net.wotonomy.foundation/src/main/java/net/wotonomy/foundation/NSMutableRange.java
new file mode 100644
index 0000000..a0bcda1
--- /dev/null
+++ b/projects/net.wotonomy.foundation/src/main/java/net/wotonomy/foundation/NSMutableRange.java
@@ -0,0 +1,116 @@
+/*
+Wotonomy: OpenStep design patterns for pure Java applications.
+Copyright (C) 2000 Blacksmith, Inc.
+
+This library is free software; you can redistribute it and/or
+modify it under the terms of the GNU Lesser General Public
+License as published by the Free Software Foundation; either
+version 2.1 of the License, or (at your option) any later version.
+
+This library is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+Lesser General Public License for more details.
+
+You should have received a copy of the GNU Lesser General Public
+License along with this library; if not, see http://www.gnu.org
+*/
+
+package net.wotonomy.foundation;
+
+/**
+* A pure java implementation of NSMutableRange.
+* An NSMutableRange is a modifiable NSRange.
+*
+* @author michael@mpowers.net
+* @author $Author: cgruber $
+* @version $Revision: 893 $
+*/
+public class NSMutableRange extends NSRange
+{
+ /**
+ * Default constructor produces an empty range.
+ */
+ public NSMutableRange ()
+ {
+ super();
+ }
+
+ /**
+ * Produces a range that has the same location and length as
+ * the specified range.
+ */
+ public NSMutableRange (NSRange aRange)
+ {
+ super( aRange );
+ }
+
+ /**
+ * Produces a range with the specified location and length.
+ */
+ public NSMutableRange (int location, int length)
+ {
+ super( location, length );
+ }
+
+ /**
+ * Sets the location of this range.
+ */
+ public void setLocation (int location)
+ {
+ loc = location;
+ }
+
+ /**
+ * Sets the length of this range.
+ */
+ public void setLength (int length)
+ {
+ len = length;
+ }
+
+ /**
+ * Modifies this range to be the union of this
+ * range and the specified range.
+ */
+ public void unionRange (NSRange aRange)
+ {
+ NSRange range = rangeByUnioningRange( aRange );
+ setLocation( range.location() );
+ setLength( range.length() );
+ }
+
+ /**
+ * Modifies this range to be the intersection of this
+ * range and the specified range.
+ */
+ public void intersectRange (NSRange aRange)
+ {
+ NSRange range = rangeByIntersectingRange( aRange );
+ setLocation( range.location() );
+ setLength( range.length() );
+ }
+
+ /**
+ * Returns a copy of this range.
+ */
+ public Object clone ()
+ {
+ return new NSMutableRange( location(), length() );
+ }
+}
+
+/*
+ * $Log$
+ * Revision 1.2 2006/02/16 13:15:00 cgruber
+ * Check in all sources in eclipse-friendly maven-enabled packages.
+ *
+ * Revision 1.1.1.1 2000/12/21 15:47:36 mpowers
+ * Contributing wotonomy.
+ *
+ * Revision 1.3 2000/12/20 16:25:38 michael
+ * Added log to all files.
+ *
+ *
+ */
+
diff --git a/projects/net.wotonomy.foundation/src/main/java/net/wotonomy/foundation/NSNotification.java b/projects/net.wotonomy.foundation/src/main/java/net/wotonomy/foundation/NSNotification.java
new file mode 100644
index 0000000..f288d3f
--- /dev/null
+++ b/projects/net.wotonomy.foundation/src/main/java/net/wotonomy/foundation/NSNotification.java
@@ -0,0 +1,164 @@
+/*
+Wotonomy: OpenStep design patterns for pure Java applications.
+Copyright (C) 2000 Blacksmith, Inc.
+
+This library is free software; you can redistribute it and/or
+modify it under the terms of the GNU Lesser General Public
+License as published by the Free Software Foundation; either
+version 2.1 of the License, or (at your option) any later version.
+
+This library is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+Lesser General Public License for more details.
+
+You should have received a copy of the GNU Lesser General Public
+License along with this library; if not, see http://www.gnu.org
+*/
+
+package net.wotonomy.foundation;
+
+import java.util.Map;
+
+/**
+* An NSNotification is a generic message that can be
+* dispatched by the NSNotificationCenter.
+*
+* @author michael@mpowers.net
+* @author $Author: cgruber $
+* @version $Revision: 893 $
+*/
+public class NSNotification
+{
+ public static boolean showStack = false;
+
+ protected String name;
+ protected Object object;
+ protected Map userInfo;
+
+ // for debugging only
+ private Throwable stackTrace;
+
+ /**
+ * Default constructor creates a new notification
+ * with no name, object, or info dictionary.
+ */
+ public NSNotification ()
+ {
+ this( null, null, null );
+ }
+
+ /**
+ * Constructor specifying name and object.
+ */
+ public NSNotification ( String aName, Object anObject )
+ {
+ this( aName, anObject, null );
+ }
+
+ /**
+ * Constructor specifying name, object, and a Map
+ * containing application specific information.
+ */
+ public NSNotification (
+ String aName, Object anObject, Map aUserInfo )
+ {
+ name = aName;
+ object = anObject;
+ if ( showStack ) stackTrace = new RuntimeException();
+ userInfo = aUserInfo;
+ }
+
+ /**
+ * Returns the name of this notification.
+ */
+ public String name ()
+ {
+ return name;
+ }
+
+ /**
+ * Returns the object of this notification.
+ */
+ public Object object ()
+ {
+ return object;
+ }
+
+ /**
+ * Returns an NSDictionary that is a copy of
+ * the map containing application specific
+ * information relating to this notification,
+ * or null if no such data exists.
+ */
+ public NSDictionary userInfo ()
+ {
+ if ( userInfo == null ) return null;
+ return new NSDictionary( userInfo );
+ }
+
+ /**
+ * Returns a Map containing application specific
+ * information relating to this notification,
+ * or null if no such data exists.
+ * Note: this method is not in the spec.
+ */
+ public Map userInfoMap ()
+ {
+ return userInfo;
+ }
+
+ /**
+ * Returns the stack trace when this notification was generated,
+ * or null if showStack is false, which is the default.
+ * NOTE: This method is not part of the specification.
+ */
+ public Throwable stackTrace()
+ {
+ return stackTrace;
+ }
+
+ /**
+ * Returns a human-readable string representation.
+ */
+ public String toString()
+ {
+ return "[ " + name() + " : " + object() + " : " + userInfo() + " ]";
+ }
+}
+
+/*
+ * $Log$
+ * Revision 1.2 2006/02/16 13:15:00 cgruber
+ * Check in all sources in eclipse-friendly maven-enabled packages.
+ *
+ * Revision 1.7 2002/10/24 18:16:04 mpowers
+ * No longer generating stack trace by default.
+ *
+ * Revision 1.6 2002/06/21 22:02:47 mpowers
+ * Oops: Fixed NPE.
+ *
+ * Revision 1.5 2002/06/21 21:50:41 mpowers
+ * Added a method to get the map directly from the notification.
+ * Changed the internal representation to a map not a dictionary.
+ * We had been creating a new dictionary with each creation.
+ * This also allows people to modify the contents of the userInfo.
+ *
+ * Revision 1.4 2001/04/09 21:41:49 mpowers
+ * Better debugging.
+ *
+ * Revision 1.3 2001/02/21 18:31:07 mpowers
+ * Finished and tested implementation of NSNotificationCenter.
+ *
+ * Revision 1.2 2001/02/20 23:57:03 mpowers
+ * Implemented NSNotificationCenter.
+ *
+ * Revision 1.1.1.1 2000/12/21 15:47:36 mpowers
+ * Contributing wotonomy.
+ *
+ * Revision 1.3 2000/12/20 16:25:38 michael
+ * Added log to all files.
+ *
+ *
+ */
+
diff --git a/projects/net.wotonomy.foundation/src/main/java/net/wotonomy/foundation/NSNotificationCenter.java b/projects/net.wotonomy.foundation/src/main/java/net/wotonomy/foundation/NSNotificationCenter.java
new file mode 100644
index 0000000..aaf8261
--- /dev/null
+++ b/projects/net.wotonomy.foundation/src/main/java/net/wotonomy/foundation/NSNotificationCenter.java
@@ -0,0 +1,671 @@
+/*
+Wotonomy: OpenStep design patterns for pure Java applications.
+Copyright (C) 2000 Blacksmith, Inc.
+
+This library is free software; you can redistribute it and/or
+modify it under the terms of the GNU Lesser General Public
+License as published by the Free Software Foundation; either
+version 2.1 of the License, or (at your option) any later version.
+
+This library is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+Lesser General Public License for more details.
+
+You should have received a copy of the GNU Lesser General Public
+License along with this library; if not, see http://www.gnu.org
+*/
+
+package net.wotonomy.foundation;
+
+import java.lang.ref.ReferenceQueue;
+import java.lang.ref.WeakReference;
+import java.util.Hashtable;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.Vector;
+
+import net.wotonomy.foundation.internal.WotonomyException;
+
+/**
+* NSNotificationCenter broadcasts NSNotifications to
+* registered observers. Observers can register for all
+* notifications of a specific type, or all notifications
+* about a specific object, or both. Observers specify
+* the method that will be called when they are notified.
+* A global notification center can be accessed with
+* defaultCenter(), but other centers can be created and
+* used independently of the default center. <br><br>
+*
+* This implementation uses weak references for observers
+* and observables. The advantage to this approach is
+* that you do not need to explicitly unregister observers
+* or observables; they will be unregistered when they are
+* garbage-collected. Note that you will need to retain
+* a reference to any objects you register or they may
+* become unregistered if no other object references them.
+*
+* @author michael@mpowers.net
+* @author $Author: cgruber $
+* @version $Revision: 893 $
+*/
+public class NSNotificationCenter
+{
+ /**
+ * Null marker class simplifies equals() logic
+ * for CompoundKey class below.
+ */
+ public static final Object NullMarker = new Object();
+
+ private static NSNotificationCenter defaultCenter = null;
+
+ /**
+ * A Map of (name,object) pairs to a List
+ * of (observer,selector) pairs.
+ */
+ private Hashtable observers; // thread-safe
+
+ /**
+ * Default constructor creates a new notification center.
+ */
+ public NSNotificationCenter()
+ {
+ observers = new Hashtable();
+ }
+
+ /**
+ * Returns the system default center, creating one
+ * if it has not yet been created.
+ */
+ static public NSNotificationCenter defaultCenter()
+ {
+ if ( defaultCenter == null )
+ {
+ defaultCenter = new NSNotificationCenter();
+ }
+ return defaultCenter;
+ }
+
+ /**
+ * Addes the specified observer to the notification queue for
+ * notifications with the specified name or the specified
+ * object or both.
+ * @param anObserver The observer that wishes to be notified.
+ * @param aSelector The selector that will be invoked.
+ * Must have exactly one argument, to which a notification
+ * will be passed.
+ * @param notificationName The name of the notifications for
+ * which the observer will be notified. If null, will notify
+ * only based on matching anObject.
+ * @param anObject The object of the notifications for which
+ * the observer will be notified. If null, will notify
+ * only based on matching notificationName.
+ */
+ public void addObserver(
+ Object anObserver, NSSelector aSelector,
+ String notificationName, Object anObject )
+ {
+ // remove freed objects
+ processKeyQueue();
+
+ Object name = notificationName;
+ if ( name == null )
+ {
+ name = NullMarker;
+ }
+ if ( anObject == null )
+ {
+ anObject = NullMarker;
+ }
+ Object key = new CompoundKey( name, anObject );
+ Object value = new CompoundValue( anObserver, aSelector );
+ List list = (List) observers.get( key );
+ if ( list == null )
+ {
+ // create new list with value and put it in map
+ list = new Vector(); // thread-safe
+ list.add( value );
+ observers.put( new CompoundKey(
+ name, anObject, keyQueue ), list );
+ }
+ else
+ {
+ // add only if not already in list
+ if ( ! list.contains( value ) )
+ {
+ list.add( value );
+ }
+ }
+ }
+
+ /**
+ * Posts the specified notification. Notifies all registered
+ * observers that match either the notification name or
+ * the notification object, or both.
+ * @param aNotification The notification that will be passed
+ * to the observers selector.
+ */
+ public void postNotification(
+ NSNotification aNotification )
+ {
+ List mergedList = new LinkedList();
+ Object key, observerList;
+
+ Object name = aNotification.name();
+ Object object = aNotification.object();
+
+ if ( name != null )
+ {
+ if ( object != null )
+ { // both are specified
+ observerList = observers.get( new CompoundKey( name, object ) );
+ if ( observerList != null )
+ {
+ mergedList.addAll( (List) observerList );
+ }
+ observerList = observers.get( new CompoundKey( name, NullMarker ) );
+ if ( observerList != null )
+ {
+ mergedList.addAll( (List) observerList );
+ }
+ observerList = observers.get( new CompoundKey( NullMarker, object ) );
+ if ( observerList != null )
+ {
+ mergedList.addAll( (List) observerList );
+ }
+ }
+ else
+ { // object is null
+ observerList = observers.get( new CompoundKey( name, NullMarker ) );
+ if ( observerList != null )
+ {
+ mergedList.addAll( (List) observerList );
+ }
+ }
+ }
+ else
+ if ( object != null )
+ { // name is null
+ observerList = observers.get( new CompoundKey( NullMarker, object ) );
+ if ( observerList != null )
+ {
+ mergedList.addAll( (List) observerList );
+ }
+ }
+
+ key = new CompoundKey(
+ NullMarker, NullMarker );
+ observerList = observers.get( key );
+ if ( observerList != null )
+ {
+ mergedList.addAll( (List) observerList );
+ }
+
+ CompoundValue value;
+ Iterator it = mergedList.iterator();
+ while ( it.hasNext() )
+ {
+ value = (CompoundValue) it.next();
+ if ( value.get() == null )
+ {
+ it.remove();
+ }
+ else
+ {
+ try
+ {
+ value.selector().invoke(
+ value.get(),
+ new Object[] { aNotification } );
+ }
+ catch ( Exception exc )
+ {
+ WotonomyException w = new WotonomyException(
+ "Error notifying object: " + value.get() + " : " + aNotification, exc );
+// throw w;
+ w.printStackTrace();
+postNotification( "Error notifying object", this, new NSDictionary( "exception", w ) );
+ }
+ }
+ }
+
+ }
+
+ /**
+ * Posts a notification created from the specified name
+ * and object. Calls postNotification( NSNotification ).
+ * @param notificationName a String key to distinguish
+ * this notification.
+ * @param anObject any object, by convention this is
+ * the originator of the notification.
+ */
+ public void postNotification(
+ String notificationName, Object anObject )
+ {
+ postNotification( new NSNotification(
+ notificationName, anObject ) );
+ }
+
+ /**
+ * Posts a notification created from the specified name,
+ * object, and info. Calls postNotification( NSNotification ).
+ * @param notificationName a String key to distinguish
+ * this notification.
+ * @param anObject any object, by convention this is
+ * the originator of the notification.
+ * @param userInfo a Map containing information specific
+ * to the originator of the notification and that may
+ * be of interest to a knowledgable observer.
+ */
+ public void postNotification(
+ String notificationName, Object anObject, Map userInfo )
+ {
+ postNotification( new NSNotification(
+ notificationName, anObject, userInfo ) );
+ }
+
+ /**
+ * Unregisters the specified observer from all notification
+ * queues for which it is registered.
+ * @param anObserver The observer to be unregistered.
+ */
+ public void removeObserver(
+ Object anObserver )
+ {
+ // remove freed objects
+ processKeyQueue();
+
+ Iterator it = new LinkedList( observers.keySet() ).iterator();
+ while ( it.hasNext() )
+ {
+ removeObserver( anObserver, it.next() );
+ }
+ }
+
+ /**
+ * Unregisters the specified observer from all notifications
+ * queues associated with the specified name or object or both.
+ * @param anObserver The observer to be unregistered, if null
+ * will unregister all observers for the specified notification
+ * name and object.
+ * @param notificationName The name of the notification for which
+ * the observer will be unregistered, if null will unregister
+ * the specified observer for all notifications with the
+ * specified object.
+ * @param anObject The object for the notification for which
+ * the observer will be unregistered, if null will unregister
+ * the specified observer for all objects with the specified
+ * notification.
+ */
+ public void removeObserver(
+ Object anObserver, String notificationName, Object anObject )
+ {
+ // remove freed objects
+ processKeyQueue();
+
+ // get key matches
+ List keys = matchingKeys( notificationName, anObject );
+
+ // remove specified observer from each matching key
+ Iterator it = keys.iterator();
+ while ( it.hasNext() )
+ {
+ removeObserver( anObserver, it.next() );
+ }
+ }
+
+ /**
+ * Returns all keys that match the specified name and object,
+ * but in this case null parameters are considered wildcards.
+ * Pass NullMarkers if you want to explicitly match nulls.
+ */
+ private List matchingKeys( String name, Object object )
+ {
+ List result = new LinkedList();
+
+ boolean willAdd;
+ CompoundKey key;
+ Iterator it = observers.keySet().iterator();
+ while ( it.hasNext() )
+ {
+ key = (CompoundKey) it.next();
+ willAdd = false;
+ if ( ( name == null ) || ( name == key.name() ) )
+ {
+ if ( ( object == null ) || ( object == key.get() ) )
+ {
+ willAdd = true;
+ }
+ }
+ if ( willAdd )
+ {
+ result.add( key );
+ }
+ }
+ return result;
+ }
+
+ /**
+ * Removes the specified observer from the list referenced
+ * by the specified key in the observer map.
+ */
+ private void removeObserver(
+ Object anObserver, Object key )
+ {
+ // if observer null, remove all observers for key
+ if ( anObserver == null )
+ {
+ observers.remove( key );
+ return;
+ }
+
+ List list = (List) observers.get( key );
+ if ( list == null ) return;
+
+ // remove specified observer from list
+ Object observer;
+ Iterator it = list.iterator();
+ while ( it.hasNext() )
+ {
+ observer = ((CompoundValue)it.next()).get();
+ if ( ( observer == null ) || ( anObserver == observer ) )
+ {
+ // remove if match or freed object
+ it.remove();
+
+ // do not return; process entire list
+ }
+ }
+ if ( list.size() == 0 )
+ {
+ observers.remove( key );
+ }
+ }
+
+ /* Reference queues for cleared WeakKeys */
+ private ReferenceQueue keyQueue = new ReferenceQueue();
+
+ /**
+ * Removes any keys whose object has been garbage collected.
+ * (Garbage collected values are removed as they are encountered.)
+ */
+ private void processKeyQueue()
+ {
+ CompoundKey ck;
+ while ((ck = (CompoundKey)keyQueue.poll()) != null)
+ {
+ //System.out.println( "EOObserverCenter.processQueue: removing object" );
+ observers.remove(ck);
+ }
+ }
+
+ /**
+ * Key combining a name with an object.
+ * The object is weakly referenced, and keys
+ * are deallocated by reference queue.
+ * equals() compares by reference.
+ */
+ private static class CompoundKey extends WeakReference
+ {
+ private Object name;
+ private int hashCode;
+
+ /**
+ * Creates compound key.
+ * Neither name nor object may be null.
+ * Use NullMarker to represent null
+ * in either name or object.
+ */
+ public CompoundKey (
+ Object aName, Object anObject )
+ {
+ super( anObject );
+ name = aName;
+ hashCode = aName.hashCode() + anObject.hashCode();
+ }
+
+ /**
+ * Creates compound key with queue.
+ * Neither name nor object may be null.
+ * Use NullMarker to represent null
+ * in either name or object.
+ */
+ public CompoundKey (
+ Object aName, Object anObject, ReferenceQueue aQueue )
+ {
+ super( anObject, aQueue );
+ name = aName;
+ hashCode = aName.hashCode() + anObject.hashCode();
+ }
+
+ public Object name()
+ {
+ return name;
+ }
+
+ public int hashCode()
+ {
+ return hashCode;
+ }
+
+ public boolean equals( Object anObject )
+ {
+ if ( this == anObject ) return true;
+ // assumes only used with other compound keys
+ CompoundKey key = (CompoundKey) anObject;
+ if ( name == key.name || ( name != null && name.equals( key.name ) ) )
+ {
+ Object object = get();
+ if ( object != null )
+ {
+ // compares by reference
+ if ( object == ( key.get() ) )
+ {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ public String toString()
+ {
+ return "[CompoundKey:"+name()+":"+get()+"]";
+ }
+ }
+
+ /**
+ * Value combining an object with a selector.
+ * The object is weakly referenced, and null
+ * values are not allowed.
+ */
+ private static class CompoundValue extends WeakReference
+ {
+ private NSSelector selector;
+ private int hashCode;
+
+ public CompoundValue( Object anObject, NSSelector aSelector )
+ {
+ super( anObject );
+ hashCode = anObject.hashCode();
+ selector = aSelector;
+ }
+
+ public NSSelector selector()
+ {
+ return selector;
+ }
+
+ public int hashCode()
+ {
+ return hashCode;
+ }
+
+ public boolean equals( Object anObject )
+ {
+ if ( this == anObject ) return true;
+ // assumes only used with other compound values
+ CompoundValue value = (CompoundValue) anObject;
+ if ( selector == value.selector ||
+ ( selector != null && selector.equals( value.selector ) ) )
+ {
+ Object object = get();
+ if ( object != null )
+ {
+ if ( object == value.get() )
+ {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ public String toString()
+ {
+ return "[CompoundValue:"+get()+":"+selector().name()+"]";
+ }
+ }
+/*
+ public static void main( String[] argv )
+ {
+ Object aSource = "aSource";
+ Object bSource = "bSource";
+
+ Object oneTest = new OneTest();
+ Object twoTest = new TwoTest();
+ NSSelector notifyMeOnce =
+ new NSSelector( "notifyMeOnce",
+ new Class[] { NSNotification.class } );
+ NSSelector notifyMeTwice =
+ new NSSelector( "notifyMeTwice",
+ new Class[] { NSNotification.class } );
+
+ NSNotificationCenter.defaultCenter().addObserver(
+ oneTest, notifyMeOnce, "aMessage", null );
+
+ NSNotificationCenter.defaultCenter().addObserver(
+ oneTest, notifyMeOnce, null, aSource );
+
+ NSNotificationCenter.defaultCenter().addObserver(
+ twoTest, notifyMeOnce, "aMessage", aSource );
+
+ NSNotificationCenter.defaultCenter().addObserver(
+ twoTest, notifyMeTwice, null, null );
+
+ NSNotificationCenter.defaultCenter().postNotification(
+ "aMessage", aSource );
+ System.out.println();
+ NSNotificationCenter.defaultCenter().postNotification(
+ "aMessage", bSource );
+ System.out.println();
+ NSNotificationCenter.defaultCenter().postNotification(
+ "bMessage", aSource );
+ System.out.println();
+ NSNotificationCenter.defaultCenter().postNotification(
+ "bMessage", bSource );
+ System.out.println( "---" );
+
+ NSNotificationCenter.defaultCenter().removeObserver(
+ oneTest, null, aSource );
+
+ NSNotificationCenter.defaultCenter().postNotification(
+ "aMessage", aSource );
+ System.out.println();
+ NSNotificationCenter.defaultCenter().postNotification(
+ "aMessage", bSource );
+ System.out.println();
+ NSNotificationCenter.defaultCenter().postNotification(
+ "bMessage", aSource );
+ System.out.println();
+ NSNotificationCenter.defaultCenter().postNotification(
+ "bMessage", bSource );
+ System.out.println( "---" );
+
+ NSNotificationCenter.defaultCenter().removeObserver(
+ null );
+
+ NSNotificationCenter.defaultCenter().postNotification(
+ "aMessage", aSource );
+ System.out.println();
+ NSNotificationCenter.defaultCenter().postNotification(
+ "aMessage", bSource );
+ System.out.println();
+ NSNotificationCenter.defaultCenter().postNotification(
+ "bMessage", aSource );
+ System.out.println();
+ NSNotificationCenter.defaultCenter().postNotification(
+ "bMessage", bSource );
+ System.out.println( "---" );
+ }
+
+ static private class OneTest
+ {
+ public void notifyMeOnce( NSNotification aNotification )
+ {
+ System.out.println( "OneTest.notifyMeOnce: " + aNotification );
+ }
+ }
+
+ static private class TwoTest
+ {
+ public void notifyMeOnce( NSNotification aNotification )
+ {
+ System.out.println( "TwoTest.notifyMeOnce: " + aNotification );
+ }
+ public void notifyMeTwice( NSNotification aNotification )
+ {
+ System.out.println( "TwoTest.notifyMeTwice: " + aNotification );
+ }
+ }
+*/
+}
+
+
+
+/*
+ * $Log$
+ * Revision 1.2 2006/02/16 13:15:00 cgruber
+ * Check in all sources in eclipse-friendly maven-enabled packages.
+ *
+ * Revision 1.11 2003/06/03 14:51:15 mpowers
+ * Added commented-out println for debugging.
+ *
+ * Revision 1.10 2003/03/27 21:46:00 mpowers
+ * Better handling for null parameters on subscribe.
+ * Better handling for null parameters on post.
+ *
+ * Revision 1.9 2003/01/28 19:44:38 mpowers
+ * Now comparing strings by value not reference.
+ *
+ * Revision 1.8 2001/06/29 16:14:23 mpowers
+ * Fixed a javac compiler error that jikes allowed: shoe's on the other foot!
+ *
+ * Revision 1.7 2001/06/07 22:09:03 mpowers
+ * Exceptions during a notification are no longer being thrown
+ * so we can assure that all notifications get handled.
+ * Instead, we're printing stack traces...
+ *
+ * Revision 1.6 2001/04/09 21:41:50 mpowers
+ * Better debugging.
+ *
+ * Revision 1.5 2001/03/15 21:09:06 mpowers
+ * Fixed notifications with null objects.
+ *
+ * Revision 1.4 2001/02/21 21:18:34 mpowers
+ * Clarified need to retain references.
+ *
+ * Revision 1.3 2001/02/21 18:31:07 mpowers
+ * Finished and tested implementation of NSNotificationCenter.
+ *
+ * Revision 1.1.1.1 2000/12/21 15:47:39 mpowers
+ * Contributing wotonomy.
+ *
+ * Revision 1.3 2000/12/20 16:25:38 michael
+ * Added log to all files.
+ *
+ *
+ */
+
diff --git a/projects/net.wotonomy.foundation/src/main/java/net/wotonomy/foundation/NSNotificationQueue.java b/projects/net.wotonomy.foundation/src/main/java/net/wotonomy/foundation/NSNotificationQueue.java
new file mode 100644
index 0000000..7350a39
--- /dev/null
+++ b/projects/net.wotonomy.foundation/src/main/java/net/wotonomy/foundation/NSNotificationQueue.java
@@ -0,0 +1,345 @@
+/*
+Wotonomy: OpenStep design patterns for pure Java applications.
+Copyright (C) 2001 Intersect Software Corporation
+
+This library is free software; you can redistribute it and/or
+modify it under the terms of the GNU Lesser General Public
+License as published by the Free Software Foundation; either
+version 2.1 of the License, or (at your option) any later version.
+
+This library is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+Lesser General Public License for more details.
+
+You should have received a copy of the GNU Lesser General Public
+License along with this library; if not, see http://www.gnu.org
+*/
+
+package net.wotonomy.foundation;
+
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+
+/**
+* NSNotificationQueue coalesces notifications to be
+* posted to the NSNotificationCenter and can post them
+* asynchronously. While calling postNotification on
+* the notification center does not return until all
+* receivers have been notified, calling enqueueNotification
+* can return immediately. Use this class when you want
+* to coalesce notifications or notify asynchronously, or
+* both, which is the typical case.
+*
+* @author michael@mpowers.net
+* @author $Author: cgruber $
+* @version $Revision: 893 $
+*/
+public class NSNotificationQueue
+{
+ private static NSNotificationQueue defaultQueue = null;
+
+ /**
+ * Posting style specifying that the notification should
+ * be posted on the next available event loop.
+ */
+ public static final int PostASAP = 4;
+
+ /**
+ * Posting style specifying that the notification should
+ * be posted on the next available idle loop.
+ */
+ public static final int PostWhenIdle = 8;
+
+ /**
+ * Posting style specifying that the notification should
+ * be posted immediately. The enqueue method will not
+ * return until all receivers have been notified.
+ */
+ public static final int PostNow = 16;
+
+ /**
+ * Used to indicate that this notification should not
+ * be coalesced with other notifications.
+ * Ignored if combined with other coalesce flags.
+ */
+ public static final int NotificationNoCoalescing = 0;
+
+ /**
+ * Used to indicate that this notification should
+ * be coalesced with other notifications with the
+ * same name.
+ * May be combined with NotificationCoalescingOnSender.
+ */
+ public static final int NotificationCoalescingOnName = 1;
+
+ /**
+ * Used to indicate that this notification should
+ * be coalesced with other notifications with the
+ * same object argument (which is typically the sender).
+ * May be combined with NotificationCoalescingOnName.
+ */
+ public static final int NotificationCoalescingOnSender = 2;
+
+ /**
+ * The ASAP queue, which should probably use a LinkedList.
+ */
+ private List queue;
+
+ /**
+ * The idle queue, which should probably use a LinkedList.
+ */
+ private List idleQueue;
+
+ /**
+ * The notification center we will be using.
+ */
+ private NSNotificationCenter center;
+
+ /**
+ * Our private ASAP notifier.
+ */
+ private Notifier notifier;
+
+ /**
+ * Our private idle notifier.
+ */
+ private Notifier idleNotifier;
+
+ /**
+ * Default constructor creates a new notification queue
+ * that uses the default notification center.
+ */
+ public NSNotificationQueue()
+ {
+ this( NSNotificationCenter.defaultCenter() );
+ }
+
+ /**
+ * Creates a new notification queue that uses the
+ * specified notification center.
+ */
+ public NSNotificationQueue(
+ NSNotificationCenter aCenter )
+ {
+ queue = new LinkedList();
+ idleQueue = new LinkedList();
+ center = aCenter;
+ notifier = new Notifier( this, queue );
+ idleNotifier = new Notifier( this, idleQueue );
+ }
+
+ /**
+ * Returns the system default queue, creating one
+ * if it has not yet been created. The system default
+ * queue uses the system default notification center.
+ */
+ static public NSNotificationQueue defaultQueue()
+ {
+ if ( defaultQueue == null )
+ {
+ defaultQueue = new NSNotificationQueue();
+ }
+ return defaultQueue;
+ }
+
+ /**
+ * Removes notifications from the queue that match
+ * the specified notification, considering the
+ * specified coalesce mask.
+ */
+ public void dequeueMatchingNotifications(
+ NSNotification aNotification,
+ int aCoalesceMask)
+ {
+ if ( aCoalesceMask == NotificationNoCoalescing ) return;
+ dequeueFromQueue( aNotification, aCoalesceMask, queue );
+ dequeueFromQueue( aNotification, aCoalesceMask, idleQueue );
+ }
+
+ private void dequeueFromQueue(
+ NSNotification aNotification,
+ int aCoalesceMask,
+ List aQueue )
+ {
+ synchronized ( aQueue )
+ {
+ int flag;
+ NSNotification notification;
+ Object name = aNotification.name();
+ Object object = aNotification.object();
+ Iterator it = aQueue.iterator();
+ while ( it.hasNext() )
+ {
+ flag = 0;
+ notification = (NSNotification) it.next();
+ // if NotificationCoalescingOnName
+ if ( ( aCoalesceMask == 1 ) || ( aCoalesceMask == 3 ) )
+ {
+ if ( name == null )
+ {
+ if ( notification.name() != null )
+ {
+ flag += NotificationCoalescingOnName;
+ }
+ }
+ else
+ {
+ // compare by value
+ if ( name.equals( notification.name() ) )
+ {
+ flag += NotificationCoalescingOnName;
+ }
+ }
+ }
+ // if NotificationCoalescingOnSender
+ if ( aCoalesceMask >= 2 )
+ {
+ // compare by reference
+ if ( object == notification.object() )
+ {
+ flag += NotificationCoalescingOnSender;
+ }
+ }
+
+ if ( flag == aCoalesceMask )
+ {
+ it.remove();
+ }
+ }
+ }
+ }
+
+ /**
+ * Adds the notification to the queue to be run at
+ * the time specified by the posting style. The
+ * notification will be coalesced with other notifications
+ * that match the same name and object (sender) argument.
+ */
+ public void enqueueNotification(
+ NSNotification aNotification,
+ int aPostingStyle)
+ {
+ enqueueNotificationWithCoalesceMaskForModes(
+ aNotification, aPostingStyle,
+ NotificationCoalescingOnName + NotificationCoalescingOnSender,
+ null );
+ }
+
+ /**
+ * Adds the notification to the queue to be run at
+ * the time specified by the posting style and coelesced
+ * as the specified mask indicates.
+ * aModeList is currently ignored and may be null.
+ */
+ public void enqueueNotificationWithCoalesceMaskForModes(
+ NSNotification aNotification,
+ int aPostingStyle,
+ int aCoalesceMask,
+ List aModeList)
+ {
+ dequeueMatchingNotifications( aNotification, aCoalesceMask );
+
+ if ( aPostingStyle == PostNow )
+ {
+ center.postNotification( aNotification );
+ return;
+ }
+
+ if ( aPostingStyle == PostASAP )
+ {
+ synchronized ( queue )
+ {
+ queue.add( aNotification );
+ if ( ! notifier.willRun )
+ {
+ // asap runs at the very first run loop ordering, plus one just in case
+ NSRunLoop.invokeLaterWithOrder( notifier, 1 );
+ notifier.willRun = true;
+ }
+ }
+ return;
+ }
+
+ if ( aPostingStyle == PostWhenIdle )
+ {
+ synchronized ( idleQueue )
+ {
+ idleQueue.add( aNotification );
+ if ( ! idleNotifier.willRun )
+ {
+ // when idle runs at the very last run loop ordering, minus one just in case
+ NSRunLoop.invokeLaterWithOrder( idleNotifier, Integer.MAX_VALUE - 1 );
+ idleNotifier.willRun = true;
+ }
+ }
+ return;
+ }
+
+ }
+
+ private class Notifier implements Runnable
+ {
+ public boolean willRun;
+
+ NSNotificationQueue parent;
+ List queue;
+
+ public Notifier(
+ NSNotificationQueue aParent, List aQueue )
+ {
+ willRun = false;
+ parent = aParent;
+ queue = aQueue;
+ }
+
+ public void run()
+ {
+ Iterator it = new LinkedList( queue ).iterator();
+ synchronized ( queue )
+ {
+ queue.clear();
+ }
+ willRun = false;
+
+ // queue must already be cleared and willRun reset
+ // because this loop might queue more notifications
+ while ( it.hasNext() )
+ {
+ parent.center.postNotification(
+ (NSNotification) it.next() );
+ }
+ }
+ }
+
+}
+
+/*
+ * $Log$
+ * Revision 1.2 2006/02/16 13:15:00 cgruber
+ * Check in all sources in eclipse-friendly maven-enabled packages.
+ *
+ * Revision 1.6 2003/08/11 18:18:08 chochos
+ * improved encoding of strings, removed warnings
+ *
+ * Revision 1.5 2003/08/06 23:07:52 chochos
+ * general code cleanup (mostly, removing unused imports)
+ *
+ * Revision 1.4 2001/11/01 15:49:26 mpowers
+ * With NSRunLoop, we can now correctly implement PostASAP and PostWhenIdle.
+ *
+ * Revision 1.3 2001/06/25 14:47:24 mpowers
+ * Fixed serious error where some notifications were not being posted at all.
+ * A simple change to Notifier class fixed the problem. Thanks to glista.
+ *
+ * Revision 1.2 2001/02/26 15:53:22 mpowers
+ * Fine-tuning notification firing.
+ * Child display groups now update properly after parent save or invalidate.
+ *
+ * Revision 1.1 2001/02/24 17:03:22 mpowers
+ * Implemented the notification queue, and changed editing context to use it.
+ *
+ *
+ */
+
diff --git a/projects/net.wotonomy.foundation/src/main/java/net/wotonomy/foundation/NSNull.java b/projects/net.wotonomy.foundation/src/main/java/net/wotonomy/foundation/NSNull.java
new file mode 100644
index 0000000..db1f216
--- /dev/null
+++ b/projects/net.wotonomy.foundation/src/main/java/net/wotonomy/foundation/NSNull.java
@@ -0,0 +1,104 @@
+/*
+Wotonomy: OpenStep design patterns for pure Java applications.
+Copyright (C) 2001 Intersect Software Corporation
+
+This library is free software; you can redistribute it and/or
+modify it under the terms of the GNU Lesser General Public
+License as published by the Free Software Foundation; either
+version 2.1 of the License, or (at your option) any later version.
+
+This library is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+Lesser General Public License for more details.
+
+You should have received a copy of the GNU Lesser General Public
+License along with this library; if not, see http://www.gnu.org
+*/
+
+package net.wotonomy.foundation;
+
+import java.io.Serializable;
+
+/**
+* NSNull is used to represent null in Collections classes
+* because List and Map do not specify whether null values
+* are allowed and because NSArray and NSDictionary explicitly
+* do not allow null values. <br><br>
+*
+* Use of the static singleton method nullValue() is required
+* by this implementation because Java cannot return a singleton
+* instance from a constructor. Even then, more than one instance
+* may exist in the application due to object serialization.
+* Be sure to compare with equals().
+*
+* @author michael@mpowers.net
+* @author $Author: cgruber $
+* @version $Revision: 892 $
+*/
+public class NSNull implements Serializable
+{
+ private static final NSNull instance = new NSNull();
+
+ /**
+ * Create a new instance of NSNull.
+ */
+ private NSNull ()
+ {
+ }
+
+ /**
+ * Returns the static instance of nullValue.
+ * Note that serialization may mean that more than
+ * one instance of NSNull exists, so be sure to
+ * compare with equals().
+ */
+ public static NSNull nullValue ()
+ {
+ return instance;
+ }
+
+ /**
+ * Returns a human-readable string representation.
+ */
+ public String toString()
+ {
+ return "[null]";
+ }
+
+ /**
+ * Hashcode of all instances is zero.
+ */
+ public int hashCode()
+ {
+ return 0;
+ }
+
+ /**
+ * Implemented to return true for any instance of NSNull.
+ */
+ public boolean equals( Object anObject )
+ {
+ return ( anObject instanceof NSNull );
+ }
+}
+
+/*
+ * $Log$
+ * Revision 1.1 2006/02/16 12:47:16 cgruber
+ * Check in all sources in eclipse-friendly maven-enabled packages.
+ *
+ * Revision 1.3 2003/08/06 23:07:52 chochos
+ * general code cleanup (mostly, removing unused imports)
+ *
+ * Revision 1.2 2001/03/01 20:36:09 mpowers
+ * Implemented equals, hashcode, and serializable.
+ *
+ * Revision 1.1 2001/02/26 22:41:51 mpowers
+ * Implemented null placeholder classes.
+ * Duplicator now uses NSNull.
+ * No longer catching base exception class.
+ *
+ *
+ */
+
diff --git a/projects/net.wotonomy.foundation/src/main/java/net/wotonomy/foundation/NSNumberFormatter.java b/projects/net.wotonomy.foundation/src/main/java/net/wotonomy/foundation/NSNumberFormatter.java
new file mode 100644
index 0000000..064ee3c
--- /dev/null
+++ b/projects/net.wotonomy.foundation/src/main/java/net/wotonomy/foundation/NSNumberFormatter.java
@@ -0,0 +1,57 @@
+/*
+Wotonomy: OpenStep design patterns for pure Java applications.
+Copyright (C) 2003 Intersect Software Corporation
+
+This library is free software; you can redistribute it and/or
+modify it under the terms of the GNU Lesser General Public
+License as published by the Free Software Foundation; either
+version 2.1 of the License, or (at your option) any later version.
+
+This library is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+Lesser General Public License for more details.
+
+You should have received a copy of the GNU Lesser General Public
+License along with this library; if not, see http://www.gnu.org
+
+$Id: NSNumberFormatter.java 893 2006-02-16 13:22:23Z cgruber $
+
+*/
+
+package net.wotonomy.foundation;
+
+import java.text.DecimalFormat;
+
+/**
+* A Format that accepts C-style number formatting syntax.
+* Not currently implemented, included for compile compatibility.
+*
+* @author michael@mpowers.net
+* @author $Author: cgruber $
+* @version $Revision: 893 $
+*/
+
+public class NSNumberFormatter extends DecimalFormat
+{
+ public NSNumberFormatter()
+ {
+ super();
+ }
+
+ public NSNumberFormatter(String aPattern)
+ {
+ super( aPattern );
+ }
+}
+
+/*
+ * $Log$
+ * Revision 1.2 2006/02/16 13:15:00 cgruber
+ * Check in all sources in eclipse-friendly maven-enabled packages.
+ *
+ * Revision 1.1 2003/01/17 14:40:50 mpowers
+ * Adding files to fix build.
+ *
+ *
+ */
diff --git a/projects/net.wotonomy.foundation/src/main/java/net/wotonomy/foundation/NSPropertyListSerialization.java b/projects/net.wotonomy.foundation/src/main/java/net/wotonomy/foundation/NSPropertyListSerialization.java
new file mode 100644
index 0000000..7f8bcc9
--- /dev/null
+++ b/projects/net.wotonomy.foundation/src/main/java/net/wotonomy/foundation/NSPropertyListSerialization.java
@@ -0,0 +1,275 @@
+
+package net.wotonomy.foundation;
+
+public class NSPropertyListSerialization {
+
+ public static final int PLIST_ARRAY = 0;
+ public static final int PLIST_DICTIONARY = 1;
+ public static final int PLIST_DATA = 2;
+ public static final int PLIST_STRING = 3;
+
+ public static final char[] TOKEN_BEGIN = new char[]{
+ '(', '{', '<', '"'
+ };
+ public static final char[] TOKEN_END = new char[]{
+ ')', '}', '>', '"'
+ };
+ public static final char[] QUOTING_CHARS = new char[]{
+ ':', '/', '-', '.', '\\'
+ };
+
+ private NSPropertyListSerialization() {
+ super();
+ }
+
+ /** Creates a NSArray object from a string representation.
+ @s The string representation of a NSArray object. */
+ public static NSArray arrayForString(String s) {
+ s = s.trim();
+ if (!(s.charAt(0) == TOKEN_BEGIN[PLIST_ARRAY] && s.charAt(s.length()-1) == TOKEN_END[PLIST_ARRAY]))
+ return null;
+ NSMutableArray arr = new NSMutableArray();
+ int pos = 1;
+ int valbegin = -1;
+ while (pos < s.length()) {
+ char c = s.charAt(pos);
+ int tokenCount = 0;
+ int what = 0;
+ for (int i = 0 ; i < TOKEN_BEGIN.length; i++) {
+ if (c == TOKEN_BEGIN[i]) {
+ tokenCount = 1;
+ what = i;
+ break;
+ }
+ }
+ if (tokenCount > 0) {
+ //mark it
+ int quote = pos;
+ //find the closing token
+ do {
+ pos++;
+ try {
+ c = s.charAt(pos);
+ } catch (StringIndexOutOfBoundsException ex) {
+ throw new IllegalArgumentException("Could not parse property list; unclosed token '" + TOKEN_BEGIN[what] + "'");
+ }
+ if (c == '"' && what == PLIST_STRING) {
+ if (pos > 0 && s.charAt(pos-1) != '\\') {
+ tokenCount--;
+ }
+ } else if (c == TOKEN_BEGIN[what])
+ tokenCount++;
+ else if (c == TOKEN_END[what])
+ tokenCount--;
+ } while (tokenCount > 0);
+ arr.addObject(propertyListFromString(s.substring(quote, pos+1)));
+ valbegin = -1;
+ //advance to the next position
+ do {
+ pos++;
+ c = s.charAt(pos);
+ } while (Character.isWhitespace(c));
+ }
+ if (c == ',' || c ==')') {
+ if (valbegin > 0) {
+ arr.addObject(s.substring(valbegin, pos).trim());
+ valbegin = -1;
+ }
+ } else if (!Character.isWhitespace(c)) {
+ if (valbegin < 0) {
+ valbegin = pos;
+ }
+ }
+ pos++;
+ }
+ return arr;
+ }
+
+ /** Creates a NSDictionary instance from a string representation.
+ @s The string representation of a NSDictionary. */
+ public static NSDictionary dictionaryForString(String s) {
+ s = s.trim();
+ if (!(s.charAt(0) == TOKEN_BEGIN[PLIST_DICTIONARY] && s.charAt(s.length()-1) == TOKEN_END[PLIST_DICTIONARY]))
+ return null;
+ NSMutableDictionary d = new NSMutableDictionary();
+ int pos = 1;
+ boolean parsing = true;
+ Object key = null;
+ int valbegin = -1;
+ while (pos < s.length()) {
+ //look for an opening token
+ char c = s.charAt(pos);
+ int tokenCount = 0;
+ int what = 0;
+ for (int i = 0 ; i < TOKEN_BEGIN.length; i++) {
+ if (c == TOKEN_BEGIN[i]) {
+ tokenCount = 1;
+ what = i;
+ break;
+ }
+ }
+ if (tokenCount > 0) {
+ //mark it
+ int quote = pos;
+ //find the closing token
+ do {
+ pos++;
+ try {
+ c = s.charAt(pos);
+ } catch (StringIndexOutOfBoundsException ex) {
+ throw new IllegalArgumentException("Could not parse property list; unclosed token '" + TOKEN_BEGIN[what] + "'");
+ }
+ if (c == '"' && what == PLIST_STRING) {
+ if (pos > 0 && s.charAt(pos-1) != '\\') {
+ tokenCount--;
+ }
+ } else if (c == TOKEN_BEGIN[what])
+ tokenCount++;
+ else if (c == TOKEN_END[what])
+ tokenCount--;
+ } while (tokenCount > 0);
+ if (key == null) {
+ key = propertyListFromString(s.substring(quote, pos+1));
+ } else {
+ d.setObjectForKey(propertyListFromString(s.substring(quote, pos+1)), key);
+ key = null;
+ }
+ valbegin = -1;
+ //advance to the next position
+ do {
+ pos++;
+ c = s.charAt(pos);
+ } while (Character.isWhitespace(c));
+ }
+ if (c == ';' || c == '=' || c == '}') {
+ if (valbegin > 0) {
+ if (key == null) {
+ key = s.substring(valbegin, pos).trim();
+ } else {
+ d.setObjectForKey(s.substring(valbegin, pos).trim(), key);
+ key = null;
+ }
+ valbegin = -1;
+ }
+ } else if (!Character.isWhitespace(c)) {
+ if (valbegin < 0) {
+ valbegin = pos;
+ }
+ }
+ pos++;
+ }
+ return d;
+ }
+
+ public static boolean booleanForString(String s) {
+ return s.trim().toLowerCase().equals("true");
+ }
+
+ /** Creates a NSData instance from a string representation.
+ @s The string representation of a NSData object. */
+ public static NSData dataFromPropertyList(String s) {
+ String hex = "0123456789ABCDEF";
+ s = s.trim();
+ if (!(s.charAt(0) == TOKEN_BEGIN[PLIST_DATA] && s.charAt(s.length()-1) == TOKEN_END[PLIST_DATA]))
+ return null;
+ int pos = 1;
+ java.io.ByteArrayOutputStream bout = new java.io.ByteArrayOutputStream();
+ while (pos < s.length()-1) {
+ char c1 = s.charAt(pos);
+ while (c1 == ' ') {
+ pos++;
+ if (pos == s.length()-1)
+ return new NSData(bout.toByteArray());
+ c1 = s.charAt(pos);
+ }
+ if (hex.indexOf(c1) < 0)
+ throw new IllegalArgumentException("The string does not represent a NSData object (" + s + ", pos " + pos + ")");
+ pos++;
+ char c2 = s.charAt(pos);
+ if (hex.indexOf(c2) < 0)
+ throw new IllegalArgumentException("The string does not represent a NSData object (" + s + ")");
+ int x = (hex.indexOf(c1) << 4) | hex.indexOf(c2);
+ bout.write(x);
+ pos++;
+ }
+ return new NSData(bout.toByteArray());
+ }
+
+ public static int intForString(String s) {
+ return Integer.parseInt(s);
+ }
+
+ /** Returns the string representation of a property list.
+ @plist The property list. It can be a String, NSData, NSArray, NSDictionary. */
+ public static String stringForPropertyList(Object plist) {
+ if (plist == null)
+ return "";
+ if (plist instanceof NSArray || plist instanceof NSDictionary || plist instanceof NSData)
+ return plist.toString();
+ String x = plist.toString();
+ boolean quote = false;
+ for (int i = 0; i < x.length(); i++) {
+ char c = x.charAt(i);
+ for (int z = 0; z < TOKEN_BEGIN.length; z++) {
+ if (c == TOKEN_BEGIN[z] || c == TOKEN_END[z])
+ quote = true;
+ }
+ if (!quote) {
+ for (int z = 0; z < QUOTING_CHARS.length; z++) {
+ if (c == QUOTING_CHARS[z])
+ quote = true;
+ }
+ }
+ if (!quote && Character.isWhitespace(c)) {
+ quote = true;
+ i = x.length();
+ }
+ }
+ if (quote)
+ return "\"" + x + "\"";
+ return x;
+ }
+
+ /** Returns an property list created from a string representation.
+ @s The string with a representation of a property list.
+ @returns A property list object; either a NSData, NSArray, NSDictionary, or a String. */
+ public static Object propertyListFromString(String s) {
+ s = s.trim();
+ int type = -1;
+ for (int i = 0; i < TOKEN_BEGIN.length; i++) {
+ if (TOKEN_BEGIN[i] == s.charAt(0)) {
+ if (TOKEN_END[i] == s.charAt(s.length()-1))
+ type = i;
+ }
+ }
+ switch (type) {
+ case PLIST_DATA:
+ return dataFromPropertyList(s);
+ case PLIST_ARRAY:
+ return arrayForString(s);
+ case PLIST_DICTIONARY:
+ return dictionaryForString(s);
+ case PLIST_STRING:
+ if (s.equals("\"\""))
+ return "";
+ return s.substring(1, s.length()-1);
+ }
+ return s;
+ }
+
+ /*
+ * $Log$
+ * Revision 1.2 2006/02/16 13:15:00 cgruber
+ * Check in all sources in eclipse-friendly maven-enabled packages.
+ *
+ * Revision 1.5 2003/08/11 18:18:08 chochos
+ * improved encoding of strings, removed warnings
+ *
+ * Revision 1.4 2003/08/11 17:33:45 chochos
+ * Implemented detection of escaped quotes (\"). improved quoting strings when converting property lists to strings.
+ *
+ * Revision 1.3 2003/08/04 23:50:55 chochos
+ * propertyListForString() now works. dictionaryForString and arrayForString can parse complex structures with nested arrays and dictionaries.
+ *
+ */
+} \ No newline at end of file
diff --git a/projects/net.wotonomy.foundation/src/main/java/net/wotonomy/foundation/NSRange.java b/projects/net.wotonomy.foundation/src/main/java/net/wotonomy/foundation/NSRange.java
new file mode 100644
index 0000000..2de52f5
--- /dev/null
+++ b/projects/net.wotonomy.foundation/src/main/java/net/wotonomy/foundation/NSRange.java
@@ -0,0 +1,351 @@
+/*
+Wotonomy: OpenStep design patterns for pure Java applications.
+Copyright (C) 2000 Blacksmith, Inc.
+
+This library is free software; you can redistribute it and/or
+modify it under the terms of the GNU Lesser General Public
+License as published by the Free Software Foundation; either
+version 2.1 of the License, or (at your option) any later version.
+
+This library is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+Lesser General Public License for more details.
+
+You should have received a copy of the GNU Lesser General Public
+License along with this library; if not, see http://www.gnu.org
+*/
+
+package net.wotonomy.foundation;
+
+/**
+* A pure java implementation of NSRange.
+* An NSRange represents a range of numbers
+* having a starting location and spanning a
+* length.
+*
+* @author michael@mpowers.net
+* @author $Author: cgruber $
+* @version $Revision: 920 $
+*/
+public class NSRange implements Cloneable
+{
+ /**
+ * An empty range.
+ */
+ public static final NSRange ZeroRange = new NSRange();
+
+ protected int loc;
+ protected int len;
+
+ /**
+ * Default constructor produces an empty range.
+ */
+ public NSRange ()
+ {
+ this( 0, 0 );
+ }
+
+ /**
+ * Produces a range with the specified location and length.
+ */
+ public NSRange (int location, int length)
+ {
+ loc = location;
+ len = length;
+ }
+
+ /**
+ * Produces a range that has the same location and length as
+ * the specified range.
+ */
+ public NSRange (NSRange aRange)
+ {
+ this( aRange.location(), aRange.length() );
+ }
+
+ /**
+ * Returns the location of this range.
+ */
+ public int location ()
+ {
+ return loc;
+ }
+
+ /**
+ * Returns the length of this range.
+ */
+ public int length ()
+ {
+ return len;
+ }
+
+ /**
+ * Returns the maximum extent of the range. This number is
+ * one more than the last position in the range.
+ */
+ public int maxRange ()
+ {
+ return location() + length() -1;
+ }
+
+ /**
+ * Returns whether this is an empty range, therefore
+ * whether the length is zero.
+ */
+ public boolean isEmpty ()
+ {
+ return ( length() == 0 );
+ }
+
+ /**
+ * Returns whether the specified location is contained
+ * within this range.
+ */
+ public boolean locationInRange (int location)
+ {
+ if ( location < location() ) return false;
+ if ( location >= maxRange() ) return false;
+ return true;
+ }
+
+ /**
+ * Returns whether the specified range is equal to this range.
+ */
+ public boolean isEqualToRange (NSRange aRange)
+ {
+ if ( aRange == null ) return false;
+ return ( ( aRange.location() == location() )
+ && ( aRange.length() == length() ) );
+ }
+
+ /**
+ * Returns whether the specified object is equal to this range.
+ */
+ public boolean equals (Object anObject)
+ {
+ if ( anObject instanceof NSRange )
+ return isEqualToRange( (NSRange) anObject );
+ return false;
+ }
+
+ /**
+ * Returns a hashCode.
+ */
+ public int hashCode ()
+ {
+ // TODO: Test this logic.
+ return ( location() << 2 ) & length(); // bitwise ops never my forte
+ }
+
+ /**
+ * Returns a string representation of this range.
+ */
+ public String toString ()
+ {
+ return "[NSRange: location = " + location()
+ + "; length = " + length() + "]";
+ }
+
+ /**
+ * Returns the union of this range and the specified range, if any.
+ * Gaps are filled, so the result is the smallest starting position
+ * and the largest ending position.
+ */
+ public NSRange rangeByUnioningRange (NSRange aRange)
+ {
+ if ( aRange == null ) return this;
+
+ // TODO: Test this logic.
+ int resultLoc = Math.min( this.location(), aRange.location() );
+ int resultLen = Math.max( this.location() + this.length(),
+ aRange.location() + aRange.length() ) - resultLoc;
+ return new NSRange( resultLoc, resultLen );
+ }
+
+ /**
+ * Returns the intersection of this range and the specified range,
+ * if any. If no intersection, returns an empty range.
+ */
+ public NSRange rangeByIntersectingRange (NSRange aRange)
+ {
+ // TODO: Test this logic.
+ if ( ! intersectsRange( aRange ) ) return ZeroRange;
+ int start = Math.max( this.location(), aRange.location() );
+ int end = Math.min( this.location() + this.length(),
+ aRange.location() + aRange.length() );
+ return new NSRange( start, end - start );
+ }
+
+ /**
+ * Returns whether the specified range overlaps
+ * at any point with this range.
+ */
+ public boolean intersectsRange (NSRange aRange)
+ {
+ // TODO: Test this logic.
+ if ( aRange == null ) return false;
+ if ( ( this.location() >= aRange.location() )
+ && ( this.location() < aRange.location() + aRange.length() ) )
+ return true;
+ if ( ( aRange.location() >= this.location() )
+ && ( aRange.location() < this.location() + this.length() ) )
+ return true;
+ return false;
+ }
+
+ /**
+ * Returns whether this range is completely
+ * contained within the specified range.
+ */
+ public boolean isSubrangeOfRange (NSRange aRange)
+ {
+ // TODO: Test this logic.
+ if ( aRange == null ) return false;
+ if ( ( this.location() >= aRange.location() )
+ && ( this.maxRange() <= aRange.maxRange() ) )
+ return true;
+ return false;
+ }
+
+ /**
+ * Eliminates any intersections between this range and the specified
+ * range. This produces two ranges, either of which may be empty.
+ * These two ranges are returned by modifying the supplied second
+ * and third parameters.
+ */
+ public void subtractRange (NSRange aRange,
+ NSMutableRange firstResult, NSMutableRange secondResult)
+ {
+ if ( aRange == null ) return;
+
+ // TODO: Test this logic.
+ // no intersection: return this and aRange without calculation
+ if ( ! intersectsRange( aRange ) )
+ {
+ if ( firstResult != null )
+ {
+ firstResult.setLocation( this.location() );
+ firstResult.setLength( this.length() );
+ }
+ if ( secondResult != null )
+ {
+ secondResult.setLocation( aRange.location() );
+ secondResult.setLength( aRange.location() );
+ }
+ return;
+ }
+
+ // TODO: Test this logic.
+ // this range is completely contained by other range
+ if ( isSubrangeOfRange( aRange ) )
+ {
+ if ( firstResult != null )
+ {
+ firstResult.setLocation( aRange.location() );
+ firstResult.setLength( this.location() - aRange.location() );
+ }
+ if ( secondResult != null )
+ {
+ secondResult.setLocation( this.maxRange() );
+ secondResult.setLength(
+ aRange.maxRange() - this.maxRange() - 1 ); // test this
+ }
+ return;
+ }
+
+ // TODO: Test this logic.
+ // other range is completely contained by this range
+ if ( aRange.isSubrangeOfRange( this ) )
+ {
+ if ( firstResult != null )
+ {
+ firstResult.setLocation( this.location() );
+ firstResult.setLength( aRange.location() - this.location() );
+ }
+ if ( secondResult != null )
+ {
+ secondResult.setLocation( aRange.maxRange() );
+ secondResult.setLength(
+ this.maxRange() - aRange.maxRange() - 1 ); // test this
+ }
+ return;
+ }
+
+ // TODO: Test this logic.
+ // ranges intersect: remove only the intersection
+
+ NSRange firstRange, secondRange;
+ if ( this.location() <= aRange.location() )
+ {
+ firstRange = this;
+ secondRange = aRange;
+ }
+ else
+ {
+ firstRange = aRange;
+ secondRange = this;
+ }
+
+ if ( firstResult != null )
+ {
+ firstResult.setLocation( firstRange.location() );
+ firstResult.setLength(
+ secondRange.location() - firstRange.location() );
+ }
+ if ( secondResult != null )
+ {
+ secondResult.setLocation( firstRange.maxRange() );
+ secondResult.setLength(
+ secondRange.maxRange() - aRange.maxRange() - 1 ); // test this
+ }
+ return;
+
+ }
+
+ /**
+ * Returns a copy of this range.
+ */
+ public Object clone ()
+ {
+ return new NSRange( location(), length() );
+ }
+
+ /**
+ * Parses a range from a string of the form "{x,y}" where
+ * x is the location and y is the length. If not parsable,
+ * an IllegalArgumentException is thrown.
+ */
+ public static NSRange fromString (String aString)
+ {
+ // TODO: Test this logic.
+ try
+ {
+ java.util.StringTokenizer tokens =
+ new java.util.StringTokenizer( aString, "{,}" );
+ int loc = Integer.parseInt( tokens.nextToken() );
+ int len = Integer.parseInt( tokens.nextToken() );
+ return new NSRange( loc, len );
+ }
+ catch ( Exception exc )
+ {
+ throw new IllegalArgumentException( exc.toString() );
+ }
+ }
+
+}
+
+/*
+ * $Log$
+ * Revision 1.2 2006/02/16 13:15:00 cgruber
+ * Check in all sources in eclipse-friendly maven-enabled packages.
+ *
+ * Revision 1.1.1.1 2000/12/21 15:47:42 mpowers
+ * Contributing wotonomy.
+ *
+ * Revision 1.3 2000/12/20 16:25:38 michael
+ * Added log to all files.
+ *
+ *
+ */
+
diff --git a/projects/net.wotonomy.foundation/src/main/java/net/wotonomy/foundation/NSRecursiveLock.java b/projects/net.wotonomy.foundation/src/main/java/net/wotonomy/foundation/NSRecursiveLock.java
new file mode 100644
index 0000000..65f58db
--- /dev/null
+++ b/projects/net.wotonomy.foundation/src/main/java/net/wotonomy/foundation/NSRecursiveLock.java
@@ -0,0 +1,148 @@
+/*
+Wotonomy: OpenStep design patterns for pure Java applications.
+Copyright (C) 2002 Israfil consulting Services Corporation
+
+This library is free software; you can redistribute it and/or
+modify it under the terms of the GNU Lesser General Public
+License as published by the Free Software Foundation; either
+version 2.1 of the License, or (at your option) any later version.
+
+This library is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+Lesser General Public License for more details.
+
+You should have received a copy of the GNU Lesser General Public
+License along with this library; if not, see http://www.gnu.org
+
+$Id: NSRecursiveLock.java 893 2006-02-16 13:22:23Z cgruber $
+
+*/
+
+package net.wotonomy.foundation;
+
+import EDU.oswego.cs.dl.util.concurrent.ReentrantLock;
+
+/**
+* A lock class that allows a thread to re-acquire it's lock
+* recursively. Currently an API-compliance wrapper around Doug Lea's
+* ReentrantLock, conforming to the API and behavior of
+* com.webobjects.foundation.NSRecursiveLock.
+*
+* @author cgruber@israfil.net
+* @author $Author: cgruber $
+* @version $Revision: 893 $
+*/
+
+public class NSRecursiveLock extends ReentrantLock implements NSLocking {
+
+ public NSRecursiveLock() {
+ }
+ /** Acquire the lock, catching the thrown exception to mirror the
+ * behavior of com.webobjects.foundation.NSRecursiveLock. Note that
+ * ReentrantLock.acquire() performs a notify() when it's interrupted.
+ *
+ * @see edu.oswego.cs.dl.util.concurrent.ReentrantLock#acquire()
+ */
+ public void lock() {
+ try {
+ acquire();
+ } catch (InterruptedException interruptedexception) {
+ // Null behavior, as notify() is already called
+ // by acquire();
+ // We may want to log here.
+ }
+ }
+
+ /** Pass the buck to tryLock(long), passing zero time as the parameter.
+ *
+ * @see #tryLock(long)
+ */
+ public boolean tryLock() {
+ return tryLock(1);
+ }
+
+ /** Attempt to acquire the lock, catching the thrown exception to mirror
+ * the behavior of com.webobjects.foundation.NSRecursiveLock. Note that
+ * ReentrantLock.attempt(*) performs a notify() when it's interrupted.
+ * Fail gracefully after the given milliseconds
+ *
+ * @param (long)
+ * @see edu.oswego.cs.dl.util.concurrent.ReentrantLock#acquire()
+ */
+ public boolean tryLock(long milliseconds) {
+ try {
+ return attempt(milliseconds);
+ } catch (InterruptedException interruptedexception) {
+ // notify() is already called by attempt();
+ // We may want to log here.
+ return false;
+ }
+ }
+ /**
+ * Attempt to acquire a lock until the timestamp is reached. Add
+ * 1 to the recursion count if the calling thread already owns the
+ * lock. Otherwise block until free or until the given timestamp
+ * is reached.
+ *
+ * @see Timestamp
+ * @see ReentrantLock.attempt(long);
+ */
+ public boolean tryLock(NSTimestamp nstimestamp) {
+ return tryLock(nstimestamp.getTime() - System.currentTimeMillis());
+ }
+
+ /** Unlock the current lock precisely once.
+ */
+ public synchronized void unlock() {
+ unlock(1);
+ }
+
+ /** Unlock the current lock count times.
+ */
+ public synchronized void unlock(long count) {
+ if (owner_ != null && Thread.currentThread() != owner_)
+ throw new IllegalStateException("Illegal Lock usage: unlocking thread not owner.");
+ if (owner_ == null || holds_ == 0L)
+ throw new IllegalStateException("Illegal Lock usage: unlock() called without a lock().");
+ release(count);
+ }
+
+ public synchronized long recursionCount() {
+ return holds();
+ }
+
+ public String toString() {
+ long holds = holds();
+ boolean oneHold = (holds == 1);
+ boolean noHolds = (holds < 1 || owner_ == null);
+ return getClass().getName() + " <" +
+ ((noHolds) ? "Unlocked" : ( "Locked " + holds + " time" + (oneHold ? "" : "s") + " by " + owner_ ) ) + ">";
+ }
+
+}
+/*
+ * $Log$
+ * Revision 1.2 2006/02/16 13:15:00 cgruber
+ * Check in all sources in eclipse-friendly maven-enabled packages.
+ *
+ * Revision 1.1 2002/07/14 21:56:16 mpowers
+ * Contributions from cgruber.
+ *
+ * Revision 1.5 2002/06/25 19:02:19 cgruber
+ * I'm a dumbass.
+ *
+ * Revision 1.4 2002/06/25 18:52:56 cgruber
+ * Fix javadocs that resulted from bad cut-and-paste of the boilerplate.
+ *
+ * Revision 1.3 2002/06/25 18:45:27 cgruber
+ * Add some javadocs.
+ *
+ * Revision 1.2 2002/06/25 18:06:48 cgruber
+ * Add implementation of NSRecursiveLock using Doug Lea's concurrent programming APIs.
+ * Specifically inherit from ReentrantLock, which magically does the exact job we want!
+ *
+ * Revision 1.1 2002/06/25 07:52:56 cgruber
+ * Add quite a few abstract classes, interfaces, and classes. All API consistent with WebObjects, but with no implementation, nor any private or package access members from the original.
+ *
+ */
diff --git a/projects/net.wotonomy.foundation/src/main/java/net/wotonomy/foundation/NSRunLoop.java b/projects/net.wotonomy.foundation/src/main/java/net/wotonomy/foundation/NSRunLoop.java
new file mode 100644
index 0000000..2d122aa
--- /dev/null
+++ b/projects/net.wotonomy.foundation/src/main/java/net/wotonomy/foundation/NSRunLoop.java
@@ -0,0 +1,522 @@
+/*
+Wotonomy: OpenStep design patterns for pure Java applications.
+Copyright (C) 2001 Intersect Software Corporation
+
+This library is free software; you can redistribute it and/or
+modify it under the terms of the GNU Lesser General Public
+License as published by the Free Software Foundation; either
+version 2.1 of the License, or (at your option) any later version.
+
+This library is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+Lesser General Public License for more details.
+
+You should have received a copy of the GNU Lesser General Public
+License along with this library; if not, see http://www.gnu.org
+*/
+
+package net.wotonomy.foundation;
+
+import java.awt.AWTEvent;
+import java.awt.EventQueue;
+import java.awt.Toolkit;
+import java.awt.event.InvocationEvent;
+import java.util.EmptyStackException;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.ListIterator;
+
+/**
+* NSRunLoop is provided specifically for EODelayedObserverQueue
+* and EOEditingContext, which assume the existence of a
+* prioritized event queue that Java does not provide. <br><br>
+*
+* This extends java.awt.EventQueue and does not conform to the
+* NSRunLoop specifications. The only supported methods are
+* NSRunLoop.currentRunLoop, performSelectorWithOrder, and
+* cancelSelectorWithOrder. Note that in Swing there is only
+* one AWT thread and one event queue; newly created threads
+* will not get their own run loop as in OpenStep.<br><br>
+*
+* That said, this event queue is servicable as a replacement
+* for the default event queue and will provide prioritized
+* execution of selectors before and after normal AWT events.
+* <br><br>
+*
+* Each run loop dispatches the lowest order event from
+* the queue. When queued events have the same ordering,
+* they are dispatched as first-in, first-out (FIFO). Because
+* all AWT events have the same ordering (AWTEventsRunLoopOrdering),
+* they are processed FIFO, just like the default event queue. <br><br>
+*
+* Note that because EventQueue is not well-factored for
+* subclassing, pushing a new event queue onto the stack
+* on top of this one will only copy the existing AWT events
+* to the new queue. For this reason, pushing new event
+* queues onto the stack is not supported and will throw
+* an exception.
+*
+* @author michael@mpowers.net
+* @author $Author: cgruber $
+* @version $Revision: 893 $
+*/
+public class NSRunLoop extends EventQueue
+{
+ /**
+ * This is the ordering at which the conventional AWT event
+ * queue will be executed. Selectors with this ordering
+ * or less will be executed before AWT events, and
+ * selectors with ordering greater than this ordering will be
+ * be executed after AWT events.
+ */
+ public static final int AWTEventsRunLoopOrdering = 500000;
+
+ /**
+ * The singleton instance.
+ */
+ protected static NSRunLoop instance;
+
+ private LinkedList earlyQueue;
+ private LinkedList lateQueue;
+
+ /**
+ * Needed because JDK1.4 made our lives more difficult.
+ */
+ private static Toolkit toolkit;
+
+ /**
+ * Because SunToolkit.flushPendingEvents was changed
+ * to a static method in 1.4, you can't compile the library
+ * in such a way that it works with both 1.3 and 1.4.
+ * So we have to rely on dynamic method invocation,
+ * which is slower, but we try to make it as fast as
+ * humanly possible.
+ */
+ private static NSSelector flushPendingEvents;
+
+ /**
+ * Create a new instance of NSRunLoop.
+ */
+ protected NSRunLoop ()
+ {
+ earlyQueue = new LinkedList();
+ lateQueue = new LinkedList();
+ }
+
+ /**
+ * Returns the singleton instance of NSRunLoop.
+ * This returns the same instance no matter what
+ * thread calls it, which is different from OpenStep.
+ * NSRunLoop is limited to a singleton instance
+ * because there is no way of obtaining the stack
+ * of event queues from EventQueue because it is
+ * private state.
+ */
+ public synchronized static NSRunLoop currentRunLoop ()
+ {
+ if ( instance == null )
+ {
+ // create and initialize
+ flushPendingEvents = new NSSelector( "flushPendingEvents" );
+ toolkit = Toolkit.getDefaultToolkit();
+ instance = new NSRunLoop();
+
+ toolkit.getSystemEventQueue().push( instance );
+ }
+
+ return instance;
+ }
+
+ /**
+ * Post a 1.1-style event to the EventQueue. If there is an
+ * existing event on the queue with the same ID and event source,
+ * the source Component's coalesceEvents method will be called.
+ *
+ * @param theEvent an instance of java.awt.AWTEvent, or a
+ * subclass of it.
+ */
+ public void postEvent(AWTEvent theEvent)
+ {
+ if ( theEvent instanceof OrderedInvocationEvent )
+ {
+ OrderedInvocationEvent event = (OrderedInvocationEvent) theEvent;
+ if ( event.getOrdering() > AWTEventsRunLoopOrdering )
+ {
+ insertEventIntoQueue( event, lateQueue );
+ }
+ else
+ {
+ insertEventIntoQueue( event, earlyQueue );
+ }
+ }
+ else
+ {
+ super.postEvent( theEvent );
+ }
+ }
+
+ private synchronized void insertEventIntoQueue( OrderedInvocationEvent e, LinkedList q )
+ {
+ OrderedInvocationEvent o;
+ int ordering = e.getOrdering();
+ ListIterator iterator =
+ q.listIterator();
+
+ // iterate forwards until we find a priority
+ // greater than our priority,
+ // then insert ourself before that element.
+ while ( iterator.hasNext() )
+ {
+ o = (OrderedInvocationEvent) iterator.next();
+ if ( o.getOrdering() > ordering )
+ {
+ // back up one
+ iterator.previous();
+ break;
+ }
+ }
+ // add after the current element
+ iterator.add( e );
+ }
+
+ /**
+ * Useful method, but not in the spec.
+ * Dispatches the next AWT event in the queue.
+ * Returns whether a selector or an event was executed:
+ * if the event queue is empty, returns false.
+ */
+ public boolean dispatchNextEvent()
+ {
+ // check for empty queue to avoid blocking
+ if ( peekEvent() == null )
+ {
+ return false;
+ }
+
+ // queue not empty: dispatch the next event
+ try
+ {
+ dispatchEvent( getNextEvent() );
+ }
+ catch ( InterruptedException exc )
+ {
+ System.out.println( "NSRunLoop: error while dispatching event: " );
+ exc.printStackTrace();
+ }
+ return true;
+ }
+
+ /**
+ * Useful method, but not in the spec.
+ * Dispatches all events in the queue before returning.
+ */
+ public void dispatchAllEvents()
+ {
+ while ( dispatchNextEvent() );
+ }
+
+ /**
+ * Remove an event from the EventQueue and return it.
+ * This override will dispatch all selectors up to 5000,
+ * and then check if there are AWT events on the queue.
+ * If the queue is empty, all remaining selectors
+ * are dispatched. Then, this method calls the
+ * super class' implementation.
+ * @return the next AWTEvent
+ * @exception InterruptedException
+ * if another thread has interrupted this thread.
+ */
+ public AWTEvent getNextEvent() throws InterruptedException
+ {
+ //NOTE: it's currently unclear to me whether we should
+ // be operating as a run loop or as a priority queue.
+ // I'm opting for priority queue now, but that means that
+ // selectors that requeue themselves could hang the application.
+ // In the future, we could fake a run loop by putting a marker
+ // event on the AWT queue to mark the boundary between loops.
+
+ AWTEvent result;
+
+ while ( true )
+ {
+ //NOTE: as of java 1.4, we have to flush pending events
+ // using this cheesy undocumented method on suntoolkit.
+ // Unsurprisingly, java.awt.EventQueue got worse, not better.
+ // See notes above about our use of an NSSelector.
+ try
+ {
+ flushPendingEvents.invoke( toolkit );
+ }
+ catch ( Throwable t )
+ {
+ System.out.println( "NSRunLoop.getNextEvent: " + Thread.currentThread() );
+ System.err.println( "Unexpected error while flushing pending events: " );
+ t.printStackTrace();
+ };
+
+ synchronized( this )
+ {
+ result = popNextEarlyEvent();
+ if ( result != null )
+ {
+//System.out.println( "getNextEvent: early : " + result );
+ return result;
+ }
+ }
+
+ if ( ( result = peekEvent() ) != null )
+ {
+//System.out.println( "getNextEvent: AWT : " + result );
+ return super.getNextEvent();
+ }
+
+ synchronized( this )
+ {
+ result = popNextLateEvent();
+ if ( result != null )
+ {
+//System.out.println( "getNextEvent: late : " + result );
+ return result;
+ }
+
+ // yield
+//System.out.println( "getNextEvent: wait" );
+ wait();
+//System.out.println( "getNextEvent: notified" );
+ }
+ }
+ }
+
+ private AWTEvent popNextEarlyEvent()
+ {
+ if ( earlyQueue == null ) return null; // shouldn't be necessary, but is
+ if ( earlyQueue.isEmpty() ) return null;
+ return (AWTEvent) earlyQueue.removeFirst();
+ }
+
+ private AWTEvent popNextLateEvent()
+ {
+ if ( lateQueue == null ) return null; // shouldn't be necessary, but is
+ if ( lateQueue.isEmpty() ) return null;
+ return (AWTEvent) lateQueue.removeFirst();
+ }
+
+ /**
+ * This implementation calls super and then throws an
+ * UnsupportedOperationException. Catch that exception
+ * and ignore it if you know what you are doing.
+ */
+ public synchronized void push(EventQueue newEventQueue)
+ {
+ super.push( newEventQueue );
+ throw new UnsupportedOperationException(
+ "NSRunLoop may not function properly with push()" );
+ }
+
+ /**
+ * This implementation calls super and then throws an
+ * UnsupportedOperationException. Catch that exception
+ * and ignore it if you know what you are doing.
+ */
+ protected void pop() throws EmptyStackException
+ {
+ super.pop();
+ throw new UnsupportedOperationException(
+ "NSRunLoop may not function properly with pop()" );
+ }
+
+ /**
+ * Schedules the specified selector with the specified target and parameter
+ * to be invoked on the next event loop with the specified ordering.
+ * The selector must be able to be invoked on the target and the target method
+ * must accept the parameter. aModeList is currently ignored.
+ */
+ public void performSelectorWithOrder(
+ NSSelector aSelector, Object aTarget, Object aParameter, int anOrdering, List aModeList )
+ {
+ postEvent( new OrderedInvocationEvent( aSelector, aTarget, aParameter, anOrdering, aModeList ) );
+ }
+
+ /**
+ * Cancels the next scheduled invocation of the specified selector, target, and parameter.
+ * If no such invocation is scheduled, does nothing.
+ */
+ public synchronized void cancelPerformSelectorWithOrder(
+ NSSelector aSelector, Object aTarget, Object aParameter )
+ {
+ ListIterator i;
+ i = earlyQueue.listIterator();
+ while ( i.hasNext() )
+ {
+ if ( ((OrderedInvocationEvent)i.next()).compareTo(
+ aSelector, aTarget, aParameter ) )
+ {
+ i.remove();
+ return;
+ }
+ }
+ i = lateQueue.listIterator();
+ while ( i.hasNext() )
+ {
+ if ( ((OrderedInvocationEvent)i.next()).compareTo(
+ aSelector, aTarget, aParameter ) )
+ {
+ i.remove();
+ return;
+ }
+ }
+ }
+
+ /**
+ * Causes runnable to have its run() method on the next
+ * event loop with the specified priority ordering.
+ */
+ public static void invokeLaterWithOrder(Runnable aRunnable, int anOrdering) {
+ currentRunLoop().postEvent(
+ new OrderedInvocationEvent( Toolkit.getDefaultToolkit(), aRunnable, anOrdering ) );
+ }
+
+ /**
+ * An invocation event that can specify a priority for execution.
+ * The prioritization only works if the current event queue is an
+ * NSRunLoop; otherwise, performs as a normal invocation event.
+ */
+ private static class OrderedInvocationEvent extends InvocationEvent
+ {
+ int ordering;
+ NSSelector selector = null;
+ Object target = null;
+ Object parameter = null;
+
+ /**
+ * Constructs an InvocationEvent with the specified source which will
+ * execute the runnable's run() method when dispatched at the specified ordering.
+ */
+ public OrderedInvocationEvent(Object source,
+ Runnable runnable, int anOrdering)
+ {
+ super( source, runnable );
+ ordering = anOrdering;
+ }
+
+ /**
+ * Constructs an InvocationEvent with the specified source which will
+ * execute the runnable's run() method when dispatched at the specified ordering.
+ * If notifier is non-null, notifyAll() will be called on it immediately after run() returns.
+ */
+ public OrderedInvocationEvent(Object source,
+ Runnable runnable,
+ Object notifier,
+ boolean catchExceptions, int anOrdering)
+ {
+ super( source, runnable, notifier, catchExceptions );
+ ordering = anOrdering;
+ }
+
+ OrderedInvocationEvent(
+ final NSSelector aSelector,
+ final Object aTarget,
+ final Object aParameter,
+ int anOrdering, List aModeList)
+ {
+ this( Toolkit.getDefaultToolkit(), new Runnable()
+ {
+ public void run()
+ {
+ try
+ {
+ aSelector.invoke( aTarget, aParameter );
+ }
+ catch ( Exception exc )
+ {
+ System.out.println( "NSRunLoop: error invoking selector: " );
+ exc.printStackTrace();
+ }
+ }
+ }, anOrdering );
+
+ selector = aSelector;
+ target = aTarget;
+ parameter = aParameter;
+ }
+
+ /**
+ * Called by cancelPerformSelectorWithOrder.
+ * Compares against the specified arguments.
+ */
+ boolean compareTo( NSSelector aSelector, Object aTarget, Object aParameter )
+ {
+ return (
+ compareByValue( selector, aSelector ) &&
+ compareByValue( target, aTarget ) &&
+ compareByValue( parameter, aParameter ) );
+ }
+
+ private boolean compareByValue( Object first, Object second )
+ {
+ if ( first == second ) return true;
+ if ( first == null ) return second.equals( first );
+ return first.equals( second );
+
+ }
+
+ /**
+ * Returns the ordering for this event in the run loop.
+ */
+ public int getOrdering()
+ {
+ return ordering;
+ }
+
+ }
+
+}
+
+/*
+ * $Log$
+ * Revision 1.2 2006/02/16 13:15:00 cgruber
+ * Check in all sources in eclipse-friendly maven-enabled packages.
+ *
+ * Revision 1.13 2003/06/06 20:48:19 mpowers
+ * Fixed race condition when run loop is started from main thread.
+ * That was causing the dispatch thread to call getNextEvent before the
+ * static fields had been initialized.
+ *
+ * Revision 1.12 2003/06/03 14:52:11 mpowers
+ * Super constructor was calling getNextEvent before selector was created.
+ *
+ * Revision 1.10 2002/05/28 21:59:19 mpowers
+ * We now can compile against 1.3 and 1.4, as well as run against both too.
+ *
+ * Revision 1.9 2002/04/09 18:10:45 mpowers
+ * Fixes for 1.4. Commented out until we start building on 1.4.
+ *
+ * Revision 1.8 2002/02/13 21:20:15 mpowers
+ * Updated comments.
+ *
+ * Revision 1.7 2001/11/01 15:48:49 mpowers
+ * Additional debug code.
+ *
+ * Revision 1.6 2001/10/30 22:14:35 mpowers
+ * Constructor is now protected, not private.
+ *
+ * Revision 1.5 2001/10/29 20:41:49 mpowers
+ * Improved docs, better support for potential subclassing, invokeLater.
+ *
+ * Revision 1.4 2001/10/26 18:46:30 mpowers
+ * Now running AWT events with the appropriate ordering.
+ * Added invokeLaterWithOrder for java compatibility.
+ *
+ * Revision 1.3 2001/10/26 14:39:46 mpowers
+ * Completed implementation.
+ *
+ * Revision 1.2 2001/10/25 22:20:21 mpowers
+ * Got to check in an interim version - this will briefly break the build.
+ *
+ * Revision 1.1 2001/10/24 19:30:38 mpowers
+ * Initial check-in: incomplete implementation.
+ *
+ *
+ */
+
diff --git a/projects/net.wotonomy.foundation/src/main/java/net/wotonomy/foundation/NSSelector.java b/projects/net.wotonomy.foundation/src/main/java/net/wotonomy/foundation/NSSelector.java
new file mode 100644
index 0000000..965606b
--- /dev/null
+++ b/projects/net.wotonomy.foundation/src/main/java/net/wotonomy/foundation/NSSelector.java
@@ -0,0 +1,391 @@
+/*
+Wotonomy: OpenStep design patterns for pure Java applications.
+Copyright (C) 2000 Blacksmith, Inc.
+
+This library is free software; you can redistribute it and/or
+modify it under the terms of the GNU Lesser General Public
+License as published by the Free Software Foundation; either
+version 2.1 of the License, or (at your option) any later version.
+
+This library is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+Lesser General Public License for more details.
+
+You should have received a copy of the GNU Lesser General Public
+License along with this library; if not, see http://www.gnu.org
+*/
+
+package net.wotonomy.foundation;
+
+import java.io.Serializable;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.util.Comparator;
+
+import net.wotonomy.foundation.internal.PropertyComparator;
+
+/**
+* A pure java implementation of NSSelector.
+*
+* @author michael@mpowers.net
+* @author $Author: cgruber $
+* @version $Revision: 892 $
+*/
+public class NSSelector implements Comparator, Serializable
+{
+ protected NSMutableDictionary methodMap; // map of classes to methods
+ protected String methodName;
+ protected Class[] parameterTypes;
+
+ /**
+ * A marker to indicate object not found.
+ */
+ protected static final String NOT_FOUND = "NOT_FOUND";
+
+ /**
+ * Saves creating a new class array for parameterless method invocation.
+ */
+ protected static final Class[] EMPTY_CLASS_ARRAY = new Class[0];
+
+ /**
+ * Saves creating a new object array for parameterless method invocation.
+ */
+ protected static final Object[] EMPTY_OBJECT_ARRAY = new Object[0];
+
+ /**
+ * Constructor specifying a method name and an array of parameter types.
+ */
+ public NSSelector (String aMethodName, Class[] aParameterTypeArray)
+ {
+ methodName = aMethodName;
+ parameterTypes = aParameterTypeArray;
+ methodMap = new NSMutableDictionary();
+ }
+
+ /**
+ * Constructor specifying a method name with no parameters.
+ */
+ public NSSelector (String aMethodName)
+ {
+ this( aMethodName, EMPTY_CLASS_ARRAY );
+ }
+
+ /**
+ * Constructor for custom subclasses that implement specific operators
+ * and that do not use dynamic method invocation.
+ */
+ protected NSSelector()
+ {
+ }
+
+ /**
+ * Returns the name of the method.
+ */
+ public String name ()
+ {
+ return methodName;
+ }
+
+ /**
+ * Returns the array of parameter types.
+ */
+ public Class[] parameterTypes ()
+ {
+ return parameterTypes;
+ }
+
+ /**
+ * A String description of this selector.
+ */
+ public String toString ()
+ {
+ StringBuffer result = new StringBuffer();
+ result.append( "[" + getClass().getName() + ": name = " + name() + ", parameter types = [" );
+ if ( parameterTypes != null )
+ {
+ if ( parameterTypes.length > 0 )
+ {
+ result.append( parameterTypes[0].toString() );
+ }
+ for ( int i = 1; i < parameterTypes.length; i++ )
+ {
+ result.append( ", " );
+ result.append( parameterTypes[i].toString() );
+ }
+ }
+ result.append( "] ]" );
+ return result.toString();
+ }
+
+ /**
+ * Returns the appropriate method for the specified class.
+ */
+ public Method methodOnClass (Class aClass)
+ throws NoSuchMethodException
+ {
+ Object result = methodMap.objectForKey( aClass );
+
+ if ( result == null )
+ {
+ result = getMethodForClass( aClass );
+ if ( result == null )
+ {
+ result = NOT_FOUND;
+ }
+ methodMap.setObjectForKey( result, aClass );
+ }
+
+ if ( result == NOT_FOUND )
+ {
+ throw new NoSuchMethodException();
+ }
+ return (Method) result;
+ }
+
+ /**
+ * Returns the appropriate method, or null if not found.
+ */
+ private Method getMethodForClass( Class aClass )
+ {
+ Method[] methods = aClass.getMethods();
+ for ( int i = 0; i < methods.length; i++ )
+ {
+ if ( methods[i].getName().equals( name() ) )
+ {
+ Class[] params = methods[i].getParameterTypes();
+ if ( params.length == parameterTypes.length )
+ {
+ boolean pass = true;
+ for ( int j = 0; j < params.length; j++ )
+ {
+ if ( ! params[j].isAssignableFrom( parameterTypes[j] ) )
+ {
+ pass = false;
+ }
+ }
+ if ( pass ) return methods[i];
+ }
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Convenience to get a method for an object.
+ */
+ public Method methodOnObject (Object anObject)
+ throws NoSuchMethodException
+ {
+ Method m = methodOnClass( anObject.getClass() );
+ if ( m == null ) throw new NoSuchMethodException( name() );
+ return m;
+ }
+
+ /**
+ * Returns whether the class implements the method for this selector.
+ */
+ public boolean implementedByClass (Class aClass)
+ {
+ try
+ {
+ methodOnClass( aClass );
+ return true;
+ }
+ catch ( NoSuchMethodException exc )
+ {
+ }
+ return false;
+ }
+
+ /**
+ * Returns whether the object's class implements the method
+ * for this selector.
+ */
+ public boolean implementedByObject (Object anObject)
+ {
+ try
+ {
+ methodOnObject( anObject );
+ return true;
+ }
+ catch ( NoSuchMethodException exc )
+ {
+ }
+ return false;
+ }
+
+ /**
+ * Invokes this selector's method on the specified object
+ * using the specified parameters.
+ */
+ public Object invoke (Object anObject, Object[] parameters)
+ throws IllegalAccessException, IllegalArgumentException,
+ InvocationTargetException, NoSuchMethodException
+ {
+ return methodOnObject( anObject ).invoke( anObject, parameters );
+ }
+
+ /**
+ * Invokes this selector's method on the specified object
+ * with no parameters.
+ */
+ public Object invoke (Object anObject)
+ throws IllegalAccessException, IllegalArgumentException,
+ InvocationTargetException, NoSuchMethodException
+ {
+ return invoke( anObject, EMPTY_OBJECT_ARRAY );
+ }
+
+ /**
+ * Invokes this selector's method on the specified object
+ * with the specified parameter.
+ */
+ public Object invoke (Object anObject, Object aParameter)
+ throws IllegalAccessException, IllegalArgumentException,
+ InvocationTargetException, NoSuchMethodException
+ {
+ return invoke( anObject, new Object[] { aParameter } );
+ }
+
+ /**
+ * Invokes this selector's method on the specified object
+ * using the specified two parameters.
+ */
+ public Object invoke (Object anObject, Object p1, Object p2)
+ throws IllegalAccessException, IllegalArgumentException,
+ InvocationTargetException, NoSuchMethodException
+ {
+ return invoke( anObject, new Object[] { p1, p2 } );
+ }
+
+ /**
+ * Invokes the method with the specified signature on the specified
+ * object using the specified parameters.
+ */
+ public static Object invoke
+ (String methodName, Class[] parameterTypes, Object anObject, Object[] parameters)
+ throws IllegalAccessException, IllegalArgumentException,
+ InvocationTargetException, NoSuchMethodException
+ {
+ return new NSSelector( methodName, parameterTypes ).invoke( anObject, parameters );
+ }
+
+ /**
+ * Invokes the method with the specified signature on the specified object
+ * with no parameters.
+ */
+ public static Object invoke
+ (String methodName, Object anObject)
+ throws IllegalAccessException, IllegalArgumentException,
+ InvocationTargetException, NoSuchMethodException
+ {
+ return NSSelector.invoke(
+ methodName, EMPTY_CLASS_ARRAY, anObject, EMPTY_OBJECT_ARRAY );
+ }
+
+ /**
+ * Invokes the method with the specified signature on the specified
+ * object using the specified parameter.
+ */
+ public static Object invoke
+ (String methodName, Class[] parameterTypes,
+ Object anObject, Object aParameter)
+ throws IllegalAccessException, IllegalArgumentException,
+ InvocationTargetException, NoSuchMethodException
+ {
+ return NSSelector.invoke(
+ methodName, parameterTypes, anObject, new Object[] { aParameter } );
+ }
+
+ /**
+ * Invokes the method with the specified signature on the specified
+ * object using the specified two parameters.
+ */
+ public static Object invoke
+ (String methodName, Class[] parameterTypes,
+ Object anObject, Object p1, Object p2)
+ throws IllegalAccessException, IllegalArgumentException,
+ InvocationTargetException, NoSuchMethodException
+ {
+ return NSSelector.invoke(
+ methodName, parameterTypes, anObject, new Object[] { p1, p2 } );
+ }
+
+ // interface Comparator
+
+ private Comparator comparator;
+
+ /**
+ * Constructor specifying a method name and a comparator.
+ * This is not in the spec.
+ */
+ public NSSelector (String aMethodName, Comparator aComparator)
+ {
+ this( aMethodName, EMPTY_CLASS_ARRAY );
+ comparator = aComparator;
+ }
+
+ /**
+ * Returns the Comparator used for this selector.
+ * This is not in the spec.
+ */
+ public Comparator comparator()
+ {
+ if ( comparator == null )
+ {
+ comparator = new PropertyComparator( methodName );
+ }
+ return comparator;
+ }
+
+ public int compare(Object o1, Object o2)
+ {
+ if ( comparator == null )
+ {
+ comparator = new PropertyComparator( methodName );
+ }
+ return comparator.compare( o1, o2 );
+ }
+
+ public boolean equals(Object obj)
+ {
+ return ( obj == this );
+ }
+}
+
+/*
+ * $Log$
+ * Revision 1.1 2006/02/16 12:47:16 cgruber
+ * Check in all sources in eclipse-friendly maven-enabled packages.
+ *
+ * Revision 1.9 2003/02/12 19:34:35 mpowers
+ * Added accessor for comparator.
+ *
+ * Revision 1.8 2003/02/07 20:23:41 mpowers
+ * Provided backwards compatibility for comparators.
+ *
+ * Revision 1.7 2003/01/22 23:02:25 mpowers
+ * Fixed a null pointer error in NSSelector.toString.
+ *
+ * Revision 1.6 2003/01/18 23:46:58 mpowers
+ * EOSortOrdering is now correctly using NSSelectors.
+ *
+ * Revision 1.4 2001/10/31 15:24:45 mpowers
+ * Implicit constructor is now protected.
+ *
+ * Revision 1.3 2001/02/07 19:25:51 mpowers
+ * Fixed: method matching uses isAssignableFrom rather than ==.
+ *
+ * Revision 1.2 2001/01/08 23:30:16 mpowers
+ * Fixed major bug - selectors were not supposed to share a method map.
+ *
+ * Revision 1.1.1.1 2000/12/21 15:47:45 mpowers
+ * Contributing wotonomy.
+ *
+ * Revision 1.3 2000/12/20 16:25:39 michael
+ * Added log to all files.
+ *
+ *
+ */
+
diff --git a/projects/net.wotonomy.foundation/src/main/java/net/wotonomy/foundation/NSSet.java b/projects/net.wotonomy.foundation/src/main/java/net/wotonomy/foundation/NSSet.java
new file mode 100644
index 0000000..5318fe8
--- /dev/null
+++ b/projects/net.wotonomy.foundation/src/main/java/net/wotonomy/foundation/NSSet.java
@@ -0,0 +1,212 @@
+/*
+Wotonomy: OpenStep design patterns for pure Java applications.
+Copyright (C) 2000 Blacksmith, Inc.
+
+This library is free software; you can redistribute it and/or
+modify it under the terms of the GNU Lesser General Public
+License as published by the Free Software Foundation; either
+version 2.1 of the License, or (at your option) any later version.
+
+This library is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+Lesser General Public License for more details.
+
+You should have received a copy of the GNU Lesser General Public
+License along with this library; if not, see http://www.gnu.org
+*/
+
+package net.wotonomy.foundation;
+
+import java.util.Collection;
+import java.util.Enumeration;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.Set;
+import java.util.Vector;
+
+/**
+* A pure java implementation of NSSet that
+* implements Set for greater java interoperability.
+*
+* @author michael@mpowers.net
+* @author $Author: cgruber $
+* @version $Revision: 893 $
+*/
+public class NSSet extends HashSet
+{
+ /**
+ * Default constructor.
+ */
+ public NSSet ()
+ {
+ super();
+ }
+
+ /**
+ * Constructs a NSSet containing the objects
+ * in the specified collection.
+ */
+ public NSSet ( Collection aCollection )
+ {
+ super( aCollection );
+ }
+
+ /**
+ * Constructs a NSSet containing only
+ * the specified object.
+ */
+ public NSSet ( Object anObject )
+ {
+ super();
+ add( anObject );
+ }
+
+ /**
+ * Constructs a NSSet containing the objects
+ * in the specified array.
+ */
+ public NSSet ( Object[] anObjectArray )
+ {
+ super();
+ for ( int i = 0; i < anObjectArray.length; i++ )
+ {
+ add( anObjectArray[i] );
+ }
+ }
+
+ /**
+ * Returns an NSArray containing all objects in the set.
+ */
+ public NSArray allObjects ()
+ {
+ return new NSArray( this );
+ }
+
+ /**
+ *
+ */
+ public Object anyObject ()
+ {
+ throw new RuntimeException( "Not implemented yet." );
+ }
+
+ /**
+ * Returns whether this set contains the
+ * specified object.
+ */
+ public boolean containsObject ( Object anObject )
+ {
+ return contains( anObject );
+ }
+
+ /**
+ * Returns the number of elements in this set.
+ */
+ public int count ()
+ {
+ return size();
+ }
+
+ /**
+ * Returns whether this set has one or more
+ * elements in common with the specified set.
+ */
+ public boolean intersectsSet ( Set aSet )
+ {
+ Iterator it = aSet.iterator();
+ while ( it.hasNext() )
+ {
+ if ( this.containsObject( it.next() ) )
+ {
+ return true;
+ }
+ }
+ return false;
+
+ }
+
+ /**
+ * Returns whether this set contains the
+ * same object as the specified set.
+ */
+ public boolean isEqualToSet ( Set aSet )
+ {
+ return equals( aSet );
+ }
+
+ /**
+ * Returns whether this set is a subset
+ * of the specified set.
+ */
+ public boolean isSubsetOfSet ( Set aSet )
+ {
+ return aSet.containsAll( this );
+ }
+
+ /**
+ *
+ */
+ public Object member ( Object anObject )
+ {
+ throw new RuntimeException( "Not implemented yet." );
+ }
+
+ /**
+ * Returns an enumerator over the objects
+ * in this set.
+ */
+ public Enumeration objectEnumerator ()
+ {
+ return new Vector( this ).elements();
+ }
+
+ /**
+ * Returns a set that is the intersection
+ * of this set and the specified set.
+ */
+ public NSSet setByIntersectingSet ( Set aSet )
+ {
+ NSSet result = new NSSet( this );
+ result.retainAll( aSet );
+ return result;
+ }
+
+ /**
+ * Returns a set that contains all elements
+ * in this set that are not in the specified set.
+ */
+ public NSSet setBySubtractingSet ( Set aSet )
+ {
+ NSSet result = new NSSet( this );
+ result.removeAll( aSet );
+ return result;
+ }
+
+ /**
+ * Returns a set that is the union
+ * of this set and the specified set.
+ */
+ public NSSet setByUnioningSet ( Set aSet )
+ {
+ NSSet result = new NSSet( this );
+ result.addAll( aSet );
+ return result;
+ }
+
+}
+
+/*
+ * $Log$
+ * Revision 1.2 2006/02/16 13:15:00 cgruber
+ * Check in all sources in eclipse-friendly maven-enabled packages.
+ *
+ * Revision 1.1.1.1 2000/12/21 15:47:45 mpowers
+ * Contributing wotonomy.
+ *
+ * Revision 1.3 2000/12/20 16:25:39 michael
+ * Added log to all files.
+ *
+ *
+ */
+
diff --git a/projects/net.wotonomy.foundation/src/main/java/net/wotonomy/foundation/NSTimeZone.java b/projects/net.wotonomy.foundation/src/main/java/net/wotonomy/foundation/NSTimeZone.java
new file mode 100644
index 0000000..171e756
--- /dev/null
+++ b/projects/net.wotonomy.foundation/src/main/java/net/wotonomy/foundation/NSTimeZone.java
@@ -0,0 +1,272 @@
+/*
+Wotonomy: OpenStep design patterns for pure Java applications.
+Copyright (C) 2002 Israfil consulting Services Corporation
+
+This library is free software; you can redistribute it and/or
+modify it under the terms of the GNU Lesser General Public
+License as published by the Free Software Foundation; either
+version 2.1 of the License, or (at your option) any later version.
+
+This library is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+Lesser General Public License for more details.
+
+You should have received a copy of the GNU Lesser General Public
+License along with this library; if not, see http://www.gnu.org
+
+$Id: NSTimeZone.java 892 2006-02-16 12:47:16Z cgruber $
+
+*/
+
+package net.wotonomy.foundation;
+
+import java.io.ObjectStreamException;
+import java.io.Serializable;
+import java.util.Date;
+import java.util.Locale;
+import java.util.TimeZone;
+/**
+* A channel to the database, representing a communication
+* stream within a context of an adaptor.
+*
+* @author cgruber@israfil.net
+* @author $Author: cgruber $
+* @version $Revision: 892 $
+*/
+
+public class NSTimeZone extends TimeZone
+ implements Cloneable, Serializable, NSCoding {
+ protected static class __NSTZPeriodComparator extends NSComparator {
+
+ protected boolean _ascending = false;
+
+ public int compare(Object obj, Object obj1) throws NSComparator.ComparisonException {
+ throw new UnsupportedOperationException("Not Yet Implemented");
+ }
+
+ public __NSTZPeriodComparator() {
+ throw new UnsupportedOperationException("Not Yet Implemented");
+ }
+
+ public __NSTZPeriodComparator(boolean flag) {
+ throw new UnsupportedOperationException("Not Yet Implemented");
+ }
+ }
+
+ protected static class __NSTZPeriod {
+
+ protected String _abbreviation = null;
+ protected int _isdst = 0;
+ protected int _offset = 0;
+ protected double _startTime = 0;
+
+ protected boolean before(__NSTZPeriod _p_nstzperiod) {
+ throw new UnsupportedOperationException("Not Yet Implemented");
+ }
+
+ protected boolean equals(__NSTZPeriod _p_nstzperiod) {
+ throw new UnsupportedOperationException("Not Yet Implemented");
+ }
+
+ protected __NSTZPeriod() {
+ throw new UnsupportedOperationException("Not Yet Implemented");
+ }
+ }
+
+
+ public static final String SystemTimeZoneDidChangeNotification = "NSSystemTimeZoneDidChangeNotification";
+ protected NSData _data = null;
+ protected transient int _hashCode = 0;
+ protected transient boolean _initialized = false;
+ protected transient TimeZone _jdkTimeZone = null;
+ protected String _name = null;
+ protected transient int _rawOffset = 0;
+ protected transient NSMutableArray _timeZonePeriods = null;
+ protected transient int _timeZonePeriodsCount = 0;
+ protected transient boolean _useDaylightTime = false;
+
+ public NSTimeZone() {
+ throw new UnsupportedOperationException("Not Yet Implemented");
+ }
+
+ protected NSTimeZone(String s, NSData nsdata) {
+ throw new UnsupportedOperationException("Not Yet Implemented");
+ }
+
+ public static NSDictionary abbreviationDictionary() {
+ throw new UnsupportedOperationException("Not Yet Implemented");
+ }
+
+ public Class classForCoder() {
+ return getClass();
+ }
+
+ public Object clone() {
+ throw new UnsupportedOperationException("Not Yet Implemented");
+ }
+
+ public static Object decodeObject(NSCoder nscoder) {
+ throw new UnsupportedOperationException("Not Yet Implemented");
+ }
+
+ public void encodeWithCoder(NSCoder nscoder) {
+ throw new UnsupportedOperationException("Not Yet Implemented");
+ }
+
+ public static synchronized NSTimeZone defaultTimeZone() {
+ throw new UnsupportedOperationException("Not Yet Implemented");
+ }
+
+ public static String[] getAvailableIDs() {
+ throw new UnsupportedOperationException("Not Yet Implemented");
+ }
+
+ public static TimeZone getDefault() {
+ throw new UnsupportedOperationException("Not Yet Implemented");
+ }
+
+ public static NSArray knownTimeZoneNames() {
+ throw new UnsupportedOperationException("Not Yet Implemented");
+ }
+
+ public static NSTimeZone localTimeZone() {
+ throw new UnsupportedOperationException("Not Yet Implemented");
+ }
+
+ public static synchronized void resetSystemTimeZone() {
+ throw new UnsupportedOperationException("Not Yet Implemented");
+ }
+
+ public static synchronized void setDefault(TimeZone timezone) {
+ throw new UnsupportedOperationException("Not Yet Implemented");
+ }
+
+ public static synchronized void setDefaultTimeZone(NSTimeZone nstimezone) {
+ throw new UnsupportedOperationException("Not Yet Implemented");
+ }
+
+ public void setID(String s) {
+ throw new IllegalStateException(getClass().getName() + " is immutable.");
+ }
+
+ public void setRawOffset(int i) {
+ throw new IllegalStateException(getClass().getName() + " is immutable.");
+ }
+
+ public static synchronized NSTimeZone systemTimeZone() {
+ throw new UnsupportedOperationException("Not Yet Implemented");
+ }
+
+ public static synchronized NSTimeZone timeZoneForSecondsFromGMT(int i) {
+ throw new UnsupportedOperationException("Not Yet Implemented");
+ }
+
+ public static synchronized NSTimeZone timeZoneWithName(String s, boolean flag) {
+ throw new UnsupportedOperationException("Not Yet Implemented");
+ }
+
+ public static synchronized NSTimeZone timeZoneWithNameAndData(String s, NSData nsdata) {
+ throw new UnsupportedOperationException("Not Yet Implemented");
+ }
+
+ public static NSTimeZone _nstimeZoneWithTimeZone(TimeZone timezone) {
+ throw new UnsupportedOperationException("Not Yet Implemented");
+ }
+
+ public String abbreviation() {
+ throw new UnsupportedOperationException("Not Yet Implemented");
+ }
+
+ public String abbreviationForTimestamp(NSTimestamp nstimestamp) {
+ throw new UnsupportedOperationException("Not Yet Implemented");
+ }
+
+ public NSData data() {
+ throw new UnsupportedOperationException("Not Yet Implemented");
+ }
+
+ public boolean equals(Object obj) {
+ throw new UnsupportedOperationException("Not Yet Implemented");
+ }
+
+ public String getDisplayName(boolean flag, int i, Locale locale) {
+ throw new UnsupportedOperationException("Not Yet Implemented");
+ }
+
+ public String getID() {
+ throw new UnsupportedOperationException("Not Yet Implemented");
+ }
+
+ public int getOffset(int i, int j, int k, int l, int i1, int j1) {
+ throw new UnsupportedOperationException("Not Yet Implemented");
+ }
+
+ public int getRawOffset() {
+ throw new UnsupportedOperationException("Not Yet Implemented");
+ }
+
+ public synchronized int hashCode() {
+ throw new UnsupportedOperationException("Not Yet Implemented");
+ }
+
+ public boolean hasSameRules(TimeZone timezone) {
+ throw new UnsupportedOperationException("Not Yet Implemented");
+ }
+
+ public boolean inDaylightTime(Date date) {
+ throw new UnsupportedOperationException("Not Yet Implemented");
+ }
+
+ public boolean isDaylightSavingTime() {
+ throw new UnsupportedOperationException("Not Yet Implemented");
+ }
+
+ public boolean isDaylightSavingTimeForTimestamp(NSTimestamp nstimestamp) {
+ throw new UnsupportedOperationException("Not Yet Implemented");
+ }
+
+ public boolean isEqualToTimeZone(NSTimeZone nstimezone) {
+ throw new UnsupportedOperationException("Not Yet Implemented");
+ }
+
+ public String name() {
+ throw new UnsupportedOperationException("Not Yet Implemented");
+ }
+
+ public int secondsFromGMT() {
+ throw new UnsupportedOperationException("Not Yet Implemented");
+ }
+
+ public int secondsFromGMTForTimestamp(NSTimestamp nstimestamp) {
+ throw new UnsupportedOperationException("Not Yet Implemented");
+ }
+
+ public String toString() {
+ throw new UnsupportedOperationException("Not Yet Implemented");
+ }
+
+ public boolean useDaylightTime() {
+ throw new UnsupportedOperationException("Not Yet Implemented");
+ }
+
+ protected Object readResolve() throws ObjectStreamException {
+ throw new UnsupportedOperationException("Not Yet Implemented");
+ }
+
+}
+/*
+ * $Log$
+ * Revision 1.1 2006/02/16 12:47:16 cgruber
+ * Check in all sources in eclipse-friendly maven-enabled packages.
+ *
+ * Revision 1.2 2003/08/06 23:07:52 chochos
+ * general code cleanup (mostly, removing unused imports)
+ *
+ * Revision 1.1 2002/07/14 21:56:16 mpowers
+ * Contributions from cgruber.
+ *
+ * Revision 1.1 2002/06/25 07:52:57 cgruber
+ * Add quite a few abstract classes, interfaces, and classes. All API consistent with WebObjects, but with no implementation, nor any private or package access members from the original.
+ *
+ */
diff --git a/projects/net.wotonomy.foundation/src/main/java/net/wotonomy/foundation/NSTimestamp.java b/projects/net.wotonomy.foundation/src/main/java/net/wotonomy/foundation/NSTimestamp.java
new file mode 100644
index 0000000..bd246e0
--- /dev/null
+++ b/projects/net.wotonomy.foundation/src/main/java/net/wotonomy/foundation/NSTimestamp.java
@@ -0,0 +1,285 @@
+/*
+Wotonomy: OpenStep design patterns for pure Java applications.
+Copyright (C) 2002 Israfil consulting Services Corporation
+
+This library is free software; you can redistribute it and/or
+modify it under the terms of the GNU Lesser General Public
+License as published by the Free Software Foundation; either
+version 2.1 of the License, or (at your option) any later version.
+
+This library is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+Lesser General Public License for more details.
+
+You should have received a copy of the GNU Lesser General Public
+License along with this library; if not, see http://www.gnu.org
+
+$Id: NSTimestamp.java 892 2006-02-16 12:47:16Z cgruber $
+
+*/
+
+package net.wotonomy.foundation;
+
+import java.sql.Timestamp;
+import java.util.Date;
+import java.util.TimeZone;
+
+/**
+* A channel to the database, representing a communication
+* stream within a context of an adaptor.
+*
+* @author cgruber@israfil.net
+* @author $Author: cgruber $
+* @version $Revision: 892 $
+*/
+
+public class NSTimestamp extends Timestamp
+ implements NSCoding {
+
+ public static class IntRef {
+
+ public int value = 0;
+
+ public String toString() {
+ return getClass().getName() + " < value = " + value + " >";
+ }
+
+ public IntRef() {
+ }
+ }
+
+
+ public static final NSTimestamp DistantPast = new NSTimestamp(0xffffc77f2e9b6800L);
+ public static final NSTimestamp DistantFuture = new NSTimestamp(0x7fffffffffffffffL);
+ public static long currentTimeIntervalSinceReferenceDate() {
+ return System.currentTimeMillis() / 1000L;
+ }
+
+ public static NSTimestamp distantFuture() {
+ return DistantFuture;
+ }
+
+ public static NSTimestamp distantPast() {
+ return DistantPast;
+ }
+
+ public static long millisecondsToTimeInterval(long l) {
+ return l / 1000L;
+ }
+
+ public static long timeIntervalToMilliseconds(long l) {
+ return l * 1000L;
+ }
+
+ public Class classForCoder() {
+ return getClass();
+ }
+
+ public static Object decodeObject(NSCoder nscoder) {
+ throw new UnsupportedOperationException("Not Yet Implemented");
+ }
+
+ public void encodeWithCoder(NSCoder nscoder) {
+ throw new UnsupportedOperationException("Not Yet Implemented");
+ }
+
+ public NSTimestamp() {
+ super(0);
+ throw new UnsupportedOperationException("Not Yet Implemented");
+ }
+
+ public NSTimestamp(long l) {
+ super(l);
+ throw new UnsupportedOperationException("Not Yet Implemented");
+ }
+
+ public NSTimestamp(long l, int i) {
+ super(0);
+ throw new UnsupportedOperationException("Not Yet Implemented");
+ }
+
+ public NSTimestamp(long l, NSTimestamp nstimestamp) {
+ super(0);
+ throw new UnsupportedOperationException("Not Yet Implemented");
+ }
+
+ public NSTimestamp(long l, TimeZone timezone) {
+ super(0);
+ throw new UnsupportedOperationException("Not Yet Implemented");
+ }
+
+ public NSTimestamp(long l, int i, TimeZone timezone) {
+ super(0);
+ throw new UnsupportedOperationException("Not Yet Implemented");
+ }
+
+ public NSTimestamp(int i, int j, int k, int l, int i1, int j1, TimeZone timezone) {
+ super(0);
+ throw new UnsupportedOperationException("Not Yet Implemented");
+ }
+
+ public NSTimestamp(Date date) {
+ super(0);
+ throw new UnsupportedOperationException("Not Yet Implemented");
+ }
+
+ public NSTimestamp(Timestamp timestamp) {
+ super(0);
+ throw new UnsupportedOperationException("Not Yet Implemented");
+ }
+
+ public NSTimestamp timestampByAddingGregorianUnits(int i, int j, int k, int l, int i1, int j1) {
+ throw new UnsupportedOperationException("Not Yet Implemented");
+ }
+
+ public NSTimestamp timestampByAddingTimeInterval(long l) {
+ throw new UnsupportedOperationException("Not Yet Implemented");
+ }
+
+ public long dayOfCommonEra() {
+ throw new UnsupportedOperationException("Not Yet Implemented");
+ }
+
+ public int dayOfMonth() {
+ throw new UnsupportedOperationException("Not Yet Implemented");
+ }
+
+ public int dayOfWeek() {
+ throw new UnsupportedOperationException("Not Yet Implemented");
+ }
+
+ public int dayOfYear() {
+ throw new UnsupportedOperationException("Not Yet Implemented");
+ }
+
+ public int hourOfDay() {
+ throw new UnsupportedOperationException("Not Yet Implemented");
+ }
+
+ public int microsecondOfSecond() {
+ throw new UnsupportedOperationException("Not Yet Implemented");
+ }
+
+ public int minuteOfHour() {
+ throw new UnsupportedOperationException("Not Yet Implemented");
+ }
+
+ public int monthOfYear() {
+ throw new UnsupportedOperationException("Not Yet Implemented");
+ }
+
+ public int secondOfMinute() {
+ throw new UnsupportedOperationException("Not Yet Implemented");
+ }
+
+ public int yearOfCommonEra() {
+ throw new UnsupportedOperationException("Not Yet Implemented");
+ }
+
+ public void gregorianUnitsSinceTimestamp(IntRef intref, IntRef intref1, IntRef intref2, IntRef intref3, IntRef intref4, IntRef intref5, NSTimestamp nstimestamp) {
+ throw new UnsupportedOperationException("Not Yet Implemented");
+ }
+
+ public long timeIntervalSinceTimestamp(NSTimestamp nstimestamp) {
+ throw new UnsupportedOperationException("Not Yet Implemented");
+ }
+
+ public long timeIntervalSinceNow() {
+ throw new UnsupportedOperationException("Not Yet Implemented");
+ }
+
+ public long timeIntervalSinceReferenceDate() {
+ throw new UnsupportedOperationException("Not Yet Implemented");
+ }
+
+ public int compare(NSTimestamp nstimestamp) {
+ throw new UnsupportedOperationException("Not Yet Implemented");
+ }
+
+ public NSTimestamp earlierTimestamp(NSTimestamp nstimestamp) {
+ throw new UnsupportedOperationException("Not Yet Implemented");
+ }
+
+ public NSTimestamp laterTimestamp(NSTimestamp nstimestamp) {
+ throw new UnsupportedOperationException("Not Yet Implemented");
+ }
+
+ public String toString() {
+ throw new UnsupportedOperationException("Not Yet Implemented");
+ }
+
+ public NSTimeZone timeZone() {
+ throw new UnsupportedOperationException("Not Yet Implemented");
+ }
+
+ public long _getTimeInMillis() {
+ throw new UnsupportedOperationException("Not Yet Implemented");
+ }
+
+ public void setNanos(int i) {
+ throw new UnsupportedOperationException("Not Yet Implemented");
+ }
+
+ /** @deprecated This method deprecated in parent java.util.Date */
+ public void setDate(int i) {
+ throw new UnsupportedOperationException("Not Yet Implemented");
+ }
+
+ /** @deprecated This method deprecated in parent java.util.Date */
+ public void setHours(int i) {
+ throw new UnsupportedOperationException("Not Yet Implemented");
+ }
+
+ /** @deprecated This method deprecated in parent java.util.Date */
+ public void setMinutes(int i) {
+ throw new UnsupportedOperationException("Not Yet Implemented");
+ }
+
+ /** @deprecated This method deprecated in parent java.util.Date */
+ public void setMonth(int i) {
+ throw new UnsupportedOperationException("Not Yet Implemented");
+ }
+
+ /** @deprecated This method deprecated in parent java.util.Date */
+ public void setSeconds(int i) {
+ throw new UnsupportedOperationException("Not Yet Implemented");
+ }
+
+ public void setTime(long l) {
+ throw new UnsupportedOperationException("Not Yet Implemented");
+ }
+
+ public long getTime() {
+ throw new UnsupportedOperationException("Not Yet Implemented");
+ }
+
+ /** @deprecated This method deprecated in parent java.util.Date */
+ public void setYear(int i) {
+ throw new UnsupportedOperationException("Not Yet Implemented");
+ }
+}
+/*
+ * $Log$
+ * Revision 1.1 2006/02/16 12:47:16 cgruber
+ * Check in all sources in eclipse-friendly maven-enabled packages.
+ *
+ * Revision 1.2 2003/08/06 23:07:52 chochos
+ * general code cleanup (mostly, removing unused imports)
+ *
+ * Revision 1.1 2002/07/14 21:56:16 mpowers
+ * Contributions from cgruber.
+ *
+ * Revision 1.3 2002/06/25 19:06:13 cgruber
+ * Comment fix.
+ *
+ * Revision 1.2 2002/06/25 19:05:27 cgruber
+ * Add deprecation statements to remove warnings
+ * about java.util.Date's deprecated APIs.
+ *
+ * Revision 1.1 2002/06/25 07:52:56 cgruber
+ * Add quite a few abstract classes, interfaces, and classes. All
+ * API consistent with WebObjects, but with no implementation, nor
+ * any private or package access members from the original.
+ *
+ */
diff --git a/projects/net.wotonomy.foundation/src/main/java/net/wotonomy/foundation/NSTimestampFormatter.java b/projects/net.wotonomy.foundation/src/main/java/net/wotonomy/foundation/NSTimestampFormatter.java
new file mode 100644
index 0000000..ecc67ca
--- /dev/null
+++ b/projects/net.wotonomy.foundation/src/main/java/net/wotonomy/foundation/NSTimestampFormatter.java
@@ -0,0 +1,65 @@
+/*
+Wotonomy: OpenStep design patterns for pure Java applications.
+Copyright (C) 2003 Intersect Software Corporation
+
+This library is free software; you can redistribute it and/or
+modify it under the terms of the GNU Lesser General Public
+License as published by the Free Software Foundation; either
+version 2.1 of the License, or (at your option) any later version.
+
+This library is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+Lesser General Public License for more details.
+
+You should have received a copy of the GNU Lesser General Public
+License along with this library; if not, see http://www.gnu.org
+
+$Id: NSTimestampFormatter.java 893 2006-02-16 13:22:23Z cgruber $
+
+*/
+
+package net.wotonomy.foundation;
+
+import java.text.DateFormatSymbols;
+import java.text.SimpleDateFormat;
+
+/**
+* A Format that accepts C-style date formatting syntax.
+* Not currently implemented, included for compile compatibility.
+*
+* @author michael@mpowers.net
+* @author $Author: cgruber $
+* @version $Revision: 893 $
+*/
+
+public class NSTimestampFormatter extends SimpleDateFormat
+{
+ public NSTimestampFormatter()
+ {
+ super();
+ }
+
+ public NSTimestampFormatter(String aPattern)
+ {
+ super( aPattern );
+ }
+
+ public NSTimestampFormatter(String aPattern,
+ DateFormatSymbols symbols)
+ {
+ super( aPattern, symbols );
+ }
+
+}
+
+/*
+ * $Log$
+ * Revision 1.2 2006/02/16 13:15:00 cgruber
+ * Check in all sources in eclipse-friendly maven-enabled packages.
+ *
+ * Revision 1.1 2003/01/17 14:40:51 mpowers
+ * Adding files to fix build.
+ *
+ *
+ */
diff --git a/projects/net.wotonomy.foundation/src/main/java/net/wotonomy/foundation/internal/Duplicator.java b/projects/net.wotonomy.foundation/src/main/java/net/wotonomy/foundation/internal/Duplicator.java
new file mode 100644
index 0000000..ddf347d
--- /dev/null
+++ b/projects/net.wotonomy.foundation/src/main/java/net/wotonomy/foundation/internal/Duplicator.java
@@ -0,0 +1,299 @@
+/*
+Wotonomy: OpenStep design patterns for pure Java applications.
+Copyright (C) 2001 Michael Powers
+
+This library is free software; you can redistribute it and/or
+modify it under the terms of the GNU Lesser General Public
+License as published by the Free Software Foundation; either
+version 2.1 of the License, or (at your option) any later version.
+
+This library is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+Lesser General Public License for more details.
+
+You should have received a copy of the GNU Lesser General Public
+License along with this library; if not, see http://www.gnu.org
+*/
+
+package net.wotonomy.foundation.internal;
+
+import net.wotonomy.foundation.*;
+import net.wotonomy.foundation.internal.Introspector;
+import net.wotonomy.foundation.internal.WotonomyException;
+
+import java.io.*;
+import java.util.*; //collections
+
+/**
+* Duplicator makes use of Introspector to duplicate objects,
+* either by shallow copy, deep copy, or by copying properties
+* from one object to apply to another object. You may find this
+* class useful because java.lang.Object.clone() only supports
+* shallow copying.
+*
+* @author michael@mpowers.net
+* @author $Author: cgruber $
+* @version $Revision: 895 $
+*/
+
+public class Duplicator
+{
+ /**
+ * Used to represent null values for properties in the
+ * maps returned by readProperties and cloneProperties
+ * and in the parameter to writeProperties.
+ * This actually references the NSNull instance.
+ */
+ public static final Object NULL = NSNull.nullValue();
+ private static NSSelector clone = new NSSelector( "clone" );
+
+ /**
+ * Returns a list of properties for the specified class
+ * that are both readable and writable.
+ */
+ static public List editablePropertiesForObject(
+ Object anObject )
+ {
+ List readProperties = new ArrayList();
+ String[] read = Introspector.getReadPropertiesForObject( anObject );
+ for ( int i = 0; i < read.length; i++ )
+ {
+ readProperties.add( read[i] );
+ }
+
+ List properties = new ArrayList();
+ String[] write = Introspector.getWritePropertiesForObject( anObject );
+ for ( int i = 0; i < write.length; i++ )
+ {
+ properties.add( write[i] );
+ }
+
+ // only use properties on both lists: read/write
+ properties.retainAll( readProperties );
+
+ return properties;
+ }
+
+ /**
+ * Returns a Map containing only the mutable properties
+ * for the specified object and their values.
+ * Any null values for properties will be represented with
+ * the NULL object.
+ */
+ static public Map readPropertiesForObject(
+ Object anObject )
+ {
+ NSMutableDictionary result = new NSMutableDictionary();
+
+ String key;
+ Object value;
+ Iterator it = editablePropertiesForObject( anObject ).iterator();
+ while ( it.hasNext() )
+ {
+ key = it.next().toString();
+ value = Introspector.get( anObject, key );
+ if ( value == null ) value = NULL;
+ result.setObjectForKey( value, key );
+ }
+ return result;
+ }
+
+ /**
+ * Returns a Map containing only the mutable properties
+ * for the specified object and deep clones of their values.
+ * Nulls are represented by the NULL object.
+ */
+ static public Map clonePropertiesForObject(
+ Object anObject )
+ {
+ Object key, value;
+ Map result = readPropertiesForObject( anObject );
+ Iterator it = result.keySet().iterator();
+ while ( it.hasNext() )
+ {
+ key = it.next();
+ value = result.get( key );
+ value = deepClone( value );
+ result.put( key, value );
+ }
+ return result;
+ }
+
+ /**
+ * Applies the map of properties and values to the
+ * specified object. Null values for properties must
+ * be represented by the NULL object.
+ */
+ static public void writePropertiesForObject(
+ Map aMap, Object anObject )
+ {
+ String key;
+ Object value;
+ Iterator it = aMap.keySet().iterator();
+ while ( it.hasNext() )
+ {
+ key = it.next().toString();
+ value = aMap.get( key );
+ if ( NULL.equals( value ) ) value = null;
+ Introspector.set( anObject, key, value );
+ }
+ }
+
+ /**
+ * Creates a new copy of the specified object.
+ * This implementation tries to call clone(),
+ * and failing that, calls newInstance
+ * and then calls copy() to transfer the values.
+ * @throws WotonomyException if any operation fails.
+ */
+ static public Object clone(
+ Object aSource )
+ {
+ Object result = null;
+ if ( clone.implementedByObject( aSource ) )
+ {
+ try
+ {
+ result = clone.invoke( aSource );
+ return result;
+ }
+ catch ( Exception exc )
+ {
+ // fall back on newInstance()
+ }
+ }
+
+ Class c = aSource.getClass();
+ try
+ {
+ result = c.newInstance();
+ }
+ catch ( Exception exc )
+ {
+ throw new WotonomyException( exc );
+ }
+ return copy( aSource, result );
+ }
+
+ /**
+ * Creates a deep copy of the specified object.
+ * Every object in this objects graph will be
+ * duplicated with new instances.
+ * @throws WotonomyException if any operation fails.
+ */
+ static public Object deepClone(
+ Object aSource )
+ {
+ // the only known way to deep copy in
+ // java without native code is serialization
+
+ try
+ {
+ ByteArrayOutputStream byteOutput =
+ new ByteArrayOutputStream();
+ ObjectOutputStream objectOutput =
+ new ObjectOutputStream( byteOutput );
+
+ objectOutput.writeObject( aSource );
+ objectOutput.flush();
+ objectOutput.close();
+
+ ByteArrayInputStream byteInput =
+ new ByteArrayInputStream( byteOutput.toByteArray() );
+ ObjectInputStream objectInput =
+ new ObjectInputStream( byteInput );
+ return objectInput.readObject();
+ }
+ catch ( Exception exc )
+ {
+ throw new WotonomyException( "Error cloning object: " + aSource, exc );
+ }
+ }
+
+ /**
+ * Copies values from one object to another.
+ * Returns the destination object.
+ * @throws WotonomyException if any operation fails.
+ */
+ static public Object copy(
+ Object aSource, Object aDestination )
+ {
+ try
+ {
+ writePropertiesForObject(
+ readPropertiesForObject( aSource ), aDestination );
+ }
+ catch ( RuntimeException exc )
+ {
+ throw new WotonomyException( exc );
+ }
+ return aDestination;
+ }
+
+ /**
+ * Deeply clones the values from one object and applies them
+ * to another object.
+ * Returns the destination object.
+ * @throws WotonomyException if any operation fails.
+ */
+ static public Object deepCopy(
+ Object aSource, Object aDestination )
+ {
+ try
+ {
+ writePropertiesForObject(
+ clonePropertiesForObject( aSource ), aDestination );
+ }
+ catch ( RuntimeException exc )
+ {
+ throw new WotonomyException( exc );
+ }
+ return aDestination;
+ }
+}
+
+/*
+ * $Log$
+ * Revision 1.1 2006/02/16 16:52:12 cgruber
+ * Add cvsignore crap to find off checking in binary crap.
+ *
+ * Revision 1.1 2006/02/16 13:22:22 cgruber
+ * Check in all sources in eclipse-friendly maven-enabled packages.
+ *
+ * Revision 1.11 2001/08/22 19:24:26 mpowers
+ * Providing a more helpful error message for cloning exceptions.
+ *
+ * Revision 1.10 2001/03/29 03:30:36 mpowers
+ * Refactored duplicator a bit.
+ * Disabled MissingPropertyExceptions for now.
+ *
+ * Revision 1.9 2001/03/28 14:11:23 mpowers
+ * Removed debugging printlns.
+ *
+ * Revision 1.8 2001/03/27 23:25:48 mpowers
+ * Basically reverting to the previous version.
+ *
+ * Revision 1.7 2001/03/06 23:18:13 mpowers
+ * Clarified some comments.
+ *
+ * Revision 1.6 2001/03/01 20:36:35 mpowers
+ * Better error handling and better handling of nulls.
+ *
+ * Revision 1.5 2001/02/27 21:43:40 mpowers
+ * Removed NullMarker class in favor of NSNull.
+ *
+ * Revision 1.4 2001/02/26 22:41:51 mpowers
+ * Implemented null placeholder classes.
+ * Duplicator now uses NSNull.
+ * No longer catching base exception class.
+ *
+ * Revision 1.3 2001/02/23 21:07:46 mpowers
+ * Documented the NULL object.
+ *
+ * Revision 1.1 2001/02/16 22:51:29 mpowers
+ * Now deep-cloning objects passed between editing contexts.
+ *
+ *
+ */
+
diff --git a/projects/net.wotonomy.foundation/src/main/java/net/wotonomy/foundation/internal/Introspector.java b/projects/net.wotonomy.foundation/src/main/java/net/wotonomy/foundation/internal/Introspector.java
new file mode 100644
index 0000000..2b313d0
--- /dev/null
+++ b/projects/net.wotonomy.foundation/src/main/java/net/wotonomy/foundation/internal/Introspector.java
@@ -0,0 +1,941 @@
+/*
+Wotonomy: OpenStep design patterns for pure Java applications.
+Copyright (C) 2000 Michael Powers
+
+This library is free software; you can redistribute it and/or
+modify it under the terms of the GNU Lesser General Public
+License as published by the Free Software Foundation; either
+version 2.1 of the License, or (at your option) any later version.
+
+This library is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+Lesser General Public License for more details.
+
+You should have received a copy of the GNU Lesser General Public
+License along with this library; if not, see http://www.gnu.org
+*/
+
+package net.wotonomy.foundation.internal;
+
+import java.lang.reflect.Field;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Hashtable;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+/**
+* This Introspector is a static utility class written to work
+* around limitations in PropertyDescriptor and Introspector.<br><br>
+*
+* Of particular note are the get() and set() methods, which will attempt
+* to get and set artibrary values on arbitrary objects to the best of its
+* ability, converting values as appropriate. Properties of the form
+* "property.nestedproperty.anotherproperty" are supported to get and set
+* values on property values directly.<br><br>
+*
+* Note that for naming getter methods, this class supports "get", "is",
+* and also the property name itself, which supports NeXT-style properties.
+* Introspector supports Maps by treating the keys a property names,
+* supports Lists by treating the indexes as property names. <br><br>
+*
+* Numeric and boolean types can be inverted by prepending a "!" before
+* the name of the property, like "manager.!active" or "task.!lag".
+*
+* @author michael@mpowers.net
+* @author $Author: cgruber $
+* @version $Revision: 893 $
+*/
+
+public class Introspector
+{
+ // allows "hasProperty" or "property" forms
+ public static boolean strict = false;
+
+ // print exception stack traces
+ private static boolean debug = true;
+
+ // path separator
+ public static final String SEPARATOR = ".";
+
+ // method cache - use hashtables for thread safety
+ private static Map getterMethods = new Hashtable();
+ private static Map setterMethods = new Hashtable();
+
+ // wildcard value - using this class to represent a "wildcard" generic class.
+ // we have to do this when matching methods by parameter types and a
+ // null value is passed in - can't tell what class the null should be.
+ public static Class WILD = Introspector.class;
+
+ // empty class array - prevents having to create one every time
+ private static final Class[] EMPTY_CLASS_ARRAY = new Class[0];
+
+ // use OGNL for property access
+ private static boolean useOGNL;
+
+ static
+ {
+ try
+ {
+ useOGNL = ( Class.forName( "ognl.Ognl" ) != null );
+ }
+ catch ( ClassNotFoundException t )
+ {
+ useOGNL = false;
+ }
+ }
+
+/**
+* Utility method to get the read method for a property belonging to a class.
+* Will search for methods in the form of "getProperty" and failing that
+* "isProperty" (to handle booleans).
+* @param objectClass the class whose property methods will be retrieved.
+* @param aProperty The property whose method will be retrieved.
+* @param paramTypes An array of class objects representing the types of parameters.
+* @return The appropriate method for the class, or null if not found.
+*/
+ static public Method getPropertyReadMethod(
+ Class objectClass, String aProperty, Class[] paramTypes )
+ {
+ Method result = null;
+
+ result = getMethodFromClass( objectClass, aProperty, paramTypes, true );
+
+ return result;
+ }
+
+/**
+* Utility method to get the write method for a property belonging to a class.
+* Will search for methods in the form of "setProperty".
+* @param objectClass the class whose property methods will be retrieved.
+* @param aProperty The property whose method will be retrieved.
+* @param paramTypes An array of class objects representing the types of parameters.
+* @return The appropriate method for the class, or null if not found.
+*/
+ static public Method getPropertyWriteMethod(
+ Class objectClass, String aProperty, Class[] paramTypes )
+ {
+ Method result = null;
+
+ result = getMethodFromClass( objectClass, aProperty, paramTypes, false );
+
+ return result;
+ }
+
+/**
+* Gets a named method from a class. Using this method is preferred because
+* the results are cached and should be faster than calling Class.getMethod().
+* Note that if an object has a "get" getter method and an "is" getter method
+* with the same signature defined for a given property. The "get" method
+* is called.
+* @param objectClass the Class whose property methods will be retrieved.
+* @param aMethodName A String containing the name of the desired method.
+* @param paramTypes An array of class objects representing the types of parameters.
+* @return The appropriate Method from the Class, or null if not found.
+*/
+ static private Method getMethodFromClass(
+ Class objectClass, String aProperty, Class[] paramTypes, boolean doGetter)
+ { // System.out.print( "Introspector.getMethodFromClass: " + aMethodName + " : " );
+
+ Map classesToMethods = (doGetter ?
+ getterMethods :
+ setterMethods);
+
+ Map allMethods = (Map) classesToMethods.get( objectClass );
+ if (allMethods == null)
+ {
+ // need to build maps for this class
+ mapPropertiesForClass( objectClass );
+ // now the map should exist
+ allMethods = (Map) classesToMethods.get( objectClass );
+ }
+
+ Method[] methods = (Method[]) allMethods.get( aProperty );
+ if ( methods == null )
+ {
+ return null; // property doesn't exist
+ }
+
+ methods_loop: // walks through all methods for name
+ for ( int i = 0; i < methods.length; i++ )
+ {
+ Class[] types = methods[i].getParameterTypes();
+
+ // if parameter lengths don't match
+ if ( types.length != paramTypes.length )
+ {
+ // System.out.println( aMethodName + " : " + types.length + " != " + paramTypes.length );
+ continue methods_loop; // continue with outer loop
+ }
+
+ // match up each parameter
+ for ( int j = 0; j < types.length; j++ )
+ {
+ // convert primitives so they'll match - ugly
+ // (would have thought isAssignableFrom() would catch this)
+ if ( types[j].isPrimitive() )
+ {
+ if ( types[j] == Boolean.TYPE )
+ {
+ types[j] = Boolean.class;
+ }
+ else
+ if ( types[j] == Character.TYPE )
+ {
+ types[j] = Character.class;
+ }
+ else
+ if ( types[j] == Byte.TYPE )
+ {
+ types[j] = Byte.class;
+ }
+ else
+ if ( types[j] == Short.TYPE )
+ {
+ types[j] = Short.class;
+ }
+ else
+ if ( types[j] == Integer.TYPE )
+ {
+ types[j] = Integer.class;
+ }
+ else
+ if ( types[j] == Long.TYPE )
+ {
+ types[j] = Long.class;
+ }
+ else
+ if ( types[j] == Float.TYPE )
+ {
+ types[j] = Float.class;
+ }
+ else
+ if ( types[j] == Double.TYPE )
+ {
+ types[j] = Double.class;
+ }
+ }
+
+ // if parameters don't match
+ if ( ( paramTypes[j] != WILD ) && ( ! types[j].isAssignableFrom( paramTypes[j] ) ) )
+ {
+// System.out.println( "Introspector.getMethodFromClass: " +
+// aProperty + " : " + types[j] + " != " + paramTypes[j] );
+ continue methods_loop; // continue with outer loop
+ }
+ }
+
+ // all params match
+ return methods[i];
+ }
+
+ // no match
+ return null;
+ }
+
+ static private final Method[] getAllMethodsForClass( Class aClass )
+ {
+ Method[] local = aClass.getDeclaredMethods(); // only local
+ Method[] all = aClass.getMethods(); // all public
+ Method[] result = new Method[ local.length + all.length ];
+ System.arraycopy( local, 0, result, 0, local.length );
+ System.arraycopy( all, 0, result, local.length, all.length );
+ return result;
+ }
+
+ /**
+ * Generates a map of properties to both getter or setter methods for the given class.
+ * Then assigned those maps into the appropriate getterMethods and setterMethods maps
+ * keyed by the specified class. Even on error, this method will at least place empty
+ * property maps into each of the methods maps.
+ */
+ static private void mapPropertiesForClass( Class objectClass )
+ {
+ try
+ {
+ Map readProperties = new HashMap();
+ getterMethods.put( objectClass, readProperties );
+ Map writeProperties = new HashMap();
+ setterMethods.put( objectClass, writeProperties );
+
+ String name, property;
+ Method[] methods = getAllMethodsForClass( objectClass ); // throws SecurityException
+ for ( int i = 0; i < methods.length; i++ )
+ {
+ name = methods[i].getName();
+ methods[i].setAccessible( true ); // throws SecurityException
+ if ( name.startsWith( "set" ) )
+ {
+ name = name.substring( 3 );
+ if ( ! "".equals( name ) ) // excludes "set()"
+ {
+ putMethodIntoPropertyMap( name, methods[i], writeProperties );
+ }
+ }
+ else
+ if ( methods[i].getReturnType() != void.class )
+ {
+ String fullname = name;
+ if ( name.startsWith( "get" ) )
+ {
+ name = name.substring( 3 );
+ }
+ else
+ if ( name.startsWith( "is" ) )
+ {
+ name = name.substring( 2 );
+ }
+ else
+ if ( name.startsWith( "has" ) && ( !strict ) ) // what about hashCode()?
+ {
+ name = name.substring( 3 );
+ }
+
+ if ( ! "".equals( name ) && ( !strict ) ) // excludes "get()", "has()", and "is()"
+ {
+ putMethodIntoPropertyMap( name, methods[i], readProperties );
+ if ( fullname != name )
+ { // allows us to match properties that include the get/set prefix as well
+ putMethodIntoPropertyMap( fullname, methods[i], readProperties );
+ }
+ }
+ }
+ }
+ }
+ catch ( SecurityException se )
+ {
+ System.out.println( "Introspector.getMethodFromClass: " + se );
+ // this class will show up with empty getter/setter maps
+ }
+ }
+
+ /**
+ * Places a property-method pair into one of the properties maps.
+ * This in effect maps a property to an array of methods.
+ */
+ private static void putMethodIntoPropertyMap( String aProperty, Method aMethod, Map aMap )
+ {
+ // ensure first character is lower case
+ StringBuffer buffer = new StringBuffer( aProperty );
+ buffer.setCharAt(0, Character.toLowerCase(buffer.charAt(0)));
+ String key = buffer.toString();
+
+ // build array of methods for property
+ Method[] result = (Method[]) aMap.get( key );
+ if ( result == null )
+ {
+ result = new Method[] { aMethod };
+ }
+ else
+ {
+ // create new array that's larger by one and copy
+ int i;
+ Method[] enlarged = new Method[ result.length + 1 ];
+ for ( i = 0; i < result.length; i ++ )
+ {
+ enlarged[i] = result[i];
+ }
+ // add the new method to end
+ enlarged[i] = aMethod;
+ result = enlarged;
+ }
+ aMap.put( key, result );
+ }
+
+/**
+* Utility method to get a method for a property belonging to a class.
+* Use this if you don't feel like making the Class array from the parameters
+* you will be using - pass in the parameters themselves.
+* @param objectClass the Class whose property methods will be retrieved.
+* @param aProperty The property whose method will be retrieved.
+* @param params An array of parameters to be used.
+* @return The appropriate method for the class, or null if not found.
+*/
+ static public Method getPropertyReadMethod(
+ Class objectClass, String aProperty, Object[] params )
+ {
+ // optimization: avoid allocating class array for common case
+ if ( params.length == 0 )
+ {
+ return getPropertyReadMethod(
+ objectClass, aProperty, EMPTY_CLASS_ARRAY );
+ }
+
+ Class[] paramList = new Class[ params.length ];
+ for ( int i = 0; i < params.length; i++ )
+ {
+ if ( params[i] != null )
+ {
+ paramList[i] = params[i].getClass();
+ }
+ else
+ {
+ paramList[i] = WILD;
+ }
+ }
+ return getPropertyReadMethod( objectClass, aProperty, paramList );
+ }
+
+/**
+* Utility method to get a method for a property belonging to a class.
+* Use this if you don't feel like making the Class array from the parameters
+* you will be using - pass in the parameters themselves.
+* @param objectClass the Class whose property methods will be retrieved.
+* @param aProperty The property whose method will be retrieved.
+* @param params An array of parameters to be used.
+* @return The appropriate method for the class, or null if not found.
+*/
+ static public Method getPropertyWriteMethod(
+ Class objectClass, String aProperty, Object[] params )
+ {
+ Class[] paramList = new Class[ params.length ];
+ for ( int i = 0; i < params.length; i++ )
+ {
+ if ( params[i] != null )
+ {
+ paramList[i] = params[i].getClass();
+ }
+ else
+ {
+ paramList[i] = WILD;
+ }
+ }
+ return getPropertyWriteMethod( objectClass, aProperty, paramList );
+ }
+
+ /**
+ * Gets a list of the readable properties for the given class.
+ * Note that readable properties may not be writable - see getWriteProperties().
+ * @return An array of property names in no particular order
+ * where each name is a string with the first character in lower case.
+ */
+ public static String[] getReadPropertiesForClass( Class objectClass )
+ {
+ Map properties = (Map) getterMethods.get( objectClass );
+ if ( properties == null )
+ {
+ // need to build maps for this class
+ mapPropertiesForClass( objectClass );
+ // now the map should exist
+ properties = (Map) getterMethods.get( objectClass );
+ }
+
+ // put property names into string array
+ Set keys = properties.keySet();
+ Iterator it = keys.iterator();
+ int len = keys.size();
+ String[] result = new String[ len ];
+ for ( int i = 0; i < len; i++ )
+ {
+ result[i] = (String) it.next();
+ }
+ return result;
+ }
+
+ /**
+ * Gets a list of the writable properties for the given class.
+ * Note that writable properties may not be writable - see getReadProperties().
+ * @return An array of property names in no particular order
+ * where each name is a string with the first character in lower case.
+ */
+ public static String[] getWritePropertiesForClass( Class objectClass )
+ {
+ Map properties = (Map) setterMethods.get( objectClass );
+ if ( properties == null )
+ {
+ // need to build maps for this class
+ mapPropertiesForClass( objectClass );
+ // now the map should exist
+ properties = (Map) setterMethods.get( objectClass );
+ }
+
+ // put property names into string array
+ Set keys = properties.keySet();
+ Iterator it = keys.iterator();
+ int len = keys.size();
+ String[] result = new String[ len ];
+ for ( int i = 0; i < len; i++ )
+ {
+ result[i] = (String) it.next();
+ }
+ return result;
+ }
+
+ /**
+ * Gets a list of the readable properties for the given object, which may
+ * not be null. This method is more useful than getReadPropertiesForClass
+ * in that Maps will return their keys as properties and Lists will return
+ * their element indices as properties.
+ * Note that readable properties may not be writable - see getWriteProperties().
+ * @return An array of property names in no particular order
+ * where each name is a string with the first character in lower case.
+ */
+ public static String[] getReadPropertiesForObject( Object anObject )
+ {
+ List properties = new ArrayList();
+ String[] classProperties =
+ getReadPropertiesForClass( anObject.getClass() );
+ if ( anObject instanceof List )
+ {
+ properties.addAll( getPropertiesForList( (List) anObject ) );
+ }
+ if ( anObject instanceof Map )
+ {
+ properties.addAll( getPropertiesForMap( (Map) anObject ) );
+ }
+ int i;
+ int len = classProperties.length + properties.size();
+ String[] result = new String[ len ];
+ for ( i = 0; i < classProperties.length; i++ )
+ {
+ result[i] = classProperties[i];
+ }
+ Iterator it = properties.iterator();
+ while ( it.hasNext() )
+ {
+ result[i++] = it.next().toString();
+ }
+ return result;
+ }
+
+ /**
+ * Gets a list of the writable properties for the given object, which may
+ * not be null. This method is more useful than getWritePropertiesForClass
+ * in that Maps will return their keys as properties and Lists will return
+ * their element indices as properties.
+ * Note that writable properties may not be writable - see getReadProperties().
+ * @return An array of property names in no particular order
+ * where each name is a string with the first character in lower case.
+ */
+ public static String[] getWritePropertiesForObject( Object anObject )
+ {
+ List properties = new ArrayList();
+ String[] classProperties =
+ getWritePropertiesForClass( anObject.getClass() );
+ if ( anObject instanceof List )
+ {
+ properties.addAll( getPropertiesForList( (List) anObject ) );
+ }
+ if ( anObject instanceof Map )
+ {
+ properties.addAll( getPropertiesForMap( (Map) anObject ) );
+ }
+
+ int i;
+ int len = classProperties.length + properties.size();
+ String[] result = new String[ len ];
+ for ( i = 0; i < classProperties.length; i++ )
+ {
+ result[i] = classProperties[i];
+ }
+ Iterator it = properties.iterator();
+ while ( it.hasNext() )
+ {
+ result[i++] = it.next().toString();
+ }
+ return result;
+ }
+
+ private static List getPropertiesForList( List aList )
+ {
+ List result = new ArrayList();
+ int len = aList.size();
+ for ( int i = 0; i < len; i++ )
+ {
+ result.add( new Integer( i ).toString() );
+ }
+ return result;
+ }
+
+ private static List getPropertiesForMap( Map aMap )
+ {
+ List result = new ArrayList();
+ Iterator it = ((Map)aMap).keySet().iterator();
+ while ( it.hasNext() )
+ {
+ result.add( it.next().toString() );
+ }
+ return result;
+ }
+
+ private static Object[] EMPTY_ARRAY = new Object[0];
+
+ /**
+ * Convenience to get a value for a property from an object.
+ * An empty property string is considered the identity property
+ * and simply returns the object.
+ * @throws MissingPropertyException if the property cannot be
+ * found on the object.
+ */
+ public static Object getValueForObject( Object anObject, String aProperty )
+ {
+ if ( ( aProperty == null ) || ( "".equals( aProperty ) ) )
+ {
+ return anObject;
+ }
+
+ if ( useOGNL && aProperty.startsWith( "ognl:" ) )
+ {
+ try
+ {
+ return ognl.Ognl.getValue( aProperty, anObject );
+ }
+ catch ( Throwable t )
+ {
+ if ( debug )
+ {
+ System.err.println(
+ "Introspector.getValueForObject: "
+ + anObject + "' ( " + anObject.getClass() + " )"
+ + ", ognl:" + aProperty );
+ System.err.println( t );
+ }
+ return null;
+ }
+ }
+
+ boolean invert = false;
+ if ( aProperty.startsWith( "!" ) )
+ {
+ aProperty = aProperty.substring(1);
+ invert = true;
+ }
+
+ Object result = null;
+ try
+ {
+ Method m = Introspector.getPropertyReadMethod(
+ anObject.getClass(), aProperty, EMPTY_ARRAY );
+ if ( m != null )
+ {
+ result = m.invoke( anObject, EMPTY_ARRAY );
+ }
+ else // no method, try for field
+ {
+ try
+ {
+ Field field = anObject.getClass().getDeclaredField( aProperty );
+ if ( field != null )
+ {
+ field.setAccessible( true ); // throws SecurityException
+ result = field.get( anObject );
+ }
+ }
+ catch ( Throwable t )
+ {
+ // ignore for now
+ }
+ }
+
+ if ( result == null )
+ {
+ if ( anObject instanceof Map )
+ {
+ result = ((Map)anObject).get( aProperty );
+ }
+ else
+ if ( anObject instanceof List )
+ {
+ result = ((List)anObject).get( Integer.parseInt( aProperty ) );
+ }
+ }
+
+ if ( invert )
+ {
+ Object inverted = ValueConverter.invert( result );
+ if ( inverted != null ) result = inverted;
+ }
+ //System.out.println( "getValueForObject: " + anObject + " : " + aProperty + " : " + result );
+ return result;
+ }
+ catch ( Throwable exc )
+ {
+ if ( exc instanceof InvocationTargetException )
+ {
+ exc = ((InvocationTargetException)exc).getTargetException();
+ }
+ if ( exc instanceof RuntimeException )
+ {
+ throw (RuntimeException)exc;
+ }
+ if ( debug )
+ {
+ System.out.println(
+ "Introspector.getValueForObject: "
+ + anObject + "' ( " + anObject.getClass() + " )"
+ + ", " + aProperty + ": " );
+ }
+ throw new WotonomyException( exc );
+ }
+//! throw new MissingPropertyException();
+ }
+
+ /**
+ * Convenience to set a value for a property from an object.
+ * Returns the return value from executing the specified method,
+ * or null if the method returns type void.
+ * @throws MissingPropertyException if the property cannot be
+ * found on the object.
+ * @throws NullPrimitiveException if the property is of primitive
+ * type and the value is null.
+ */
+ public static Object setValueForObject(
+ Object anObject, String aProperty, Object aValue )
+ {
+ if ( useOGNL && aProperty.startsWith( "ognl:" ) )
+ {
+ try
+ {
+ ognl.Ognl.setValue( aProperty, anObject, aValue );
+ }
+ catch ( Throwable t )
+ {
+ if ( debug )
+ {
+ System.err.println(
+ "Introspector.setValueForObject: "
+ + anObject + "' ( " + anObject.getClass() + " )"
+ + ", ognl:" + aProperty + " : " + aValue );
+ System.err.println( t );
+ }
+ }
+ return null;
+ }
+
+ try
+ {
+ if ( aProperty.startsWith( "!" ) )
+ {
+ aProperty = aProperty.substring(1);
+ Object inverted = ValueConverter.invert( aValue );
+ if ( inverted != null ) aValue = inverted;
+ }
+
+ Method m = null;
+ if ( aValue != null )
+ {
+ m = Introspector.getPropertyWriteMethod(
+ anObject.getClass(), aProperty, new Class[] { aValue.getClass() } );
+ }
+ if ( m == null )
+ {
+ m = Introspector.getPropertyWriteMethod(
+ anObject.getClass(), aProperty, new Class[] { WILD } );
+ if ( ( m != null ) && ( aValue != null ) )
+ {
+ // check for null primitive
+ if ( ( aValue == null ) &&
+ ( m.getParameterTypes()[0].isPrimitive() ) )
+ {
+ throw new NullPrimitiveException();
+ }
+
+ // convert if possible
+ Object o = ValueConverter.convertObjectToClass(
+ aValue, m.getParameterTypes()[0] );
+ if ( o != null )
+ {
+ aValue = o;
+ }
+ }
+ }
+ if ( m != null )
+ {
+ return m.invoke( anObject, new Object[] { aValue } );
+ }
+ else // no method, try for field
+ {
+ try
+ {
+ Field field = anObject.getClass().getDeclaredField( aProperty );
+ if ( field != null )
+ {
+ field.setAccessible( true ); // throws SecurityException
+ field.set( anObject, aValue );
+ return null;
+ }
+ }
+ catch ( Throwable t )
+ {
+ // ignore for now
+ }
+ }
+
+ if ( anObject instanceof Map )
+ {
+ return ((Map)anObject).put( aProperty, aValue );
+ }
+ if ( anObject instanceof List )
+ {
+ List list = (List) anObject;
+ int i = Integer.parseInt( aProperty );
+ if ( list.size() < i+1 )
+ {
+ // expand list as necessary
+ for ( int j = list.size(); j <= i; j++ )
+ {
+ list.add( new Object() ); // placeholder
+ }
+ }
+ return list.set( i, aValue );
+ }
+ }
+ catch ( Throwable exc )
+ {
+ if ( exc instanceof IllegalArgumentException )
+ {
+ System.out.println(
+ "Introspector.setValueForObject: "
+ + anObject + " , " + aProperty + " , '"
+ + aValue + "' ):" );
+ System.out.println( exc );
+ }
+ else
+ if ( exc instanceof InvocationTargetException )
+ {
+ exc = ((InvocationTargetException)exc).getTargetException();
+ }
+ if ( exc instanceof RuntimeException )
+ {
+ throw (RuntimeException)exc;
+ }
+ if ( debug )
+ {
+ System.out.println(
+ "Introspector.setValueForObject: "
+ + anObject + " , " + aProperty + " , '"
+ + aValue + "' ):" );
+ }
+ throw new WotonomyException( exc );
+ }
+ return null;
+//! throw new MissingPropertyException();
+ }
+
+ /**
+ * Gets a value from an object or any of its child objects.
+ * This will parse the property string for "."'s and get
+ * values for each successive object's property in the path.
+ * An empty property string is considered the identity property
+ * and simply returns the object.
+ */
+ public static Object get( Object anObject, String aProperty )
+ {
+ int i = aProperty.indexOf( SEPARATOR );
+ if ( i == -1 ) return getValueForObject( anObject, aProperty );
+
+ String pathElement = aProperty.substring( 0, i );
+ String remainder = aProperty.substring( i+1 );
+
+ Object result = getValueForObject( anObject, pathElement );
+ if ( result == null ) return null;
+ return get( result, remainder );
+ }
+
+ /**
+ * Sets a value in an object or any of its child objects.
+ * This will parse the property string for "."'s and set
+ * values for each successive object's property in the path.<br><br>
+ *
+ * If a property is not found, this method will try to
+ * implicitly create hash maps (if possible) to fill out the path.
+ * This is useful when dealing with trees of nested maps.
+ */
+ public static Object set( Object anObject, String aProperty, Object aValue )
+ {
+ int i = aProperty.indexOf( SEPARATOR );
+ if ( i == -1 ) return setValueForObject( anObject, aProperty, aValue );
+
+ String pathElement = aProperty.substring( 0, i );
+ String remainder = aProperty.substring( i+1 );
+
+ Object result = getValueForObject( anObject, pathElement );
+ if ( result == null )
+ {
+ result = new HashMap(2);
+ setValueForObject( anObject, pathElement, result );
+ }
+ return set( result, remainder, aValue );
+ }
+
+ /**
+ * If set to true, exceptions printed to System.out.println.
+ * Defaults to true.
+ */
+ public void setDebug( boolean isDebug )
+ {
+ debug = isDebug;
+ }
+}
+
+/*
+ * $Log$
+ * Revision 1.2 2006/02/16 13:11:47 cgruber
+ * Check in all sources in eclipse-friendly maven-enabled packages.
+ *
+ * Revision 1.19 2004/02/05 02:20:34 mpowers
+ * Added experimental ognl support (if ognl is present).
+ *
+ * Revision 1.18 2003/03/26 16:44:35 mpowers
+ * Now correctly reflecting on all methods, not just locally declared ones.
+ *
+ * Revision 1.17 2003/02/21 21:10:51 mpowers
+ * Now reaching package, protected, and private methods and fields.
+ *
+ * Revision 1.16 2003/01/28 22:11:59 mpowers
+ * Now more lenient in resolving properties starting with "is" "get" or "has".
+ *
+ * Revision 1.15 2003/01/27 15:10:54 mpowers
+ * Better handling for illegal argument exceptions.
+ *
+ * Revision 1.14 2003/01/18 23:30:42 mpowers
+ * WODisplayGroup now compiles.
+ *
+ * Revision 1.13 2002/10/11 15:35:12 mpowers
+ * Removed printlns.
+ *
+ * Revision 1.11 2001/05/02 17:58:41 mpowers
+ * Removed debugging code, added comments.
+ *
+ * Revision 1.10 2001/04/08 21:00:54 mpowers
+ * Changes to support new objectsForFetchSpecification scheme.
+ *
+ * Revision 1.9 2001/03/29 03:30:36 mpowers
+ * Refactored duplicator a bit.
+ * Disabled MissingPropertyExceptions for now.
+ *
+ * Revision 1.8 2001/03/28 17:52:45 mpowers
+ * Corrected the throws in the docs.
+ *
+ * Revision 1.7 2001/03/28 17:49:13 mpowers
+ * Better exception handling in Introspector.
+ *
+ * Revision 1.6 2001/03/13 21:40:20 mpowers
+ * Improved handling of runtime exceptions.
+ *
+ * Revision 1.5 2001/03/09 22:06:35 mpowers
+ * Now extracting the wrapped exception from InvocationTargetExceptions.
+ *
+ * Revision 1.4 2001/03/01 20:36:35 mpowers
+ * Better error handling and better handling of nulls.
+ *
+ * Revision 1.3 2001/01/17 16:20:57 mpowers
+ * Introspector now handles the identity property.
+ *
+ * Revision 1.2 2001/01/09 20:08:17 mpowers
+ * Slight optimization.
+ *
+ * Revision 1.1.1.1 2000/12/21 15:52:04 mpowers
+ * Contributing wotonomy.
+ *
+ * Revision 1.5 2000/12/20 16:25:46 michael
+ * Added log to all files.
+ *
+ *
+ */
+
diff --git a/projects/net.wotonomy.foundation/src/main/java/net/wotonomy/foundation/internal/IntrospectorException.java b/projects/net.wotonomy.foundation/src/main/java/net/wotonomy/foundation/internal/IntrospectorException.java
new file mode 100644
index 0000000..b1ad824
--- /dev/null
+++ b/projects/net.wotonomy.foundation/src/main/java/net/wotonomy/foundation/internal/IntrospectorException.java
@@ -0,0 +1,32 @@
+/*
+Wotonomy: OpenStep design patterns for pure Java applications.
+Copyright (C) 2001 Michael Powers
+
+This library is free software; you can redistribute it and/or
+modify it under the terms of the GNU Lesser General Public
+License as published by the Free Software Foundation; either
+version 2.1 of the License, or (at your option) any later version.
+
+This library is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+Lesser General Public License for more details.
+
+You should have received a copy of the GNU Lesser General Public
+License along with this library; if not, see http://www.gnu.org
+*/
+
+package net.wotonomy.foundation.internal;
+
+/**
+* A WotonomyException that is thrown by Introspector.
+* This class serves as a base class for other exceptions.
+*
+* @author michael@mpowers.net
+* @author $Author: cgruber $
+* @version $Revision: 893 $
+*/
+
+public class IntrospectorException extends WotonomyException
+{
+}
diff --git a/projects/net.wotonomy.foundation/src/main/java/net/wotonomy/foundation/internal/MissingPropertyException.java b/projects/net.wotonomy.foundation/src/main/java/net/wotonomy/foundation/internal/MissingPropertyException.java
new file mode 100644
index 0000000..c1e30d3
--- /dev/null
+++ b/projects/net.wotonomy.foundation/src/main/java/net/wotonomy/foundation/internal/MissingPropertyException.java
@@ -0,0 +1,32 @@
+/*
+Wotonomy: OpenStep design patterns for pure Java applications.
+Copyright (C) 2001 Michael Powers
+
+This library is free software; you can redistribute it and/or
+modify it under the terms of the GNU Lesser General Public
+License as published by the Free Software Foundation; either
+version 2.1 of the License, or (at your option) any later version.
+
+This library is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+Lesser General Public License for more details.
+
+You should have received a copy of the GNU Lesser General Public
+License along with this library; if not, see http://www.gnu.org
+*/
+
+package net.wotonomy.foundation.internal;
+
+/**
+* A IntrospectorException that is thrown by Introspector when
+* a property does not exist for an object.
+*
+* @author michael@mpowers.net
+* @author $Author: cgruber $
+* @version $Revision: 893 $
+*/
+
+public class MissingPropertyException extends IntrospectorException
+{
+}
diff --git a/projects/net.wotonomy.foundation/src/main/java/net/wotonomy/foundation/internal/NetworkClassLoader.java b/projects/net.wotonomy.foundation/src/main/java/net/wotonomy/foundation/internal/NetworkClassLoader.java
new file mode 100644
index 0000000..43c14a5
--- /dev/null
+++ b/projects/net.wotonomy.foundation/src/main/java/net/wotonomy/foundation/internal/NetworkClassLoader.java
@@ -0,0 +1,368 @@
+/*
+ * ====================================================================
+ *
+ * The Apache Software License, Version 1.1
+ *
+ * Copyright (c) 1999 The Apache Software Foundation. All rights
+ * reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * 3. The end-user documentation included with the redistribution, if
+ * any, must include the following acknowlegement:
+ * "This product includes software developed by the
+ * Apache Software Foundation (http://www.apache.org/)."
+ * Alternately, this acknowlegement may appear in the software itself,
+ * if and wherever such third-party acknowlegements normally appear.
+ *
+ * 4. The names "The Jakarta Project", "Tomcat", and "Apache Software
+ * Foundation" must not be used to endorse or promote products derived
+ * from this software without prior written permission. For written
+ * permission, please contact apache@apache.org.
+ *
+ * 5. Products derived from this software may not be called "Apache"
+ * nor may "Apache" appear in their names without prior written
+ * permission of the Apache Group.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ * ====================================================================
+ */
+
+package net.wotonomy.foundation.internal;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URL;
+import java.util.Enumeration;
+import java.util.Hashtable;
+
+/**
+ * The correct name for this class should be URLClassLoader.
+ * But there is already a class by that name in JDK1.2.
+ *
+ * I have had quite a few problems with URLClassLoader in
+ * past, so I ended up writing this ClassLoader. I found that
+ * the Java 2's URLClassLoader, does not close the Jar file once
+ * opened. It is a pretty good optimization step, but if you
+ * modify the class in the jar file, it does not pick it up. Some
+ * operating systems may not let you modify the jar file while it is
+ * still open. IMHO, it does make sense to close the jar file
+ * after you are done reading the class data. But this approach may not
+ * get you the performance of the URLClassLoader, but it works in all
+ * cases and also runs on JDK1.1. I have enhanced this class loader
+ * to read all the zip/jar entries once & cache the data, so that
+ * there is no overhead of opening/closing jar file to pick up
+ * each entry.
+ *
+ *
+ * @author Harish Prabandham
+ */
+public class NetworkClassLoader extends ClassLoader {
+ private ClassLoader parent = null; // parent classloader
+ private Hashtable classCache = new Hashtable();
+ private Hashtable urlset = new Hashtable();
+
+ /**
+ * Creates a new instance of the class loader.
+ * @param delegate/parent class loader.
+ */
+ public NetworkClassLoader(ClassLoader parent) {
+ setParent(parent);
+ }
+
+ /**
+ * Sets the parent/delegate class loader.
+ * @param delegate/parent class loader.
+ */
+ protected final void setParent(ClassLoader parent) {
+ this.parent = parent;
+ }
+
+ /**
+ * Adds the given URL to this class loader. If the URL
+ * ends with "/", then it is assumed to be a directory
+ * otherwise, it is assumed to be a zip/jar file. If the
+ * same URL is added again, the URL is re-opened and this
+ * zip/jar file is used for serving any future class requests.
+ * @param URL where to look for the classes.
+ */
+ public synchronized void addURL(URL url) {
+ // System.out.println("Adding url: " + url);
+ if(!urlset.containsKey(url)) {
+ try {
+ urlset.put(url, new URLResourceReader(url));
+ }catch(IOException ioe){
+ // Probably a bad url...
+ }
+ } else {
+ // remove the old one & add a new one...
+ try{
+ URLResourceReader newu = new URLResourceReader(url);
+ URLResourceReader oldu = (URLResourceReader) urlset.get(url);
+ oldu.close();
+ urlset.remove(url);
+ urlset.put(url, newu);
+ } catch (IOException ioe) {
+ }
+ }
+ }
+
+ /**
+ * @return An enumeration of URLs where this class loader
+ * looks for classes.
+ */
+ public Enumeration getURLs() {
+ return urlset.keys();
+ }
+
+ /**
+ * Call this to bypass the implementation of loadClass.
+ */
+ public Class findClass(String name) {
+ byte[] b = loadClassData(name);
+ if ( b == null ) return null;
+ return defineClass(name, b, 0, b.length);
+ }
+
+ protected byte[] loadResource(URL url, String resourceName)
+ throws IOException {
+ URLResourceReader urr = (URLResourceReader) urlset.get(url);
+ // System.out.println("Loading from " + urr + " " + resourceName);
+ if(urr != null) {
+ return urr.getResource(resourceName);
+ }
+
+ return null;
+ }
+
+ protected byte[] loadResource(String resource) {
+ byte[] barray = null;
+ for(Enumeration e = urlset.keys(); e.hasMoreElements();) {
+ URL url = (URL) e.nextElement();
+
+ try {
+ barray = loadResource(url, resource);
+ } catch(Exception ex) {
+ } finally {
+ if(barray != null)
+ break;
+ }
+ }
+
+ return barray;
+ }
+
+ protected byte[] loadClassData(String classname) {
+ String resourceName = classname.replace('.', '/') + ".class";
+ return loadResource(resourceName);
+ }
+
+ /**
+ * Overridden to search for a resource and return
+ * a "jar"-style URL or normal "file" URL as necessary.
+ */
+ protected URL findResource(String name)
+ { //System.out.println( "findResource: " + name );
+ URL url;
+ byte[] barray = null;
+
+ for ( Enumeration e = urlset.keys(); e.hasMoreElements(); )
+ {
+ url = (URL) e.nextElement();
+ try
+ {
+ barray = loadResource(url, name); // loads fully: wasteful
+ }
+ catch(Exception ex)
+ {
+ // do nothing
+ }
+ if( barray != null )
+ {
+ try
+ {
+ String ref = url.toString();
+ if ( ref.endsWith( ".jar" ) )
+ {
+ //System.out.println( "jar:" + ref + "!/" + name );
+ return new URL( "jar:" + ref + "!/" + name );
+ }
+ else
+ {
+ //System.out.println( new URL( url, name ).toString() );
+ return new URL( url, name );
+ }
+ }
+ catch ( Throwable t )
+ {
+ t.printStackTrace();
+ }
+ }
+ }
+
+ return null;
+ }
+
+ /**
+ * @return The resource as the input stream if such a resource
+ * exists, otherwise returns null.
+ */
+ public InputStream getResourceAsStream(String name) {
+ //System.out.println( "getResourceAsStream: " + name );
+ InputStream istream = null;
+
+ // Algorithm:
+ //
+ // 1. first check the system path for the resource
+ // 2. next check the delegate/parent class loader for the resource
+ // 3. then attempt to get the resource from the url set.
+ //
+
+ // Lets check the system path for the resource.
+ istream = getSystemResourceAsStream(name);
+ if(istream != null)
+ return istream;
+
+ // Lets check the parent/delegate class loader for the resource.
+ if(parent != null) {
+ istream = parent.getResourceAsStream(name);
+ if(istream != null)
+ return istream;
+ }
+
+ // Lets load it ourselves.
+ byte[] data = loadResource(name);
+ if(data != null) {
+ istream = new ByteArrayInputStream(data);
+ }
+
+ return istream;
+ }
+
+ /**
+ * java.lang.ClassLoader's defineClass method is final, so the
+ * its subclasses cannot override this method. But, this class
+ * calls this method in the loadClass() instead.
+ * @param The name of the class without ".class" extension.
+ * @param The class data bytes.
+ * @return The class object.
+ */
+ protected Class defineClass(String classname, byte[] classdata) {
+ return defineClass(classname, classdata, 0, classdata.length);
+ }
+
+ public synchronized Class loadClass(String name, boolean resolve)
+ throws ClassNotFoundException {
+ Class c = null;
+
+ // Algorithm: (Please do not change the order; unless you
+ // have a good reason to do so).
+ //
+ // 1. first check the system class loader.
+ // 2. next check the delegate/parent class loader.
+ // 3. next check the class cache
+ // 4. then attempt to load classes from the URL set.
+ //
+
+ // Lets see if the class is in system class loader.
+ try {
+ c = findSystemClass(name);
+ }catch(ClassNotFoundException cnfe) {
+ }finally {
+ if(c != null)
+ return c;
+ }
+
+ // Lets see if the class is in parent class loader.
+ try {
+ if(parent != null)
+ c = parent.loadClass(name);
+ }catch(ClassNotFoundException cnfe) {
+ }finally {
+ if(c != null)
+ return c;
+ }
+
+ // Lets see if the class is in the cache..
+ c = (Class) classCache.get(name);
+
+ if(c != null)
+ return c;
+
+
+ // Lets see if we find the class all by ourselves.
+ byte[] data = loadClassData(name);
+
+ if(data != null) {
+ // we did !!
+ c = defineClass(name, data);
+ classCache.put(name, c);
+ if(resolve)
+ resolveClass(c);
+ } else {
+ // We are out of luck at this point...
+ throw new ClassNotFoundException(name);
+ }
+
+ return c;
+ }
+
+ /**
+ * This method resets this ClassLoader's state. It completely
+ * removes all the URLs and classes in this class loader cache.
+ */
+ public final void clear() {
+ urlset.clear();
+ classCache.clear();
+ }
+
+ /**
+ * This method resets this ClassLoader's state and resets the
+ * references for garbage collection.
+ */
+ protected void finalize() throws Throwable {
+ // Cleanup real well. Otherwise, this can be
+ // a major source of memory leaks...
+
+ // remove all the urls & class entries.
+ clear();
+
+ parent = null;
+ urlset = null;
+ classCache = null;
+ }
+}
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/projects/net.wotonomy.foundation/src/main/java/net/wotonomy/foundation/internal/NullPrimitiveException.java b/projects/net.wotonomy.foundation/src/main/java/net/wotonomy/foundation/internal/NullPrimitiveException.java
new file mode 100644
index 0000000..e367211
--- /dev/null
+++ b/projects/net.wotonomy.foundation/src/main/java/net/wotonomy/foundation/internal/NullPrimitiveException.java
@@ -0,0 +1,32 @@
+/*
+Wotonomy: OpenStep design patterns for pure Java applications.
+Copyright (C) 2001 Michael Powers
+
+This library is free software; you can redistribute it and/or
+modify it under the terms of the GNU Lesser General Public
+License as published by the Free Software Foundation; either
+version 2.1 of the License, or (at your option) any later version.
+
+This library is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+Lesser General Public License for more details.
+
+You should have received a copy of the GNU Lesser General Public
+License along with this library; if not, see http://www.gnu.org
+*/
+
+package net.wotonomy.foundation.internal;
+
+/**
+* A IntrospectorException that is thrown by Introspector when
+* trying to set a primitive type to null.
+*
+* @author michael@mpowers.net
+* @author $Author: cgruber $
+* @version $Revision: 893 $
+*/
+
+public class NullPrimitiveException extends IntrospectorException
+{
+}
diff --git a/projects/net.wotonomy.foundation/src/main/java/net/wotonomy/foundation/internal/PropertyComparator.java b/projects/net.wotonomy.foundation/src/main/java/net/wotonomy/foundation/internal/PropertyComparator.java
new file mode 100644
index 0000000..abdc82f
--- /dev/null
+++ b/projects/net.wotonomy.foundation/src/main/java/net/wotonomy/foundation/internal/PropertyComparator.java
@@ -0,0 +1,100 @@
+/*
+Wotonomy: OpenStep design patterns for pure Java applications.
+Copyright (C) 2000 Intersect Software Corporation
+
+This library is free software; you can redistribute it and/or
+modify it under the terms of the GNU Lesser General Public
+License as published by the Free Software Foundation; either
+version 2.1 of the License, or (at your option) any later version.
+
+This library is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+Lesser General Public License for more details.
+
+You should have received a copy of the GNU Lesser General Public
+License along with this library; if not, see http://www.gnu.org
+*/
+
+package net.wotonomy.foundation.internal;
+
+import java.io.Serializable;
+import java.util.Comparator;
+
+
+/**
+* A Comparator that will sort elements based on the
+* property specified in the constructor.
+*
+* @author michael@mpowers.net
+* @author $Author: cgruber $
+* @version $Revision: 893 $
+*/
+public class PropertyComparator implements Comparator, Serializable
+{
+ private String property;
+
+/**
+* Standard constructor to configure the comparator.
+* @param aProperty A property whose value is used to sort elements.
+*/
+ public PropertyComparator( String aProperty )
+ {
+ property = aProperty;
+ }
+
+ // interface Comparator
+
+ public int compare(Object o1, Object o2)
+ {
+ Object v1 = Introspector.get( o1, property );
+ Object v2 = Introspector.get( o2, property );
+ if ( v1 instanceof Comparable )
+ {
+ return ((Comparable)v1).compareTo( v2 );
+ }
+ else
+ if ( v2 instanceof Comparable )
+ {
+ return ((Comparable)v2).compareTo( v1 );
+ }
+ else
+ {
+ if ( v1 == null )
+ {
+ if ( v2 == null )
+ {
+ return 0; // both nulls are equal
+ }
+ return -1; // null is less than any object
+ }
+ else
+ if ( v2 == null )
+ {
+ return 1; // any object is greater than null
+ }
+ }
+ // last resort: compare string conversions
+ return v1.toString().compareTo( v2.toString() );
+ }
+
+ public boolean equals( Object obj )
+ {
+ return ( obj instanceof PropertyComparator );
+ }
+}
+
+/*
+ * $Log$
+ * Revision 1.2 2006/02/16 13:11:47 cgruber
+ * Check in all sources in eclipse-friendly maven-enabled packages.
+ *
+ * Revision 1.1.1.1 2000/12/21 15:52:07 mpowers
+ * Contributing wotonomy.
+ *
+ * Revision 1.2 2000/12/20 16:25:46 michael
+ * Added log to all files.
+ *
+ *
+ */
+
diff --git a/projects/net.wotonomy.foundation/src/main/java/net/wotonomy/foundation/internal/PropertyListParser.java b/projects/net.wotonomy.foundation/src/main/java/net/wotonomy/foundation/internal/PropertyListParser.java
new file mode 100644
index 0000000..03231c7
--- /dev/null
+++ b/projects/net.wotonomy.foundation/src/main/java/net/wotonomy/foundation/internal/PropertyListParser.java
@@ -0,0 +1,546 @@
+/*
+Wotonomy: OpenStep design patterns for pure Java applications.
+Copyright (C) 2000 Blacksmith, Inc.
+
+This library is free software; you can redistribute it and/or
+modify it under the terms of the GNU Lesser General Public
+License as published by the Free Software Foundation; either
+version 2.1 of the License, or (at your option) any later version.
+
+This library is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+Lesser General Public License for more details.
+
+You should have received a copy of the GNU Lesser General Public
+License along with this library; if not, see http://www.gnu.org
+*/
+
+package net.wotonomy.foundation.internal;
+
+import java.util.*; //collections
+import java.io.*;
+
+/**
+ * PropertyListParser can parse a property list (plist) file or string, and
+ * return the top-level object represented by the plist. <p>
+ *
+ * A property list is a heirarchical data structure containing only Maps,
+ * Lists, and Strings -- nothing else. In other words, a property list is
+ * either a Map, List, or String instance, with the restrictions that the
+ * collections may only contain Map, List, or String instances. <p>
+ *
+ * This class can read a particularly-formatted string or file, and create
+ * the property list structure described. It provides a convenient means
+ * for having a structured data file, letting programs simply deal with the
+ * structure rather than having to do a lot of string parsing work as well.
+ * The concept is similar to Properties files, except that the values can
+ * be nested Maps or Lists instead of only Strings. <p>
+ *
+ * A Map is specified in a file by key/value pairs surrounded by brace
+ * characters. An equal sign (=) must be between the key and value, and
+ * there must be a semicolon (;) following the value.
+ *
+ * <pre>
+ * {
+ * key1 = value1;
+ * key2 = value2;
+ * etc...
+ * }
+ * </pre>
+ *
+ * A List is specified by a comma-separated list of values surrounded by parentheses, like:
+ * <pre>
+ * ( value1, value2, value3, etc... )
+ * </pre>
+ *
+ * A String can either be quoted in the manner of a constant string in
+ * Java, or unquoted. If unquoted, the string can only contain
+ * alphanumerics, underscores (_), periods (.), dollar signs ($), colons
+ * (:), or forward slashes (/). If any other character appears in the
+ * string, it must be quoted (i.e., surrounded by &quot; characters).
+ * Quoted strings may also contain \n, \t, \f, \v, \b, and \a escapes,
+ * octal escapes of the form \000, and unicode escapes of the form of \U
+ * followed by four hexadecimal characters. Any other character escaped
+ * by a backslash will be treated as that character, and the escaping
+ * backslash character will be omitted. Thus, to represent an actual
+ * backslash, it must appear as \\ in the quoted string. <p>
+ *
+ * All whitespace between elements is ignored, and both //-style and
+ * /*-style comments are allowed to appear anywhere between elements. <p>
+ *
+ * If there are any syntax errors encountered while parsing,
+ * RuntimeExceptions are thrown with the line number and column of the
+ * problem. <p>
+ *
+ * Currenty, HashMaps and ArrayLists are the actual Map and List classes
+ * used when creating the property list. <p>
+ *
+ * Examples: <p><blockquote>
+ <pre>
+ // This plist file represents a Map, since it starts with a '{'.
+ {
+ Map1 = { subkey1 = "foo"; };
+ Map2 =
+ {
+ "key1" = "This is a quoted string.";
+ "key 2" = "bar\nbaz"; // the value has a newline in it
+ key3 = ("a", b, c, "quux quux"); // a List of four Strings
+ }; // We need a semicolon here, since it's following the value of the "Map2" key
+
+ List1 = (foobar,foobaz,"foo,baz", (aa, ab, ac)); // a List of 3 Strings and a List
+
+ // And now a List of two Maps
+ List2 = (
+ {
+ key1 = value1;
+ key2 = "value 2";
+ key3 = (a,b,c,d);
+ key4 = ();
+ }, // We need the comma here
+ {
+ key1 = {}; // an empty Map
+ key2 = "another String value";
+ }
+ );
+ }
+ </pre>
+ </blockquote>
+ * For those wondering, this is essentially a re-implementation of
+ * NeXT/Apple's property lists, except that data values are not supported.
+ *
+ * @author clindberg@blacksmith.com
+ * @version $Revision: 899 $
+ */
+
+public class PropertyListParser
+{
+ private char buffer[];
+ private int currIndex;
+ private int lineNumber;
+ private int currLineStartIndex;
+
+ /** Reads an object (String, List, or Map) from plistString and returns it.
+ * RuntimeExceptions are raised if there are parse problems.
+ */
+ public static Object propertyListFromString(String plistString)
+ {
+ PropertyListParser parser = new PropertyListParser(plistString);
+ return parser.readTopLevelObject();
+ }
+
+ /**
+ * Reads all remaining characters from the Reader, and returns the
+ * result of propertyListFromString(). RuntimeExceptions are raised if
+ * there are parse problems
+ */
+ public static Object propertyListFromReader(Reader reader) throws IOException
+ {
+ char charBuffer[] = new char[2048];
+ StringBuffer stringBuffer = new StringBuffer();
+ int numRead = 0;
+
+ while (numRead >= 0)
+ {
+ numRead = reader.read(charBuffer);
+ if (numRead > 0) stringBuffer.append(charBuffer, 0, numRead);
+ }
+
+ return propertyListFromString(stringBuffer.toString());
+ }
+
+ /**
+ * Reads the contents of the specified file, and parses the contents.
+ * If any error occurs, prints out a message using System.out.println()
+ * and returns null.
+ */
+ public static Object propertyListFromFile(String filename)
+ {
+ try {
+ FileInputStream stream = new FileInputStream(filename);
+ return propertyListFromReader(new InputStreamReader(stream));
+ } catch (Exception exception) {
+ String errorMessage = exception.getMessage();
+ System.out.println("Error parsing property list from "+filename+": "+errorMessage);
+ }
+
+ return null;
+ }
+
+ /**
+ * Creates a new PropertyListParser to parse the contents of the
+ * specified String.
+ */
+ public PropertyListParser(String plistString)
+ {
+ this(plistString.toCharArray());
+ }
+
+ /**
+ * Creates a new PropertyListParser to parse the specified char array.
+ */
+ public PropertyListParser(char[] charArray)
+ {
+ buffer = charArray;
+ lineNumber = 1;
+ currLineStartIndex = 1;
+ currIndex = 0;
+ }
+
+ public Object readTopLevelObject()
+ {
+ Object plist = readObject();
+
+ skipCommentWhitespace();
+ if (!isAtEnd())
+ {
+ throwParseException("Extra characters in plist string after parsing object. A plist should only contain one top-level object.");
+ }
+
+ return plist;
+ }
+
+ private void throwParseException(String errorMessage)
+ {
+ int column = currIndex - currLineStartIndex + 1;
+ throw new RuntimeException(errorMessage + " (Line " + lineNumber + ", column " + column + ")");
+ }
+
+ private void updateLineNumberWithIndex(int lineStartIndex)
+ {
+ lineNumber++;
+ currLineStartIndex = lineStartIndex;
+ }
+
+ private boolean isAtEnd()
+ {
+ return currIndex >= buffer.length;
+ }
+
+ private void skipDoubleslashComment()
+ {
+ while (!isAtEnd() && buffer[currIndex] != '\n') {
+ currIndex++;
+ }
+ }
+
+ private void skipStandardCComment()
+ {
+ currIndex++; //skip over the starting '/'
+
+ while (!isAtEnd())
+ {
+ if (buffer[currIndex] == '\n')
+ updateLineNumberWithIndex(currIndex+1);
+
+ currIndex++;
+
+ if (buffer[currIndex-2] == '*' && buffer[currIndex-1] == '/')
+ {
+ return;
+ }
+ }
+
+ throwParseException("Input exhausted while parsing comment");
+ }
+
+ private void skipWhitespace()
+ {
+ while (!isAtEnd() && isWhitespace(buffer[currIndex]))
+ {
+ if (buffer[currIndex] == '\n')
+ updateLineNumberWithIndex(currIndex+1);
+ currIndex++;
+ }
+ }
+
+ private void skipCommentWhitespace()
+ {
+ boolean done = false;
+
+ while (!done)
+ {
+ done = true;
+
+ skipWhitespace();
+ if ((buffer.length - currIndex) > 1 && buffer[currIndex] == '/')
+ {
+ if (buffer[currIndex+1] == '/') {
+ done = false; //iterate again
+ skipDoubleslashComment();
+ }
+ else if (buffer[currIndex+1] == '*') {
+ done = false; //iterate again
+ skipStandardCComment();
+ }
+ }
+ }
+ }
+
+ private Object readObject()
+ {
+ skipCommentWhitespace();
+ if (isAtEnd()) return null;
+
+ // Data (i.e. byte[]) not supported
+ if (buffer[currIndex] == '"')
+ return readQuotedString();
+ if (buffer[currIndex] == '(')
+ return readList();
+ if (buffer[currIndex] == '{')
+ return readMap();
+
+ return readUnquotedString();
+ }
+
+ private static final byte valueForHexDigit(char c)
+ {
+ if(c >= '0' && c <= '9') return (byte)(c - '0');
+ if(c >= 'a' && c <= 'f') return (byte)((c - 'a') + 10);
+ if(c >= 'A' && c <= 'F') return (byte)((c - 'A') + 10);
+
+ return 0;
+ }
+
+ private static final boolean isOctalDigit(char c)
+ {
+ return c >= '0' && c <= '7';
+ }
+
+ private static final boolean isHexDigit(char c)
+ {
+ return (c >= '0' && c <= '9') ||
+ (c >= 'a' && c <= 'f') ||
+ (c >= 'A' && c <= 'F');
+ }
+
+ private static String unquotedStringChars = "._$:/"; // chars allowed in unquoted strings
+ private static String whitespaceChars = " \t\n\r\f";
+
+ private static final boolean isWhitespace(char c)
+ {
+ return whitespaceChars.indexOf(c) >= 0;
+ }
+
+ private static final boolean isValidUnquotedStringChar(char c)
+ {
+ return ((c >= 'a' && c <= 'z') ||
+ (c >= 'A' && c <= 'Z') ||
+ (c >= '0' && c <= '9') ||
+ unquotedStringChars.indexOf(c) >= 0);
+ }
+
+ private String readUnquotedString()
+ {
+ int startIndex = currIndex;
+
+ while (!isAtEnd() && isValidUnquotedStringChar(buffer[currIndex]))
+ currIndex++;
+
+ if (startIndex == currIndex)
+ throwParseException("No allowable characters found to parse unquoted string");
+
+ return new String(buffer, startIndex, currIndex - startIndex);
+ }
+
+ private String readQuotedString()
+ {
+ currIndex++; //skip over '"'
+
+ StringBuffer stringBuffer = new StringBuffer();
+ int startIndex = currIndex;
+
+ while (!isAtEnd() && buffer[currIndex] != '"')
+ {
+ if (buffer[currIndex] != '\\')
+ {
+ if (buffer[currIndex] == '\n')
+ updateLineNumberWithIndex(currIndex+1);
+
+ /*
+ * Just increment the index -- all these characters will be
+ * appended in chunks, either before an escape sequence or
+ * at the end.
+ */
+ currIndex++;
+ }
+ else // it's an escape
+ {
+ /* Append anything scanned past before the '\\' */
+ if (startIndex < currIndex)
+ stringBuffer.append(buffer, startIndex, currIndex - startIndex);
+ currIndex++; // skip over '\\'
+
+ if (isAtEnd())
+ throwParseException("Input exhausted while parsing escape sequence");
+
+ switch (buffer[currIndex])
+ {
+ case 't': stringBuffer.append('\t'); currIndex++; break; // tab
+ case 'n': stringBuffer.append('\n'); currIndex++; break; // newline
+ case 'r': stringBuffer.append('\r'); currIndex++; break; // carriage return
+ case 'f': stringBuffer.append('\f'); currIndex++; break; // form feed
+ case 'b': stringBuffer.append('\b'); currIndex++; break; // backspace
+ case 'a': stringBuffer.append('\007'); currIndex++; break; // bell
+ case 'v': stringBuffer.append('\013'); currIndex++; break; // vertical tab
+ case 'U':
+ case 'u':
+ {
+ /* A Unicode escape. Always followed by 4 hex digits. */
+ currIndex++; // skip past the 'U'
+ if ((currIndex+4) > buffer.length)
+ throwParseException("Not enough chars to parse \\U sequence");
+
+ if(!isHexDigit(buffer[currIndex]) || !isHexDigit(buffer[currIndex+1]) ||
+ !isHexDigit(buffer[currIndex+2]) || !isHexDigit(buffer[currIndex+3]))
+ {
+ throwParseException("Four hex digits not found for \\U sequence");
+ }
+
+ byte byte3 = valueForHexDigit(buffer[currIndex]);
+ byte byte2 = valueForHexDigit(buffer[currIndex+1]);
+ byte byte1 = valueForHexDigit(buffer[currIndex+2]);
+ byte byte0 = valueForHexDigit(buffer[currIndex+3]);
+ char theChar = (char)((byte3 << 12) + (byte2 << 8) + (byte1 << 4) + byte0);
+ stringBuffer.append(theChar);
+ currIndex += 4;
+ break;
+ }
+ case '0': case '1': case '2': case '3':
+ case '4': case '5': case '6': case '7':
+ {
+ /* An octal escape. Expect 1, 2, or 3 octal digits. */
+ int digits = 0;
+ int value = 0;
+
+ do {
+ value *= 8;
+ value += (int)(buffer[currIndex] - '0');
+ currIndex++;
+ digits++;
+ } while (digits <= 3 && !isAtEnd() && isOctalDigit(buffer[currIndex]));
+
+ if (value > 255)
+ throwParseException("Value too large in octal escape sequence (> 0377)");
+
+ // This assumes value is in ISO Latin 1 encoding
+ stringBuffer.append((char)value);
+ break;
+ }
+ /* I guess plists can't have the \x{HEX}{HEX} escapes */
+ default:
+ {
+ // Unknown escape sequence, just add the character.
+ // GCC warns if this isn't a '"', '\'', or '\\'...
+ stringBuffer.append(buffer[currIndex]);
+ if (buffer[currIndex] == '\n')
+ updateLineNumberWithIndex(currIndex+1);
+ currIndex++;
+ break;
+ }
+ } // end case
+
+ /* Reset startIndex, so a verbatim copy will now start from this index */
+ startIndex = currIndex;
+
+ } //end '\\' escape
+ }
+
+ if (isAtEnd())
+ throwParseException("Input exhausted while parsing quoted string");
+ if (startIndex < currIndex)
+ stringBuffer.append(buffer, startIndex, currIndex - startIndex);
+ currIndex++; //skip past '"'
+
+ return stringBuffer.toString();
+ }
+
+ private List readList()
+ {
+ List newList = new ArrayList();
+
+ currIndex++; //skip over '('
+ skipCommentWhitespace();
+ while (!isAtEnd() && buffer[currIndex] != ')')
+ {
+ /* A comma is required between list elements */
+ if (newList.size() > 0)
+ {
+ if (buffer[currIndex] != ',')
+ throwParseException("List parsing failed: expecting ','");
+ currIndex++;
+ skipCommentWhitespace();
+ if (isAtEnd())
+ throwParseException("Input exhausted while parsing list");
+ }
+
+ if (buffer[currIndex] != ')')
+ {
+ Object plistObject = readObject();
+ if (plistObject == null)
+ throwParseException("List parsing failed: could not read contained object.");
+ newList.add(plistObject);
+ skipCommentWhitespace();
+ }
+ }
+
+ if (isAtEnd())
+ throwParseException("Input exhausted while parsing list");
+ currIndex++; //skip past ')'
+
+ return newList;
+ }
+
+ private Map readMap()
+ {
+ HashMap newMap = new HashMap();
+
+ currIndex++; // skip over open brace
+ skipCommentWhitespace();
+
+ while (!isAtEnd() && buffer[currIndex] != '}')
+ {
+ Object key;
+ Object value;
+
+ key = readObject();
+ if (key == null || !(key instanceof String))
+ throwParseException("Map parsing failed: could not parse key or key is not a String");
+
+ skipCommentWhitespace();
+ if (isAtEnd() || buffer[currIndex] != '=')
+ throwParseException("Map parsing failed: expecting '='");
+ currIndex++; //skip over '='
+ skipCommentWhitespace();
+ if (isAtEnd())
+ throwParseException("Input exhausted while parsing map");
+
+ value = readObject();
+ if (value == null)
+ throwParseException("Map parsing failed: could not parse value object");
+
+ skipCommentWhitespace();
+ if (isAtEnd() || buffer[currIndex] != ';')
+ throwParseException("Map parsing failed: expecting ';'");
+ currIndex++; //skip over ';'
+ skipCommentWhitespace();
+
+ newMap.put(key, value);
+ }
+
+ if (isAtEnd())
+ throwParseException("Input exhausted while parsing map");
+ currIndex++; //skip past '}'
+
+ return newMap;
+ }
+
+
+ public static void main(String[] args)
+ {
+ String filename = args[0];
+ Object plist = PropertyListParser.propertyListFromFile(filename);
+ System.out.println(plist);
+ }
+}
+
diff --git a/projects/net.wotonomy.foundation/src/main/java/net/wotonomy/foundation/internal/QueueMap.java b/projects/net.wotonomy.foundation/src/main/java/net/wotonomy/foundation/internal/QueueMap.java
new file mode 100644
index 0000000..6d35b7b
--- /dev/null
+++ b/projects/net.wotonomy.foundation/src/main/java/net/wotonomy/foundation/internal/QueueMap.java
@@ -0,0 +1,538 @@
+/*
+Wotonomy: OpenStep design patterns for pure Java applications.
+Copyright (C) 2000 Blacksmith, Inc.
+
+This library is free software; you can redistribute it and/or
+modify it under the terms of the GNU Lesser General Public
+License as published by the Free Software Foundation; either
+version 2.1 of the License, or (at your option) any later version.
+
+This library is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+Lesser General Public License for more details.
+
+You should have received a copy of the GNU Lesser General Public
+License along with this library; if not, see http://www.gnu.org
+*/
+
+package net.wotonomy.foundation.internal;
+
+import java.util.*; //collections
+
+/**
+* A queue based implementation of the Map interface. This class provides for
+* all the opertions of a map, but keeps the entries in the same sequence as
+* originally added. The first entry placed in the map will be the first
+* entry retreived during iteration: first-in, first-out (FIFO). <BR><BR>
+*
+* Keys cannot be duplicated. If an entry is made using a key that already
+* exists, the value for that key will be replaced with that new value. There
+* are no such restrictions on the values. The values may be null. <BR><BR>
+*
+* Some convenience methods are provided for the queue type operations. The
+* first key can be retreived as well as the last key. A key can be used
+* to retreive its corresponding value from the map. A value can also be used
+* to retreive its key from the map, however, since there may be multiple values
+* of the same equality, the first key found will be returned. <BR><BR>
+*
+* @author rglista@blacksmith.com
+* @author mpowers@blacksmith.com
+* @date $Date: 2006-02-18 17:41:36 -0500 (Sat, 18 Feb 2006) $
+* @revision $Revision: 899 $
+*/
+public class QueueMap implements Map
+{
+ List values;
+ List keys;
+ Map keyToValue;
+
+/**
+* Creates an empty QueueMap.
+*/
+ public QueueMap()
+ {
+ values = new LinkedList();
+ keys = new LinkedList();
+ keyToValue = new HashMap();
+ }
+
+/**
+* Creates a QueueMap and places the entries from the passed in map into this map.
+* The order of the entries is based on however the iterator iteratated through
+* the map entries.
+* @param t A map object.
+*/
+ public QueueMap( Map t )
+ {
+ values = new ArrayList();
+ keys = new ArrayList();
+ keyToValue = new HashMap();
+
+ putAll( t );
+ }
+
+/**
+* Removes all the entries from this map.
+*/
+ public void clear()
+ {
+ values.clear();
+ keys.clear();
+ keyToValue.clear();
+ }
+
+/**
+* Tests to see if the key is contained in the map. If the key is present in
+* the map, then TRUE is returned, otherwise FALSE is returned. The equals()
+* operation is used to determine equality.
+* @return True if the map contains the given key, false otherwise.
+*/
+ public boolean containsKey( Object key )
+ {
+ return keyToValue.containsKey( key );
+ }
+
+/**
+* Tests to see if the value is contained in the map. If the value is present in
+* the map, then TRUE is returned, otherwise FALSE is returned. The equals()
+* operation is used to determine equality. The value can be null and will
+* return TRUE if null is a value in the map. If TRUE is returned, then there
+* may be more than one values in the map.
+* @return True if the map contains the given value, false otherwise.
+*/
+ public boolean containsValue( Object value )
+ {
+ return keyToValue.containsValue( value );
+ }
+
+/**
+* Returns a set view of the mappings contained in this map. Each element
+* in the returned set is a <tt>Map.Entry</tt>. The returned set is NOT backed
+* by the map, so changes to the map are NOT reflected in the set, and vice-versa.
+* The returned set is independent of this Map and its underlying structure.
+*
+* @return a set view of the mappings contained in this map.
+*/
+ public Set entrySet()
+ {
+ Set result = new HashSet(keys.size(), 1F);
+
+ for ( int i = 0; i < keys.size(); i++ )
+ {
+ result.add( new KeyValuePair( keys.get(i), values.get(i) ) );
+ }
+
+ return result;
+ }
+
+/**
+* Compares the specified object with this map for equality. Returns
+* <tt>true</tt> if the given object is also a map and the two Maps
+* represent the same mappings. More formally, two maps <tt>t1</tt> and
+* <tt>t2</tt> represent the same mappings if
+* <tt>t1.entrySet().equals(t2.entrySet())</tt>. This ensures that the
+* <tt>equals</tt> method works properly across different implementations
+* of the <tt>Map</tt> interface.
+*
+* @param o object to be compared for equality with this map.
+* @return <tt>true</tt> if the specified object is equal to this map.
+*/
+ public boolean equals( Object o )
+ {
+ return keyToValue.equals( o );
+ }
+
+/**
+* Returns the corresponding value for the given key. The returned value may be
+* null as that is a legal value in this map. However, if the key is not
+* contained in this map then null is also returned. Use the containsKey()
+* method to distinguish between the two cases.
+* @param key A key into the map.
+* @return The value corresponding to the key (can be null), or null if the key
+* is not contained in the map.
+*/
+ public Object get( Object key )
+ {
+ return keyToValue.get( key );
+ }
+
+/**
+* Returns the hash code value for this map. The hash code of a map
+* is defined to be the sum of the hashCodes of each entry in the map's
+* entrySet view. This ensures that <tt>t1.equals(t2)</tt> implies
+* that <tt>t1.hashCode()==t2.hashCode()</tt> for any two maps
+* <tt>t1</tt> and <tt>t2</tt>, as required by the general
+* contract of Object.hashCode.
+*
+* @return the hash code value for this map.
+*/
+ public int hashCode()
+ {
+ return keyToValue.hashCode();
+ }
+
+/**
+* Returns true is this map contains no key-value mappings.
+* @return True is this map contains no entries, false otherwise.
+*/
+ public boolean isEmpty()
+ {
+ return keyToValue.isEmpty();
+ }
+
+/**
+* Returns the keys used in the map. There is no order implied in the returned
+* set and may be different than the the order in which they were inserted.
+* @return A Set containing all the keys used in the map.
+*/
+ public Set keySet()
+ {
+ Set result = new HashSet(keys.size(), 1F);
+
+ for ( int i = 0; i < keys.size(); i++ )
+ {
+ result.add( keys.get(i) );
+ }
+
+ return result;
+ }
+
+/**
+* Places the key/value entry into the map at the end position. If the key
+* already exists in the map, then its value is replaced by the new given
+* value. The mapping does not change order in this case. The specified
+* key cannot be null.
+* @param key The key to place into the map, cannot be null.
+* @param value The value to associate with the key, may be null.
+* @return Null if the key is new, the replaced value if the key already
+* existed. (Note: If the key was null, then null is returned.)
+*/
+ public Object put( Object key, Object value )
+ {
+ if ( key == null ) return null;
+
+ if ( keys.contains( key ) )
+ {
+ values.remove( keys.indexOf( key ) );
+ values.add( keys.indexOf( key ), value );
+ }
+ else
+ {
+ values.add( value );
+ keys.add( key );
+ }
+
+ return keyToValue.put( key, value );
+ }
+
+/**
+* Places all the entries from this given map into this map. If the keys
+* already exist, then there values are replaced.
+* @param t A map of key/value entries.
+*/
+ public void putAll( Map t )
+ {
+ if ( t == null )
+ {
+ // Nothing to do!
+ return;
+ }
+
+ // Place the entries from the passed in map into this map.
+ for ( Iterator it = t.keySet().iterator(); it.hasNext(); )
+ {
+ Object aKey = it.next();
+ put( aKey, t.get( aKey ) );
+ }
+ }
+
+/**
+* Remove the mapping of the key and associated value from this map.
+* Note: null is a valid value in the map.
+* @param key A key to remove from the map, cannot be null.
+* @return The value of the removed mapping, null if the mapping did not exist.
+* Null could also be returned if the associated value of the key was null.
+*/
+ public Object remove( Object key )
+ {
+ if ( key == null ) return null;
+
+ Object value = null;
+
+ if ( containsKey( key ) )
+ {
+ value = keyToValue.remove( key );
+ int i = values.indexOf( value );
+ if ( i != -1 )
+ {
+ keys.remove( i );
+ values.remove( i );
+ }
+ }
+
+ return value;
+ }
+
+/**
+* Returns the number of key/value pairs in this map.
+* @return The number of pairs.
+*/
+ public int size()
+ {
+ return values.size();
+ }
+
+/**
+* Returns an ordered list of keys from the map. The order is the same
+* as the added order of the mapped items.<br>
+* NOTE: The returned list is the underlying keys list used by this class.
+* If modification are to be made to this list, it should be cloned first.
+* @return An ordered list of keys.
+*/
+ public List keys()
+ {
+ return keys;
+ }
+
+/**
+* Returns an ordered list of values from the map. The order is the same
+* as the added order of the mapped items.
+* NOTE: The returned list is the underlying keys list used by this class.
+* If modification are to be made to this list, it should be cloned first.
+* @return An ordered list of values.
+*/
+ public Collection values()
+ {
+ return values;
+ }
+
+/**
+* Returns the corresponding value for the given key. The returned value may be
+* null as that is a legal value in this map. However, if the key is not
+* contained in this map then null is also returned. Use the containsKey()
+* method to distinguish between the two cases.
+* @param key A key into the map.
+* @return The value corresponding to the key (can be null), or null if the key
+* is not contained in the map.
+*/
+ public Object getValueForKey( Object key )
+ {
+ return keyToValue.get( key );
+ }
+
+/**
+* Returns the associated key for this value. Since there may be more than one
+* of the same value in the map, this returns the first key associated with this
+* value. Null is returned if the value is not in the map.
+* @param value A value that is contained in this map.
+* @return A first key that corresponds to this value.
+*/
+ public Object getKeyForValue( Object value )
+ {
+ int i = values.indexOf( value );
+ if ( i != -1 )
+ {
+ return keys.get( i );
+ }
+ return null;
+ }
+
+/**
+* Returns the first key in the map. If the map is empty, then null is
+* returned.
+* @return The first key in the map, null if there are no mappings.
+*/
+ public Object getFirstKey()
+ {
+ if ( keys.size() < 1 )
+ {
+ return null;
+ }
+ return keys.get(0);
+ }
+
+/**
+* Returns the last key in the map. If the map is empty, then null is
+* returned.
+* @return The last key in the map, null if there are no mappings.
+*/
+ public Object getLastKey()
+ {
+ if ( keys.size() < 1 )
+ {
+ return null;
+ }
+ return keys.get( keys.size() -1 );
+ }
+
+/**
+* This class contains a key/value pair. The key must be a valid object,
+* it cannot be null. The value can be a valid object or null.
+*/
+ static public class KeyValuePair implements Map.Entry
+ {
+ Object key;
+ Object value;
+
+ /**
+ * Default constructor. The constructor takes the key and value as parameters.
+ * Since the key cannot be null, it must be specified during initialization.
+ * The value can be null.
+ * @param key The key object of this pair. The key cannot be null.
+ * @param value The value object of this pair. The value can be null.
+ */
+ public KeyValuePair( Object aKey, Object aValue )
+ {
+ key = aKey;
+ value = aValue;
+ }
+
+ /**
+ * Returns the key object of this pair.
+ * @return The key object.
+ */
+ public Object getKey()
+ {
+ return key;
+ }
+
+ /**
+ * Returns the the value object of this pair. May be null.
+ * @return The value object.
+ */
+ public Object getValue()
+ {
+ return value;
+ }
+
+ /**
+ * Sets the value object of this pair. May be an object or null.
+ * @param aValue The value object to place into this pair.
+ */
+ public Object setValue( Object aValue )
+ {
+ Object result = value;
+ value = aValue;
+ return result;
+ }
+
+ public boolean equals( Object o )
+ {
+ if ( o instanceof KeyValuePair )
+ {
+ KeyValuePair p = (KeyValuePair) o;
+ if ( ( key.equals( p.getKey() ) ) && ( value.equals( p.getValue() ) ) )
+ {
+ return true;
+ }
+ }
+ return false;
+ }
+ }
+
+ /**
+ * Returns a string reprsentation of this class. The contents of the
+ * map are placed in the string in its proper order.
+ */
+ public String toString()
+ {
+ int max = size() - 1;
+ StringBuffer buf = new StringBuffer();
+
+ buf.append("{");
+ for (int j = 0; j <= max; j++)
+ {
+ buf.append(keys.get(j) + "=" + values.get(j));
+ if (j < max)
+ {
+ buf.append(", ");
+ }
+ }
+ buf.append("}");
+
+ return buf.toString();
+ }
+
+ // unit test
+ public static void main( String[] argv )
+ {
+ QueueMap qMap;
+
+ qMap = new QueueMap();
+ for (int i = 0; i < 5; i++)
+ {
+ qMap.put(Integer.toString(i), Integer.toString(i));
+ }
+ System.out.println("\nMap = " + qMap);
+ System.out.println("Keys = " + qMap.keys());
+ System.out.println("Values = " + qMap.values());
+
+ qMap = new QueueMap();
+ for (int i = 0; i < 5; i++)
+ {
+ qMap.put(Integer.toString(i), "A");
+ }
+ System.out.println("\nMap = " + qMap);
+ System.out.println("Keys = " + qMap.keys());
+ System.out.println("Values = " + qMap.values());
+
+ qMap = new QueueMap();
+ for (int i = 0; i < 5; i++)
+ {
+ qMap.put(Integer.toString(i), null);
+ }
+ System.out.println("\nMap = " + qMap);
+ System.out.println("Keys = " + qMap.keys());
+ System.out.println("Values = " + qMap.values());
+
+ Map aMap = new HashMap();
+ for (int i = 0; i < 5; i++)
+ {
+ aMap.put(Integer.toString(i), Integer.toString(i));
+ }
+ qMap = new QueueMap( aMap );
+ System.out.println("\nHashMap = " + aMap);
+ System.out.println("Map = " + qMap);
+ System.out.println("Keys = " + qMap.keys());
+ System.out.println("Values = " + qMap.values());
+
+ qMap = new QueueMap();
+ qMap.put( "Test1", "String1" );
+ qMap.put( "Test2", "String2" );
+ qMap.put( "Test3", "String3" );
+ qMap.put( "Test4", "String4" );
+ qMap.put( "Test5", "String5" );
+ System.out.println("\nStandard Test, Map = " + qMap);
+ qMap.put( "Test6", "String6" );
+ qMap.put( "Test7", "String7" );
+ System.out.println("Put New Test, Map = " + qMap);
+ qMap.put( "Test2", "New String2" );
+ qMap.put( "Test6", "New String6" );
+ System.out.println("Put Existing Test, Map = " + qMap);
+ qMap.put( "Test5", null );
+ qMap.put( "Test8", null );
+ System.out.println("Put Null Test, Map = " + qMap);
+ qMap.remove( "Test1" );
+ qMap.remove( "Test3" );
+ qMap.remove( "Test5" );
+ qMap.remove( "Test9" );
+ System.out.println("Remove Test, Map = " + qMap);
+ System.out.println(" Keys = " + qMap.keys());
+ System.out.println(" Values = " + qMap.values());
+ qMap.clear();
+ qMap.put( "Test10", "String10" );
+ qMap.put( "Test11", "String11" );
+ qMap.put( "Test12", "String12" );
+ System.out.println("Clear Test, Map = " + qMap);
+
+ aMap = new HashMap();
+ aMap.put( "Test10", "String10" );
+ aMap.put( "Test11", "String11" );
+ aMap.put( "Test12", "String12" );
+ System.out.println("Equality Test, Equal = " + qMap.equals( aMap ));
+ }
+
+}
+
+
diff --git a/projects/net.wotonomy.foundation/src/main/java/net/wotonomy/foundation/internal/URLResourceReader.java b/projects/net.wotonomy.foundation/src/main/java/net/wotonomy/foundation/internal/URLResourceReader.java
new file mode 100644
index 0000000..9133a8d
--- /dev/null
+++ b/projects/net.wotonomy.foundation/src/main/java/net/wotonomy/foundation/internal/URLResourceReader.java
@@ -0,0 +1,207 @@
+/*
+ * ====================================================================
+ *
+ * The Apache Software License, Version 1.1
+ *
+ * Copyright (c) 1999 The Apache Software Foundation. All rights
+ * reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * 3. The end-user documentation included with the redistribution, if
+ * any, must include the following acknowlegement:
+ * "This product includes software developed by the
+ * Apache Software Foundation (http://www.apache.org/)."
+ * Alternately, this acknowlegement may appear in the software itself,
+ * if and wherever such third-party acknowlegements normally appear.
+ *
+ * 4. The names "The Jakarta Project", "Tomcat", and "Apache Software
+ * Foundation" must not be used to endorse or promote products derived
+ * from this software without prior written permission. For written
+ * permission, please contact apache@apache.org.
+ *
+ * 5. Products derived from this software may not be called "Apache"
+ * nor may "Apache" appear in their names without prior written
+ * permission of the Apache Group.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ * ====================================================================
+ */
+
+package net.wotonomy.foundation.internal;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URL;
+import java.util.Enumeration;
+import java.util.Hashtable;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipInputStream;
+
+/**
+ * This implementation of URL Resource Reader assumes 2 types
+ * of base urls. A base url that ends with / is considered a
+ * resource folder, whereas a resource that does not end with
+ * / is considered a zip/jar resource folder.
+ *
+ * If the resource folder happens is a zip/jar archive, the
+ * entries are always cached.
+ * For non-zip base urls, one could specify whether or not it should
+ * be cached.
+ *
+ * @author Harish Prabandham
+ */
+public class URLResourceReader {
+ private Hashtable resourceCache = new Hashtable();
+ private boolean iszip = true;
+ private URL url = null;
+ private boolean cache = true;
+
+ /**
+ * Creates a new URLResourceReader object. You can either give
+ * the URL of the zip/jar file or a base url where to
+ * look for additional resources. If the url ends with
+ * "/" then it is assumed to be a Base URL.
+ * @param The base url to look for the resources.
+ * @param If the base url is not a zip/jar, then true indicates
+ * that entries should be cached, false otherwise.
+ */
+ public URLResourceReader(URL baseurl, boolean cache) throws IOException {
+ this.url = baseurl;
+ this.cache = cache;
+ this.iszip = !url.getFile().endsWith("/");
+ if(this.iszip)
+ this.cache = true;
+ initialize();
+ }
+
+ /**
+ * equivalent to URLResourceReader(baseurl, false)
+ */
+ public URLResourceReader(URL baseurl) throws IOException {
+ this(baseurl, false);
+ }
+
+ /**
+ * Creates a new URLResourceReader object with the given
+ * input stream. The stream is assumed to be a zip/jar
+ * stream.
+ */
+ public URLResourceReader(InputStream is) throws IOException {
+ init(is);
+ }
+
+ private void initialize() throws IOException {
+ if(iszip) {
+ InputStream is = url.openStream();
+ init(is);
+ is.close();
+ }
+ }
+
+ private byte[] readFully(InputStream is) throws IOException {
+ byte[] buf = new byte[1024];
+ int num = 0;
+ ByteArrayOutputStream bout = new ByteArrayOutputStream();
+
+ while( (num = is.read(buf)) != -1) {
+ bout.write(buf, 0, num);
+ }
+
+ return bout.toByteArray();
+ }
+
+ private void init(InputStream is) throws IOException {
+ ZipInputStream zstream = new ZipInputStream(is);
+ ZipEntry entry;
+
+ while( (entry = zstream.getNextEntry()) != null) {
+ byte[] entryData = readFully(zstream);
+ if(cache)
+ resourceCache.put(entry.getName(), entryData);
+ zstream.closeEntry();
+ }
+
+ zstream.close();
+ }
+
+ /**
+ * Returns an Enumeration of all "known" resource names.
+ */
+ public Enumeration getResourceNames() {
+ return resourceCache.keys();
+ }
+
+ /**
+ * Returns an array of bytes read for this resource if the
+ * resource exists. This method blocks until the resource
+ * has been fully read. If the resource does not exist,
+ * this method returns null.
+ */
+ public byte[] getResource(String resource) {
+ // lookup the data in the cache...
+ byte[] data = (byte[]) resourceCache.get(resource);
+ if(data != null) {
+ return data;
+ }
+
+ // if the data was to come from a zip file that we
+ // already read fully & cached , then it is probably
+ // not there.
+ if(iszip) {
+ return null;
+ }
+
+ // Now the only choice left is to make a url connection.
+ try {
+ URL realURL = new URL(url.getProtocol(), url.getHost(),
+ url.getFile() + resource);
+ data = readFully(realURL.openStream());
+ // add it to cache if needed...
+ if(cache)
+ resourceCache.put(resource, data);
+ return data;
+ } catch(Exception e) {
+ return null;
+ }
+ }
+
+ public void close() {
+ resourceCache.clear();
+ resourceCache = null;
+ }
+
+ public String toString() {
+ return url.toString();
+ }
+}
+
+
+
+
+
+
+
+
diff --git a/projects/net.wotonomy.foundation/src/main/java/net/wotonomy/foundation/internal/ValueConverter.java b/projects/net.wotonomy.foundation/src/main/java/net/wotonomy/foundation/internal/ValueConverter.java
new file mode 100644
index 0000000..d6bc797
--- /dev/null
+++ b/projects/net.wotonomy.foundation/src/main/java/net/wotonomy/foundation/internal/ValueConverter.java
@@ -0,0 +1,718 @@
+/*
+Wotonomy: OpenStep design patterns for pure Java applications.
+Copyright (C) 2000 Michael Powers
+
+This library is free software; you can redistribute it and/or
+modify it under the terms of the GNU Lesser General Public
+License as published by the Free Software Foundation; either
+version 2.1 of the License, or (at your option) any later version.
+
+This library is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+Lesser General Public License for more details.
+
+You should have received a copy of the GNU Lesser General Public
+License along with this library; if not, see http://www.gnu.org
+*/
+
+package net.wotonomy.foundation.internal;
+import java.lang.reflect.Array;
+import java.lang.reflect.Constructor;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.Map;
+
+/**
+* A utility class to convert objects to a desired class.
+*
+* @author michael@mpowers.net
+* @author $Author: cgruber $
+* @version $Revision: 893 $
+*/
+public class ValueConverter
+{
+ /**
+ * Returns the specified object as converted to an instance of the
+ * specified class, or null if the conversion could not be performed.
+ */
+ static public Object convertObjectToClass( Object anObject, Class aClass )
+ {
+ if ( aClass == String.class )
+ {
+ return getString( anObject );
+ }
+ if ( aClass == Short.class )
+ {
+ return getShort( anObject );
+ }
+ if ( aClass == short.class )
+ {
+ return getShort( anObject );
+ }
+ if ( aClass == Integer.class )
+ {
+ return getInteger( anObject );
+ }
+ if ( aClass == int.class )
+ {
+ return getInteger( anObject );
+ }
+ if ( aClass == Long.class )
+ {
+ return getLong( anObject );
+ }
+ if ( aClass == long.class )
+ {
+ return getLong( anObject );
+ }
+ if ( aClass == Float.class )
+ {
+ return getFloat( anObject );
+ }
+ if ( aClass == float.class )
+ {
+ return getFloat( anObject );
+ }
+ if ( aClass == Double.class )
+ {
+ return getDouble( anObject );
+ }
+ if ( aClass == double.class )
+ {
+ return getDouble( anObject );
+ }
+ if ( aClass == java.util.Date.class )
+ {
+ return getDate( anObject );
+ }
+ if ( aClass == Boolean.class )
+ {
+ return getBoolean( anObject );
+ }
+ if ( aClass == boolean.class )
+ {
+ return getBoolean( anObject );
+ }
+ if ( aClass == Character.class )
+ {
+ return getCharacter( anObject );
+ }
+ if ( aClass == char.class )
+ {
+ return getCharacter( anObject );
+ }
+ if ( aClass == Byte.class )
+ {
+ return getByte( anObject );
+ }
+ if ( aClass == byte.class )
+ {
+ return getByte( anObject );
+ }
+ if ( Collection.class.isAssignableFrom( aClass ) )
+ {
+ return getCollection( anObject, aClass );
+ }
+ if ( aClass.isArray() )
+ {
+ return getArray( anObject, aClass );
+ }
+
+ return convert( anObject, aClass );
+ }
+
+ /**
+ * Called by convertObjectToClass() when we need to
+ * convert to an unrecognized type.
+ * This implementation scans the constructors of the
+ * specified class for the best fit to the object.
+ * and returns a new instance with that constructor.
+ * Subclasses can override to directly support specific
+ * types.
+ */
+ static protected Object convert( Object anObject, Class aClass )
+ {
+ Constructor[] ctors = aClass.getConstructors();
+
+ Class[] types;
+ for ( int i = 0; i < ctors.length; i++ )
+ {
+ types = ctors[i].getParameterTypes();
+ if ( types.length == 1 )
+ {
+ if ( types[0].equals( anObject.getClass() ) )
+ {
+ try
+ {
+ return ctors[i].newInstance( new Object[] { anObject } );
+ }
+ catch ( Exception exc )
+ {
+ // fall through
+ }
+ }
+ }
+ }
+
+ for ( int i = 0; i < ctors.length; i++ )
+ {
+ types = ctors[i].getParameterTypes();
+ if ( types.length == 1 )
+ {
+ if ( anObject.getClass().isAssignableFrom( types[0] ) )
+ {
+ try
+ {
+ return ctors[i].newInstance( new Object[] { anObject } );
+ }
+ catch ( Exception exc )
+ {
+ // fall through
+ }
+ }
+ }
+ }
+
+ return null;
+ }
+
+ /**
+ * Tries to convert all objects to either Numbers or objects
+ * that will produce a parsable toString result.
+ */
+ static protected Object preprocess( Object anObject )
+ {
+ if ( anObject instanceof Boolean )
+ {
+ if ( ((Boolean)anObject).booleanValue() )
+ {
+ return new Double( 1.0 );
+ }
+ return new Double( 0.0 );
+ }
+
+ if ( anObject instanceof Character )
+ {
+ return anObject.toString();
+ }
+
+ return anObject;
+ }
+
+ static public short getShortValue( Object anObject )
+ {
+ Short result = getShort( anObject );
+ return ( result == null ) ? (short) 0 : result.shortValue();
+ }
+ static public Short getShort( Object anObject )
+ {
+ if ( anObject == null ) return new Short( (short) 0 );
+ if ( "".equals( anObject ) ) return new Short( (short) 0 );
+ if ( anObject instanceof Short ) return (Short) anObject;
+
+ anObject = preprocess( anObject );
+
+ if ( anObject instanceof Number )
+ {
+ return new Short( ((Number)anObject).shortValue() );
+ }
+
+ try
+ {
+ return Short.valueOf( anObject.toString() );
+ }
+ catch ( Exception exc )
+ {
+ return null;
+ }
+ }
+
+ static public int getIntValue( Object anObject )
+ {
+ Integer result = getInteger( anObject );
+ return ( result == null ) ? 0 : result.intValue();
+ }
+ static public Integer getInteger( Object anObject )
+ {
+ if ( anObject == null ) return new Integer( 0 );
+ if ( "".equals( anObject ) ) return new Integer( 0 );
+ if ( anObject instanceof Integer ) return (Integer) anObject;
+
+ anObject = preprocess( anObject );
+
+ if ( anObject instanceof Number )
+ {
+ return new Integer( ((Number)anObject).intValue() );
+ }
+
+ try
+ {
+ return Integer.valueOf( anObject.toString() );
+ }
+ catch ( Exception exc )
+ {
+ return null;
+ }
+ }
+
+ static public long getLongValue ( Object anObject )
+ {
+ Long result = getLong( anObject );
+ return ( result == null ) ? (long) 0 : result.longValue();
+ }
+ static public Long getLong( Object anObject )
+ {
+ if ( anObject == null ) return new Long( 0 );
+ if ( "".equals( anObject ) ) return new Long( 0 );
+ if ( anObject instanceof Long ) return (Long) anObject;
+
+ anObject = preprocess( anObject );
+
+ if ( anObject instanceof Number )
+ {
+ return new Long( ((Number)anObject).longValue() );
+ }
+
+ try
+ {
+ return Long.valueOf( anObject.toString() );
+ }
+ catch ( Exception exc )
+ {
+ return null;
+ }
+ }
+
+ static public double getDoubleValue ( Object anObject )
+ {
+ Double result = getDouble( anObject );
+ return ( result == null ) ? 0.0f : result.doubleValue();
+ }
+ static public Double getDouble( Object anObject )
+ {
+ if ( anObject == null ) return new Double( 0.0 );
+ if ( "".equals( anObject ) ) return new Double( 0 );
+ if ( anObject instanceof Double ) return (Double) anObject;
+
+ anObject = preprocess( anObject );
+
+ if ( anObject instanceof Number )
+ {
+ return new Double( ((Number)anObject).doubleValue() );
+ }
+
+ try
+ {
+ return Double.valueOf( anObject.toString() );
+ }
+ catch ( Exception exc )
+ {
+ return null;
+ }
+ }
+
+ static public float getFloatValue( Object anObject )
+ {
+ Float result = getFloat( anObject );
+ return ( result == null ) ? 0.0f : result.floatValue();
+ }
+ static public Float getFloat( Object anObject )
+ {
+ if ( anObject == null ) return new Float( 0.0 );
+ if ( "".equals( anObject ) ) return new Float( 0.0 );
+ if ( anObject instanceof Float ) return (Float) anObject;
+
+ anObject = preprocess( anObject );
+
+ if ( anObject instanceof Number )
+ {
+ return new Float( ((Number)anObject).floatValue() );
+ }
+
+ try
+ {
+ return Float.valueOf( anObject.toString() );
+ }
+ catch ( Exception exc )
+ {
+ return null;
+ }
+ }
+
+ static public char getCharValue( Object anObject )
+ {
+ Character result = getCharacter( anObject );
+ return ( result == null ) ? (char) 0 : result.charValue();
+ }
+ static public Character getCharacter( Object anObject )
+ {
+ if ( anObject == null ) return new Character( (char) 0 );
+ if ( anObject instanceof Character ) return (Character) anObject;
+
+ anObject = preprocess( anObject );
+
+ if ( anObject instanceof Number )
+ {
+ return new Character( (char) ((Number)anObject).byteValue() );
+ }
+
+ try
+ {
+ return new Character( anObject.toString().charAt( 0 ) );
+ }
+ catch ( Exception exc )
+ {
+ return null;
+ }
+ }
+
+ static public byte getByteValue( Object anObject )
+ {
+ Byte result = getByte ( anObject );
+ return ( result == null ) ? (byte) 0 : result.byteValue();
+ }
+ static public Byte getByte( Object anObject )
+ {
+ if ( anObject == null ) return new Byte( Byte.MIN_VALUE );
+ if ( "".equals( anObject ) ) return new Byte( Byte.MIN_VALUE );
+ if ( anObject instanceof Byte ) return (Byte) anObject;
+
+ anObject = preprocess( anObject );
+
+ if ( anObject instanceof Number )
+ {
+ return new Byte( ((Number)anObject).byteValue() );
+ }
+
+ try
+ {
+ return Byte.decode( anObject.toString() );
+ }
+ catch ( Exception exc )
+ {
+ // fall through
+ }
+
+ try
+ {
+ return Byte.valueOf( anObject.toString() );
+ }
+ catch ( Exception exc )
+ {
+ return null;
+ }
+ }
+
+ /**
+ * Calls getBoolean and converts result to primitive.
+ */
+ static public boolean getBooleanValue( Object anObject )
+ {
+ Boolean result = getBoolean( anObject );
+ return ( result == null ) ? false : result.booleanValue();
+ }
+
+ /**
+ * Numbers equal to zero are true; Strings equal to "yes" are true;
+ * Strings are then passed to the Boolean constructor.
+ * Other values return null.
+ */
+ static public Boolean getBoolean( Object anObject )
+ {
+ if ( anObject instanceof Boolean )
+ {
+ return (Boolean) anObject;
+ }
+ if ( anObject instanceof Number )
+ {
+ return new Boolean( ((Number)anObject).doubleValue() == 0.0 );
+ }
+
+ if ( anObject instanceof String )
+ {
+ if ( anObject.toString().toLowerCase().equals( "yes" ) )
+ {
+ return Boolean.TRUE;
+ }
+ return new Boolean( (String) anObject );
+ }
+
+ return null;
+ }
+
+ /**
+ * Get an appropriate String representation for the
+ * object. Nulls are converted to "null". Date are
+ * formatted according to the current date format.
+ * All else uses toString.
+ */
+ static public String getString( Object anObject )
+ {
+ if ( anObject == null ) return "null";
+ if ( anObject instanceof java.util.Date )
+ {
+ return dateFormat.format( (java.util.Date) anObject );
+ }
+ return anObject.toString();
+ }
+
+ /**
+ * Converts the object into the specified collection class.
+ * If unable to convert in any other way, resorts to creating
+ * a new collection of the specified type containing the
+ * specified object.
+ */
+ static public Collection getCollection( Object anObject, Class aCollectionClass )
+ {
+ if ( anObject == null ) return null;
+ if ( aCollectionClass.isAssignableFrom( anObject.getClass() ) )
+ {
+ return (Collection) anObject;
+ }
+
+ Collection converted = null;
+
+ // convert to collection class
+ if ( anObject instanceof Collection )
+ {
+ converted = (Collection) anObject;
+ }
+ else
+ // try to convert an array
+ if ( anObject.getClass().isArray() )
+ {
+ try
+ {
+ int length = Array.getLength( anObject );
+ converted = new LinkedList();
+ for ( int i = 0; i < length; i++ )
+ {
+ converted.add( Array.get( anObject, i ) );
+ }
+ }
+ catch ( Exception exc )
+ {
+ // try another approach
+ }
+ }
+ else
+ // convert map values to collection and pass through
+ if ( anObject instanceof Map )
+ {
+ converted = ((Map)anObject).values();
+ }
+
+ // fall back on list containing the object
+ if ( converted == null )
+ {
+ converted = new LinkedList();
+ converted.add( anObject );
+ }
+
+ Collection result = null;
+
+ if ( converted != null )
+ {
+ try
+ {
+ // collections required to have the copy constructor.
+ Constructor ctor = aCollectionClass.getConstructor(
+ new Class[] { Collection.class } );
+ result = (Collection) ctor.newInstance( new Object[] { converted } );
+ }
+ catch ( Exception exc )
+ {
+ try
+ {
+ result = new LinkedList();
+ result.addAll( converted );
+ }
+ catch ( Exception exc2 )
+ {
+ // all attempts failed
+ result = null;
+ }
+ }
+ }
+
+ return result;
+ }
+
+ /**
+ * Convert the object to the specified array type.
+ */
+ static public Object getArray( Object anObject, Class anArrayClass )
+ {
+ if ( anObject == null ) return null;
+
+ // try to convert an array
+ if ( anObject.getClass().isArray() )
+ {
+ try
+ {
+ int length = Array.getLength( anObject );
+ Object result = Array.newInstance(
+ anArrayClass.getComponentType(), length );
+ for ( int i = 0; i < length; i++ )
+ {
+ Array.set( result, i, Array.get( anObject, i ) );
+ }
+ return result;
+ }
+ catch ( Exception exc )
+ {
+ // try another approach
+ }
+ }
+ // convert map values to collection and pass through
+ if ( anObject instanceof Map )
+ {
+ anObject = ((Map)anObject).values();
+ }
+ // try to convert a collection
+ if ( anObject instanceof Collection )
+ {
+ try
+ {
+ int length = ((Collection)anObject).size();
+ Object result = Array.newInstance(
+ anArrayClass.getComponentType(), length );
+ Iterator it = ((Collection)anObject).iterator();
+ for ( int i = 0; i < length; i++ )
+ {
+ Array.set( result, i, it.next() );
+ }
+ return result;
+ }
+ catch ( Exception exc )
+ {
+ // try another approach
+ }
+ }
+ // if appropriate type, put the object in a single element array
+ if ( anObject.getClass().equals( anArrayClass.getComponentType() ) )
+ {
+ try
+ {
+ Object result = Array.newInstance(
+ anArrayClass.getComponentType(), 1 );
+ Array.set( result, 0, anObject );
+ return result;
+ }
+ catch ( Exception exc )
+ {
+ // try another approach
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Get an appropriate Date from the given object.
+ */
+ static public java.util.Date getDate( Object anObject )
+ {
+ if ( anObject == null ) return new java.util.Date( 0 );
+ if ( anObject instanceof java.util.Date )
+ return (java.util.Date) anObject;
+
+ if ( anObject instanceof Number )
+ {
+ return new java.util.Date( getLongValue( anObject ) );
+ }
+
+ try
+ {
+ return dateFormat.parse( anObject.toString() );
+ }
+ catch ( Exception exc )
+ {
+ return null;
+ }
+ }
+
+ static private java.text.DateFormat dateFormat =
+ new java.text.SimpleDateFormat();
+ static public java.text.DateFormat getDateFormat()
+ {
+ return dateFormat;
+ }
+ static public void setDateFormat( java.text.DateFormat aDateFormat )
+ {
+ if ( aDateFormat != null )
+ {
+ dateFormat = aDateFormat;
+ }
+ }
+
+ /**
+ * Returns the "inverted" value of the specified object.
+ * Numbers except for chars and bytes are converted to
+ * their negative representation. Chars and bytes are
+ * treated as booleans. String are converted to booleans.
+ * Booleans are converted to their opposite.
+ * All other types return null.
+ */
+ public static Object invert( Object anObject )
+ {
+ if ( anObject == null ) return null;
+ Class aClass = anObject.getClass();
+
+ if ( ( ( anObject instanceof Number )
+ &&! ( anObject instanceof Byte )
+ &&! ( anObject instanceof Character ) )
+ || ( aClass == short.class )
+ || ( aClass == int.class )
+ || ( aClass == long.class )
+ || ( aClass == float.class )
+ || ( aClass == double.class ) )
+ {
+ return convertObjectToClass(
+ new Double( getDoubleValue( anObject ) * -1 ), aClass );
+ }
+
+ Boolean converted = getBoolean( anObject );
+ if ( converted != null )
+ {
+ if ( converted.booleanValue() )
+ {
+ return convertObjectToClass(
+ Boolean.FALSE, anObject.getClass() );
+ }
+ else
+ {
+ return convertObjectToClass(
+ Boolean.TRUE, anObject.getClass() );
+ }
+ }
+
+ return null;
+ }
+}
+
+/*
+ * $Log$
+ * Revision 1.2 2006/02/16 13:11:47 cgruber
+ * Check in all sources in eclipse-friendly maven-enabled packages.
+ *
+ * Revision 1.4 2002/10/11 15:33:53 mpowers
+ * Now supporting "!" to invert the value of a string property.
+ *
+ * Revision 1.3 2001/07/02 16:29:08 mpowers
+ * XMLRPC decoder was relying on ValueConverter to convert LinkedLists into
+ * the appropriate type. This is now implemented in ValueConverter.
+ *
+ * Revision 1.2 2001/03/01 20:36:35 mpowers
+ * Better error handling and better handling of nulls.
+ *
+ * Revision 1.1.1.1 2000/12/21 15:52:33 mpowers
+ * Contributing wotonomy.
+ *
+ * Revision 1.8 2000/12/20 16:25:48 michael
+ * Added log to all files.
+ *
+ *
+ */
+
diff --git a/projects/net.wotonomy.foundation/src/main/java/net/wotonomy/foundation/internal/WotonomyException.java b/projects/net.wotonomy.foundation/src/main/java/net/wotonomy/foundation/internal/WotonomyException.java
new file mode 100644
index 0000000..e2210d0
--- /dev/null
+++ b/projects/net.wotonomy.foundation/src/main/java/net/wotonomy/foundation/internal/WotonomyException.java
@@ -0,0 +1,134 @@
+/*
+Wotonomy: OpenStep design patterns for pure Java applications.
+Copyright (C) 2000 Michael Powers
+
+This library is free software; you can redistribute it and/or
+modify it under the terms of the GNU Lesser General Public
+License as published by the Free Software Foundation; either
+version 2.1 of the License, or (at your option) any later version.
+
+This library is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+Lesser General Public License for more details.
+
+You should have received a copy of the GNU Lesser General Public
+License along with this library; if not, see http://www.gnu.org
+*/
+
+package net.wotonomy.foundation.internal;
+
+import java.io.PrintStream;
+import java.io.PrintWriter;
+
+/**
+* This is a simple RuntimeException that can encapsulate
+* another throwable. Behaves as a normal RuntimeException
+* except that it prints a stack trace of the wrapped
+* throwable if one is specified.
+*
+* Intended to be used anytime you'd
+* report an exception with System.out.println: that is,
+* for debugging and non-user-visible exceptions.
+*
+* @author michael@mpowers.net
+* @author $Author: cgruber $
+* @version $Revision: 893 $
+*/
+
+public class WotonomyException extends RuntimeException
+{
+ protected String message;
+ protected Throwable wrappedThrowable;
+
+ /**
+ * Default constructor.
+ */
+ public WotonomyException()
+ {
+ super();
+ message = null;
+ wrappedThrowable = null;
+ }
+
+ /**
+ * Standard constructor with message.
+ */
+ public WotonomyException( String aMessage )
+ {
+ super( aMessage );
+ message = aMessage;
+ wrappedThrowable = null;
+ }
+
+ /**
+ * Specifies a throwable to wrap.
+ */
+ public WotonomyException( Throwable aThrowable )
+ {
+ super();
+ message = null;
+ wrappedThrowable = aThrowable;
+ }
+
+ /**
+ * Specifies a message and a throwable to wrap.
+ */
+ public WotonomyException( String aMessage, Throwable aThrowable )
+ {
+ super( aMessage );
+ message = aMessage;
+ wrappedThrowable = aThrowable;
+ }
+
+ /**
+ * Returns the wrapped throwable.
+ */
+ public Throwable getWrappedThrowable()
+ {
+ return wrappedThrowable;
+ }
+
+ public void printStackTrace(PrintWriter s)
+ {
+ if ( message != null )
+ {
+ s.println( "Exception: " + message );
+ }
+ if ( wrappedThrowable != null )
+ {
+ wrappedThrowable.printStackTrace( s );
+ return;
+ }
+ super.printStackTrace( s );
+ }
+
+ public void printStackTrace(PrintStream s)
+ {
+ if ( message != null )
+ {
+ s.println( "Exception: " + message );
+ }
+ if ( wrappedThrowable != null )
+ {
+ wrappedThrowable.printStackTrace( s );
+ return;
+ }
+ super.printStackTrace( s );
+ }
+
+ public void printStackTrace()
+ {
+ if ( message != null )
+ {
+ System.err.println( "Exception: " + message );
+ }
+ if ( wrappedThrowable != null )
+ {
+ wrappedThrowable.printStackTrace();
+ return;
+ }
+ super.printStackTrace();
+ }
+
+}
diff --git a/projects/net.wotonomy.foundation/src/main/java/net/wotonomy/foundation/package.html b/projects/net.wotonomy.foundation/src/main/java/net/wotonomy/foundation/package.html
new file mode 100644
index 0000000..c52ca95
--- /dev/null
+++ b/projects/net.wotonomy.foundation/src/main/java/net/wotonomy/foundation/package.html
@@ -0,0 +1,18 @@
+<body>
+<p>
+A set of collections classes that are needed
+by the ui and web packages. These correspond
+to the foundation classes in the OpenStep
+specification.
+</p>
+<p>
+These classes for the most part extend their
+java counterparts, adding methods to be API
+compatible with the OpenStep specification.
+In this way, they retain compatibility with
+the Java Collections APIs.
+</p>
+<p>
+This package has no external dependencies.
+</p>
+</body>
diff --git a/projects/net.wotonomy.foundation/src/main/java/net/wotonomy/foundation/xml/XMLDecoder.java b/projects/net.wotonomy.foundation/src/main/java/net/wotonomy/foundation/xml/XMLDecoder.java
new file mode 100644
index 0000000..64ac4b2
--- /dev/null
+++ b/projects/net.wotonomy.foundation/src/main/java/net/wotonomy/foundation/xml/XMLDecoder.java
@@ -0,0 +1,68 @@
+/*
+Wotonomy: OpenStep design patterns for pure Java applications.
+Copyright (C) 2000 Intersect Software Corporation
+
+This library is free software; you can redistribute it and/or
+modify it under the terms of the GNU Lesser General Public
+License as published by the Free Software Foundation; either
+version 2.1 of the License, or (at your option) any later version.
+
+This library is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+Lesser General Public License for more details.
+
+You should have received a copy of the GNU Lesser General Public
+License along with this library; if not, see http://www.gnu.org
+*/
+
+package net.wotonomy.foundation.xml;
+
+import java.io.InputStream;
+import java.net.URL;
+
+/**
+* Defines an interface for classes that can de-serialize
+* java objects from an XML representation.
+*/
+public interface XMLDecoder
+{
+ /**
+ * Decodes an object from the specified input stream.
+ * @param anInputStream The input stream from which to read.
+ * The stream will be read fully.
+ * @param aDescription A description to accompany error messages
+ * for the stream, typically a file name.
+ * @param aURL A URL against which relative references within the
+ * XML will be resolved.
+ * @return The object that was constructed from the XML content,
+ * or null if no object could be constructed.
+ */
+ public Object decode(
+ InputStream anInputStream, String aDescription, URL aURL );
+}
+
+/*
+ * $Log$
+ * Revision 1.1 2006/02/18 22:21:10 cgruber
+ * Add in simple xml interfaces from net.wotonomy.xml project.
+ * Add cobertura.ser to .cvsignore. God I wish sourceforge would move on this subversion thing...
+ *
+ * Revision 1.1 2006/02/16 13:22:22 cgruber
+ * Check in all sources in eclipse-friendly maven-enabled packages.
+ *
+ * Revision 1.2 2001/02/06 14:34:23 mpowers
+ * Forgot to rename the package declarations.
+ *
+ * Revision 1.1 2001/02/06 14:31:19 mpowers
+ * Moving XML utilities from util to xml package.
+ *
+ * Revision 1.1.1.1 2000/12/21 15:52:33 mpowers
+ * Contributing wotonomy.
+ *
+ * Revision 1.2 2000/12/20 16:25:48 michael
+ * Added log to all files.
+ *
+ *
+ */
+
diff --git a/projects/net.wotonomy.foundation/src/main/java/net/wotonomy/foundation/xml/XMLEncoder.java b/projects/net.wotonomy.foundation/src/main/java/net/wotonomy/foundation/xml/XMLEncoder.java
new file mode 100644
index 0000000..e543bca
--- /dev/null
+++ b/projects/net.wotonomy.foundation/src/main/java/net/wotonomy/foundation/xml/XMLEncoder.java
@@ -0,0 +1,61 @@
+/*
+Wotonomy: OpenStep design patterns for pure Java applications.
+Copyright (C) 2000 Intersect Software Corporation
+
+This library is free software; you can redistribute it and/or
+modify it under the terms of the GNU Lesser General Public
+License as published by the Free Software Foundation; either
+version 2.1 of the License, or (at your option) any later version.
+
+This library is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+Lesser General Public License for more details.
+
+You should have received a copy of the GNU Lesser General Public
+License along with this library; if not, see http://www.gnu.org
+*/
+
+package net.wotonomy.foundation.xml;
+
+import java.io.OutputStream;
+
+/**
+* Defines an interface for classes that can serialize
+* an arbitrary java object into XML format.
+*/
+public interface XMLEncoder
+{
+ /**
+ * Encodes an object to the specified output stream as XML.
+ * @param anObject The object to be serialized to XML format.
+ * @param anOutputStream The output stream to which the object
+ * will be written. The stream will not be flushed nor closed.
+ */
+ public void encode( Object anObject, OutputStream anOutputStream );
+}
+
+/*
+ * $Log$
+ * Revision 1.1 2006/02/18 22:21:10 cgruber
+ * Add in simple xml interfaces from net.wotonomy.xml project.
+ * Add cobertura.ser to .cvsignore. God I wish sourceforge would move on this subversion thing...
+ *
+ * Revision 1.1 2006/02/16 13:22:22 cgruber
+ * Check in all sources in eclipse-friendly maven-enabled packages.
+ *
+ * Revision 1.2 2001/02/06 14:34:23 mpowers
+ * Forgot to rename the package declarations.
+ *
+ * Revision 1.1 2001/02/06 14:31:19 mpowers
+ * Moving XML utilities from util to xml package.
+ *
+ * Revision 1.1.1.1 2000/12/21 15:52:33 mpowers
+ * Contributing wotonomy.
+ *
+ * Revision 1.2 2000/12/20 16:25:48 michael
+ * Added log to all files.
+ *
+ *
+ */
+