From aedc34d55462a75e329bbf342251ff6504cd117e Mon Sep 17 00:00:00 2001 From: Benjamin Culkin Date: Sun, 19 May 2024 17:56:33 -0400 Subject: Initial import from SVN --- .../java/net/wotonomy/test/BindingController.java | 35 ++ .../main/java/net/wotonomy/test/BindingPanel.java | 172 ++++++++ .../src/main/java/net/wotonomy/test/DataKeyID.java | 88 ++++ .../java/net/wotonomy/test/DataObjectStore.java | 483 +++++++++++++++++++++ .../java/net/wotonomy/test/EditController.java | 214 +++++++++ .../src/main/java/net/wotonomy/test/EditPanel.java | 61 +++ .../net/wotonomy/test/InspectorController.java | 137 ++++++ .../src/main/java/net/wotonomy/test/Test.java | 120 +++++ .../java/net/wotonomy/test/TestController.java | 395 +++++++++++++++++ .../java/net/wotonomy/test/TestDataSource.java | 115 +++++ .../src/main/java/net/wotonomy/test/TestMap.java | 164 +++++++ .../main/java/net/wotonomy/test/TestObject.java | 362 +++++++++++++++ .../net/wotonomy/test/TestObjectClassDesc.java | 31 ++ .../java/net/wotonomy/test/TestObjectStore.java | 258 +++++++++++ .../src/main/java/net/wotonomy/test/TestPanel.java | 110 +++++ .../java/net/wotonomy/test/TreeController.java | 285 ++++++++++++ .../net/wotonomy/test/TreeInspectorController.java | 184 ++++++++ .../src/main/java/net/wotonomy/test/TreePanel.java | 39 ++ 18 files changed, 3253 insertions(+) create mode 100644 projects/net.wotonomy.test/src/main/java/net/wotonomy/test/BindingController.java create mode 100644 projects/net.wotonomy.test/src/main/java/net/wotonomy/test/BindingPanel.java create mode 100644 projects/net.wotonomy.test/src/main/java/net/wotonomy/test/DataKeyID.java create mode 100644 projects/net.wotonomy.test/src/main/java/net/wotonomy/test/DataObjectStore.java create mode 100644 projects/net.wotonomy.test/src/main/java/net/wotonomy/test/EditController.java create mode 100644 projects/net.wotonomy.test/src/main/java/net/wotonomy/test/EditPanel.java create mode 100644 projects/net.wotonomy.test/src/main/java/net/wotonomy/test/InspectorController.java create mode 100644 projects/net.wotonomy.test/src/main/java/net/wotonomy/test/Test.java create mode 100644 projects/net.wotonomy.test/src/main/java/net/wotonomy/test/TestController.java create mode 100644 projects/net.wotonomy.test/src/main/java/net/wotonomy/test/TestDataSource.java create mode 100644 projects/net.wotonomy.test/src/main/java/net/wotonomy/test/TestMap.java create mode 100644 projects/net.wotonomy.test/src/main/java/net/wotonomy/test/TestObject.java create mode 100644 projects/net.wotonomy.test/src/main/java/net/wotonomy/test/TestObjectClassDesc.java create mode 100644 projects/net.wotonomy.test/src/main/java/net/wotonomy/test/TestObjectStore.java create mode 100644 projects/net.wotonomy.test/src/main/java/net/wotonomy/test/TestPanel.java create mode 100644 projects/net.wotonomy.test/src/main/java/net/wotonomy/test/TreeController.java create mode 100644 projects/net.wotonomy.test/src/main/java/net/wotonomy/test/TreeInspectorController.java create mode 100644 projects/net.wotonomy.test/src/main/java/net/wotonomy/test/TreePanel.java (limited to 'projects/net.wotonomy.test/src/main/java/net/wotonomy/test') diff --git a/projects/net.wotonomy.test/src/main/java/net/wotonomy/test/BindingController.java b/projects/net.wotonomy.test/src/main/java/net/wotonomy/test/BindingController.java new file mode 100644 index 0000000..be83eb4 --- /dev/null +++ b/projects/net.wotonomy.test/src/main/java/net/wotonomy/test/BindingController.java @@ -0,0 +1,35 @@ +package net.wotonomy.test; + +//import net.wotonomy.foundation.*; +import javax.swing.JDialog; + +import net.wotonomy.ui.EOAssociation; +import net.wotonomy.ui.EODisplayGroup; +import net.wotonomy.ui.swing.TreeAssociation; +import net.wotonomy.ui.swing.util.WindowUtilities; + +/** +* A simple editor panel with a few textfields. +*/ +public class BindingController +{ + public BindingController( EODisplayGroup titlesGroup, EODisplayGroup childGroup ) + { + BindingPanel bindingPanel = new BindingPanel(); + + EOAssociation ta; + ta = new TreeAssociation( bindingPanel.treeChooser, "People" ); + ta.bindAspect( EOAssociation.TitlesAspect, titlesGroup, "lastName" ); + ta.bindAspect( EOAssociation.ChildrenAspect, childGroup, "children" ); + ta.bindAspect( EOAssociation.IsLeafAspect, titlesGroup, "childCount" ); + ta.establishConnection(); + + JDialog d = new JDialog(); + d.getContentPane().add( bindingPanel ); + d.setTitle( "Chooser Panel" ); + d.pack(); + WindowUtilities.cascade( d ); + d.show(); + } + +} diff --git a/projects/net.wotonomy.test/src/main/java/net/wotonomy/test/BindingPanel.java b/projects/net.wotonomy.test/src/main/java/net/wotonomy/test/BindingPanel.java new file mode 100644 index 0000000..624dc37 --- /dev/null +++ b/projects/net.wotonomy.test/src/main/java/net/wotonomy/test/BindingPanel.java @@ -0,0 +1,172 @@ +package net.wotonomy.test; + +import java.awt.BorderLayout; +import java.awt.event.ActionEvent; + +import javax.swing.AbstractAction; +import javax.swing.JPanel; +import javax.swing.UIManager; +import javax.swing.border.EmptyBorder; + +import net.wotonomy.ui.swing.components.ButtonPanel; +import net.wotonomy.ui.swing.components.TreeChooser; + +/** +* BindingPanel is a FileChooser-like panel that +* uses a TreeModel as a data source. It basically +* provides an alternative to JTree for rendering +* and manipulating tree-like data. +*/ +public class BindingPanel extends JPanel +{ + protected TreeChooser treeChooser; + protected ButtonPanel okPanel; + + public BindingPanel() + { + init(); + } + + protected void init() + { + this.setBorder( new EmptyBorder( 10, 10, 10, 10 ) ); + this.setLayout( new BorderLayout( 10, 10 ) ); + + this.add( treeChooser = new TreeChooser(), BorderLayout.CENTER ); + + okPanel = new ButtonPanel( new String[] { "OK", "Cancel" } ); + this.add( okPanel, BorderLayout.SOUTH ); + } + + /** + * Creates a new folder. + */ + protected class NewFolderAction extends AbstractAction + { + protected NewFolderAction() + { + super("New Folder", UIManager.getIcon("FileChooser.newFolderIcon") ); + } + public void actionPerformed(ActionEvent e) + { + } + } + + /** + * Acts on the "home" key event or equivalent event. + */ + protected class GoHomeAction extends AbstractAction + { + protected GoHomeAction() + { + super("Go Home", UIManager.getIcon("FileChooser.homeFolderIcon") ); + } + public void actionPerformed(ActionEvent e) + { + } + } + + protected class ChangeToParentDirectoryAction extends AbstractAction + { + protected ChangeToParentDirectoryAction() + { + super("Go Up", UIManager.getIcon("FileChooser.upFolderIcon") ); + } + public void actionPerformed(ActionEvent e) + { + } + } + + /** + * Responds to an Open or Save request + */ + protected class ApproveSelectionAction extends AbstractAction { + public void actionPerformed(ActionEvent e) + { + } + } + + + /** + * Responds to a cancel request. + */ + protected class CancelSelectionAction extends AbstractAction { + public void actionPerformed(ActionEvent e) + { + } + } + + /** + * Rescans the files in the current directory + */ + protected class UpdateAction extends AbstractAction { + public void actionPerformed(ActionEvent e) + { + } + } + + // + // Renderer for DirectoryComboBox + // +/* + class DirectoryComboBoxRenderer extends DefaultListCellRenderer { + IndentIcon ii = new IndentIcon(); + public Component getListCellRendererComponent(JList list, Object value, + int index, boolean isSelected, + boolean cellHasFocus) { + + super.getListCellRendererComponent(list, value, index, + isSelected, cellHasFocus); + File directory = (File) value; + if(directory == null) { + setText(""); + return this; + } + + String fileName = getFileChooser().getName(directory); + setText(fileName); + + // Find the depth of the directory + int depth = 0; + if(index != -1) { + File f = directory; + while(f.getParent() != null) { + depth++; + f = getFileChooser().getFileSystemView().createFileObject( + f.getParent() + ); + } + } + + Icon icon = getFileChooser().getIcon(directory); + + ii.icon = icon; + ii.depth = depth; + + setIcon(ii); + + return this; + } + } + + final static int space = 10; + class IndentIcon implements Icon { + + Icon icon = null; + int depth = 0; + + public void paintIcon(Component c, Graphics g, int x, int y) { + icon.paintIcon(c, g, x+depth*space, y); + } + + public int getIconWidth() { + return icon.getIconWidth() + depth*space; + } + + public int getIconHeight() { + return icon.getIconHeight(); + } + + } +*/ +} diff --git a/projects/net.wotonomy.test/src/main/java/net/wotonomy/test/DataKeyID.java b/projects/net.wotonomy.test/src/main/java/net/wotonomy/test/DataKeyID.java new file mode 100644 index 0000000..fca6c98 --- /dev/null +++ b/projects/net.wotonomy.test/src/main/java/net/wotonomy/test/DataKeyID.java @@ -0,0 +1,88 @@ +package net.wotonomy.test; + +import net.wotonomy.control.EOGlobalID; +import net.wotonomy.datastore.DataKey; + +/** +* A test implementation of EOGlobalID that +* wraps a DataKey. +* +* @author michael@mpowers.net +* @author $Author: cgruber $ +* @version $Revision: 893 $ +*/ +public class DataKeyID extends EOGlobalID +{ + private DataKey key; + + /** + * Constructor takes a data key. + */ + public DataKeyID( DataKey aKey ) + { + key = aKey; + } + + /** + * Returns the wrapped data key. + */ + public DataKey getKey() + { + return key; + } + + public boolean isTemporary() + { + return false; + } + + public Object clone() + { + return new DataKeyID( (DataKey) key.clone() ); + } + + public String toString() + { + return "[DataKeyID:"+key.toString()+"]"; + } + + public int hashCode() + { + return key.hashCode(); + } + + public boolean equals( Object anObject ) + { + if ( anObject instanceof DataKeyID ) + { + if ( ((DataKeyID)anObject).key.equals( key ) ) + { + return true; + } + } + return false; + } + +} + +/* + * $Log$ + * Revision 1.1 2006/02/16 13:22:22 cgruber + * Check in all sources in eclipse-friendly maven-enabled packages. + * + * Revision 1.3 2001/02/23 23:44:44 mpowers + * Fixes for hashcode to ensure proper key comparison. + * + * Revision 1.2 2001/02/22 20:56:07 mpowers + * Tests of notification handling. + * + * Revision 1.1 2001/02/15 21:14:45 mpowers + * Test suite now using a persistent object store with editing context. + * + * Revision 1.1 2001/02/05 03:45:37 mpowers + * Starting work on EOEditingContext. + * + * + */ + + diff --git a/projects/net.wotonomy.test/src/main/java/net/wotonomy/test/DataObjectStore.java b/projects/net.wotonomy.test/src/main/java/net/wotonomy/test/DataObjectStore.java new file mode 100644 index 0000000..d175f6e --- /dev/null +++ b/projects/net.wotonomy.test/src/main/java/net/wotonomy/test/DataObjectStore.java @@ -0,0 +1,483 @@ +package net.wotonomy.test; + +import java.util.Iterator; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; + +import net.wotonomy.control.ArrayFault; +import net.wotonomy.control.EOEditingContext; +import net.wotonomy.control.EOFetchSpecification; +import net.wotonomy.control.EOGlobalID; +import net.wotonomy.control.EOObjectStore; +import net.wotonomy.control.EOObserverCenter; +import net.wotonomy.control.KeyValueCodingUtilities; +import net.wotonomy.datastore.DataKey; +import net.wotonomy.datastore.DataSoup; +import net.wotonomy.datastore.DataView; +import net.wotonomy.datastore.XMLFileSoup; +import net.wotonomy.foundation.NSArray; +import net.wotonomy.foundation.NSMutableArray; +import net.wotonomy.foundation.NSMutableDictionary; +import net.wotonomy.foundation.NSNotification; +import net.wotonomy.foundation.NSNotificationQueue; +import net.wotonomy.foundation.internal.WotonomyException; + +/** +* An object store that wraps a datastore for vending test objects. +*/ +public class DataObjectStore extends EOObjectStore +{ + DataSoup soup; + + /** + * Constructor specifies path to datastore. + */ + public DataObjectStore( String aPath ) + { + soup = new XMLFileSoup( aPath ); + } + + /** + * This implementation returns an appropriately configured array fault. + */ + public NSArray arrayFaultWithSourceGlobalID ( + EOGlobalID aGlobalID, + String aRelationship, + EOEditingContext aContext ) + { + return new ArrayFault( + aGlobalID, aRelationship, aContext ); + } + + /** + * This implementation returns the actual + * object for the specified id. + */ + public Object faultForGlobalID ( + EOGlobalID aGlobalID, + EOEditingContext aContext ) + { +System.out.println( "DataObjectStore.faultForGlobalID: * reading object * : " + aGlobalID ); + Object result = soup.getObjectByKey( + ((DataKeyID)aGlobalID).getKey() ); + + if ( result == null ) return null; + + //! transpose keys to objects + convertRelationKeysToObjects( aContext, result, aGlobalID ); + //! + + aContext.recordObject( result, aGlobalID ); + return result; + } + + /** + * Returns a fault representing an object of + * the specified entity type with values from + * the specified dictionary. The fault should + * belong to the specified editing context. + */ + public Object faultForRawRow ( + Map aDictionary, + String anEntityName, + EOEditingContext aContext ) + { + //TODO: faults are not yet supported + throw new WotonomyException( + "Faults are not yet supported." ); + } + + /** + * Given a newly instantiated object, this method + * initializes its properties to values appropriate + * for the specified id. The object should belong + * to the specified editing context. + * This method is called to populate faults. + */ + public void initializeObject(Object anObject, EOGlobalID aGlobalID, + EOEditingContext aContext) { + if (aGlobalID.isTemporary()) { + // TODO: this should never happen, but it does now until we get + // faults. + + // do not reinit an uncommitted object + return; + } + + System.out.println("DataObjectStore.initializeObject: * reading object * : " + + aGlobalID); + //net.wotonomy.ui.swing.util.StackTraceInspector.printShortStackTrace(); + Object original = soup.getObjectByKey(((DataKeyID) aGlobalID).getKey()); + + // ! transpose keys to objects + convertRelationKeysToObjects(aContext, original, aGlobalID); + // ! + EOObserverCenter.notifyObserversObjectWillChange(anObject); + KeyValueCodingUtilities.copy(aContext, original, aContext, anObject); + } + + /** + * Remove all values from all objects in memory, turning them into faults, + * and posts a notification that all objects have been invalidated. + */ + public void invalidateAllObjects () + { + // does nothing except post notification + + NSNotificationQueue.defaultQueue().enqueueNotification( + new NSNotification( + InvalidatedAllObjectsInStoreNotification, this ), + NSNotificationQueue.PostNow ); + } + + /** + * Removes values with the specified ids from memory, + * turning them into faults, and posts a notification + * that those objects have been invalidated. + */ + public void invalidateObjectsWithGlobalIDs ( + List aList ) + { + // does nothing + } + + /** + * Returns false because locking is not permitted. + */ + public boolean isObjectLockedWithGlobalID ( + EOGlobalID aGlobalID, + EOEditingContext aContext ) + { + return false; + } + + /** + * Does nothing because locking is not permitted. + */ + public void lockObjectWithGlobalID ( + EOGlobalID aGlobalID, + EOEditingContext aContext ) + { + // does nothing + } + + /** + * Returns a List of objects associated with the object + * with the specified id for the specified property + * relationship. This method may not return an array fault + * because array faults call this method to fetch on demand. + * All objects must be registered the specified editing context. + * The specified relationship key must produce a result of + * type Collection for the source object or an exception is thrown. + */ + public NSArray objectsForSourceGlobalID ( + EOGlobalID aGlobalID, + String aRelationship, + EOEditingContext aContext ) + { + System.out.println( "DataObjectStore.objectsForSourceGlobalID: * reading object * : " + aGlobalID ); + Object object = soup.getObjectByKey(((DataKeyID)aGlobalID).getKey() ); + + if ( object == null ) return null; + + Object fault; + EOGlobalID id; + NSMutableArray result = new NSMutableArray(); + + Iterator it = ((TestObject)object).getChildList().iterator(); + while ( it.hasNext() ) + { + id = new DataKeyID((DataKey)it.next()); + fault = aContext.faultForGlobalID( id, aContext ); + + // if key still exists + if ( fault != null ) + { +//System.out.println( "objectsForSourceGlobalID: found: " + id + " : " + fault ); + result.add( fault ); + +// for testing purposes +((TestObject)fault).setParent( (TestObject) object ); + } + else // key no longer exists + { + // do not add +System.out.println( "objectsForSourceGlobalID: could not find fault for id: " + id ); + } + } + return result; + + } + + /** + * Returns a List of objects the meet the criteria of + * the supplied specification. + * Each object is registered with the specified editing context. + * If any object is already registered in the specified context, + * it is not refetched and that object should be used in the array. + */ + public NSArray objectsWithFetchSpecification ( + EOFetchSpecification aFetchSpec, + EOEditingContext aContext ) + { + //TODO: fetch specs are not yet supported + + DataView view = soup.queryObjects( null, null ); +System.out.println( "DataObjectStore: ** querying all objects **" ); + + // we've changed this implementation so that + // it simply calls faultForGlobalID on the context + // for each id in the result set. + // this way, child contexts inherit parent's state. + // however, it's unclear if the specification allows + // faults in the resulting array. sounds like it doesn't. + NSMutableArray result = new NSMutableArray(); + DataKeyID id; + Iterator it = view.iterator(); + while ( it.hasNext() ) + { + id = new DataKeyID( view.getKeyForObject( it.next() ) ); + result.addObject( aContext.faultForGlobalID( id, aContext ) ); + } + return result; + } + + /** + * Removes all values from the specified object, + * converting it into a fault for the specified id. + * New or deleted objects should not be refaulted. + */ + public void refaultObject ( + Object anObject, + EOGlobalID aGlobalID, + EOEditingContext aContext ) + { + //TODO: faults are not yet supported + // just re-initialize the object + initializeObject( anObject, aGlobalID, aContext ); + } + + /** + * Writes all changes in the specified editing context + * to the respository. + */ + public void saveChangesInEditingContext ( + EOEditingContext aContext ) + { + Object o; + DataKeyID id; + Iterator it; + + // process deletes + it = aContext.deletedObjects().iterator(); + while ( it.hasNext() ) + { + o = it.next(); + id = (DataKeyID) aContext.globalIDForObject( o ); +System.out.println( "DataObjectStore: * deleting object * : " + id ); + soup.removeObject( id.getKey() ); + // remove object from editing context + aContext.forgetObject( o ); + } + + // process inserts + NSMutableDictionary userInfo = null; + it = aContext.insertedObjects().iterator(); + while ( it.hasNext() ) + { + o = it.next(); + EOGlobalID oldId = aContext.globalIDForObject( o ); + + //! transpose objects to keys + convertRelationObjectsToKeys( aContext, (TestObject) o ); + id = new DataKeyID( soup.addObject( o ) ); + convertRelationKeysToObjects( aContext, (TestObject) o, oldId ); + //! + +System.out.println( "DataObjectStore: * adding object * : " + id ); + + // save mapping of old id to new id + if ( userInfo == null ) + { + userInfo = new NSMutableDictionary(); + } + userInfo.setObjectForKey( id, oldId ); + } + + // broadcast inserted objects' new ids if necessary + if ( userInfo != null ) + { + NSNotificationQueue.defaultQueue().enqueueNotification( + new NSNotification( + EOGlobalID.GlobalIDChangedNotification, null, userInfo ), + NSNotificationQueue.PostNow ); + } + + System.out.println( aContext.updatedObjects() ); + + // process updates + it = aContext.updatedObjects().iterator(); + while ( it.hasNext() ) + { +//if ( true ) // test validation error message handling +//throw new RuntimeException( "Update not allowed." ); + o = it.next(); + id = (DataKeyID) aContext.globalIDForObject( o ); +System.out.println( "DataObjectStore: * updating object * : " + id ); + + //! transpose objects to keys + convertRelationObjectsToKeys( aContext, (TestObject) o ); + soup.updateObject( id.getKey(), o ); + convertRelationKeysToObjects( aContext, (TestObject) o, id ); + //! + + } + } + + private void convertRelationKeysToObjects( + EOEditingContext aContext, Object anObject, EOGlobalID aGlobalID ) + { // System.out.println( "convertRelationKeysToObjects: " + anObject ); +// set editing context for testing +((TestObject)anObject).editingContext = aContext; + + Object fault; + DataKeyID id; + List result = new LinkedList(); + Iterator it = ((TestObject)anObject).getChildList().iterator(); + while ( it.hasNext() ) + { + id = new DataKeyID((DataKey)it.next()); + fault = aContext.faultForGlobalID( id, aContext ); + + // if key still exists + if ( fault != null ) + { +//System.out.println( "convertRelationObjectsToKeys: found: " + id + " : " + fault ); + result.add( fault ); + +// for testing purposes +((TestObject)fault).setParent( (TestObject) anObject ); + } + else // key no longer exists + { + // do not add +System.out.println( "convertRelationObjectsToKeys: could not find fault for id: " + id ); + } + } + // this tests loading manually on-demand +// ((TestObject)anObject).setChildList( null ); + // this tests loading immediately + ((TestObject)anObject).setChildList( result ); + // this tests loading array faults +// ((TestObject)result).setChildList( null ); + ((TestObject)anObject).setChildList( + aContext.arrayFaultWithSourceGlobalID( + aGlobalID, "childList", aContext ) ); + + } + + private void convertRelationObjectsToKeys( + EOEditingContext aContext, Object anObject ) + { // System.out.println( "convertRelationObjectsToKeys: " + anObject ); + Object o; + DataKeyID id; + List result = new LinkedList(); + Iterator it = ((TestObject)anObject).getChildList().iterator(); +// for testing purposes +((TestObject)anObject).setParent( null ); +((TestObject)anObject).editingContext = null; + while ( it.hasNext() ) + { + o = it.next(); +//System.out.println( "convertRelationObjectsToKeys: " + o + " : " + aContext.globalIDForObject( o ) ); + id = (DataKeyID)aContext.globalIDForObject( o ); + + // if object still exists in context + if ( id != null ) + { + result.add( id.getKey() ); + } + else // object was deleted + { + // do not add +System.out.println( "convertRelationObjectsToKeys: could not find id for object: " + o ); +System.out.println( aContext.registeredObjects() ); + } + + } + ((TestObject)anObject).setChildList( result ); + } + + +/* + * $Log$ + * Revision 1.1 2006/02/19 16:30:25 cgruber + * Update imports and maven dependencies. + * + * Revision 1.1 2006/02/16 13:18:56 cgruber + * Check in all sources in eclipse-friendly maven-enabled packages. + * + * Revision 1.18 2002/03/11 03:18:39 mpowers + * Now properly handling ObserverChangesLater. + * + * Revision 1.17 2001/10/26 18:39:44 mpowers + * Posting notifications immediately, rather than delayed. + * + * Revision 1.16 2001/05/06 18:27:10 mpowers + * More broadly catching editing contexts for now. + * + * Revision 1.15 2001/05/05 23:05:43 mpowers + * Implemented Array Faults. + * + * Revision 1.14 2001/05/05 15:00:06 mpowers + * Tested load-on-demand: still works. + * Now using registerClone for consistency. + * Editing context is temporarily posting notification on objectWillChange. + * + * Revision 1.13 2001/05/04 23:24:30 mpowers + * Changes to test code. + * + * Revision 1.12 2001/05/04 16:57:56 mpowers + * Now correctly transposing references to editing contexts when + * cloning/copying between editing contexts. + * + * Revision 1.11 2001/05/02 17:33:28 mpowers + * More changes for testing. + * + * Revision 1.10 2001/04/30 13:15:24 mpowers + * Child contexts re-initializing objects invalidated in parent now + * propery transpose relationships. + * + * Revision 1.9 2001/04/29 22:02:45 mpowers + * Work on id transposing between editing contexts. + * + * Revision 1.8 2001/04/29 02:29:31 mpowers + * Debugging relationship faulting. + * + * Revision 1.7 2001/04/28 22:17:51 mpowers + * Revised PropertyDataSource to be EOClassDescription-aware. + * + * Revision 1.6 2001/04/28 16:18:44 mpowers + * Implementing relationships. + * + * Revision 1.5 2001/04/13 16:33:36 mpowers + * Now broadcasting notifications. + * + * Revision 1.4 2001/04/08 21:00:54 mpowers + * Changes to support new objectsForFetchSpecification scheme. + * + * Revision 1.3 2001/03/22 21:37:52 mpowers + * Testing new features. + * + * Revision 1.2 2001/03/15 21:10:41 mpowers + * Implemented global id re-registration for newly saved inserts. + * + * Revision 1.1 2001/03/05 22:12:11 mpowers + * Created the control package for a datastore-specific implementation + * of EOObjectStore. + * + * + */ +} + diff --git a/projects/net.wotonomy.test/src/main/java/net/wotonomy/test/EditController.java b/projects/net.wotonomy.test/src/main/java/net/wotonomy/test/EditController.java new file mode 100644 index 0000000..b304ade --- /dev/null +++ b/projects/net.wotonomy.test/src/main/java/net/wotonomy/test/EditController.java @@ -0,0 +1,214 @@ +package net.wotonomy.test; + +import java.awt.BorderLayout; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; +import java.awt.event.WindowAdapter; +import java.awt.event.WindowEvent; + +import javax.swing.AbstractButton; +import javax.swing.BorderFactory; +import javax.swing.JDialog; +import javax.swing.JFrame; + +import net.wotonomy.control.ChildDataSource; +import net.wotonomy.control.EODataSource; +import net.wotonomy.control.PropertyDataSource; +import net.wotonomy.foundation.NSArray; +import net.wotonomy.ui.EOAssociation; +import net.wotonomy.ui.EODisplayGroup; +import net.wotonomy.ui.MasterDetailAssociation; +import net.wotonomy.ui.swing.DisplayGroupActionAssociation; +import net.wotonomy.ui.swing.ListAssociation; +import net.wotonomy.ui.swing.RadioPanelAssociation; +import net.wotonomy.ui.swing.TextAssociation; +import net.wotonomy.ui.swing.components.ButtonPanel; +import net.wotonomy.ui.swing.util.WindowUtilities; + +/** +* A simple editor panel with a few textfields. +*/ +public class EditController +{ + EODisplayGroup group; + JDialog dialog; + + public EditController( EODataSource aDataSource ) + { + EditPanel editPanel = new EditPanel(); + editPanel.infoPanel.setBorder( + BorderFactory.createCompoundBorder( + BorderFactory.createRaisedBevelBorder(), + BorderFactory.createEmptyBorder( 10, 10, 10, 10 ) ) ); + editPanel.setBorder( + BorderFactory.createEmptyBorder( 0, 0, 0, 0 ) ); + ButtonPanel okPanel = new ButtonPanel( + new String[] { "Revert", "Refault", "Refresh", "Commit" } ); + editPanel.add( okPanel, BorderLayout.SOUTH ); + + group = new EODisplayGroup(); + group.setDataSource( aDataSource ); + group.fetch(); + group.selectNext(); + + // text associations + + EOAssociation ta; + + ta = new TextAssociation( editPanel.firstNameField ); + ta.bindAspect( EOAssociation.ValueAspect, group, "firstName" ); + ta.establishConnection(); + + ta = new TextAssociation( editPanel.middleNameField ); + ta.bindAspect( EOAssociation.ValueAspect, group, "middleName" ); + ta.establishConnection(); + + ta = new TextAssociation( editPanel.lastNameField ); + ta.bindAspect( EOAssociation.ValueAspect, group, "lastName" ); + ta.establishConnection(); + + // radio panels + + ta = new RadioPanelAssociation( editPanel.yearRadioPanel ); + ta.bindAspect( EOAssociation.ValueAspect, group, "createDate.year" ); + + EODisplayGroup yearTitles = new EODisplayGroup(); + yearTitles.setObjectArray( new NSArray( + new Object[] { "1999", "2000", "2001" } ) ); + ta.bindAspect( EOAssociation.TitlesAspect, yearTitles, "" ); + + EODisplayGroup yearObjects = new EODisplayGroup(); + yearObjects.setObjectArray( new NSArray( + new Object[] { new Integer( 99 ), new Integer( 100 ), new Integer( 101 ) } ) ); + ta.bindAspect( EOAssociation.ObjectsAspect, yearObjects, "" ); + + ta.establishConnection(); + + // detail group + + final EODisplayGroup detailGroup = new EODisplayGroup(); + detailGroup.setDataSource( new PropertyDataSource( + aDataSource.editingContext(), TestObject.class ) ); + + ta = new MasterDetailAssociation( detailGroup ); + ta.bindAspect( EOAssociation.ParentAspect, group, "childList" ); + ta.establishConnection(); + + ta = new ListAssociation( editPanel.list ); + ta.bindAspect( EOAssociation.TitlesAspect, detailGroup, "fullName" ); + ta.establishConnection(); + + // display group action associations + + AbstractButton button = (AbstractButton) + editPanel.addPanel.getButton( "Add" ); + button.addActionListener( new ActionListener() + { + public void actionPerformed( ActionEvent evt ) + { + detailGroup.insertNewObjectAtIndex( 0 ); + } + } ); + + ta = new DisplayGroupActionAssociation( + editPanel.addPanel.getButton( "Remove" ) ); + ta.bindAspect( EOAssociation.ActionAspect, detailGroup, "deleteSelection" ); + ta.establishConnection(); + + // ok / cancel buttons + + button = (AbstractButton) + okPanel.getButton( "Commit" ); + button.addActionListener( new ActionListener() + { + public void actionPerformed( ActionEvent evt ) + { + group.dataSource().editingContext().saveChanges(); + } + } ); + + button = (AbstractButton) + okPanel.getButton( "Refresh" ); + button.addActionListener( new ActionListener() + { + public void actionPerformed( ActionEvent evt ) + { + group.dataSource().editingContext().invalidateAllObjects(); + } + } ); + + button = (AbstractButton) + okPanel.getButton( "Refault" ); + button.addActionListener( new ActionListener() + { + public void actionPerformed( ActionEvent evt ) + { +/* + Object o = group.displayedObjects().objectAtIndex( 0 ); + group.dataSource().editingContext().refaultObject( + o, + group.dataSource().editingContext().globalIDForObject( o ), + group.dataSource().editingContext() ); +*/ + group.dataSource().editingContext().revert(); + group.dataSource().editingContext().refaultObjects(); + } + } ); + + button = (AbstractButton) + okPanel.getButton( "Revert" ); + button.addActionListener( new ActionListener() + { + public void actionPerformed( ActionEvent evt ) + { + group.dataSource().editingContext().revert(); + } + } ); + + // add mouse listener for list + + editPanel.list.addMouseListener( new MouseAdapter() + { + public void mouseClicked(MouseEvent e) + { + if ( e.getClickCount() == 2 ) + { + Object item = detailGroup.selectedObject(); + if ( item != null ) + { +// new InspectorController( item ); + + new EditController( + new ChildDataSource( + group.dataSource(), item ) ); + } + } + } + }); + + // launch + + dialog = new JDialog(); + // add WindowListener for frame + dialog.setDefaultCloseOperation( JFrame.DISPOSE_ON_CLOSE ); + dialog.getContentPane().add( editPanel ); + dialog.setTitle( "Edit Panel" ); + dialog.pack(); +// dialog.setSize( 300, dialog.getSize().height ); + WindowUtilities.cascade( dialog ); + dialog.show(); + + // workaround for memory issues on jdk1.2.2 + dialog.addWindowListener( new WindowAdapter() + { + // exit on close + public void windowClosing(WindowEvent e) + { + ((JDialog)e.getWindow()).getContentPane().removeAll(); + } + }); + } + +} diff --git a/projects/net.wotonomy.test/src/main/java/net/wotonomy/test/EditPanel.java b/projects/net.wotonomy.test/src/main/java/net/wotonomy/test/EditPanel.java new file mode 100644 index 0000000..63b1317 --- /dev/null +++ b/projects/net.wotonomy.test/src/main/java/net/wotonomy/test/EditPanel.java @@ -0,0 +1,61 @@ +package net.wotonomy.test; + +import java.awt.BorderLayout; +import java.awt.FlowLayout; + +import javax.swing.JList; +import javax.swing.JPanel; +import javax.swing.JScrollPane; +import javax.swing.JTextField; +import javax.swing.border.EmptyBorder; +import javax.swing.text.JTextComponent; + +import net.wotonomy.ui.swing.components.ButtonPanel; +import net.wotonomy.ui.swing.components.InfoPanel; +import net.wotonomy.ui.swing.components.RadioButtonPanel; + +/** +* A simple editor panel with a few textfields. +*/ +public class EditPanel extends JPanel +{ + public JTextComponent firstNameField; + public JTextField middleNameField, lastNameField; + public RadioButtonPanel yearRadioPanel; + public InfoPanel infoPanel; + public JList list; + public ButtonPanel addPanel; + + + public EditPanel() + { + this.setLayout( new BorderLayout() ); + this.setBorder( new EmptyBorder( 10, 10, 10, 10 ) ); + + infoPanel = new InfoPanel(); + + // name fields + firstNameField = new JTextField(); + infoPanel.addPair( "First Name", firstNameField ); + middleNameField = new JTextField(); + infoPanel.addPair( "Middle Name", middleNameField ); + lastNameField = new JTextField(); + infoPanel.addPair( "Last Name", lastNameField ); + yearRadioPanel = new RadioButtonPanel(); + infoPanel.addPair( "Year", yearRadioPanel ); + + list = new JList(); + JPanel containerPanel = new JPanel(); + containerPanel.setLayout( new BorderLayout( 0, 5 ) ); + JScrollPane scrollPane = new JScrollPane( list ); + scrollPane.setPreferredSize( new java.awt.Dimension( 100, 100 ) ); + addPanel = new ButtonPanel( new String[] { "Add", "Remove" } ); + addPanel.setAlignment( FlowLayout.CENTER ); + containerPanel.add( scrollPane, BorderLayout.CENTER ); + containerPanel.add( addPanel, BorderLayout.SOUTH ); + infoPanel.addRow( "Children", containerPanel ); + + this.add( infoPanel, BorderLayout.CENTER ); + } + +} diff --git a/projects/net.wotonomy.test/src/main/java/net/wotonomy/test/InspectorController.java b/projects/net.wotonomy.test/src/main/java/net/wotonomy/test/InspectorController.java new file mode 100644 index 0000000..58e2d9a --- /dev/null +++ b/projects/net.wotonomy.test/src/main/java/net/wotonomy/test/InspectorController.java @@ -0,0 +1,137 @@ +package net.wotonomy.test; + +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; +import java.awt.event.WindowAdapter; +import java.awt.event.WindowEvent; + +import javax.swing.AbstractButton; +import javax.swing.JDialog; + +import net.wotonomy.foundation.NSArray; +import net.wotonomy.ui.EOAssociation; +import net.wotonomy.ui.EODisplayGroup; +import net.wotonomy.ui.MasterDetailAssociation; +import net.wotonomy.ui.swing.DisplayGroupActionAssociation; +import net.wotonomy.ui.swing.ListAssociation; +import net.wotonomy.ui.swing.RadioPanelAssociation; +import net.wotonomy.ui.swing.TextAssociation; +import net.wotonomy.ui.swing.util.WindowUtilities; + +/** +* A simple editor panel with a few textfields. +*/ +public class InspectorController +{ + public InspectorController( Object o ) + { + EditPanel editPanel = new EditPanel(); + + EODisplayGroup group = new EODisplayGroup(); + group.setDataSource( new TestDataSource() ); + group.setObjectArray( new NSArray( o ) ); + group.selectNext(); + + // text associations + + EOAssociation ta; + + ta = new TextAssociation( editPanel.firstNameField ); + ta.bindAspect( EOAssociation.ValueAspect, group, "firstName" ); + ta.establishConnection(); + + ta = new TextAssociation( editPanel.middleNameField ); + ta.bindAspect( EOAssociation.ValueAspect, group, "middleName" ); + ta.establishConnection(); + + ta = new TextAssociation( editPanel.lastNameField ); + ta.bindAspect( EOAssociation.ValueAspect, group, "lastName" ); + ta.establishConnection(); + + // radio panels + + ta = new RadioPanelAssociation( editPanel.yearRadioPanel ); + ta.bindAspect( EOAssociation.ValueAspect, group, "createDate.year" ); + + EODisplayGroup yearTitles = new EODisplayGroup(); + yearTitles.setObjectArray( new NSArray( + new Object[] { "1999", "2000", "2001" } ) ); + ta.bindAspect( EOAssociation.TitlesAspect, yearTitles, "" ); + + EODisplayGroup yearObjects = new EODisplayGroup(); + yearObjects.setObjectArray( new NSArray( + new Object[] { new Integer( 99 ), new Integer( 100 ), new Integer( 101 ) } ) ); + ta.bindAspect( EOAssociation.ObjectsAspect, yearObjects, "" ); + + ta.establishConnection(); + + // detail group + + final EODisplayGroup detailGroup = new EODisplayGroup(); + + ta = new MasterDetailAssociation( detailGroup ); + ta.bindAspect( EOAssociation.ParentAspect, group, "childList" ); + ta.establishConnection(); + + ta = new ListAssociation( editPanel.list ); + ta.bindAspect( EOAssociation.TitlesAspect, detailGroup, "fullName" ); + ta.establishConnection(); + + // display group action associations + + AbstractButton button = (AbstractButton) + editPanel.addPanel.getButton( "Add" ); + button.addActionListener( new ActionListener() + { + public void actionPerformed( ActionEvent evt ) + { + detailGroup.insertNewObjectAtIndex( 0 ); + } + } ); + + ta = new DisplayGroupActionAssociation( + editPanel.addPanel.getButton( "Remove" ) ); + ta.bindAspect( EOAssociation.ActionAspect, detailGroup, "deleteSelection" ); + ta.establishConnection(); + + // add mouse listener for list + + editPanel.list.addMouseListener( new MouseAdapter() + { + public void mouseClicked(MouseEvent e) + { + if ( e.getClickCount() == 2 ) + { + Object item = detailGroup.selectedObject(); + if ( item != null ) + { + new InspectorController( item ); + } + } + } + }); + + // launch + + JDialog dialog = new JDialog(); + dialog.getContentPane().add( editPanel ); + dialog.setTitle( "Inspector Panel" ); + dialog.pack(); + dialog.setSize( 300, dialog.getSize().height ); + WindowUtilities.cascade( dialog ); + dialog.show(); + + // workaround for memory issues on jdk1.2.2 + dialog.addWindowListener( new WindowAdapter() + { + // exit on close + public void windowClosing(WindowEvent e) + { + ((JDialog)e.getWindow()).getContentPane().removeAll(); + } + }); + } + +} diff --git a/projects/net.wotonomy.test/src/main/java/net/wotonomy/test/Test.java b/projects/net.wotonomy.test/src/main/java/net/wotonomy/test/Test.java new file mode 100644 index 0000000..ba6a1dc --- /dev/null +++ b/projects/net.wotonomy.test/src/main/java/net/wotonomy/test/Test.java @@ -0,0 +1,120 @@ +package net.wotonomy.test; + +import java.awt.BorderLayout; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.awt.event.KeyEvent; +import java.awt.event.WindowAdapter; +import java.awt.event.WindowEvent; + +import javax.swing.JFrame; +import javax.swing.JMenu; +import javax.swing.JMenuBar; +import javax.swing.JMenuItem; +import javax.swing.KeyStroke; +import javax.swing.UIManager; + +import net.wotonomy.control.EOEditingContext; +import net.wotonomy.control.EOObjectStore; + +/** +* A simple test-bed for wotonomy. +* Shows a JFrame containing the TestPanel +* which is controlled by the TestController. +*/ +public class Test +{ + static EOObjectStore objectStore; + static EOEditingContext editingContext; + static public void main( String[] argv ) + { +// NSRunLoop.currentRunLoop(); + + // system l&f + try + { +// UIManager.setLookAndFeel("com.sun.java.swing.plaf.motif.MotifLookAndFeel"); + UIManager.setLookAndFeel("com.sun.java.swing.plaf.windows.WindowsLookAndFeel"); +// UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName()); +// UIManager.setLookAndFeel(UIManager.getCrossPlatformLookAndFeelClassName()); + } + catch (Exception e) + { + // no system l&f - fail silently + } + + // launch notification monitor if desired + for ( int i = 0; i < argv.length; i++ ) + { + if ( argv[i].indexOf( "monitor" ) != -1 ) + { + new net.wotonomy.ui.swing.NotificationInspector(); + } + } + new net.wotonomy.ui.swing.NotificationInspector(); + + // set up editing context hierarchy + objectStore = new DataObjectStore( "data" ); + editingContext = new EOEditingContext( objectStore ); + + // connect panel to controller + TestPanel testPanel = new TestPanel(); + final TestController controller = new TestController( testPanel ); + + // create frame and show + JFrame frame = new JFrame(); + frame.setDefaultCloseOperation( JFrame.DISPOSE_ON_CLOSE ); + + // setup menus + JMenu menu; + JMenuItem menuItem; + JMenuBar menuBar = new JMenuBar(); + menu = new JMenu( "File" ); + menu.add( "New" ); + menuItem = new JMenuItem( "Save" ); + menuItem.setAccelerator( KeyStroke.getKeyStroke( KeyEvent.VK_S, KeyEvent.CTRL_MASK ) ); + menuItem.addActionListener( new ActionListener() + { + public void actionPerformed( ActionEvent evt ) + { + controller.displayGroup.dataSource().editingContext().saveChanges(); + } + }); + + menu.add( menuItem ); + menu.add( "Close" ); + menuBar.add( menu ); + menu = new JMenu( "Edit" ); + menu.add( "Cut" ); + menu.add( "Copy" ); + menu.add( "Paste" ); + menuBar.add( menu ); + frame.setJMenuBar( menuBar ); + + frame.getContentPane().add( testPanel, BorderLayout.CENTER ); + frame.setTitle( "Test Frame" ); + frame.setBounds( 50, 50, 750, 500 ); + frame.show(); // comment out this to avoid memory leak from jdk1.2.2 bug + + // add WindowListener for frame + frame.addWindowListener( new WindowAdapter() + { + // exit on close + public void windowClosing(WindowEvent e) + { + System.exit( 0 ); + } + }); + + /* uncomment this to avoid memory leak from jdk1.2.2 bug + frame.getContentPane().removeAll(); + */ +/* + NSNotificationCenter.defaultCenter().addObserver( + frame, + new NSSelector( "hitMe", new Class[] { NSNotification.class } ), + null, null ); +*/ + } + +} diff --git a/projects/net.wotonomy.test/src/main/java/net/wotonomy/test/TestController.java b/projects/net.wotonomy.test/src/main/java/net/wotonomy/test/TestController.java new file mode 100644 index 0000000..8bbb452 --- /dev/null +++ b/projects/net.wotonomy.test/src/main/java/net/wotonomy/test/TestController.java @@ -0,0 +1,395 @@ +package net.wotonomy.test; + +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; +import java.text.DateFormat; +import java.util.Iterator; +import java.util.LinkedList; +import java.util.List; + +import javax.swing.AbstractButton; +import javax.swing.Icon; +import javax.swing.JButton; +import javax.swing.JComponent; +import javax.swing.JOptionPane; +import javax.swing.UIManager; +import javax.swing.table.TableColumn; + +import net.wotonomy.control.ChildDataSource; +import net.wotonomy.control.EOEditingContext; +import net.wotonomy.control.EOGlobalID; +import net.wotonomy.control.EOKeyValueQualifier; +import net.wotonomy.control.EOQualifier; +import net.wotonomy.foundation.NSArray; +import net.wotonomy.foundation.NSNotification; +import net.wotonomy.ui.EOAssociation; +import net.wotonomy.ui.EODisplayGroup; +import net.wotonomy.ui.swing.ButtonAssociation; +import net.wotonomy.ui.swing.ComboBoxAssociation; +import net.wotonomy.ui.swing.DisplayGroupActionAssociation; +import net.wotonomy.ui.swing.SliderAssociation; +import net.wotonomy.ui.swing.TableColumnAssociation; +import net.wotonomy.ui.swing.TextAssociation; +import net.wotonomy.ui.swing.TreeColumnAssociation; +import net.wotonomy.ui.swing.components.AlternatingRowCellRenderer; +import net.wotonomy.ui.swing.components.FormattedCellRenderer; +import net.wotonomy.ui.swing.components.IconCellRenderer; +import net.wotonomy.ui.swing.components.KeyableCellEditor; + +/** +* Controller for the TestPanel. +*/ +public class TestController implements ActionListener +{ + EODisplayGroup displayGroup; + TestPanel panel; + + public TestController( TestPanel aPanel ) + { + panel = aPanel; + + // setup display group + + displayGroup = new EODisplayGroup(); + displayGroup.setSortOrderings( new NSArray( new Object[] + { "firstName", "middleName", "lastName" } ) ); + + // fetch the data +// displayGroup.setUsesOptimisticRefresh( true ); + displayGroup.setDataSource( new TestDataSource() ); +// displayGroup.setSelectsFirstObjectAfterFetch( true ); + displayGroup.fetch(); + displayGroup.selectNext(); + +displayGroup.setDelegate( this ); + + // set up associations + + EOAssociation assoc; + + // table association + + TableColumn column; + + column = new TableColumn(); + column.setHeaderValue( "First" ); + IconCellRenderer iconRenderer = new IconCellRenderer() + { + private Icon icon = UIManager.getIcon("FileChooser.homeFolderIcon"); + public Icon getIconForContext( + JComponent container, Object value, + int row, int col, + boolean isSelected, boolean hasFocus, + boolean isExpanded, boolean isLeaf ) + { + return icon; + } + }; + iconRenderer.addActionListener( this ); + column.setCellRenderer( new AlternatingRowCellRenderer( iconRenderer ) ); +// column.setCellEditor( iconRenderer ); +// assoc = new TableColumnAssociation( column ); + assoc = new TreeColumnAssociation( column ); + assoc.bindAspect( EOAssociation.ValueAspect, displayGroup, "firstName" ); +//new net.wotonomy.ui.swing.DisplayGroupInspector( displayGroup ); +//displayGroup = new EODisplayGroup(); +// assoc.bindAspect( EOAssociation.ChildrenAspect, displayGroup, "childList" ); + assoc.bindAspect( EOAssociation.EditableAspect, null, "true" ); + assoc.bindAspect( EOAssociation.IsLeafAspect, null, "childCount" ); + ((TableColumnAssociation)assoc).setTable( panel.table ); + assoc.establishConnection(); + ((TreeColumnAssociation)assoc).getTreeModelAssociation().setInsertingAfter( false ); + ((TreeColumnAssociation)assoc).getTreeModelAssociation().setInsertingChild( false ); + +// column.setCellRenderer( new AlternatingRowCellRenderer( column.getCellRenderer() ) ); + +/* + // test the standalone mode of the icon cell renderer + panel.add( iconRenderer, java.awt.BorderLayout.SOUTH ); + iconRenderer.setText( "Hello World!" ); + iconRenderer.setIcon( UIManager.getIcon("FileChooser.homeFolderIcon") ); +*/ + + column = new TableColumn(); + column.setHeaderValue( "Middle" ); + column.setCellRenderer( new AlternatingRowCellRenderer() ); + assoc = new TableColumnAssociation( column ); + ((TableColumnAssociation)assoc).setSortCaseSensitive( true ); + assoc.bindAspect( EOAssociation.ValueAspect, displayGroup, "middleName" ); + ((TableColumnAssociation)assoc).setTable( panel.table ); + assoc.establishConnection(); + + column = new TableColumn(); + column.setHeaderValue( "Last" ); + column.setCellRenderer( new AlternatingRowCellRenderer() ); + column.setCellEditor( new KeyableCellEditor() ); + assoc = new TableColumnAssociation( column ); + assoc.bindAspect( EOAssociation.ValueAspect, displayGroup, "lastName" ); + assoc.bindAspect( EOAssociation.EditableAspect, null, "true" ); + ((TableColumnAssociation)assoc).setTable( panel.table ); + assoc.establishConnection(); + + column = new TableColumn(); + column.setHeaderValue( "Created" ); + FormattedCellRenderer renderer = new FormattedCellRenderer(); + renderer.setFormat( DateFormat.getDateInstance() ); + column.setCellRenderer( new AlternatingRowCellRenderer( renderer ) ); + assoc = new TableColumnAssociation( column ); + assoc.bindAspect( EOAssociation.ValueAspect, displayGroup, "createDate" ); + ((TableColumnAssociation)assoc).setTable( panel.table ); + assoc.establishConnection(); + + column = new TableColumn(); + column.setHeaderValue( "Special" ); + column.setCellRenderer( new AlternatingRowCellRenderer( + panel.table.getDefaultRenderer( Boolean.class ) ) ); + assoc = new TableColumnAssociation( column ); + assoc.bindAspect( EOAssociation.ValueAspect, displayGroup, "special" ); + assoc.bindAspect( EOAssociation.EditableAspect, null, "true" ); + ((TableColumnAssociation)assoc).setTable( panel.table ); + assoc.establishConnection(); + + // text associations + + assoc = new TextAssociation( panel.firstNameField ); + assoc.bindAspect( EOAssociation.ValueAspect, displayGroup, "firstName" ); +//EODisplayGroup controllerDisplayGroup = new EODisplayGroup(); +//controllerDisplayGroup.setObjectArray( new NSArray( this ) ); +//controllerDisplayGroup.selectNext(); +//assoc.bindAspect( EOAssociation.ValueAspect, controllerDisplayGroup, "filter" ); + assoc.establishConnection(); + + assoc = new TextAssociation( panel.middleNameField ); + assoc.bindAspect( EOAssociation.ValueAspect, displayGroup, "middleName" ); + assoc.establishConnection(); + + assoc = new TextAssociation( panel.lastNameField ); + assoc.bindAspect( EOAssociation.ValueAspect, displayGroup, "lastName" ); + assoc.bindAspect( EOAssociation.LabelAspect, displayGroup, "special" ); + assoc.establishConnection(); + + assoc = new ButtonAssociation( panel.checkbox ); + assoc.bindAspect( EOAssociation.ValueAspect, displayGroup, "special" ); +// assoc.bindAspect( EOAssociation.EnabledAspect, displayGroup, "special" ); +// assoc.bindAspect( EOAssociation.VisibleAspect, displayGroup, "special" ); + assoc.establishConnection(); + + // combo associations + + assoc = new ComboBoxAssociation( panel.dateBox ); + assoc.bindAspect( EOAssociation.ValueAspect, displayGroup, "createDate.date" ); + // no titles aspect: uses existing combobox options + assoc.establishConnection(); + + assoc = new ComboBoxAssociation( panel.monthBox ); + assoc.bindAspect( EOAssociation.ValueAspect, displayGroup, "createDate.month" ); + EODisplayGroup monthTitlesGroup = new EODisplayGroup(); + monthTitlesGroup.setObjectArray( new NSArray( + new Object[] { "January", "February", "March", "April", "May", "June", + "July", "August", "September", "October", "November", "December" } ) ); + assoc.bindAspect( EOAssociation.TitlesAspect, monthTitlesGroup, "" ); + + EODisplayGroup monthObjectsGroup = new EODisplayGroup(); + monthObjectsGroup.setObjectArray( new NSArray( + new Object[] { new Integer( 0 ), + new Integer( 1 ), new Integer( 2 ), new Integer( 3 ), + new Integer( 4 ), new Integer( 5 ), new Integer( 6 ), + new Integer( 7 ), new Integer( 8 ), new Integer( 9 ), + new Integer( 10 ), new Integer( 11 ) } ) ); + assoc.bindAspect( EOAssociation.ObjectsAspect, monthObjectsGroup, "" ); + + assoc.establishConnection(); + + + assoc = new ComboBoxAssociation( panel.yearBox ); + assoc.bindAspect( EOAssociation.ValueAspect, displayGroup, "createDate.year" ); + + EODisplayGroup yearTitlesGroup = new EODisplayGroup(); + yearTitlesGroup.setObjectArray( new NSArray( + new Object[] { "1999", "2000", "2001" } ) ); + assoc.bindAspect( EOAssociation.TitlesAspect, yearTitlesGroup, "" ); + + EODisplayGroup yearObjectsGroup = new EODisplayGroup(); + yearObjectsGroup.setObjectArray( new NSArray( + new Object[] { new Integer( 99 ), new Integer( 100 ), new Integer( 101 ) } ) ); + assoc.bindAspect( EOAssociation.ObjectsAspect, yearObjectsGroup, "" ); + + assoc.establishConnection(); + + assoc = new SliderAssociation( panel.slider ); + assoc.bindAspect( EOAssociation.ValueAspect, displayGroup, "createDate.date" ); + assoc.bindAspect( EOAssociation.VisibleAspect, displayGroup, "special" ); + assoc.establishConnection(); + + assoc = new TextAssociation( panel.infoPanel.getLabelForKey( "Day of Month" ) ); + assoc.bindAspect( EOAssociation.VisibleAspect, displayGroup, "special" ); + assoc.establishConnection(); + + // display group action associations + AbstractButton button; + + button = (AbstractButton) + panel.savePanel.getButton( "Refresh All" ); + button.addActionListener( new ActionListener() + { + public void actionPerformed( ActionEvent evt ) + { // panel.lastNameField.setText( panel.lastNameField.getText().trim() ); + + // test all three ways + + displayGroup.dataSource().editingContext().invalidateAllObjects(); +// displayGroup.dataSource().editingContext().parentObjectStore().invalidateAllObjects(); +// displayGroup.dataSource().editingContext().revert(); + } + } ); + + button = (AbstractButton) + panel.savePanel.getButton( "Commit" ); + button.addActionListener( new ActionListener() + { + public void actionPerformed( ActionEvent evt ) + { + try + { + displayGroup.dataSource().editingContext().saveChanges(); + } + catch ( RuntimeException exc ) + { + JOptionPane.showMessageDialog( + (java.awt.Component)evt.getSource(), exc.getMessage() ); + exc.printStackTrace(); + } + } + } ); + + button = (AbstractButton) + panel.buttonPanel.getButton( "Add" ); + button.addActionListener( new ActionListener() + { + public void actionPerformed( ActionEvent evt ) + { + displayGroup.insertNewObjectAtIndex( 0 ); + } + } ); + + assoc = new DisplayGroupActionAssociation( + panel.buttonPanel.getButton( "Remove" ) ); + assoc.bindAspect( EOAssociation.ActionAspect, displayGroup, "deleteSelection" ); + assoc.establishConnection(); +/* + assoc = new DisplayGroupActionAssociation( + panel.infoPanel.getButtonPanel().getButton( "Refresh" ) ); + assoc.bindAspect( EOAssociation.ActionAspect, displayGroup, "updateDisplayedObjects" ); + assoc.establishConnection(); + + assoc = new DisplayGroupActionAssociation( + panel.infoPanel.getButtonPanel().getButton( "Commit" ) ); + assoc.bindAspect( EOAssociation.ActionAspect, displayGroup, "updateDisplayedObjects" ); + assoc.establishConnection(); +*/ + // add MouseListener for table + panel.table.addMouseListener( new MouseAdapter() + { + public void mouseClicked(MouseEvent e) + { + if ( e.getClickCount() == 2 ) + { + Object o = displayGroup.selectedObject(); + if ( o != null ) + { +// new InspectorController( o ); + new EditController( + new ChildDataSource( + displayGroup.dataSource(), o ) ); + } + } + } + }); + + // add ActionListener for tree button + ((JButton) panel.buttonPanel.getButton( + "Tree View" ) ).addActionListener( new ActionListener() + { + public void actionPerformed( ActionEvent evt ) + { + EOEditingContext parentContext = Test.editingContext; + EOEditingContext childContext = new EOEditingContext( parentContext ); + + // transpose objects to ids to faults + List ids = new LinkedList(); + Iterator i = displayGroup.selectedObjects().iterator(); + while ( i.hasNext() ) + { + ids.add( parentContext.globalIDForObject( i.next() ) ); + } + List objects = new LinkedList(); + i = ids.iterator(); + while( i.hasNext() ) + { + objects.add( childContext.faultForGlobalID( (EOGlobalID) i.next(), childContext ) ); + } + + EODisplayGroup treeGroup = new EODisplayGroup(); + treeGroup.setSortOrderings( new NSArray( "lastName" ) ); + treeGroup.setObjectArray( objects ); + + EODisplayGroup childGroup = new EODisplayGroup(); + + //childGroup.setDelegate( new DebuggingDelegate() ); + //new TreeInspectorController( treeGroup, childGroup ); + //new BindingController( treeGroup, childGroup ); + + new TreeController( childContext, treeGroup, childGroup ); + + //NOTE: ChildDataSource is fundamentally broken +// new TreeController( new ChildDataSource( +// displayGroup.dataSource(), displayGroup.selectedObjects() ) ); + } + }); + +/* + NSNotificationCenter.defaultCenter().addObserver( + this, + new NSSelector( "hitMe", new Class[] { NSNotification.class } ), + null, null ); +*/ + } + + private String filter; + public String getFilter() + { + return filter; + } + + public void setFilter( String aFilter ) + { + filter = aFilter; + + EOQualifier qualifier = null; + if ( ! "".equals( aFilter ) ) + { + qualifier = new EOKeyValueQualifier( + "firstName", EOQualifier.QualifierOperatorContains, filter ); + } + displayGroup.setQualifier( qualifier ); + displayGroup.updateDisplayedObjects(); + } + + public void hitMe( NSNotification aNote ) + { + System.out.println( aNote ); + } + + public void actionPerformed( ActionEvent evt ) + { + Object o = displayGroup.selectedObject(); + if ( o != null ) + { +// new ObjectInspector( o ); + new InspectorController( o ); + } + } + +} diff --git a/projects/net.wotonomy.test/src/main/java/net/wotonomy/test/TestDataSource.java b/projects/net.wotonomy.test/src/main/java/net/wotonomy/test/TestDataSource.java new file mode 100644 index 0000000..1d36bef --- /dev/null +++ b/projects/net.wotonomy.test/src/main/java/net/wotonomy/test/TestDataSource.java @@ -0,0 +1,115 @@ +package net.wotonomy.test; + +import net.wotonomy.control.EOClassDescription; +import net.wotonomy.control.EODataSource; +import net.wotonomy.control.EOEditingContext; +import net.wotonomy.control.EOFetchSpecification; +import net.wotonomy.foundation.NSArray; + +/** +* A custom DataSource that works with +* the datastore package for persistence. +*/ +public class TestDataSource extends EODataSource +{ + private EOEditingContext context; + private Object source; + private String key; + + public TestDataSource() + { + this( Test.editingContext ); + } + + public TestDataSource( EOEditingContext aContext ) + { + context = aContext; + } + + public EOEditingContext editingContext() + { + return context; + } + + /** + * This implementation does nothing. + */ + public void insertObject ( Object anObject ) + { + // creates are handled by createObject(). + } + + /** + * Deletes the specified object from this data source. + */ + public void deleteObject ( Object anObject ) + { + editingContext().deleteObject( anObject ); + } + + /** + * Returns a List containing the objects in this + * data source. This implementation returns all + * TestObjects that have been persisted to the + * datastore in the data directory. + */ + public NSArray fetchObjects () + { + if ( source == null ) + { + NSArray result = editingContext().objectsWithFetchSpecification( + new EOFetchSpecification() ); + if ( result.size() > 0 ) + { + result = new NSArray( result.objectAtIndex( 0 ) ); +//result.add( result.objectAtIndex( 0 ) ); + } + return result; + } + else + { + return new NSArray( + ((TestObject)source).getChildList() ); + } + } + + /** + * Returns a data source that is capable of + * manipulating objects of the type returned by + * applying the specified key to objects + * vended by this data source. + * @see #qualifyWithRelationshipKey + */ + public EODataSource + dataSourceQualifiedByKey ( String aKey ) + { + return new TestDataSource( editingContext() ); + } + + /** + * Restricts this data source to vend those + * objects that are associated with the specified + * key on the specified object. + */ + public void + qualifyWithRelationshipKey ( + String aKey, Object anObject ) + { + key = aKey; + source = anObject; + } + + /** + * Returns the description of the class of the + * objects that is vended by this data source, + * or null if this cannot be determined. + * This implementation returns TestObject. + */ + public EOClassDescription + classDescriptionForObjects () + { + return EOClassDescription.classDescriptionForClass( + TestObject.class ); + } + +} diff --git a/projects/net.wotonomy.test/src/main/java/net/wotonomy/test/TestMap.java b/projects/net.wotonomy.test/src/main/java/net/wotonomy/test/TestMap.java new file mode 100644 index 0000000..8a88e68 --- /dev/null +++ b/projects/net.wotonomy.test/src/main/java/net/wotonomy/test/TestMap.java @@ -0,0 +1,164 @@ +package net.wotonomy.test; + +import java.util.Date; +import java.util.HashMap; +import java.util.LinkedList; +import java.util.List; +import java.util.Random; +import java.util.StringTokenizer; + +import net.wotonomy.datastore.DataSoup; +import net.wotonomy.datastore.SerializedFileSoup; +import net.wotonomy.datastore.XMLFileSoup; +import net.wotonomy.foundation.internal.ValueConverter; + +public class TestMap extends HashMap +{ + public TestMap() + { + put( "date", new Date() ); + put( "firstName", randomParse( + "Bert|Ernie|Elmo|Zoe|Arthur|Emily|DJ|Grover|Oscar|Max|Big|Twinkle") ); + put( "middleName", new StringBuffer( randomParse( + "Rufus|Remy|Martin|Josephus|Ulysses|Homer|Bart|Tip|Onegin|Meredith|Jay") ) ); + put( "lastName", randomParse( + "Alejandro|Alexander|Bird|Gosling|Joy|Van Hoff|Pedia|Marr|McNealy|Ping") ); + put( "address", randomParse( "1|2|3|4" ) + randomParse( "0|1|00|10|5|50" ) + + randomParse( "0|00|1|01|5|05|9|09||000" ) + " " + randomParse( + "Merry|Berry|Perry|Jerry|Meadow|Falls|Elm|Raspberry|Strawberry") + " " + + randomParse( "Road|Lane|Court|Drive|Parkway|Terrace" ) ); + put( "city", randomParse( + "Springfield|Sterling|Cascades|Vienna|Reston|Paris|London|Runnymeade") ); + put( "state", randomParse( + "TX|NJ|NY|VA|DC|MD|NC|SC|WV|AR|FL|CA|TN" ) ); + put( "zip", ValueConverter.getInteger( + randomParse( "1|2|3|4" ) + "0" + randomParse( "0|1|2|3|5" ) + + randomParse( "6|7|8|9" ) + randomParse( "6|7|8|9" ) ) ); + put( "age", new Short( (short) ( new Random().nextDouble() * 40 + 18 ) ) ); + childCount = -1; + } + + protected int childCount; + public int getChildCount() + { + if ( childCount == -1 ) + { + //childCount = (int) ( random.nextDouble() * 6 ) - 3; // + 100; // tree scalability test + if ( childCount < 0 ) childCount = 0; + } + return childCount; + }; + + protected TestMap[] children; + public TestMap[] getChildren() + { + if ( get( "children" ) == null ) + { + int n = getChildCount(); + TestMap[] children = new TestMap[ n ]; + for ( int i = 0; i < n; i++ ) + { + children[i] = new TestMap(); + } + put( "children", children ); + } + return (TestMap[]) get( "children" ); + } + public void setChildren( TestMap[] aChildArray ) + { + put( "children", aChildArray ); + } + public List getChildList() + { + List result = new LinkedList(); + TestMap[] childArray = getChildren(); + for ( int i = 0; i < childArray.length; i++ ) + { + result.add( childArray[i] ); + } + return result; + } + public void setChildList( List aChildList ) + { + TestMap[] children = new TestMap[ aChildList.size() ]; + for ( int i = 0; i < children.length; i++ ) + { + children[i] = (TestMap) aChildList.get( i ); + } + setChildren( children ); + } + + public String getFullName() + { + return get( "firstName" ) + " " + get( "middleName" ) + " " + get( "lastName" ); + } + + public boolean equals( Object anObject ) + { + return anObject == this; + } + + public String toString() + { + return "[" + getClass().getName() + ":" + getFullName() + "]"; + } + + // statics + + private static Random random = new Random(); + private static String randomParse( String aString ) + { + String result = ""; + StringTokenizer tokens = new StringTokenizer( aString, "|" ); + int n = (int) ( random.nextDouble() * tokens.countTokens() ); + for ( int i = 0; i <= n; i++ ) + { + result = tokens.nextToken(); + } + return result; + } + + public static void main( String[] argv ) + { + int count = 100; + boolean xmlMode = false; + if ( argv.length > 0 ) + { + Integer parsed = ValueConverter.getInteger( argv[0] ); + if ( parsed != null ) count = parsed.intValue(); + + if ( argv.length > 1 ) + { + if ( argv[1].indexOf( "xml" ) > -1 ) + { + xmlMode = true; + } + } + } + +long millis = System.currentTimeMillis(); + + DataSoup store = null; + if ( xmlMode ) + { + store = new XMLFileSoup( "testMaps-xml" ); + } + else + { + store = new SerializedFileSoup( "testMaps-java" ); + } + + Object o; + for ( int i = 0; i < count; i++ ) + { + store.addObject( new TestMap() ); + } + /* + store.addIndex( "age", "age" ); + store.addIndex( "zipCode", "zipCode" ); + store.addIndex( "firstName", "firstName" ); + store.addIndex( "lastName", "lastName" ); +*/ +System.out.println( System.currentTimeMillis() - millis + " milliseconds" ); + } +} diff --git a/projects/net.wotonomy.test/src/main/java/net/wotonomy/test/TestObject.java b/projects/net.wotonomy.test/src/main/java/net/wotonomy/test/TestObject.java new file mode 100644 index 0000000..72a3dbc --- /dev/null +++ b/projects/net.wotonomy.test/src/main/java/net/wotonomy/test/TestObject.java @@ -0,0 +1,362 @@ +package net.wotonomy.test; + +import java.io.Serializable; +import java.util.Date; +import java.util.LinkedList; +import java.util.List; +import java.util.Random; +import java.util.StringTokenizer; + +import net.wotonomy.control.EOEditingContext; +import net.wotonomy.control.EOKeyValueCodingSupport; +import net.wotonomy.datastore.DataSoup; +import net.wotonomy.datastore.SerializedFileSoup; +import net.wotonomy.datastore.XMLFileSoup; +import net.wotonomy.foundation.internal.ValueConverter; + +public class TestObject implements Serializable // , EOKeyValueCoding +{ + static final long serialVersionUID = -5482454640042392838L; + +// for testing manual array faulting +public EOEditingContext editingContext; +public EOEditingContext getEditingContext() { return editingContext; }; + + public TestObject() + { + date = new Date(); + firstName = randomParse( + "Bert|Ernie|Elmo|Zoe|Arthur|Emily|DJ|Grover|Oscar|Max|Big|Twinkle"); + middleName = new StringBuffer( randomParse( + "Rufus|Remy|Martin|Josephus|Ulysses|Homer|Bart|Tip|Onegin|Meredith|Jay") ); + lastName = randomParse( + "Alejandro|Alexander|Bird|Gosling|Joy|Van Hoff|Pedia|Marr|McNealy|Ping"); + address = randomParse( "1|2|3|4" ) + randomParse( "0|1|00|10|5|50" ) + + randomParse( "0|00|1|01|5|05|9|09||000" ) + " " + randomParse( + "Merry|Berry|Perry|Jerry|Meadow|Falls|Elm|Raspberry|Strawberry") + " " + + randomParse( "Road|Lane|Court|Drive|Parkway|Terrace" ); + city = randomParse( + "Springfield|Sterling|Cascades|Vienna|Reston|Paris|London|Runnymeade"); + state = randomParse( + "TX|NJ|NY|VA|DC|MD|NC|SC|WV|AR|FL|CA|TN" ); + zip = ValueConverter.getIntValue( + randomParse( "1|2|3|4" ) + "0" + randomParse( "0|1|2|3|5" ) + + randomParse( "6|7|8|9" ) + randomParse( "6|7|8|9" ) ); + age = (short) ( new Random().nextDouble() * 40 + 18 ); + childCount = -1; +// children = null; + childList = null; + } + + protected Date date; + public Date getCreateDate() { return date; } + public void setCreateDate( Date aDate ) { date = aDate; } + + protected String firstName; + public String getFirstName() { return firstName; } + public void setFirstName( String aName ) { firstName = aName; } + + protected String lastName; + public String getLastName() { return lastName; } + public void setLastName( String aName ) { + if ( "Jones".equals( aName ) ) throw new RuntimeException( "Jones not allowed" ) ; + lastName = aName; + } + + protected StringBuffer middleName; + public StringBuffer getMiddleName() { return middleName; } + public void setMiddleName( StringBuffer aName ) { middleName = aName; } + + protected String address; + public String getAddress() { return address; } + public void setAddress( String anAddress ) { address = anAddress; } + + protected String city; + public String getCity() { return city; } + public void setCity( String aCity ) { city = aCity; } + + protected String state; + public String getState() { return state; } + public void setState( String aState ) { state = aState; } + + protected int zip; + public int getZipCode() { return zip; } + public void setZipCode( int aZipCode ) { zip = aZipCode; } + + protected short age; + public short getAge() { return age; } + public void setAge( short anAge ) { age = anAge; } + + protected boolean special; + public Boolean isSpecial() { return new Boolean( special ); } + public void setSpecial( Boolean isSpecial ) { special = isSpecial.booleanValue(); } + +/* + protected Object[] children; + + private Object[] getChildren() + { + if ( children == null ) + { + int n = getChildCount(); + children = new Object[ n ]; + for ( int i = 0; i < n; i++ ) + { + children[i] = new TestObject(); + } + //System.out.println( "TestObject.getChildren: " + toString() + " : " + getChildCount() ); + } + return children; + } + private void setChildren( Object[] aChildArray ) + { + children = aChildArray; + childCount = aChildArray.length; + } + + // following child list implementation wraps child array + + public List getChildList() + { + List result = new LinkedList(); + Object[] childArray = getChildren(); + for ( int i = 0; i < childArray.length; i++ ) + { + result.add( childArray[i] ); + } + return result; + } + public void setChildList( List aChildList ) + { + children = new Object[ aChildList.size() ]; + for ( int i = 0; i < children.length; i++ ) + { + children[i] = (TestObject) aChildList.get( i ); + } + childCount = children.length; + } +*/ + protected int childCount; + public int getChildCount() + { + if ( childCount == -1 ) + { +// uncomment this to enable random children +// childCount = (int) ( random.nextDouble() * 6 ) - 3; // + 100; // tree scalability test + if ( childCount < 0 ) childCount = 0; + } + +// this tests internal count +// return childCount; +// this tests deferred count + if ( childList != null ) + { + return childList.size(); + } + else + { + return 0; + } + }; + + // following child list implementation stands alone + + protected List childList; + public List getChildList() + { +/* + // this tests random child population + if ( childList == null ) + { + int n = getChildCount(); + childList = new LinkedList(); + for ( int i = 0; i < n; i++ ) + { + childList.add( new TestObject() ); + } + } +*/ + // this tests manual loading + if ( childList == null ) + { + childList = new LinkedList(); + } + return childList; + } + public void setChildList( List aChildList ) + { + childList = aChildList; + } + + protected TestObject parent; + public TestObject getParent() { return parent; } + public void setParent( TestObject anObject ) { parent = anObject; } + + public String getHash() { return Integer.toHexString( System.identityHashCode( this ) ); } + + public String getFullName() + { +// return getHash() + ": " + firstName + " " + middleName + " " + lastName; + return firstName + " " + middleName + " " + lastName; + } + + public boolean equals( Object anObject ) + { + return anObject == this; + } + + public String toString() + { + return "[" + getClass().getName() + + "@" + Integer.toHexString( System.identityHashCode(this) ) + + ":" + getFullName() + "]"; + } + + // statics + + private static Random random = new Random(); + private static String randomParse( String aString ) + { + String result = ""; + StringTokenizer tokens = new StringTokenizer( aString, "|" ); + int n = (int) ( random.nextDouble() * tokens.countTokens() ); + for ( int i = 0; i <= n; i++ ) + { + result = tokens.nextToken(); + } + return result; + } + + + // interface EOKeyValueCoding: + // disable this interface by commenting out the "implements" declaration + + /** + * Returns the value for the specified property. + * If the property does not exist, this method should + * call handleQueryWithUnboundKey. + */ + public Object valueForKey( String aKey ) + { + System.out.println( "valueForKey: " + aKey ); + return EOKeyValueCodingSupport.valueForKey( this, 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. + */ + public void takeValueForKey( Object aValue, String aKey ) + { + System.out.println( "takeValueForKey: " + aValue + " : " + aKey ); + EOKeyValueCodingSupport.takeValueForKey( this, aValue, aKey ); + } + + /** + * Returns the value for the private field that + * corresponds to the specified property. + */ + public Object storedValueForKey( String aKey ) + { + System.out.println( "storedValueForKey: " + aKey ); + return EOKeyValueCodingSupport.storedValueForKey( this, aKey ); + } + + /** + * Sets the the private field that corresponds to the + * specified property to the specified value. + */ + public void takeStoredValueForKey( Object aValue, String aKey ) + { + System.out.println( "takeStoredValueForKey: " + aValue + " : " + aKey ); + EOKeyValueCodingSupport.takeStoredValueForKey( this, aValue, 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. + */ + public Object handleQueryWithUnboundKey( String aKey ) + { + System.out.println( "handleQueryWithUnboundKey: " + aKey ); + return EOKeyValueCodingSupport.handleQueryWithUnboundKey( this, 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. + */ + public void handleTakeValueForUnboundKey( Object aValue, String aKey ) + { + System.out.println( "handleTakeValueForUnboundKey: " + aValue + " : " + aKey ); + EOKeyValueCodingSupport.handleTakeValueForUnboundKey( this, aValue, 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. + */ + public void unableToSetNullForKey( String aKey ) + { + System.out.println( "unableToSetNullForKey: " + aKey ); + EOKeyValueCodingSupport.unableToSetNullForKey( this, aKey ); + } + + + // main entry point + + public static void main( String[] argv ) + { + int count = 100; + boolean xmlMode = false; + if ( argv.length > 0 ) + { + Integer parsed = ValueConverter.getInteger( argv[0] ); + if ( parsed != null ) count = parsed.intValue(); + + if ( argv.length > 1 ) + { + if ( argv[1].indexOf( "xml" ) > -1 ) + { + xmlMode = true; + } + } + } + +long millis = System.currentTimeMillis(); + + DataSoup store = null; + if ( xmlMode ) + { + store = new XMLFileSoup( "testObjects-xml" ); + } + else + { + store = new SerializedFileSoup( "testObjects-java" ); + } + + Object o; + for ( int i = 0; i < count; i++ ) + { + store.addObject( new TestObject() ); + } + /* + store.addIndex( "age", "age" ); + store.addIndex( "zipCode", "zipCode" ); + store.addIndex( "firstName", "firstName" ); + store.addIndex( "lastName", "lastName" ); +*/ +System.out.println( System.currentTimeMillis() - millis + " milliseconds" ); + } +} diff --git a/projects/net.wotonomy.test/src/main/java/net/wotonomy/test/TestObjectClassDesc.java b/projects/net.wotonomy.test/src/main/java/net/wotonomy/test/TestObjectClassDesc.java new file mode 100644 index 0000000..beb852c --- /dev/null +++ b/projects/net.wotonomy.test/src/main/java/net/wotonomy/test/TestObjectClassDesc.java @@ -0,0 +1,31 @@ +package net.wotonomy.test; + +import net.wotonomy.control.EOClassDescription; +import net.wotonomy.foundation.NSArray; + +/** +* A simple class description for testing. +*/ +public class TestObjectClassDesc extends EOClassDescription +{ + public TestObjectClassDesc() + { + super( TestObject.class ); + } + + public EOClassDescription classDescriptionForDestinationKey( + String detailKey ) + { + if ( "childList".equals( detailKey ) ) + { + return this; + } + return null; + } + + public NSArray toManyRelationshipKeys() + { + return new NSArray( "childList" ); + } + +} diff --git a/projects/net.wotonomy.test/src/main/java/net/wotonomy/test/TestObjectStore.java b/projects/net.wotonomy.test/src/main/java/net/wotonomy/test/TestObjectStore.java new file mode 100644 index 0000000..8fcb8a0 --- /dev/null +++ b/projects/net.wotonomy.test/src/main/java/net/wotonomy/test/TestObjectStore.java @@ -0,0 +1,258 @@ +package net.wotonomy.test; + +import java.util.Iterator; +import java.util.List; +import java.util.Map; + +import net.wotonomy.control.EOEditingContext; +import net.wotonomy.control.EOFetchSpecification; +import net.wotonomy.control.EOGlobalID; +import net.wotonomy.control.EOObjectStore; +import net.wotonomy.control.EOObserverCenter; +import net.wotonomy.test.DataKeyID; +import net.wotonomy.datastore.DataSoup; +import net.wotonomy.datastore.DataView; +import net.wotonomy.datastore.XMLFileSoup; +import net.wotonomy.foundation.NSArray; +import net.wotonomy.foundation.NSMutableArray; +import net.wotonomy.foundation.internal.Duplicator; +import net.wotonomy.foundation.internal.WotonomyException; + +/** +* An object store that wraps a datastore +* for vending test objects. +*/ +public class TestObjectStore extends EOObjectStore +{ + DataSoup soup; + + /** + * Constructor specifies path to datastore. + */ + public TestObjectStore( String aPath ) + { + soup = new XMLFileSoup( aPath ); + } + + /** + * This implementation simply returns + * objectsWithSourceGlobalID. + */ + public NSArray arrayFaultWithSourceGlobalID ( + EOGlobalID aGlobalID, + String aRelationship, + EOEditingContext aContext ) + { + return objectsForSourceGlobalID( + aGlobalID, aRelationship, aContext ); + } + + /** + * This implementation returns the actual + * object for the specified id. + */ + public Object faultForGlobalID ( + EOGlobalID aGlobalID, + EOEditingContext aContext ) + { +System.out.println( "TestObjectStore: * reading object * : " + aGlobalID ); + return soup.getObjectByKey( + ((DataKeyID)aGlobalID).getKey() ); + } + + /** + * Returns a fault representing an object of + * the specified entity type with values from + * the specified dictionary. The fault should + * belong to the specified editing context. + */ + public Object faultForRawRow ( + Map aDictionary, + String anEntityName, + EOEditingContext aContext ) + { + //TODO: faults are not yet supported + throw new WotonomyException( + "Faults are not yet supported." ); + } + + /** + * Given a newly instantiated object, this method + * initializes its properties to values appropriate + * for the specified id. The object should belong + * to the specified editing context. + * This method is called to populate faults. + */ + public void initializeObject ( + Object anObject, + EOGlobalID aGlobalID, + EOEditingContext aContext ) + { +System.out.println( "TestObjectStore: * reading object * : " + aGlobalID ); + Object original = soup.getObjectByKey( + ((DataKeyID)aGlobalID).getKey() ); + EOObserverCenter.notifyObserversObjectWillChange( anObject ); + Duplicator.deepCopy( original, anObject ); + //TODO: need to handle child object registration in aContext + } + + /** + * Remove all values from all objects in memory, + * turning them into faults, and posts a notification + * that all objects have been invalidated. + */ + public void invalidateAllObjects () + { + // does nothing + } + + /** + * Removes values with the specified ids from memory, + * turning them into faults, and posts a notification + * that those objects have been invalidated. + */ + public void invalidateObjectsWithGlobalIDs ( + List aList ) + { + // does nothing + } + + /** + * Returns false because locking is not permitted. + */ + public boolean isObjectLockedWithGlobalID ( + EOGlobalID aGlobalID, + EOEditingContext aContext ) + { + return false; + } + + /** + * Does nothing because locking is not permitted. + */ + public void lockObjectWithGlobalID ( + EOGlobalID aGlobalID, + EOEditingContext aContext ) + { + // does nothing + } + + /** + * Returns a List of objects associated with the object + * with the specified id for the specified property + * relationship. Faults are not allowed in the array. + * All objects should belong to the specified editing context. + */ + public NSArray objectsForSourceGlobalID ( + EOGlobalID aGlobalID, + String aRelationship, + EOEditingContext aContext ) + { + //TODO: relationships are not yet supported + throw new WotonomyException( + "Relationships are not yet supported." ); + } + + /** + * Returns a List of objects the meet the criteria of + * the supplied specification. Faults are not allowed in the array. + * Each object is registered with the specified editing context. + * If any object is already registered in the specified context, + * it is not refetched and that object should be used in the array. + */ + public NSArray objectsWithFetchSpecification ( + EOFetchSpecification aFetchSpec, + EOEditingContext aContext ) + { + //TODO: fetch specs are not yet supported + + DataView view = soup.queryObjects( null, null ); +System.out.println( "TestObjectStore: ** querying all objects **" ); + NSMutableArray result = new NSMutableArray(); + Object o; + Object existing; + DataKeyID id; + Iterator it = view.iterator(); + while ( it.hasNext() ) + { + o = it.next(); + id = new DataKeyID( view.getKeyForObject( o ) ); + existing = aContext.objectForGlobalID( id ); + if ( existing != null ) + { + o = existing; + } + else + { + aContext.recordObject( o, id ); + } + result.addObject( o ); + } + return result; + } + + /** + * Removes all values from the specified object, + * converting it into a fault for the specified id. + * New or deleted objects should not be refaulted. + */ + public void refaultObject ( + Object anObject, + EOGlobalID aGlobalID, + EOEditingContext aContext ) + { + //TODO: faults are not yet supported + // just re-initialize the object + initializeObject( anObject, aGlobalID, aContext ); + } + + /** + * Writes all changes in the specified editing context + * to the respository. + */ + public void saveChangesInEditingContext ( + EOEditingContext aContext ) + { + Object o; + DataKeyID id; + Iterator it; + + System.out.println( aContext.updatedObjects() ); + + // process updates + it = aContext.updatedObjects().iterator(); + while ( it.hasNext() ) + { + o = it.next(); + id = (DataKeyID) aContext.globalIDForObject( o ); +System.out.println( "TestObjectStore: * updating object * : " + id ); + soup.updateObject( id.getKey(), o ); + } + + // process deletes + it = aContext.deletedObjects().iterator(); + while ( it.hasNext() ) + { + o = it.next(); + id = (DataKeyID) aContext.globalIDForObject( o ); +System.out.println( "TestObjectStore: * deleting object * : " + id ); + soup.removeObject( id.getKey() ); + // remove object from editing context + aContext.forgetObject( o ); + } + + // process inserts + it = aContext.insertedObjects().iterator(); + while ( it.hasNext() ) + { + o = it.next(); + id = new DataKeyID( soup.addObject( o ) ); +System.out.println( "TestObjectStore: * adding object * : " + id ); + // register object in editing context with new id + aContext.forgetObject( o ); + aContext.recordObject( o, id ); + } + + } +} + diff --git a/projects/net.wotonomy.test/src/main/java/net/wotonomy/test/TestPanel.java b/projects/net.wotonomy.test/src/main/java/net/wotonomy/test/TestPanel.java new file mode 100644 index 0000000..7e868d0 --- /dev/null +++ b/projects/net.wotonomy.test/src/main/java/net/wotonomy/test/TestPanel.java @@ -0,0 +1,110 @@ +package net.wotonomy.test; + +import java.awt.BorderLayout; +import java.awt.Component; +import java.awt.Insets; +import java.util.Vector; + +import javax.swing.JCheckBox; +import javax.swing.JComboBox; +import javax.swing.JPanel; +import javax.swing.JScrollPane; +import javax.swing.JSlider; +import javax.swing.JTable; +import javax.swing.JTextField; +import javax.swing.border.EmptyBorder; +import javax.swing.text.JTextComponent; + +import net.wotonomy.ui.swing.components.BetterFlowLayout; +import net.wotonomy.ui.swing.components.ButtonPanel; +import net.wotonomy.ui.swing.components.InfoPanel; + +/** +* A master-detail panel with a list, some +* textfields and some buttons. +*/ +public class TestPanel extends JPanel +{ +// public JList list; + public JTable table; + public InfoPanel infoPanel; + public ButtonPanel savePanel; + public ButtonPanel buttonPanel; + public JTextComponent firstNameField; + public JTextComponent middleNameField, lastNameField; + public JComboBox dateBox, monthBox, yearBox; + public JSlider slider; + public JCheckBox checkbox; + + public TestPanel() + { + this.setLayout( new BorderLayout( 10, 10 ) ); + this.setBorder( new EmptyBorder( 10, 10, 10, 10 ) ); + + JPanel overviewPanel = new JPanel(); + overviewPanel.setLayout( new BorderLayout() ); + + //list = new JList(); + //JScrollPane scrollPane = new JScrollPane( list ); + + table = new JTable(); + JScrollPane scrollPane = new JScrollPane( table ); + + overviewPanel.add( scrollPane, BorderLayout.CENTER ); + + this.add( overviewPanel, BorderLayout.CENTER ); + + infoPanel = new InfoPanel(); + infoPanel.setColumns( 1 ); + + // name fields + firstNameField = new JTextField(); +// infoPanel.addPair( "First Name", firstNameField ); + middleNameField = new JTextField(); +// infoPanel.addPair( "Middle Name", middleNameField ); + lastNameField = new JTextField(); +// infoPanel.addPair( "Last Name", lastNameField ); + checkbox = new JCheckBox(); + + infoPanel.addRow( "Name", new Component[] { + firstNameField, middleNameField, lastNameField, checkbox } ); + + // date comboboxen + Vector datesList = new Vector(); + for ( int i = 1; i < 32; i++ ) datesList.add( new Integer( i ) ); + dateBox = new JComboBox( datesList ); + dateBox.setEditable( true ); + monthBox = new JComboBox(); + yearBox = new JComboBox(); + infoPanel.addRow( "Create Date", + dateBox, monthBox, yearBox ); + + // year slider + infoPanel.addRow( "Day of Month", slider = new JSlider( + JSlider.HORIZONTAL, 1, 31, 1 ) ); + + // navigation buttons + + JPanel navigationPanel = new JPanel(); + navigationPanel.setLayout( new BorderLayout() ); + + buttonPanel = new ButtonPanel( new String[] { "Tree View", "Add", "Remove" } ); + buttonPanel.setAlignment( BetterFlowLayout.LEFT ); + buttonPanel.setInsets( new Insets( 0, 0, 0, 0 ) ); + navigationPanel.add( buttonPanel, BorderLayout.WEST ); + + savePanel = new ButtonPanel( new String[] { "Refresh All", "Commit" } ); + savePanel.setAlignment( BetterFlowLayout.RIGHT ); + navigationPanel.add( savePanel, BorderLayout.EAST ); + + // bottom panel layout + + JPanel bottomPanel = new JPanel(); + bottomPanel.setLayout( new BorderLayout() ); + bottomPanel.add( infoPanel, BorderLayout.NORTH ); + bottomPanel.add( navigationPanel, BorderLayout.SOUTH ); + + this.add( bottomPanel, BorderLayout.SOUTH ); + } + +} diff --git a/projects/net.wotonomy.test/src/main/java/net/wotonomy/test/TreeController.java b/projects/net.wotonomy.test/src/main/java/net/wotonomy/test/TreeController.java new file mode 100644 index 0000000..d61d8ff --- /dev/null +++ b/projects/net.wotonomy.test/src/main/java/net/wotonomy/test/TreeController.java @@ -0,0 +1,285 @@ +package net.wotonomy.test; + +import java.awt.BorderLayout; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; +import java.awt.event.WindowAdapter; +import java.awt.event.WindowEvent; + +import javax.swing.AbstractButton; +import javax.swing.BorderFactory; +import javax.swing.JDialog; + +import net.wotonomy.control.EODataSource; +import net.wotonomy.control.EOEditingContext; +import net.wotonomy.foundation.NSArray; +import net.wotonomy.ui.EOAssociation; +import net.wotonomy.ui.EODisplayGroup; +import net.wotonomy.ui.MasterDetailAssociation; +import net.wotonomy.ui.swing.ListAssociation; +import net.wotonomy.ui.swing.RadioPanelAssociation; +import net.wotonomy.ui.swing.TextAssociation; +import net.wotonomy.ui.swing.TreeAssociation; +import net.wotonomy.ui.swing.components.ButtonPanel; +import net.wotonomy.ui.swing.util.ObjectInspector; +import net.wotonomy.ui.swing.util.WindowUtilities; + +/** +* A simple editor panel with a few textfields. +*/ +public class TreeController implements ActionListener +{ + final EODisplayGroup group; + final EODisplayGroup titlesGroup; + final EODisplayGroup detailGroup; + final EOEditingContext editingContext; + + public TreeController( EODataSource aDataSource ) + { + titlesGroup = new EODisplayGroup(); + group = new EODisplayGroup(); + detailGroup = new EODisplayGroup(); + titlesGroup.setDataSource( aDataSource ); + editingContext = aDataSource.editingContext(); + init(); + } + + public TreeController( EOEditingContext aContext, + EODisplayGroup aTitlesGroup, EODisplayGroup aChildGroup ) + { + titlesGroup = aTitlesGroup; + group = aChildGroup; + detailGroup = new EODisplayGroup(); + editingContext = aContext; + init(); + } + + public void init() + { + final TreePanel treePanel = new TreePanel(); + treePanel.panel.setBorder( + BorderFactory.createCompoundBorder( + BorderFactory.createRaisedBevelBorder(), + BorderFactory.createEmptyBorder( 10, 10, 10, 10 ) ) ); + treePanel.setBorder( + BorderFactory.createEmptyBorder( 0, 0, 0, 0 ) ); + ButtonPanel okPanel = new ButtonPanel( + new String[] { "Refresh", "Commit" } ); + treePanel.add( okPanel, BorderLayout.SOUTH ); +/* + + // set up renderer + IconCellRenderer iconRenderer = new IconCellRenderer() + { + private Icon icon = UIManager.getIcon("FileChooser.homeFolderIcon"); + public Icon getIconForContext( + JComponent container, Object value, + int row, int col, + boolean isSelected, boolean hasFocus, + boolean isExpanded, boolean isLeaf ) + { + return icon; + } + }; + treePanel.tree.setCellRenderer( iconRenderer ); + + // enable icon clicking + treePanel.tree.setCellEditor( iconRenderer ); + iconRenderer.addActionListener( this ); +*/ + + treePanel.tree.setEditable( true ); + + // set up display groups + + titlesGroup.fetch(); + + // text associations + EOAssociation ta; + + ta = new TreeAssociation( treePanel.tree, "People" ); + ta.bindAspect( EOAssociation.TitlesAspect, titlesGroup, "fullName" ); + ta.bindAspect( EOAssociation.ChildrenAspect, group, "childList" ); + ta.bindAspect( EOAssociation.IsLeafAspect, titlesGroup, "childCount" ); +// ta.bindAspect( EOAssociation.IsLeafAspect, null, "false" ); + ta.establishConnection(); + treePanel.tree.setEditable( true ); + + ta = new TextAssociation( treePanel.editPanel.firstNameField ); + ta.bindAspect( EOAssociation.ValueAspect, group, "firstName" ); + ta.establishConnection(); + + ta = new TextAssociation( treePanel.editPanel.middleNameField ); + ta.bindAspect( EOAssociation.ValueAspect, group, "middleName" ); + ta.establishConnection(); + + ta = new TextAssociation( treePanel.editPanel.lastNameField ); + ta.bindAspect( EOAssociation.ValueAspect, group, "lastName" ); + ta.establishConnection(); + + ta = new RadioPanelAssociation( treePanel.editPanel.yearRadioPanel ); + ta.bindAspect( EOAssociation.ValueAspect, group, "createDate.year" ); + + EODisplayGroup yearTitles = new EODisplayGroup(); + yearTitles.setObjectArray( new NSArray( + new Object[] { "1999", "2000", "2001" } ) ); + ta.bindAspect( EOAssociation.TitlesAspect, yearTitles, "" ); + + EODisplayGroup yearObjects = new EODisplayGroup(); + yearObjects.setObjectArray( new NSArray( + new Object[] { new Integer( 99 ), new Integer( 100 ), new Integer( 101 ) } ) ); + ta.bindAspect( EOAssociation.ObjectsAspect, yearObjects, "" ); + + ta.establishConnection(); + + // detail group + + ta = new MasterDetailAssociation( detailGroup ); + ta.bindAspect( EOAssociation.ParentAspect, group, "childList" ); + ta.establishConnection(); + + ta = new ListAssociation( treePanel.editPanel.list ); + ta.bindAspect( EOAssociation.TitlesAspect, detailGroup, "fullName" ); + ta.establishConnection(); + + // display group action associations + + AbstractButton addButton = (AbstractButton) + treePanel.editPanel.addPanel.getButton( "Add" ); + addButton.addActionListener( new ActionListener() + { + public void actionPerformed( ActionEvent evt ) + { + Object testObject = new TestObject(); + editingContext.insertObject( testObject ); + detailGroup.insertObjectAtIndex( testObject, 0 ); + } + } ); + + AbstractButton removeButton = (AbstractButton) + treePanel.editPanel.addPanel.getButton( "Remove" ); + removeButton.addActionListener( new ActionListener() + { + public void actionPerformed( ActionEvent evt ) + { + if ( detailGroup.selectedObject() != null ) + detailGroup.deleteSelection(); + } + } ); + + // ok / cancel buttons + + AbstractButton button; + + button = (AbstractButton) + okPanel.getButton( "Commit" ); + button.addActionListener( new ActionListener() + { + public void actionPerformed( ActionEvent evt ) + { + editingContext.saveChanges(); + } + } ); + + button = (AbstractButton) + okPanel.getButton( "Refresh" ); + button.addActionListener( new ActionListener() + { + public void actionPerformed( ActionEvent evt ) + { + editingContext.revert(); +// editingContext.invalidateAllObjects(); + } + } ); + + +/* + AbstractButton refreshButton = (AbstractButton) + treePanel.editPanel.addPanel.getButton( "Refresh" ); + refreshButton.addActionListener( new ActionListener() + { + int count = 0; + public void actionPerformed( ActionEvent evt ) + { + EODisplayGroup displayGroup = (EODisplayGroup) + treePanel.tree.getSelectionPath().getLastPathComponent(); +// displayGroup.insertObjectAtIndex( new TestObject(), 0 ); +// displayGroup.deleteObjectAtIndex( 0 ); + List sortOrderings = new LinkedList(); + if ( count++ % 2 == 0 ) + { + sortOrderings.add( new EOSortOrdering( + "lastName", EOSortOrdering.CompareAscending ) ); + } + else + { + sortOrderings.add( new EOSortOrdering( + "lastName", EOSortOrdering.CompareDescending ) ); + } + displayGroup.setSortOrderings( sortOrderings ); + displayGroup.updateDisplayedObjects(); + } + } ); +*/ +/* + ta = new DisplayGroupActionAssociation( + treePanel.editPanel.addPanel.getButton( "Remove" ) ); + ta.bindAspect( EOAssociation.ActionAspect, detailGroup, "deleteSelection" ); + ta.establishConnection(); + + ta = new DisplayGroupActionAssociation( + treePanel.editPanel.addPanel.getButton( "Refresh" ) ); + ta.bindAspect( EOAssociation.ActionAspect, group, "updateDisplayedObjects" ); + ta.establishConnection(); +*/ + + // add mouse listener for list + + treePanel.editPanel.list.addMouseListener( new MouseAdapter() + { + public void mouseClicked(MouseEvent e) + { + if ( e.getClickCount() == 2 ) + { + Object item = detailGroup.selectedObject(); + if ( item != null ) + { + new InspectorController( item ); + } + } + } + }); + + // launch + + JDialog d = new JDialog(); + d.getContentPane().add( treePanel ); + d.setTitle( "Tree Panel" ); + d.pack(); + WindowUtilities.cascade( d ); + d.show(); + + // workaround for memory issues on jdk1.2.2 + d.addWindowListener( new WindowAdapter() + { + // exit on close + public void windowClosing(WindowEvent e) + { + ((JDialog)e.getWindow()).getContentPane().removeAll(); + } + }); + } + + public void actionPerformed( ActionEvent evt ) + { + Object item = group.selectedObject(); + if ( item != null ) + { +// new InspectorController( item ); + new ObjectInspector( item ); + } + } + +} diff --git a/projects/net.wotonomy.test/src/main/java/net/wotonomy/test/TreeInspectorController.java b/projects/net.wotonomy.test/src/main/java/net/wotonomy/test/TreeInspectorController.java new file mode 100644 index 0000000..8ec9554 --- /dev/null +++ b/projects/net.wotonomy.test/src/main/java/net/wotonomy/test/TreeInspectorController.java @@ -0,0 +1,184 @@ +package net.wotonomy.test; + +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; +import java.awt.event.WindowAdapter; +import java.awt.event.WindowEvent; + +import javax.swing.AbstractButton; +import javax.swing.JDialog; + +import net.wotonomy.foundation.NSArray; +import net.wotonomy.ui.EOAssociation; +import net.wotonomy.ui.EODisplayGroup; +import net.wotonomy.ui.MasterDetailAssociation; +import net.wotonomy.ui.swing.ListAssociation; +import net.wotonomy.ui.swing.RadioPanelAssociation; +import net.wotonomy.ui.swing.TextAssociation; +import net.wotonomy.ui.swing.TreeAssociation; +import net.wotonomy.ui.swing.util.WindowUtilities; + +/** +* A simple editor panel with a few textfields. +*/ +public class TreeInspectorController +{ + public TreeInspectorController( EODisplayGroup titlesGroup, EODisplayGroup childrenGroup ) + { + final EODisplayGroup group = childrenGroup; +// final EODisplayGroup group = titlesGroup; + final TreePanel treePanel = new TreePanel(); + + // text associations + + EOAssociation ta; + + ta = new TreeAssociation( treePanel.tree, "People" ); + ta.bindAspect( EOAssociation.TitlesAspect, titlesGroup, "lastName" ); + ta.bindAspect( EOAssociation.ChildrenAspect, group, "childList" ); + ta.bindAspect( EOAssociation.IsLeafAspect, titlesGroup, "childCount" ); +// ta.bindAspect( EOAssociation.IsLeafAspect, null, "false" ); + ta.establishConnection(); + treePanel.tree.setEditable( true ); + + ta = new TextAssociation( treePanel.editPanel.firstNameField ); + ta.bindAspect( EOAssociation.ValueAspect, group, "firstName" ); + ta.establishConnection(); + + ta = new TextAssociation( treePanel.editPanel.middleNameField ); + ta.bindAspect( EOAssociation.ValueAspect, group, "middleName" ); + ta.establishConnection(); + + ta = new TextAssociation( treePanel.editPanel.lastNameField ); + ta.bindAspect( EOAssociation.ValueAspect, group, "lastName" ); + ta.establishConnection(); + + ta = new RadioPanelAssociation( treePanel.editPanel.yearRadioPanel ); + ta.bindAspect( EOAssociation.ValueAspect, group, "createDate.year" ); + + EODisplayGroup yearTitles = new EODisplayGroup(); + yearTitles.setObjectArray( new NSArray( + new Object[] { "1999", "2000", "2001" } ) ); + ta.bindAspect( EOAssociation.TitlesAspect, yearTitles, "" ); + + EODisplayGroup yearObjects = new EODisplayGroup(); + yearObjects.setObjectArray( new NSArray( + new Object[] { new Integer( 99 ), new Integer( 100 ), new Integer( 101 ) } ) ); + ta.bindAspect( EOAssociation.ObjectsAspect, yearObjects, "" ); + + ta.establishConnection(); + + // detail group + + final EODisplayGroup detailGroup = new EODisplayGroup(); + + ta = new MasterDetailAssociation( detailGroup ); + ta.bindAspect( EOAssociation.ParentAspect, group, "childList" ); + ta.establishConnection(); + + ta = new ListAssociation( treePanel.editPanel.list ); + ta.bindAspect( EOAssociation.TitlesAspect, detailGroup, "fullName" ); + ta.establishConnection(); + + // display group action associations + + AbstractButton addButton = (AbstractButton) + treePanel.editPanel.addPanel.getButton( "Add" ); + addButton.addActionListener( new ActionListener() + { + public void actionPerformed( ActionEvent evt ) + { + detailGroup.insertObjectAtIndex( new TestObject(), 0 ); + } + } ); + + AbstractButton removeButton = (AbstractButton) + treePanel.editPanel.addPanel.getButton( "Remove" ); + removeButton.addActionListener( new ActionListener() + { + public void actionPerformed( ActionEvent evt ) + { + if ( detailGroup.selectedObject() != null ) + detailGroup.deleteSelection(); + } + } ); +/* + AbstractButton refreshButton = (AbstractButton) + treePanel.editPanel.addPanel.getButton( "Refresh" ); + refreshButton.addActionListener( new ActionListener() + { + int count = 0; + public void actionPerformed( ActionEvent evt ) + { + EODisplayGroup displayGroup = (EODisplayGroup) + treePanel.tree.getSelectionPath().getLastPathComponent(); +// displayGroup.insertObjectAtIndex( new TestObject(), 0 ); +// displayGroup.deleteObjectAtIndex( 0 ); + List sortOrderings = new LinkedList(); + if ( count++ % 2 == 0 ) + { + sortOrderings.add( new EOSortOrdering( + "lastName", EOSortOrdering.CompareAscending ) ); + } + else + { + sortOrderings.add( new EOSortOrdering( + "lastName", EOSortOrdering.CompareDescending ) ); + } + displayGroup.setSortOrderings( sortOrderings ); + displayGroup.updateDisplayedObjects(); + } + } ); +*/ +/* + ta = new DisplayGroupActionAssociation( + treePanel.editPanel.addPanel.getButton( "Remove" ) ); + ta.bindAspect( EOAssociation.ActionAspect, detailGroup, "deleteSelection" ); + ta.establishConnection(); + + ta = new DisplayGroupActionAssociation( + treePanel.editPanel.addPanel.getButton( "Refresh" ) ); + ta.bindAspect( EOAssociation.ActionAspect, group, "updateDisplayedObjects" ); + ta.establishConnection(); +*/ + + // add mouse listener for list + + treePanel.editPanel.list.addMouseListener( new MouseAdapter() + { + public void mouseClicked(MouseEvent e) + { + if ( e.getClickCount() == 2 ) + { + Object item = detailGroup.selectedObject(); + if ( item != null ) + { + new InspectorController( item ); + } + } + } + }); + + // launch + + JDialog d = new JDialog(); + d.getContentPane().add( treePanel ); + d.setTitle( "Tree Panel" ); + d.pack(); + WindowUtilities.cascade( d ); + d.show(); + + // workaround for memory issues on jdk1.2.2 + d.addWindowListener( new WindowAdapter() + { + // exit on close + public void windowClosing(WindowEvent e) + { + ((JDialog)e.getWindow()).getContentPane().removeAll(); + } + }); + } + +} diff --git a/projects/net.wotonomy.test/src/main/java/net/wotonomy/test/TreePanel.java b/projects/net.wotonomy.test/src/main/java/net/wotonomy/test/TreePanel.java new file mode 100644 index 0000000..48ebd81 --- /dev/null +++ b/projects/net.wotonomy.test/src/main/java/net/wotonomy/test/TreePanel.java @@ -0,0 +1,39 @@ +package net.wotonomy.test; + +import java.awt.BorderLayout; +import java.awt.Dimension; + +import javax.swing.JPanel; +import javax.swing.JScrollPane; +import javax.swing.JTree; +import javax.swing.border.EmptyBorder; + +/** +* A simple editor panel with a few textfields. +*/ +public class TreePanel extends JPanel +{ + public JTree tree; + public EditPanel editPanel; + public JPanel panel; + + public TreePanel() + { + panel = new JPanel(); + panel.setLayout( new BorderLayout() ); + panel.setBorder( new EmptyBorder( 10, 10, 10, 10 ) ); + + tree = new JTree(); + tree.setRootVisible( false ); + tree.setShowsRootHandles( true ); + JScrollPane scrollPane = new JScrollPane( tree ); + scrollPane.setPreferredSize( new Dimension( 150, 200 ) ); + panel.add( scrollPane, BorderLayout.CENTER ); + editPanel = new EditPanel(); + panel.add( editPanel, BorderLayout.EAST ); + + this.setLayout( new BorderLayout() ); + this.add( panel, BorderLayout.CENTER ); + } + +} -- cgit v1.2.3