summaryrefslogtreecommitdiff
path: root/projects/net.wotonomy.persistence
diff options
context:
space:
mode:
authorBenjamin Culkin <scorpress@gmail.com>2024-05-19 17:56:33 -0400
committerBenjamin Culkin <scorpress@gmail.com>2024-05-19 17:56:33 -0400
commitaedc34d55462a75e329bbf342251ff6504cd117e (patch)
treebcc8f1f2352582717b484df302aeea6696b8f000 /projects/net.wotonomy.persistence
Initial import from SVN
Diffstat (limited to 'projects/net.wotonomy.persistence')
-rw-r--r--projects/net.wotonomy.persistence/.classpath8
-rw-r--r--projects/net.wotonomy.persistence/.cvsignore3
-rw-r--r--projects/net.wotonomy.persistence/.project23
-rw-r--r--projects/net.wotonomy.persistence/.settings/org.eclipse.jdt.core.prefs12
-rw-r--r--projects/net.wotonomy.persistence/.settings/org.eclipse.jdt.ui.prefs3
-rw-r--r--projects/net.wotonomy.persistence/pom.xml45
-rw-r--r--projects/net.wotonomy.persistence/src/main/java/net/wotonomy/access/EOAccessArrayFaultHandler.java71
-rw-r--r--projects/net.wotonomy.persistence/src/main/java/net/wotonomy/access/EOAccessFaultHandler.java66
-rw-r--r--projects/net.wotonomy.persistence/src/main/java/net/wotonomy/access/EOAccessGenericFaultHandler.java75
-rw-r--r--projects/net.wotonomy.persistence/src/main/java/net/wotonomy/access/EOAccessLock.java60
-rw-r--r--projects/net.wotonomy.persistence/src/main/java/net/wotonomy/access/EOAdaptor.java269
-rw-r--r--projects/net.wotonomy.persistence/src/main/java/net/wotonomy/access/EOAdaptorChannel.java244
-rw-r--r--projects/net.wotonomy.persistence/src/main/java/net/wotonomy/access/EOAdaptorContext.java118
-rw-r--r--projects/net.wotonomy.persistence/src/main/java/net/wotonomy/access/EOAdaptorOperation.java120
-rw-r--r--projects/net.wotonomy.persistence/src/main/java/net/wotonomy/access/EOAttribute.java381
-rw-r--r--projects/net.wotonomy.persistence/src/main/java/net/wotonomy/access/EODatabase.java267
-rw-r--r--projects/net.wotonomy.persistence/src/main/java/net/wotonomy/access/EODatabaseChannel.java138
-rw-r--r--projects/net.wotonomy.persistence/src/main/java/net/wotonomy/access/EODatabaseContext.java568
-rw-r--r--projects/net.wotonomy.persistence/src/main/java/net/wotonomy/access/EODatabaseOperation.java48
-rw-r--r--projects/net.wotonomy.persistence/src/main/java/net/wotonomy/access/EOEntity.java637
-rw-r--r--projects/net.wotonomy.persistence/src/main/java/net/wotonomy/access/EOEntityClassDescription.java185
-rw-r--r--projects/net.wotonomy.persistence/src/main/java/net/wotonomy/access/EOGeneralAdaptorException.java62
-rw-r--r--projects/net.wotonomy.persistence/src/main/java/net/wotonomy/access/EOJoin.java56
-rw-r--r--projects/net.wotonomy.persistence/src/main/java/net/wotonomy/access/EOModel.java396
-rw-r--r--projects/net.wotonomy.persistence/src/main/java/net/wotonomy/access/EOModelGroup.java198
-rw-r--r--projects/net.wotonomy.persistence/src/main/java/net/wotonomy/access/EOProperty.java46
-rw-r--r--projects/net.wotonomy.persistence/src/main/java/net/wotonomy/access/EOPropertyListEncoding.java53
-rw-r--r--projects/net.wotonomy.persistence/src/main/java/net/wotonomy/access/EOQualifierSQLGeneration.java241
-rw-r--r--projects/net.wotonomy.persistence/src/main/java/net/wotonomy/access/EORelationship.java319
-rw-r--r--projects/net.wotonomy.persistence/src/main/java/net/wotonomy/access/EOSQLExpression.java697
-rw-r--r--projects/net.wotonomy.persistence/src/main/java/net/wotonomy/access/EOSQLExpressionFactory.java131
-rw-r--r--projects/net.wotonomy.persistence/src/main/java/net/wotonomy/access/EOStoredProcedure.java141
-rw-r--r--projects/net.wotonomy.persistence/src/main/java/net/wotonomy/control/AbstractObjectStore.java762
-rw-r--r--projects/net.wotonomy.persistence/src/main/java/net/wotonomy/control/ArrayFault.java219
-rw-r--r--projects/net.wotonomy.persistence/src/main/java/net/wotonomy/control/ChildDataSource.java187
-rw-r--r--projects/net.wotonomy.persistence/src/main/java/net/wotonomy/control/EOAndQualifier.java167
-rw-r--r--projects/net.wotonomy.persistence/src/main/java/net/wotonomy/control/EOClassDescription.java601
-rw-r--r--projects/net.wotonomy.persistence/src/main/java/net/wotonomy/control/EOCooperatingObjectStore.java82
-rw-r--r--projects/net.wotonomy.persistence/src/main/java/net/wotonomy/control/EOCustomObject.java673
-rw-r--r--projects/net.wotonomy.persistence/src/main/java/net/wotonomy/control/EODataSource.java164
-rw-r--r--projects/net.wotonomy.persistence/src/main/java/net/wotonomy/control/EODatabaseDataSource.java339
-rw-r--r--projects/net.wotonomy.persistence/src/main/java/net/wotonomy/control/EODeferredFaulting.java48
-rw-r--r--projects/net.wotonomy.persistence/src/main/java/net/wotonomy/control/EODelayedObserver.java152
-rw-r--r--projects/net.wotonomy.persistence/src/main/java/net/wotonomy/control/EODelayedObserverQueue.java323
-rw-r--r--projects/net.wotonomy.persistence/src/main/java/net/wotonomy/control/EOEditingContext.java3247
-rw-r--r--projects/net.wotonomy.persistence/src/main/java/net/wotonomy/control/EOEnterpriseObject.java219
-rw-r--r--projects/net.wotonomy.persistence/src/main/java/net/wotonomy/control/EOFaultHandler.java102
-rw-r--r--projects/net.wotonomy.persistence/src/main/java/net/wotonomy/control/EOFaulting.java79
-rw-r--r--projects/net.wotonomy.persistence/src/main/java/net/wotonomy/control/EOFetchSpecification.java565
-rw-r--r--projects/net.wotonomy.persistence/src/main/java/net/wotonomy/control/EOGenericRecord.java151
-rw-r--r--projects/net.wotonomy.persistence/src/main/java/net/wotonomy/control/EOGlobalID.java83
-rw-r--r--projects/net.wotonomy.persistence/src/main/java/net/wotonomy/control/EOIntegralKeyGlobalID.java72
-rw-r--r--projects/net.wotonomy.persistence/src/main/java/net/wotonomy/control/EOKeyComparisonQualifier.java151
-rw-r--r--projects/net.wotonomy.persistence/src/main/java/net/wotonomy/control/EOKeyGlobalID.java146
-rw-r--r--projects/net.wotonomy.persistence/src/main/java/net/wotonomy/control/EOKeyValueArchiver.java106
-rw-r--r--projects/net.wotonomy.persistence/src/main/java/net/wotonomy/control/EOKeyValueArchiving.java41
-rw-r--r--projects/net.wotonomy.persistence/src/main/java/net/wotonomy/control/EOKeyValueCoding.java129
-rw-r--r--projects/net.wotonomy.persistence/src/main/java/net/wotonomy/control/EOKeyValueCodingAdditions.java176
-rw-r--r--projects/net.wotonomy.persistence/src/main/java/net/wotonomy/control/EOKeyValueCodingSupport.java235
-rw-r--r--projects/net.wotonomy.persistence/src/main/java/net/wotonomy/control/EOKeyValueQualifier.java233
-rw-r--r--projects/net.wotonomy.persistence/src/main/java/net/wotonomy/control/EOKeyValueUnarchiver.java165
-rw-r--r--projects/net.wotonomy.persistence/src/main/java/net/wotonomy/control/EONotQualifier.java129
-rw-r--r--projects/net.wotonomy.persistence/src/main/java/net/wotonomy/control/EONullValue.java113
-rw-r--r--projects/net.wotonomy.persistence/src/main/java/net/wotonomy/control/EOObjectStore.java320
-rw-r--r--projects/net.wotonomy.persistence/src/main/java/net/wotonomy/control/EOObjectStoreCoordinator.java204
-rw-r--r--projects/net.wotonomy.persistence/src/main/java/net/wotonomy/control/EOObserverCenter.java547
-rw-r--r--projects/net.wotonomy.persistence/src/main/java/net/wotonomy/control/EOObserverProxy.java104
-rw-r--r--projects/net.wotonomy.persistence/src/main/java/net/wotonomy/control/EOObserving.java51
-rw-r--r--projects/net.wotonomy.persistence/src/main/java/net/wotonomy/control/EOOrQualifier.java169
-rw-r--r--projects/net.wotonomy.persistence/src/main/java/net/wotonomy/control/EOQualifier.java680
-rw-r--r--projects/net.wotonomy.persistence/src/main/java/net/wotonomy/control/EOQualifierEvaluation.java42
-rw-r--r--projects/net.wotonomy.persistence/src/main/java/net/wotonomy/control/EORelationshipManipulation.java76
-rw-r--r--projects/net.wotonomy.persistence/src/main/java/net/wotonomy/control/EOSortOrdering.java406
-rw-r--r--projects/net.wotonomy.persistence/src/main/java/net/wotonomy/control/EOTemporaryGlobalID.java219
-rw-r--r--projects/net.wotonomy.persistence/src/main/java/net/wotonomy/control/EOValidation.java72
-rw-r--r--projects/net.wotonomy.persistence/src/main/java/net/wotonomy/control/EOVectorKeyGlobalID.java79
-rw-r--r--projects/net.wotonomy.persistence/src/main/java/net/wotonomy/control/EditingContext.java283
-rw-r--r--projects/net.wotonomy.persistence/src/main/java/net/wotonomy/control/KeyValueCodingUtilities.java740
-rw-r--r--projects/net.wotonomy.persistence/src/main/java/net/wotonomy/control/ObservableArray.java346
-rw-r--r--projects/net.wotonomy.persistence/src/main/java/net/wotonomy/control/OrderedDataSource.java57
-rw-r--r--projects/net.wotonomy.persistence/src/main/java/net/wotonomy/control/PropertyDataSource.java549
-rw-r--r--projects/net.wotonomy.persistence/src/main/java/net/wotonomy/control/internal/Surrogate.java259
-rw-r--r--projects/net.wotonomy.persistence/src/main/java/net/wotonomy/control/package.html6
-rw-r--r--projects/net.wotonomy.persistence/src/test/java/net/wotonomy/access/EOEntityTest.java14
84 files changed, 20763 insertions, 0 deletions
diff --git a/projects/net.wotonomy.persistence/.classpath b/projects/net.wotonomy.persistence/.classpath
new file mode 100644
index 0000000..38003e1
--- /dev/null
+++ b/projects/net.wotonomy.persistence/.classpath
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<classpath>
+ <classpathentry kind="src" path="src/main/java"/>
+ <classpathentry output="target/test-classes" kind="src" path="src/test/java"/>
+ <classpathentry sourcepath="JRE_SRC" kind="var" path="JRE_LIB"/>
+ <classpathentry kind="con" path="org.maven.ide.eclipse.MAVEN2_CLASSPATH_CONTAINER"/>
+ <classpathentry kind="output" path="target/classes"/>
+</classpath>
diff --git a/projects/net.wotonomy.persistence/.cvsignore b/projects/net.wotonomy.persistence/.cvsignore
new file mode 100644
index 0000000..c14b7c4
--- /dev/null
+++ b/projects/net.wotonomy.persistence/.cvsignore
@@ -0,0 +1,3 @@
+target
+target/*
+cobertura.ser
diff --git a/projects/net.wotonomy.persistence/.project b/projects/net.wotonomy.persistence/.project
new file mode 100644
index 0000000..d5d8f41
--- /dev/null
+++ b/projects/net.wotonomy.persistence/.project
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+ <name>net.wotonomy.persistence</name>
+ <comment></comment>
+ <projects>
+ </projects>
+ <buildSpec>
+ <buildCommand>
+ <name>org.eclipse.jdt.core.javabuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ <buildCommand>
+ <name>org.maven.ide.eclipse.maven2Builder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ </buildSpec>
+ <natures>
+ <nature>org.eclipse.jdt.core.javanature</nature>
+ <nature>org.maven.ide.eclipse.maven2Nature</nature>
+ </natures>
+</projectDescription>
diff --git a/projects/net.wotonomy.persistence/.settings/org.eclipse.jdt.core.prefs b/projects/net.wotonomy.persistence/.settings/org.eclipse.jdt.core.prefs
new file mode 100644
index 0000000..9cfcb04
--- /dev/null
+++ b/projects/net.wotonomy.persistence/.settings/org.eclipse.jdt.core.prefs
@@ -0,0 +1,12 @@
+#Thu Feb 16 21:52:22 EST 2006
+eclipse.preferences.version=1
+org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled
+org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.2
+org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve
+org.eclipse.jdt.core.compiler.compliance=1.4
+org.eclipse.jdt.core.compiler.debug.lineNumber=generate
+org.eclipse.jdt.core.compiler.debug.localVariable=generate
+org.eclipse.jdt.core.compiler.debug.sourceFile=generate
+org.eclipse.jdt.core.compiler.problem.assertIdentifier=warning
+org.eclipse.jdt.core.compiler.problem.enumIdentifier=warning
+org.eclipse.jdt.core.compiler.source=1.3
diff --git a/projects/net.wotonomy.persistence/.settings/org.eclipse.jdt.ui.prefs b/projects/net.wotonomy.persistence/.settings/org.eclipse.jdt.ui.prefs
new file mode 100644
index 0000000..4728b95
--- /dev/null
+++ b/projects/net.wotonomy.persistence/.settings/org.eclipse.jdt.ui.prefs
@@ -0,0 +1,3 @@
+#Thu Feb 16 21:52:22 EST 2006
+eclipse.preferences.version=1
+internal.default.compliance=default
diff --git a/projects/net.wotonomy.persistence/pom.xml b/projects/net.wotonomy.persistence/pom.xml
new file mode 100644
index 0000000..7990db5
--- /dev/null
+++ b/projects/net.wotonomy.persistence/pom.xml
@@ -0,0 +1,45 @@
+<project>
+ <modelVersion>4.0.0</modelVersion>
+ <parent>
+ <groupId>net.wotonomy</groupId>
+ <artifactId>wotonomy-all</artifactId>
+ <version>1.0-alpha5-SNAPSHOT</version>
+ </parent>
+ <artifactId>wotonomy-persistence</artifactId>
+ <name>Wotonomy - Persistence</name>
+ <packaging>jar</packaging>
+ <dependencies>
+ <dependency>
+ <groupId>${project.groupId}</groupId>
+ <artifactId>wotonomy-foundation</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>junit</groupId>
+ <artifactId>junit</artifactId>
+ <version>3.8.1</version>
+ <scope>test</scope>
+ </dependency>
+ </dependencies>
+ <reporting>
+ <plugins>
+ <plugin>
+ <artifactId>maven-javadoc-plugin</artifactId>
+ </plugin>
+ <plugin>
+ <groupId>org.codehaus.mojo</groupId>
+ <artifactId>surefire-report-maven-plugin</artifactId>
+ </plugin>
+ <!--
+ <plugin>
+ <groupId>org.codehaus.mojo</groupId>
+ <artifactId>cobertura-maven-plugin</artifactId>
+ </plugin>
+ -->
+ </plugins>
+ </reporting>
+ <scm>
+ <connection>scm:svn:https://svn.sourceforge.net/svnroot/wotonomy/trunk/projects/net.wotonomy.persistence</connection>
+ <developerConnection>scm:svn:https://svn.sourceforge.net/svnroot/wotonomy/trunk/projects/net.wotonomy.persistence</developerConnection>
+ </scm>
+</project>
diff --git a/projects/net.wotonomy.persistence/src/main/java/net/wotonomy/access/EOAccessArrayFaultHandler.java b/projects/net.wotonomy.persistence/src/main/java/net/wotonomy/access/EOAccessArrayFaultHandler.java
new file mode 100644
index 0000000..1aafa13
--- /dev/null
+++ b/projects/net.wotonomy.persistence/src/main/java/net/wotonomy/access/EOAccessArrayFaultHandler.java
@@ -0,0 +1,71 @@
+/*
+ Wotonomy: OpenStep design patterns for pure Java applications.
+ Copyright (C) 2001 Michael Powers
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, see http://www.gnu.org
+ */
+package net.wotonomy.access;
+
+import net.wotonomy.control.EOEditingContext;
+import net.wotonomy.control.EOKeyGlobalID;
+
+/**
+* A fault handler for to-many relationships.
+*
+* @author ezamudio@nasoft.com
+* @author $Author: cgruber $
+* @version $Revision: 894 $
+*/public class EOAccessArrayFaultHandler extends EOAccessGenericFaultHandler {
+
+ protected EOKeyGlobalID _sourceID;
+ protected String _relation;
+
+ public EOAccessArrayFaultHandler(EOKeyGlobalID sourceID, String relationName, EODatabaseContext dbc, EOEditingContext ec) {
+ super();
+ _sourceID = sourceID;
+ _relation = relationName;
+ setContext(dbc, ec);
+ }
+
+ /* (non-Javadoc)
+ * @see net.wotonomy.control.EOFaultHandler#completeInitializationOfObject(java.lang.Object)
+ */
+ public void completeInitializationOfObject(Object obj) {
+ // TODO Auto-generated method stub
+
+ }
+
+ public String relationshipName() {
+ return _relation;
+ }
+
+ public EOKeyGlobalID sourceGlobalID() {
+ return _sourceID;
+ }
+
+}
+/*
+ * $Log$
+ * Revision 1.2 2006/02/16 16:47:14 cgruber
+ * Move some classes in to "internal" packages and re-work imports, etc.
+ *
+ * Also use UnsupportedOperationExceptions where appropriate, instead of WotonomyExceptions.
+ *
+ * Revision 1.1 2006/02/16 13:19:57 cgruber
+ * Check in all sources in eclipse-friendly maven-enabled packages.
+ *
+ * Revision 1.1 2003/08/19 19:53:20 chochos
+ * EOAccess fault handlers (still incomplete)
+ *
+ */
diff --git a/projects/net.wotonomy.persistence/src/main/java/net/wotonomy/access/EOAccessFaultHandler.java b/projects/net.wotonomy.persistence/src/main/java/net/wotonomy/access/EOAccessFaultHandler.java
new file mode 100644
index 0000000..d4cabe9
--- /dev/null
+++ b/projects/net.wotonomy.persistence/src/main/java/net/wotonomy/access/EOAccessFaultHandler.java
@@ -0,0 +1,66 @@
+/*
+ Wotonomy: OpenStep design patterns for pure Java applications.
+ Copyright (C) 2001 Michael Powers
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, see http://www.gnu.org
+ */
+package net.wotonomy.access;
+
+import net.wotonomy.control.EOEditingContext;
+import net.wotonomy.control.EOKeyGlobalID;
+
+/**
+* A fault handler for single objects. Usually the destinations of a
+* to-one relationship.
+*
+* @author ezamudio@nasoft.com
+* @author $Author: cgruber $
+* @version $Revision: 894 $
+*/public class EOAccessFaultHandler extends EOAccessGenericFaultHandler {
+
+ protected EOKeyGlobalID _gid;
+
+ public EOAccessFaultHandler(EOKeyGlobalID gid, EODatabaseContext dbc, EOEditingContext ec) {
+ super();
+ _gid = gid;
+ setContext(dbc, ec);
+ }
+
+ /* (non-Javadoc)
+ * @see net.wotonomy.control.EOFaultHandler#completeInitializationOfObject(java.lang.Object)
+ */
+ public void completeInitializationOfObject(Object obj) {
+ // TODO Auto-generated method stub
+
+ }
+
+ public EOKeyGlobalID globalID() {
+ return _gid;
+ }
+
+}
+/*
+ * $Log$
+ * Revision 1.2 2006/02/16 16:47:14 cgruber
+ * Move some classes in to "internal" packages and re-work imports, etc.
+ *
+ * Also use UnsupportedOperationExceptions where appropriate, instead of WotonomyExceptions.
+ *
+ * Revision 1.1 2006/02/16 13:19:57 cgruber
+ * Check in all sources in eclipse-friendly maven-enabled packages.
+ *
+ * Revision 1.1 2003/08/19 19:53:20 chochos
+ * EOAccess fault handlers (still incomplete)
+ *
+ */
diff --git a/projects/net.wotonomy.persistence/src/main/java/net/wotonomy/access/EOAccessGenericFaultHandler.java b/projects/net.wotonomy.persistence/src/main/java/net/wotonomy/access/EOAccessGenericFaultHandler.java
new file mode 100644
index 0000000..6876151
--- /dev/null
+++ b/projects/net.wotonomy.persistence/src/main/java/net/wotonomy/access/EOAccessGenericFaultHandler.java
@@ -0,0 +1,75 @@
+/*
+ Wotonomy: OpenStep design patterns for pure Java applications.
+ Copyright (C) 2001 Michael Powers
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, see http://www.gnu.org
+ */
+package net.wotonomy.access;
+
+import net.wotonomy.control.EOEditingContext;
+import net.wotonomy.control.EOFaultHandler;
+
+/**
+* A generic fault handler for EOAccess.
+*
+* @author ezamudio@nasoft.com
+* @author $Author: cgruber $
+* @version $Revision: 894 $
+*/
+public abstract class EOAccessGenericFaultHandler extends EOFaultHandler {
+
+ protected EODatabaseContext _dbContext;
+ protected EOEditingContext _ec;
+
+ public EOAccessGenericFaultHandler() {
+ super();
+ }
+
+ /* (non-Javadoc)
+ * @see net.wotonomy.control.EOFaultHandler#faultWillFire(java.lang.Object)
+ */
+ public void faultWillFire(Object obj) {
+ // TODO Auto-generated method stub
+
+ }
+
+ protected void setContext(EODatabaseContext dbc, EOEditingContext ec) {
+ _dbContext = dbc;
+ _ec = ec;
+ }
+
+ public EODatabaseContext databaseContext() {
+ return _dbContext;
+ }
+
+ public EOEditingContext editingContext() {
+ return _ec;
+ }
+
+}
+/*
+ * $Log$
+ * Revision 1.2 2006/02/16 16:47:14 cgruber
+ * Move some classes in to "internal" packages and re-work imports, etc.
+ *
+ * Also use UnsupportedOperationExceptions where appropriate, instead of WotonomyExceptions.
+ *
+ * Revision 1.1 2006/02/16 13:19:57 cgruber
+ * Check in all sources in eclipse-friendly maven-enabled packages.
+ *
+ * Revision 1.1 2003/08/19 19:53:20 chochos
+ * EOAccess fault handlers (still incomplete)
+ *
+ */
+ \ No newline at end of file
diff --git a/projects/net.wotonomy.persistence/src/main/java/net/wotonomy/access/EOAccessLock.java b/projects/net.wotonomy.persistence/src/main/java/net/wotonomy/access/EOAccessLock.java
new file mode 100644
index 0000000..28199c5
--- /dev/null
+++ b/projects/net.wotonomy.persistence/src/main/java/net/wotonomy/access/EOAccessLock.java
@@ -0,0 +1,60 @@
+/*
+ Wotonomy: OpenStep design patterns for pure Java applications.
+ Copyright (C) 2001 Michael Powers
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, see http://www.gnu.org
+ */
+package net.wotonomy.access;
+
+import net.wotonomy.foundation.NSRecursiveLock;
+
+/**
+ * This class offers a very simple interface to a global locking
+ * mechanism to be used by the whole access layer.
+ *
+ * @author ezamudio@nasoft.com
+ * @author $Author: cgruber $
+ * @version $Revision: 894 $
+ */
+public class EOAccessLock {
+
+ private static NSRecursiveLock _lock = new NSRecursiveLock();
+
+ private EOAccessLock() {
+ super();
+ }
+
+ public static void lock() {
+ _lock.lock();
+ }
+
+ public static void unlock() {
+ _lock.unlock();
+ }
+
+}
+/*
+ * $Log$
+ * Revision 1.2 2006/02/16 16:47:13 cgruber
+ * Move some classes in to "internal" packages and re-work imports, etc.
+ *
+ * Also use UnsupportedOperationExceptions where appropriate, instead of WotonomyExceptions.
+ *
+ * Revision 1.1 2006/02/16 13:19:57 cgruber
+ * Check in all sources in eclipse-friendly maven-enabled packages.
+ *
+ * Revision 1.1 2003/08/29 20:43:25 chochos
+ * a global access layer lock
+ *
+ */
diff --git a/projects/net.wotonomy.persistence/src/main/java/net/wotonomy/access/EOAdaptor.java b/projects/net.wotonomy.persistence/src/main/java/net/wotonomy/access/EOAdaptor.java
new file mode 100644
index 0000000..28295d1
--- /dev/null
+++ b/projects/net.wotonomy.persistence/src/main/java/net/wotonomy/access/EOAdaptor.java
@@ -0,0 +1,269 @@
+/*
+ Wotonomy: OpenStep design patterns for pure Java applications.
+ Copyright (C) 2001 Michael Powers
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, see http://www.gnu.org
+ */
+package net.wotonomy.access;
+
+import net.wotonomy.foundation.NSArray;
+import net.wotonomy.foundation.NSData;
+import net.wotonomy.foundation.NSDictionary;
+import net.wotonomy.foundation.NSKeyValueCoding;
+import net.wotonomy.foundation.NSMutableArray;
+import net.wotonomy.foundation.NSMutableDictionary;
+import net.wotonomy.foundation.NSTimestamp;
+
+/**
+*
+* @author ezamudio@nasoft.com
+* @author $Author: cgruber $
+* @version $Revision: 894 $
+*/
+
+public abstract class EOAdaptor {
+
+ protected String _name;
+ protected NSDictionary _connectionDictionary = NSDictionary.EmptyDictionary;
+ protected NSMutableArray _contexts = new NSMutableArray();
+ protected Class _expressionClass;
+ private static NSMutableDictionary _expressionClassesByName = new NSMutableDictionary();
+
+ public EOAdaptor(String name) {
+ super();
+ _name = name;
+ }
+
+ /**
+ * Creates an adaptor with model's adaptorName and sets its connection
+ * dictionary to the model's connection dictionary.
+ * @param model The model to take adaptorName and connectionDictionary from.
+ * @return The adaptor specified in model.
+ */
+ public static EOAdaptor adaptorWithModel(EOModel model) {
+ if (model == null)
+ throw new IllegalArgumentException("Model must not be null.");
+ if (model.adaptorName() == null || model.adaptorName().length() == 0)
+ throw new IllegalArgumentException("Cannot create an adaptor with an empty name.");
+ EOAdaptor adaptor = adaptorWithName(model.adaptorName());
+ if (adaptor == null)
+ throw new IllegalArgumentException("Cannot create adaptor with name " + model.adaptorName());
+ adaptor.setConnectionDictionary(model.connectionDictionary());
+ return adaptor;
+ }
+
+ /**
+ * Instantiates an adaptor of a concrete EOAdaptor subclass, based on name.
+ * If name is a fully qualified class name, then it returns an instance of that class
+ * by invoking the constructor with a string argument. Otherwise, it
+ * tries to find a class called (name)Adaptor in a package called
+ * net.wotonomy.(lowercase name)adaptor; if it can't find one, an exception is raised.
+ * @param name The name of the adaptor, or a fully qualified class name.
+ * @return
+ */
+ public static EOAdaptor adaptorWithName(String name) {
+ Class adaptorClass = null;
+ String cname = null;
+ if (name.endsWith("Adaptor") && name.indexOf('.') > 0) {
+ cname = name;
+ int lastdot = name.lastIndexOf('.');
+ //take off the package and the 'Adaptor' suffix
+ name = cname.substring(lastdot, cname.length() - 7);
+ } else {
+ //construct the fully qualified class name
+ cname = "net.wotonomy." + name.toLowerCase() + "adaptor." + name + "Adaptor";
+ }
+ try {
+ adaptorClass = Class.forName(cname);
+ } catch (ClassNotFoundException ex) {
+ throw new IllegalArgumentException("Cannot find class named " + name);
+ }
+ EOAdaptor adaptor = null;
+ java.lang.reflect.Constructor callme = null;
+ try {
+ callme = adaptorClass.getConstructor(new Class[]{ String.class });
+ adaptor = (EOAdaptor)callme.newInstance((Object[])new String[]{ name });
+ } catch (ClassCastException ex) {
+ throw new IllegalArgumentException("Class " + adaptorClass.getName() + " must inherit from net.wotonomy.access.EOAdaptor");
+ }catch (Exception ex) {
+ throw new IllegalArgumentException("Cannot find or invoke constructor with name argument in class " + adaptorClass.getName());
+ }
+ return adaptor;
+ }
+
+ public static void setExpressionClassName(String expClassName, String adaptorClassName) {
+ _expressionClassesByName.setObjectForKey(expClassName, adaptorClassName);
+ }
+ public static String expressionClassName(String adaptorClassName) {
+ return (String)_expressionClassesByName.objectForKey(adaptorClassName);
+ }
+
+ public void assignExternalInfoForAttribute(EOAttribute attribute) {
+ if (!attribute.isDerived()) {
+ attribute.setColumnName(attribute.name().toUpperCase());
+ }
+ assignExternalTypeForAttribute(attribute);
+ }
+
+ public void assignExternalTypeForAttribute(EOAttribute attribute) {
+ }
+
+ public void assignExternalInfoForEntity(EOEntity entity) {
+ entity.setExternalName(entity.name().toUpperCase());
+ }
+
+ public void assignExternalInfoForEntireModel(EOModel model) {
+ NSArray ents = model.entities();
+ for (int i = 0; i < ents.count(); i++) {
+ EOEntity e = (EOEntity)ents.objectAtIndex(i);
+ //TODO: check that entity is not a prototypes entity
+ NSArray atts = e.attributes();
+ for (int j = 0; j < atts.count(); j++) {
+ EOAttribute a = (EOAttribute)atts.objectAtIndex(i);
+ assignExternalInfoForAttribute(a);
+ }
+ assignExternalInfoForEntity(e);
+ }
+ }
+
+ public boolean canServiceModel(EOModel model) {
+ NSDictionary mcd = model.connectionDictionary();
+ if (mcd == null && _connectionDictionary == null)
+ return true;
+ if (mcd == null || _connectionDictionary == null)
+ return false;
+ return mcd.equals(_connectionDictionary);
+ }
+
+ public void setConnectionDictionary(NSDictionary connection) {
+ _connectionDictionary = connection;
+ }
+ public NSDictionary connectionDictionary() {
+ return _connectionDictionary;
+ }
+
+ public NSArray contexts() {
+ return new NSArray(_contexts);
+ }
+
+ public abstract void assertConnectionDictionaryIsValid();
+
+ public abstract EOAdaptorContext createAdaptorContext();
+
+ public abstract Class defaultExpressionClass();
+
+ public abstract EOSQLExpressionFactory expressionFactory();
+
+ public abstract boolean isValidQualifierType(String typeName, EOModel model);
+
+ public Class expressionClass() {
+ if (_expressionClass != null)
+ return _expressionClass;
+ String cname = expressionClassName(name());
+ if (cname != null) {
+ try {
+ _expressionClass = Class.forName(cname);
+ } catch (ClassNotFoundException ex) {
+ throw new IllegalStateException("Cannot find expression class named " + cname);
+ }
+ }
+ return defaultExpressionClass();
+ }
+
+ public NSArray externalTypesWithModel(EOModel model) {
+ return NSArray.EmptyArray;
+ }
+
+ public NSData fetchedValueForDataValue(NSData value, EOAttribute attr) {
+ return value;
+ }
+
+ public NSTimestamp fetchedValueForDateValue(NSTimestamp value, EOAttribute attr) {
+ return value;
+ }
+
+ public Number fetchedValueForNumberValue(Number value, EOAttribute attr) {
+ return value;
+ }
+
+ public String fetchedValueForStringValue(String value, EOAttribute attr) {
+ return value;
+ }
+
+ public Object fetchedValueForValue(Object value, EOAttribute attr) {
+ if (value == NSKeyValueCoding.NullValue)
+ return value;
+ if (value instanceof String)
+ return fetchedValueForStringValue((String)value, attr);
+ if (value instanceof NSData)
+ return fetchedValueForDataValue((NSData)value, attr);
+ if (value instanceof Number)
+ return fetchedValueForNumberValue((Number)value, attr);
+ if (value instanceof NSTimestamp)
+ return fetchedValueForDateValue((NSTimestamp)value, attr);
+ return value;
+ }
+
+ public void handleDroppedConnection() {
+ for (int i = 0; i < _contexts.count(); i++) {
+ EOAdaptorContext c = (EOAdaptorContext)_contexts.objectAtIndex(i);
+ c.transactionDidRollback();
+ c.handleDroppedConnection();
+ }
+ _contexts.removeAllObjects();
+ }
+
+ public boolean hasOpenChannels() {
+ for (int i = 0; i < _contexts.count(); i++) {
+ EOAdaptorContext c = (EOAdaptorContext)_contexts.objectAtIndex(i);
+ if (c.hasOpenChannels())
+ return true;
+ }
+ return false;
+ }
+
+ public String internalTypeForExternalType(String extType, EOModel model) {
+ return null;
+ }
+
+ public boolean isDroppedConnectionException(Exception ex) {
+ return false;
+ }
+
+ public String name() {
+ return _name;
+ }
+
+ public NSArray prototypeAttributes() {
+ return NSArray.EmptyArray;
+ }
+
+}
+/*
+ * $Log$
+ * Revision 1.2 2006/02/16 16:47:14 cgruber
+ * Move some classes in to "internal" packages and re-work imports, etc.
+ *
+ * Also use UnsupportedOperationExceptions where appropriate, instead of WotonomyExceptions.
+ *
+ * Revision 1.1 2006/02/16 13:19:57 cgruber
+ * Check in all sources in eclipse-friendly maven-enabled packages.
+ *
+ * Revision 1.2 2005/12/08 06:52:32 cgruber
+ * Move tests, improve build.xml, and make certain casts explicit so that Java 1.5 doesn't complain about varargs.
+ *
+ * Revision 1.1 2003/08/13 00:37:45 chochos
+ * an almost complete implementation of the abstract adaptor-layer classes
+ *
+ */ \ No newline at end of file
diff --git a/projects/net.wotonomy.persistence/src/main/java/net/wotonomy/access/EOAdaptorChannel.java b/projects/net.wotonomy.persistence/src/main/java/net/wotonomy/access/EOAdaptorChannel.java
new file mode 100644
index 0000000..09b8a6d
--- /dev/null
+++ b/projects/net.wotonomy.persistence/src/main/java/net/wotonomy/access/EOAdaptorChannel.java
@@ -0,0 +1,244 @@
+/*
+ Wotonomy: OpenStep design patterns for pure Java applications.
+ Copyright (C) 2001 Michael Powers
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, see http://www.gnu.org
+ */
+package net.wotonomy.access;
+
+import net.wotonomy.control.EOFetchSpecification;
+import net.wotonomy.control.EOQualifier;
+import net.wotonomy.foundation.NSArray;
+import net.wotonomy.foundation.NSDictionary;
+import net.wotonomy.foundation.NSMutableDictionary;
+
+/**
+*
+* @author ezamudio@nasoft.com
+* @author $Author: cgruber $
+* @version $Revision: 894 $
+*/
+public abstract class EOAdaptorChannel {
+
+ protected EOAdaptorContext _context;
+
+ public EOAdaptorChannel(EOAdaptorContext context) {
+ super();
+ _context = context;
+ }
+
+ public EOAdaptorContext adaptorContext() {
+ return _context;
+ }
+
+ public void addStoredProceduresNamed(NSArray names, EOModel model) {
+ }
+
+ public abstract NSArray attributesToFetch();
+
+ public abstract void cancelFetch();
+
+ public abstract void closeChannel();
+
+ public abstract NSArray describeResults();
+
+ public abstract int deleteRowsDescribedByQualifier(EOQualifier q, EOEntity entity);
+
+ public abstract void evaluateExpression(EOSQLExpression sql);
+
+ public abstract void executeStoredProcedure(EOStoredProcedure proc, NSDictionary values);
+
+ public abstract NSMutableDictionary fetchRow();
+
+ public abstract void insertRow(NSDictionary row, EOEntity entity);
+
+ public abstract boolean isFetchInProgress();
+
+ public abstract boolean isOpen();
+
+ public abstract void openChannel();
+
+ public abstract NSDictionary returnValuesForLastStoredProcedureInvocation();
+
+ public abstract void selectAttributes(NSArray atts, EOFetchSpecification fspec, boolean lock, EOEntity entity);
+
+ public abstract void setAttributesToFetch(NSArray atts);
+
+ public abstract int updateValuesInRowsDescribedByQualifier(NSDictionary row, EOQualifier q, EOEntity entity);
+
+ public void deleteRowDescribedByQualifier(EOQualifier q, EOEntity entity) {
+ adaptorContext().beginTransaction();
+ int count = deleteRowsDescribedByQualifier(q, entity);
+ if (count != 1) {
+ adaptorContext().rollbackTransaction();
+ throw new EOGeneralAdaptorException("Qualifier deleted " + count + " rows instead of exactly one.");
+ }
+ adaptorContext().commitTransaction();
+ }
+
+ public EOModel describeModelWithTableNames(NSArray names) {
+ return null;
+ }
+
+ public NSArray describeStoredProcedureNames() {
+ return NSArray.EmptyArray;
+ }
+
+ public NSArray describeTableNames() {
+ return NSArray.EmptyArray;
+ }
+
+ public NSMutableDictionary dictionaryWithObjectsForAttributes(Object[] values, NSArray attributes) {
+ Object[] keys = new Object[attributes.count()];
+ for (int i = 0; i < attributes.count(); i++)
+ keys[i] = ((EOAttribute)attributes.objectAtIndex(i)).name();
+ return new NSMutableDictionary(values, keys);
+ }
+
+ public void lockRowComparingAttributes(NSArray atts, EOEntity entity, EOQualifier q, NSDictionary snapshot) {
+ EOFetchSpecification fspec = new EOFetchSpecification(entity.name(), q, null);
+ adaptorContext().beginTransaction();
+ selectAttributes(atts, fspec, true, entity);
+ if (isFetchInProgress()) {
+ NSDictionary row = fetchRow();
+ if (row == null) {
+ cancelFetch();
+ adaptorContext().rollbackTransaction();
+ throw new EOGeneralAdaptorException("Cannot obtain row to lock. Probably modified from the outside.");
+ }
+ if (isFetchInProgress()) {
+ if (fetchRow() != null) {
+ cancelFetch();
+ adaptorContext().rollbackTransaction();
+ throw new EOGeneralAdaptorException("Qualifier returns more than one row.");
+ }
+ }
+ java.util.Enumeration enumeration = snapshot.keyEnumerator();
+ while (enumeration.hasMoreElements()) {
+ Object key = enumeration.nextElement();
+ Object svalue = snapshot.objectForKey(key);
+ Object rvalue = row.objectForKey(key);
+ if (rvalue == null) {
+ cancelFetch();
+ adaptorContext().rollbackTransaction();
+ throw new EOGeneralAdaptorException("Value for key " + key + " not found in locked row.");
+ }
+ if (!rvalue.equals(svalue)) {
+ cancelFetch();
+ adaptorContext().rollbackTransaction();
+ throw new EOGeneralAdaptorException("Value for key " + key + " differes from snapshot.");
+ }
+ }
+ adaptorContext().commitTransaction();
+ return;
+ }
+ adaptorContext().rollbackTransaction();
+ throw new EOGeneralAdaptorException("A fetch was never generated.");
+ }
+
+ public void performAdaptorOperation(EOAdaptorOperation operation) {
+ int opcode = operation.adaptorOperator();
+ switch (opcode) {
+ case EODatabaseOperation.AdaptorLockOperator:
+ if (operation.entity() == null)
+ throw new EOGeneralAdaptorException("A lock operation must have an entity assigned to it.",
+ new NSDictionary(operation, "operation"));
+ if (operation.qualifier() == null)
+ throw new EOGeneralAdaptorException("A lock operation must have a qualifier assigned to it.",
+ new NSDictionary(operation, "operation"));
+ if (operation.qualifier() == null)
+ throw new EOGeneralAdaptorException("A lock operation must have changedValues assigned to it.",
+ new NSDictionary(operation, "operation"));
+ lockRowComparingAttributes(operation.attributes(), operation.entity(), operation.qualifier(), operation.changedValues());
+ break;
+ case EODatabaseOperation.AdaptorInsertOperator:
+ if (operation.entity() == null)
+ throw new EOGeneralAdaptorException("An insert operation must have an entity assigned to it.",
+ new NSDictionary(operation, "operation"));
+ if (operation.changedValues() == null)
+ throw new EOGeneralAdaptorException("An insert operation must have changedValues assigned to it.",
+ new NSDictionary(operation, "operation"));
+ insertRow(operation.changedValues(), operation.entity());
+ break;
+ case EODatabaseOperation.AdaptorUpdateOperator:
+ if (operation.entity() == null)
+ throw new EOGeneralAdaptorException("An update operation must have an entity assigned to it.",
+ new NSDictionary(operation, "operation"));
+ if (operation.changedValues() == null)
+ throw new EOGeneralAdaptorException("An update operation must have changedValues assigned to it.",
+ new NSDictionary(operation, "operation"));
+ updateValuesInRowsDescribedByQualifier(operation.changedValues(), operation.qualifier(), operation.entity());
+ break;
+ case EODatabaseOperation.AdaptorDeleteOperator:
+ if (operation.entity() == null)
+ throw new EOGeneralAdaptorException("A delete operation must have an entity assigned to it.",
+ new NSDictionary(operation, "operation"));
+ deleteRowsDescribedByQualifier(operation.qualifier(), operation.entity());
+ break;
+ case EODatabaseOperation.AdaptorStoredProcedureOperator:
+ if (operation.storedProcedure() == null)
+ throw new EOGeneralAdaptorException("A stored procedure operation must have a stored procedure assigned to it.",
+ new NSDictionary(operation, "operation"));
+ executeStoredProcedure(operation.storedProcedure(), operation.changedValues());
+ break;
+ default:
+ throw new EOGeneralAdaptorException("I don't know how to perform an operation with code " + opcode,
+ new NSDictionary(operation, "operation"));
+ }
+ }
+
+ public void performAdaptorOperations(NSArray ops) {
+ for (int i = 0; i < ops.count(); i++) {
+ EOAdaptorOperation adop = (EOAdaptorOperation)ops.objectAtIndex(i);
+ performAdaptorOperation(adop);
+ }
+ }
+
+ public NSArray primaryKeysForNewRowsWithEntity(int count, EOEntity entity) {
+ NSDictionary[] keys = new NSDictionary[count];
+ for (int i = 0; i < count; i++)
+ keys[i] = NSDictionary.EmptyDictionary;
+ return new NSArray(keys);
+ }
+
+ public void updateValuesInRowDescribedByQualifier(NSDictionary row, EOQualifier q, EOEntity entity) {
+ adaptorContext().beginTransaction();
+ int count = updateValuesInRowsDescribedByQualifier(row, q, entity);
+ if (count != 1) {
+ adaptorContext().rollbackTransaction();
+ throw new EOGeneralAdaptorException("The qualifier should describe exactly one row (updated " + count + " rows)");
+ }
+ adaptorContext().commitTransaction();
+ }
+
+}
+/*
+ * $Log$
+ * Revision 1.2 2006/02/16 16:47:14 cgruber
+ * Move some classes in to "internal" packages and re-work imports, etc.
+ *
+ * Also use UnsupportedOperationExceptions where appropriate, instead of WotonomyExceptions.
+ *
+ * Revision 1.1 2006/02/16 13:19:57 cgruber
+ * Check in all sources in eclipse-friendly maven-enabled packages.
+ *
+ * Revision 1.2 2005/05/11 15:21:53 cgruber
+ * Change enum to enumeration, since enum is now a keyword as of Java 5.0
+ *
+ * A few other comments in the code.
+ *
+ * Revision 1.1 2003/08/13 00:37:45 chochos
+ * an almost complete implementation of the abstract adaptor-layer classes
+ *
+ */ \ No newline at end of file
diff --git a/projects/net.wotonomy.persistence/src/main/java/net/wotonomy/access/EOAdaptorContext.java b/projects/net.wotonomy.persistence/src/main/java/net/wotonomy/access/EOAdaptorContext.java
new file mode 100644
index 0000000..aff0ddd
--- /dev/null
+++ b/projects/net.wotonomy.persistence/src/main/java/net/wotonomy/access/EOAdaptorContext.java
@@ -0,0 +1,118 @@
+/*
+ Wotonomy: OpenStep design patterns for pure Java applications.
+ Copyright (C) 2001 Michael Powers
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, see http://www.gnu.org
+ */
+package net.wotonomy.access;
+
+import net.wotonomy.foundation.NSArray;
+import net.wotonomy.foundation.NSMutableArray;
+import net.wotonomy.foundation.NSNotificationCenter;
+
+/**
+*
+* @author ezamudio@nasoft.com
+* @author $Author: cgruber $
+* @version $Revision: 894 $
+*/
+
+public abstract class EOAdaptorContext {
+
+ public static final String AdaptorContextBeginTransactionNotification = "AdaptorContextBeginTransaction";
+ public static final String AdaptorContextCommitTransactionNotification = "AdaptorContextCommitTransaction";
+ public static final String AdaptorContextRollbackTransactionNotification = "AdaptorContextRollbackTransaction";
+
+ protected EOAdaptor _adaptor;
+ protected NSMutableArray _channels = new NSMutableArray();
+ protected boolean _hasOpenTransaction;
+
+ public EOAdaptorContext(EOAdaptor adaptor) {
+ super();
+ _adaptor = adaptor;
+ }
+
+ public EOAdaptor adaptor() {
+ return _adaptor;
+ }
+
+ public abstract void beginTransaction();
+
+ public abstract void commitTransaction();
+
+ public abstract void rollbackTransaction();
+
+ public abstract EOAdaptorChannel createAdaptorChannel();
+
+ public abstract void handleDroppedConnection();
+
+ public NSArray channels() {
+ return new NSArray(_channels);
+ }
+
+ public boolean hasBusyChannels() {
+ for (int i = 0; i < _channels.count(); i++) {
+ EOAdaptorChannel chan = (EOAdaptorChannel)_channels.objectAtIndex(i);
+ if (chan.isFetchInProgress())
+ return true;
+ }
+ return false;
+ }
+
+ public boolean hasOpenChannels() {
+ for (int i = 0; i < _channels.count(); i++) {
+ EOAdaptorChannel chan = (EOAdaptorChannel)_channels.objectAtIndex(i);
+ if (chan.isOpen())
+ return true;
+ }
+ return false;
+ }
+
+ public boolean hasOpenTransaction() {
+ return _hasOpenTransaction;
+ }
+
+ public void transactionDidBegin() {
+ _hasOpenTransaction = true;
+ NSNotificationCenter.defaultCenter().postNotification(
+ AdaptorContextBeginTransactionNotification, this);
+ }
+
+ public void transactionDidCommit() {
+ _hasOpenTransaction = false;
+ NSNotificationCenter.defaultCenter().postNotification(
+ AdaptorContextCommitTransactionNotification, this);
+ }
+
+ public void transactionDidRollback() {
+ _hasOpenTransaction = false;
+ NSNotificationCenter.defaultCenter().postNotification(
+ AdaptorContextRollbackTransactionNotification, this);
+ }
+
+}
+/*
+ * $Log$
+ * Revision 1.2 2006/02/16 16:47:14 cgruber
+ * Move some classes in to "internal" packages and re-work imports, etc.
+ *
+ * Also use UnsupportedOperationExceptions where appropriate, instead of WotonomyExceptions.
+ *
+ * Revision 1.1 2006/02/16 13:19:57 cgruber
+ * Check in all sources in eclipse-friendly maven-enabled packages.
+ *
+ * Revision 1.1 2003/08/13 00:37:45 chochos
+ * an almost complete implementation of the abstract adaptor-layer classes
+ *
+ */ \ No newline at end of file
diff --git a/projects/net.wotonomy.persistence/src/main/java/net/wotonomy/access/EOAdaptorOperation.java b/projects/net.wotonomy.persistence/src/main/java/net/wotonomy/access/EOAdaptorOperation.java
new file mode 100644
index 0000000..818985d
--- /dev/null
+++ b/projects/net.wotonomy.persistence/src/main/java/net/wotonomy/access/EOAdaptorOperation.java
@@ -0,0 +1,120 @@
+/*
+ Wotonomy: OpenStep design patterns for pure Java applications.
+ Copyright (C) 2001 Michael Powers
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, see http://www.gnu.org
+ */
+package net.wotonomy.access;
+
+import net.wotonomy.control.EOQualifier;
+import net.wotonomy.foundation.NSArray;
+import net.wotonomy.foundation.NSDictionary;
+
+/**
+* Represents a single primitive operation in a database server.
+* Can be insert, update, delete, lock a row, or execute a
+* stored procedure.
+*
+* @author ezamudio@nasoft.com
+* @author $Author: cgruber $
+* @version $Revision: 894 $
+*/
+public class EOAdaptorOperation {
+
+ protected EOEntity _entity;
+ protected NSArray _attributes;
+ protected NSDictionary _changedValues;
+ protected Throwable _exception;
+ protected EOQualifier _qualifier;
+ protected EOStoredProcedure _proc;
+ protected int _adaptorOp;
+
+ public EOAdaptorOperation(EOEntity entity) {
+ super();
+ _entity = entity;
+ }
+
+ public void setAdaptorOperator(int adOp) {
+ _adaptorOp = adOp;
+ }
+ public int adaptorOperator() {
+ return _adaptorOp;
+ }
+
+ public void setAttributes(NSArray atts) {
+ _attributes = atts;
+ }
+ public NSArray attributes() {
+ return _attributes;
+ }
+
+ public void setChangedValues(NSDictionary values) {
+ _changedValues = values;
+ }
+ public NSDictionary changedValues() {
+ return _changedValues;
+ }
+
+ public int compareAdaptorOperation(EOAdaptorOperation op) {
+ if (op.entity() != null && entity() != null) {
+ if (!op.entity().name().equals(entity().name()))
+ op.entity().name().compareTo(entity().name());
+ }
+ if (adaptorOperator() < op.adaptorOperator())
+ return -1;
+ if (adaptorOperator() > op.adaptorOperator())
+ return 1;
+ return 0;
+ }
+
+ public EOEntity entity() {
+ return _entity;
+ }
+
+ public void setException(Throwable t) {
+ _exception = t;
+ }
+ public Throwable exception() {
+ return _exception;
+ }
+
+ public void setQualifier(EOQualifier q) {
+ _qualifier = q;
+ }
+ public EOQualifier qualifier() {
+ return _qualifier;
+ }
+
+ public void setStoredProcedure(EOStoredProcedure sp) {
+ _proc = sp;
+ }
+ public EOStoredProcedure storedProcedure() {
+ return _proc;
+ }
+
+}
+/*
+ * $Log$
+ * Revision 1.2 2006/02/16 16:47:13 cgruber
+ * Move some classes in to "internal" packages and re-work imports, etc.
+ *
+ * Also use UnsupportedOperationExceptions where appropriate, instead of WotonomyExceptions.
+ *
+ * Revision 1.1 2006/02/16 13:19:57 cgruber
+ * Check in all sources in eclipse-friendly maven-enabled packages.
+ *
+ * Revision 1.1 2003/08/13 00:37:45 chochos
+ * an almost complete implementation of the abstract adaptor-layer classes
+ *
+ */ \ No newline at end of file
diff --git a/projects/net.wotonomy.persistence/src/main/java/net/wotonomy/access/EOAttribute.java b/projects/net.wotonomy.persistence/src/main/java/net/wotonomy/access/EOAttribute.java
new file mode 100644
index 0000000..8b651ec
--- /dev/null
+++ b/projects/net.wotonomy.persistence/src/main/java/net/wotonomy/access/EOAttribute.java
@@ -0,0 +1,381 @@
+/*
+ Wotonomy: OpenStep design patterns for pure Java applications.
+ Copyright (C) 2001 Michael Powers
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, see http://www.gnu.org
+ */
+package net.wotonomy.access;
+
+import net.wotonomy.foundation.NSDictionary;
+import net.wotonomy.foundation.NSMutableDictionary;
+import net.wotonomy.foundation.NSSelector;
+
+/**
+* Represents an attribute inside an entity. Contains mapping data for
+* the attribute's external name, external and internal datatypes, etc.
+* It can also represent a flattened or derived attribute, or a prototype;
+* and they are also used to represent parameters in a stored procedure.
+*
+* @author ezamudio@nasoft.com
+* @author $Author: cgruber $
+* @version $Revision: 894 $
+*/
+public class EOAttribute extends EOProperty implements EOPropertyListEncoding {
+
+ //These are used for stored procedure parameters.
+ public static final int Void = 0;
+ public static final int InParameter = 1;
+ public static final int OutParameter = 2;
+ public static final int InOutParameter = 3;
+
+ protected EOEntity _entity;
+ protected String _name;
+ protected String _columnName;
+ protected String _definition;
+ protected String _className;
+ protected String _externalType;
+ protected Class _valueClass;
+ protected String _valueClassName;
+ protected String _valueType;
+ protected String _valueFactoryMethodName;
+ protected String _readFormat;
+ protected String _writeFormat;
+ protected String _prototypeName;
+ protected EOAttribute _prototype;
+ protected NSSelector _valueFactoryMethod;
+ protected boolean _allowsNull;
+ protected boolean _readOnly;
+ protected boolean _isFlattened;
+ protected boolean _knowsIfFlattened;
+ protected int _precision;
+ protected int _scale;
+ protected int _width;
+ protected int _parameterDirection;
+ protected NSDictionary _internalInfo;
+ protected NSDictionary _userInfo;
+
+ protected boolean _has_allowsNull;
+
+ public EOAttribute() {
+ super();
+ _allowsNull = true;
+ }
+
+ public EOAttribute(NSDictionary dict, Object obj) {
+ super();
+ if (obj instanceof EOEntity)
+ _entity = (EOEntity)obj;
+ setName((String)dict.objectForKey("name"));
+ if (dict.objectForKey("columnName") != null)
+ setColumnName((String)dict.objectForKey("columnName"));
+ if (dict.objectForKey("definition") != null)
+ setDefinition((String)dict.objectForKey("definition"));
+ _prototypeName = (String)dict.objectForKey("prototypeName");
+ setExternalType((String)dict.objectForKey("externalType"));
+ setClassName((String)dict.objectForKey("valueClassName"));
+ setValueType((String)dict.objectForKey("valueType"));
+ _writeFormat = (String)dict.objectForKey("writeFormat");
+ _readFormat = (String)dict.objectForKey("readFormat");
+ if (dict.objectForKey("precision") != null)
+ setPrecision(Integer.parseInt((String)dict.objectForKey("precision")));
+ if (dict.objectForKey("scale") != null)
+ setScale(Integer.parseInt((String)dict.objectForKey("scale")));
+ if (dict.objectForKey("width") != null)
+ setWidth(Integer.parseInt((String)dict.objectForKey("width")));
+ if (dict.objectForKey("parameterDirection") != null)
+ setParameterDirection(Integer.parseInt((String)dict.objectForKey("parameterDirection")));
+ setAllowsNull("Y".equals(dict.objectForKey("allowsNull")));
+ }
+
+ void setEntity(EOEntity value) {
+ _entity = value;
+ }
+
+ public void setName(String name) {
+ _name = name;
+ }
+ public String name() {
+ return _name;
+ }
+
+ public void setColumnName(String name) {
+ _columnName = name;
+ }
+ public String columnName() {
+ if (_columnName != null)
+ return _columnName;
+ if (prototype() != null)
+ if (_prototype.columnName() != null)
+ return _prototype.columnName();
+ return null;
+ }
+
+ public void setClassName(String name) {
+ _className = name;
+ }
+ public String className() {
+ if (_className != null)
+ return _className;
+ if (prototype() != null)
+ if (_prototype.className() != null)
+ return _prototype.className();
+ return null;
+ }
+
+ public void setDefinition(String def) {
+ _definition = def;
+ _columnName = null;
+ }
+ public String definition() {
+ if (_definition != null)
+ return _definition;
+ if (prototype() != null)
+ if (_prototype.definition() != null)
+ return _prototype.definition();
+ return null;
+ }
+
+ public void setExternalType(String type) {
+ _externalType = type;
+ }
+ public String externalType() {
+ if (_externalType != null)
+ return _externalType;
+ if (prototype() != null)
+ if (_prototype.externalType() != null)
+ return _prototype.externalType();
+ return null;
+ }
+
+ public void setAllowsNull(boolean flag) {
+ _allowsNull = flag;
+ _has_allowsNull = true;
+ }
+ public boolean allowsNull() {
+ if (_has_allowsNull)
+ return _allowsNull;
+ if (prototype() != null)
+ return _prototype.allowsNull();
+ return _allowsNull;
+ }
+
+ public void setReadOnly(boolean flag) {
+ _readOnly = flag;
+ }
+ public boolean readOnly() {
+ return _readOnly;
+ }
+
+ public void setPrototype(EOAttribute proto) {
+ _prototype = proto;
+ if (proto != null)
+ _prototypeName = proto.name();
+ else
+ _prototypeName = null;
+ }
+ public EOAttribute prototype() {
+ if (_prototypeName != null && _prototype == null) {
+ try {
+ EOModel m = _entity.model();
+ EOModelGroup g = m.modelGroup();
+ EOEntity protos = g.entityNamed("EO" + m.adaptorName() + "Prototypes");
+ if (protos == null)
+ protos = g.entityNamed("EOPrototypes");
+ _prototype = protos.attributeNamed(_prototypeName);
+ } catch (NullPointerException e) {
+ }
+ }
+ return _prototype;
+ }
+
+ public void setPrecision(int value) {
+ _precision = value;
+ }
+ public int precision() {
+ if (_precision > 0)
+ return _precision;
+ if (prototype() != null)
+ return _prototype.precision();
+ return _precision;
+ }
+
+ public void setScale(int value) {
+ _scale = value;
+ }
+ public int scale() {
+ if (_scale > 0)
+ return _scale;
+ if (prototype() != null)
+ return _prototype.scale();
+ return _scale;
+ }
+
+ public void setWidth(int value) {
+ _width = value;
+ }
+ public int width() {
+ if (_width > 0)
+ return _width;
+ if (prototype() != null)
+ return _prototype.width();
+ return _width;
+ }
+
+ /** @deprecated Use setClassName instead. */
+ public void setValueClassName(String name) {
+ setClassName(name);
+ }
+ /** @deprecated Use className() instead. */
+ public String valueClassName() {
+ return className();
+ }
+
+ public void setValueType(String type) {
+ _valueType = type;
+ }
+ public String valueType() {
+ if (_valueType != null)
+ return _valueType;
+ if (prototype() != null)
+ return _prototype.valueType();
+ return null;
+ }
+
+ public void setReadFormat(String value) {
+ _readFormat = value;
+ }
+ public String readFormat() {
+ return _readFormat;
+ }
+
+ public void setWriteFormat(String value) {
+ _writeFormat = value;
+ }
+ public String writeFormat() {
+ return _writeFormat;
+ }
+
+ public boolean isDerived() {
+ return (definition() != null);
+ }
+
+ /** Determines whether the receiver is a flattened attribute.
+ * A flattened attribute has as its definition a relationship
+ * path that can be resolved to an attribute.
+ * @return true if the receiver is flattened.
+ */
+ public boolean isFlattened() {
+ if (_knowsIfFlattened)
+ return _isFlattened;
+ _knowsIfFlattened = true;
+ if (definition() == null)
+ return false;
+ _isFlattened = (entity()._attributeForPath(definition()) != null);
+ return _isFlattened;
+ }
+
+ public EOEntity entity() {
+ return _entity;
+ }
+
+ public void setParameterDirection(int dir) {
+ _parameterDirection = dir;
+ }
+ public int parameterDirection() {
+ return _parameterDirection;
+ }
+
+ public String relationshipPath() {
+ if (isFlattened())
+ return definition();
+ return null;
+ }
+
+ public void setUserInfo(NSDictionary value) {
+ _userInfo = value;
+ }
+ public NSDictionary userInfo() {
+ return _userInfo;
+ }
+
+ public void awakeWithPropertyList(NSDictionary plist) {
+ }
+
+ public void encodeIntoPropertyList(NSMutableDictionary dict) {
+ dict.setObjectForKey(name(), "name");
+ if (_prototypeName != null)
+ dict.setObjectForKey(_prototypeName, "prototypeName");
+ if (_columnName != null)
+ dict.setObjectForKey(_columnName, "columnName");
+ if (_definition != null)
+ dict.setObjectForKey(_definition, "definition");
+ if (_className != null)
+ dict.setObjectForKey(_className, "valueClassName");
+ if (_valueType != null)
+ dict.setObjectForKey(_valueType, "valueType");
+ if (_precision > 0)
+ dict.setObjectForKey(new Integer(_precision), "precision");
+ if (_scale > 0)
+ dict.setObjectForKey(new Integer(_scale), "scale");
+ if (_width > 0)
+ dict.setObjectForKey(new Integer(_width), "width");
+ if (_externalType != null)
+ dict.setObjectForKey(_externalType, "externalType");
+ if (_readFormat != null)
+ dict.setObjectForKey(_readFormat, "readFormat");
+ if (_writeFormat != null)
+ dict.setObjectForKey(_writeFormat, "writeFormat");
+ if (_allowsNull)
+ dict.setObjectForKey("Y", "allowsNull");
+ if (_entity == null)
+ dict.setObjectForKey(new Integer(parameterDirection()), "parameterDirection");
+ if (userInfo() != null && userInfo().count() > 0)
+ dict.setObjectForKey(userInfo(), "userInfo");
+ if (_internalInfo != null && _internalInfo.count() > 0)
+ dict.setObjectForKey(_internalInfo, "internalInfo");
+ }
+
+}
+/*
+ * $Log$
+ * Revision 1.2 2006/02/16 16:47:13 cgruber
+ * Move some classes in to "internal" packages and re-work imports, etc.
+ *
+ * Also use UnsupportedOperationExceptions where appropriate, instead of WotonomyExceptions.
+ *
+ * Revision 1.1 2006/02/16 13:19:57 cgruber
+ * Check in all sources in eclipse-friendly maven-enabled packages.
+ *
+ * Revision 1.7 2003/08/14 02:13:56 chochos
+ * implement and use _attributeForPath()
+ *
+ * Revision 1.6 2003/08/12 01:45:04 chochos
+ * added some code to handle prototypes
+ *
+ * Revision 1.5 2003/08/11 19:38:27 chochos
+ * Can now read from a file and re-write to another file.
+ *
+ * Revision 1.4 2003/08/09 01:35:35 chochos
+ * implement EOPropertyListEncoding
+ *
+ * Revision 1.3 2003/08/08 06:52:09 chochos
+ * isFlattened() works
+ *
+ * Revision 1.2 2003/08/08 02:15:03 chochos
+ * added parameterDirection (for use with stored procedures)
+ *
+ * Revision 1.1 2003/08/07 02:39:45 chochos
+ * EOAttribute. Can be initialized from a property list.
+ *
+*/ \ No newline at end of file
diff --git a/projects/net.wotonomy.persistence/src/main/java/net/wotonomy/access/EODatabase.java b/projects/net.wotonomy.persistence/src/main/java/net/wotonomy/access/EODatabase.java
new file mode 100644
index 0000000..a9177c5
--- /dev/null
+++ b/projects/net.wotonomy.persistence/src/main/java/net/wotonomy/access/EODatabase.java
@@ -0,0 +1,267 @@
+/*
+ Wotonomy: OpenStep design patterns for pure Java applications.
+ Copyright (C) 2001 Michael Powers
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, see http://www.gnu.org
+ */
+package net.wotonomy.access;
+
+import java.util.Enumeration;
+
+import net.wotonomy.control.EOEnterpriseObject;
+import net.wotonomy.control.EOGlobalID;
+import net.wotonomy.foundation.NSArray;
+import net.wotonomy.foundation.NSDictionary;
+import net.wotonomy.foundation.NSMutableArray;
+import net.wotonomy.foundation.NSMutableDictionary;
+import net.wotonomy.foundation.NSTimestamp;
+
+/**
+*
+* @author ezamudio@nasoft.com
+* @author $Author: cgruber $
+* @version $Revision: 894 $
+*/
+public class EODatabase {
+
+ protected EOAdaptor _adaptor;
+ protected NSMutableArray _models = new NSMutableArray();
+ protected NSMutableArray _contexts = new NSMutableArray();
+ protected NSMutableDictionary _resultCache = new NSMutableDictionary();
+ protected NSMutableDictionary _snapshots = new NSMutableDictionary();
+ protected NSTimestamp _timestamp;
+ protected static boolean _releaseUnrefSnapshots = true;
+
+ public EODatabase(EOAdaptor adaptor) {
+ super();
+ if (adaptor == null)
+ throw new IllegalArgumentException("Adaptor cannot be null.");
+ _adaptor = adaptor;
+ }
+
+ public EODatabase(EOModel model) {
+ super();
+ _adaptor = EOAdaptor.adaptorWithModel(model);
+ addModel(model);
+ }
+
+ public EOAdaptor adaptor() {
+ return _adaptor;
+ }
+
+ public void addModel(EOModel model) {
+ if (!addModelIfCompatible(model))
+ throw new IllegalArgumentException("Model is not compatible with this database.");
+ }
+
+ public void removeMode(EOModel model) {
+ _models.removeObject(model);
+ }
+
+ public boolean addModelIfCompatible(EOModel model) {
+ if (_models.containsObject(model))
+ return false;
+ if (model.adaptorName().equals(adaptor().name())) {
+ if (adaptor().canServiceModel(model)) {
+ _models.addObject(model);
+ return true;
+ }
+ }
+ return false;
+ }
+
+ public void decrementSnapshotCountForGlobalID(EOGlobalID gid) {
+ if (_releaseUnrefSnapshots) {
+ }
+ }
+
+ public void incrementSnapshotCountForGlobalID(EOGlobalID gid) {
+ if (_releaseUnrefSnapshots) {
+ }
+ }
+
+ public static void disableSnapshotRefCounting() {
+ _releaseUnrefSnapshots = false;
+ }
+
+ public EOEntity entityForObject(EOEnterpriseObject eo) {
+ String cname = eo.getClass().getName();
+ for (int i = 0; i < _models.count(); i++) {
+ EOModel m = (EOModel)_models.objectAtIndex(i);
+ NSArray ents = m.entities();
+ for (int j = 0; j < ents.count(); j++) {
+ EOEntity e = (EOEntity)ents.objectAtIndex(i);
+ if (e.className().equals(cname))
+ return e;
+ }
+ }
+ return null;
+ }
+
+ public EOEntity entityNamed(String name) {
+ for (int i = 0; i < _models.count(); i++) {
+ EOModel m = (EOModel)_models.objectAtIndex(i);
+ NSArray ents = m.entities();
+ for (int j = 0; j < ents.count(); j++) {
+ EOEntity e = (EOEntity)ents.objectAtIndex(i);
+ if (e.name().equals(name))
+ return e;
+ }
+ }
+ return null;
+ }
+
+ public void forgetAllSnapshots() {
+ _snapshots.removeAllObjects();
+ }
+
+ public void forgetSnapshotForGlobalID(EOGlobalID gid) {
+ _snapshots.removeObjectForKey(gid);
+ }
+
+ public void forgetSnapshotsForGlobalIDs(NSArray gids) {
+ for (int i = 0; i < gids.count(); i++)
+ forgetSnapshotForGlobalID((EOGlobalID)gids.objectAtIndex(i));
+ }
+
+ public void handleDroppedConnection() {
+ adaptor().handleDroppedConnection();
+ for (int i = 0; i < _contexts.count(); i++) {
+ EODatabaseContext c = (EODatabaseContext)_contexts.objectAtIndex(i);
+ c.handleDroppedConnection();
+ }
+ }
+
+ public void invalidateResultCache() {
+ _resultCache.removeAllObjects();
+ }
+
+ public void invalidateResultCacheForEntityNamed(String name) {
+ _resultCache.removeObjectForKey(name);
+ }
+
+ public NSArray models() {
+ return new NSArray(_models);
+ }
+
+ public void recordSnapshotForGlobalID(NSDictionary snap, EOGlobalID gid) {
+ _snapshots.setObjectForKey(snap, gid);
+ }
+
+ public void recordSnapshotForSourceGlobalID(NSArray gids, EOGlobalID gid, String name) {
+ NSMutableDictionary d = (NSMutableDictionary)_snapshots.objectForKey(gid);
+ if (d == null) {
+ d = new NSMutableDictionary();
+ _snapshots.setObjectForKey(d, gid);
+ }
+ d.setObjectForKey(gids, name);
+ }
+
+ public void recordSnapshots(NSDictionary snaps) {
+ _snapshots.addEntriesFromDictionary(snaps);
+ }
+
+ public void recordToManySnapshots(NSDictionary snaps) {
+ Enumeration enumeration = snaps.keyEnumerator();
+ while (enumeration.hasMoreElements()) {
+ EOGlobalID gid = (EOGlobalID)enumeration.nextElement();
+ NSDictionary rels = (NSDictionary)snaps.objectForKey(gid);
+ Enumeration relEnum = rels.keyEnumerator();
+ while (relEnum.hasMoreElements()) {
+ String relName = (String)relEnum.nextElement();
+ NSArray gids = (NSArray)rels.objectForKey(relName);
+ recordSnapshotForSourceGlobalID(gids, gid, relName);
+ }
+ }
+ }
+
+ public void registerContext(EODatabaseContext context) {
+ if (!_contexts.contains(context)) {
+ if (context.database() != this)
+ throw new IllegalStateException("Cannot register context assigned to a different database.");
+ _contexts.addObject(context);
+ }
+ }
+
+ public void unregisterContext(EODatabaseContext context) {
+ _contexts.removeObject(context);
+ }
+
+ public NSArray registeredContexts() {
+ return new NSArray(_contexts);
+ }
+
+ public NSArray resultCacheForEntityNamed(String name) {
+ return (NSArray)_resultCache.objectForKey(name);
+ }
+
+ public void setResultCache(NSArray cache, String entityName) {
+ _resultCache.setObjectForKey(cache, entityName);
+ }
+
+ public void setTimestampToNow() {
+ _timestamp = new NSTimestamp();
+ }
+
+ public NSDictionary snapshotForGlobalID(EOGlobalID gid) {
+ return (NSDictionary)_snapshots.objectForKey(gid);
+ }
+
+ public NSDictionary snapshotForGlobalID(EOGlobalID gid, long l) {
+ return null;
+ }
+
+ public NSArray snapshotForSourceGlobalID(EOGlobalID gid, String name) {
+ NSDictionary d = (NSDictionary)_snapshots.objectForKey(gid);
+ if (d == null)
+ return null;
+ return (NSArray)d.objectForKey(name);
+ }
+
+ public NSDictionary snapshotForSourceGlobalID(EOGlobalID gid, String s, long l) {
+ return null;
+ }
+
+ public NSDictionary snapshots() {
+ return _snapshots;
+ }
+
+ public long timestampForGlobalID(EOGlobalID gid) {
+ return NSTimestamp.DistantPast.timeIntervalSinceReferenceDate();
+ }
+
+ public long timestampForSourceGlobalID(EOGlobalID gid, String s) {
+ return 0;
+ }
+
+}
+/*
+ * $Log$
+ * Revision 1.2 2006/02/16 16:47:13 cgruber
+ * Move some classes in to "internal" packages and re-work imports, etc.
+ *
+ * Also use UnsupportedOperationExceptions where appropriate, instead of WotonomyExceptions.
+ *
+ * Revision 1.1 2006/02/16 13:19:57 cgruber
+ * Check in all sources in eclipse-friendly maven-enabled packages.
+ *
+ * Revision 1.2 2005/05/11 15:21:53 cgruber
+ * Change enum to enumeration, since enum is now a keyword as of Java 5.0
+ *
+ * A few other comments in the code.
+ *
+ * Revision 1.1 2003/08/19 01:54:43 chochos
+ * The EODatabase layer still needs a lot of work, but it's on its way...
+ *
+ */ \ No newline at end of file
diff --git a/projects/net.wotonomy.persistence/src/main/java/net/wotonomy/access/EODatabaseChannel.java b/projects/net.wotonomy.persistence/src/main/java/net/wotonomy/access/EODatabaseChannel.java
new file mode 100644
index 0000000..10424e9
--- /dev/null
+++ b/projects/net.wotonomy.persistence/src/main/java/net/wotonomy/access/EODatabaseChannel.java
@@ -0,0 +1,138 @@
+/*
+ Wotonomy: OpenStep design patterns for pure Java applications.
+ Copyright (C) 2001 Michael Powers
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, see http://www.gnu.org
+ */
+package net.wotonomy.access;
+
+import net.wotonomy.control.EOClassDescription;
+import net.wotonomy.control.EOEditingContext;
+import net.wotonomy.control.EOFetchSpecification;
+import net.wotonomy.control.EOGlobalID;
+import net.wotonomy.control.EOKeyValueCodingAdditions;
+import net.wotonomy.foundation.NSArray;
+import net.wotonomy.foundation.NSDictionary;
+import net.wotonomy.foundation.NSMutableArray;
+
+/**
+*
+* @author ezamudio@nasoft.com
+* @author $Author: cgruber $
+* @version $Revision: 894 $
+*/
+public class EODatabaseChannel {
+
+ protected EODatabaseContext _context;
+ protected EOAdaptorChannel _channel;
+ protected EOEntity _currEntity;
+ protected EOEditingContext _currEC;
+ protected NSArray _attributes;
+ protected boolean _locking;
+ protected boolean _refreshing;
+
+ public EODatabaseChannel(EODatabaseContext context) {
+ super();
+ _context = context;
+ _channel = _context.adaptorContext().createAdaptorChannel();
+ }
+
+ public EOAdaptorChannel adaptorChannel() {
+ return _channel;
+ }
+
+ public EODatabaseContext databaseContext() {
+ return _context;
+ }
+
+ public void cancelFetch() {
+ _channel.cancelFetch();
+ }
+
+ public Object fetchObject() {
+ NSDictionary r = _channel.fetchRow();
+ EOGlobalID gid = _currEntity.globalIDForRow(r);
+ Object eo = _currEC.objectForGlobalID(gid);
+ if (eo == null) {
+ eo = EOClassDescription.classDescriptionForEntityName(_currEntity.name()).createInstanceWithEditingContext(_currEC, gid);
+ if (eo instanceof EOKeyValueCodingAdditions)
+ ((EOKeyValueCodingAdditions)eo).takeValuesFromDictionary(r);
+ else
+ EOKeyValueCodingAdditions.DefaultImplementation.takeValuesFromDictionary(eo, r);
+ } else {
+ if (isRefreshingObjects()) {
+ //TODO: refresh object (how?)
+ }
+ }
+ return eo;
+ }
+
+ public boolean isFetchInProgress() {
+ return _channel.isFetchInProgress();
+ }
+
+ public void setIsLocking(boolean flag) {
+ _locking = flag;
+ }
+
+ public boolean isLocking() {
+ return _locking;
+ }
+
+ public void setIsRefreshingObjects(boolean flag) {
+ _refreshing = flag;
+ }
+
+ public boolean isRefreshingObjects() {
+ return _refreshing;
+ }
+
+ public void selectObjectsWithFetchSpecification(EOFetchSpecification fspec, EOEditingContext ec) {
+ setIsLocking(fspec.locksObjects());
+ setIsRefreshingObjects(fspec.refreshesRefetchedObjects());
+ setCurrentEditingContext(ec);
+ setCurrentEntity(databaseContext().database().entityNamed(fspec.entityName()));
+ NSMutableArray atts = new NSMutableArray();
+ atts.addObjectsFromArray(_currEntity.attributes());
+ adaptorChannel().selectAttributes(atts, fspec, isLocking(), _currEntity);
+ adaptorChannel().setAttributesToFetch(atts);
+ _attributes = atts;
+ }
+
+ public void setCurrentEditingContext(EOEditingContext ec) {
+ _currEC = ec;
+ }
+
+ public void setCurrentEntity(EOEntity entity) {
+ _currEntity = entity;
+ }
+
+}
+/*
+ * $Log$
+ * Revision 1.2 2006/02/16 16:47:14 cgruber
+ * Move some classes in to "internal" packages and re-work imports, etc.
+ *
+ * Also use UnsupportedOperationExceptions where appropriate, instead of WotonomyExceptions.
+ *
+ * Revision 1.1 2006/02/16 13:19:57 cgruber
+ * Check in all sources in eclipse-friendly maven-enabled packages.
+ *
+ * Revision 1.2 2003/08/29 21:15:46 chochos
+ * use EOEntity's globalIDForRow method.
+ *
+ * Revision 1.1 2003/08/19 01:54:43 chochos
+ * The EODatabase layer still needs a lot of work, but it's on its way...
+ *
+ */ \ No newline at end of file
diff --git a/projects/net.wotonomy.persistence/src/main/java/net/wotonomy/access/EODatabaseContext.java b/projects/net.wotonomy.persistence/src/main/java/net/wotonomy/access/EODatabaseContext.java
new file mode 100644
index 0000000..af696fe
--- /dev/null
+++ b/projects/net.wotonomy.persistence/src/main/java/net/wotonomy/access/EODatabaseContext.java
@@ -0,0 +1,568 @@
+/*
+ Wotonomy: OpenStep design patterns for pure Java applications.
+ Copyright (C) 2001 Michael Powers
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, see http://www.gnu.org
+ */
+package net.wotonomy.access;
+
+import java.util.Enumeration;
+import java.util.List;
+import java.util.Map;
+
+import net.wotonomy.control.EOAndQualifier;
+import net.wotonomy.control.EOCooperatingObjectStore;
+import net.wotonomy.control.EOEditingContext;
+import net.wotonomy.control.EOEnterpriseObject;
+import net.wotonomy.control.EOFaultHandler;
+import net.wotonomy.control.EOFaulting;
+import net.wotonomy.control.EOFetchSpecification;
+import net.wotonomy.control.EOGlobalID;
+import net.wotonomy.control.EOKeyGlobalID;
+import net.wotonomy.control.EOKeyValueCoding;
+import net.wotonomy.control.EOKeyValueCodingSupport;
+import net.wotonomy.control.EOKeyValueQualifier;
+import net.wotonomy.control.EOObjectStoreCoordinator;
+import net.wotonomy.control.EOQualifier;
+import net.wotonomy.foundation.NSArray;
+import net.wotonomy.foundation.NSDictionary;
+import net.wotonomy.foundation.NSLocking;
+import net.wotonomy.foundation.NSMutableArray;
+import net.wotonomy.foundation.NSMutableDictionary;
+
+/**
+*
+* @author ezamudio@nasoft.com
+* @author $Author: cgruber $
+* @version $Revision: 894 $
+*/
+public class EODatabaseContext
+ extends EOCooperatingObjectStore implements NSLocking {
+
+ private static Class _contextClass;
+ protected EODatabase _database;
+ protected EOAdaptorContext _context;
+ protected NSMutableArray _channels = new NSMutableArray();
+ protected NSMutableArray _lockedObjects = new NSMutableArray();
+
+ protected NSMutableDictionary _simpleSnaps;
+ protected NSMutableDictionary _manySnaps;
+
+ protected EOObjectStoreCoordinator _coordinator;
+ protected EOEditingContext _currEC;
+ protected int _updateStrategy;
+
+ public EODatabaseContext(EODatabase database) {
+ super();
+ _database = database;
+ _context = _database.adaptor().createAdaptorContext();
+ }
+
+ public EOAdaptorContext adaptorContext() {
+ return _context;
+ }
+
+ public EODatabase database() {
+ return _database;
+ }
+
+ public EODatabaseChannel availableChannel() {
+ for (int i = 0; i < _channels.count(); i++) {
+ EODatabaseChannel c = (EODatabaseChannel)_channels.objectAtIndex(i);
+ if (!c.isFetchInProgress())
+ return c;
+ }
+ EODatabaseChannel c = new EODatabaseChannel(this);
+ registerChannel(c);
+ return c;
+ }
+
+ public void batchFetchRelationship(EORelationship rel, NSArray arr, EOEditingContext ec) {
+ }
+
+ public static void setContextClassToRegister(Class contextClass) {
+ _contextClass = contextClass;
+ }
+ public static Class contextClassToRegister() {
+ if (_contextClass == null)
+ _contextClass = EODatabaseContext.class;
+ return _contextClass;
+ }
+
+ public EOObjectStoreCoordinator coordinator() {
+ return _coordinator;
+ }
+
+ public void editingContextDidForgetObjectWithGlobalID(EOEditingContext ec, EOGlobalID gid) {
+ database().decrementSnapshotCountForGlobalID(gid);
+ }
+
+ public void handleDroppedConnection() {
+ //TODO: unregister channels
+ adaptorContext().handleDroppedConnection();
+ }
+
+ /* (non-Javadoc)
+ * @see net.wotonomy.control.EOCooperatingObjectStore#ownsGlobalID(net.wotonomy.control.EOGlobalID)
+ */
+ public boolean ownsGlobalID(EOGlobalID gid) {
+ if (!(gid instanceof EOKeyGlobalID))
+ return false;
+ return (database().entityNamed(((EOKeyGlobalID)gid).entityName()) != null);
+ }
+
+ /* (non-Javadoc)
+ * @see net.wotonomy.control.EOCooperatingObjectStore#ownsObject(net.wotonomy.control.EOEnterpriseObject)
+ */
+ public boolean ownsObject(EOEnterpriseObject eo) {
+ if (eo.entityName() == null)
+ return false;
+ return (database().entityNamed(eo.entityName()) != null);
+ }
+
+ /* (non-Javadoc)
+ * @see net.wotonomy.control.EOCooperatingObjectStore#handlesFetchSpecification(net.wotonomy.control.EOFetchSpecification)
+ */
+ public boolean handlesFetchSpecification(EOFetchSpecification fspec) {
+ String ename = fspec.entityName();
+ return (database().entityNamed(ename) != null);
+ }
+
+ public boolean hasBusyChannels() {
+ return adaptorContext().hasBusyChannels();
+ }
+
+ /* (non-Javadoc)
+ * @see net.wotonomy.control.EOCooperatingObjectStore#prepareForSaveWithCoordinator(net.wotonomy.control.EOObjectStoreCoordinator, net.wotonomy.control.EOEditingContext)
+ */
+ public void prepareForSaveWithCoordinator(EOObjectStoreCoordinator coord, EOEditingContext ec) {
+ // TODO Auto-generated method stub
+ _coordinator = coord;
+ _currEC = ec;
+ }
+
+ /* (non-Javadoc)
+ * @see net.wotonomy.control.EOCooperatingObjectStore#recordChangesInEditingContext()
+ */
+ public void recordChangesInEditingContext() {
+ // TODO insert, delete, update
+ }
+
+ /* (non-Javadoc)
+ * @see net.wotonomy.control.EOCooperatingObjectStore#recordUpdateForObject(net.wotonomy.control.EOEnterpriseObject, net.wotonomy.foundation.NSDictionary)
+ */
+ public void recordUpdateForObject(EOEnterpriseObject eo, NSDictionary changes) {
+ // TODO Auto-generated method stub
+ }
+
+ public void recordSnapshotForGlobalID(NSDictionary snap, EOGlobalID gid) {
+ if (_simpleSnaps == null)
+ throw new IllegalArgumentException("Attempt to record a snapshot without a transaction in progress");
+ _simpleSnaps.setObjectForKey(snap, gid);
+ }
+
+ public void recordSnapshotForSourceGlobalID(NSArray gids, EOGlobalID gid, String relationName) {
+ if (_manySnaps == null)
+ throw new IllegalArgumentException("Attempt to record a snapshot without a transaction in progress");
+ NSMutableDictionary d = (NSMutableDictionary)_manySnaps.objectForKey(gid);
+ if (d == null) {
+ d = new NSMutableDictionary();
+ _manySnaps.setObjectForKey(d, gid);
+ }
+ d.setObjectForKey(gids, relationName);
+ }
+
+ public void recordSnapshots(NSDictionary snaps) {
+ if (_simpleSnaps == null)
+ throw new IllegalArgumentException("Attempt to record snapshots without a transaction in progress.");
+ _simpleSnaps.addEntriesFromDictionary(snaps);
+ /* Make sure we don't need to do this instead
+ Enumeration enumeration = snaps.keyEnumerator();
+ while (enumeration.hasMoreElements()) {
+ EOGlobalID g = (EOGlobalID)enumeration.nextElement();
+ NSDictionary d = (NSDictionary)snaps.objectForKey(g);
+ recordSnapshotForGlobalID(d, g);
+ }*/
+ }
+
+ public void recordToManySnapshots(NSDictionary snaps) {
+ if (_manySnaps == null)
+ throw new IllegalArgumentException("Attempt to record snapshots without a transaction in progress.");
+ Enumeration enumeration = snaps.keyEnumerator();
+ while (enumeration.hasMoreElements()) {
+ Object key = enumeration.nextElement();
+ NSDictionary d = (NSDictionary)snaps.objectForKey(key);
+ NSMutableDictionary d2 = (NSMutableDictionary)_manySnaps.objectForKey(key);
+ if (d2 == null) {
+ d2 = new NSMutableDictionary();
+ _manySnaps.setObjectForKey(d2, key);
+ }
+ //this could also be done with many calls to recordSnapshotForSourceGID
+ d2.addEntriesFromDictionary(d);
+ }
+ }
+
+ /* (non-Javadoc)
+ * @see net.wotonomy.control.EOCooperatingObjectStore#performChanges()
+ */
+ public void performChanges() {
+ // TODO Auto-generated method stub
+
+ }
+
+ /* (non-Javadoc)
+ * @see net.wotonomy.control.EOCooperatingObjectStore#commitChanges()
+ */
+ public void commitChanges() {
+ adaptorContext().commitTransaction();
+ }
+
+ /* (non-Javadoc)
+ * @see net.wotonomy.control.EOCooperatingObjectStore#rollbackChanges()
+ */
+ public void rollbackChanges() {
+ adaptorContext().rollbackTransaction();
+ }
+
+ /* (non-Javadoc)
+ * @see net.wotonomy.control.EOCooperatingObjectStore#valuesForKeys(net.wotonomy.foundation.NSArray, net.wotonomy.control.EOEnterpriseObject)
+ */
+ public NSDictionary valuesForKeys(NSArray keys, EOEnterpriseObject eo) {
+ // TODO check snapshots; eo could be a fault
+ return eo.valuesForKeys(keys);
+ }
+
+ /* (non-Javadoc)
+ * @see net.wotonomy.foundation.NSLocking#lock()
+ */
+ public void lock() {
+ EOAccessLock.lock();
+ }
+
+ /* (non-Javadoc)
+ * @see net.wotonomy.foundation.NSLocking#unlock()
+ */
+ public void unlock() {
+ EOAccessLock.unlock();
+ }
+
+ /* (non-Javadoc)
+ * @see net.wotonomy.control.EOObjectStore#arrayFaultWithSourceGlobalID(net.wotonomy.control.EOGlobalID, java.lang.String, net.wotonomy.control.EOEditingContext)
+ */
+ public NSArray arrayFaultWithSourceGlobalID(
+ EOGlobalID gid, String relName, EOEditingContext ec) {
+ if (!(gid instanceof EOKeyGlobalID))
+ throw new IllegalArgumentException("an EOKeyGlobalID is needed.");
+ EOAccessArrayFaultHandler handler = new EOAccessArrayFaultHandler((EOKeyGlobalID)gid, relName, this, ec);
+ return new NSArray(handler);
+ }
+
+ /* (non-Javadoc)
+ * @see net.wotonomy.control.EOObjectStore#faultForGlobalID(net.wotonomy.control.EOGlobalID, net.wotonomy.control.EOEditingContext)
+ */
+ public /*EOEnterpriseObject*/Object faultForGlobalID(EOGlobalID gid, EOEditingContext ec) {
+ if (!(gid instanceof EOKeyGlobalID))
+ throw new IllegalArgumentException("Cannot fault an object that doesn't have a key global ID.");
+ EOAccessFaultHandler handler = new EOAccessFaultHandler((EOKeyGlobalID)gid, this, ec);
+ EOEntity e = database().entityNamed(((EOKeyGlobalID)gid).entityName());
+ Object o = e.classDescriptionForInstances().createInstanceWithEditingContext(ec, gid);
+ EOFaultHandler.makeObjectIntoFault(o, handler);
+ return o;
+ }
+
+ /* (non-Javadoc)
+ * @see net.wotonomy.control.EOObjectStore#faultForRawRow(java.util.Map, java.lang.String, net.wotonomy.control.EOEditingContext)
+ */
+ public /*EOEnterpriseObject*/ Object faultForRawRow(Map row, String entityName, EOEditingContext ec) {
+ EOEntity e = database().entityNamed(entityName);
+ EOGlobalID gid = e.globalIDForRow(row);
+ return faultForGlobalID(gid, ec);
+ }
+
+ public void forgetSnapshotForGlobalID(EOGlobalID gid) {
+ if (_simpleSnaps == null)
+ throw new IllegalArgumentException("Attempt to forget snapshot with no transaction in progress.");
+ _simpleSnaps.removeObjectForKey(gid);
+ _manySnaps.removeObjectForKey(gid);
+ }
+
+ public void forgetSnapshotsForGlobalIDs(List gids) {
+ for (int i = 0; i < gids.size(); i++) {
+ EOGlobalID g = (EOGlobalID)gids.get(i);
+ forgetSnapshotForGlobalID(g);
+ database().forgetSnapshotForGlobalID(g);
+ }
+ }
+
+ /* (non-Javadoc)
+ * @see net.wotonomy.control.EOObjectStore#initializeObject(java.lang.Object, net.wotonomy.control.EOGlobalID, net.wotonomy.control.EOEditingContext)
+ */
+ public void initializeObject(/*EOEnterpriseObject*/Object eo, EOGlobalID gid, EOEditingContext ec) {
+ if (gid.isTemporary())
+ return;
+ NSDictionary snap = snapshotForGlobalID(gid);
+ Object obj = ec.objectForGlobalID(gid);
+ EOEntity e = database().entityNamed(((EOKeyGlobalID)gid).entityName());
+ NSArray props = e.classProperties();
+ for (int i = 0; i < props.count(); i++) {
+ EOProperty p = (EOProperty)props.objectAtIndex(i);
+ Object val = snap.objectForKey(p.name());
+ if (p instanceof EOAttribute) {
+ if ( eo instanceof EOKeyValueCoding )
+ {
+ ((EOKeyValueCoding)eo).takeValueForKey(val, p.name());
+ }
+ else
+ {
+ EOKeyValueCodingSupport.takeValueForKey( eo, val, p.name() );
+ }
+ } else if (p instanceof EORelationship) {
+ if (((EORelationship)p).isToMany()) {
+ val = arrayFaultWithSourceGlobalID(gid, p.name(), ec);
+ } else {
+ EOEntity dest = ((EORelationship)p).destinationEntity();
+ EOKeyGlobalID kgid = (EOKeyGlobalID)dest.globalIDForRow(snap);
+ val = ec.objectForGlobalID(kgid);
+ if (val == null)
+ val = new EOAccessFaultHandler(kgid, this, ec);
+ }
+ if (val == EOKeyValueCoding.NullValue)
+ val = null;
+ if ( eo instanceof EOKeyValueCoding )
+ {
+ ((EOKeyValueCoding)eo).takeValueForKey(val, p.name());
+ }
+ else
+ {
+ EOKeyValueCodingSupport.takeValueForKey( eo, val, p.name() );
+ }
+ }
+ }
+ }
+
+ /* (non-Javadoc)
+ * @see net.wotonomy.control.EOObjectStore#invalidateAllObjects()
+ */
+ public void invalidateAllObjects() {
+ invalidateObjectsWithGlobalIDs(database().snapshots().allKeys());
+ }
+
+ /* (non-Javadoc)
+ * @see net.wotonomy.control.EOObjectStore#invalidateObjectsWithGlobalIDs(java.util.List)
+ */
+ public void invalidateObjectsWithGlobalIDs(List aList) {
+ forgetSnapshotsForGlobalIDs(aList);
+ }
+
+ /* (non-Javadoc)
+ * @see net.wotonomy.control.EOObjectStore#isObjectLockedWithGlobalID(net.wotonomy.control.EOGlobalID, net.wotonomy.control.EOEditingContext)
+ */
+ public boolean isObjectLockedWithGlobalID(EOGlobalID gid, EOEditingContext ec) {
+ return isObjectLockedWithGlobalID(gid);
+ }
+
+ public boolean isObjectLockedWithGlobalID(EOGlobalID gid) {
+ return _lockedObjects.containsObject(gid);
+ }
+
+ /* (non-Javadoc)
+ * @see net.wotonomy.control.EOObjectStore#lockObjectWithGlobalID(net.wotonomy.control.EOGlobalID, net.wotonomy.control.EOEditingContext)
+ */
+ public void lockObjectWithGlobalID(EOGlobalID gid, EOEditingContext ec) {
+ NSDictionary snap = snapshotForGlobalID(gid);
+ if (snap == null)
+ return;
+ if (!(gid instanceof EOKeyGlobalID))
+ return;
+ EOEntity e = database().entityNamed(((EOKeyGlobalID)gid).entityName());
+ EOQualifier q = e.qualifierForPrimaryKey(snap);
+ EOFetchSpecification fspec = new EOFetchSpecification(e.name(), q, null);
+ fspec.setLocksObjects(true);
+ NSArray arr = ec.objectsWithFetchSpecification(fspec);
+ if (arr.count() != 1)
+ throw new IllegalStateException("Cannot lock object with Global ID " + gid);
+ }
+
+ /* (non-Javadoc)
+ * @see net.wotonomy.control.EOObjectStore#objectsForSourceGlobalID(net.wotonomy.control.EOGlobalID, java.lang.String, net.wotonomy.control.EOEditingContext)
+ */
+ public NSArray objectsForSourceGlobalID(
+ EOGlobalID gid, String relationName, EOEditingContext ec) {
+ EOEnterpriseObject eo = (EOEnterpriseObject)ec.objectForGlobalID(gid);
+ if (eo == null)
+ throw new IllegalStateException("Cannot find object for global ID " + gid + " in specified editing context.");
+ //Get the source object
+ EOEnterpriseObject source = (EOEnterpriseObject) faultForGlobalID(gid, ec);
+ if (source == null)
+ throw new IllegalStateException("There is no snapshot for source global ID " + gid);
+
+ //Check if there is already a value here
+ NSArray value = (NSArray)source.valueForKey(relationName);
+ EOAccessArrayFaultHandler handler = null;
+ if (value != null) {
+ if (EOFaultHandler.isFault(value)) {
+ handler = new EOAccessArrayFaultHandler((EOKeyGlobalID)gid, relationName, this, ec);
+ //TODO: fire the fault an return the value
+ } else
+ return value;
+ }
+
+ //Get the relationship
+ EOEntity entity = database().entityNamed(eo.entityName());
+ EORelationship rel = entity.relationshipNamed(relationName);
+ if (rel == null)
+ throw new IllegalStateException("Cannot find relationship named " + relationName + " in entity " + entity.name());
+
+ //create a fetch specification for this
+ EOQualifier q = null;
+ NSArray joins = rel.joins();
+ NSMutableArray subq = new NSMutableArray(joins.count());
+ for (int i = 0; i < joins.count(); i++) {
+ EOJoin j = (EOJoin)joins.objectAtIndex(i);
+ String key = j.destinationAttribute().name();
+ Object val = eo.valueForKey(j.sourceAttribute().name());
+ subq.addObject(new EOKeyValueQualifier(key, EOQualifier.QualifierOperatorEqual, val));
+ }
+ if (subq.count() == 1) {
+ q = (EOQualifier)subq.objectAtIndex(0);
+ } else {
+ q = new EOAndQualifier(subq);
+ }
+ EOFetchSpecification fspec = new EOFetchSpecification(rel.destinationEntity().name(), q, null);
+ NSArray res = ec.objectsWithFetchSpecification(fspec, ec);
+ NSMutableArray gids = new NSMutableArray(res.count());
+ for (int i = 0; i < res.count(); i++)
+ gids.addObject(ec.globalIDForObject(res.objectAtIndex(i)));
+ recordSnapshotForSourceGlobalID(gids, gid, relationName);
+ return res;
+ }
+
+ /* (non-Javadoc)
+ * @see net.wotonomy.control.EOObjectStore#objectsWithFetchSpecification(net.wotonomy.control.EOFetchSpecification, net.wotonomy.control.EOEditingContext)
+ */
+ public NSArray objectsWithFetchSpecification(EOFetchSpecification fspec, EOEditingContext ec) {
+ EODatabaseChannel channel = availableChannel();
+ channel.selectObjectsWithFetchSpecification(fspec, ec);
+ NSMutableArray arr = new NSMutableArray();
+ while (channel.isFetchInProgress()) {
+ Object o = channel.fetchObject();
+ if (o != null) {
+ arr.addObject(o);
+ }
+ }
+ return arr;
+ }
+
+ /* (non-Javadoc)
+ * @see net.wotonomy.control.EOObjectStore#refaultObject(java.lang.Object, net.wotonomy.control.EOGlobalID, net.wotonomy.control.EOEditingContext)
+ */
+ public void refaultObject(Object obj, EOGlobalID gid, EOEditingContext ec) {
+ if (!(gid instanceof EOKeyGlobalID))
+ throw new IllegalArgumentException("GlobalID must be an EOKeyGlobalID");
+ if (obj instanceof EOFaulting)
+ //check if it's already a fault
+ if (!EOFaultHandler.isFault(obj)) {
+ ((EOFaulting)obj).turnIntoFault(EOFaultHandler.handlerForFault(obj));
+ }
+ }
+
+ /* (non-Javadoc)
+ * @see net.wotonomy.control.EOObjectStore#saveChangesInEditingContext(net.wotonomy.control.EOEditingContext)
+ */
+ public void saveChangesInEditingContext(EOEditingContext ec) {
+ prepareForSaveWithCoordinator(null, ec);
+ recordChangesInEditingContext();
+ performChanges();
+ commitChanges();
+ }
+
+ public void registerChannel(EODatabaseChannel channel) {
+ if (channel.databaseContext() != this)
+ throw new IllegalArgumentException("Cannot register a channel on a context other than its own.");
+ if (_channels.containsObject(channel))
+ throw new IllegalArgumentException("Attempt to register a channel more than once.");
+ _channels.addObject(channel);
+ }
+
+ public void unregisterChannel(EODatabaseChannel channel) {
+ if (channel.databaseContext() != this)
+ throw new IllegalArgumentException("Attempt to unregister a channel from a context other than its own.");
+ _channels.removeObject(channel);
+ }
+
+ public NSArray registeredChannels() {
+ return new NSArray(_channels);
+ }
+
+ public NSDictionary snapshotForGlobalID(EOGlobalID gid) {
+ NSDictionary d = null;
+ if (_simpleSnaps != null) {
+ d = (NSDictionary)_simpleSnaps.objectForKey(gid);
+ }
+ if (d == null)
+ d = database().snapshotForGlobalID(gid);
+ return d;
+ }
+
+ public NSArray snapshotForSourceGlobalID(EOGlobalID gid, String name) {
+ NSArray a = null;
+ if (_manySnaps != null) {
+ NSDictionary d = (NSDictionary)_manySnaps.objectForKey(gid);
+ a = (NSArray)d.objectForKey(name);
+ }
+ if (a == null)
+ a = database().snapshotForSourceGlobalID(gid, name);
+ return a;
+ }
+
+ public void setUpdateStrategy(int strategy) {
+ _updateStrategy = strategy;
+ }
+
+ public int updateStrategy() {
+ return _updateStrategy;
+ }
+
+}
+/*
+ * $Log$
+ * Revision 1.2 2006/02/16 16:47:13 cgruber
+ * Move some classes in to "internal" packages and re-work imports, etc.
+ *
+ * Also use UnsupportedOperationExceptions where appropriate, instead of WotonomyExceptions.
+ *
+ * Revision 1.1 2006/02/16 13:19:57 cgruber
+ * Check in all sources in eclipse-friendly maven-enabled packages.
+ *
+ * Revision 1.5 2005/05/11 15:21:53 cgruber
+ * Change enum to enumeration, since enum is now a keyword as of Java 5.0
+ *
+ * A few other comments in the code.
+ *
+ * Revision 1.4 2003/12/18 15:37:38 mpowers
+ * Changes to retain ability to work with objects that don't necessarily
+ * implement EOEnterpriseObject. I would still like to preserve this case
+ * for general usage, however the access package is free to assume that
+ * those objects will be EOs and cast appropriately.
+ *
+ * Revision 1.3 2003/08/29 21:14:44 chochos
+ * implement a couple more methods.
+ *
+ * Revision 1.2 2003/08/20 01:16:22 chochos
+ * more methods have code now, but there's no way to test this yet. The core is still pending.
+ *
+ * Revision 1.1 2003/08/19 01:54:43 chochos
+ * The EODatabase layer still needs a lot of work, but it's on its way...
+ *
+ */ \ No newline at end of file
diff --git a/projects/net.wotonomy.persistence/src/main/java/net/wotonomy/access/EODatabaseOperation.java b/projects/net.wotonomy.persistence/src/main/java/net/wotonomy/access/EODatabaseOperation.java
new file mode 100644
index 0000000..596180e
--- /dev/null
+++ b/projects/net.wotonomy.persistence/src/main/java/net/wotonomy/access/EODatabaseOperation.java
@@ -0,0 +1,48 @@
+/*
+ Wotonomy: OpenStep design patterns for pure Java applications.
+ Copyright (C) 2001 Michael Powers
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, see http://www.gnu.org
+ */
+package net.wotonomy.access;
+
+/**
+*
+* @author ezamudio@nasoft.com
+* @author $Author: cgruber $
+* @version $Revision: 893 $
+*/
+public class EODatabaseOperation {
+
+ public static final int AdaptorLockOperator = 0;
+ public static final int AdaptorInsertOperator = 1;
+ public static final int AdaptorUpdateOperator = 2;
+ public static final int AdaptorDeleteOperator = 3;
+ public static final int AdaptorStoredProcedureOperator = 4;
+
+ public static final int DatabaseNothingOperator = 0;
+ public static final int DatabaseInsertOperator = 1;
+ public static final int DatabaseUpdateOperator = 2;
+ public static final int DatabaseDeleteOperator = 3;
+
+}
+/*
+ * $Log$
+ * Revision 1.1 2006/02/16 13:19:57 cgruber
+ * Check in all sources in eclipse-friendly maven-enabled packages.
+ *
+ * Revision 1.1 2003/08/13 00:38:47 chochos
+ * for the moment, this only contains some constants needed by EOAdaptorChannel and EOAdaptorOperation.
+ *
+ */ \ No newline at end of file
diff --git a/projects/net.wotonomy.persistence/src/main/java/net/wotonomy/access/EOEntity.java b/projects/net.wotonomy.persistence/src/main/java/net/wotonomy/access/EOEntity.java
new file mode 100644
index 0000000..4adc4a1
--- /dev/null
+++ b/projects/net.wotonomy.persistence/src/main/java/net/wotonomy/access/EOEntity.java
@@ -0,0 +1,637 @@
+/*
+ Wotonomy: OpenStep design patterns for pure Java applications.
+ Copyright (C) 2001 Michael Powers
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, see http://www.gnu.org
+ */
+package net.wotonomy.access;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.util.Map;
+
+import net.wotonomy.control.EOAndQualifier;
+import net.wotonomy.control.EOClassDescription;
+import net.wotonomy.control.EOFetchSpecification;
+import net.wotonomy.control.EOGenericRecord;
+import net.wotonomy.control.EOGlobalID;
+import net.wotonomy.control.EOIntegralKeyGlobalID;
+import net.wotonomy.control.EOKeyGlobalID;
+import net.wotonomy.control.EOKeyValueArchiver;
+import net.wotonomy.control.EOKeyValueQualifier;
+import net.wotonomy.control.EOKeyValueUnarchiver;
+import net.wotonomy.control.EOQualifier;
+import net.wotonomy.control.EOVectorKeyGlobalID;
+import net.wotonomy.foundation.NSArray;
+import net.wotonomy.foundation.NSDictionary;
+import net.wotonomy.foundation.NSKeyValueCoding;
+import net.wotonomy.foundation.NSMutableArray;
+import net.wotonomy.foundation.NSMutableDictionary;
+import net.wotonomy.foundation.NSPropertyListSerialization;
+
+/**
+* An EOEntity is a mapping between a Java class and a database table or view.
+* It indicates which attributes should be fetched from the table/view, what
+* attributes are part of the primary key, what class the entity should map to.
+*
+* @author ezamudio@nasoft.com
+* @author $Author: cgruber $
+* @version $Revision: 894 $
+*/
+public class EOEntity implements EOPropertyListEncoding {
+
+ protected NSMutableDictionary _attributes = new NSMutableDictionary();
+ protected NSMutableDictionary _relations = new NSMutableDictionary();
+ private NSMutableArray _classPropertyNames = new NSMutableArray();
+ private NSMutableArray _classProperties = new NSMutableArray();
+ private NSMutableArray _classPropertyAttributes = new NSMutableArray();
+ private NSMutableArray _classPropertyManyRelationships = new NSMutableArray();
+ private NSMutableArray _classPropertyOneRelationships = new NSMutableArray();
+ protected NSArray _pkAttributes = NSArray.EmptyArray;
+ protected NSArray _pkAttributeNames = NSArray.EmptyArray;
+ protected NSMutableDictionary _fetchSpecs = new NSMutableDictionary();
+ protected NSMutableArray _lockingAttributes = new NSMutableArray();
+
+ protected String _className;
+ protected String _name;
+ protected String _externalName;
+ protected boolean _isAbstract;
+ protected boolean _isReadOnly;
+ protected EOModel _model;
+ protected NSDictionary _userInfo;
+ protected NSDictionary _internalInfo;
+ private boolean _loadedFetchSpecs;
+
+ public EOEntity() {
+ super();
+ }
+
+ public EOEntity(NSDictionary dict, Object obj) {
+ super();
+ _model = (EOModel)obj;
+ setName((String)dict.objectForKey("name"));
+ setExternalName((String)dict.objectForKey("externalName"));
+ setClassName((String)dict.objectForKey("className"));
+ if (dict.objectForKey("internalInfo") != null)
+ _internalInfo = (NSDictionary)dict.objectForKey("internalInfo");
+ if (dict.objectForKey("userInfo") != null)
+ _userInfo = (NSDictionary)dict.objectForKey("userInfo");
+
+ //Read the attributes
+ NSArray atr = (NSArray)dict.objectForKey("attributes");
+ for (int i = 0; i < atr.count(); i++) {
+ NSDictionary d = (NSDictionary)atr.objectAtIndex(i);
+ EOAttribute atrib = new EOAttribute(d, this);
+ addAttribute(atrib);
+ }
+
+ //Set the primary key
+ atr = (NSArray)dict.objectForKey("primaryKeyAttributes");
+ NSMutableArray pka = new NSMutableArray();
+ for (int i = 0; i < atr.count(); i++) {
+ EOAttribute a = attributeNamed((String)atr.objectAtIndex(i));
+ pka.addObject(a);
+ }
+ _pkAttributes = new NSArray(pka);
+ _pkAttributeNames = atr;
+
+ //attributes used for locking
+ _lockingAttributes.removeAllObjects();
+ atr = (NSArray)dict.objectForKey("attributesUsedForLocking");
+ for (int i = 0; i < atr.count(); i++) {
+ String x = (String)atr.objectAtIndex(i);
+ EOAttribute a = attributeNamed(x);
+ _lockingAttributes.addObject(a);
+ }
+
+ //class properties
+ atr = (NSArray)dict.objectForKey("classProperties");
+ if (atr != null) {
+ for (int i = 0; i < atr.count(); i++)
+ if (!_classPropertyNames.containsObject((atr.objectAtIndex(i))))
+ _classPropertyNames.addObject(atr.objectAtIndex(i));
+ }
+
+ //Read the relationships
+ atr = (NSArray)dict.objectForKey("relationships");
+ if (atr != null) {
+ for (int i = 0; i < atr.count(); i++) {
+ NSDictionary d = (NSDictionary)atr.objectAtIndex(i);
+ EORelationship rel = new EORelationship(d, this);
+ addRelationship(rel);
+ }
+ }
+ }
+
+ public void addAttribute(EOAttribute atr) {
+ if (atr.name() == null)
+ throw new IllegalArgumentException("Cannot add an unnamed attribute to an entity.");
+ if (_attributes.objectForKey(atr.name()) != null)
+ throw new IllegalArgumentException("Entity " + name() + " already has an attribute named " + atr.name());
+ _attributes.setObjectForKey(atr, atr.name());
+ atr.setEntity(this);
+ _lockingAttributes.addObject(atr);
+ _classProperties.addObject(atr);
+ _classPropertyNames.addObject(atr.name());
+ _classPropertyAttributes.addObject(atr);
+ }
+
+ public void removeAttribute(EOAttribute atr) {
+ _attributes.removeObjectForKey(atr.name());
+ atr.setEntity(null);
+ _classProperties.removeObject(atr);
+ _classPropertyNames.removeObject(atr.name());
+ _classPropertyAttributes.removeObject(atr);
+ }
+
+ public void addFetchSpecification(EOFetchSpecification fspec, String name) {
+ loadFetchSpecifications();
+ if (_fetchSpecs.objectForKey(name) != null)
+ throw new IllegalArgumentException("Entity " + name() + " already has a fetch specification named " + name);
+ _fetchSpecs.setObjectForKey(fspec, name);
+ }
+
+ public void removeFetchSpecificationNamed(String name) {
+ _fetchSpecs.removeObjectForKey(name);
+ }
+
+ public EOFetchSpecification fetchSpecificationNamed(String name) {
+ loadFetchSpecifications();
+ return (EOFetchSpecification)_fetchSpecs.objectForKey(name);
+ }
+
+ public NSArray fetchSpecificationNames() {
+ loadFetchSpecifications();
+ return _fetchSpecs.allKeys();
+ }
+
+ /** Loads fetch specifications from the .fspec file,
+ * if one exists.
+ */
+ private void loadFetchSpecifications() {
+ if (_loadedFetchSpecs)
+ return;
+ _loadedFetchSpecs = true;
+ if (model().path() == null)
+ return;
+ File f = new File(model().path());
+ //Read the fetch specification file, if it exists
+ f = new File(f, name() + ".fspec");
+ if (!f.exists())
+ return;
+ NSDictionary fdict = null;
+ String x = null;
+ try {
+ FileInputStream fin = new FileInputStream(f);
+ byte[] b = new byte[fin.available()];
+ fin.read(b);
+ fin.close();
+ x = new String(b);
+ } catch (IOException ex) {
+ throw new IllegalArgumentException("Cannot read file " + f);
+ }
+ fdict = NSPropertyListSerialization.dictionaryForString(x);
+ if (fdict == null)
+ throw new IllegalArgumentException("Cannot read dictionary from " + f);
+ NSArray keys = fdict.allKeys();
+
+ //Unarchive the fetch specification
+ EOKeyValueUnarchiver unarch = new EOKeyValueUnarchiver(fdict);
+ for (int i = 0; i < keys.count(); i++) {
+ String k = (String)keys.objectAtIndex(i);
+ EOFetchSpecification fs = (EOFetchSpecification)unarch.decodeObjectForKey(k);
+ if (fs != null)
+ _fetchSpecs.setObjectForKey(fs, k);
+ }
+ }
+
+ public NSArray attributes() {
+ return _attributes.allValues();
+ }
+
+ public EOAttribute attributeNamed(String name) {
+ return (EOAttribute)_attributes.objectForKey(name);
+ }
+
+ public NSArray flattenedAttributes() {
+ return null;
+ }
+
+ public void setClassName(String name) {
+ _className = name;
+ }
+ public String className() {
+ return _className;
+ }
+
+ public void setName(String name) {
+ _name = name;
+ }
+ public String name() {
+ return _name;
+ }
+
+ public void setExternalName(String name) {
+ _externalName = name;
+ }
+ public String externalName() {
+ return _externalName;
+ }
+
+ public void addRelationship(EORelationship rel) {
+ if (rel.name() == null)
+ throw new IllegalArgumentException("Cannot add an unnamed relationship to an entity.");
+ if (_relations.objectForKey(rel.name()) != null)
+ throw new IllegalArgumentException("Entity " + name() + " already has a relationship named " + rel.name());
+ if (_attributes.objectForKey(rel.name()) != null)
+ throw new IllegalArgumentException("Entity " + name() + " has an attribute named " + rel.name());
+ _relations.setObjectForKey(rel, rel.name());
+ _classProperties.addObject(rel);
+ _classPropertyNames.addObject(rel.name());
+ if (rel.isToMany())
+ _classPropertyManyRelationships.addObject(rel);
+ else
+ _classPropertyOneRelationships.addObject(rel);
+ }
+
+ public void removeRelationship(EORelationship rel) {
+ _relations.removeObjectForKey(rel.name());
+ _classProperties.removeObject(rel);
+ _classPropertyNames.removeObject(rel.name());
+ _classPropertyManyRelationships.removeObject(rel);
+ _classPropertyOneRelationships.removeObject(rel);
+ }
+
+ /** Returns the relationships from this entity to other entities.
+ * @return An array of the relationships of this entity.
+ */
+ public NSArray relationships() {
+ return _relations.allValues();
+ }
+
+ public EORelationship relationshipNamed(String name) {
+ return (EORelationship)_relations.objectForKey(name);
+ }
+ public EOModel model() {
+ return _model;
+ }
+
+ public void setPrimaryKeyAtributes(NSArray pk) {
+ _pkAttributes = pk;
+ }
+ public NSArray primaryKeyAttributes() {
+ return _pkAttributes;
+ }
+
+ public NSArray primaryKeyAttributeNames() {
+ if (_pkAttributeNames.count() != _pkAttributes.count()) {
+ NSMutableArray arr = new NSMutableArray();
+ for (int i = 0; i < _pkAttributes.count(); i++) {
+ EOAttribute a = (EOAttribute)_pkAttributes.objectAtIndex(i);
+ arr.addObject(a.name());
+ }
+ _pkAttributeNames = new NSArray(arr);
+ }
+ return _pkAttributeNames;
+ }
+
+ public boolean hasSimplePrimaryKey() {
+ return _pkAttributes.count() == 1;
+ }
+
+ public boolean isValidPrimaryKeyAttribute(EOAttribute attr) {
+ return !attr.allowsNull();
+ }
+
+ public void setAttributesUsedForLocking(NSArray value) {
+ _lockingAttributes.removeAllObjects();
+ _lockingAttributes.addObjectsFromArray(value);
+ }
+ public NSArray attributesUsedForLocking() {
+ return new NSArray(_lockingAttributes);
+ }
+
+ public void setClassProperties(NSArray value) {
+ _classProperties.removeAllObjects();
+ _classProperties.addObjectsFromArray(value);
+ _classPropertyNames.removeAllObjects();
+ _classPropertyAttributes.removeAllObjects();
+ _classPropertyOneRelationships.removeAllObjects();
+ _classPropertyManyRelationships.removeAllObjects();
+ for (int i = 0; i < value.count(); i++) {
+ EOProperty o = (EOProperty)value.objectAtIndex(i);
+ _classPropertyNames.addObject(o.name());
+ if (o instanceof EOAttribute) {
+ _classPropertyAttributes.addObject(o);
+ } else if (o instanceof EORelationship) {
+ if (((EORelationship)o).isToMany())
+ _classPropertyManyRelationships.addObject(o);
+ else
+ _classPropertyOneRelationships.addObject(o);
+ }
+ }
+ }
+ public NSArray classProperties() {
+ if (_classProperties == null) {
+ if (_classPropertyNames == null)
+ return NSArray.EmptyArray;
+ else {
+ NSMutableArray props = new NSMutableArray();
+ NSMutableArray atribs = new NSMutableArray();
+ NSMutableArray ones = new NSMutableArray();
+ NSMutableArray manies = new NSMutableArray();
+ for (int i = 0; i < _classPropertyNames.count(); i++) {
+ String name = (String)_classPropertyNames.objectAtIndex(i);
+ EOAttribute a = attributeNamed(name);
+ EORelationship r = relationshipNamed(name);
+ if (a != null) {
+ props.addObject(a);
+ atribs.addObject(a);
+ } else if (r != null) {
+ props.addObject(r);
+ if (r.isToMany())
+ manies.addObject(r);
+ else
+ ones.addObject(r);
+ } else
+ throw new IllegalArgumentException("Cannot find attribute or relationship named " + name);
+ }
+ _classProperties = props;
+ _classPropertyAttributes = atribs;
+ _classPropertyOneRelationships = ones;
+ _classPropertyManyRelationships = manies;
+ }
+ }
+ return _classProperties;
+ }
+
+ public NSArray classPropertyNames() {
+ return _classPropertyNames;
+ }
+
+ public NSArray classPropertyAttributeNames() {
+ if (_classPropertyAttributes == null)
+ return NSArray.EmptyArray;
+ NSMutableArray arr = new NSMutableArray(_classPropertyAttributes.count());
+ for (int i = 0 ; i < _classPropertyAttributes.count(); i++) {
+ EOAttribute a = (EOAttribute)_classPropertyAttributes.objectAtIndex(i);
+ arr.addObject(a.name());
+ }
+ return arr;
+ }
+
+ public NSArray classPropertyToManyRelationshipNames() {
+ if (_classPropertyManyRelationships == null)
+ return NSArray.EmptyArray;
+ NSMutableArray arr = new NSMutableArray(_classPropertyManyRelationships.count());
+ for (int i = 0 ; i < _classPropertyManyRelationships.count(); i++) {
+ EOAttribute a = (EOAttribute)_classPropertyManyRelationships.objectAtIndex(i);
+ arr.addObject(a.name());
+ }
+ return arr;
+ }
+
+ public NSArray classPropertyToOneRelationshipNames() {
+ if (_classPropertyOneRelationships == null)
+ return NSArray.EmptyArray;
+ NSMutableArray arr = new NSMutableArray(_classPropertyOneRelationships.count());
+ for (int i = 0 ; i < _classPropertyOneRelationships.count(); i++) {
+ EOAttribute a = (EOAttribute)_classPropertyOneRelationships.objectAtIndex(i);
+ arr.addObject(a.name());
+ }
+ return arr;
+ }
+
+ public void setIsAbstractEntity(boolean flag) {
+ _isAbstract = flag;
+ }
+ public boolean isAbstractEntity() {
+ return _isAbstract;
+ }
+
+ public void setReadOnly(boolean flag) {
+ _isReadOnly = flag;
+ }
+ public boolean isReadOnly() {
+ return _isReadOnly;
+ }
+
+ public void setStoredProcedure(EOStoredProcedure proc, String operation) {
+ }
+ public EOStoredProcedure storedProcedureForOperation(String operation) {
+ return null;
+ }
+
+ public NSArray subEntities() {
+ return null;
+ }
+
+ public NSArray attributesToFetch() {
+ return attributes();
+ }
+
+ public NSArray externalModelsReferenced() {
+ return null;
+ }
+
+ public EOClassDescription classDescriptionForInstances() {
+ EOClassDescription cd = EOClassDescription.classDescriptionForEntityName(name());
+ if (cd == null) {
+ cd = new EOEntityClassDescription(this);
+ Class cl = null;
+ try {
+ cl = Class.forName(className());
+ } catch (ClassNotFoundException ex) {
+ cl = EOGenericRecord.class;
+ }
+ EOClassDescription.registerClassDescription(cd, cl);
+ }
+ return cd;
+ }
+
+ /**
+ * Creates a global ID for a row. The row must have values
+ * for all the primary key attributes.
+ * @param row A raw row for this entity.
+ * @return A key global ID to identify this row.
+ */
+ public EOGlobalID globalIDForRow(Map row) {
+ NSArray pknames = primaryKeyAttributeNames();
+ EOKeyGlobalID gid = null;
+ if (pknames.count() == 1 && row.get(pknames.objectAtIndex(0)) instanceof Number) {
+ Number n = (Number)row.get(pknames.objectAtIndex(0));
+ gid = new EOIntegralKeyGlobalID(name(), n);
+ } else {
+ Object[] vals = new Object[pknames.count()];
+ for (int i = 0; i < pknames.count(); i++) {
+ Object v = row.get(pknames.objectAtIndex(i));
+ vals[i] = v;
+ }
+ gid = new EOVectorKeyGlobalID(name(), vals);
+ }
+ return gid;
+ }
+
+ /**
+ * Returns a dictionary with the primary key values contained in
+ * the global id.
+ * @param gid A Key global ID.
+ * @return A dictionary with the primary key values for gid.
+ */
+ public NSDictionary primaryKeyForGlobalID(EOGlobalID gid) {
+ if (!(gid instanceof EOKeyGlobalID))
+ return null;
+ Object[] vals = ((EOKeyGlobalID)gid).keyValues();
+ NSArray pknames = primaryKeyAttributeNames();
+ return new NSDictionary(vals, pknames.toArray());
+ }
+
+ public EOQualifier qualifierForPrimaryKey(Map pkey) {
+ NSArray pknames = primaryKeyAttributeNames();
+ EOQualifier q = null;
+ NSMutableArray subq = new NSMutableArray(pknames.count());
+ for (int i = 0; i < pknames.count(); i++) {
+ String key = (String)pknames.objectAtIndex(i);
+ Object v = pkey.get(key);
+ if (v == null || v == NSKeyValueCoding.NullValue)
+ throw new IllegalArgumentException("Primary key with null values.");
+ subq.addObject(new EOKeyValueQualifier(key, EOQualifier.QualifierOperatorEqual, v));
+ }
+ if (subq.count() == 1)
+ q = (EOQualifier)subq.objectAtIndex(0);
+ else
+ q = new EOAndQualifier(subq);
+ return q;
+ }
+
+ public void setUserInfo(NSDictionary value) {
+ _userInfo = value;
+ }
+ public NSDictionary userInfo() {
+ return _userInfo;
+ }
+
+ public void awakeWithPropertyList(NSDictionary plist) {
+ }
+
+ public void encodeIntoPropertyList(NSMutableDictionary dict) {
+ dict.setObjectForKey(name(), "name");
+ dict.setObjectForKey(externalName(), "externalName");
+ dict.setObjectForKey(className(), "className");
+
+ //Encode attributes
+ NSMutableArray arr = new NSMutableArray(_attributes.allValues());
+ for (int i = 0; i < _attributes.count(); i++) {
+ EOAttribute a = (EOAttribute)arr.objectAtIndex(i);
+ NSMutableDictionary d = new NSMutableDictionary();
+ a.encodeIntoPropertyList(d);
+ arr.replaceObjectAtIndex(i, d);
+ }
+ dict.setObjectForKey(arr, "attributes");
+ //Encode relationships
+ if (_relations.count() > 0) {
+ arr = new NSMutableArray(_relations.allValues());
+ for (int i = 0; i < _relations.count(); i++) {
+ EORelationship r = (EORelationship)arr.objectAtIndex(i);
+ NSMutableDictionary d = new NSMutableDictionary();
+ r.encodeIntoPropertyList(d);
+ arr.replaceObjectAtIndex(i, d);
+ }
+ dict.setObjectForKey(arr, "relationships");
+ }
+
+ //Fetch specifications
+ NSMutableDictionary d = new NSMutableDictionary();
+ loadFetchSpecifications();
+ java.util.Enumeration enumeration = _fetchSpecs.keyEnumerator();
+ while (enumeration.hasMoreElements()) {
+ String k = (String)enumeration.nextElement();
+ EOFetchSpecification f = (EOFetchSpecification)_fetchSpecs.objectForKey(k);
+ EOKeyValueArchiver arch = new EOKeyValueArchiver();
+ f.encodeWithKeyValueArchiver(arch);
+ d.setObjectForKey(arch.dictionary(), k);
+ }
+ dict.setObjectForKey(d, "fetchSpecificationDictionary");
+
+ //Other information
+ dict.setObjectForKey(_classPropertyNames, "classProperties");
+ dict.setObjectForKey(_pkAttributeNames, "primaryKeyAttributes");
+ arr = new NSMutableArray(_lockingAttributes);
+ for (int i = 0; i < arr.count(); i++)
+ arr.replaceObjectAtIndex(i, ((EOAttribute)arr.objectAtIndex(i)).name());
+ dict.setObjectForKey(arr, "attributesUsedForLocking");
+ if (_userInfo != null && _userInfo.count() > 0 )
+ dict.setObjectForKey(_userInfo, "userInfo");
+ if (_internalInfo != null && _internalInfo.count() > 0 )
+ dict.setObjectForKey(_internalInfo, "internalInfo");
+ }
+
+ public EOAttribute _attributeForPath(String path) {
+ NSArray comps = NSArray.componentsSeparatedByString(path, ".");
+ if (comps.count() < 2)
+ return null;
+ EORelationship r = null;
+ EOEntity e = this;
+ for (int i = 0; i < comps.count()-1; i++) {
+ String name = (String)comps.objectAtIndex(i);
+ r = e.relationshipNamed(name);
+ if (r == null)
+ return null;
+ e = r.destinationEntity();
+ }
+ return e.attributeNamed((String)comps.lastObject());
+ }
+
+}
+/*
+ * $Log$
+ * Revision 1.2 2006/02/16 16:47:14 cgruber
+ * Move some classes in to "internal" packages and re-work imports, etc.
+ *
+ * Also use UnsupportedOperationExceptions where appropriate, instead of WotonomyExceptions.
+ *
+ * Revision 1.1 2006/02/16 13:19:57 cgruber
+ * Check in all sources in eclipse-friendly maven-enabled packages.
+ *
+ * Revision 1.9 2005/05/11 15:21:53 cgruber
+ * Change enum to enumeration, since enum is now a keyword as of Java 5.0
+ *
+ * A few other comments in the code.
+ *
+ * Revision 1.8 2003/08/19 19:47:58 chochos
+ * added some methods to handle EOGlobalIDs and primary keys.
+ *
+ * Revision 1.7 2003/08/14 02:13:56 chochos
+ * implement and use _attributeForPath()
+ *
+ * Revision 1.6 2003/08/11 19:38:27 chochos
+ * Can now read from a file and re-write to another file.
+ *
+ * Revision 1.5 2003/08/11 18:19:33 chochos
+ * encoding into property list seems to work fine now.
+ *
+ * Revision 1.4 2003/08/09 01:39:04 chochos
+ * better handling of class properties; use EOKeyValueArchiving to encode/decode fetch specifications; implement EOPropertyListEncoding
+ *
+ * Revision 1.3 2003/08/08 05:52:21 chochos
+ * gets the class description for the entity.
+ *
+ * Revision 1.2 2003/08/08 02:14:20 chochos
+ * now it can read stored procedures.
+ *
+ * Revision 1.1 2003/08/07 02:38:33 chochos
+ * implementation of EOEntity. What works for now is reading an entity from file.
+ *
+ */
diff --git a/projects/net.wotonomy.persistence/src/main/java/net/wotonomy/access/EOEntityClassDescription.java b/projects/net.wotonomy.persistence/src/main/java/net/wotonomy/access/EOEntityClassDescription.java
new file mode 100644
index 0000000..a235c99
--- /dev/null
+++ b/projects/net.wotonomy.persistence/src/main/java/net/wotonomy/access/EOEntityClassDescription.java
@@ -0,0 +1,185 @@
+/*
+Wotonomy: OpenStep design patterns for pure Java applications.
+Copyright (C) 2001 Intersect Software Corporation
+
+This library is free software; you can redistribute it and/or
+modify it under the terms of the GNU Lesser General Public
+License as published by the Free Software Foundation; either
+version 2.1 of the License, or (at your option) any later version.
+
+This library is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+Lesser General Public License for more details.
+
+You should have received a copy of the GNU Lesser General Public
+License along with this library; if not, see http://www.gnu.org
+*/
+package net.wotonomy.access;
+
+import net.wotonomy.control.EOClassDescription;
+import net.wotonomy.control.EOEditingContext;
+import net.wotonomy.control.EOFetchSpecification;
+import net.wotonomy.control.EOGenericRecord;
+import net.wotonomy.control.EOGlobalID;
+import net.wotonomy.foundation.NSArray;
+import net.wotonomy.foundation.NSMutableArray;
+
+/**
+* @author ezamudio@nasoft.com
+* @author $Author: cgruber $
+* @version $Revision: 894 $
+*/
+public class EOEntityClassDescription extends EOClassDescription {
+
+ protected EOEntity _entity;
+
+ public EOEntityClassDescription() {
+ super();
+ }
+
+ public EOEntityClassDescription(EOEntity entity) {
+ this();
+ _entity = entity;
+ }
+
+ public NSArray allAttributeKeys() {
+ NSArray arr = entity().attributes();
+ NSMutableArray a = new NSMutableArray(arr.count());
+ for (int i = 0; i < arr.count(); i++) {
+ EOAttribute atrib = (EOAttribute)arr.objectAtIndex(i);
+ a.addObject(atrib);
+ }
+ return a;
+ }
+
+ public NSArray allPropertyKeys() {
+ return entity().classPropertyNames();
+ }
+
+ public NSArray allToManyRelationshipKeys() {
+ NSArray arr = entity().relationships();
+ NSMutableArray a = new NSMutableArray(arr.count());
+ for (int i = 0; i < arr.count(); i++) {
+ EORelationship r = (EORelationship)arr.objectAtIndex(i);
+ if (r.isToMany())
+ a.addObject(r);
+ }
+ return a;
+ }
+
+ public NSArray allToOneRelationshipKeys() {
+ NSArray arr = entity().relationships();
+ NSMutableArray a = new NSMutableArray(arr.count());
+ for (int i = 0; i < arr.count(); i++) {
+ EORelationship r = (EORelationship)arr.objectAtIndex(i);
+ if (!r.isToMany())
+ a.addObject(r);
+ }
+ return a;
+ }
+
+ /** Returns all attributes that correspond to columns
+ * in a database table.
+ */
+ public NSArray attributeKeys() {
+ NSArray arr = entity().attributes();
+ NSMutableArray a = new NSMutableArray(arr.count());
+ for (int i = 0; i < arr.count(); i++) {
+ EOAttribute atrib = (EOAttribute)arr.objectAtIndex(i);
+ if (!atrib.isDerived())
+ a.addObject(atrib);
+ }
+ return a;
+ }
+
+ public EOClassDescription classDescriptionForDestinationKey(String key) {
+ EORelationship r = entity().relationshipNamed(key);
+ if (r == null)
+ return null;
+ return r.destinationEntity().classDescriptionForInstances();
+ }
+
+ public NSArray clientAttributeKeys() {
+ return null;
+ }
+
+ public NSArray clientToManyRelationshipKeys() {
+ return null;
+ }
+
+ public NSArray clientToOneRelationshipKeys() {
+ return null;
+ }
+
+ public EOEntity entity() {
+ return _entity;
+ }
+
+ public String entityName() {
+ return _entity.name();
+ }
+
+ public EOFetchSpecification fetchSpecificationNamed(String name) {
+ return entity().fetchSpecificationNamed(name);
+ }
+
+ public NSArray toManyRelationshipKeys() {
+ NSArray arr = entity().relationships();
+ NSMutableArray a = new NSMutableArray(arr.count());
+ for (int i = 0; i < arr.count(); i++) {
+ EORelationship r = (EORelationship)arr.objectAtIndex(i);
+ if (r.isToMany() && !r.isFlattened())
+ a.addObject(r);
+ }
+ return a;
+ }
+
+ public NSArray toOneRelationshipKeys() {
+ NSArray arr = entity().relationships();
+ NSMutableArray a = new NSMutableArray(arr.count());
+ for (int i = 0; i < arr.count(); i++) {
+ EORelationship r = (EORelationship)arr.objectAtIndex(i);
+ if (!r.isToMany() && !r.isFlattened())
+ a.addObject(r);
+ }
+ return a;
+ }
+
+ public Object createInstanceWithEditingContext(EOEditingContext ec, EOGlobalID gid) {
+ if (theClass == null) {
+ try {
+ theClass = Class.forName(entity().className());
+ } catch (ClassNotFoundException ex) {
+ if (entity().className().equals("net.wotonomy.control.EOGenericRecord"))
+ throw new IllegalArgumentException("Cannot find class " + entity().className());
+ theClass = EOGenericRecord.class;
+ }
+ }
+ return super.createInstanceWithEditingContext(ec, gid);
+ }
+
+}
+/*
+ * $Log$
+ * Revision 1.2 2006/02/16 16:47:13 cgruber
+ * Move some classes in to "internal" packages and re-work imports, etc.
+ *
+ * Also use UnsupportedOperationExceptions where appropriate, instead of WotonomyExceptions.
+ *
+ * Revision 1.1 2006/02/16 13:19:57 cgruber
+ * Check in all sources in eclipse-friendly maven-enabled packages.
+ *
+ * Revision 1.4 2003/08/19 01:55:54 chochos
+ * the behavior is now more consistent with its Apple counterpart.
+ *
+ * Revision 1.3 2003/08/09 01:40:31 chochos
+ * use EOClassDescription's methods to get a destination entity's class description.
+ *
+ * Revision 1.2 2003/08/08 05:51:59 chochos
+ * createInstanceWithEditingContext now works. It sets theClass with the class from the entity className() before calling super.
+ *
+ * Revision 1.1 2003/08/08 00:36:19 chochos
+ * concrete implementation of EOClassDescription that uses an EOEntity
+ *
+ */ \ No newline at end of file
diff --git a/projects/net.wotonomy.persistence/src/main/java/net/wotonomy/access/EOGeneralAdaptorException.java b/projects/net.wotonomy.persistence/src/main/java/net/wotonomy/access/EOGeneralAdaptorException.java
new file mode 100644
index 0000000..6c48a3f
--- /dev/null
+++ b/projects/net.wotonomy.persistence/src/main/java/net/wotonomy/access/EOGeneralAdaptorException.java
@@ -0,0 +1,62 @@
+/*
+ Wotonomy: OpenStep design patterns for pure Java applications.
+ Copyright (C) 2001 Michael Powers
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, see http://www.gnu.org
+ */
+package net.wotonomy.access;
+
+import net.wotonomy.foundation.NSDictionary;
+
+/**
+* Generic exception thrown by wotonomy.access.
+*
+* @author ezamudio@nasoft.com
+* @author $Author: cgruber $
+* @version $Revision: 893 $
+*/
+public class EOGeneralAdaptorException extends RuntimeException implements java.io.Serializable {
+
+ private NSDictionary _userInfo;
+
+ public EOGeneralAdaptorException(String msg) {
+ super(msg);
+ }
+
+ public EOGeneralAdaptorException(String name, NSDictionary userInfo) {
+ super(name);
+ _userInfo = userInfo;
+ }
+
+ public EOGeneralAdaptorException(String selectorName, String className, String msg) {
+ super(msg);
+ _userInfo = new NSDictionary(
+ new Object[]{ selectorName, className },
+ new Object[]{ "selectorName", "className" });
+ }
+
+ public NSDictionary userInfo() {
+ return _userInfo;
+ }
+
+}
+/*
+ * $Log$
+ * Revision 1.1 2006/02/16 13:19:57 cgruber
+ * Check in all sources in eclipse-friendly maven-enabled packages.
+ *
+ * Revision 1.1 2003/08/13 00:41:50 chochos
+ * general adaptor exception
+ *
+ */ \ No newline at end of file
diff --git a/projects/net.wotonomy.persistence/src/main/java/net/wotonomy/access/EOJoin.java b/projects/net.wotonomy.persistence/src/main/java/net/wotonomy/access/EOJoin.java
new file mode 100644
index 0000000..693a7d0
--- /dev/null
+++ b/projects/net.wotonomy.persistence/src/main/java/net/wotonomy/access/EOJoin.java
@@ -0,0 +1,56 @@
+/*
+ Wotonomy: OpenStep design patterns for pure Java applications.
+ Copyright (C) 2001 Michael Powers
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, see http://www.gnu.org
+ */
+package net.wotonomy.access;
+
+/**
+* An EOJoin represents a connection between two attributes in
+* different entities.
+*
+* @author ezamudio@nasoft.com
+* @author $Author: cgruber $
+* @version $Revision: 893 $
+*/
+public class EOJoin {
+
+ protected EOAttribute _source;
+ protected EOAttribute _destination;
+
+ public EOJoin(EOAttribute src, EOAttribute dst) {
+ super();
+ _source = src;
+ _destination = dst;
+ }
+
+ public EOAttribute sourceAttribute() {
+ return _source;
+ }
+
+ public EOAttribute destinationAttribute() {
+ return _destination;
+ }
+
+ /*
+ * $Log$
+ * Revision 1.1 2006/02/16 13:19:57 cgruber
+ * Check in all sources in eclipse-friendly maven-enabled packages.
+ *
+ * Revision 1.1 2003/08/07 02:41:04 chochos
+ * these don't do much for now.
+ *
+ */
+}
diff --git a/projects/net.wotonomy.persistence/src/main/java/net/wotonomy/access/EOModel.java b/projects/net.wotonomy.persistence/src/main/java/net/wotonomy/access/EOModel.java
new file mode 100644
index 0000000..46fc8d0
--- /dev/null
+++ b/projects/net.wotonomy.persistence/src/main/java/net/wotonomy/access/EOModel.java
@@ -0,0 +1,396 @@
+/*
+ Wotonomy: OpenStep design patterns for pure Java applications.
+ Copyright (C) 2001 Michael Powers
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, see http://www.gnu.org
+ */
+package net.wotonomy.access;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.PrintStream;
+import java.util.Enumeration;
+
+import net.wotonomy.foundation.NSArray;
+import net.wotonomy.foundation.NSDictionary;
+import net.wotonomy.foundation.NSMutableArray;
+import net.wotonomy.foundation.NSMutableDictionary;
+import net.wotonomy.foundation.NSPropertyListSerialization;
+
+/**
+* An EOModel is a set of entities and stored procedures, along with a connection
+* dictionary to connect to a database.
+*
+* @author ezamudio@nasoft.com
+* @author $Author: cgruber $
+* @version $Revision: 894 $
+*/
+public class EOModel {
+
+ protected static final String IDX_NAME = "index.eomodeld";
+
+ //This array contains dictionaries with "className" and "name"
+ protected NSMutableArray _entities = new NSMutableArray();
+ protected NSMutableDictionary _entitiesByName = new NSMutableDictionary();
+ protected NSMutableDictionary _entitiesByClass = new NSMutableDictionary();
+ protected NSDictionary _connectionDictionary = NSDictionary.EmptyDictionary;
+ protected String _adaptorName = "JDBC";
+ protected NSMutableDictionary _prototypesByName = new NSMutableDictionary();
+ protected NSMutableDictionary _storedProcedures = new NSMutableDictionary();
+ protected NSMutableArray _storedProcedureNames = new NSMutableArray();
+ protected NSDictionary _userInfo = NSDictionary.EmptyDictionary;
+ protected String _name;
+ protected String _path;
+ protected NSDictionary _internalInfo = NSDictionary.EmptyDictionary;
+ protected EOModelGroup _group;
+
+ public EOModel() {
+ super();
+ }
+
+ public EOModel(NSDictionary dict, Object o) {
+ this();
+ }
+
+ public EOModel(String path) {
+ this();
+ File f = new File(path);
+ _path = f.getAbsolutePath();
+ if (!(f.exists() && f.isDirectory()))
+ throw new IllegalArgumentException("The path is invalid (" + f + ")");
+ _name = f.getName();
+ f = new File(f, "index.eomodeld");
+ if (!(f.exists() && f.isFile()))
+ throw new IllegalArgumentException("Cannot find " + f);
+ String x = null;
+ try {
+ FileInputStream in = new FileInputStream(f);
+ byte[] b = new byte[in.available()];
+ in.read(b);
+ in.close();
+ x = new String(b);
+ } catch (IOException e) {
+ throw new IllegalArgumentException("Cannot read index.eomodeld");
+ }
+ NSDictionary d = NSPropertyListSerialization.dictionaryForString(x);
+ String version = (String)d.objectForKey("EOModelVersion");
+ if (version == null || !version.startsWith("2."))
+ throw new IllegalArgumentException("Invalid eomodel version: " + version);
+ setAdaptorName((String)d.objectForKey("adaptorName"));
+ setConnectionDictionary((NSDictionary)d.objectForKey("connectionDictionary"));
+ _entities = ((NSArray)d.objectForKey("entities")).mutableClone();
+ if (d.objectForKey("storedProcedures") != null)
+ _storedProcedureNames.addObjectsFromArray((NSArray)d.objectForKey("storedProcedures"));
+ if (d.objectForKey("internalInfo") != null)
+ _internalInfo = (NSDictionary)d.objectForKey("internalInfo");
+ if (d.objectForKey("userInfo") != null)
+ _userInfo = (NSDictionary)d.objectForKey("userInfo");
+ entityNamed("EOPrototypes");
+ }
+
+ public void setConnectionDictionary(NSDictionary dict) {
+ _connectionDictionary = dict;
+ }
+ public NSDictionary connectionDictionary() {
+ return _connectionDictionary;
+ }
+
+ public void addEntity(EOEntity ent) {
+ _entitiesByName.setObjectForKey(ent, ent.name());
+ _entitiesByClass.setObjectForKey(ent, ent.className());
+ }
+
+ public void removeEntity(EOEntity ent) {
+ _entitiesByName.removeObjectForKey(ent.name());
+ _entitiesByClass.removeObjectForKey(ent.className());
+ }
+
+ public void addStoredProcedure(EOStoredProcedure proc) {
+ if (!_storedProcedureNames.containsObject(proc))
+ _storedProcedureNames.addObject(proc);
+ _storedProcedures.setObjectForKey(proc, proc.name());
+ }
+
+ public void removeStoredProcedure(EOStoredProcedure proc) {
+ _storedProcedureNames.removeObject(proc.name());
+ _storedProcedures.removeObjectForKey(proc.name());
+ }
+
+ public EOStoredProcedure storedProcedureNamed(String name) {
+ EOStoredProcedure proc = (EOStoredProcedure)_storedProcedures.objectForKey(name);
+ //if we can't find it, check if we should load it
+ if (proc == null && _path != null && _storedProcedureNames.containsObject(name)) {
+ //try to read it from file
+ File f = new File(new File(_path), name + ".storedProcedure");
+ if (!f.exists())
+ return null;
+ String sdict = null;
+ try {
+ FileInputStream fin = new FileInputStream(f);
+ byte[] b = new byte[fin.available()];
+ fin.read(b);
+ fin.close();
+ sdict = new String(b);
+ } catch (IOException ex) {
+ return null;
+ }
+ NSDictionary plist = NSPropertyListSerialization.dictionaryForString(sdict);
+ if (plist == null)
+ throw new IllegalArgumentException("File " + f + " does not contain a valid stored procedure property list.");
+ proc = new EOStoredProcedure(plist, this);
+ //add it to our collection
+ _storedProcedures.setObjectForKey(proc, proc.name());
+ }
+ return proc;
+ }
+
+ public NSArray storedProcedures() {
+ if (_storedProcedures.count() < _storedProcedureNames.count()) {
+ for (int i=0; i<_storedProcedureNames.count(); i++)
+ storedProcedureNamed((String)_storedProcedureNames.objectAtIndex(i));
+ }
+ return _storedProcedures.allValues();
+ }
+
+ public NSArray storedProcedureNames() {
+ return _storedProcedures.allKeys();
+ }
+
+ public void setAdaptorName(String value) {
+ _adaptorName = value;
+ }
+ public String adaptorName() {
+ return _adaptorName;
+ }
+
+ public NSArray entities() {
+ if (_entitiesByName.count() >= _entities.count())
+ return _entitiesByName.allValues();
+ NSMutableArray es = new NSMutableArray();
+ for (int i = 0; i < _entities.count(); i++) {
+ NSDictionary d = (NSDictionary)_entities.objectAtIndex(i);
+ es.addObject(entityNamed((String)d.objectForKey("name")));
+ }
+ return es;
+ }
+
+ public NSArray entityNames() {
+ if (_entitiesByName.count() >= _entities.count())
+ return _entitiesByName.allKeys();
+ NSMutableArray names = new NSMutableArray();
+ for (int i = 0; i < _entities.count(); i++) {
+ NSDictionary d = (NSDictionary)_entities.objectAtIndex(i);
+ names.addObject(d.objectForKey("name"));
+ }
+ return names;
+ }
+
+ public EOEntity entityNamed(String name) {
+ EOEntity e = (EOEntity)_entitiesByName.objectForKey(name);
+ if (e == null && path() != null) {
+ boolean exists = false;
+ for (int i = 0; i < _entities.count(); i++) {
+ NSDictionary d = (NSDictionary)_entities.objectAtIndex(i);
+ if (d.objectForKey("name").equals(name))
+ exists = true;
+ }
+ if (!exists)
+ return null;
+ File f = new File(new File(path()), name + ".plist");
+ if (!(f.exists() && f.isFile()))
+ throw new IllegalArgumentException("Cannot find " + name + ".plist");
+ String s = null;
+ try {
+ FileInputStream fin = new FileInputStream(f);
+ byte[] b = new byte[fin.available()];
+ fin.read(b);
+ fin.close();
+ s = new String(b);
+ } catch (IOException ex) {
+ throw new IllegalArgumentException("Cannot read " + f);
+ }
+ NSDictionary d = NSPropertyListSerialization.dictionaryForString(s);
+ if (d == null)
+ throw new IllegalArgumentException("Cannot parse dictionary for " + f);
+ e = new EOEntity(d, this);
+ _entitiesByName.setObjectForKey(e, e.name());
+ }
+ return e;
+ }
+
+ public String name() {
+ return _name;
+ }
+
+ public String path() {
+ return _path;
+ }
+
+ public void setModelGroup(EOModelGroup group) {
+ _group = group;
+ }
+ public EOModelGroup modelGroup() {
+ return _group;
+ }
+
+ public void setUserInfo(NSDictionary dict) {
+ _userInfo = dict;
+ }
+
+ public NSDictionary userInfo() {
+ return _userInfo;
+ }
+
+ public void write() {
+ }
+
+ public void writeToFile(String path) {
+ if (!path.endsWith(".eomodeld"))
+ path += ".eomodeld";
+ File f = new File(path);
+ if (f.exists()) {
+ if (f.isDirectory()) {
+ File[] kids = f.listFiles();
+ for (int i = 0; i < kids.length; i++) {
+ String fname = kids[i].getName();
+ if (kids[i].isFile() && (fname.endsWith(".plist") || fname.endsWith(".eomodeld") || fname.endsWith(".fspec") || fname.endsWith(".storedProcedure")))
+ kids[i].delete();
+ }
+ } else
+ f.delete();
+ } else
+ f.mkdirs();
+ File kid = new File(f, "index.eomodeld");
+
+ //encode the index file
+ NSMutableDictionary d = new NSMutableDictionary();
+ d.setObjectForKey(_adaptorName, "adaptorName");
+ d.setObjectForKey("2.1", "EOModelVersion");
+ d.setObjectForKey(_connectionDictionary, "connectionDictionary");
+ if (_internalInfo != null && _internalInfo.count() > 0)
+ d.setObjectForKey(_internalInfo, "internalInfo");
+ if (_userInfo != null && _userInfo.count() > 0)
+ d.setObjectForKey(_userInfo, "userInfo");
+ if (_storedProcedureNames.count() > 0)
+ d.setObjectForKey(_storedProcedureNames, "storedProcedures");
+
+ //encode the entity list
+ entities();
+ storedProcedures();
+ NSMutableArray arr = new NSMutableArray(_entitiesByName.count());
+ Enumeration enumeration = _entitiesByName.keyEnumerator();
+ NSMutableDictionary md = new NSMutableDictionary();
+ while (enumeration.hasMoreElements()) {
+ String key = (String)enumeration.nextElement();
+ EOEntity ent = (EOEntity)_entitiesByName.objectForKey(key);
+ md.removeAllObjects();
+ ent.encodeIntoPropertyList(md);
+ File plist;
+ NSDictionary fetchSpecs = (NSDictionary)md.objectForKey("fetchSpecificationDictionary");
+ if (fetchSpecs != null) {
+ if (fetchSpecs.count() > 0) {
+ plist = new File(f, key + ".fspec");
+ String ps = NSPropertyListSerialization.stringForPropertyList(md.objectForKey("fetchSpecificationDictionary"));
+ try {
+ PrintStream stream = new PrintStream(new FileOutputStream(plist));
+ stream.println(ps);
+ stream.close();
+ } catch (IOException ex) {
+ }
+ }
+ md.removeObjectForKey("fetchSpecificationDictionary");
+ }
+ plist = new File(f, key + ".plist");
+ String ps = NSPropertyListSerialization.stringForPropertyList(md);
+ try {
+ PrintStream stream = new PrintStream(new FileOutputStream(plist));
+ stream.println(ps);
+ stream.close();
+ } catch (IOException ex) {
+ }
+ //add to the entity list for the index
+ arr.addObject(new NSDictionary(
+ new Object[]{ ent.name(), ent.className() },
+ new Object[]{ "name", "className" }));
+ }
+ d.setObjectForKey(arr, "entities");
+
+ //write the index file
+ String s = NSPropertyListSerialization.stringForPropertyList(d);
+ try {
+ PrintStream stream = new PrintStream(new FileOutputStream(kid));
+ stream.println(s);
+ stream.close();
+ } catch (IOException ex) {
+ throw new RuntimeException("Cannot write index.eomodeld");
+ }
+
+ //write the stored procedures
+ for (int i = 0; i < _storedProcedureNames.count(); i++) {
+ EOStoredProcedure proc = (EOStoredProcedure)_storedProcedures.objectForKey(_storedProcedureNames.objectAtIndex(i));
+ kid = new File(f, proc.name() + ".storedProcedure");
+ d.removeAllObjects();
+ proc.encodeIntoPropertyList(d);
+ s = NSPropertyListSerialization.stringForPropertyList(d);
+ try {
+ PrintStream stream = new PrintStream(new FileOutputStream(kid));
+ stream.println(s);
+ stream.close();
+ } catch (IOException ex) {
+ throw new RuntimeException("Cannot write " + f);
+ }
+ }
+ _path = f.getAbsolutePath();
+ }
+
+}
+/*
+ * $Log$
+ * Revision 1.2 2006/02/16 16:47:14 cgruber
+ * Move some classes in to "internal" packages and re-work imports, etc.
+ *
+ * Also use UnsupportedOperationExceptions where appropriate, instead of WotonomyExceptions.
+ *
+ * Revision 1.1 2006/02/16 13:19:57 cgruber
+ * Check in all sources in eclipse-friendly maven-enabled packages.
+ *
+ * Revision 1.8 2005/05/11 15:21:53 cgruber
+ * Change enum to enumeration, since enum is now a keyword as of Java 5.0
+ *
+ * A few other comments in the code.
+ *
+ * Revision 1.7 2003/08/13 20:46:12 chochos
+ * entityNamed() only throws if the searched entity is in the entities array and not in a file.
+ *
+ * Revision 1.6 2003/08/12 01:45:04 chochos
+ * added some code to handle prototypes
+ *
+ * Revision 1.5 2003/08/11 19:38:27 chochos
+ * Can now read from a file and re-write to another file.
+ *
+ * Revision 1.4 2003/08/11 18:20:08 chochos
+ * saving to a file seems to work now.
+ *
+ * Revision 1.3 2003/08/08 02:16:55 chochos
+ * can now read stored procedures from file.
+ *
+ * Revision 1.2 2003/08/08 00:37:00 chochos
+ * add stored procedure functionality
+ *
+ * Revision 1.1 2003/08/07 02:42:28 chochos
+ * EOModel can read an .eomodeld file. EOModelGroup doesn't do much for now.
+ *
+*/ \ No newline at end of file
diff --git a/projects/net.wotonomy.persistence/src/main/java/net/wotonomy/access/EOModelGroup.java b/projects/net.wotonomy.persistence/src/main/java/net/wotonomy/access/EOModelGroup.java
new file mode 100644
index 0000000..dbab09d
--- /dev/null
+++ b/projects/net.wotonomy.persistence/src/main/java/net/wotonomy/access/EOModelGroup.java
@@ -0,0 +1,198 @@
+/*
+ Wotonomy: OpenStep design patterns for pure Java applications.
+ Copyright (C) 2001 Michael Powers
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, see http://www.gnu.org
+ */
+package net.wotonomy.access;
+
+import net.wotonomy.control.EOFetchSpecification;
+import net.wotonomy.control.EOObjectStoreCoordinator;
+import net.wotonomy.foundation.NSArray;
+import net.wotonomy.foundation.NSDictionary;
+import net.wotonomy.foundation.NSMutableDictionary;
+
+/**
+* A group of models that connect to the same database. Entities in
+* these models can have relationships that point to other entities
+* in any model of the same group.
+*
+* @author ezamudio@nasoft.com
+* @author $Author: cgruber $
+* @version $Revision: 894 $
+*/
+public class EOModelGroup {
+
+ private static EOModelGroup _defaultGroup;
+ private static EOModelGroup _globalGroup;
+ protected NSMutableDictionary _models;
+
+ public EOModelGroup() {
+ super();
+ }
+
+ public void addModel(EOModel model) {
+ if (model.name() == null)
+ throw new IllegalArgumentException("Cannot add an unnamed model to a group.");
+ if (_models.objectForKey(model.name()) != null)
+ throw new IllegalArgumentException("Cannot add model " + model.name() +
+ " to group because it already contains a model with the same name.");
+ NSArray ents = model.entityNames();
+ for (int i = 0; i < ents.count(); i++) {
+ String ename = (String)ents.objectAtIndex(i);
+ if (entityNamed(ename) != null)
+ throw new IllegalArgumentException("Cannot add model " + model.name() +
+ " to group because it contains entity named " + ename);
+ }
+ _models.setObjectForKey(model, model.name());
+ }
+
+ public void removeModel(EOModel model) {
+ _models.removeObjectForKey(model.name());
+ }
+
+ public void addModelWithPath(String path) {
+ EOModel model = new EOModel(path);
+ addModel(model);
+ }
+
+ public void addModelsFromDirectory(String dir) {
+ }
+
+ public static void setDefaultGroup(EOModelGroup group) {
+ _defaultGroup = group;
+ }
+ public static EOModelGroup defaultGroup() {
+ if (_defaultGroup == null) {
+ _defaultGroup = globalModelGroup();
+ }
+ return _defaultGroup;
+ }
+
+ public static EOModelGroup globalModelGroup() {
+ if (_globalGroup == null) {
+ _globalGroup = new EOModelGroup();
+ //TODO: read all frameworks and get models from them
+ }
+ return _globalGroup;
+ }
+
+ public EOEntity entityForObject(net.wotonomy.control.EOEnterpriseObject eo) {
+ return null;
+ }
+
+ public EOEntity entityNamed(String name) {
+ java.util.Enumeration enumeration = _models.objectEnumerator();
+ while (enumeration.hasMoreElements()) {
+ EOModel m = (EOModel)enumeration.nextElement();
+ if (m.entityNamed(name) != null)
+ return m.entityNamed(name);
+ }
+ return null;
+ }
+
+ public EOModel modelNamed(String name) {
+ return (EOModel)_models.objectForKey(name);
+ }
+
+ public NSArray modelNames() {
+ return _models.allKeys();
+ }
+
+ public NSArray models() {
+ return _models.allValues();
+ }
+
+ public EOModel modelWithPath(String path) {
+ java.util.Enumeration enumeration = _models.objectEnumerator();
+ while (enumeration.hasMoreElements()) {
+ EOModel m = (EOModel)enumeration.nextElement();
+ if (m.path() != null && m.path().equals(path))
+ return m;
+ }
+ return null;
+ }
+
+ public EOStoredProcedure storedProcedureNamed(String name) {
+ java.util.Enumeration enumeration = _models.objectEnumerator();
+ while (enumeration.hasMoreElements()) {
+ EOModel m = (EOModel)enumeration.nextElement();
+ if (m.storedProcedureNamed(name) != null)
+ return m.storedProcedureNamed(name);
+ }
+ return null;
+ }
+
+ public EOFetchSpecification fetchSpecificationNamed(String fetchSpecName, String entityName) {
+ EOEntity e = entityNamed(entityName);
+ if (e == null)
+ return null;
+ return e.fetchSpecificationNamed(fetchSpecName);
+ }
+
+ public void loadAllModelObjects() {
+ java.util.Enumeration enumeration = _models.objectEnumerator();
+ while (enumeration.hasMoreElements()) {
+ EOModel m = (EOModel)enumeration.nextElement();
+ //this causes all entities to be loaded
+ NSArray ents = m.entities();
+ for (int i=0; i<ents.count(); i++) {
+ EOEntity e = (EOEntity)ents.objectAtIndex(i);
+ //this loads all the fetch specifications
+ e.fetchSpecificationNamed("whatever");
+ }
+ }
+ }
+
+ public static void setModelGroupForObjectStoreCoordinator(EOObjectStoreCoordinator coord, EOModelGroup group) {
+ NSMutableDictionary d = new NSMutableDictionary(coord.userInfo());
+ d.setObjectForKey(group, "ModelGroup");
+ coord.setUserInfo(d);
+ }
+ public static EOModelGroup modelGroupForObjectStoreCoordinator(EOObjectStoreCoordinator coord) {
+ NSDictionary d = coord.userInfo();
+ if (d == null)
+ return defaultGroup();
+ Object g = d.objectForKey("ModelGroup");
+ if (g != null && g instanceof EOModelGroup)
+ return (EOModelGroup)g;
+ return defaultGroup();
+ }
+
+}
+/*
+ * $Log$
+ * Revision 1.2 2006/02/16 16:47:14 cgruber
+ * Move some classes in to "internal" packages and re-work imports, etc.
+ *
+ * Also use UnsupportedOperationExceptions where appropriate, instead of WotonomyExceptions.
+ *
+ * Revision 1.1 2006/02/16 13:19:57 cgruber
+ * Check in all sources in eclipse-friendly maven-enabled packages.
+ *
+ * Revision 1.4 2005/05/11 15:21:53 cgruber
+ * Change enum to enumeration, since enum is now a keyword as of Java 5.0
+ *
+ * A few other comments in the code.
+ *
+ * Revision 1.3 2003/08/08 00:44:04 chochos
+ * manage model groups for object store coordinators.
+ *
+ * Revision 1.2 2003/08/08 00:36:41 chochos
+ * add a little more functionality
+ *
+ * Revision 1.1 2003/08/07 02:42:28 chochos
+ * EOModel can read an .eomodeld file. EOModelGroup doesn't do much for now.
+ *
+*/ \ No newline at end of file
diff --git a/projects/net.wotonomy.persistence/src/main/java/net/wotonomy/access/EOProperty.java b/projects/net.wotonomy.persistence/src/main/java/net/wotonomy/access/EOProperty.java
new file mode 100644
index 0000000..89f1fea
--- /dev/null
+++ b/projects/net.wotonomy.persistence/src/main/java/net/wotonomy/access/EOProperty.java
@@ -0,0 +1,46 @@
+/*
+Wotonomy: OpenStep design patterns for pure Java applications.
+Copyright (C) 2001 Intersect Software Corporation
+
+This library is free software; you can redistribute it and/or
+modify it under the terms of the GNU Lesser General Public
+License as published by the Free Software Foundation; either
+version 2.1 of the License, or (at your option) any later version.
+
+This library is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+Lesser General Public License for more details.
+
+You should have received a copy of the GNU Lesser General Public
+License along with this library; if not, see http://www.gnu.org
+*/
+package net.wotonomy.access;
+
+/**
+* Abstract superclass of EOAttribute and EORelationship.
+*
+* @author ezamudio@nasoft.com
+* @author $Author: cgruber $
+* @version $Revision: 893 $
+*/
+public abstract class EOProperty {
+
+ public EOProperty() {
+ super();
+ }
+
+ public abstract String name();
+
+ public abstract String relationshipPath();
+
+}
+/*
+ * $Log$
+ * Revision 1.1 2006/02/16 13:19:57 cgruber
+ * Check in all sources in eclipse-friendly maven-enabled packages.
+ *
+ * Revision 1.1 2003/08/08 06:51:37 chochos
+ * abstract superclass for relationships and attributes
+ *
+ */
diff --git a/projects/net.wotonomy.persistence/src/main/java/net/wotonomy/access/EOPropertyListEncoding.java b/projects/net.wotonomy.persistence/src/main/java/net/wotonomy/access/EOPropertyListEncoding.java
new file mode 100644
index 0000000..af456d2
--- /dev/null
+++ b/projects/net.wotonomy.persistence/src/main/java/net/wotonomy/access/EOPropertyListEncoding.java
@@ -0,0 +1,53 @@
+/*
+ Wotonomy: OpenStep design patterns for pure Java applications.
+ Copyright (C) 2001 Michael Powers
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, see http://www.gnu.org
+ */
+package net.wotonomy.access;
+
+import net.wotonomy.foundation.NSDictionary;
+import net.wotonomy.foundation.NSMutableDictionary;
+
+/**
+* Implemented by classes that are read from property lists and that
+* can be written back to property lists.
+*
+* @author ezamudio@nasoft.com
+* @author $Author: cgruber $
+* @version $Revision: 894 $
+*/
+public interface EOPropertyListEncoding {
+
+ public abstract void awakeWithPropertyList(NSDictionary plist);
+
+ public abstract void encodeIntoPropertyList(NSMutableDictionary plist);
+
+}
+
+/* $Log$
+ * Revision 1.2 2006/02/16 16:47:14 cgruber
+ * Move some classes in to "internal" packages and re-work imports, etc.
+ *
+ * Also use UnsupportedOperationExceptions where appropriate, instead of WotonomyExceptions.
+ *
+/* Revision 1.1 2006/02/16 13:19:57 cgruber
+/* Check in all sources in eclipse-friendly maven-enabled packages.
+/*
+/* Revision 1.1 2003/08/09 01:34:43 chochos
+/* an interface to provide property list encoding capabilities
+/*
+ *
+ */
+ \ No newline at end of file
diff --git a/projects/net.wotonomy.persistence/src/main/java/net/wotonomy/access/EOQualifierSQLGeneration.java b/projects/net.wotonomy.persistence/src/main/java/net/wotonomy/access/EOQualifierSQLGeneration.java
new file mode 100644
index 0000000..4627f76
--- /dev/null
+++ b/projects/net.wotonomy.persistence/src/main/java/net/wotonomy/access/EOQualifierSQLGeneration.java
@@ -0,0 +1,241 @@
+/*
+Wotonomy: OpenStep design patterns for pure Java applications.
+Copyright (C) 2000 Michael Powers
+
+This library is free software; you can redistribute it and/or
+modify it under the terms of the GNU Lesser General Public
+License as published by the Free Software Foundation; either
+version 2.1 of the License, or (at your option) any later version.
+
+This library is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+Lesser General Public License for more details.
+
+You should have received a copy of the GNU Lesser General Public
+License along with this library; if not, see http://www.gnu.org
+*/
+package net.wotonomy.access;
+
+import net.wotonomy.control.EOAndQualifier;
+import net.wotonomy.control.EOKeyComparisonQualifier;
+import net.wotonomy.control.EOKeyValueQualifier;
+import net.wotonomy.control.EONotQualifier;
+import net.wotonomy.control.EOOrQualifier;
+import net.wotonomy.control.EOQualifier;
+import net.wotonomy.foundation.NSArray;
+import net.wotonomy.foundation.NSMutableDictionary;
+
+/**
+* @author ezamudio@nasoft.com
+* @author $Author: cgruber $
+* @version $Revision: 894 $
+*/
+
+public interface EOQualifierSQLGeneration {
+
+ public EOQualifier qualifierMigratedFromEntityRelationshipPath(
+ EOEntity entity, String path);
+
+ public EOQualifier schemaBasedQualifierWithRootEntity(EOEntity entity);
+
+ public String sqlStringForSQLExpression(EOSQLExpression expression);
+
+ public abstract class Support {
+
+ private static NSMutableDictionary _classes = new NSMutableDictionary();
+
+ static {
+ setSupportForClass(new KeyValueQualifierSupport(), EOKeyValueQualifier.class);
+ setSupportForClass(new KeyComparisonQualifierSupport(), EOKeyComparisonQualifier.class);
+ setSupportForClass(new AndQualifierSupport(), EOAndQualifier.class);
+ setSupportForClass(new OrQualifierSupport(), EOOrQualifier.class);
+ setSupportForClass(new NotQualifierSupport(), EONotQualifier.class);
+ }
+
+ public Support() {
+ super();
+ }
+
+ public static void setSupportForClass(Support sup, Class aClass) {
+ _classes.setObjectForKey(sup, aClass.getName());
+ }
+ public static Support supportForClass(Class aClass) {
+ return (Support)_classes.objectForKey(aClass.getName());
+ }
+
+ public abstract String sqlStringForSQLExpression(EOQualifier q, EOSQLExpression exp);
+
+ public abstract EOQualifier schemaBasedQualifierWithRootEntity(EOQualifier q, EOEntity e);
+
+ public abstract EOQualifier qualifierMigratedFromEntityRelationshipPath(EOQualifier q, EOEntity e, String path);
+
+ }
+
+ public class KeyValueQualifierSupport extends Support {
+
+ public KeyValueQualifierSupport() {
+ super();
+ }
+
+ public String sqlStringForSQLExpression(EOQualifier qualifier, EOSQLExpression exp) {
+ EOKeyValueQualifier q = (EOKeyValueQualifier)qualifier;
+ String sql1 = exp.sqlStringForAttributeNamed(q.key());
+ String sql2 = exp.sqlStringForSelector(q.selector(), q.value());
+ String sql3 = exp.sqlStringForValue(q.value(), q.key());
+ if (q.selector() == EOQualifier.QualifierOperatorCaseInsensitiveLike)
+ return exp.sqlStringForCaseInsensitiveLike(sql1, sql3);
+ else if (q.selector() == EOQualifier.QualifierOperatorLike)
+ sql3 = exp.sqlPatternFromShellPattern(sql3);
+ return sql1 + sql2 + sql3;
+ }
+
+ public EOQualifier schemaBasedQualifierWithRootEntity(EOQualifier q, EOEntity e) {
+ return q;
+ }
+
+ public EOQualifier qualifierMigratedFromEntityRelationshipPath(EOQualifier q, EOEntity e, String path) {
+ return q;
+ }
+
+ }
+
+ public class KeyComparisonQualifierSupport extends Support {
+
+ public KeyComparisonQualifierSupport() {
+ super();
+ }
+
+ public String sqlStringForSQLExpression(EOQualifier qualifier, EOSQLExpression exp) {
+ EOKeyComparisonQualifier q = (EOKeyComparisonQualifier)qualifier;
+ return exp.sqlStringForAttributeNamed(q.leftKey()) +
+ exp.sqlStringForSelector(q.selector(), null) +
+ exp.sqlStringForAttributeNamed(q.rightKey());
+ }
+
+ public EOQualifier schemaBasedQualifierWithRootEntity(EOQualifier q, EOEntity e) {
+ return q;
+ }
+
+ public EOQualifier qualifierMigratedFromEntityRelationshipPath(EOQualifier q, EOEntity e, String path) {
+ return q;
+ }
+
+ }
+
+ public class NotQualifierSupport extends Support {
+
+ public NotQualifierSupport() {
+ super();
+ }
+
+ public String sqlStringForSQLExpression(EOQualifier qualifier, EOSQLExpression exp) {
+ EONotQualifier q = (EONotQualifier)qualifier;
+ return "NOT (" + EOQualifierSQLGeneration.Support.supportForClass(q.qualifier().getClass()).sqlStringForSQLExpression(q.qualifier(), exp) + ")";
+ }
+
+ public EOQualifier schemaBasedQualifierWithRootEntity(EOQualifier qualifier, EOEntity e) {
+ EONotQualifier q = (EONotQualifier)qualifier;
+ return new EONotQualifier(EOQualifierSQLGeneration.Support.supportForClass(q.qualifier().getClass()).schemaBasedQualifierWithRootEntity(q.qualifier(), e));
+ }
+
+ public EOQualifier qualifierMigratedFromEntityRelationshipPath(EOQualifier qualifier, EOEntity e, String path) {
+ EONotQualifier q = (EONotQualifier)qualifier;
+ return new EONotQualifier(EOQualifierSQLGeneration.Support.supportForClass(q.qualifier().getClass()).qualifierMigratedFromEntityRelationshipPath(q.qualifier(), e, path));
+ }
+
+ }
+
+ public class AndQualifierSupport extends Support {
+
+ public AndQualifierSupport() {
+ super();
+ }
+
+ public String sqlStringForSQLExpression(EOQualifier qualifier, EOSQLExpression exp) {
+ EOAndQualifier q = (EOAndQualifier)qualifier;
+ NSArray qus = q.qualifiers();
+ StringBuffer buf = new StringBuffer();
+ for (int i = 0; i < qus.count(); i++) {
+ EOQualifier sub = (EOQualifier)qus.objectAtIndex(i);
+ EOQualifierSQLGeneration.Support sup = EOQualifierSQLGeneration.Support.supportForClass(sub.getClass());
+ if (sup == null)
+ throw new IllegalStateException("Cannot find support class for " + sub.getClass().getName());
+ buf.append(sup.sqlStringForSQLExpression(sub, exp));
+ if (i < qus.count()-1)
+ buf.append(" AND ");
+ }
+ if (qus.count() > 1) {
+ buf.insert(0, '(');
+ buf.append(')');
+ }
+ return buf.toString();
+ }
+
+ public EOQualifier schemaBasedQualifierWithRootEntity(EOQualifier q, EOEntity e) {
+ return q;
+ }
+
+ public EOQualifier qualifierMigratedFromEntityRelationshipPath(EOQualifier q, EOEntity e, String path) {
+ return q;
+ }
+
+ }
+
+ public class OrQualifierSupport extends Support {
+
+ public OrQualifierSupport() {
+ super();
+ }
+
+ public String sqlStringForSQLExpression(EOQualifier qualifier, EOSQLExpression exp) {
+ EOOrQualifier q = (EOOrQualifier)qualifier;
+ NSArray qus = q.qualifiers();
+ StringBuffer buf = new StringBuffer();
+ for (int i = 0; i < qus.count(); i++) {
+ EOQualifier sub = (EOQualifier)qus.objectAtIndex(i);
+ EOQualifierSQLGeneration.Support sup = EOQualifierSQLGeneration.Support.supportForClass(sub.getClass());
+ if (sup == null)
+ throw new IllegalStateException("Cannot find support class for " + sub.getClass().getName());
+ buf.append(sup.sqlStringForSQLExpression(sub, exp));
+ if (i < qus.count()-1)
+ buf.append(" OR ");
+ }
+ if (qus.count() > 1) {
+ buf.insert(0, '(');
+ buf.append(')');
+ }
+ return buf.toString();
+ }
+
+ public EOQualifier schemaBasedQualifierWithRootEntity(EOQualifier q, EOEntity e) {
+ return q;
+ }
+
+ public EOQualifier qualifierMigratedFromEntityRelationshipPath(EOQualifier q, EOEntity e, String path) {
+ return q;
+ }
+
+ }
+
+}
+/*
+ * $Log$
+ * Revision 1.2 2006/02/16 16:47:14 cgruber
+ * Move some classes in to "internal" packages and re-work imports, etc.
+ *
+ * Also use UnsupportedOperationExceptions where appropriate, instead of WotonomyExceptions.
+ *
+ * Revision 1.1 2006/02/16 13:19:57 cgruber
+ * Check in all sources in eclipse-friendly maven-enabled packages.
+ *
+ * Revision 1.3 2003/08/14 02:13:10 chochos
+ * KeyValueQualifierSupport generates proper SQL
+ *
+ * Revision 1.2 2003/08/14 01:05:51 chochos
+ * added abstract Support inner class, with incomplete implementations for the main qualifiers (not, and, or, key-value, key-comparison)
+ *
+ * Revision 1.1 2003/08/12 01:45:49 chochos
+ * interface to be implemented by qualifiers (or support classes) to indicate they can generate SQL
+ *
+ */ \ No newline at end of file
diff --git a/projects/net.wotonomy.persistence/src/main/java/net/wotonomy/access/EORelationship.java b/projects/net.wotonomy.persistence/src/main/java/net/wotonomy/access/EORelationship.java
new file mode 100644
index 0000000..a5a207f
--- /dev/null
+++ b/projects/net.wotonomy.persistence/src/main/java/net/wotonomy/access/EORelationship.java
@@ -0,0 +1,319 @@
+/*
+ Wotonomy: OpenStep design patterns for pure Java applications.
+ Copyright (C) 2001 Michael Powers
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, see http://www.gnu.org
+ */
+package net.wotonomy.access;
+
+import net.wotonomy.foundation.NSArray;
+import net.wotonomy.foundation.NSDictionary;
+import net.wotonomy.foundation.NSMutableArray;
+import net.wotonomy.foundation.NSMutableDictionary;
+/**
+* Represents a relationship from one entity to another. Relationships are unidirectional.
+*
+* @author ezamudio@nasoft.com
+* @author $Author: cgruber $
+* @version $Revision: 894 $
+*/
+public class EORelationship extends EOProperty implements EOPropertyListEncoding {
+
+ public static final int InnerJoin = 0;
+ public static final int FullOuterJoin = 1;
+ public static final int LeftOuterJoin = 2;
+ public static final int RightOuterJoin = 3;
+
+ protected String _name;
+ protected int _batchCount;
+ protected int _deleteRule;
+ protected int _joinSemantic;
+ protected EOEntity _destination;
+ protected EOEntity _entity;
+ protected NSMutableArray _joins = new NSMutableArray();
+ protected boolean _isMandatory;
+ protected boolean _isToMany;
+ protected boolean _isFlattened;
+ protected boolean _knowsIfFlattened;
+ protected boolean _ownsDestination;
+ protected boolean _propagatesPrimaryKey;
+ protected boolean _useBatchFaulting;
+ protected NSDictionary _userInfo = NSDictionary.EmptyDictionary;
+ protected NSDictionary _internalInfo = NSDictionary.EmptyDictionary;
+ protected NSDictionary plist;
+ protected String _definition;
+
+ public EORelationship() {
+ super();
+ }
+
+ public EORelationship(NSDictionary dict, Object obj) {
+ super();
+ _entity = (EOEntity)obj;
+ setName((String)dict.objectForKey("name"));
+ setToMany("Y".equals(dict.objectForKey("isToMany")));
+ setPropagatesPrimaryKey("Y".equals(dict.objectForKey("propagatesPrimaryKey")));
+ setIsMandatory("Y".equals(dict.objectForKey("isMandatory")));
+ setOwnsDestination("Y".equals(dict.objectForKey("ownsDestination")));
+ setDefinition((String)dict.objectForKey("definition"));
+ String delrule = (String)dict.objectForKey("deleteRule");
+ if (delrule != null) {
+ if (delrule.equals("EODeleteRuleCascade"))
+ setDeleteRule(0);
+ else if (delrule.equals("EODeleteRuleDeny"))
+ setDeleteRule(0);
+ else if (delrule.equals("EODeleteRuleNoAction"))
+ setDeleteRule(0);
+ else if (delrule.equals("EODeleteRuleNullify"))
+ setDeleteRule(0);
+ }
+ delrule = (String)dict.objectForKey("joinSemantic");
+ if (delrule != null) {
+ if (delrule.equals("EOInnerJoin"))
+ setJoinSemantic(InnerJoin);
+ else if (delrule.equals("EOFullOuterJoin"))
+ setJoinSemantic(FullOuterJoin);
+ else if (delrule.equals("EOLeftOuterJoin"))
+ setJoinSemantic(LeftOuterJoin);
+ else if (delrule.equals("EORightOuterJoin"))
+ setJoinSemantic(RightOuterJoin);
+ }
+ delrule = (String)dict.objectForKey("batchCount");
+ if (delrule != null)
+ setNumberOfToManyFaultsToBatchFetch(Integer.parseInt(delrule));
+ NSDictionary d = (NSDictionary)dict.objectForKey("userInfo");
+ if (d != null)
+ _userInfo = d;
+ d = (NSDictionary)dict.objectForKey("internalInfo");
+ if (d != null)
+ _internalInfo = d;
+ plist = dict;
+ }
+
+ public void setName(String name) {
+ _name = name;
+ }
+ public String name() {
+ return _name;
+ }
+
+ public void addJoin(EOJoin join) {
+ _joins.addObject(join);
+ }
+
+ public void removeJoin(EOJoin join) {
+ _joins.removeObject(join);
+ }
+
+ public EOEntity entity() {
+ return _entity;
+ }
+
+ public EOEntity destinationEntity() {
+ isFlattened();
+ if (_destination == null && plist != null) {
+ EOModel model = _entity.model();
+ EOModelGroup group = model.modelGroup();
+ String destEntity = (String)plist.objectForKey("destination");
+ if (group != null)
+ _destination = group.entityNamed(destEntity);
+ else
+ _destination = model.entityNamed(destEntity);
+ }
+ return _destination;
+ }
+
+ public void setOwnsDestination(boolean flag) {
+ _ownsDestination = flag;
+ }
+ public boolean ownsDestination() {
+ return _ownsDestination;
+ }
+
+ public void setToMany(boolean flag) {
+ _isToMany = flag;
+ }
+ public boolean isToMany() {
+ return _isToMany;
+ }
+
+ public void setIsMandatory(boolean flag) {
+ _isMandatory = flag;
+ }
+ public boolean isMandatory() {
+ return _isMandatory;
+ }
+
+ public void setPropagatesPrimaryKey(boolean flag) {
+ _propagatesPrimaryKey = flag;
+ }
+ public boolean propagatesPrimaryKey() {
+ return _propagatesPrimaryKey;
+ }
+
+ public void setDeleteRule(int value) {
+ _deleteRule = value;
+ }
+ public int deleteRule() {
+ return _deleteRule;
+ }
+
+ public void setJoinSemantic(int value) {
+ _joinSemantic = value;
+ }
+ public int joinSemantic() {
+ return _joinSemantic;
+ }
+
+ public void setNumberOfToManyFaultsToBatchFetch(int count) {
+ _batchCount = count;
+ }
+ public int numberOfToManyFaultsToBatchFetch() {
+ return _batchCount;
+ }
+
+ public NSArray joins() {
+ if (_joins.count() == 0 && plist != null) {
+ NSArray joins = (NSArray)plist.objectForKey("joins");
+ for (int i = 0; i < joins.count(); i++) {
+ NSDictionary d = (NSDictionary)joins.objectAtIndex(i);
+ String srcName = (String)d.objectForKey("sourceAttribute");
+ String dstName = (String)d.objectForKey("destinationAttribute");
+ EOAttribute a1 = _entity.attributeNamed(srcName);
+ EOAttribute a2 = destinationEntity().attributeNamed(dstName);
+ EOJoin j = new EOJoin(a1, a2);
+ addJoin(j);
+ }
+ }
+ return new NSArray(_joins);
+ }
+
+ public void setDefinition(String def) {
+ _definition = def;
+ }
+ public String definition() {
+ return _definition;
+ }
+
+ public boolean isFlattened() {
+ if (_knowsIfFlattened)
+ return _isFlattened;
+ _knowsIfFlattened = true;
+ if (definition() == null)
+ return false;
+ NSArray comps = NSArray.componentsSeparatedByString(definition(), ".");
+ if (comps.count() < 2)
+ return false;
+ EORelationship r = null;
+ EOEntity e = entity();
+ for (int i = 0; i < comps.count(); i++) {
+ String name = (String)comps.objectAtIndex(i);
+ r = e.relationshipNamed(name);
+ if (r == null)
+ return false;
+ e = r.destinationEntity();
+ }
+ _destination = e;
+ _isFlattened = true;
+ return _isFlattened;
+ }
+
+ public boolean isMultiHop() {
+ return false;
+ }
+
+ public String relationshipPath() {
+ if (isFlattened())
+ return _definition;
+ return null;
+ }
+
+ public void setUserInfo(NSDictionary value) {
+ _userInfo = value;
+ }
+ public NSDictionary userInfo() {
+ return _userInfo;
+ }
+
+ public void awakeWithPropertyList(NSDictionary plist) {
+ }
+
+ public void encodeIntoPropertyList(NSMutableDictionary dict) {
+ dict.setObjectForKey(name(), "name");
+ if (destinationEntity() != null && definition() == null)
+ dict.setObjectForKey(_destination.name(), "destination");
+ if (_internalInfo != null && _internalInfo.count() > 0)
+ dict.setObjectForKey(_internalInfo, "internalInfo");
+ if (_userInfo != null && _userInfo.count() > 0)
+ dict.setObjectForKey(_userInfo, "userInfo");
+ if (isToMany())
+ dict.setObjectForKey("Y", "isToMany");
+ switch (_joinSemantic) {
+ case InnerJoin:
+ dict.setObjectForKey("EOInnerJoin", "joinSemantic");
+ break;
+ case FullOuterJoin:
+ dict.setObjectForKey("EOFullOuterJoin", "joinSemantic");
+ break;
+ case LeftOuterJoin:
+ dict.setObjectForKey("EOLefOuterJoin", "joinSemantic");
+ break;
+ case RightOuterJoin:
+ dict.setObjectForKey("EORightOuterJoin", "joinSemantic");
+ break;
+ }
+ if (_batchCount > 0)
+ dict.setObjectForKey(new Integer(_batchCount), "batchCount");
+ if (definition() != null)
+ dict.setObjectForKey(definition(), "definition");
+ else {
+ NSMutableArray jarr = new NSMutableArray(joins().count());
+ for (int i = 0; i < _joins.count(); i++) {
+ EOJoin j = (EOJoin)_joins.objectAtIndex(i);
+ NSDictionary d = new NSDictionary(
+ new Object[]{ j.sourceAttribute().name(), j.destinationAttribute().name() },
+ new Object[]{ "sourceAttribute", "destinationAttribute" });
+ jarr.addObject(d);
+ }
+ dict.setObjectForKey(jarr, "joins");
+ }
+ }
+
+}
+/*
+ * $Log$
+ * Revision 1.2 2006/02/16 16:47:13 cgruber
+ * Move some classes in to "internal" packages and re-work imports, etc.
+ *
+ * Also use UnsupportedOperationExceptions where appropriate, instead of WotonomyExceptions.
+ *
+ * Revision 1.1 2006/02/16 13:19:57 cgruber
+ * Check in all sources in eclipse-friendly maven-enabled packages.
+ *
+ * Revision 1.5 2003/08/11 19:38:27 chochos
+ * Can now read from a file and re-write to another file.
+ *
+ * Revision 1.4 2003/08/09 01:35:35 chochos
+ * implement EOPropertyListEncoding
+ *
+ * Revision 1.3 2003/08/08 06:51:53 chochos
+ * isFlattened() works
+ *
+ * Revision 1.2 2003/08/08 02:17:43 chochos
+ * main accessors are in place.
+ *
+ * Revision 1.1 2003/08/07 02:41:30 chochos
+ * a relationship that for the moment can be created from a property list.
+ *
+*/ \ No newline at end of file
diff --git a/projects/net.wotonomy.persistence/src/main/java/net/wotonomy/access/EOSQLExpression.java b/projects/net.wotonomy.persistence/src/main/java/net/wotonomy/access/EOSQLExpression.java
new file mode 100644
index 0000000..4cb15e5
--- /dev/null
+++ b/projects/net.wotonomy.persistence/src/main/java/net/wotonomy/access/EOSQLExpression.java
@@ -0,0 +1,697 @@
+/*
+Wotonomy: OpenStep design patterns for pure Java applications.
+Copyright (C) 2000 Michael Powers
+
+This library is free software; you can redistribute it and/or
+modify it under the terms of the GNU Lesser General Public
+License as published by the Free Software Foundation; either
+version 2.1 of the License, or (at your option) any later version.
+
+This library is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+Lesser General Public License for more details.
+
+You should have received a copy of the GNU Lesser General Public
+License along with this library; if not, see http://www.gnu.org
+*/
+package net.wotonomy.access;
+
+import java.util.Enumeration;
+
+import net.wotonomy.control.EOAndQualifier;
+import net.wotonomy.control.EOFetchSpecification;
+import net.wotonomy.control.EOKeyComparisonQualifier;
+import net.wotonomy.control.EOKeyValueQualifier;
+import net.wotonomy.control.EOOrQualifier;
+import net.wotonomy.control.EOQualifier;
+import net.wotonomy.control.EOSortOrdering;
+import net.wotonomy.foundation.NSArray;
+import net.wotonomy.foundation.NSData;
+import net.wotonomy.foundation.NSDictionary;
+import net.wotonomy.foundation.NSKeyValueCoding;
+import net.wotonomy.foundation.NSMutableArray;
+import net.wotonomy.foundation.NSMutableDictionary;
+import net.wotonomy.foundation.NSSelector;
+import net.wotonomy.foundation.NSTimestamp;
+import net.wotonomy.foundation.NSTimestampFormatter;
+
+/**
+* @author ezamudio@nasoft.com
+* @author $Author: cgruber $
+* @version $Revision: 894 $
+*/
+public abstract class EOSQLExpression {
+
+ public static final String BindVariableAttributeKey = "BindVariableAttribute";
+ public static final String BindVariableColumnKey = "BindVariableColumn";
+ public static final String BindVariableNameKey = "BindVariableName";
+ public static final String BindVariablePlaceHolderKey = "BindVariablePlaceholder";
+ public static final String BindVariableValueKey = "BindVariableValue";
+ private static int UseBindings;
+ private static final int _DefaultFormatSQLStringLength = 64;
+ private static final int _DefaultListStringLength = 256;
+ private static final int _DefaultOrderByStringLength = 128;
+ private static final int _DefaultPathLength = 128;
+ private static final int _DefaultTableListLength = 128;
+ protected static final char[] _hexChars = new char[]{
+ '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'
+ };
+ private static final int _ValueLengthLimit = 40;
+ protected NSMutableDictionary _aliasesByRelationshipPath;
+ protected NSMutableDictionary _aliasesByEntityName;
+ protected NSMutableArray _bindings;
+ protected NSMutableArray _contextStack;
+ protected static NSTimestampFormatter _defaultDateFormatter;
+ protected EOEntity _entity;
+ protected StringBuffer _joinClauseString;
+ protected StringBuffer _listString;
+ protected StringBuffer _orderByString;
+ protected String _statement;
+ protected String _upperFunctionName;
+ protected boolean _useAliases = true;
+ protected static boolean _quoteExternalNames;
+ protected StringBuffer _valueListString;
+ protected String _whereClauseString;
+
+ private EOSQLExpression() {
+ super();
+ }
+
+ public EOSQLExpression(EOEntity entity) {
+ super();
+ _entity = entity;
+ }
+
+ public String _aliasForRelatedAttributeRelationshipPath(EOAttribute a, String path) {
+ return null;
+ }
+
+ public String _aliasForRelationshipPath(String path) {
+ return (String)_aliasesByRelationshipPath.objectForKey(path);
+ }
+
+ protected NSTimestampFormatter _defaultDateFormatter() {
+ return _defaultDateFormatter;
+ }
+
+ protected StringBuffer _listString() {
+ if (_listString == null)
+ _listString = new StringBuffer();
+ return _listString;
+ }
+
+ protected StringBuffer _orderByString() {
+ if (_orderByString == null)
+ _orderByString = new StringBuffer();
+ return _orderByString;
+ }
+
+ public EOEntity _rootEntityForExpression() {
+ return _entity;
+ }
+
+ public void _setEntity(EOEntity value) {
+ _entity = value;
+ }
+
+ public String _sqlStringForJoinSemanticMatchSemantic(int semantic,int match) {
+ return null;
+ }
+
+ protected String _stringForDate(NSTimestamp timestamp) {
+ return null;
+ }
+
+ protected StringBuffer _valueList() {
+ if (_valueListString == null)
+ _valueListString = new StringBuffer();
+ return _valueListString;
+ }
+
+ public void addBindVariableDictionary( NSDictionary dict ) {
+ }
+
+ /** Adds the SQL to create the attribute to the attribute list.
+ * The appended text is of the form attr_name attr_type allow_null
+ * @param attr The attribute to create the SQL for.
+ */
+ public void addCreateClauseForAttribute(EOAttribute attr) {
+ StringBuffer buf = new StringBuffer(attr.columnName());
+ buf.append(' ');
+ buf.append(columnTypeStringForAttribute(attr));
+ buf.append(allowsNullClauseForConstraint(attr.allowsNull()));
+ appendItemToListString(buf.toString(), _listString());
+ }
+
+ public void addInsertListAttribute(EOAttribute attr, Object o) {
+ }
+
+ public void addJoinClause(String left, String right, int semantic) {
+ String s = assembleJoinClause(left, right, semantic);
+ if (_joinClauseString == null)
+ _joinClauseString = new StringBuffer();
+ if (_joinClauseString.length() > 0)
+ _joinClauseString.append(" AND ");
+ _joinClauseString.append(s);
+ }
+
+ public void addOrderByAttributeOrdering(EOSortOrdering order) {
+ String sql = sqlStringForAttributeNamed(order.key());
+ if (order.selector().equals(EOSortOrdering.CompareCaseInsensitiveAscending) || order.selector().equals(EOSortOrdering.CompareCaseInsensitiveDescending))
+ sql = "UPPER(" + sql + ")";
+ if (order.selector().equals(EOSortOrdering.CompareCaseInsensitiveAscending) || order.selector().equals(EOSortOrdering.CompareAscending))
+ sql += " ASC";
+ else
+ sql += " DESC";
+ appendItemToListString(sql, _orderByString());
+ }
+
+ public void addSelectListAttribute(EOAttribute attr) {
+ appendItemToListString(formatSQLString(sqlStringForAttribute(attr), attr.readFormat()), _listString());
+ }
+
+ public void addUpdateListAttribute(EOAttribute attr, Object o) {
+ StringBuffer buf = new StringBuffer(attr.columnName());
+ buf.append('=');
+ buf.append(formatSQLString(formatValueForAttribute(o, attr), attr.writeFormat()));
+ appendItemToListString(buf.toString(), _listString());
+ }
+
+ public NSMutableDictionary aliasesByRelationshipPath() {
+ if (_aliasesByRelationshipPath == null) {
+ _aliasesByRelationshipPath = new NSMutableDictionary();
+ if (!_useAliases)
+ return _aliasesByRelationshipPath;
+ _aliasesByRelationshipPath.setObjectForKey("t0", "");
+ }
+ return _aliasesByRelationshipPath;
+ }
+
+ public String allowsNullClauseForConstraint(boolean flag) {
+ return flag ? "" : " NOT NULL";
+ }
+
+ public void appendItemToListString(String item, StringBuffer list) {
+ if (list.length() > 0)
+ list.append(", ");
+ list.append(item);
+ }
+
+ public String assembleDeleteStatementWithQualifier(EOQualifier q, String tableList, String whereClause) {
+ String s = "DELETE FROM " + tableList;
+ if (whereClause != null && whereClause.length() > 0)
+ s += " WHERE " + whereClause;
+ return s;
+ }
+
+ public String assembleInsertStatementWithRow(NSDictionary row,
+ String tableList, String columnList, String valueList) {
+ String sql = "INSERT INTO " + tableList;
+ if (columnList != null)
+ sql += " (" + columnList + ")";
+ sql += " VALUES " + valueList;
+ return sql;
+ }
+
+ public String assembleJoinClause(String leftName, String rightName, int semantic) {
+ String op = "=";
+ if (semantic == EORelationship.LeftOuterJoin)
+ op = "*=";
+ else if (semantic == EORelationship.RightOuterJoin)
+ op = "=*";
+ return leftName + op + rightName;
+ }
+
+ public String assembleSelectStatementWithAttributes(NSArray attributes,
+ boolean lock, EOQualifier q, NSArray fetchOrder, String selectString, String columnList,
+ String tableList, String whereClause, String joinClause, String orderByClause, String lockClause) {
+ String sql = selectString + " " + columnList + " FROM " + tableList;
+ if (lockClause != null)
+ sql += " " + lockClause;
+ if (whereClause != null || joinClause != null)
+ sql += " WHERE ";
+ if (whereClause != null)
+ sql += whereClause;
+ if (whereClause != null && joinClause != null)
+ sql += " AND ";
+ if (joinClause != null)
+ sql += joinClause;
+ if (orderByClause != null)
+ sql += " ORDER BY " + orderByClause;
+ return sql;
+ }
+
+ public String assembleUpdateStatementWithRow(NSDictionary row, EOQualifier q, String tableList, String updateList, String whereClause) {
+ String s = "UPDATE " + tableList + " SET " + updateList;
+ if (whereClause != null && whereClause.length() > 0)
+ s += " WHERE " + whereClause;
+ return s;
+ }
+
+ public NSArray bindVariableDictionaries() {
+ return null;
+ }
+
+ public abstract NSMutableDictionary bindVariableDictionaryForAttribute(EOAttribute attr, Object o);
+
+ public String columnTypeStringForAttribute(EOAttribute attr) {
+ String x = attr.externalType();
+ if (attr.precision() > 0) {
+ x += " (" + attr.precision() + "," + attr.scale() + ")";
+ } else if (attr.width() > 0) {
+ x += " (" + attr.width() + ")";
+ }
+ return x;
+ }
+
+ public EOEntity entity() {
+ return _entity;
+ }
+
+ public String externalNameQuoteCharacter() {
+ return "\"";
+ }
+
+ public String formatSQLString(String value, String format) {
+ if (format == null)
+ return value;
+ return value;
+ }
+
+ /**
+ * Returns the received string wrapped in single quotes,
+ * with any quotes or escape chars found inside it
+ * properly escaped.
+ * @param s The string to format.
+ */
+ public String formatStringValue(String s) {
+ StringBuffer buf = new StringBuffer(s);
+ for (int i = buf.length()-1; i >= 0; i--) {
+ if (buf.charAt(i) == sqlEscapeChar()) {
+ buf.insert(i, sqlEscapeChar());
+ i++;
+ }
+ if (buf.charAt(i) == '\'') {
+ buf.insert(i, sqlEscapeChar());
+ i++;
+ }
+ }
+ buf.append('\'');
+ buf.insert(0, '\'');
+ return buf.toString();
+ }
+
+ public String formatValueForAttribute(Object value, EOAttribute attr) {
+ if (value == null || value == NSKeyValueCoding.NullValue)
+ return "NULL";
+ if (value instanceof String)
+ return formatStringValue((String)value);
+ if (value instanceof Number)
+ return sqlStringForNumber((Number)value);
+ //TODO: format timestamps
+ return value.toString();
+ }
+
+ public String joinClauseString() {
+ if (_joinClauseString == null)
+ return null;
+ return _joinClauseString.toString();
+ }
+
+ public void joinExpression() {
+ _joinClauseString = null;
+ if (_aliasesByEntityName.count() > 1) {
+ }
+ }
+
+ public String listString() {
+ return _listString().toString();
+ }
+
+ public String lockClause() {
+ return "";
+ }
+
+ public boolean mustUseBindVariableForAttribute(EOAttribute attr) {
+ return false;
+ }
+
+ public String orderByString() {
+ if (_orderByString == null)
+ return null;
+ return _orderByString.toString();
+ }
+
+ public void prepareConstraintStatementForRelationship(EORelationship rel, NSArray arr1, NSArray arr2) {
+ }
+
+ public void prepareDeleteExpressionForQualifier(EOQualifier q) {
+ String where = null;
+ setStatement(assembleDeleteStatementWithQualifier(q, _entity.externalName(), where));
+ }
+
+ public void prepareInsertExpressionWithRow(NSDictionary row) {
+ StringBuffer cols = new StringBuffer("(");
+ StringBuffer values = new StringBuffer("(");
+ Enumeration enumeration = row.keyEnumerator();
+ while (enumeration.hasMoreElements()) {
+ String key = (String)enumeration.nextElement();
+ EOAttribute a = _entity.attributeNamed(key);
+ cols.append(a.columnName());
+ values.append(formatValueForAttribute(row.objectForKey(key), a));
+ if (enumeration.hasMoreElements()) {
+ cols.append(", ");
+ values.append(", ");
+ }
+ }
+ cols.append(")");
+ cols.append(")");
+ setStatement(assembleInsertStatementWithRow(row, _entity.externalName(), cols.toString(), values.toString()));
+ }
+
+ public void prepareSelectExpressionWithAttributes(NSArray atts, boolean lock, EOFetchSpecification fspec) {
+ _aliasesByRelationshipPath = new NSMutableDictionary();
+ _aliasesByEntityName = new NSMutableDictionary();
+ EOQualifier q = null;
+ NSArray order = null;
+ if (fspec != null) {
+ q = fspec.qualifier();
+ order = fspec.sortOrderings();
+ }
+ //Assemble the column list (this yields the alias list)
+ for (int i = 0; i < atts.count(); i++)
+ addSelectListAttribute((EOAttribute)atts.objectAtIndex(i));
+ //assemble the where string
+ if (q != null) {
+ if (q instanceof EOQualifierSQLGeneration)
+ setWhereClauseString(sqlStringForQualifier((EOQualifierSQLGeneration)q));
+ else {
+ EOQualifierSQLGeneration.Support sup = EOQualifierSQLGeneration.Support.supportForClass(q.getClass());
+ setWhereClauseString(sup.sqlStringForSQLExpression(q, this));
+ }
+ }
+ //assemble the join string
+ joinExpression();
+ //assemble the order by string
+ if (order != null && order.count() > 0) {
+ for (int i = 0; i < order.count(); i++) {
+ EOSortOrdering so = (EOSortOrdering)order.objectAtIndex(i);
+ addOrderByAttributeOrdering(so);
+ }
+ }
+ //create the statement
+ setStatement(assembleSelectStatementWithAttributes(atts, lock, q, order, "SELECT", listString(),
+ tableListWithRootEntity(_entity), whereClauseString(), joinClauseString(), orderByString(), lockClause()));
+ }
+
+ /** Build an UPDATE statement with the given information. */
+ public void prepareUpdateExpressionWithRow(NSDictionary row, EOQualifier q) {
+ StringBuffer buf = new StringBuffer();
+ Enumeration enumeration = row.keyEnumerator();
+ while (enumeration.hasMoreElements()) {
+ String key = (String)enumeration.nextElement();
+ EOAttribute a = _entity.attributeNamed(key);
+ if (a == null)
+ throw new EOGeneralAdaptorException("Cannot find attribute named " + key + " in entity " + _entity.name());
+ buf.append(a.columnName());
+ buf.append('=');
+ buf.append(formatValueForAttribute(row.objectForKey(key), a));
+ if (enumeration.hasMoreElements())
+ buf.append(", ");
+ }
+ if (q != null) {
+ setWhereClauseString(sqlStringForQualifier(null));
+ }
+ setStatement(assembleUpdateStatementWithRow(row, q, _entity.externalName(), buf.toString(), whereClauseString()));
+ }
+
+ public void setStatement(String statement) {
+ _statement = statement;
+ }
+
+ public String statement() {
+ return _statement;
+ }
+
+ public void setUseAliases(boolean flag) {
+ _useAliases = flag;
+ }
+
+ public boolean useAliases() {
+ return _useAliases;
+ }
+
+ public void setUseBindVariables(boolean flag) {
+ }
+
+ public boolean useBindVariables() {
+ return System.getProperty("EOAdaptorUseBindVariables", "false").equals("true");
+ }
+
+ /** @deprecated Check externalNameQuoteCharacter instead. */
+ public static void setUseQuotedExternalNames(boolean flag) {
+ _quoteExternalNames = flag;
+ }
+ /** @deprecated Use the instance method externalNameQuoteCharacter instead. */
+ public static boolean useQuotedExternalNames() {
+ return _quoteExternalNames;
+ }
+
+ public void setWhereClauseString(String clause) {
+ _whereClauseString = clause;
+ }
+
+ public String whereClauseString() {
+ return _whereClauseString;
+ }
+
+ public boolean shouldUseBindVariableForAttribute(EOAttribute attr) {
+ return false;
+ }
+
+ public char sqlEscapeChar() {
+ return '\\';
+ }
+
+ public String sqlPatternFromShellPattern(String pattern) {
+ return sqlPatternFromShellPatternWithEscapeCharacter(pattern, sqlEscapeChar());
+ }
+
+ public String sqlPatternFromShellPatternWithEscapeCharacter(String pattern, char escape) {
+ StringBuffer buf = new StringBuffer(pattern);
+ int idx = 0;
+ //escape all '%'
+ do {
+ idx = buf.indexOf("%");
+ if (idx == 0)
+ buf.insert(escape, 0);
+ else if (idx > 0 && buf.charAt(idx-1) != escape)
+ buf.insert(escape, idx);
+ } while (idx >= 0);
+ //escape all '_'
+ do {
+ idx = buf.indexOf("_");
+ if (idx == 0)
+ buf.insert(escape, 0);
+ else if (idx > 0 && buf.charAt(idx-1) != escape)
+ buf.insert(escape, idx);
+ } while (idx >= 0);
+ //substitute all '*'
+ do {
+ idx = buf.indexOf("*");
+ if (idx >= 0)
+ buf.replace(idx, idx+1, "%");
+ } while (idx >= 0);
+ //substitute all '?'
+ do {
+ idx = buf.indexOf("?");
+ if (idx >= 0)
+ buf.replace(idx, idx+1, "_");
+ } while (idx >= 0);
+ return buf.toString();
+ }
+
+ public String sqlStringForAttribute(EOAttribute attr) {
+ if (_aliasesByEntityName == null)
+ _aliasesByEntityName = new NSMutableDictionary();
+ String alias = (String)_aliasesByEntityName.objectForKey(attr.entity().name());
+ if (alias == null) {
+ alias = "t" + (_aliasesByEntityName.count() + 1);
+ _aliasesByEntityName.setObjectForKey(alias, attr.entity().name());
+ }
+ if (useAliases())
+ return alias + "." + attr.columnName();
+ else
+ return attr.entity().externalName() + "." + attr.columnName();
+ }
+
+ public String sqlStringForAttributeNamed(String name) {
+ if (name.indexOf('.') > 0) {
+ return sqlStringForAttribute(_entity._attributeForPath(name));
+ }
+ return sqlStringForAttribute(_entity.attributeNamed(name));
+
+ }
+
+ /**
+ * Returns a string representing the path from the first
+ * relationship in the array to the last one.
+ * @param path An array of EORelationship objects.
+ * @return A string consisting of the names of the relationships
+ * separated by dots.
+ */
+ public String sqlStringForAttributePath(NSArray path) {
+ StringBuffer buf = new StringBuffer();
+ for (int i = 0; i < path.count(); i++) {
+ EORelationship rel = (EORelationship)path.objectAtIndex(i);
+ if (i > 0)
+ buf.append('.');
+ buf.append(rel.name());
+ }
+ return buf.toString();
+ }
+
+ public String sqlStringForCaseInsensitiveLike(String key, String value) {
+ return "LOWER(" + key + ") LIKE LOWER(" + sqlPatternFromShellPattern(value) + ")";
+ }
+
+ public String sqlStringForConjoinedQualifiers(NSArray qualifiers) {
+ EOAndQualifier q = new EOAndQualifier(qualifiers);
+ return EOQualifierSQLGeneration.Support.supportForClass(EOAndQualifier.class).sqlStringForSQLExpression(q, this);
+ }
+
+ public String sqlStringForData(NSData data) {
+ byte[] b = data.bytes();
+ char[] c = new char[b.length * 2];
+ int pos = 0;
+ for (int i = 0; i < b.length; i++) {
+ int x = (b[i] & 0xf0) >> 4;
+ c[pos++] = _hexChars[x];
+ x = (b[i] & 0x0f);
+ c[pos++] = _hexChars[x];
+ }
+ return new String(c);
+ }
+
+ public String sqlStringForDisjoinedQualifiers(NSArray qualifiers) {
+ EOOrQualifier q = new EOOrQualifier(qualifiers);
+ return EOQualifierSQLGeneration.Support.supportForClass(EOOrQualifier.class).sqlStringForSQLExpression(q, this);
+ }
+
+ public String sqlStringForKeyComparisonQualifier(EOKeyComparisonQualifier q) {
+ return EOQualifierSQLGeneration.Support.supportForClass(EOKeyComparisonQualifier.class).sqlStringForSQLExpression(q, this);
+ }
+
+ public String sqlStringForKeyValueQualifier(EOKeyValueQualifier q) {
+ return EOQualifierSQLGeneration.Support.supportForClass(EOKeyValueQualifier.class).sqlStringForSQLExpression(q, this);
+ }
+
+ public String sqlStringForNegatedQualifier(EOQualifier q) {
+ EOQualifierSQLGeneration.Support sup = EOQualifierSQLGeneration.Support.supportForClass(q.getClass());
+ String sql = sup.sqlStringForSQLExpression(q, this);
+ return "NOT (" + sql + ")";
+ }
+
+ public static String sqlStringForNumber(Number number) {
+ return number.toString();
+ }
+
+ public String sqlStringForQualifier(EOQualifierSQLGeneration sql) {
+ return sql.sqlStringForSQLExpression(this);
+ }
+
+ public String sqlStringForSchemaObjectName(String name) {
+ return name;
+ }
+
+ public String sqlStringForSelector(NSSelector sel, Object value) {
+ if (sel == EOQualifier.QualifierOperatorEqual) {
+ if (value == NSKeyValueCoding.NullValue)
+ return " is ";
+ return "=";
+ } else if (sel == EOQualifier.QualifierOperatorNotEqual) {
+ if (value == NSKeyValueCoding.NullValue)
+ return " is not ";
+ return "<>";
+ } else if (sel == EOQualifier.QualifierOperatorLessThan) {
+ return "<";
+ } else if (sel == EOQualifier.QualifierOperatorGreaterThan) {
+ return ">";
+ } else if (sel == EOQualifier.QualifierOperatorLessThanOrEqualTo) {
+ return "<=";
+ } else if (sel == EOQualifier.QualifierOperatorGreaterThanOrEqualTo) {
+ return ">=";
+ } else if (sel == EOQualifier.QualifierOperatorLike || sel == EOQualifier.QualifierOperatorCaseInsensitiveLike) {
+ return " like ";
+ }
+ return sel.name();
+ }
+
+ public static String sqlStringForString(String s) {
+ return s;
+ }
+
+ public String sqlStringForValue(Object value, String keyPath) {
+ EOAttribute a = _entity._attributeForPath(keyPath);
+ return formatValueForAttribute(value, a);
+ }
+
+ public String tableListWithRootEntity(EOEntity root) {
+ StringBuffer buf = new StringBuffer(root.externalName());
+ if (useAliases()) {
+ buf.append(" ");
+ buf.append(_aliasesByEntityName.objectForKey(root.name()));
+ }
+ if (_aliasesByEntityName.count() > 0) {
+ Enumeration enumeration = _aliasesByEntityName.keyEnumerator();
+ while (enumeration.hasMoreElements()) {
+ String key = (String)enumeration.nextElement();
+ if (!key.equals(root.name())) {
+ buf.append(", ");
+ buf.append(key);
+ if (useAliases())
+ buf.append(_aliasesByEntityName.objectForKey(key));
+ }
+ }
+ }
+ return buf.toString();
+ }
+
+ public String toString() {
+ return "<" + getClass().getName() + "> " + statement();
+ }
+
+ public String valueList() {
+ return _valueList().toString();
+ }
+
+}
+/*
+ * $Log$
+ * Revision 1.2 2006/02/16 16:47:14 cgruber
+ * Move some classes in to "internal" packages and re-work imports, etc.
+ *
+ * Also use UnsupportedOperationExceptions where appropriate, instead of WotonomyExceptions.
+ *
+ * Revision 1.1 2006/02/16 13:19:57 cgruber
+ * Check in all sources in eclipse-friendly maven-enabled packages.
+ *
+ * Revision 1.5 2005/05/11 15:21:53 cgruber
+ * Change enum to enumeration, since enum is now a keyword as of Java 5.0
+ *
+ * A few other comments in the code.
+ *
+ * Revision 1.4 2003/08/29 21:14:18 chochos
+ * fix the algorithm in formatStringValue.
+ *
+ * Revision 1.3 2003/08/14 02:12:32 chochos
+ * implemented use of (simple) qualifiers
+ *
+ * Revision 1.2 2003/08/13 22:59:39 chochos
+ * Can now generate simple one-entity select statements.
+ *
+ * Revision 1.1 2003/08/13 01:04:32 chochos
+ * the SQL generation classes. EOSQLExpression still needs a lot of work, but the factory is pretty much done.
+ *
+ */ \ No newline at end of file
diff --git a/projects/net.wotonomy.persistence/src/main/java/net/wotonomy/access/EOSQLExpressionFactory.java b/projects/net.wotonomy.persistence/src/main/java/net/wotonomy/access/EOSQLExpressionFactory.java
new file mode 100644
index 0000000..6bdbffe
--- /dev/null
+++ b/projects/net.wotonomy.persistence/src/main/java/net/wotonomy/access/EOSQLExpressionFactory.java
@@ -0,0 +1,131 @@
+/*
+ Wotonomy: OpenStep design patterns for pure Java applications.
+ Copyright (C) 2001 Michael Powers
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, see http://www.gnu.org
+ */
+package net.wotonomy.access;
+
+import java.lang.reflect.Constructor;
+
+import net.wotonomy.control.EOFetchSpecification;
+import net.wotonomy.control.EOQualifier;
+import net.wotonomy.foundation.NSArray;
+import net.wotonomy.foundation.NSDictionary;
+
+/**
+*
+* @author ezamudio@nasoft.com
+* @author $Author: cgruber $
+* @version $Revision: 894 $
+*/
+public class EOSQLExpressionFactory {
+
+ protected EOAdaptor _adaptor;
+ protected Class _expressionClass;
+ private Constructor _instantiator;
+
+ public EOSQLExpressionFactory(EOAdaptor adaptor) {
+ super();
+ _adaptor = adaptor;
+ _expressionClass = _adaptor.expressionClass();
+ if (_expressionClass == null)
+ throw new IllegalStateException("EOAdaptor " + _adaptor.name() + " returned null for expressionClass()");
+ }
+
+ public EOAdaptor adaptor() {
+ return _adaptor;
+ }
+
+ /**
+ * Creates an instance of the adaptor's expression class,
+ * with entity assigned to it.
+ * @param entity The entity with which to initialize the expression.
+ * @return An EOSQLExpression.
+ */
+ public EOSQLExpression createExpression(EOEntity entity) {
+ EOSQLExpression expr = null;
+ if (_instantiator == null) {
+ try {
+ _instantiator = _expressionClass.getConstructor(new Class[]{ EOEntity.class });
+ } catch (Exception ex) {
+ throw new IllegalArgumentException("The expression class " + _expressionClass.getName() + " has no constructor with an entity parameter.");
+ }
+ }
+ try {
+ expr = (EOSQLExpression)_instantiator.newInstance(new Object[]{ entity });
+ } catch (Exception ex) {
+ throw new IllegalArgumentException("Cannot create new expression of class " + _expressionClass.getName());
+ }
+ return expr;
+ }
+
+ public EOSQLExpression expressionForEntity(EOEntity entity) {
+ return createExpression(entity);
+ }
+
+ public EOSQLExpression deleteStatementWithQualifier(EOQualifier qualifier, EOEntity entity) {
+ EOSQLExpression expr = createExpression(entity);
+ expr.prepareDeleteExpressionForQualifier(qualifier);
+ return expr;
+ }
+
+ public EOSQLExpression insertStatementForRow(NSDictionary row, EOEntity entity) {
+ EOSQLExpression expr = createExpression(entity);
+ expr.prepareInsertExpressionWithRow(row);
+ return expr;
+ }
+
+ public EOSQLExpression selectStatementForAttributes(NSArray atts, boolean lock, EOFetchSpecification fspec, EOEntity entity) {
+ EOSQLExpression expr = createExpression(entity);
+ expr.prepareSelectExpressionWithAttributes(atts, lock, fspec);
+ return expr;
+ }
+
+ public EOSQLExpression updateStatementForRow(NSDictionary row, EOQualifier qualifier, EOEntity entity) {
+ EOSQLExpression expr = createExpression(entity);
+ expr.prepareUpdateExpressionWithRow(row, qualifier);
+ return expr;
+ }
+
+ public EOSQLExpression expressionForString(String sql) {
+ EOSQLExpression expr = null;
+ try {
+ expr = (EOSQLExpression)_expressionClass.newInstance();
+ } catch (Exception e) {
+ return null;
+ }
+ expr.setStatement(sql);
+ return expr;
+ }
+
+ public Class expressionClass() {
+ return _expressionClass;
+ }
+
+}
+/*
+ * $Log$
+ * Revision 1.2 2006/02/16 16:47:14 cgruber
+ * Move some classes in to "internal" packages and re-work imports, etc.
+ *
+ * Also use UnsupportedOperationExceptions where appropriate, instead of WotonomyExceptions.
+ *
+ * Revision 1.1 2006/02/16 13:19:57 cgruber
+ * Check in all sources in eclipse-friendly maven-enabled packages.
+ *
+ * Revision 1.1 2003/08/13 01:04:32 chochos
+ * the SQL generation classes. EOSQLExpression still needs a lot of work, but the factory is pretty much done.
+ *
+ */ \ No newline at end of file
diff --git a/projects/net.wotonomy.persistence/src/main/java/net/wotonomy/access/EOStoredProcedure.java b/projects/net.wotonomy.persistence/src/main/java/net/wotonomy/access/EOStoredProcedure.java
new file mode 100644
index 0000000..f38ec2a
--- /dev/null
+++ b/projects/net.wotonomy.persistence/src/main/java/net/wotonomy/access/EOStoredProcedure.java
@@ -0,0 +1,141 @@
+/*
+ Wotonomy: OpenStep design patterns for pure Java applications.
+ Copyright (C) 2001 Michael Powers
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, see http://www.gnu.org
+ */
+
+package net.wotonomy.access;
+
+import net.wotonomy.foundation.NSArray;
+import net.wotonomy.foundation.NSDictionary;
+import net.wotonomy.foundation.NSMutableArray;
+import net.wotonomy.foundation.NSMutableDictionary;
+/**
+* Represents a stored procedure in a database.
+*
+* @author ezamudio@nasoft.com
+* @author $Author: cgruber $
+* @version $Revision: 894 $
+*/
+public class EOStoredProcedure implements EOPropertyListEncoding {
+
+ protected String _name;
+ protected String _externalName;
+ protected EOModel _model;
+ protected NSArray _arguments = NSArray.EmptyArray;
+ protected NSDictionary _userInfo = NSDictionary.EmptyDictionary;
+ protected NSDictionary _internalInfo = NSDictionary.EmptyDictionary;
+
+ /** Creates a stored procedure from a property list. */
+ public EOStoredProcedure(NSDictionary dict, Object obj) {
+ super();
+ if (obj instanceof EOModel)
+ _model = (EOModel)obj;
+ NSArray a = (NSArray)dict.objectForKey("arguments");
+ if (a != null) {
+ NSMutableArray args = new NSMutableArray(a.count());
+ for (int i = 0; i < a.count(); i++) {
+ NSDictionary ad = (NSDictionary)a.objectAtIndex(i);
+ EOAttribute arg = new EOAttribute(ad, this);
+ args.addObject(arg);
+ }
+ _arguments = args;
+ }
+ setName((String)dict.objectForKey("name"));
+ setExternalName((String)dict.objectForKey("externalName"));
+ if (dict.objectForKey("userInfo") != null)
+ setUserInfo((NSDictionary)dict.objectForKey("userInfo"));
+ if (dict.objectForKey("internalInfo") != null)
+ _internalInfo = (NSDictionary)dict.objectForKey("internalInfo");
+ }
+
+ public EOStoredProcedure(String withName) {
+ super();
+ setName(withName);
+ }
+
+ public void setName(String value) {
+ _name = value;
+ }
+ public String name() {
+ return _name;
+ }
+
+ public void setExternalName(String value) {
+ _externalName = value;
+ }
+ public String externalName() {
+ return _externalName;
+ }
+
+ public void setArguments(NSArray value) {
+ _arguments = value;
+ }
+ public NSArray arguments() {
+ return _arguments;
+ }
+
+ public EOModel model() {
+ return _model;
+ }
+
+ public void setUserInfo(NSDictionary info) {
+ _userInfo = info;
+ }
+ public NSDictionary userInfo() {
+ return _userInfo;
+ }
+
+ public void awakeWithPropertyList(NSDictionary plist) {
+ }
+
+ public void encodeIntoPropertyList(NSMutableDictionary plist) {
+ plist.setObjectForKey(name(), "name");
+ plist.setObjectForKey(externalName(), "externalName");
+ NSMutableArray arr = new NSMutableArray(_arguments.count());
+ NSMutableDictionary d = null;
+ for (int i = 0; i < _arguments.count(); i++) {
+ EOAttribute a = (EOAttribute)_arguments.objectAtIndex(i);
+ d = new NSMutableDictionary();
+ a.encodeIntoPropertyList(d);
+ arr.addObject(d);
+ }
+ plist.setObjectForKey(arr, "arguments");
+ }
+
+}
+/*
+ * $Log$
+ * Revision 1.2 2006/02/16 16:47:14 cgruber
+ * Move some classes in to "internal" packages and re-work imports, etc.
+ *
+ * Also use UnsupportedOperationExceptions where appropriate, instead of WotonomyExceptions.
+ *
+ * Revision 1.1 2006/02/16 13:19:57 cgruber
+ * Check in all sources in eclipse-friendly maven-enabled packages.
+ *
+ * Revision 1.4 2003/08/11 19:38:27 chochos
+ * Can now read from a file and re-write to another file.
+ *
+ * Revision 1.3 2003/08/09 01:36:32 chochos
+ * implement EOPropertyListEncoding
+ *
+ * Revision 1.2 2003/08/08 02:14:43 chochos
+ * can create a stored procedure from a property list. main accessors are in place.
+ *
+ * Revision 1.1 2003/08/07 02:41:04 chochos
+ * these don't do much for now.
+ *
+*/ \ No newline at end of file
diff --git a/projects/net.wotonomy.persistence/src/main/java/net/wotonomy/control/AbstractObjectStore.java b/projects/net.wotonomy.persistence/src/main/java/net/wotonomy/control/AbstractObjectStore.java
new file mode 100644
index 0000000..ddaacf5
--- /dev/null
+++ b/projects/net.wotonomy.persistence/src/main/java/net/wotonomy/control/AbstractObjectStore.java
@@ -0,0 +1,762 @@
+/*
+Wotonomy: OpenStep design patterns for pure Java applications.
+Copyright (C) 2001 Michael Powers
+
+This library is free software; you can redistribute it and/or
+modify it under the terms of the GNU Lesser General Public
+License as published by the Free Software Foundation; either
+version 2.1 of the License, or (at your option) any later version.
+
+This library is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+Lesser General Public License for more details.
+
+You should have received a copy of the GNU Lesser General Public
+License along with this library; if not, see http://www.gnu.org
+*/
+
+package net.wotonomy.control;
+
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+
+import net.wotonomy.foundation.NSArray;
+import net.wotonomy.foundation.NSMutableArray;
+import net.wotonomy.foundation.NSMutableDictionary;
+import net.wotonomy.foundation.NSNotification;
+import net.wotonomy.foundation.NSNotificationCenter;
+import net.wotonomy.foundation.NSNotificationQueue;
+import net.wotonomy.foundation.NSSelector;
+import net.wotonomy.foundation.internal.WotonomyException;
+
+/**
+* An abstract implementation of object store that
+* implements common functionality. Subclasses must
+* implement data object creation, initialization, and
+* refault logic, as well as logic to commit an editing
+* context.
+*/
+public abstract class AbstractObjectStore extends EOObjectStore
+{
+ private NSMutableArray insertedIDsBuffer;
+ private NSMutableArray updatedIDsBuffer;
+ private NSMutableArray deletedIDsBuffer;
+ private NSMutableArray invalidatedIDsBuffer;
+
+ private Map snapshots;
+ private List exceptionList;
+
+ /**
+ * Constructs a new instance of this object store.
+ */
+ public AbstractObjectStore()
+ {
+ snapshots = new HashMap();
+ exceptionList = null;
+
+ insertedIDsBuffer = new NSMutableArray();
+ updatedIDsBuffer = new NSMutableArray();
+ deletedIDsBuffer = new NSMutableArray();
+ invalidatedIDsBuffer = new NSMutableArray();
+
+ // register for notifications
+ NSSelector handleNotification =
+ new NSSelector( "handleNotification",
+ new Class[] { NSNotification.class } );
+ NSNotificationCenter.defaultCenter().addObserver(
+ this,
+ handleNotification,
+ EOClassDescription.ClassDescriptionNeededForEntityNameNotification,
+ null );
+ }
+
+ /**
+ * This implementation returns an appropriately configured array fault.
+ */
+ public NSArray arrayFaultWithSourceGlobalID( EOGlobalID aGlobalID,
+ String aRelationship, EOEditingContext aContext )
+ { // System.out.println( "arrayFaultWithSourceGlobalID: " + aGlobalID + " : " + aRelationship );
+ return new ArrayFault(
+ aGlobalID, aRelationship, aContext );
+ }
+
+ /**
+ * This implementation returns the actual object for the specified id.
+ */
+ public /*EOEnterpriseObject*/Object faultForGlobalID( EOGlobalID aGlobalID,
+ EOEditingContext aContext )
+ { // System.out.println( "faultForGlobalID: " + aGlobalID );
+ return /*(EOEnterpriseObject)*/createInstanceWithEditingContext( aGlobalID, aContext );
+ }
+
+ /**
+ * 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.
+ * NOTE: Faults are not supported yet.
+ */
+ public /*EOEnterpriseObject*/Object faultForRawRow( Map aDictionary, String anEntityName,
+ EOEditingContext aContext )
+ {
+ //TODO: raw rows 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( "initializeObject: " + aGlobalID );
+ try
+ {
+ String entity = entityForGlobalIDOrObject( aGlobalID, null );
+ EOClassDescription classDesc =
+ EOClassDescription.classDescriptionForEntityName( entity );
+ if ( classDesc == null )
+ {
+ throw new WotonomyException( "Unknown entity type: " + entity );
+ }
+
+ Collection attributes = classDesc.attributeKeys();
+ Map data = readFromCache( aGlobalID, attributes );
+ String key;
+ Iterator iterator = attributes.iterator();
+ while ( iterator.hasNext() )
+ {
+ key = iterator.next().toString();
+
+ // write the snapshot's reference into the object
+ if ( anObject instanceof EOKeyValueCoding )
+ {
+ ((EOKeyValueCoding)anObject).takeStoredValueForKey( data.get( key ), key );
+ }
+ else
+ {
+ EOKeyValueCodingSupport.takeStoredValueForKey( anObject, data.get( key ), key );
+ }
+
+ //NOTE: our objects are expected to make a copy
+ // of their data before it is modified, so it's okay
+ // to return them our copy of the data:
+ // we trust that they will not modify it.
+ }
+ }
+ catch ( Exception exc )
+ {
+ exc.printStackTrace();
+ }
+ }
+
+ /**
+ * Reads the local data snapshot for the specified id.
+ * If no snapshot exists, a new snapshot is created.
+ * If the specified keys are not in the snapshot,
+ * new data is fetched into the snapshot.
+ * If null is specified, all known keys are returned.
+ * Will not return null.
+ * Result will have values for those keys and only
+ * those keys requested. Missing keys indicate an
+ * error occurred.
+ */
+ protected Map readFromCache( EOGlobalID aGlobalID, Collection keys )
+ {
+ Map snapshot = (Map) snapshots.get( aGlobalID );
+
+ // if no snapshot for this id, create an empty one
+ if ( snapshot == null )
+ {
+ snapshot = new HashMap();
+ snapshots.put( aGlobalID, snapshot );
+ }
+
+ // if we don't have all the necessary keys
+ if ( ( keys == null ) || ( ! snapshot.keySet().containsAll( keys ) ) )
+ {
+ // we need to make a server call
+ try
+ {
+ Map data = readObject( aGlobalID, keys );
+
+ // compare timestamps
+ Comparable localTimestamp = (Comparable) timestampForData( snapshot );
+ // if our local snapshot has an timestamp (new snapshots don't have timestamp)
+ if ( localTimestamp != null )
+ {
+ Comparable incomingTimestamp = (Comparable) timestampForData( data );
+ if ( incomingTimestamp == null )
+ {
+ // not allowed to happen
+ new RuntimeException( "Server returned data without an timestamp" ).printStackTrace();
+ // however, we can just assume it's a newer timestamp and continue
+ }
+
+ // if timestamps don't match
+ if ( ( incomingTimestamp == null ) || ( ! incomingTimestamp.equals( localTimestamp ) ) )
+ {
+ // dump our existing snapshot's data
+ snapshot.clear();
+ // queue for a notification on this oid as updated
+ //TODO: implement this
+ }
+ }
+
+ // copy new data into our local snapshot
+ snapshot.putAll( data );
+ }
+ catch ( Exception exc )
+ {
+ exc.printStackTrace();
+ }
+ }
+
+ // return just the requested keys from our updated snapshot
+ Map result = new HashMap();
+ if ( keys == null )
+ {
+ result.putAll( snapshot );
+ }
+ else
+ {
+ Object key;
+ Iterator iterator = keys.iterator();
+ while ( iterator.hasNext() )
+ {
+ key = iterator.next();
+ result.put( key, snapshot.get( key ) );
+ }
+ }
+ return snapshot;
+ }
+
+ /**
+ * Returns a comparable object (typically a Date or Long) for
+ * the given data map or snapshot. This is used to determine
+ * whether a local snapshot should be dumped in favor of fetched
+ * data from the server.
+ * Returns null if no timestamp can be determined, in which
+ * case the fetched data will assumed to be more recent than
+ * any local snapshot.
+ */
+ abstract protected Comparable timestampForData( Map aDataMap );
+
+ /**
+ * Extracts the global id for the fetched data or snapshot.
+ * Some entities have multi-attribute keys that would be
+ * assembled into a single instance of EOGlobalID.
+ */
+ abstract protected EOGlobalID globalIDForData( Map aDataMap );
+
+ /**
+ * Returns the entity that corresponds to the specified global id
+ * and/or object. Either may be null, but both will not be null.
+ * //FIXME: This is less than elegant.
+ */
+ abstract protected String entityForGlobalIDOrObject(
+ EOGlobalID aGlobalID, Object anObject );
+
+ /**
+ * Returns the keys that have changed on the specified object.
+ * If null, all keys are presumed changed, including relationships.
+ */
+ abstract protected Collection changedKeysForObject( Object anObject );
+
+ /**
+ * Returns the data for the row corresponding to the specified id
+ * containing at least the specified keys. Implementations are allowed
+ * to return more data than requested, and callers are advised to take
+ * advantage of the returned data.
+ */
+ abstract protected Map readObject( EOGlobalID aGlobalID, Collection keys );
+
+ /**
+ * Returns the data for the row corresponding to the specified id.
+ * //TODO: Need a better return value? How to return invalidated list?
+ */
+ abstract protected Map insertObject( EOGlobalID aGlobalID, Map aDataMap );
+
+ /**
+ * Returns the data for the row corresponding to the specified id.
+ * //TODO: Need a better return value? How to return invalidated list?
+ */
+ abstract protected Object updateObject( EOGlobalID aGlobalID, Map aDataMap );
+
+ /**
+ * Returns the data for the row corresponding to the specified id.
+ * //TODO: Need a better return value? How to return invalidated list?
+ */
+ abstract protected Object deleteObject( EOGlobalID aGlobalID );
+
+ /**
+ * Creates a new instance of an object that corresponds to the
+ * specified global id and is registered in the specified context.
+ * This implementation extracts the entity type from getEntityForGlobaID
+ * and construct a new instance from the class description that
+ * corresponds to the entity type. Override to change this behavior.
+ */
+ protected Object createInstanceWithEditingContext(
+ EOGlobalID aGlobalID, EOEditingContext aContext )
+ {
+ String entity = entityForGlobalIDOrObject( aGlobalID, null );
+ EOClassDescription classDesc =
+ EOClassDescription.classDescriptionForEntityName( entity );
+ if ( classDesc == null )
+ {
+ throw new WotonomyException( "Unknown entity type: " + entity );
+ }
+
+ Object result = classDesc.createInstanceWithEditingContext( aContext, aGlobalID );
+ if ( result instanceof EOFaulting )
+ {
+ ((EOFaulting)result).turnIntoFault( null );
+ }
+ return result;
+ }
+
+ /**
+ * Dumps the snapshot corresponding to the specified id.
+ */
+ protected void invalidateObject( EOGlobalID aGlobalID )
+ {
+ snapshots.remove( aGlobalID );
+ }
+
+ /**
+ * Dumps all snapshots.
+ */
+ protected void invalidateAllCache()
+ {
+ snapshots.clear();
+ }
+
+ /**
+ * 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()
+ {
+ invalidateAllCache();
+
+ // 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 )
+ {
+ NSArray empty = new NSArray();
+ NSMutableArray invalidated = new NSMutableArray();
+
+ Object object;
+ Iterator iterator = aList.iterator();
+ while ( iterator.hasNext() )
+ {
+ object = iterator.next();
+ invalidateObject( (EOGlobalID) object );
+ invalidated.addObject( object );
+ }
+
+ NSMutableDictionary info = new NSMutableDictionary();
+ info.setObjectForKey( empty, InsertedKey );
+ info.setObjectForKey( empty, UpdatedKey );
+ info.setObjectForKey( empty, DeletedKey );
+ info.setObjectForKey( invalidated, InvalidatedKey );
+
+ // post notification
+ NSNotificationQueue.defaultQueue().
+ enqueueNotificationWithCoalesceMaskForModes( new NSNotification(
+ ObjectsChangedInStoreNotification, this, info ),
+ NSNotificationQueue.PostNow,
+ NSNotificationQueue.NotificationNoCoalescing, null );
+ }
+
+ /**
+ * Returns false because locking is not currently permitted.
+ */
+ public boolean isObjectLockedWithGlobalID( EOGlobalID aGlobalID,
+ EOEditingContext aContext )
+ {
+ return false;
+ }
+
+ /**
+ * Does nothing because locking is not currently 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( "objectsForSourceGlobalID: " + aGlobalID + " : " + aRelationship + " : " );
+
+ Map snapshot = readFromCache( aGlobalID, new NSArray( aRelationship ) );
+ Object value = snapshot.get( aRelationship );
+ if ( value == null ) value = new NSArray(); // empty list
+ if ( ! ( value instanceof Collection ) )
+ {
+ throw new RuntimeException( "Specified relationship is not a collection: "
+ + aRelationship + " : " + aGlobalID + " : " + value );
+ }
+
+ NSArray result = new NSMutableArray();
+
+ // get fault for each id
+ EOGlobalID id;
+ Object fault;
+ Iterator iterator = ((Collection)value).iterator();
+ while ( iterator.hasNext() )
+ {
+ id = (EOGlobalID) iterator.next();
+
+ // get registered fault
+ fault = aContext.faultForGlobalID( id, aContext );
+
+ // assert fault
+ if ( fault == null )
+ {
+ // this should never happen
+ throw new RuntimeException(
+ "Could not find fault for ID: " + id );
+ }
+
+ result.add( fault );
+ }
+
+ fireObjectsChangedInStore();
+
+//System.out.println( "done" );
+ return result;
+ }
+
+ /**
+ * 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 fetched in the specified context,
+ * it is not refetched and that object should be used in the array.
+ */
+ public NSArray objectsWithFetchSpecification(
+ EOFetchSpecification aFetchSpec, EOEditingContext aContext )
+ {
+ NSMutableArray result = new NSMutableArray();
+
+ //TODO: implement this
+
+ return result;
+ }
+
+ /**
+ * Fires ObjectsChangedInStoreNotification
+ * with contents of buffers and then clears buffers.
+ * If buffers are empty, does nothing.
+ */
+ private void fireObjectsChangedInStore()
+ {
+ // check for changes to broadcast
+ if ( insertedIDsBuffer.size() + updatedIDsBuffer.size() +
+ deletedIDsBuffer.size() + invalidatedIDsBuffer.size() == 0 )
+ {
+ return;
+ }
+
+ // broadcast ObjectsChangedInStoreNotification
+ // for the benefit of child editing contexts
+
+ NSMutableDictionary storeInfo = new NSMutableDictionary();
+
+ storeInfo.setObjectForKey(
+ new NSArray( (Collection) insertedIDsBuffer ),
+ EOObjectStore.InsertedKey );
+ storeInfo.setObjectForKey(
+ new NSArray( (Collection) updatedIDsBuffer ),
+ EOObjectStore.UpdatedKey );
+ storeInfo.setObjectForKey(
+ new NSArray( (Collection) deletedIDsBuffer ),
+ EOObjectStore.DeletedKey );
+ storeInfo.setObjectForKey(
+ new NSArray( (Collection) invalidatedIDsBuffer ),
+ EOObjectStore.InvalidatedKey );
+
+ // clear buffers
+
+ insertedIDsBuffer.removeAllObjects();
+ updatedIDsBuffer.removeAllObjects();
+ deletedIDsBuffer.removeAllObjects();
+ invalidatedIDsBuffer.removeAllObjects();
+
+ // post notification
+ NSNotificationQueue.defaultQueue().
+ enqueueNotificationWithCoalesceMaskForModes( new NSNotification(
+ ObjectsChangedInStoreNotification, this, storeInfo ),
+ NSNotificationQueue.PostNow,
+ NSNotificationQueue.NotificationNoCoalescing, null );
+ }
+
+ /**
+ * 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 )
+ {
+//System.out.println( "refaultObject: " + aGlobalID );
+//new net.wotonomy.ui.swing.util.StackTraceInspector();
+ if ( anObject instanceof EOFaulting )
+ {
+ ((EOFaulting)anObject).turnIntoFault( null );
+ }
+ }
+
+ /**
+ * Writes all changes in the specified editing context
+ * to the respository.
+ */
+ public void saveChangesInEditingContext ( EOEditingContext aContext )
+ {
+ Object result; // need a container result?
+ Map updateMap;
+ Object object;
+ EOGlobalID id;
+ Iterator iterator;
+
+ //TODO: the ordering of operations here
+ // needs to be a lot more sophisticated.
+
+ // process deletes first
+ iterator = aContext.deletedObjects().iterator();
+ while ( iterator.hasNext() )
+ {
+ object = iterator.next();
+ id = aContext.globalIDForObject( object );
+ try
+ {
+ result = deleteObject( id );
+ }
+ catch ( Exception exc )
+ {
+ System.out.println( "Error deleting object: " + id );
+ exc.printStackTrace();
+ }
+ }
+
+ // process inserts next
+ iterator = aContext.insertedObjects().iterator();
+ while ( iterator.hasNext() )
+ {
+ object = iterator.next();
+ processInsert( aContext, object );
+ }
+
+ // process updates last
+ iterator = aContext.updatedObjects().iterator();
+ while ( iterator.hasNext() )
+ {
+ object = iterator.next();
+ id = aContext.globalIDForObject( object );
+ try
+ {
+ updateMap = getUpdateMap( aContext, object );
+ result = updateObject( id, updateMap );
+ }
+ catch ( Exception exc )
+ {
+ System.out.println( "Error updating object: " + id );
+ exc.printStackTrace();
+ }
+ }
+
+ //aContext.invalidateAllObjects();
+ }
+
+ protected Object processInsert( EOEditingContext aContext, Object object )
+ {
+ Map result = null;
+ EOGlobalID id = aContext.globalIDForObject( object );
+ try
+ {
+ Map updateMap;
+ updateMap = getUpdateMap( aContext, object );
+ result = insertObject( id, updateMap );
+ id = globalIDForData( result ); // read new permanent id
+
+ // broadcast that the global id has changed.
+ NSMutableDictionary userInfo = new NSMutableDictionary();
+ userInfo.setObjectForKey( id, aContext.globalIDForObject( object ) );
+ NSNotificationQueue.defaultQueue().enqueueNotification(
+ new NSNotification( EOGlobalID.GlobalIDChangedNotification,
+ null , userInfo ), NSNotificationQueue.PostNow );
+
+ }
+ catch ( Exception exc )
+ {
+ System.out.println( "Error inserting object: " + id );
+ exc.printStackTrace();
+ }
+ return result;
+ }
+
+ /**
+ * This method returns a map containing just the keys that are modified
+ * for a given object, converting any to-one or to-many relationships
+ * to id references.
+ */
+ protected Map getUpdateMap( EOEditingContext aContext, Object anObject )
+ {
+ Map result = new HashMap();
+ EOEditingContext context = aContext;
+
+ String entity = entityForGlobalIDOrObject( null, anObject );
+ EOClassDescription classDesc =
+ EOClassDescription.classDescriptionForEntityName( entity );
+ if ( classDesc == null )
+ {
+ throw new WotonomyException( "Unknown entity type: " + entity );
+ }
+
+ NSArray oneKeys = classDesc.toOneRelationshipKeys();
+ NSArray manyKeys = classDesc.toManyRelationshipKeys();
+
+ String key;
+ Object value;
+ EOGlobalID id;
+
+ Collection changedKeys = changedKeysForObject( anObject );
+ if ( changedKeys == null )
+ {
+ // assume all keys changed
+ changedKeys = classDesc.attributeKeys();
+ changedKeys.addAll( oneKeys );
+ changedKeys.addAll( manyKeys );
+ }
+ Iterator iterator = changedKeys.iterator();
+ while ( iterator.hasNext() )
+ {
+ key = iterator.next().toString();
+ if ( anObject instanceof EOKeyValueCoding )
+ {
+ value = ((EOKeyValueCoding)anObject).storedValueForKey( key );
+ }
+ else
+ {
+ value = EOKeyValueCodingSupport.storedValueForKey( anObject, key );
+ }
+
+ // convert to-one relationship to oid
+ if ( oneKeys.contains( key ) )
+ {
+ id = context.globalIDForObject( value );
+
+ // if this id hasn't been persisted, save it first
+ // NOTE: this won't work for self-referential graphs of objects!
+ if ( id.isTemporary() )
+ {
+ processInsert( aContext, value );
+ id = context.globalIDForObject( value );
+ }
+
+ value = id;
+ }
+ else
+ // convert to-many relationship list to oid list
+ if ( manyKeys.contains( key ) )
+ {
+ //NOTE: we can assume that array faults that
+ // are marked as changed have been fired.
+ if ( value instanceof Collection )
+ {
+ Object object;
+ Collection newValue = new LinkedList();
+ Iterator jiterator = ((Collection)value).iterator();
+ while ( jiterator.hasNext() )
+ {
+ object = jiterator.next();
+ id = context.globalIDForObject( object );
+
+ // if this id hasn't been persisted, save it first
+ // NOTE: this won't work for self-referential graphs of objects!
+ if ( id.isTemporary() )
+ {
+ processInsert( aContext, object );
+ id = context.globalIDForObject( object );
+ }
+ newValue.add( id );
+ }
+ value = newValue;
+ }
+ else
+ {
+ // should never happen
+ new RuntimeException(
+ "Can't update to-many relationship because it's not a Collection." )
+ .printStackTrace();
+ }
+ }
+
+ // place value in map
+ result.put( key, value );
+ }
+
+System.out.println( result );
+ return result;
+ }
+
+/*
+ * $Log$
+ * Revision 1.2 2006/02/16 16:47:14 cgruber
+ * Move some classes in to "internal" packages and re-work imports, etc.
+ *
+ * Also use UnsupportedOperationExceptions where appropriate, instead of WotonomyExceptions.
+ *
+ * Revision 1.1 2006/02/16 13:19:57 cgruber
+ * Check in all sources in eclipse-friendly maven-enabled packages.
+ *
+ * Revision 1.5 2003/12/18 15:37:38 mpowers
+ * Changes to retain ability to work with objects that don't necessarily
+ * implement EOEnterpriseObject. I would still like to preserve this case
+ * for general usage, however the access package is free to assume that
+ * those objects will be EOs and cast appropriately.
+ *
+ * Revision 1.4 2003/08/19 01:53:12 chochos
+ * EOObjectStore had some incompatible return types (Object instead of EOEnterpriseObject, in fault methods mostly). It's internally consistent but I hope it doesn't break anything based on this, even though fault methods mostly throw exceptions for now.
+ *
+ * Revision 1.3 2002/10/24 18:18:12 mpowers
+ * NSArray's are now considered read-only, so we can return our internal
+ * representation to reduce unnecessary object allocation.
+ *
+ * Revision 1.2 2002/01/19 17:27:49 mpowers
+ * Implemented most of it.
+ *
+ * Revision 1.1 2001/11/25 22:44:02 mpowers
+ * Contributing draft of AbstractObjectStore.
+ *
+ *
+ */
+}
+
diff --git a/projects/net.wotonomy.persistence/src/main/java/net/wotonomy/control/ArrayFault.java b/projects/net.wotonomy.persistence/src/main/java/net/wotonomy/control/ArrayFault.java
new file mode 100644
index 0000000..59e135b
--- /dev/null
+++ b/projects/net.wotonomy.persistence/src/main/java/net/wotonomy/control/ArrayFault.java
@@ -0,0 +1,219 @@
+/*
+Wotonomy: OpenStep design patterns for pure Java applications.
+Copyright (C) 2001 Intersect Software Corporation
+
+This library is free software; you can redistribute it and/or
+modify it under the terms of the GNU Lesser General Public
+License as published by the Free Software Foundation; either
+version 2.1 of the License, or (at your option) any later version.
+
+This library is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+Lesser General Public License for more details.
+
+You should have received a copy of the GNU Lesser General Public
+License along with this library; if not, see http://www.gnu.org
+*/
+
+package net.wotonomy.control;
+
+import java.util.Iterator;
+import java.util.List;
+import java.util.ListIterator;
+
+import net.wotonomy.foundation.NSArray;
+
+/**
+* A class that extends NSArray to intercept any accessor calls
+* in order to defer loading until the last possible moment.<br><br>
+*
+* Because ArrayFault inherits from NSArray which implements
+* List which implements Collection, data objects may declare
+* their relationships to be of type NSArray, List, or Collection.<br><br>
+*
+* This class should be returned by implementations of
+* EOObjectStore.arrayFaultForSourceGlobalID().
+*/
+public class ArrayFault extends NSArray
+{
+ private EOEditingContext editingContext;
+ private EOGlobalID sourceID;
+ private String relationshipKey;
+ private boolean fetched;
+
+ public ArrayFault(
+ EOGlobalID aSourceID,
+ String aRelationshipKey,
+ EOEditingContext aContext )
+ {
+ super();
+ editingContext = aContext;
+ sourceID = aSourceID;
+ relationshipKey = aRelationshipKey;
+ fetched = false;
+ }
+
+ public boolean isFetched()
+ {
+ return fetched;
+ }
+
+ protected void fireFault()
+ {
+ if ( !fetched )
+ {
+//new net.wotonomy.ui.swing.util.StackTraceInspector();
+//System.out.println( "ArrayFault.fireFault: before:" + this );
+ fetched = true;
+ super.protectedAddAll(
+ editingContext.parentObjectStore().objectsForSourceGlobalID(
+ sourceID,
+ relationshipKey,
+ editingContext ) );
+//System.out.println( "ArrayFault.fireFault: after:" + this );
+ }
+ }
+
+ public Object clone()
+ {
+ fireFault();
+ return super.clone();
+ }
+
+ public boolean contains(Object elem)
+ {
+ fireFault();
+ return super.contains( elem );
+ }
+
+ public boolean equals(Object o)
+ {
+ fireFault();
+ return super.equals( o );
+ }
+
+ public Object get(int index)
+ {
+ fireFault();
+ return super.get( index );
+ }
+
+ /**
+ * Overridden to return the identity hash.
+ * This somewhat violates the List contract,
+ * but otherwise calling hash code would
+ * fire the fault. Bottom line: don't use
+ * array faults as keys in hash maps.
+ */
+ public int hashCode()
+ {
+ return System.identityHashCode( this );
+ }
+
+ public int indexOf(Object o)
+ {
+ fireFault();
+ return super.indexOf( o );
+ }
+
+ public boolean isEmpty()
+ {
+ fireFault();
+ return super.isEmpty();
+ }
+
+ public Iterator iterator()
+ {
+ fireFault();
+ return super.iterator();
+ }
+
+ public int lastIndexOf(Object o)
+ {
+ fireFault();
+ return super.lastIndexOf( o );
+ }
+
+ public ListIterator listIterator()
+ {
+ fireFault();
+ return super.listIterator();
+ }
+
+ public ListIterator listIterator(int index)
+ {
+ fireFault();
+ return super.listIterator( index );
+ }
+
+ public int size()
+ {
+ fireFault();
+ return super.size();
+ }
+
+ public List subList(int fromIndex, int toIndex)
+ {
+ fireFault();
+ return super.subList( fromIndex, toIndex );
+ }
+
+ public Object[] toArray()
+ {
+ fireFault();
+ return super.toArray();
+ }
+
+ public Object[] toArray(Object[] a)
+ {
+ fireFault();
+ return super.toArray( a );
+ }
+
+ /**
+ * Overridden to display information about
+ * the fault only if not fetched.
+ * Calls to super if fetched.
+ */
+ public String toString()
+ {
+ if ( isFetched() )
+ {
+ return super.toString();
+ }
+ return "[ArrayFault@"+Integer.toHexString( System.identityHashCode( this ) )+":"+sourceID+":"+relationshipKey+"]";
+ }
+}
+
+/*
+ * $Log$
+ * Revision 1.2 2006/02/16 16:47:14 cgruber
+ * Move some classes in to "internal" packages and re-work imports, etc.
+ *
+ * Also use UnsupportedOperationExceptions where appropriate, instead of WotonomyExceptions.
+ *
+ * Revision 1.1 2006/02/16 13:19:57 cgruber
+ * Check in all sources in eclipse-friendly maven-enabled packages.
+ *
+ * Revision 1.5 2003/12/18 15:37:38 mpowers
+ * Changes to retain ability to work with objects that don't necessarily
+ * implement EOEnterpriseObject. I would still like to preserve this case
+ * for general usage, however the access package is free to assume that
+ * those objects will be EOs and cast appropriately.
+ *
+ * Revision 1.4 2003/08/19 01:53:12 chochos
+ * EOObjectStore had some incompatible return types (Object instead of EOEnterpriseObject, in fault methods mostly). It's internally consistent but I hope it doesn't break anything based on this, even though fault methods mostly throw exceptions for now.
+ *
+ * Revision 1.3 2002/10/24 18:17:37 mpowers
+ * ArrayFaults are now read-only.
+ *
+ * Revision 1.2 2001/05/06 22:22:55 mpowers
+ * Debugging.
+ *
+ * Revision 1.1 2001/05/05 23:05:42 mpowers
+ * Implemented Array Faults.
+ *
+ *
+ */
+
diff --git a/projects/net.wotonomy.persistence/src/main/java/net/wotonomy/control/ChildDataSource.java b/projects/net.wotonomy.persistence/src/main/java/net/wotonomy/control/ChildDataSource.java
new file mode 100644
index 0000000..8123668
--- /dev/null
+++ b/projects/net.wotonomy.persistence/src/main/java/net/wotonomy/control/ChildDataSource.java
@@ -0,0 +1,187 @@
+package net.wotonomy.control;
+
+import java.util.Collection;
+import java.util.Iterator;
+
+import net.wotonomy.foundation.NSArray;
+import net.wotonomy.foundation.NSMutableArray;
+
+/**
+* A data source that automates the process of
+* creating a child editing context and copying
+* objects from a parent context into it.
+* Attach this data source to a display group
+* that represents a "detail" or "drill-down"
+* view. <br><br>
+*
+* Once created, editingContext() will return the
+* child context, and fetch() will return the objects
+* that were copied into the child context.
+*/
+public class ChildDataSource extends EODataSource
+{
+ private EODataSource parent;
+ private EOEditingContext context;
+ private EOClassDescription classDescription;
+ private NSMutableArray objects;
+
+ /**
+ * Creates a child editing context for the
+ * specified parent's context and copies the
+ * specified object into the child context.
+ * The object must exist in the parent context.
+ * fetch() will return the child's object.
+ */
+ public ChildDataSource(
+ EODataSource aParentSource,
+ Object anObject )
+ {
+ this( aParentSource, new NSArray( (Object) anObject ) );
+ }
+
+ /**
+ * Creates a child editing context for the
+ * specified parent's context and copies the
+ * specified objects into the child context.
+ * The objects must exist in the parent context.
+ * The order of the parent's objects in the
+ * collection will determine the order in
+ * which the child objects are returned from
+ * fetch().
+ */
+ public ChildDataSource(
+ EODataSource aParentSource,
+ Collection anObjectList )
+ {
+ EOEditingContext parentContext =
+ aParentSource.editingContext();
+
+ parent = aParentSource;
+ context = new EOEditingContext( parentContext );
+//!new net.wotonomy.ui.swing.util.ObjectInspector( context );
+ objects = new NSMutableArray();
+ classDescription = null;
+
+ Object o;
+ Object copy;
+ boolean allSameClass = true;
+ Iterator it = anObjectList.iterator();
+ while ( it.hasNext() )
+ {
+ o = it.next();
+
+ // determine class
+ if ( allSameClass == true )
+ {
+ Class c = o.getClass();
+ if ( classDescription == null )
+ {
+ classDescription =
+ EOClassDescription.classDescriptionForClass( c );
+ }
+ else
+ {
+ if ( c != classDescription.getDescribedClass() )
+ {
+ allSameClass = false;
+ classDescription = null;
+ }
+ }
+ }
+
+ // copy and add to list
+ objects.addObject( parentContext.faultForGlobalID(
+ parentContext.globalIDForObject( o ), context ) );
+ }
+ }
+
+ /**
+ * Returns the editing context for this data source,
+ * which was created in the constructor and whose
+ * parent is the editing context specified in the
+ * constructor.
+ */
+ public EOEditingContext editingContext()
+ {
+ return context;
+ }
+
+ /**
+ * This implementation does nothing.
+ */
+ public void insertObject ( Object anObject )
+ {
+
+ }
+
+ /**
+ * This implementation does nothing.
+ */
+ public void deleteObject ( Object 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 ()
+ {
+ return new NSArray( (Collection) objects );
+ }
+
+ /**
+ * 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.
+ * This implementation forwards the call to
+ * the parent data source.
+ * @see #qualifyWithRelationshipKey
+ */
+ public EODataSource
+ dataSourceQualifiedByKey ( String aKey )
+ {
+ //FIXME: This is fundamentally broken.
+ // Objects vended from the returned source
+ // are not registered in our editing context.
+ // We probably need yet another utility data
+ // source class that would wrap another source
+ // and convert vended objects into a different
+ // context.
+
+ return parent.dataSourceQualifiedByKey( aKey );
+ }
+
+ /**
+ * Restricts this data source to vend those
+ * objects that are associated with the specified
+ * key on the specified object.
+ * This implementation forwards the call to
+ * the parent data source.
+ */
+ public void
+ qualifyWithRelationshipKey (
+ String aKey, Object anObject )
+ {
+ parent.qualifyWithRelationshipKey( aKey, 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 the class of the
+ * objects passed to the constructor if they are
+ * all the same class, otherwise returns null.
+ */
+ public EOClassDescription
+ classDescriptionForObjects ()
+ {
+ return classDescription;
+ }
+
+}
diff --git a/projects/net.wotonomy.persistence/src/main/java/net/wotonomy/control/EOAndQualifier.java b/projects/net.wotonomy.persistence/src/main/java/net/wotonomy/control/EOAndQualifier.java
new file mode 100644
index 0000000..049eb33
--- /dev/null
+++ b/projects/net.wotonomy.persistence/src/main/java/net/wotonomy/control/EOAndQualifier.java
@@ -0,0 +1,167 @@
+/*
+Wotonomy: OpenStep design patterns for pure Java applications.
+Copyright (C) 2001 Intersect Software Corporation
+
+This library is free software; you can redistribute it and/or
+modify it under the terms of the GNU Lesser General Public
+License as published by the Free Software Foundation; either
+version 2.1 of the License, or (at your option) any later version.
+
+This library is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+Lesser General Public License for more details.
+
+You should have received a copy of the GNU Lesser General Public
+License along with this library; if not, see http://www.gnu.org
+*/
+
+package net.wotonomy.control;
+
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+
+import net.wotonomy.foundation.NSArray;
+import net.wotonomy.foundation.NSDictionary;
+import net.wotonomy.foundation.NSMutableArray;
+import net.wotonomy.foundation.internal.WotonomyException;
+
+/**
+* EOAndQualifier contains other EOQualifiers,
+* evaluating as true only if all of the contained
+* qualifiers evaluate as true.
+*
+* @author michael@mpowers.net
+* @author yjcheung@intersectsoft.com
+* @author $Author: cgruber $
+* @version $Revision: 894 $
+*/
+public class EOAndQualifier extends EOQualifier
+ implements EOKeyValueArchiving, EOQualifierEvaluation
+{
+ private List qualifiers;
+
+ public EOAndQualifier(
+ List aQualifierList )
+ {
+ qualifiers = new LinkedList( aQualifierList );
+ }
+
+ /**
+ * Returns a List of qualifiers contained by this qualifier.
+ */
+ public NSArray qualifiers()
+ {
+ return new NSArray( qualifiers );
+ }
+
+ /**
+ * Add a new qualifier to the list.
+ */
+ public void addQualifier(EOQualifier qualifier)
+ {
+ qualifiers.add(qualifier);
+ }
+
+ /**
+ * Evaluates this qualifier for the specified object,
+ * and returns whether the object is qualified.
+ * selector() is invoked on the value for key() on the
+ * specified object, with value() as the parameter.
+ *
+ * Note: this has the lazy "and" implementation. Ex.
+ * Qal1 and Qal2. If the Qal1 is evaluated to be false, then it returns
+ * false without evaluating Qa12.
+ */
+ public boolean evaluateWithObject( Object anObject )
+ {
+ boolean retVal = true;
+ Iterator it = qualifiers.iterator();
+ while (it.hasNext() && retVal)
+ {
+ retVal = ((EOQualifier) it.next()).evaluateWithObject(anObject);
+ }
+ return retVal;
+ }
+
+ /**
+ * Returns a string representation of this qualifier.
+ */
+ public String toString()
+ {
+ StringBuffer myBuf = new StringBuffer("(");
+ Iterator it = qualifiers.iterator();
+ while (it.hasNext())
+ {
+ myBuf = myBuf.append(((EOQualifier) it.next()).toString()).append(" and ");
+ }
+ String myStr = myBuf.toString();
+ myStr = myStr.substring(0, myStr.lastIndexOf(" and")).concat(")");
+ return myStr;
+ }
+
+ public static Object decodeWithKeyValueUnarchiver(EOKeyValueUnarchiver arch) {
+ NSArray a = (NSArray)arch.decodeObjectForKey("qualifiers");
+ if (a == null)
+ return null;
+ NSMutableArray l = new NSMutableArray();
+ for (int i = 0; i < a.count(); i++) {
+ NSDictionary d = (NSDictionary)a.objectAtIndex(i);
+ EOKeyValueUnarchiver ua = new EOKeyValueUnarchiver(d);
+ EOQualifier q = (EOQualifier)EOQualifier.decodeWithKeyValueUnarchiver(ua);
+ if (q != null)
+ l.addObject(q);
+ }
+ return new EOAndQualifier(l);
+ }
+
+ public void encodeWithKeyValueArchiver(EOKeyValueArchiver arch) {
+ arch.encodeObject("EOAndQualifier", "class");
+ NSMutableArray arr = new NSMutableArray(qualifiers.size());
+ for (int i = 0; i < qualifiers.size(); i++) {
+ EOQualifier q = (EOQualifier)qualifiers.get(i);
+ if (q instanceof EOKeyValueArchiving) {
+ EOKeyValueArchiver ar2 = new EOKeyValueArchiver();
+ ((EOKeyValueArchiving)q).encodeWithKeyValueArchiver(ar2);
+ arr.addObject(ar2.dictionary());
+ } else
+ throw new WotonomyException("Cannot archive instance of " + q.getClass().getName());
+ }
+ arch.encodeObject(arr, "qualifiers");
+ }
+
+}
+
+/*
+ * $Log$
+ * Revision 1.2 2006/02/16 16:47:14 cgruber
+ * Move some classes in to "internal" packages and re-work imports, etc.
+ *
+ * Also use UnsupportedOperationExceptions where appropriate, instead of WotonomyExceptions.
+ *
+ * Revision 1.1 2006/02/16 13:19:57 cgruber
+ * Check in all sources in eclipse-friendly maven-enabled packages.
+ *
+ * Revision 1.6 2003/08/12 01:43:04 chochos
+ * formally implement EOQualifierEvaluation
+ *
+ * Revision 1.5 2003/08/09 01:22:51 chochos
+ * qualifiers implement EOKeyValueArchiving
+ *
+ * Revision 1.4 2003/08/06 23:07:52 chochos
+ * general code cleanup (mostly, removing unused imports)
+ *
+ * Revision 1.3 2001/10/31 15:25:14 mpowers
+ * Cleanup of qualifiers.
+ *
+ * Revision 1.2 2001/10/30 22:57:28 mpowers
+ * EOQualifier framework is now working.
+ *
+ * Revision 1.1 2001/09/13 15:25:56 mpowers
+ * Started implementation of the EOQualifier framework.
+ *
+ *
+ */
+
+
diff --git a/projects/net.wotonomy.persistence/src/main/java/net/wotonomy/control/EOClassDescription.java b/projects/net.wotonomy.persistence/src/main/java/net/wotonomy/control/EOClassDescription.java
new file mode 100644
index 0000000..cd07ebb
--- /dev/null
+++ b/projects/net.wotonomy.persistence/src/main/java/net/wotonomy/control/EOClassDescription.java
@@ -0,0 +1,601 @@
+/*
+Wotonomy: OpenStep design patterns for pure Java applications.
+Copyright (C) 2001 Intersect Software Corporation
+
+This library is free software; you can redistribute it and/or
+modify it under the terms of the GNU Lesser General Public
+License as published by the Free Software Foundation; either
+version 2.1 of the License, or (at your option) any later version.
+
+This library is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+Lesser General Public License for more details.
+
+You should have received a copy of the GNU Lesser General Public
+License along with this library; if not, see http://www.gnu.org
+*/
+
+package net.wotonomy.control;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import net.wotonomy.foundation.NSArray;
+import net.wotonomy.foundation.NSMutableArray;
+import net.wotonomy.foundation.NSNotificationCenter;
+import net.wotonomy.foundation.internal.Introspector;
+import net.wotonomy.foundation.internal.WotonomyException;
+
+/**
+* EOClassDescription provides meta-information about a class
+* and is used to customize certain behaviors within wotonomy
+* and specifically within editing contexts and object stores.
+* <br><br>
+*
+* The default implementation works for most well-formed java beans,
+* but you will want to create your own subclass most typically
+* to customize the toOne and toMany relationships for your
+* class to ensure that an entire graph of objects is not
+* persisted in order to perist a single object.
+* <br><br>
+*
+* The easiest way to register your subclass is to create it
+* in the same package as the class it describes but with
+* a "ClassDesc" suffix. For example, "my.package.MyEntity"
+* would be described by "my.package.MyEntityClassDesc". <br><br>
+*
+* Note that while the interface is the same, the implementation
+* of this class differs substantially from the specification
+* in order to be more useful for java classes.
+*
+* @author michael@mpowers.net
+* @author $Author: cgruber $
+* @version $Revision: 900 $
+*/
+public class EOClassDescription
+{
+ /**
+ * A delete rule specifying that object(s) that reference
+ * this object should have those references set to null
+ * when this object is deleted.
+ */
+ public static final int DeleteRuleNullify = 0;
+
+ /**
+ * A delete rule specifying that object(s) referenced by
+ * this object should also be deleted when this object
+ * is deleted.
+ */
+ public static final int DeleteRuleCascade = 1;
+
+ /**
+ * A delete rule specicying that this object should
+ * not be allowed to be deleted if it references any
+ * object(s).
+ */
+ public static final int DeleteRuleDeny = 2;
+
+ /**
+ * A delete rule specifying that no action be taken
+ * when this object is deleted. This is the default.
+ */
+ public static final int DeleteRuleNoAction = 3;
+
+ /**
+ * Notification fired when a class description has been requested
+ * for a class. Observers should watch for this notification and
+ * call registerClassDescription so that class descriptions can be
+ * loaded on-demand.
+ * The notification's object is the requested class and the
+ * user info dictionary is null.
+ */
+ public static final String ClassDescriptionNeededForClassNotification =
+ "ClassDescriptionNeededForClassNotification";
+
+ /**
+ * Notification fired when a class description has been requested
+ * for an entity name. Observers should watch for this notification and
+ * call registerClassDescription so that class descriptions can be
+ * loaded on-demand.
+ * The notification's object is the requested name and the
+ * user info dictionary is null.
+ */
+ public static final String ClassDescriptionNeededForEntityNameNotification =
+ "ClassDescriptionNeededForEntityNameNotification";
+
+ public EOClassDescription() {
+ super();
+ }
+
+ /**
+ * Returns the class description that corresponds to the specified class.
+ * If the class description has not already been loaded, a
+ * ClassDescriptionNeededForClassNotification is posted.
+ * If the class description is still not found, the class' loader
+ * is consulted for a class in the same package and named the same as the
+ * specified class but appended with "ClassDesc", e.g. "EmployeeObjectClassDesc".
+ * If the class description is still not found, a class description is
+ * returned that uses java bean introspection to provide reasonable values.
+ */
+ public static EOClassDescription classDescriptionForClass(
+ Class aClass )
+ {
+ if ( classMap == null ) classMap = new HashMap();
+ EOClassDescription result = (EOClassDescription) classMap.get( aClass );
+ if ( result == null )
+ {
+ // if not found, post notification
+ NSNotificationCenter.defaultCenter().postNotification(
+ ClassDescriptionNeededForClassNotification, aClass, null );
+ result = (EOClassDescription) classMap.get( aClass );
+ }
+ if ( result == null )
+ {
+ // if not found, look for similarly named class
+ String className = aClass.getName() + ClassNameSuffix;
+ Class classDesc;
+ try
+ {
+ classDesc = aClass.getClassLoader().loadClass( className );
+ if ( classDesc != null )
+ {
+ result = (EOClassDescription) classDesc.newInstance();
+ registerClassDescription( result, aClass );
+ }
+ }
+ catch ( Exception exc )
+ {
+ // ignore exceptions and resume
+ }
+ }
+ if ( result == null )
+ {
+ // if not found, default to this class
+ result = new EOClassDescription( aClass );
+ registerClassDescription( result, aClass );
+ }
+ return result;
+ }
+
+ /**
+ * Returns the class description that corresponds to the specified
+ * entity name. If the class description has not already been
+ * loaded, a ClassDescriptionNeededForEntityNameNotification is posted.
+ * Returns null if no class description can be found for the entity name.
+ */
+ public static EOClassDescription classDescriptionForEntityName(
+ String aName )
+ {
+ if ( entityMap == null ) entityMap = new HashMap();
+ EOClassDescription result = (EOClassDescription) entityMap.get( aName );
+ if ( result == null )
+ {
+ // if not found, post notification
+ NSNotificationCenter.defaultCenter().postNotification(
+ ClassDescriptionNeededForEntityNameNotification, aName, null );
+ result = (EOClassDescription) entityMap.get( aName );
+ }
+ return result;
+ }
+
+ /**
+ * Clears all cached class descriptions so that new requests
+ * for class descriptions will be re-loaded on-demand.
+ */
+ public static void invalidateClassDescriptionCache()
+ {
+ classMap.clear();
+ entityMap.clear();
+ }
+
+ /**
+ * Registers the specified class descriptiong for the specified class.
+ * Nulls are not allowed - to clear the cache call invalidateClassDescriptionCache().
+ */
+ public static void registerClassDescription(
+ EOClassDescription description,
+ Class aClass )
+ {
+ if ( classMap == null ) classMap = new HashMap();
+ if ( entityMap == null ) entityMap = new HashMap();
+ description.theClass = aClass;
+ classMap.put( aClass, description );
+ entityMap.put( description.entityName(), description );
+ }
+
+/*
+ public static Object classDelegate()
+ {
+ throw new WotonomyException( "Not implemented yet." );
+ }
+
+ public static void setClassDelegate(
+ Object aDelegate)
+ {
+ throw new WotonomyException( "Not implemented yet." );
+ }
+*/
+
+ /**
+ * The string appended to the java class name when
+ * searching the class path for an appropriate description.
+ */
+ private final static String ClassNameSuffix = "ClassDesc";
+
+ private static Map classMap;
+ private static Map entityMap;
+
+ protected Class theClass;
+ private NSMutableArray attributes;
+
+ /**
+ * Constructor may only be called by subclasses.
+ */
+ protected EOClassDescription( Class aClass )
+ {
+ theClass = aClass;
+ }
+
+ /**
+ * Returns a List of all the attributes for this class.
+ * This implementation reflects on the java class to produce
+ * a list of attributes, and then removes those keys that
+ * are returned by toOneRelationshipKeys and toManyRelationhipKeys.
+ */
+ public NSArray attributeKeys()
+ {
+ if ( attributes == null )
+ {
+ NSMutableArray readProperties = new NSMutableArray();
+ String[] read = Introspector.getReadPropertiesForClass( theClass );
+ for ( int i = 0; i < read.length; i++ )
+ {
+ readProperties.addObject( read[i] );
+ }
+
+ attributes = new NSMutableArray();
+ String[] write = Introspector.getWritePropertiesForClass( theClass );
+ for ( int i = 0; i < write.length; i++ )
+ {
+ attributes.addObject( write[i] );
+ }
+
+ // only use properties on both lists: read/write
+ attributes.retainAll( readProperties );
+
+ // remove relationship keys
+ attributes.removeAll( toOneRelationshipKeys() );
+ attributes.removeAll( toManyRelationshipKeys() );
+ }
+ return attributes;
+ }
+
+ /**
+ * This method is called when the specified object has been
+ * fetched into the specified editing context. Fetch means
+ * an object was fetched using a fetch specification - it is
+ * not the same thing as an insertion.
+ * This implementation does nothing.
+ */
+ public void awakeObjectFromFetch(
+ Object object,
+ EOEditingContext anEditingContext )
+ {
+ }
+
+ /**
+ * This method is called when the specified object has been
+ * inserted into the specified editing context. Insertion
+ * means an object was inserted by a display group - it does
+ * not mean the same thing as a fetch.
+ * This implementation does nothing.
+ */
+ public void awakeObjectFromInsertion(
+ Object object,
+ EOEditingContext anEditingContext )
+ {
+ // does nothing
+ }
+
+ /**
+ * Returns the class decription for the object referenced
+ * by the specified relationship key, or null if the
+ * class description cannot be determined for that key.
+ * This implementation returns null.
+ */
+ public EOClassDescription classDescriptionForDestinationKey(
+ String detailKey )
+ {
+ return null;
+ }
+
+ /**
+ * Creates a new instance of the class represented by this
+ * class description, registering it with the specified
+ * editing context and global id. The class description
+ * may not keep references to the newly created object.
+ * The editing context and/or the id may be null.
+ * This implementation constructs a new instance of the class
+ * and registers it with the specified editing context.
+ * If the global id is specified, the object will be populated
+ * with the appropriate data, otherwise the object will be
+ * treated as a newly inserted object.
+ * If no editing context is specified, the global id is
+ * ignored and the new instance of the class is returned.
+ */
+ public Object createInstanceWithEditingContext(
+ EOEditingContext anEditingContext,
+ EOGlobalID globalID )
+ {
+//System.out.println( "createInstanceWithEditingContext: " + this + " : " + theClass );
+ Object result = null;
+ try
+ {
+ result = theClass.newInstance();
+ if ( anEditingContext != null )
+ {
+ if ( globalID != null )
+ {
+ if ( result instanceof EOEnterpriseObject )
+ {
+ ((EOEnterpriseObject)result).awakeFromFetch( anEditingContext );
+ }
+ // register in editing context
+ anEditingContext.recordObject( result, globalID );
+ }
+ else // no global id specified
+ {
+ if ( result instanceof EOEnterpriseObject )
+ {
+ ((EOEnterpriseObject)result).awakeFromInsertion( anEditingContext );
+ }
+ // register as new object in editing context
+ anEditingContext.insertObject( result );
+ }
+ }
+ }
+ catch ( Exception exc )
+ {
+ // error instantiating
+ throw new WotonomyException( exc );
+ }
+ return result;
+ }
+
+/*
+ public NSFormatter defaultFormatterForKey(
+ String key )
+ {
+ throw new WotonomyException( "Not implemented yet." );
+ }
+*/
+
+ /**
+ * Returns the delete rule to be used for the specified
+ * relationship key.
+ * This implementation returns DeleteRuleNoAction.
+ */
+ public int deleteRuleForRelationshipKey(
+ String relationshipKey )
+ {
+ return DeleteRuleNoAction;
+ }
+
+ /**
+ * Returns a human-readable title for the specified key.
+ * For example, displayNameForKey( "firstName" ) might
+ * return "First Name".
+ * This implementation attempts to construct such a string
+ * from the key, uppercasing the first character and
+ * inserting spaces before subsequent uppercase characters.
+ */
+ public String displayNameForKey(
+ String key )
+ {
+ if ( key == null ) return "";
+ if ( key.length() == 0 ) return "";
+
+ StringBuffer result = new StringBuffer();
+ result.append( Character.toUpperCase( key.charAt(0) ) );
+
+ char c;
+ int len = key.length();
+ for ( int i = 1; i < len; i++ )
+ {
+ c = key.charAt(i);
+ if ( Character.isUpperCase( c ) )
+ {
+ result.append( ' ' );
+ }
+ result.append( c );
+ }
+
+ return result.toString();
+ }
+
+ /**
+ * Returns a human-readable title for the class of objects
+ * that this class description represents. For example,
+ * class CustomerObject might return "Customer".
+ * This implementation returns the class name.
+ */
+ public String entityName()
+ {
+ String result = theClass.getName();
+ int index = result.lastIndexOf( "." );
+ if ( index == -1 ) return result;
+ return result.substring( index+1 );
+ }
+
+ /**
+ * Returns the fetch specification associated with this
+ * class description that corresponds to the specified name,
+ * or null if not found.
+ * This implementation returns null.
+ */
+ public EOFetchSpecification fetchSpecificationNamed(
+ String aString )
+ {
+ return null;
+ }
+
+ /**
+ * Returns the relationship key by which the object at the
+ * other end of the specified relationship key refers to
+ * this object, or null if not found.
+ * This implementation returns null.
+ */
+ public String inverseForRelationshipKey(
+ String relationshipKey )
+ {
+ return null;
+ }
+
+ public boolean ownsDestinationObjectsForRelationshipKey(
+ String relationshipKey )
+ {
+ throw new WotonomyException( "Not implemented yet." );
+ }
+
+ /**
+ * Called when this object has been deleted from the
+ * specified editing context. The delete rules for this
+ * object's relationships should be executed.
+ */
+ public void propagateDeleteForObject(
+ Object object,
+ EOEditingContext anEditingContext )
+ {
+ throw new WotonomyException( "Not implemented yet." );
+ }
+
+ /**
+ * Returns a List of the "to many" relationships for
+ * this class.
+ * This implementation returns an empty list.
+ */
+ public NSArray toManyRelationshipKeys()
+ {
+ return NSArray.EmptyArray;
+ }
+
+ /**
+ * Returns a List of the "to one" relationships for
+ * this class.
+ * This implementation returns an empty list.
+ */
+ public NSArray toOneRelationshipKeys()
+ {
+ return NSArray.EmptyArray;
+ }
+
+ /**
+ * Returns a human-readable description of the specified object
+ * that should not exceed 60 characters.
+ * This implementation returns anObject.toString().
+ */
+ public String userPresentableDescriptionForObject(
+ Object anObject )
+ {
+ return anObject.toString();
+ }
+
+ /**
+ * Verifies that the specified object may be deleted.
+ * Throws an exception with a user-readable error message
+ * if the delete operation should not be allowed.
+ * This implementation does nothing.
+ */
+ public void validateObjectForDelete(
+ Object object )
+ {
+ // does nothing
+ }
+
+ /**
+ * Verifies that the specified object may be saved.
+ * Throws an exception with a user-readable error message
+ * if the save operation should not be allowed.
+ * This implementation does nothing.
+ */
+ public void validateObjectForSave(
+ Object object )
+ {
+ // does nothing
+ }
+
+ /**
+ * Validates the specified value for the specified key on this
+ * this class. Returns null if the value is acceptable, or
+ * returns an object that should be used in place of the specified
+ * object, or throws an exception with a user-readable error message
+ * if no acceptable value can be determined.
+ * This implementation returns null.
+ */
+ public Object validateValueForKey( Object value, String key)
+ {
+ return null;
+ }
+
+ /**
+ * Returns the Java Class that this description describes.
+ * NOTE: This method is not in the specification.
+ */
+ public Class getDescribedClass()
+ {
+ return theClass;
+ }
+
+}
+
+/*
+ * $Log$
+ * Revision 1.3 2006/02/18 22:46:44 cgruber
+ * Add Surrogate map from .util into control's internal package, and fix imports.
+ *
+ * Revision 1.2 2006/02/16 16:47:14 cgruber
+ * Move some classes in to "internal" packages and re-work imports, etc.
+ *
+ * Also use UnsupportedOperationExceptions where appropriate, instead of WotonomyExceptions.
+ *
+ * Revision 1.1 2006/02/16 13:19:57 cgruber
+ * Check in all sources in eclipse-friendly maven-enabled packages.
+ *
+ * Revision 1.11 2003/08/08 05:50:32 chochos
+ * theClass is protected instead of private
+ *
+ * Revision 1.10 2003/08/08 00:37:44 chochos
+ * default constructor is needed by subclasses
+ *
+ * Revision 1.9 2001/12/20 18:55:46 mpowers
+ * Hooks for awakeFromInsertion and awakeFromFetch.
+ *
+ * Revision 1.8 2001/12/01 23:51:45 mpowers
+ * Corrected createWithEditingContext.
+ *
+ * Revision 1.7 2001/11/25 22:43:38 mpowers
+ * Corrected createInstanceWithEditingContext.
+ *
+ * Revision 1.6 2001/04/29 02:29:31 mpowers
+ * Debugging relationship faulting.
+ *
+ * Revision 1.5 2001/04/28 22:17:51 mpowers
+ * Revised PropertyDataSource to be EOClassDescription-aware.
+ *
+ * Revision 1.4 2001/04/28 14:12:23 mpowers
+ * Refactored cloning/copying into KeyValueCodingUtilities.
+ *
+ * Revision 1.3 2001/04/27 23:37:20 mpowers
+ * Now using EOClassDescription in the EODataSource class, as we should.
+ *
+ * Revision 1.2 2001/04/27 00:27:42 mpowers
+ * Partial implementation.
+ *
+ * Revision 1.1 2001/03/29 03:29:49 mpowers
+ * Now using KeyValueCoding and Support instead of Introspector.
+ *
+ *
+ */
+
+
diff --git a/projects/net.wotonomy.persistence/src/main/java/net/wotonomy/control/EOCooperatingObjectStore.java b/projects/net.wotonomy.persistence/src/main/java/net/wotonomy/control/EOCooperatingObjectStore.java
new file mode 100644
index 0000000..072f867
--- /dev/null
+++ b/projects/net.wotonomy.persistence/src/main/java/net/wotonomy/control/EOCooperatingObjectStore.java
@@ -0,0 +1,82 @@
+/*
+Wotonomy: OpenStep design patterns for pure Java applications.
+Copyright (C) 2002 Israfil consulting Services Corporation
+
+This library is free software; you can redistribute it and/or
+modify it under the terms of the GNU Lesser General Public
+License as published by the Free Software Foundation; either
+version 2.1 of the License, or (at your option) any later version.
+
+This library is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+Lesser General Public License for more details.
+
+You should have received a copy of the GNU Lesser General Public
+License along with this library; if not, see http://www.gnu.org
+
+$Id: EOCooperatingObjectStore.java 894 2006-02-16 16:47:14Z cgruber $
+
+*/
+
+package net.wotonomy.control;
+
+import net.wotonomy.foundation.NSArray;
+import net.wotonomy.foundation.NSDictionary;
+import net.wotonomy.foundation.NSLocking;
+/**
+* A representation of a channel of communication to the database.
+*
+* @author cgruber@israfil.net
+* @author $Author: cgruber $
+* @version $Revision: 894 $
+*/
+
+public abstract class EOCooperatingObjectStore extends EOObjectStore
+ implements NSLocking {
+
+ public EOCooperatingObjectStore() {
+ }
+
+ public abstract boolean ownsGlobalID(EOGlobalID eoglobalid);
+
+ public abstract boolean ownsObject(EOEnterpriseObject eoenterpriseobject);
+
+ public abstract boolean handlesFetchSpecification(EOFetchSpecification eofetchspecification);
+
+ public abstract void prepareForSaveWithCoordinator(EOObjectStoreCoordinator eoobjectstorecoordinator, EOEditingContext eoeditingcontext);
+
+ public abstract void recordChangesInEditingContext();
+
+ public abstract void recordUpdateForObject(EOEnterpriseObject eoenterpriseobject, NSDictionary nsdictionary);
+
+ public abstract void performChanges();
+
+ public abstract void commitChanges();
+
+ public abstract void rollbackChanges();
+
+ public abstract NSDictionary valuesForKeys(NSArray nsarray, EOEnterpriseObject eoenterpriseobject);
+
+ public abstract void lock();
+
+ public abstract void unlock();
+
+}
+/*
+ * $Log$
+ * Revision 1.2 2006/02/16 16:47:14 cgruber
+ * Move some classes in to "internal" packages and re-work imports, etc.
+ *
+ * Also use UnsupportedOperationExceptions where appropriate, instead of WotonomyExceptions.
+ *
+ * Revision 1.1 2006/02/16 13:19:57 cgruber
+ * Check in all sources in eclipse-friendly maven-enabled packages.
+ *
+ * Revision 1.1 2002/07/14 21:59:06 mpowers
+ * Contributions from cgruber.
+ *
+ * Revision 1.2 2002/06/21 22:14:30 cgruber
+ * Add a log trail
+ *
+ */
diff --git a/projects/net.wotonomy.persistence/src/main/java/net/wotonomy/control/EOCustomObject.java b/projects/net.wotonomy.persistence/src/main/java/net/wotonomy/control/EOCustomObject.java
new file mode 100644
index 0000000..6b262cb
--- /dev/null
+++ b/projects/net.wotonomy.persistence/src/main/java/net/wotonomy/control/EOCustomObject.java
@@ -0,0 +1,673 @@
+/*
+Wotonomy: OpenStep design patterns for pure Java applications.
+Copyright (C) 2001 Intersect Software Corporation
+
+This library is free software; you can redistribute it and/or
+modify it under the terms of the GNU Lesser General Public
+License as published by the Free Software Foundation; either
+version 2.1 of the License, or (at your option) any later version.
+
+This library is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+Lesser General Public License for more details.
+
+You should have received a copy of the GNU Lesser General Public
+License along with this library; if not, see http://www.gnu.org
+*/
+
+package net.wotonomy.control;
+
+import java.util.Collection;
+import java.util.List;
+import java.util.Map;
+
+import net.wotonomy.foundation.NSArray;
+import net.wotonomy.foundation.NSDictionary;
+import net.wotonomy.foundation.NSSet;
+import net.wotonomy.foundation.internal.WotonomyException;
+
+/**
+* EOCustomObject implements all the necessary interfaces to
+* receive first-class treatment from the control framework.
+* The implementation delegates as much class meta-behavior as
+* possible to EOClassDescription, letting subclasses
+* focus exclusively on business logic while still allowing
+* them to customize as much class behavior as needed.
+*
+* @author michael@mpowers.net
+* @author $Author: cgruber $
+* @version $Revision: 894 $
+*/
+public class EOCustomObject
+ implements EOEnterpriseObject,
+ EOKeyValueCodingAdditions,
+ EODeferredFaulting,
+ EORelationshipManipulation,
+ EOValidation
+{
+ private transient static EOClassDescription classDescription;
+ private transient EOEditingContext editingContext;
+
+ // static configuration
+
+ /**
+ * Specifies whether the implementation of EOKeyValueCoding
+ * is permitted to access field directly. This implementation
+ * returns true; subclasses may override to customize this behavior.
+ */
+ public static boolean canAccessFieldsDirectly()
+ {
+ return true;
+ }
+
+ /**
+ * Specifies whether the implementation of EOKeyValueCoding
+ * is permitted to access private accessors. This implementation
+ * returns true; subclasses may override to customize this behavior.
+ */
+ public static boolean shouldUseStoredAccessors()
+ {
+ return true;
+ }
+
+ /**
+ * Specifies whether deferred faults should be used. This implementation
+ * returns false; subclasses may override to customize this behavior.
+ */
+ public static boolean usesDeferredFaultCreation()
+ {
+ return false;
+ }
+
+ // constructors
+
+ /**
+ * Default constructor initializes private state.
+ * EditingContext and ClassDescription are set to null.
+ */
+ public EOCustomObject()
+ {
+ editingContext = null;
+ classDescription = null;
+ }
+
+ /**
+ * Preferred constructor, specifying an editing context,
+ * a class description, and a global id, any or all of which
+ * may be null. Subclasses should invoke this constructor.
+ */
+ public EOCustomObject(
+ EOEditingContext aContext,
+ EOClassDescription aClassDescription,
+ EOGlobalID aGlobalID )
+ {
+ editingContext = aContext;
+ classDescription = aClassDescription;
+ }
+
+ // interface EOEnterpriseObject
+
+ /**
+ * Returns a List of all property keys defined on this object.
+ * This includes both attributes and relationships.
+ * This implementation returns the union of attributeKeys,
+ * toOneRelationshipKeys, and toManyRelationshipKeys.
+ */
+ public NSArray allPropertyKeys()
+ {
+ NSSet union = new NSSet();
+ union.addAll( attributeKeys() );
+ union.addAll( toOneRelationshipKeys() );
+ union.addAll( toManyRelationshipKeys() );
+ return new NSArray( (Collection) union );
+ }
+
+ /**
+ * Returns a list of all attributes defined on this object.
+ * Attributes are all properties that are not relationships.
+ * This implementation retrieves the keys from the class
+ * description.
+ */
+ public NSArray attributeKeys()
+ {
+ return classDescription().attributeKeys();
+ }
+
+ //void awakeFromClientUpdate(EOEditingContext aContext)
+
+ /**
+ * Called when the object has first been fetched into the
+ * specified editing context. This implementation calls
+ * awakeObjectFromFetch on the class description.
+ */
+ public void awakeFromFetch(EOEditingContext anEditingContext)
+ {
+ classDescription().awakeObjectFromFetch( this, anEditingContext );
+ }
+
+ /**
+ * Called when the object has been inserted into the
+ * specified editing context. This implementation calls
+ * awakeObjectFromInsertion on the class description.
+ */
+ public void awakeFromInsertion(EOEditingContext anEditingContext)
+ {
+ classDescription().awakeObjectFromInsertion( this, anEditingContext );
+ }
+
+ /**
+ * Returns a Map representing the delta of the current state
+ * from the state represented in the specified snapshot.
+ * The result will contain only the keys that have changed
+ * and their values. Relationship keys will map to an NSArray
+ * that contains an NSArray of added objects and an NSArray
+ * of removed objects, in that order.
+ */
+ public NSDictionary changesFromSnapshot(NSDictionary snapshot)
+ {
+ throw new WotonomyException( "Not implemented yet." );
+ }
+
+ /**
+ * Returns a class description for this object.
+ * Calls EOClassDescription.classDescriptionForClass.
+ */
+ public EOClassDescription classDescription()
+ {
+ if ( classDescription == null )
+ {
+ classDescription = EOClassDescription.classDescriptionForClass( getClass() );
+ if ( classDescription == null )
+ {
+ throw new WotonomyException(
+ "No class description found for class: " + getClass() );
+ }
+ }
+ return classDescription;
+ }
+
+ /**
+ * Returns a class description for the object at the
+ * other end of the specified relationship key.
+ * This implementation calls to the classDescription.
+ */
+ public EOClassDescription classDescriptionForDestinationKey(String aKey)
+ {
+ return classDescription().classDescriptionForDestinationKey( aKey );
+ }
+
+ /**
+ * Clears all property values for this object.
+ * This method is called to clean-up an object that
+ * will no longer be used, and implementations should
+ * ensure that all references are set to null to
+ * prevent problems with garbage-collection.
+ */
+ public void clearProperties()
+ {
+ //FIXME: clear properties here
+ }
+
+ /**
+ * Returns the delete rule constant defined on EOClassDescription
+ * for the relationship defined by the specified key.
+ * This implementation calls to the classDescription.
+ */
+ public int deleteRuleForRelationshipKey(String aRelationshipKey)
+ {
+ return classDescription().deleteRuleForRelationshipKey( aRelationshipKey );
+ }
+
+ /**
+ * Returns the editing context in which this object is registered.
+ */
+ public EOEditingContext editingContext()
+ {
+ return editingContext;
+ }
+
+ /**
+ * Returns the name of the entity that this object represents.
+ */
+ public String entityName()
+ {
+ return classDescription().entityName();
+ }
+
+ /**
+ * Returns a String containing all property keys and values for
+ * this object. Relationships should be represented by calling
+ * eoShallowDescription() on the object.
+ */
+ public String eoDescription()
+ {
+ throw new WotonomyException( "Not implemented yet." );
+ }
+
+ /**
+ * Returns a String containing all attribute keys and values for
+ * this object. Relationships are not included.
+ */
+ public String eoShallowDescription()
+ {
+ throw new WotonomyException( "Not implemented yet." );
+ }
+
+ /**
+ * Returns the key used to reference this object on the
+ * object at the other end of the specified relationship.
+ * This implementation calls to the class description.
+ */
+ public String inverseForRelationshipKey(String aRelationshipKey)
+ {
+ return classDescription().inverseForRelationshipKey( aRelationshipKey );
+ }
+
+ //Object invokeRemoteMethod(
+ // String aMethodName, Class[] aTypeArray Object[] anArgumentArray)
+
+ /**
+ * Returns whether the specified relationship key represents
+ * a to-many relationship.
+ */
+ public boolean isToManyKey(String aKey)
+ {
+ return toManyRelationshipKeys().containsObject( aKey );
+ }
+
+ /**
+ * Returns whether the objects at the other end of the specified
+ * relationship should be deleted when this object is deleted.
+ * This implementation calls to the class description.
+ */
+ public boolean ownsDestinationObjectsForRelationshipKey(String aKey)
+ {
+ return classDescription().ownsDestinationObjectsForRelationshipKey( aKey );
+ }
+
+ //void prepareValuesForClient()
+
+ /**
+ * Called to perform the delete propagation for this object
+ * on the specified editing context. All relationships
+ * should be processed according to their corresponding
+ * delete rule.
+ * This implementation calls to the class description.
+ */
+ public void propagateDeleteWithEditingContext(EOEditingContext aContext)
+ {
+ classDescription().propagateDeleteForObject( this, aContext );
+ }
+
+ /**
+ * Applies the changes from the specified snapshot to
+ * this object.
+ * @see #changesFromSnapshot(NSDictionary)
+ */
+ public void reapplyChangesFromDictionary(NSDictionary aDeltaSnapshot)
+ {
+ throw new WotonomyException( "Not implemented yet." );
+ }
+
+ /**
+ * Returns a snapshot of the current state of this object.
+ * All property keys are mapped to their values; nulls are
+ * represented by NSNull.
+ */
+ public NSDictionary snapshot()
+ {
+ throw new WotonomyException( "Not implemented yet." );
+ }
+
+ /**
+ * Returns a List of the to-many relationship keys
+ * for this object.
+ * This implementation calls to the class description.
+ */
+ public NSArray toManyRelationshipKeys()
+ {
+ return classDescription().toManyRelationshipKeys();
+ }
+
+ /**
+ * Returns a List of the to-one relationship keys
+ * for this object.
+ * This implementation calls to the class description.
+ */
+ public NSArray toOneRelationshipKeys()
+ {
+ return classDescription().toOneRelationshipKeys();
+ }
+
+ /**
+ * Applies the specified snapshot to this object,
+ * converting NSNulls to null and calling
+ * takeStoredValueForKey for each key in the Map.
+ */
+ public void updateFromSnapshot(NSDictionary aSnapshot)
+ {
+ throw new WotonomyException( "Not implemented yet." );
+ }
+
+ /**
+ * Returns a short, stateful string representation
+ * of this object.
+ * This implementation calls to the class description.
+ */
+ public String userPresentableDescription()
+ {
+ return classDescription().userPresentableDescriptionForObject( this );
+ }
+
+ /**
+ * This method should be called by each setter method
+ * on this object before changes are made to the
+ * object's internal state. This implementation calls
+ * EOObserverCenter.notifyObserversObjectWillChange( this ),
+ */
+ public void willChange()
+ {
+ EOObserverCenter.notifyObserversObjectWillChange( this );
+ }
+
+ // interface EOKeyValueCoding
+
+ /**
+ * Returns the value for the specified property.
+ * If the property does not exist, this method should
+ * call handleQueryWithUnboundKey.
+ */
+ public Object valueForKey( String 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 )
+ {
+ EOKeyValueCodingSupport.takeValueForKey( this, aValue, aKey );
+ }
+
+ /**
+ * Returns the value for the private field that
+ * corresponds to the specified property.
+ */
+ public Object storedValueForKey( String 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 )
+ {
+ 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 )
+ {
+ 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 )
+ {
+ 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 )
+ {
+ EOKeyValueCodingSupport.unableToSetNullForKey( this, aKey );
+ }
+
+ // interface EOKeyValueCodingAdditions
+
+ /**
+ * Returns the value for the specified key path, which is
+ * a series of keys delimited by ".", for example:
+ * "createTime.year.length".
+ */
+ public Object valueForKeyPath( String aKeyPath )
+ {
+ throw new WotonomyException( "Not implemented yet." );
+ }
+
+ /**
+ * Sets the value for the specified key path, which is
+ * a series of keys delimited by ".", for example:
+ * "createTime.year.length".
+ * The value is set for the last object referenced by
+ * the key path.
+ */
+ public void takeValueForKeyPath( Object aValue, String aKeyPath )
+ {
+ throw new WotonomyException( "Not implemented yet." );
+ }
+
+ /**
+ * Returns a Map of the specified keys to their values,
+ * each of which might be obtained by calling valueForKey.
+ */
+ public NSDictionary valuesForKeys( List aKeyList )
+ {
+ return KeyValueCodingUtilities.valuesForKeys( this, aKeyList );
+ }
+
+ /**
+ * Takes the keys from the specified map as properties
+ * and applies the corresponding values, each of which
+ * might be set by calling takeValueForKey.
+ */
+ public void takeValuesFromDictionary( Map aMap )
+ {
+ KeyValueCodingUtilities.takeValuesFromDictionary( this, aMap );
+ }
+
+ // interface EOFaulting
+
+ /**
+ * Called by EOFaultHandler to prepare the object to be turned into a fault.
+ */
+ public void clearFault()
+ {
+ throw new WotonomyException( "Not implemented yet." );
+ }
+
+ /**
+ * Returns this object's EOFaultHandler.
+ */
+ public EOFaultHandler faultHandler()
+ {
+ throw new WotonomyException( "Not implemented yet." );
+ }
+
+ /**
+ * Returns whether this object is currently a fault.
+ * Returns true if this object has not yet retrieved any values.
+ */
+ public boolean isFault()
+ {
+ throw new WotonomyException( "Not implemented yet." );
+ }
+
+ /**
+ * Turns this object into a fault using the specified fault handler.
+ */
+ public void turnIntoFault( EOFaultHandler aFaultHandler )
+ {
+ throw new WotonomyException( "Not implemented yet." );
+ }
+
+ /**
+ * Called to completely fire the fault, reading all attributes.
+ * This method may be implemented to call willRead(null).
+ */
+ public void willRead()
+ {
+ throw new WotonomyException( "Not implemented yet." );
+ }
+
+ /**
+ * Called to fire the fault for the specified key.
+ * The fault manager is required to populate the specified key
+ * with a value, and may populate any or all of the other values
+ * on this object. A null key will populate all values on the object.
+ * NOTE: This method is not part of the specification.
+ */
+ public void willRead( String aKey )
+ {
+ throw new WotonomyException( "Not implemented yet." );
+ }
+
+ // interface EODeferredFaulting
+
+ /**
+ * Returns a fault for the specified deferred fault.
+ */
+ public Object willReadRelationship( Object anObject )
+ {
+ throw new WotonomyException( "Not implemented yet." );
+ }
+
+ // interface EORelationshipManipulation
+
+ /**
+ * Adds the specified object to the relationship on this
+ * object specified by the key. For to-one relationships,
+ * this operation is the same as valueForKey.
+ */
+ public void addObjectToPropertyWithKey(
+ Object anObject, String aKey )
+ {
+ throw new WotonomyException( "Not implemented yet." );
+ }
+
+ /**
+ * Removes the specified object from the relationship on
+ * this object specified by the key. For to-one relationships,
+ * this operation is the same as takeValueForKey with a null
+ * value.
+ */
+ public void removeObjectFromPropertyWithKey(
+ Object anObject, String aKey )
+ {
+ throw new WotonomyException( "Not implemented yet." );
+ }
+
+ /**
+ * As addObjectToProperty with key, but also performs the
+ * reciprocal operation on the other side of the relationship.
+ */
+ public void addObjectToBothSidesOfRelationshipWithKey(
+ EORelationshipManipulation anObject, String aKey )
+ {
+ throw new WotonomyException( "Not implemented yet." );
+ }
+
+ /**
+ * As removeObjectFromPropertyWithKey with key, but also performs the
+ * reciprocal operation on the other side of the relationship.
+ */
+ public void removeObjectFromBothSidesOfRelationshipWithKey(
+ EORelationshipManipulation anObject, String aKey )
+ {
+ throw new WotonomyException( "Not implemented yet." );
+ }
+
+ // interface EOValidation
+
+ /**
+ * Validates this object for delete.
+ * Throws an exception if this object cannot be deleted.
+ * This implementation calls to the class description.
+ */
+ public void validateForDelete()
+ {
+ classDescription().validateObjectForDelete( this );
+ }
+
+ /**
+ * Validates this object for insertion into the external store.
+ * Throws an exception if this object cannot be inserted.
+ * Validations here should be specific to insertion.
+ * This implementation calls validateForSave().
+ */
+ public void validateForInsert()
+ {
+ validateForSave();
+ }
+
+ /**
+ * Validates this object for a commit to the external store.
+ * Throws an exception if this object cannot be committed.
+ * Validations here are not specific to either inserts or updates.
+ * This implementation calls to the class description.
+ */
+ public void validateForSave()
+ {
+ classDescription().validateObjectForSave( this );
+ }
+
+ /**
+ * Validates this object for update to the external store.
+ * Throws an exception if this object cannot be updated.
+ * Validations here should be specific to updates.
+ * This implementation calls validateForSave().
+ */
+ public void validateForUpdate()
+ {
+ validateForSave();
+ }
+}
+
+/*
+ * $Log$
+ * Revision 1.2 2006/02/16 16:47:14 cgruber
+ * Move some classes in to "internal" packages and re-work imports, etc.
+ *
+ * Also use UnsupportedOperationExceptions where appropriate, instead of WotonomyExceptions.
+ *
+ * Revision 1.1 2006/02/16 13:19:57 cgruber
+ * Check in all sources in eclipse-friendly maven-enabled packages.
+ *
+ * Revision 1.3 2001/12/06 16:42:29 mpowers
+ * Added appropriate constructor.
+ *
+ * Revision 1.2 2001/11/24 17:37:29 mpowers
+ * Implemented static methods.
+ *
+ * Revision 1.1 2001/11/17 17:18:15 mpowers
+ * Initial implementation of EOCustomObject.
+ *
+ *
+ */
+
+
diff --git a/projects/net.wotonomy.persistence/src/main/java/net/wotonomy/control/EODataSource.java b/projects/net.wotonomy.persistence/src/main/java/net/wotonomy/control/EODataSource.java
new file mode 100644
index 0000000..c7e5284
--- /dev/null
+++ b/projects/net.wotonomy.persistence/src/main/java/net/wotonomy/control/EODataSource.java
@@ -0,0 +1,164 @@
+/*
+Wotonomy: OpenStep design patterns for pure Java applications.
+Copyright (C) 2000 Michael Powers
+
+This library is free software; you can redistribute it and/or
+modify it under the terms of the GNU Lesser General Public
+License as published by the Free Software Foundation; either
+version 2.1 of the License, or (at your option) any later version.
+
+This library is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+Lesser General Public License for more details.
+
+You should have received a copy of the GNU Lesser General Public
+License along with this library; if not, see http://www.gnu.org
+*/
+
+package net.wotonomy.control;
+
+import net.wotonomy.foundation.NSArray;
+
+/**
+* EODataSource is used by EODisplayGroup.fetch() to retrieve
+* a list of objects to display. When a display group has a
+* data source, the display group will use the data source to
+* populate the object list and to create new objects to be
+* displayed in the list, and will update the data source when
+* objects are inserted or removed from the list. <br><br>
+*
+* In certain cases, as when a display group needs to populate
+* a child display group to show a one-to-many relationship,
+* the display group will call dataSourceQualifiedByKey to
+* return a new data source that can vend objects associated
+* with the specified key and then call qualifyWithRelationshipKey
+* to specify the object and key that are the source of the
+* child relationship. <br><br>
+*
+* Concrete subclasses are expected to override fetch() and
+* are required to override insertObject and removeObject.
+*
+* @author michael@mpowers.net
+* @author $Author: cgruber $
+* @version $Revision: 894 $
+*/
+public abstract class EODataSource
+{
+ /**
+ * Creates a new object. You should call
+ * insertObject() to insert the new object into
+ * this data source.
+ * This implementation attempts to create a new
+ * instance of the class returned by
+ * classDescriptionForObjects().
+ * Override to return an object specific to
+ * your implementation.
+ * @return The newly created object, or null if
+ * new objects are not supported by this data source.
+ * @see #classDescriptionForObjects
+ */
+ public Object createObject ()
+ {
+ Object result = null;
+ EOClassDescription c = classDescriptionForObjects();
+ if ( c != null )
+ {
+ result = c.createInstanceWithEditingContext( editingContext(), null );
+ }
+ return result;
+ }
+
+ /**
+ * Inserts the specified object into this data source.
+ */
+ public abstract void insertObject ( Object anObject );
+
+ /**
+ * Deletes the specified object from this data source.
+ */
+ public abstract void deleteObject ( Object anObject );
+
+ /**
+ * Returns the editing context for this data source,
+ * or null if no editing context is used.
+ * This implementation returns null.
+ */
+ public EOEditingContext editingContext ()
+ {
+ return null;
+ }
+
+ /**
+ * Returns a List containing the objects in this
+ * data source. This implementation returns null.
+ */
+ public NSArray fetchObjects ()
+ {
+ return null;
+ }
+
+ /**
+ * 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 abstract EODataSource
+ dataSourceQualifiedByKey ( String aKey );
+
+ /**
+ * Restricts this data source to vend those
+ * objects that are associated with the specified
+ * key on the specified object.
+ */
+ public abstract void
+ qualifyWithRelationshipKey (
+ String aKey, Object 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 null.
+ */
+ public EOClassDescription
+ classDescriptionForObjects ()
+ {
+ return null;
+ }
+
+}
+
+/*
+ * $Log$
+ * Revision 1.2 2006/02/16 16:47:14 cgruber
+ * Move some classes in to "internal" packages and re-work imports, etc.
+ *
+ * Also use UnsupportedOperationExceptions where appropriate, instead of WotonomyExceptions.
+ *
+ * Revision 1.1 2006/02/16 13:19:57 cgruber
+ * Check in all sources in eclipse-friendly maven-enabled packages.
+ *
+ * Revision 1.5 2001/05/21 14:02:44 mpowers
+ * Corrected javadoc.
+ *
+ * Revision 1.4 2001/04/27 23:37:20 mpowers
+ * Now using EOClassDescription in the EODataSource class, as we should.
+ *
+ * Revision 1.3 2001/02/27 23:11:07 mpowers
+ * Removed object registration from createObject().
+ *
+ * Revision 1.2 2001/02/16 18:34:19 mpowers
+ * Implementing nested contexts.
+ *
+ * Revision 1.1.1.1 2000/12/21 15:46:38 mpowers
+ * Contributing wotonomy.
+ *
+ * Revision 1.3 2000/12/20 16:25:34 michael
+ * Added log to all files.
+ *
+ *
+ */
+
diff --git a/projects/net.wotonomy.persistence/src/main/java/net/wotonomy/control/EODatabaseDataSource.java b/projects/net.wotonomy.persistence/src/main/java/net/wotonomy/control/EODatabaseDataSource.java
new file mode 100644
index 0000000..2e350f1
--- /dev/null
+++ b/projects/net.wotonomy.persistence/src/main/java/net/wotonomy/control/EODatabaseDataSource.java
@@ -0,0 +1,339 @@
+/*
+Wotonomy: OpenStep design patterns for pure Java applications.
+Copyright (C) 2001 Michael Powers
+
+This library is free software; you can redistribute it and/or
+modify it under the terms of the GNU Lesser General Public
+License as published by the Free Software Foundation; either
+version 2.1 of the License, or (at your option) any later version.
+
+This library is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+Lesser General Public License for more details.
+
+You should have received a copy of the GNU Lesser General Public
+License along with this library; if not, see http://www.gnu.org
+*/
+
+package net.wotonomy.control;
+
+import java.util.Collection;
+import java.util.Map;
+
+import net.wotonomy.foundation.NSArray;
+import net.wotonomy.foundation.NSDictionary;
+import net.wotonomy.foundation.NSMutableArray;
+import net.wotonomy.foundation.NSSet;
+import net.wotonomy.foundation.internal.WotonomyException;
+
+/**
+* EODatabaseSource is a general-purpose implementation
+* of EODataSource that is EOClassDescription-aware and
+* that can vend appropriate EODetailDataSources.
+*
+* @author michael@mpowers.net
+* @author $Author: cgruber $
+* @version $Revision: 894 $
+*/
+public abstract class EODatabaseDataSource
+{
+ EOQualifier auxiliaryQualifier;
+ EOEditingContext editingContext;
+ String entityName;
+ String fetchSpecificationName;
+ EOFetchSpecification fetchSpecification;
+ NSDictionary qualifierBindings;
+ EOClassDescription classDescription;
+ boolean fetchEnabled;
+
+ /**
+ * Constructs a data source that fetches all objects of
+ * the specified entity type.
+ */
+ public EODatabaseDataSource(
+ EOEditingContext aContext, String anEntityName)
+ {
+ this( aContext, anEntityName, null );
+ }
+
+ /**
+ * Constructs a data source that fetches objects of the
+ * specified entity type according to the fetch specification
+ * with the specified name.
+ */
+ public EODatabaseDataSource(
+ EOEditingContext aContext, String anEntityName, String aFetchSpecName)
+ {
+ fetchEnabled = true;
+ editingContext = aContext;
+ entityName = anEntityName;
+ setFetchSpecificationByName( fetchSpecificationName );
+ }
+
+ /**
+ * Returns the qualifier that is applied to the results fetched by the fetch
+ * specification before objects are returned by fetch objects, or null if no
+ * such qualifier has been specified.
+ */
+ public EOQualifier auxiliaryQualifier()
+ {
+ return auxiliaryQualifier;
+ }
+
+ /**
+ * Returns the description of the class of the
+ * objects that is vended by this data source,
+ * or null if no entity name is specified.
+ */
+ public EOClassDescription classDescriptionForObjects ()
+ {
+ if ( entityName == null ) return null;
+ return EOClassDescription.classDescriptionForEntityName( entityName );
+ }
+
+ /**
+ * Returns the object store at the root of the
+ * editing context's editing hierarchy.
+ */
+ public EOObjectStore databaseContext()
+ {
+ EOObjectStore store = editingContext();
+ while ( store instanceof EOEditingContext )
+ {
+ store = ((EOEditingContext)store).parentObjectStore();
+ }
+ return store;
+ }
+
+ /**
+ * Returns a detail 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 )
+ {
+ throw new WotonomyException( "Not implemented yet." );
+ }
+
+ /**
+ * Deletes the specified object from this data source.
+ * This implementation deletes the specified object from
+ * the editing context.
+ */
+ public void deleteObject ( Object anObject )
+ {
+ editingContext.deleteObject( anObject );
+ }
+
+ /**
+ * Returns the editing context for this data source,
+ * or null if no editing context was specified.
+ */
+ public EOEditingContext editingContext ()
+ {
+ return editingContext;
+ }
+
+/*
+ public EOEntity entity() {}
+*/
+
+ /**
+ * Returns a List containing the objects of the current
+ * entity type that conform to the specified fetch specification.
+ * If an auxiliary qualifier has been specified, that qualifier
+ * is applied to the objects before returning the result.
+ * If fetch is not enabled, this method returns null.
+ */
+ public NSArray fetchObjects ()
+ {
+ if ( ! isFetchEnabled() ) return null;
+ NSArray result =
+ editingContext.objectsWithFetchSpecification( fetchSpecification() );
+ if ( auxiliaryQualifier() != null )
+ {
+ result = EOQualifier.filteredArrayWithQualifier( result, auxiliaryQualifier() );
+ }
+ return result;
+ }
+
+ /**
+ * Returns the fetch specification currently used by this data
+ * source to fetch objects, or null if none is specified.
+ * If null, this fetchObjects() will return all objects of the
+ * specified entity type.
+ */
+ public EOFetchSpecification fetchSpecification()
+ {
+ return fetchSpecification;
+ }
+
+ /**
+ * Returns a copy of the fetch specification that will be used to
+ * determine fetch for this data source. If this data source has
+ * an auxiliary qualifier, that qualifier will be inserted into
+ * the returned fetch specification's qualifier.
+ */
+ public EOFetchSpecification fetchSpecificationForFetch()
+ {
+ EOFetchSpecification result = (EOFetchSpecification) fetchSpecification.clone();
+ if ( auxiliaryQualifier() != null )
+ {
+ NSMutableArray join = new NSMutableArray();
+ join.addObject( fetchSpecification.qualifier() );
+ join.addObject( auxiliaryQualifier() );
+ result.setQualifier( new EOAndQualifier( join ) );
+ }
+ return result;
+ }
+
+ /**
+ * Returns the name of the current fetch specification, or null
+ * if no name has been specified.
+ */
+ public String fetchSpecificationName()
+ {
+ return fetchSpecificationName;
+ }
+
+ /**
+ * Inserts the specified object into this data source.
+ * This implementation registers the object as an inserted
+ * object with the editing context.
+ */
+ public void insertObject ( Object anObject )
+ {
+ editingContext.insertObject( anObject );
+ }
+
+ /**
+ * Returns whether fetching is currently allowed.
+ * If false, fetchObjects() will return null.
+ * Default is true.
+ */
+ public boolean isFetchEnabled()
+ {
+ return fetchEnabled;
+ }
+
+ /**
+ * Returns a List of the union of the binding keys for the fetch spec's
+ * qualifier and the auxiliary qualifier.
+ */
+ public NSArray qualifierBindingKeys()
+ {
+ NSSet union = new NSSet();
+ if ( ( fetchSpecification != null )
+ && ( fetchSpecification.qualifier() != null ) )
+ {
+ union.addAll( fetchSpecification.qualifier().bindingKeys() );
+ }
+ if ( auxiliaryQualifier() != null )
+ {
+ union.addAll( auxiliaryQualifier().bindingKeys() );
+ }
+ return new NSArray( (Collection) union );
+ }
+
+ /**
+ * Returns a Map of the bindings that will be applied against
+ * the fetch spec's qualifier and the auxiliary qualifier,
+ * or null if no bindings exist.
+ */
+ public NSDictionary qualifierBindings()
+ {
+ if ( qualifierBindings == null ) return null;
+ return new NSDictionary( (Map) qualifierBindings );
+ }
+
+ /**
+ * 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 )
+ {
+ throw new WotonomyException( "Not implemented yet" );
+ }
+
+ /**
+ * Sets the auxiliary qualifier that will be applied to
+ * objects returned from the fetch described by the fetch specification.
+ */
+ public void setAuxiliaryQualifier(EOQualifier aQualifier)
+ {
+ auxiliaryQualifier = aQualifier;
+ }
+
+ /**
+ * Sets whether fetches are currently allowed.
+ * If false, fetchObjects() will return null.
+ */
+ public void setFetchEnabled(boolean isFetchEnabled)
+ {
+ fetchEnabled = isFetchEnabled;
+ }
+
+ /**
+ * Sets the fetch specification used by this data source.
+ * If null, all objects of the specified entity type will
+ * be returned by fetchObjects().
+ */
+ public void setFetchSpecification( EOFetchSpecification aFetchSpec)
+ {
+ fetchSpecificationName = null;
+ fetchSpecification = aFetchSpec;
+ }
+
+ /**
+ * Sets the fetch specification used by this data source,
+ * requesting it from the class description for this data source's
+ * entity class description, if any. If the name cannot be resolved,
+ * the fetch specification will be set to null.
+ */
+ public void setFetchSpecificationByName(String aName)
+ {
+ fetchSpecificationName = aName;
+ fetchSpecification = EOFetchSpecification.fetchSpecificationNamed( aName, entityName );
+ }
+
+ /*
+ public void setParentDataSourceRelationshipKey( EODataSource aDataSource, String aKey)
+ */
+
+ /**
+ * Sets the bindings to be applied to the fetch specification and the auxiliary qualifier.
+ */
+ public void setQualifierBindings(Map aBindingMap)
+ {
+ if ( aBindingMap == null )
+ {
+ qualifierBindings = null;
+ }
+ else
+ {
+ qualifierBindings = new NSDictionary( (Map) aBindingMap );
+ }
+ }
+}
+
+/*
+ * $Log$
+ * Revision 1.2 2006/02/16 16:47:14 cgruber
+ * Move some classes in to "internal" packages and re-work imports, etc.
+ *
+ * Also use UnsupportedOperationExceptions where appropriate, instead of WotonomyExceptions.
+ *
+ * Revision 1.1 2006/02/16 13:19:57 cgruber
+ * Check in all sources in eclipse-friendly maven-enabled packages.
+ *
+ * Revision 1.1 2001/11/24 17:38:00 mpowers
+ * Contributing EODatabaseDataSource.
+ *
+ *
+ */
+
diff --git a/projects/net.wotonomy.persistence/src/main/java/net/wotonomy/control/EODeferredFaulting.java b/projects/net.wotonomy.persistence/src/main/java/net/wotonomy/control/EODeferredFaulting.java
new file mode 100644
index 0000000..c87a097
--- /dev/null
+++ b/projects/net.wotonomy.persistence/src/main/java/net/wotonomy/control/EODeferredFaulting.java
@@ -0,0 +1,48 @@
+/*
+Wotonomy: OpenStep design patterns for pure Java applications.
+Copyright (C) 2001 Michael Powers
+
+This library is free software; you can redistribute it and/or
+modify it under the terms of the GNU Lesser General Public
+License as published by the Free Software Foundation; either
+version 2.1 of the License, or (at your option) any later version.
+
+This library is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+Lesser General Public License for more details.
+
+You should have received a copy of the GNU Lesser General Public
+License along with this library; if not, see http://www.gnu.org
+*/
+
+package net.wotonomy.control;
+
+/**
+* EODeferredFaulting defines a method
+* to handle relationships that are deferred faults.
+*
+* @author michael@mpowers.net
+* @author $Author: cgruber $
+* @version $Revision: 893 $
+*/
+public interface EODeferredFaulting extends EOFaulting
+{
+ /**
+ * Returns a fault for the specified deferred fault.
+ */
+ Object willReadRelationship( Object anObject );
+}
+
+/*
+ * $Log$
+ * Revision 1.1 2006/02/16 13:19:57 cgruber
+ * Check in all sources in eclipse-friendly maven-enabled packages.
+ *
+ * Revision 1.1 2001/11/13 04:13:59 mpowers
+ * Added interfaces needed to begin work on EOCustomObject.
+ *
+ *
+ */
+
+
diff --git a/projects/net.wotonomy.persistence/src/main/java/net/wotonomy/control/EODelayedObserver.java b/projects/net.wotonomy.persistence/src/main/java/net/wotonomy/control/EODelayedObserver.java
new file mode 100644
index 0000000..758fb40
--- /dev/null
+++ b/projects/net.wotonomy.persistence/src/main/java/net/wotonomy/control/EODelayedObserver.java
@@ -0,0 +1,152 @@
+/*
+Wotonomy: OpenStep design patterns for pure Java applications.
+Copyright (C) 2000 Intersect Software Corporation
+
+This library is free software; you can redistribute it and/or
+modify it under the terms of the GNU Lesser General Public
+License as published by the Free Software Foundation; either
+version 2.1 of the License, or (at your option) any later version.
+
+This library is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+Lesser General Public License for more details.
+
+You should have received a copy of the GNU Lesser General Public
+License along with this library; if not, see http://www.gnu.org
+*/
+
+package net.wotonomy.control;
+
+import java.util.Observable;
+import java.util.Observer;
+
+/**
+* This is an abstract class for receiving coalesced
+* notifications of changes from objects.
+* This class also implements Observer for greater
+* compatibility.
+* The point of EODelayedObservers is that when
+* they receive a willChange message, they
+* queue themselves with a EODelayedObserverQueue
+* so they can receive a single subjectChanged()
+* after all changes from an observed object take
+* place.
+*
+* @author michael@mpowers.net
+* @author $Author: cgruber $
+* @version $Revision: 894 $
+*/
+public abstract class EODelayedObserver
+ implements EOObserving, Observer
+{
+ /**
+ * Notified immediately.
+ */
+ public static final int ObserverPriorityImmediate = 0;
+ public static final int ObserverPriorityFirst = 1;
+ public static final int ObserverPrioritySecond = 2;
+ public static final int ObserverPriorityThird = 3;
+ public static final int ObserverPriorityFourth = 4;
+ public static final int ObserverPriorityFifth = 5;
+ public static final int ObserverPrioritySixth = 6;
+ public static final int ObserverPriorityLater = 7;
+ public static final int ObserverNumberOfPriorities = 8;
+
+ /**
+ * Default constructor.
+ */
+ public EODelayedObserver ()
+ {
+ }
+
+ /**
+ * Removes this observer from the observer queue
+ * for a currently pending notification.
+ */
+ public void discardPendingNotification ()
+ {
+ observerQueue().dequeueObserver( this );
+ }
+
+ /**
+ * Returns the observer queue to which this observer
+ * belongs. This implementation returns the default
+ * EODelayedObserverQueue.
+ * Override to use a different one.
+ */
+ public EODelayedObserverQueue observerQueue ()
+ {
+ return EODelayedObserverQueue.defaultObserverQueue();
+ }
+
+ /**
+ * Returns the priority of this observer in the queue.
+ * This implementation returns ObserverPriorityThird.
+ * Override to be notified before other observers.
+ */
+ public int priority ()
+ {
+ return ObserverPriorityThird;
+ }
+
+ /**
+ * Notifies observer that one or more objects that
+ * it is observing have changed. The observer should
+ * check all objects it is observing for changes.
+ */
+ public abstract void subjectChanged ();
+
+ // interface EOObserving
+
+ /**
+ * Called when the specified object is about to change.
+ * This implementation puts this observer on a
+ * notification queue.
+ */
+ public void objectWillChange ( Object anObject )
+ {
+ observerQueue().enqueueObserver( this );
+ }
+
+ // interface Observer
+
+ /**
+ * Called when the specified object has changed,
+ * with the specified argument.
+ * This method is included for interacting with
+ * the java.lang.Observer pattern.
+ * This implementation simply objectWillChange(anObject)
+ * so that the observer still gets a single subjectChanged
+ * call in response to multiple changes.
+ */
+ public void update ( Observable anObject, Object aValue )
+ {
+ objectWillChange( anObject );
+ }
+
+}
+
+/*
+ * $Log$
+ * Revision 1.2 2006/02/16 16:47:14 cgruber
+ * Move some classes in to "internal" packages and re-work imports, etc.
+ *
+ * Also use UnsupportedOperationExceptions where appropriate, instead of WotonomyExceptions.
+ *
+ * Revision 1.1 2006/02/16 13:19:57 cgruber
+ * Check in all sources in eclipse-friendly maven-enabled packages.
+ *
+ * Revision 1.2 2001/10/26 18:38:10 mpowers
+ * Reordered priorities.
+ *
+ * Revision 1.1.1.1 2000/12/21 15:46:38 mpowers
+ * Contributing wotonomy.
+ *
+ * Revision 1.3 2000/12/20 16:25:35 michael
+ * Added log to all files.
+ *
+ *
+ */
+
+
diff --git a/projects/net.wotonomy.persistence/src/main/java/net/wotonomy/control/EODelayedObserverQueue.java b/projects/net.wotonomy.persistence/src/main/java/net/wotonomy/control/EODelayedObserverQueue.java
new file mode 100644
index 0000000..6b9b9c3
--- /dev/null
+++ b/projects/net.wotonomy.persistence/src/main/java/net/wotonomy/control/EODelayedObserverQueue.java
@@ -0,0 +1,323 @@
+/*
+Wotonomy: OpenStep design patterns for pure Java applications.
+Copyright (C) 2000 Intersect Software Corporation
+
+This library is free software; you can redistribute it and/or
+modify it under the terms of the GNU Lesser General Public
+License as published by the Free Software Foundation; either
+version 2.1 of the License, or (at your option) any later version.
+
+This library is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+Lesser General Public License for more details.
+
+You should have received a copy of the GNU Lesser General Public
+License along with this library; if not, see http://www.gnu.org
+*/
+
+package net.wotonomy.control;
+
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+
+import net.wotonomy.foundation.NSRunLoop;
+import net.wotonomy.foundation.NSSelector;
+
+/**
+* EODelayedObserverQueue allows EODelayedObservers
+* to receive only one subjectChanged() message
+* after numerous willChange() messages have
+* been sent. Observers are then notified in order
+* of their priority property,
+* so that certain observers can be notified before
+* others for whatever application-specific purpose.
+* This class is not thread-safe and should be used
+* only for single-threaded GUI clients (AWT and Swing).
+* <br><br>
+*
+* Important note: because AWT's event queue does
+* not allow for priority-based scheduling, this
+* class installs a custom event queue, replacing
+* the existing queue on the AWT dispatch thread.
+* We know of no way around this problem.
+* <br><br>
+*
+* Implementation note: this queue relies on the
+* result of equals() for maintaining a set of
+* objects on the queue. If two EODelayedObservers
+* evaluate to the same value using equals(), only
+* one of them will exist on the queue. If this,
+* starts to suck, we can change it.
+*
+* @author michael@mpowers.net
+* @author $Author: cgruber $
+* @version $Revision: 894 $
+*/
+
+public class EODelayedObserverQueue
+{
+ /**
+ * The default run loop ordering flushes the delayed observers
+ * up to ObserverPrioritySixth before dispatching the AWT event
+ * queue. ObserverPriorityLater is run last.
+ */
+ public static int FlushDelayedObserversRunLoopOrdering = 400000;
+
+ private static EODelayedObserverQueue
+ defaultObserverQueue = null;
+
+ private static NSSelector runLaterSelector =
+ new NSSelector( "flushObserverQueue",
+ new Class[] { Object.class } );
+
+ private boolean willRunLater;
+ private LinkedList priorityQueue;
+
+ /**
+ * Default constructor.
+ */
+ public EODelayedObserverQueue ()
+ {
+ willRunLater = false;
+ priorityQueue = new LinkedList();
+ }
+
+ /**
+ * Returns the system default observer queue.
+ */
+ public static EODelayedObserverQueue defaultObserverQueue ()
+ {
+ if ( defaultObserverQueue == null )
+ {
+ defaultObserverQueue = new EODelayedObserverQueue();
+ }
+ return defaultObserverQueue;
+ }
+
+ /**
+ * Removes the specified observer from the queue.
+ */
+ public void dequeueObserver (
+ EODelayedObserver anObserver )
+ {
+//System.out.println( "dequeueObserver: " + anObserver );
+ //synchronized ( priorityQueue )
+ //{
+ priorityQueue.remove( anObserver );
+ //}
+ }
+
+ /**
+ * Adds the specified observer to the queue.
+ * An already enqueued observer will not be
+ * added again.
+ * If the observer's priority is
+ * ObserverPriorityImmediate, it will be
+ * notified immediately and not added to the
+ * queue.
+ * Otherwise, the queue sets itself up to
+ * call notifyObserversUpToPriority during the
+ * run loop as specified by
+ * FlushDelayedObserversRunLoopOrdering.
+ */
+ public void enqueueObserver (
+ EODelayedObserver anObserver )
+ {
+ // syntactic glue for Runnables
+ final EODelayedObserver observer = anObserver;
+
+ if ( observer.priority() ==
+ EODelayedObserver.ObserverPriorityImmediate )
+ {
+ // invoke immediately
+ observer.subjectChanged();
+ }
+ else
+ {
+ // place in the delayed observer queue
+
+ //synchronized ( priorityQueue )
+ //{
+ int i = 0;
+ int priority = observer.priority();
+ Object o;
+
+ Iterator iterator = priorityQueue.iterator();
+
+ // scan entire list to ensure we're not already queued
+ while ( iterator.hasNext() )
+ {
+ o = iterator.next();
+ if ( o == observer )
+ {
+ // already queued
+ return;
+ }
+ if ( ((EODelayedObserver)o).priority() > priority )
+ {
+ // insert at this index: break now
+ break;
+ }
+ i++;
+ }
+
+ // if we broke early, we found a threshhold:
+ // continue scanning to ensure we're not already queued
+ while ( iterator.hasNext() )
+ {
+ if ( iterator.next() == observer )
+ {
+ // already queued
+ return;
+ }
+ }
+
+ // insert before items of lower priority,
+ // otherwise insert at end of list.
+ priorityQueue.add( i, observer );
+
+ //}
+ runLater();
+ }
+//System.out.println( "enqueueObserver: " + anObserver + " : " + priorityQueue );
+ }
+
+ /**
+ * Notifies all observers with priority equal to
+ * or greater than the specified priority.
+ */
+ public void notifyObserversUpToPriority ( int priority )
+ {
+//System.out.println( "notifyObserversUpToPriority: priorityQueue size = " + priorityQueue.size() );
+ EODelayedObserver o;
+ while ( ! priorityQueue.isEmpty() )
+ {
+ o = (EODelayedObserver) priorityQueue.getFirst();
+ if ( o.priority() > priority ) break;
+ priorityQueue.removeFirst();
+
+ try
+ {
+ o.subjectChanged();
+ }
+ catch ( Exception exc )
+ {
+ System.out.println( "Error notifying observer: " + o );
+ exc.printStackTrace();
+ }
+ }
+ }
+
+ /**
+ * Called to ensure that notifyObserversUpToPriority
+ * will be called on the next event loop.
+ */
+ private void runLater()
+ {
+ if ( ! willRunLater )
+ {
+ willRunLater = true;
+ NSRunLoop.currentRunLoop().performSelectorWithOrder(
+ runLaterSelector, this, null, FlushDelayedObserversRunLoopOrdering, null );
+ }
+ }
+
+ /**
+ * This method is called by the event queue run loop
+ * and calls notifyObserversUpToPriority with
+ * ObserverPriorityLater.
+ * NOTE: This method is not part of the specification.
+ */
+ public void flushObserverQueue( Object anObject )
+ {
+//System.out.println( "EODelayedObserverQueue: running" );
+ notifyObserversUpToPriority( EODelayedObserver.ObserverPrioritySixth );
+ if ( ! priorityQueue.isEmpty() )
+ {
+ // assumes all remaining on queue are ObserverPriorityLater
+ NSRunLoop.invokeLater(
+ new PriorityLaterRunnable( new LinkedList( priorityQueue ) ) );
+ priorityQueue.clear();
+ }
+ willRunLater = false;
+ }
+
+ /**
+ * A runnable for dispatching remaining observers running at ObserverPriorityLater.
+ */
+ class PriorityLaterRunnable implements Runnable
+ {
+ List observers;
+
+ public PriorityLaterRunnable( List anObserverList )
+ {
+ observers = anObserverList;
+ }
+
+ public void run()
+ {
+ EODelayedObserver o = null;
+ Iterator i = observers.iterator();
+ while ( i.hasNext() )
+ {
+ try
+ {
+ o = (EODelayedObserver) i.next();
+ o.subjectChanged();
+ }
+ catch ( Exception exc )
+ {
+ System.out.println( "Error notifying observer: " + o );
+ exc.printStackTrace();
+ }
+ }
+ }
+ }
+
+}
+
+/*
+ * $Log$
+ * Revision 1.2 2006/02/16 16:47:14 cgruber
+ * Move some classes in to "internal" packages and re-work imports, etc.
+ *
+ * Also use UnsupportedOperationExceptions where appropriate, instead of WotonomyExceptions.
+ *
+ * Revision 1.1 2006/02/16 13:19:57 cgruber
+ * Check in all sources in eclipse-friendly maven-enabled packages.
+ *
+ * Revision 1.8 2003/08/19 01:53:12 chochos
+ * EOObjectStore had some incompatible return types (Object instead of EOEnterpriseObject, in fault methods mostly). It's internally consistent but I hope it doesn't break anything based on this, even though fault methods mostly throw exceptions for now.
+ *
+ * Revision 1.7 2002/05/20 15:08:35 mpowers
+ * Optimization for enqueueObserver: we were scanning the entire list anyway;
+ * now we compare priorities and ensure we're not double-queued on same pass.
+ *
+ * Revision 1.6 2002/05/15 13:45:57 mpowers
+ * RunLater now appropriately runs later: at the end of the current awt queue.
+ *
+ * Revision 1.5 2002/03/11 03:18:39 mpowers
+ * Now properly handling ObserverChangesLater.
+ *
+ * Revision 1.4 2001/10/26 18:37:15 mpowers
+ * Now using NSRunLoop instead of AWT EventQueue.
+ *
+ * Revision 1.3 2001/10/22 21:54:16 mpowers
+ * Removed swing dependency in favor of jdk1.3 event queue.
+ * Optimized priority queue population.
+ *
+ * Revision 1.2 2001/10/12 18:01:59 mpowers
+ * Now catching exceptions before they disrupt the awt event queue.
+ *
+ * Revision 1.1.1.1 2000/12/21 15:46:42 mpowers
+ * Contributing wotonomy.
+ *
+ * Revision 1.5 2000/12/20 16:25:35 michael
+ * Added log to all files.
+ *
+ *
+ */
+
+
diff --git a/projects/net.wotonomy.persistence/src/main/java/net/wotonomy/control/EOEditingContext.java b/projects/net.wotonomy.persistence/src/main/java/net/wotonomy/control/EOEditingContext.java
new file mode 100644
index 0000000..b01727d
--- /dev/null
+++ b/projects/net.wotonomy.persistence/src/main/java/net/wotonomy/control/EOEditingContext.java
@@ -0,0 +1,3247 @@
+/*
+Wotonomy: OpenStep design patterns for pure Java applications.
+Copyright (C) 2001 Michael Powers
+
+This library is free software; you can redistribute it and/or
+modify it under the terms of the GNU Lesser General Public
+License as published by the Free Software Foundation; either
+version 2.1 of the License, or (at your option) any later version.
+
+This library is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+Lesser General Public License for more details.
+
+You should have received a copy of the GNU Lesser General Public
+License along with this library; if not, see http://www.gnu.org
+*/
+
+package net.wotonomy.control;
+
+import java.lang.ref.WeakReference;
+import java.util.Collection;
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import net.wotonomy.foundation.NSArray;
+import net.wotonomy.foundation.NSDictionary;
+import net.wotonomy.foundation.NSMutableArray;
+import net.wotonomy.foundation.NSMutableDictionary;
+import net.wotonomy.foundation.NSNotification;
+import net.wotonomy.foundation.NSNotificationCenter;
+import net.wotonomy.foundation.NSRunLoop;
+import net.wotonomy.foundation.NSSelector;
+import net.wotonomy.foundation.internal.WotonomyException;
+
+// swing dependency for undo manager
+//import javax.swing.undo.UndoManager;
+
+/**
+* EOEditingContext provides transactional support for
+* fetching, editing, and committing changes made on a
+* collection of objects to a parent object store. <br><br>
+*
+* EOEditingContext is itself a subclass of EOObjectStore,
+* and this means that EOEditingContexts can use other
+* EOEditingContexts as their parent. However, there
+* still must exist an EOObjectStore as the root of the
+* editing hierarchy that can maintain persistent state.
+*
+* @author michael@mpowers.net
+* @author $Author: cgruber $
+* @version $Revision: 894 $
+*/
+public class EOEditingContext
+ extends EOObjectStore
+ implements EOObserving
+{
+ /**
+ * Key for the NSNotification posted after this editing context
+ * saves changes. Object of the notification will be this editing
+ * context, and user info will contain InsertedKey, UpdatedKey,
+ * and DeletedKey (keys are defined in EOObjectStore).
+ */
+ public static final String
+ EditingContextDidSaveChangesNotification =
+ "EOEditingContextDidSaveChangesNotification";
+
+ /**
+ * Key for the NSNotification posted after this editing context
+ * observes changes. Object of the notification will be this editing
+ * context, and user info will contain InsertedKey, UpdatedKey, InvalidatedKey,
+ * and DeletedKey (keys are defined in EOObjectStore), however
+ * the objects in the corresponding Lists will be the actual
+ * objects, not their ids.
+ */
+ public static final String
+ ObjectsChangedInEditingContextNotification =
+ "EOObjectsChangedInEditingContextNotification";
+
+ /**
+ * The default run loop ordering processes recent changes
+ * before delayed observers are notified and before dispatching
+ * the AWT event queue.
+ */
+ public static int
+ EditingContextFlushChangesRunLoopOrdering = 300000;
+
+ private static NSSelector runLaterSelector =
+ new NSSelector( "flushRecentChanges",
+ new Class[] { Object.class } );
+
+ private static EOObjectStore defaultParentObjectStore = null;
+ private static double defaultFetchTimestampLag = 0;
+ private static boolean retainsRegisteredObjects = true;
+
+ private EOObjectStore parentStore;
+ private WeakReference delegate;
+ private WeakReference messageHandler;
+ private List editorSet;
+ private double fetchTimestamp;
+ private boolean lockBeforeModify;
+ private boolean propagateDeletesAfterEvent;
+ private boolean stopValidationAfterError;
+ private NSMutableArray insertedObjects;
+ private NSMutableArray insertedObjectsBuffer;
+ private NSArray insertedObjectsProxy;
+ private NSMutableArray updatedObjects;
+ private NSMutableArray updatedObjectsBuffer;
+ private NSArray updatedObjectsProxy;
+ private NSMutableArray deletedObjects;
+ private NSMutableArray deletedObjectsBuffer;
+ private NSArray deletedObjectsProxy;
+ private NSMutableArray deletedIDsBuffer;
+ private NSMutableArray invalidatedObjectsBuffer;
+ private NSMutableArray invalidatedIDsBuffer;
+ private Registrar registrar;
+// private UndoManager undoManager;
+
+ // so we don't have to trouble EOObserverCenter
+ private boolean ignoreChanges;
+
+ // for delayed handling of processRecentChanges
+ private boolean willRunLater;
+
+ // for handling of notifications posted
+ // while we're in the saveChanges method
+ private boolean isInvalidating;
+
+ // for i18n or other customization
+ static protected String MessageChangeConflict =
+ "Another user changed an object you are editing: ";
+
+ /**
+ * Default constructor creates a new editing context
+ * that uses the default object store. If the default
+ * object store has not been set, an exception is thrown.
+ */
+ public EOEditingContext()
+ {
+ this( defaultParentObjectStore() );
+ }
+
+ /**
+ * Creates a new editing context that uses the specified
+ * object store as its parent object store.
+ */
+ public EOEditingContext( EOObjectStore anObjectStore )
+ {
+ if ( anObjectStore == null )
+ {
+ throw new IllegalArgumentException(
+ "A parent object store must be specified." );
+ }
+
+ parentStore = anObjectStore;
+ delegate = null;
+ messageHandler = null;
+ editorSet = new LinkedList();
+ fetchTimestamp = 0;
+ lockBeforeModify = false;
+ propagateDeletesAfterEvent = true;
+ stopValidationAfterError = true;
+ insertedObjects = new NSMutableArray();
+ insertedObjectsBuffer = new NSMutableArray();
+ insertedObjectsProxy = NSArray.arrayBackedByList( insertedObjects );
+ updatedObjects = new NSMutableArray();
+ updatedObjectsBuffer = new NSMutableArray();
+ updatedObjectsProxy = NSArray.arrayBackedByList( updatedObjects );
+ deletedObjects = new NSMutableArray();
+ deletedObjectsBuffer = new NSMutableArray();
+ deletedObjectsProxy = NSArray.arrayBackedByList( deletedObjects );
+ deletedIDsBuffer = new NSMutableArray();
+ invalidatedObjectsBuffer = new NSMutableArray();
+ invalidatedIDsBuffer = new NSMutableArray();
+
+ if ( instancesRetainRegisteredObjects() )
+ {
+ registrar = new Registrar( this );
+ }
+ else
+ {
+ registrar = new WeakRegistrar( this );
+ }
+
+ ignoreChanges = false;
+ willRunLater = false;
+ isInvalidating = false;
+
+ // create undo manager
+ //TODO: this should be NSUndoManager
+// undoManager = new UndoManager();
+
+ // register for notifications
+ NSSelector handleNotification =
+ new NSSelector( "handleNotification",
+ new Class[] { NSNotification.class } );
+ // any from parent store
+ NSNotificationCenter.defaultCenter().addObserver(
+ this, handleNotification, null, parentStore );
+ // global id change from any
+ NSNotificationCenter.defaultCenter().addObserver(
+ this, handleNotification, EOGlobalID.GlobalIDChangedNotification, null );
+//new net.wotonomy.ui.swing.NotificationInspector( null, parentStore );
+ }
+
+ /**
+ * Registers the specified object as an editor for this
+ * context. The object is expected to implement
+ * EOEditingContext.Editor.
+ */
+ public void addEditor ( Object anEditor )
+ {
+ if ( anEditor == null ) return;
+ editorSet.add( new WeakReference( anEditor ) );
+ }
+
+ /**
+ * Returns a read-only List of objects associated with the object
+ * with the specified id for the specified property
+ * relationship, or may return a placeholder array that
+ * will defer the fetch until needed (aka an array fault).
+ * All objects must be registered in the specified editing context.
+ * This implementation calls to its parent object store's
+ * implementation if the requested source object is not
+ * registered in this editing context.
+ * The specified relationship key must produce a result of
+ * type Collection for the source object or an exception is thrown.
+ */
+ public NSArray arrayFaultWithSourceGlobalID (
+ EOGlobalID aGlobalID,
+ String aRelationshipKey,
+ EOEditingContext aContext )
+ {
+ NSArray result = null;
+ Object source = registrar.objectForGlobalID( aGlobalID );
+
+ // if not registered in our context
+ if ( source == null )
+ {
+ // get the object registered into our context
+ result = parentStore.arrayFaultWithSourceGlobalID(
+ aGlobalID, aRelationshipKey, this );
+ }
+ else // source is registered in our context
+ {
+ // get existing value
+ Object value;
+ if ( source instanceof EOKeyValueCoding )
+ {
+ value = ((EOKeyValueCoding)source).storedValueForKey(
+ aRelationshipKey );
+ }
+ else // handle directly
+ {
+ value = EOKeyValueCodingSupport.storedValueForKey(
+ source, aRelationshipKey );
+ }
+
+ if ( value == null )
+ {
+ // do the same as if the source was null
+ result = parentStore.arrayFaultWithSourceGlobalID(
+ aGlobalID, aRelationshipKey, this );
+ }
+ else
+ if ( value instanceof NSArray )
+ {
+ result = (NSArray) value;
+ }
+ else // not NSArray
+ if ( value instanceof Collection )
+ {
+ // convert to NSArray
+ result = new NSArray( (Collection) value );
+ }
+ else
+ {
+ throw new WotonomyException(
+ "Relationship key did not return a collection: "
+ + aGlobalID + " : " + aRelationshipKey );
+ }
+ }
+
+ // if our context is not the specified context
+ if ( aContext != this )
+ {
+ result = (NSArray) clone( this, result, aContext );
+ }
+
+ return result;
+ }
+
+ /**
+ * Returns a snapshot of the specified object as it
+ * existed when it was last read or committed to the
+ * parent object store.
+ */
+ public NSDictionary committedSnapshotForObject (
+ Object anObject )
+ {
+ byte[] snapshot = (byte[])
+ registrar.getCommitSnapshot( anObject );
+ if ( snapshot == null )
+ {
+ // this object not modified: take a current snapshot
+ snapshot = takeSnapshot( anObject );
+ }
+ return convertSnapshotToDictionary( snapshot );
+ }
+
+ /**
+ * Returns a snapshot of the specified object as it
+ * existed before the edits triggered by the current
+ * event loop were processed.
+ */
+ public NSDictionary currentEventSnapshotForObject (
+ Object anObject )
+ {
+ byte[] result = (byte[])
+ registrar.getCurrentSnapshot( anObject );
+ if ( result == null )
+ {
+ return committedSnapshotForObject( anObject );
+ }
+ return convertSnapshotToDictionary( result );
+ }
+
+ /**
+ * Returns the delegate for this editing context,
+ * or null if no delegate has been set.
+ */
+ public Object delegate ()
+ {
+ if ( delegate == null ) return null;
+ return delegate.get();
+ }
+
+ /**
+ * Deletes the specified object from this editing context.
+ * The editing context marks the object as deleted and
+ * will notify the parent store when changes are committed.
+ */
+ public void deleteObject (
+ Object anObject )
+ {
+ willChange();
+
+ int i;
+ // remove from added objects if necessary
+ i = insertedObjects.indexOfIdenticalObject( anObject );
+ if ( i != NSArray.NotFound )
+ {
+ insertedObjects.removeObjectAtIndex( i );
+
+ // if in the inserted objects buffer
+ int index = insertedObjectsBuffer.indexOfIdenticalObject( anObject );
+ if ( index != NSArray.NotFound )
+ {
+ // remove from inserted objects buffer
+ insertedObjectsBuffer.removeObjectAtIndex( index );
+ }
+
+ // now forget the object ever existed.
+ forgetObject( anObject );
+
+ // we're done
+ return;
+ }
+ else // otherwise add to deleted objects list
+ {
+ deletedObjects.addObject( anObject );
+ }
+
+ // remove from updated objects if necessary
+ i = updatedObjects.indexOfIdenticalObject( anObject );
+ if ( i != NSArray.NotFound )
+ {
+ updatedObjects.removeObjectAtIndex( i );
+ }
+
+ // add to buffer
+ deletedObjectsBuffer.addObject( anObject );
+ deletedIDsBuffer.addObject( globalIDForObject( anObject ) );
+ }
+
+ /**
+ * Returns a read-only List of all objects marked as deleted
+ * in this editing context.
+ */
+ public NSArray deletedObjects ()
+ {
+ return deletedObjectsProxy;
+ }
+
+ /**
+ * Called by child editing contexts when they no longer
+ * need to track the specified id.
+ * This implementation forwards the call to the parent store.
+ */
+ public void editingContextDidForgetObjectWithGlobalID (
+ EOEditingContext aContext,
+ EOGlobalID aGlobalID )
+ {
+ parentStore.editingContextDidForgetObjectWithGlobalID(
+ aContext, aGlobalID );
+ }
+
+ /**
+ * Returns a read-only List of registered editors of this
+ * editing context.
+ */
+ public NSArray editors ()
+ {
+ NSMutableArray result = new NSMutableArray();
+ Object o;
+ Iterator i = editorSet.iterator();
+ while ( i.hasNext() )
+ {
+ o = ((WeakReference)i.next()).get();
+ if ( o != null )
+ {
+ result.addObject( o );
+ }
+ else
+ {
+ i.remove();
+ }
+ }
+ return result;
+ }
+
+/*
+ public static void encodeObjectWithCoder (
+ Object anObject,
+ NSCoder aCoder )
+ {
+ throw new net.wotonomy.util.WotonomyException("Not implemented yet.");
+ }
+*/
+
+ /**
+ * Returns the object for the specified id.
+ * If the object is registered in in this context
+ * but not in the specified context,
+ * this implementation will create a copy of the object
+ * and register it in the specified context.
+ * Otherwise it will forward the call to the parent
+ * object store.
+ */
+ public /*EOEnterpriseObject*/ Object faultForGlobalID (
+ EOGlobalID aGlobalID,
+ EOEditingContext aContext )
+ {
+ Object result = registrar.objectForGlobalID( aGlobalID );
+
+ // if not registered in our context
+ if ( result == null )
+ {
+ // get the object registered into our context
+ result = parentStore.faultForGlobalID( aGlobalID, this );
+ }
+
+ // if our context is not the specified context
+ if ( aContext != this )
+ {
+ result = registerClone( aGlobalID, this, result, aContext );
+ }
+
+ return result;
+ }
+
+ /**
+ * Returns a fault representing an object of
+ * the specified entity type with values from
+ * the specified dictionary.
+ * This implementation calls faultForRawRow
+ * on the parent store.
+ */
+ public Object faultForRawRow (
+ Map aDictionary,
+ String anEntityName )
+ {
+ return parentStore.faultForRawRow(
+ aDictionary, anEntityName, this );
+ }
+
+ /**
+ * 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.
+ * This implementation forwards the call to
+ * the parent store.
+ */
+ public /*EOEnterpriseObject*/ Object faultForRawRow (
+ Map aDictionary,
+ String anEntityName,
+ EOEditingContext aContext )
+ {
+ return parentStore.faultForRawRow(
+ aDictionary, anEntityName, aContext );
+ }
+
+ /**
+ * Returns the fetch timestamp for this editing context.
+ */
+ public double fetchTimestamp ()
+ {
+ return fetchTimestamp;
+ }
+
+ /**
+ * Unregisters the specified object from this editing context,
+ * removing all references to it. Use this method to remove
+ * an object from the context without marking it for deletion.
+ */
+ public void forgetObject (
+ Object anObject )
+ {
+ EOGlobalID id = registrar.globalIDForObject( anObject );
+ if ( id == null )
+ {
+ System.err.println(
+ "EOEditingContext.forgetObject: not registered: " + anObject );
+ return;
+ }
+
+ // unregister object
+ registrar.forgetObject( anObject );
+
+ // remove from all, inserted, updated, and deleted lists
+ int index;
+ index = updatedObjects.indexOfIdenticalObject( anObject );
+ if ( index != NSArray.NotFound )
+ {
+ updatedObjects.removeObjectAtIndex( index );
+ }
+ index = insertedObjects.indexOfIdenticalObject( anObject );
+ if ( index != NSArray.NotFound )
+ {
+ insertedObjects.removeObjectAtIndex( index );
+ return;
+ }
+ index = deletedObjects.indexOfIdenticalObject( anObject );
+ if ( index != NSArray.NotFound )
+ {
+ deletedObjects.removeObjectAtIndex( index );
+ return;
+ }
+
+ // notify parent context
+ parentStore.editingContextDidForgetObjectWithGlobalID( this, id );
+ }
+
+ /**
+ * Returns the id for the specified object, or null
+ * if the object is not registered in this context.
+ */
+ public EOGlobalID globalIDForObject (
+ Object anObject )
+ {
+ return registrar.globalIDForObject( anObject );
+ }
+
+ /**
+ * Returns an array of ids for an array of objects.
+ */
+ private NSArray globalIDsForObjects(
+ List anObjectList )
+ {
+ NSMutableArray result = new NSMutableArray();
+ Iterator it = anObjectList.iterator();
+ while ( it.hasNext() )
+ {
+ result.add( globalIDForObject( it.next() ) );
+ }
+ return result;
+ }
+
+ /**
+ * Returns whether this editing context has changes that
+ * have not yet been committed to the parent object store.
+ */
+ public boolean hasChanges ()
+ {
+ if ( updatedObjects.count() > 0 ) return true;
+ if ( insertedObjects.count() > 0 ) return true;
+ if ( deletedObjects.count() > 0 ) return true;
+ return false;
+ }
+
+ /**
+ * Given a newly instantiated object, this method
+ * initializes its properties to values appropriate
+ * for the specified id. The object should already
+ * belong to the specified editing context.
+ * This method is called to populate faults.
+ * This implementation will try to apply the values
+ * from an object with a matching id in this editing
+ * context if possible, calling to the parent object
+ * store only if such an object is not found.
+ */
+ public void initializeObject (
+ /*EOEnterpriseObject*/ Object anObject,
+ EOGlobalID aGlobalID,
+ EOEditingContext aContext )
+ {
+ Object existingObject = registrar.objectForGlobalID( aGlobalID );
+
+ // if not registered in our context
+ if ( existingObject == null )
+ {
+ // get the object registered into our context
+ existingObject = parentStore.faultForGlobalID( aGlobalID, this );
+ }
+
+ if ( aContext == this )
+ {
+ // initialize the object
+ parentStore.initializeObject(
+ /*(EOEnterpriseObject)*/existingObject, aGlobalID, this );
+ }
+ else // ( aContext != this )
+ {
+ // translates child relationships
+ copy( this, existingObject, aContext, anObject );
+ }
+
+ aContext.registrar.setCommitSnapshot( anObject, null );
+ aContext.registrar.setCurrentSnapshot( anObject, null );
+ }
+
+ /**
+ * Inserts the specified object into this editing context.
+ * This implementation calls insertObjectWithGlobalID
+ * with an EOTemporaryGlobalID.
+ */
+ public void insertObject ( Object anObject )
+ {
+ insertObjectWithGlobalID(
+ anObject, new EOTemporaryGlobalID() );
+ }
+
+ /**
+ * Inserts the specified object into this editing context
+ * with the specified id, which is expected to be a
+ * temporary id.
+ */
+ public void insertObjectWithGlobalID (
+ Object anObject,
+ EOGlobalID aGlobalID )
+ {
+ willChange();
+
+ // if this object was marked for deletion
+ int index = deletedObjects.indexOfIdenticalObject( anObject );
+ if ( index != NSArray.NotFound )
+ {
+ // don't need to re-register: just update the lists
+
+ // remove from deleted list
+ deletedObjects.removeObjectAtIndex( index );
+
+ // if in the deleted ids buffer
+ index = deletedIDsBuffer.indexOfIdenticalObject( anObject );
+ if ( index != NSArray.NotFound )
+ {
+ // remove from deleted ids buffer
+ deletedIDsBuffer.removeObjectAtIndex( index );
+ }
+
+ // if in the deleted objects buffer
+ index = deletedObjectsBuffer.indexOfIdenticalObject( anObject );
+ if ( index != NSArray.NotFound )
+ {
+ // remove from deleted objects buffer
+ deletedObjectsBuffer.removeObjectAtIndex( index );
+ }
+ else // not in the deleted objects buffer
+ {
+ // add to the inserted objects buffer
+ insertedObjectsBuffer.addObject( anObject );
+ }
+
+ // we're done
+ return;
+ }
+
+ // make sure object is not already in editing context
+ if ( objectForGlobalID( aGlobalID ) != null )
+ {
+ throw new WotonomyException(
+ "Tried to insert but object was already registered:"
+ + aGlobalID );
+ }
+
+ // register object
+ recordObject( anObject, aGlobalID );
+
+ // add to inserted list
+ insertedObjects.addObject( anObject );
+ // add to buffer
+ insertedObjectsBuffer.addObject( anObject );
+ }
+
+ /**
+ * Returns a read-only List of the objects that have been
+ * inserted into this editing context.
+ */
+ public NSArray insertedObjects ()
+ {
+ return insertedObjectsProxy;
+ }
+
+ /**
+ * Turn all objects in this editing context into faults,
+ * so that they will be fetched the next time they are
+ * accessed, and calls invalidateObjectsWithGlobalIDs
+ * on the parent object store.
+ */
+ public void invalidateAllObjects ()
+ {
+ // register change so processRecentChanges is called
+ willChange();
+
+ invalidateAllObjectsQuietly();
+
+ // post notification
+ NSNotificationCenter.defaultCenter().postNotification(
+ new NSNotification(
+ InvalidatedAllObjectsInStoreNotification, this ) );
+ }
+
+ /**
+ * Only refaults all objects, does not notify will change
+ * nor post notification, but does call parent store.
+ * Called by invalidateAllObjects() and handleNotification().
+ */
+ private void invalidateAllObjectsQuietly()
+ {
+ // remember the ids
+ NSMutableArray ids = new NSMutableArray( registrar.registeredGlobalIDs() );
+
+ // track of discarded IDs (from inserted objects)
+ NSMutableArray discardedIDs = new NSMutableArray();
+
+ // refault all objects
+ EOGlobalID id;
+ Object o;
+ Enumeration e = ids.objectEnumerator();
+ while ( e.hasMoreElements() )
+ {
+ id = (EOGlobalID) e.nextElement();
+ o = objectForGlobalID( id );
+
+ // some objects may have been manually discarded
+ if ( o != null )
+ {
+ // don't refault newly inserted objects
+ if ( insertedObjects.indexOfIdenticalObject( o ) == NSArray.NotFound )
+ {
+ refaultObject( o, id, this );
+ }
+ else
+ {
+ // discard inserted objects
+ forgetObject( o );
+ discardedIDs.add( id );
+ }
+ invalidatedObjectsBuffer.add( o );
+ }
+ invalidatedIDsBuffer.add( id );
+ }
+ ids.removeAll( discardedIDs );
+
+ // call to parent store (should call this after posting instead?)
+ isInvalidating = true;
+ parentStore.invalidateObjectsWithGlobalIDs( ids );
+ isInvalidating = false;
+ }
+
+ /**
+ * Turns the objects with the specified ids into faults,
+ * so that they will be fetched the next time they are
+ * accessed, and forwards the call to the parent object store.
+ */
+ public void invalidateObjectsWithGlobalIDs (
+ List anArray )
+ {
+ // register change so processRecentChanges is called
+ willChange();
+
+ // call to parent to invalidate objects
+ parentStore.invalidateObjectsWithGlobalIDs( anArray );
+
+ Object o;
+ EOGlobalID id;
+ Iterator it = anArray.iterator();
+ while ( it.hasNext() )
+ {
+ id = (EOGlobalID) it.next();
+ if ( id != null )
+ {
+ o = objectForGlobalID( id );
+ if ( o != null )
+ {
+ Object result = notifyDelegate(
+ "editingContextShouldInvalidateObject",
+ new Class[] { EOEditingContext.class, Object.class, EOGlobalID.class },
+ new Object[] { this, o, id } );
+ if ( ( result == null ) || ( Boolean.TRUE.equals( result ) ) )
+ {
+ // refault the object
+ refaultObject( o, id, this );
+ invalidatedObjectsBuffer.add( o );
+ invalidatedIDsBuffer.add( id );
+ }
+ }
+ }
+ else
+ {
+ throw new WotonomyException(
+ "Attempted to invalidate a null global id: " + anArray );
+ }
+ }
+
+ }
+/*
+ public boolean invalidatesObjectsWhenFinalized ( )
+ {
+ throw new net.wotonomy.util.WotonomyException("Not implemented yet.");
+ }
+
+ public boolean invalidatesObjectsWhenFreed ( )
+ {
+ throw new net.wotonomy.util.WotonomyException("Not implemented yet.");
+ }
+*/
+ /**
+ * Returns whether the object referenced by the
+ * specified id is locked.
+ * This implementation simply forwards the call to
+ * the parent object store.
+ */
+ public boolean isObjectLockedWithGlobalID (
+ EOGlobalID aGlobalID,
+ EOEditingContext aContext)
+ {
+ return parentStore.isObjectLockedWithGlobalID(
+ aGlobalID, aContext );
+ }
+
+/*
+ public void lock ()
+ {
+ throw new net.wotonomy.util.WotonomyException("Not implemented yet.");
+ }
+*/
+ /**
+ * Locks the specified object in this editing context
+ * by calling lockObjectWithGlobalID on the parent store.
+ */
+ public void lockObject (
+ Object anObject )
+ {
+ parentStore.lockObjectWithGlobalID(
+ globalIDForObject( anObject ), this );
+ }
+
+ /**
+ * Locks the object referenced by the specified id
+ * in the specified editing context.
+ * This implementation simply forwards the call to
+ * the parent object store.
+ */
+ public void lockObjectWithGlobalID (
+ EOGlobalID aGlobalID,
+ EOEditingContext aContext)
+ {
+ parentStore.lockObjectWithGlobalID(
+ aGlobalID, aContext );
+ }
+
+ /**
+ * Returns whether this editing context attempts to
+ * lock objects when they are first modified.
+ */
+ public boolean locksObjectsBeforeFirstModification ()
+ {
+ return lockBeforeModify;
+ }
+
+ /**
+ * Returns the message handler for this editing context,
+ * or null if no message handler has been set.
+ */
+ public Object messageHandler ()
+ {
+ if ( messageHandler == null ) return null;
+ return messageHandler.get();
+ }
+
+ /**
+ * Returns the object registered in this editing context
+ * for the specified id, or null if that id is not
+ * registered.
+ */
+ public Object objectForGlobalID (
+ EOGlobalID aGlobalID )
+ {
+ return registrar.objectForGlobalID( aGlobalID );
+ }
+
+ /**
+ * Returns a read-only 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 aRelationshipKey,
+ EOEditingContext aContext )
+ {
+//System.out.println( "EOEditingContext.objectsForSourceGlobalID: "
+//+ aGlobalID + " : " + aRelationshipKey );
+
+ NSArray result = null;
+
+if ( aContext == this )
+{
+ throw new WotonomyException( "Assert failed: calling objectsForSourceGlobalID on ourself." );
+}
+ Object source = registrar.objectForGlobalID( aGlobalID );
+
+ // if not registered in our context
+ if ( source == null )
+ {
+ // get the object registered into our context
+ result = parentStore.objectsForSourceGlobalID(
+ aGlobalID, aRelationshipKey, this );
+ }
+ else // source is registered in our context
+ {
+ // get existing value
+ Object value;
+ if ( source instanceof EOKeyValueCoding )
+ {
+ value = ((EOKeyValueCoding)source).storedValueForKey(
+ aRelationshipKey );
+ }
+ else // handle directly
+ {
+ value = EOKeyValueCodingSupport.storedValueForKey(
+ source, aRelationshipKey );
+ }
+
+ // if we don't have a valid value on our object
+ if ( ( value == null )
+ || ( ( value instanceof ArrayFault )
+ && ( !((ArrayFault)value).isFetched() ) ) )
+ {
+ // do the same as if the source was null
+ result = parentStore.objectsForSourceGlobalID(
+ aGlobalID, aRelationshipKey, this );
+
+ // set our value since we have it
+ if ( source instanceof EOKeyValueCoding )
+ {
+ ((EOKeyValueCoding)source).takeStoredValueForKey(
+ result, aRelationshipKey );
+ }
+ else // handle directly
+ {
+ EOKeyValueCodingSupport.takeStoredValueForKey(
+ source, result, aRelationshipKey );
+ }
+ }
+ else
+ if ( ( value instanceof ArrayFault )
+ && ( !((ArrayFault)value).isFetched() ) )
+ {
+ // do the same as if the source was null
+ result = parentStore.objectsForSourceGlobalID(
+ aGlobalID, aRelationshipKey, this );
+ }
+ else
+ if ( value instanceof NSArray )
+ {
+ result = (NSArray) value;
+ }
+ else // not NSArray
+ if ( value instanceof Collection )
+ {
+ // convert to NSArray
+ result = new NSArray( (Collection) value );
+ }
+ else
+ {
+ throw new WotonomyException(
+ "Relationship key did not return a collection: "
+ + aGlobalID + " : " + aRelationshipKey );
+ }
+ }
+
+ // if our context is not the specified context
+ if ( aContext != this )
+ {
+ result = (NSArray) clone( this, result, aContext );
+ }
+
+ return result;
+ }
+
+ /**
+ * Returns a read-only List of objects the meet the criteria of
+ * the supplied specification. This method simply calls
+ * objectsWithFetchSpecification on this editing context
+ * with this editing context as the parameter.
+ */
+ public NSArray objectsWithFetchSpecification (
+ EOFetchSpecification aFetchSpec )
+ {
+ return objectsWithFetchSpecification( aFetchSpec, this );
+ }
+
+ /**
+ * Returns a read-only List of objects the meet the criteria of
+ * the supplied specification. Faults are not allowed in the array.
+ * If any objects are already fetched, they should not be
+ * refetched. All objects should belong to the specified editing context.
+ * This implementation forwards the call to the parent object
+ * store, which will register each object in the specified editing
+ * context only if it does not already exist.
+ */
+ public NSArray objectsWithFetchSpecification (
+ EOFetchSpecification aFetchSpec,
+ EOEditingContext aContext)
+ {
+ if ( aContext == this )
+ {
+ Object result = notifyDelegate(
+ "editingContextShouldFetchObjects",
+ new Class[] { EOEditingContext.class, EOFetchSpecification.class },
+ new Object[] { aContext, aFetchSpec } );
+ if ( result instanceof NSArray ) return (NSArray) result;
+ }
+ return parentStore.objectsWithFetchSpecification( aFetchSpec, aContext );
+ }
+
+ /**
+ * Returns the parent object store for this editing context.
+ * The result will not be null.
+ */
+ public EOObjectStore parentObjectStore ()
+ {
+ return parentStore;
+ }
+
+ /**
+ * Updates the inserted, updated, and deleted objects lists,
+ * and posts notifications about which objects have been changed.
+ * This method is called at the end of an event loop in which
+ * objects were modified. This method is additionally called
+ * by saveChanges() so that any changes in the same event loop
+ * will be processed correctly before calling to the parent
+ * object store.
+ * This implementation updates those lists immediately, but
+ * only posts notifications when this method is called.
+ */
+ public void processRecentChanges ()
+ { // System.out.println( "EOEditingContext.processRecentChanges: " + invalidatedObjectsBuffer );
+
+ /*
+ * This implementation actually updates those lists immediately,
+ * but keeps a separate buffer of changes in the current event
+ * loop for the purposes of posting a notification.
+ * NOTE: to reenable buffering, uncomment lines from this method
+ * body and reenable the RecentChangesObserver in the constructor.
+ */
+
+ // broadcast ObjectsChangedInStoreNotification
+ // for the benefit of child editing contexts
+
+ boolean postStoreInfo =
+ ( insertedObjectsBuffer.size() +
+ updatedObjectsBuffer.size() +
+ deletedIDsBuffer.size() +
+ invalidatedIDsBuffer.size() > 0 );
+
+ NSMutableDictionary storeInfo = new NSMutableDictionary();
+ if ( postStoreInfo )
+ {
+ storeInfo.setObjectForKey(
+ globalIDsForObjects( insertedObjectsBuffer ),
+ // globalIDsForObjects( insertedObjects ),
+ EOObjectStore.InsertedKey );
+ storeInfo.setObjectForKey(
+ globalIDsForObjects( updatedObjectsBuffer ),
+ // globalIDsForObjects( updatedObjects ),
+ EOObjectStore.UpdatedKey );
+ storeInfo.setObjectForKey(
+ new NSArray( (Collection) deletedIDsBuffer ),
+ // globalIDsForObjects( deletedObjects ),
+ EOObjectStore.DeletedKey );
+ storeInfo.setObjectForKey(
+ new NSArray( (Collection) invalidatedIDsBuffer ),
+ EOObjectStore.InvalidatedKey );
+ }
+
+ // broadcast ObjectsChangedInEditingContextNotification
+ // for the benefit of attached display groups
+
+ boolean postContextInfo =
+ ( insertedObjectsBuffer.size() +
+ updatedObjectsBuffer.size() +
+ deletedObjectsBuffer.size() +
+ invalidatedObjectsBuffer.size() > 0 );
+
+ NSMutableDictionary contextInfo = new NSMutableDictionary();
+
+ if ( postContextInfo )
+ {
+
+ contextInfo.setObjectForKey(
+ new NSArray( (Collection) insertedObjectsBuffer ),
+ // new NSArray( (Collection) insertedObjects ),
+ EOObjectStore.InsertedKey );
+ contextInfo.setObjectForKey(
+ new NSArray( (Collection) updatedObjectsBuffer ),
+ // new NSArray( (Collection) updatedObjects ),
+ EOObjectStore.UpdatedKey );
+ contextInfo.setObjectForKey(
+ new NSArray( (Collection) deletedObjectsBuffer ),
+ // new NSArray( (Collection) deletedObjects ),
+ EOObjectStore.DeletedKey );
+ contextInfo.setObjectForKey(
+ new NSArray( (Collection) invalidatedObjectsBuffer ),
+ EOObjectStore.InvalidatedKey );
+ }
+
+ // update the current snapshots
+
+ Object o;
+ Iterator it;
+ it = insertedObjectsBuffer.iterator();
+ while ( it.hasNext() )
+ {
+ o = it.next();
+ registrar.setCurrentSnapshot( o, takeSnapshot( o ) );
+ }
+ it = updatedObjectsBuffer.iterator();
+ while ( it.hasNext() )
+ {
+ o = it.next();
+ registrar.setCurrentSnapshot( o, takeSnapshot( o ) );
+ }
+
+ // clear buffers
+
+ insertedObjectsBuffer.removeAllObjects();
+ updatedObjectsBuffer.removeAllObjects();
+ deletedObjectsBuffer.removeAllObjects();
+ deletedIDsBuffer.removeAllObjects();
+ invalidatedObjectsBuffer.removeAllObjects();
+ invalidatedIDsBuffer.removeAllObjects();
+
+ // post notifications (does order matter?)
+
+ if ( postStoreInfo )
+ {
+ NSNotificationCenter.defaultCenter().postNotification(
+ new NSNotification(
+ ObjectsChangedInStoreNotification, this, storeInfo ) );
+ }
+
+ if ( postContextInfo )
+ {
+ NSNotificationCenter.defaultCenter().postNotification(
+ new NSNotification(
+ ObjectsChangedInEditingContextNotification, this, contextInfo ) );
+ }
+
+ }
+
+ /**
+ * Returns whether this editing context propagates deletes
+ * immediately after the event that triggered the delete.
+ * Otherwise, propagation occurs only before commit.
+ */
+ public boolean propagatesDeletesAtEndOfEvent ()
+ {
+ return propagateDeletesAfterEvent;
+ }
+
+ /**
+ * Registers the specified object in this editing context
+ * for the specified id. This method is called by an object
+ * store when fetching objects for a display group, or when
+ * objects are inserted into a display group.
+ * This implementation will re-register the object under the
+ * new id if it is already registered under a different id.
+ */
+ public void recordObject (
+ Object anObject,
+ EOGlobalID aGlobalID )
+ {
+ // find state for re-registration
+ boolean inserted = false;
+ boolean updated = false;
+ boolean deleted = false;
+
+ // is the object already registered?
+ EOGlobalID existingID = globalIDForObject( anObject );
+ if ( existingID != null )
+ {
+ // remember object state
+ int index;
+ index = insertedObjects.indexOfIdenticalObject( anObject );
+ if ( index != NSArray.NotFound ) inserted = true;
+ index = updatedObjects.indexOfIdenticalObject( anObject );
+ if ( index != NSArray.NotFound ) updated = true;
+ index = deletedObjects.indexOfIdenticalObject( anObject );
+ if ( index != NSArray.NotFound ) deleted = true;
+ // forget the object
+ forgetObject( anObject );
+ }
+
+ // is the global id already in use?
+ Object existingObject = objectForGlobalID( aGlobalID );
+ if ( existingObject != null )
+ {
+ // forget it (don't worry about state?)
+ forgetObject( existingObject );
+ }
+
+ registrar.registerObject( anObject, aGlobalID );
+
+ // restore state if necessary
+ if ( inserted ) insertedObjects.addObject( anObject );
+ if ( updated ) updatedObjects.addObject( anObject );
+ if ( deleted ) deletedObjects.addObject( anObject );
+ }
+
+ /**
+ * Undoes the last undo operation.
+ */
+ public void redo ()
+ {
+ //TODO: not supported yet
+ throw new UnsupportedOperationException("Not implemented yet.");
+ }
+
+ /**
+ * Refaults this editing context, turning all unmodified
+ * objects into faults. This implementation calls
+ * editingContextWillSaveChanges() on all editors, and
+ * then calls refaultObjects().
+ */
+ public void refault ()
+ {
+ fireWillSaveChanges();
+ refaultObjects();
+ }
+
+ /**
+ * Refaults the specified object, turning it into a fault
+ * for the specified global id in the specified context.
+ */
+ public void refaultObject (
+ Object anObject,
+ EOGlobalID aGlobalID,
+ EOEditingContext aContext)
+ {
+ aContext.registrar.setCurrentSnapshot( anObject, null );
+
+ ignoreChanges = true;
+ parentStore.refaultObject( anObject, aGlobalID, aContext );
+ ignoreChanges = false;
+
+ // remove from updated objects if necessary
+ int i = updatedObjects.indexOfIdenticalObject( anObject );
+ if ( i != NSArray.NotFound )
+ {
+ updatedObjects.removeObjectAtIndex( i );
+ }
+
+ // add to invalidated notification queue
+ invalidatedObjectsBuffer.addObject( anObject );
+ invalidatedIDsBuffer.addObject( aGlobalID );
+ }
+
+ /**
+ * Turns all unmodified objects into faults, calling
+ * processRecentChanges() and then refaultObject() for
+ * each unmodified object.
+ */
+ public void refaultObjects ()
+ {
+ // is this call really needed?
+ // processRecentChanges();
+
+ Object o;
+ EOGlobalID id;
+ Iterator it = registeredObjects().iterator();
+ while ( it.hasNext() )
+ {
+ o = it.next();
+ if ( ( updatedObjects.indexOfIdenticalObject( o ) == NSArray.NotFound )
+ && ( insertedObjects.indexOfIdenticalObject( o ) == NSArray.NotFound )
+ && ( deletedObjects.indexOfIdenticalObject( o ) == NSArray.NotFound ) )
+ {
+ id = globalIDForObject( o );
+ refaultObject( o, id, this );
+ }
+ }
+ }
+
+ /**
+ * Calls editingContextWillSaveChanges() on all editors,
+ * and then calls invalidateAllObjects().
+ */
+ public void refetch ()
+ {
+ fireWillSaveChanges();
+ invalidateAllObjects();
+ }
+
+ /**
+ * Returns a read-only List of all objects registered in this
+ * editing context.
+ */
+ public NSArray registeredObjects ()
+ {
+ return registrar.registeredObjects();
+ }
+
+ /**
+ * Unregisters the specified editor with this editing context.
+ */
+ public void removeEditor ( Object anObject )
+ {
+ if ( anObject == null ) return;
+
+ Object o;
+ Iterator i = editorSet.iterator();
+ while ( i.hasNext() )
+ {
+ o = ((WeakReference)i.next()).get();
+ if ( ( o == null ) || ( o == anObject ) )
+ {
+ i.remove();
+ }
+ }
+ }
+
+ /**
+ * Unregisters all objects from this editing context,
+ * and resets the fetch timestamp.
+ */
+ public void reset ()
+ {
+ Iterator it = registeredObjects().iterator();
+ while ( it.hasNext() )
+ {
+ forgetObject( it.next() );
+ }
+ fetchTimestamp = 0; //FIXME: reset timestamp properly
+ }
+
+ /**
+ * Reverts the objects in this editing context to
+ * their original state.
+ * Calls editingContextWillSaveChanges on all editors,
+ * discards all inserted objects, restores deleted
+ * objects, and applies the fetch snapshot to all
+ * registered objects.
+ */
+ public void revert ()
+ {
+ willChange();
+ fireWillSaveChanges();
+
+ Iterator it;
+
+ // forget inserted objects
+ it = new NSArray( insertedObjects ).iterator();
+ while ( it.hasNext() )
+ {
+ forgetObject( it.next() );
+ }
+
+ EOGlobalID id;
+ Object o;
+ byte[] snapshot;
+
+ // re-initialize updated objects
+ it = new NSArray( updatedObjects ).iterator();
+ while ( it.hasNext() )
+ {
+ o = it.next();
+ snapshot = (byte[]) registrar.getCommitSnapshot( o );
+ if ( snapshot != null )
+ {
+ applySnapshot( snapshot, o );
+ }
+ registrar.setCommitSnapshot( o, null );
+ updatedObjectsBuffer.addObject( o );
+ }
+
+ // re-initialize deleted objects
+ it = new NSArray( deletedObjects ).iterator();
+ while ( it.hasNext() )
+ {
+ o = it.next();
+ snapshot = (byte[]) registrar.getCommitSnapshot( o );
+ if ( snapshot != null )
+ {
+ applySnapshot( snapshot, o );
+ }
+ registrar.setCommitSnapshot( o, null );
+ updatedObjectsBuffer.addObject( o );
+ }
+
+ // reset lists
+ insertedObjects.removeAllObjects(); // unneccessary?
+ deletedObjects.removeAllObjects();
+ updatedObjects.removeAllObjects();
+
+ // post notification
+ processRecentChanges();
+ }
+
+ /**
+ * Returns the root object store, which is the parent
+ * of all parent object stores of this editing context.
+ */
+ public EOObjectStore rootObjectStore ()
+ {
+ EOObjectStore parent = parentObjectStore();
+ while ( parent instanceof EOEditingContext )
+ {
+ parent = ((EOEditingContext)parent).parentObjectStore();
+ }
+ return parent;
+ }
+
+ /**
+ * Calls editingContextWillSaveChanges on all editors,
+ * and commits all changes in this editing context to
+ * the parent editing context by calling
+ * saveChangesInEditingContext to the parent.
+ * Then posts EditingContextDidSaveChangeNotification.
+ */
+ public void saveChanges ()
+ {
+//System.out.println( "EOEditingContext.saveChanges: " + this );
+ willChange();
+
+ // process any changes
+ processRecentChanges();
+
+ // set up user info for notification to be posted.
+ NSMutableDictionary userInfo = new NSMutableDictionary();
+ userInfo.setObjectForKey(
+ new NSArray( (Collection) insertedObjects ),
+ EOObjectStore.InsertedKey );
+ userInfo.setObjectForKey(
+ new NSArray( (Collection) updatedObjects ),
+ EOObjectStore.UpdatedKey );
+ userInfo.setObjectForKey(
+ new NSArray( (Collection) deletedObjects ),
+ EOObjectStore.DeletedKey );
+
+ // notify the editors
+ fireWillSaveChanges();
+
+ // notify the delegate
+ notifyDelegate(
+ "editingContextWillSaveChanges",
+ new Class[] { EOEditingContext.class },
+ new Object[] { this } );
+
+ // needed for notification handling
+ isInvalidating = true;
+ try
+ {
+ // ask parent to save us
+ parentStore.saveChangesInEditingContext( this );
+ }
+ catch ( RuntimeException e )
+ {
+ // unset save flag and rethrow
+ isInvalidating = false;
+ throw e;
+ }
+ isInvalidating = false;
+
+ // no exceptions: proceed!
+
+ Object o, key;
+ Iterator it;
+
+ // update the committed snapshots
+ it = insertedObjects.iterator();
+ while ( it.hasNext() )
+ {
+ o = it.next();
+ registrar.setCommitSnapshot( o, null );
+ registrar.setCurrentSnapshot( o, null );
+ }
+ it = updatedObjects.iterator();
+ while ( it.hasNext() )
+ {
+ o = it.next();
+ registrar.setCommitSnapshot( o, null );
+ registrar.setCurrentSnapshot( o, null );
+ }
+
+ // clear the lists
+ updatedObjects.removeAllObjects();
+ insertedObjects.removeAllObjects();
+ it = new NSArray( deletedObjects() ).iterator();
+ while ( it.hasNext() )
+ { // parent is doing this as well?
+ forgetObject( it.next() );
+ }
+
+ // post notification
+ NSNotificationCenter.defaultCenter().postNotification(
+ new NSNotification(
+ EditingContextDidSaveChangesNotification, this, userInfo ) );
+ }
+
+ /**
+ * Commits all changes in the specified editing context
+ * to this one. Called by child editing contexts in
+ * their saveChanges() method.
+ */
+ public void saveChangesInEditingContext (
+ EOEditingContext aContext)
+ {
+ Object o;
+ Iterator it;
+
+ // process deletes
+ List deletes = new NSArray( aContext.deletedObjects() );
+ it = deletes.iterator();
+ while ( it.hasNext() )
+ {
+ o = it.next();
+ EOGlobalID id = aContext.globalIDForObject( o );
+ Object localVersion = objectForGlobalID( id );
+ if ( localVersion == null )
+ {
+ // make a local copy and register it
+ localVersion = registerClone( id, aContext, o, this );
+ if ( localVersion == null )
+ {
+ throw new WotonomyException(
+ "Deleted object could not be serialized: "
+ + id + " : " + o );
+ }
+ }
+ else // we have a local version, copy changes
+ {
+ copy( aContext, o, this, localVersion );
+ // copy marks the object as updated: will be on both lists
+ }
+ // delete our copy -- marks context as changed
+ deleteObject( localVersion );
+ }
+
+ // process inserts - all inserts are new objects
+ List inserts = new NSArray( aContext.insertedObjects() );
+ it = inserts.iterator();
+ while ( it.hasNext() )
+ {
+ o = it.next();
+ // make a local copy and register it
+ EOGlobalID id = aContext.globalIDForObject( o );
+ willChange(); // need to mark editing context as changed
+ Object localVersion = registerClone( id, aContext, o, this );
+ if ( localVersion == null )
+ {
+ throw new WotonomyException(
+ "Inserted object could not be serialized: "
+ + o );
+ }
+ // insert our copy manually so a new id is not generated
+ insertedObjects.addObject( localVersion );
+ insertedObjectsBuffer.addObject( localVersion );
+ }
+
+ // process updates
+ List updates = new NSArray( aContext.updatedObjects() );
+ it = updates.iterator();
+ while ( it.hasNext() )
+ {
+ willChange(); // need to mark editing context as changed
+ o = it.next();
+ EOGlobalID id = aContext.globalIDForObject( o );
+ Object localVersion = objectForGlobalID( id );
+ if ( localVersion == null )
+ {
+ // make a local copy and register it
+ localVersion = registerClone( id, aContext, o, this );
+ if ( localVersion == null )
+ {
+ throw new WotonomyException(
+ "Updated object could not be serialized: "
+ + id + " : " + o );
+ }
+ if ( id.isTemporary() )
+ {
+ // mark this object as inserted
+ insertedObjects.addObject( localVersion );
+ insertedObjectsBuffer.addObject( localVersion );
+ }
+ else
+ {
+ // mark this object as updated
+ updatedObjects.addObject( localVersion );
+
+ // notify of update only if not on deleted list
+ if ( deletedObjectsBuffer.indexOfIdenticalObject(
+ localVersion ) == NSArray.NotFound )
+ {
+ updatedObjectsBuffer.addObject( localVersion );
+ }
+ }
+ }
+ else // we have a local version, copy changes
+ {
+ copy( aContext, o, this, localVersion );
+ // copy marks the object as updated
+ }
+ }
+
+ }
+
+ /**
+ * Sets the delegate for this editing context.
+ * Note: this implementation retains only a
+ * weak reference to the specified object.
+ */
+ public void setDelegate (
+ Object anObject )
+ {
+ if ( anObject == null ) delegate = null;
+ delegate = new WeakReference( anObject );
+ }
+
+ /**
+ * Sets the fetch timestamp for this editing context.
+ */
+ public void setFetchTimestamp (
+ double aDouble )
+ {
+ fetchTimestamp = aDouble;
+ }
+/*
+ public void setInvalidatesObjectsWhenFinalized (
+ boolean invalidatesObjects )
+ {
+ throw new net.wotonomy.util.WotonomyException("Not implemented yet.");
+ }
+
+ public void setInvalidatesObjectsWhenFreed (
+ boolean invalidatesObjects )
+ {
+ throw new net.wotonomy.util.WotonomyException("Not implemented yet.");
+ }
+*/
+ /**
+ * Sets whether this editing context attempts to
+ * lock objects when they are first modified.
+ * Default is false.
+ */
+ public void setLocksObjectsBeforeFirstModification (
+ boolean locksObjects )
+ {
+ lockBeforeModify = locksObjects;
+ }
+
+ /**
+ * Sets the message handler for this editing context.
+ * Note: this implementation retains only a
+ * weak reference to the specified object.
+ */
+ public void setMessageHandler (
+ Object anObject )
+ {
+ if ( anObject == null ) messageHandler = null;
+ messageHandler = new WeakReference( anObject );
+ }
+
+ /**
+ * Sets whether this editing context propagates deletes
+ * immediately after the event that triggered the delete.
+ * Otherwise, propagation occurs only before commit.
+ * Default is true.
+ */
+ public void setPropagatesDeletesAtEndOfEvent (
+ boolean propagatesDeletes )
+ {
+ propagateDeletesAfterEvent = propagatesDeletes;
+ }
+/*
+ public void setSharedEditingContext (
+ EOSharedEditingContext aSharedEditingContext )
+ {
+ throw new net.wotonomy.util.WotonomyException("Not implemented yet.");
+ }
+*/
+ /**
+ * Sets whether validation is stopped after the
+ * first error occurs. Otherwise, validation will
+ * continue for all other objects.
+ * Default is true.
+ */
+ public void setStopsValidationAfterFirstError (
+ boolean stopsValidation )
+ {
+ stopValidationAfterError = stopsValidation;
+ }
+
+ /**
+ * Sets the undo manager to be used for this context.
+ * Note: This is currently javax.swing.undo.UndoManager,
+ * until we have a implementation of NSUndoManager.
+ */
+/*
+ public void setUndoManager (
+ UndoManager anUndoManager )
+ {
+ undoManager = anUndoManager;
+ }
+*/
+/*
+ public EOSharedEditingContext sharedEditingContext ()
+ {
+ throw new net.wotonomy.util.WotonomyException("Not implemented yet.");
+ }
+*/
+ /**
+ * Returns whether validation is stopped after the
+ * first error occurs. Otherwise, validation will
+ * continue for all other objects.
+ */
+ public boolean stopsValidationAfterFirstError ()
+ {
+ return stopValidationAfterError;
+ }
+
+ /**
+ * Reverts the last change on the undo stack.
+ */
+ public void undo ()
+ {
+ //TODO: not supported yet
+ throw new UnsupportedOperationException("Not implemented yet.");
+ }
+/*
+ public NSUndoManager undoManager ()
+ {
+ throw new net.wotonomy.util.WotonomyException("Not implemented yet.");
+ }
+*/
+/**
+ public void unlock ()
+ {
+ throw new net.wotonomy.util.WotonomyException("Not implemented yet.");
+ }
+*/
+ /**
+ * Returns a read-only list of all objects marked as modified,
+ * but not inserted or deleted, in this editing context.
+ */
+ public NSArray updatedObjects ()
+ {
+ return updatedObjectsProxy;
+ }
+
+ /**
+ * Notify editors of changes.
+ */
+ private void fireWillSaveChanges()
+ {
+ Object o = null;
+ Iterator i = editors().iterator();
+ while ( i.hasNext() )
+ {
+ try
+ {
+ o = i.next();
+ NSSelector.invoke( "editingContextWillSaveChanges",
+ new Class[] { EOEditingContext.class }, o, this );
+ }
+ catch ( NoSuchMethodException e )
+ {
+ // ignore: not implemented
+ }
+ catch ( Exception exc )
+ {
+ // log to standard error
+ System.err.println( "Error while notifying editor of pending save: " + o );
+ exc.printStackTrace();
+ }
+ }
+ }
+
+ /**
+ * Handles notifications from parent store, looking for
+ * InvalidatedAllObjectsInStoreNotification and
+ * ObjectsChangedInStoreNotification.
+ * The former causes all objects in this store to be
+ * invalidated.
+ * The latter refaults the invalidated ids, merges changes
+ * from the updated ids, and forgets the deleted ids, then
+ * posts a ObjectsChangedInStoreNotification.
+ * Note: This method is not in the public specification.
+ */
+ public void handleNotification( NSNotification aNotification )
+ { // System.out.println( "EOEditingContext: " + this + " : " + aNotification );
+
+ willChange();
+ if ( InvalidatedAllObjectsInStoreNotification
+ .equals( aNotification.name() ) )
+ {
+ refaultObjects();
+
+ // relay notification
+ NSNotificationCenter.defaultCenter().postNotification(
+ new NSNotification(
+ InvalidatedAllObjectsInStoreNotification, this ) );
+ }
+ else
+ if ( EOGlobalID.GlobalIDChangedNotification
+ .equals( aNotification.name() ) )
+ {
+ NSDictionary userInfo = aNotification.userInfo();
+
+ // if any keys in userInfo are registered ids,
+ // re-register with new permanent values.
+
+ Object o;
+ EOGlobalID id;
+ Enumeration e = userInfo.keyEnumerator();
+ while ( e.hasMoreElements() )
+ {
+ id = (EOGlobalID) e.nextElement();
+ o = objectForGlobalID( id );
+ if ( o != null )
+ {
+ // record object is assumed to handle key updates
+ recordObject( o, (EOGlobalID) userInfo.objectForKey( id ) );
+ }
+ }
+ }
+ else
+ if ( EOObjectStore.ObjectsChangedInStoreNotification
+ .equals( aNotification.name() ) )
+ {
+ //System.out.println( "EOEditingContext.handleNotification: " + aNotification + " : " + this );
+ // post so child contexts are notified
+ willChange();
+
+ Object o;
+ EOGlobalID id;
+ Enumeration e;
+ NSDictionary userInfo = aNotification.userInfo();
+
+ // inserted objects are ignored
+
+ // existing deleted objects are removed
+ NSArray deletes = (NSArray) userInfo.objectForKey(
+ EOObjectStore.DeletedKey );
+ e = deletes.objectEnumerator();
+ while ( e.hasMoreElements() )
+ {
+ id = (EOGlobalID) e.nextElement();
+ o = objectForGlobalID( id );
+ if ( o != null )
+ {
+ //System.out.println( "EOEditingContext: deleted: " + id );
+ forgetObject( o );
+ deletedObjectsBuffer.addObject( o );
+ deletedIDsBuffer.addObject( id );
+ }
+ }
+
+ // existing updated objects are merged
+ NSArray updates = (NSArray) userInfo.objectForKey(
+ EOObjectStore.UpdatedKey );
+ e = updates.objectEnumerator();
+ while ( e.hasMoreElements() )
+ {
+ id = (EOGlobalID) e.nextElement();
+ o = objectForGlobalID( id );
+ if ( o != null )
+ {
+ //System.out.println( "EOEditingContext: updated: " + id );
+ if ( updatedObjects // only update if unchanged
+ .indexOfIdenticalObject( o ) == NSArray.NotFound )
+ {
+ refaultObject( o, id, this );
+ updatedObjectsBuffer.addObject( o );
+ }
+ else
+ {
+ // notify user and/or merge
+ handleUpdateConflict( id, o );
+ }
+ }
+ }
+
+ // existing invalidated objects are refaulted
+ NSArray invalidates = (NSArray) userInfo.objectForKey(
+ EOObjectStore.InvalidatedKey );
+ e = invalidates.objectEnumerator();
+ while ( e.hasMoreElements() )
+ {
+ id = (EOGlobalID) e.nextElement();
+ o = objectForGlobalID( id );
+ if ( o != null )
+ {
+ if ( updatedObjects // only invalidate if unchanged
+ .indexOfIdenticalObject( o ) == NSArray.NotFound )
+ {
+ refaultObject( o, id, this );
+ }
+ else
+ {
+ // notify user and/or merge
+ handleUpdateConflict( id, o );
+ }
+ if ( invalidatedObjectsBuffer
+ .indexOfIdenticalObject( o ) == NSArray.NotFound )
+ {
+ invalidatedObjectsBuffer.addObject( o );
+ }
+ if ( invalidatedIDsBuffer
+ .indexOfIdenticalObject( id ) == NSArray.NotFound )
+ {
+ invalidatedIDsBuffer.addObject( id );
+ }
+ }
+ }
+
+ }
+ }
+
+ /**
+ * Called by handleNotification to resolve the case where we have
+ * received notification that another user or context has updated
+ * an object that is currently marked as edited in this context.
+ * This implementation first asks the delegate if it should merge.
+ * If true or no delegate, the changes are merged. True or false,
+ * the delegate is then sent editingContextDidMergeChanges.
+ */
+ private void handleUpdateConflict( EOGlobalID anID, Object anObject )
+ {
+ // if we're causing the invalidation, ignore
+ // (this is probably better handled by the caller...)
+ if ( isInvalidating )
+ {
+ ignoreChanges = true;
+ parentStore.refaultObject( anObject, anID, this );
+ ignoreChanges = false;
+ return;
+ }
+
+ Boolean result = (Boolean) notifyDelegate(
+ "editingContextShouldMergeChangesForObject",
+ new Class[] { EOEditingContext.class, Object.class },
+ new Object[] { this, anObject } );
+
+ if ( ( result == null ) || ( Boolean.TRUE.equals( result ) ) )
+ {
+ // do merge
+ mergeExternalChanges( anID, anObject );
+ }
+ else // Boolean.FALSE
+ {
+ // do nothing: don't lose the user's changes
+ }
+
+ // notify merge did happen
+ notifyDelegate(
+ "editingContextDidMergeChanges",
+ new Class[] { EOEditingContext.class },
+ new Object[] { this } );
+ }
+
+ /**
+ * For the currently modified object with the specified global id,
+ * this method merges changes with the updated version in the parent
+ * object store. This implementation looks at the fetch snapshot
+ * to determine which changes where made by the user, fetches the
+ * updated version of the object, and then determine what external
+ * changes were made. If the changes do not overlap, the original
+ * changes are applied to the updated version. If there is a conflict,
+ * notifies the user of the conflict.
+ */
+ private boolean mergeExternalChanges( EOGlobalID anID, Object anObject )
+ {
+ try
+ {
+ Iterator i;
+ Object key;
+
+ // get fetch snapshot
+ Map fetchSnapshot = committedSnapshotForObject( anObject );
+
+ // get current snapshot
+ Map currentSnapshot = currentEventSnapshotForObject( anObject );
+
+ // diff against fetch snapshot
+ Map currentDiff = new HashMap();
+ i = currentSnapshot.keySet().iterator();
+ while ( i.hasNext() )
+ {
+ key = i.next();
+ if ( ! currentSnapshot.get( key ).equals( fetchSnapshot.get( key ) ) )
+ {
+ currentDiff.put( key, currentSnapshot.get( key ) );
+ }
+ }
+
+ // refault
+ ignoreChanges = true;
+ parentStore.refaultObject( anObject, anID, this );
+ ignoreChanges = false;
+
+ // get updated snapshot
+ Map updatedSnapshot = convertSnapshotToDictionary( takeSnapshot( anObject ) );
+
+ // diff against fetch snapshot
+ Map updatedDiff = new HashMap();
+ i = updatedSnapshot.keySet().iterator();
+ while ( i.hasNext() )
+ {
+ key = i.next();
+ if ( ! updatedSnapshot.get( key ).equals( fetchSnapshot.get( key ) ) )
+ {
+ updatedDiff.put( key, updatedSnapshot.get( key ) );
+ }
+ }
+
+ // determine if there's a conflict
+ boolean proceed = true;
+ Set updatedKeys = updatedDiff.keySet();
+ i = currentDiff.keySet().iterator();
+ while ( i.hasNext() )
+ {
+ if ( updatedKeys.contains( i.next() ) )
+ {
+ proceed = false;
+ break;
+ }
+ }
+
+ // if no conflicts, apply original diff to current object and exit
+ if ( proceed )
+ {
+ KeyValueCodingUtilities.takeStoredValuesFromDictionary( anObject, currentDiff );
+ return true; // exit!
+ }
+ }
+ catch ( Exception exc )
+ {
+ // log error to standard out
+ exc.printStackTrace();
+ }
+
+ // notify user we're unable to merge
+ notifyMessageHandler( MessageChangeConflict + anObject );
+ return false;
+ }
+
+ /**
+ * Sends the specified message to the message handler.
+ */
+ private void notifyMessageHandler( String aMessage )
+ {
+ Object handler = null;
+ try
+ {
+ handler = messageHandler();
+ if ( handler == null ) return;
+ NSSelector.invoke( "editingContextPresentErrorMessage",
+ new Class[] { EOEditingContext.class, String.class },
+ handler, this, aMessage );
+ }
+ catch ( NoSuchMethodException e )
+ {
+ // ignore: not implemented
+ }
+ catch ( Exception exc )
+ {
+ // log to standard error
+ System.err.println(
+ "Error while notifying message handler: " +
+ handler + " : " + aMessage );
+ exc.printStackTrace();
+ }
+ }
+
+ /**
+ * Sends the specified message to the delegate.
+ * Returns the return value of the method,
+ * or null if no return value or no delegate
+ * or no implementation.
+ */
+ private Object notifyDelegate(
+ String aMethodName, Class[] types, Object[] params )
+ {
+ try
+ {
+ Object delegate = delegate();
+ if ( delegate == null ) return null;
+ return NSSelector.invoke(
+ aMethodName, types, delegate, params );
+ }
+ catch ( NoSuchMethodException e )
+ {
+ // ignore: not implemented
+ }
+ catch ( Exception exc )
+ {
+ // log to standard error
+ System.err.println(
+ "Error while messaging delegate: " +
+ delegate + " : " + aMethodName );
+ exc.printStackTrace();
+ }
+
+ return null;
+ }
+
+ // interface EOObserving
+
+ /**
+ * Implementation of the EOObserving interface.
+ * Called before objects are modified.
+ */
+ public void objectWillChange (
+ Object anObject )
+ {
+ if ( ignoreChanges ) return;
+//NSNotificationCenter.defaultCenter().postNotification( "objectWillChange", this, new NSDictionary( "object", anObject ) );
+//new RuntimeException().printStackTrace();
+
+ willChange();
+
+ // mark as updated if not marked already
+ int i = updatedObjects.indexOfIdenticalObject( anObject );
+ if ( i == NSArray.NotFound )
+ {
+ // don't mark inserted objects as updated
+ i = insertedObjects.indexOfIdenticalObject( anObject );
+ if ( i == NSArray.NotFound )
+ {
+ i = deletedObjects.indexOfIdenticalObject( anObject );
+ if ( i == NSArray.NotFound )
+ {
+ // add object
+ updatedObjects.addObject( anObject );
+
+ // record revert snapshot
+ registrar.setCommitSnapshot( anObject, takeSnapshot( anObject ) );
+ }
+ }
+ }
+
+ // add to buffer
+ if ( updatedObjectsBuffer.indexOfIdenticalObject( anObject )
+ == NSArray.NotFound )
+ {
+ updatedObjectsBuffer.addObject( anObject );
+ }
+ }
+
+ // static methods
+
+ public static double defaultFetchTimestampLag ()
+ {
+ return defaultFetchTimestampLag;
+ }
+
+ /**
+ * Returns the default parent object store for all
+ * object stores created with the parameterless
+ * constructor.
+ */
+ public static EOObjectStore defaultParentObjectStore ()
+ {
+ return defaultParentObjectStore;
+ }
+
+/*
+ public static Object initObjectWithCoder (
+ Object anObject,
+ NSCoder aCoder )
+ {
+ throw new net.wotonomy.util.WotonomyException("Not implemented yet.");
+ }
+*/
+
+ /**
+ * Returns whether editing contexts are configured to retain strong
+ * references to their registered objects. If false, editing contexts
+ * will only retain weak references to their registered objects.
+ */
+ public static boolean instancesRetainRegisteredObjects()
+ {
+ return retainsRegisteredObjects;
+ }
+
+ /**
+ * Sets the global default fetch timestamp lag.
+ */
+ public static void setDefaultFetchTimestampLag (
+ double aDouble )
+ {
+ defaultFetchTimestampLag = aDouble;
+ }
+
+ /**
+ * Sets the global default parent object store,
+ * used for the parameterless constructor.
+ */
+ public static void setDefaultParentObjectStore (
+ EOObjectStore anObjectStore )
+ {
+ defaultParentObjectStore = anObjectStore;
+ }
+
+ public static void setInstancesRetainRegisteredObjects (
+ boolean retainsObjects )
+ {
+ retainsRegisteredObjects = retainsObjects;
+ }
+
+/*
+ public static void setSubstitutionEditingContext (
+ EOEditingContext aContext)
+ {
+ throw new net.wotonomy.util.WotonomyException("Not implemented yet.");
+ }
+
+ public static void setUsesContextRelativeEncoding (
+ boolean usesRelativeEncoding )
+ {
+ throw new net.wotonomy.util.WotonomyException("Not implemented yet.");
+ }
+
+ public static EOEditingContext substitutionEditingContext ()
+ {
+ throw new net.wotonomy.util.WotonomyException("Not implemented yet.");
+ }
+
+ public static boolean usesContextRelativeEncoding ()
+ {
+ throw new net.wotonomy.util.WotonomyException("Not implemented yet.");
+ }
+*/
+
+ public String toString()
+ {
+ return "[EOEditingContext@"+Integer.toHexString(System.identityHashCode(this))+":"+
+ " inserted="+idsForObjects(insertedObjects)+
+ " updated="+idsForObjects(updatedObjects)+
+ " deleted="+idsForObjects(deletedObjects)+
+ " registered="+registrar.registeredGlobalIDs()+" ]";
+ }
+ private List idsForObjects( List objects )
+ {
+ List result = new LinkedList();
+ Iterator i = objects.iterator();
+ while ( i.hasNext() ) result.add( globalIDForObject( i.next() ) );
+ return result;
+ }
+
+ // snapshots
+
+ /**
+ * Returns a NSDictionary containing only the mutable properties
+ * for the specified object and deep clones of their values.
+ * Nulls are represented by NSNull.nullValue().
+ */
+ private byte[] takeSnapshot( Object anObject )
+ { // System.out.println( "takeSnapshot: " + anObject );
+ return KeyValueCodingUtilities.freeze( anObject, this, anObject, true );
+ }
+
+ /**
+ * Applies the map of properties and values to the
+ * specified object. Null values for properties must
+ * be represented by the NSNull.nullValue().
+ * Posts a willChange event before applying changes.
+ */
+ private void applySnapshot( byte[] aSnapshot, Object anObject )
+ {
+ // must clone snapshot to avoid changing existing snapshot
+ NSDictionary values = convertSnapshotToDictionary( aSnapshot );
+
+//System.out.println( "applySnapshot: " + aSnapshot + " : " + values );
+//ignoreChanges = true;
+ willChange();
+ KeyValueCodingUtilities.takeStoredValuesFromDictionary( anObject, values );
+//ignoreChanges = false;
+ }
+
+ /**
+ * Snapshots are stored internally in binary format,
+ * but exposed to the user as NSDictionaries.
+ */
+ private NSDictionary convertSnapshotToDictionary( byte[] aSnapshot )
+ {
+ // get the object
+ Object clone = KeyValueCodingUtilities.thaw( aSnapshot, this, true );
+ // get all keys for this object
+ EOClassDescription classDesc =
+ EOClassDescription.classDescriptionForClass( clone.getClass() );
+ List keys = new LinkedList();
+ keys.addAll( classDesc.attributeKeys() );
+ keys.addAll( classDesc.toOneRelationshipKeys() );
+ keys.addAll( classDesc.toManyRelationshipKeys() );
+
+ return KeyValueCodingUtilities.valuesForKeys( clone, keys );
+ }
+ /**
+ * Creates a deep clone of the specified object.
+ * (Object.clone() only creates a shallow clone.)
+ * Returns null if operation fails.
+ */
+ static private Object clone(
+ EOEditingContext aSourceContext,
+ Object aSource,
+ EOEditingContext aDestinationContext )
+ { // System.out.println( "clone: " + aSource );
+ return KeyValueCodingUtilities.clone(
+ aSourceContext, aSource, aDestinationContext );
+ }
+
+ /**
+ * Creates a deep clone of the specified object.
+ * but does not transpose references. This allows
+ * us to register an object before transposing
+ * references so that child objects will be able
+ * to resolve references to their parent.
+ * After recording the object, we copy the source
+ * object into the clone, which does transpose
+ * and resolve properly.
+ * Returns null if operation fails.
+ */
+ static private Object registerClone(
+ EOGlobalID aGlobalID,
+ EOEditingContext aSourceContext,
+ Object aSource,
+ EOEditingContext aDestinationContext )
+ {
+ // while we'd like to just transpose/clone at the same time
+ // we must record a clone without transposing: this
+ // avoids a endless loop if the object graph has a cycle
+ Object clone = KeyValueCodingUtilities.thaw(
+ KeyValueCodingUtilities.freeze(
+ aSource, aSourceContext, aSource, false ),
+ aDestinationContext, false );
+
+ aDestinationContext.recordObject( clone, aGlobalID );
+
+ // need to copy to transpose references into this context
+ // while preserving the same instance of the object
+ aDestinationContext.ignoreChanges = true;
+ copy( aSourceContext, aSource, aDestinationContext, clone );
+ aDestinationContext.ignoreChanges = false;
+
+ // return our clone
+ return clone;
+ }
+
+ /**
+ * Copies values from one object to another.
+ * Returns the destination object, or throws exception
+ * if operation fails.
+ */
+ static private Object copy(
+ EOEditingContext aSourceContext,
+ Object aSource,
+ EOEditingContext aDestinationContext,
+ Object aDestination )
+ { // System.out.println( "copy: " );
+ EOObserverCenter.notifyObserversObjectWillChange( aDestination );
+ KeyValueCodingUtilities.copy( aSourceContext, aSource, aDestinationContext, aDestination );
+ return aDestination;
+ }
+
+ // process recent changes
+
+ /**
+ * Called to notify observers of changes.
+ * Also calls runLater().
+ */
+ private void willChange()
+ {
+ EOObserverCenter.notifyObserversObjectWillChange( this );
+ runLater();
+ }
+
+ /**
+ * Called to ensure that processRecentChanges
+ * will be called on the next event loop.
+ */
+ private void runLater()
+ {
+ if ( ! willRunLater )
+ {
+ willRunLater = true;
+ NSRunLoop.currentRunLoop().performSelectorWithOrder(
+ runLaterSelector, this, null, EditingContextFlushChangesRunLoopOrdering, null );
+ }
+ }
+
+ /**
+ * This method is called by the event queue run loop
+ * and calls processRecentChanges.
+ * NOTE: This method is not part of the specification.
+ */
+ public void flushRecentChanges( Object anObject )
+ {
+//System.out.println( "EODelayedObserverQueue: running" );
+ processRecentChanges();
+ willRunLater = false;
+ }
+
+ // inner classes
+
+ /**
+ * Gatekeeper for all access to registered objects.
+ */
+ static private class Registrar
+ {
+ EOEditingContext context;
+ NSMutableDictionary IDsToObjects;
+ NSMutableDictionary objectsToIDs;
+ NSMutableDictionary objectsToCommitSnapshots;
+ NSMutableDictionary objectsToCurrentSnapshots;
+
+ ReferenceKey comparisonKey; //FIXME not thread safe!
+
+ public Registrar( EOEditingContext aContext )
+ {
+ context = aContext;
+ IDsToObjects = new NSMutableDictionary();
+ objectsToIDs = new NSMutableDictionary();
+ objectsToCommitSnapshots = new NSMutableDictionary();
+ objectsToCurrentSnapshots = new NSMutableDictionary();
+ comparisonKey = new ReferenceKey();
+ }
+
+ public NSArray registeredObjects()
+ {
+ return IDsToObjects.allValues();
+ }
+
+ public NSArray registeredGlobalIDs()
+ {
+ return IDsToObjects.allKeys();
+ }
+
+ public Object objectForGlobalID( EOGlobalID aGlobalID )
+ {
+ return IDsToObjects.objectForKey( aGlobalID );
+ }
+
+ public EOGlobalID globalIDForObject( Object anObject )
+ {
+ comparisonKey.set( anObject );
+ return (EOGlobalID) objectsToIDs.objectForKey( comparisonKey );
+ }
+
+ public byte[] getCommitSnapshot( Object anObject )
+ {
+ comparisonKey.set( anObject );
+ return (byte[]) objectsToCommitSnapshots.objectForKey( comparisonKey );
+ }
+
+ public void setCommitSnapshot( Object anObject, byte[] aSnapshot )
+ {
+ if ( aSnapshot == null )
+ {
+ comparisonKey.set( anObject );
+ objectsToCommitSnapshots.removeObjectForKey( comparisonKey );
+ }
+ else
+ {
+ objectsToCommitSnapshots.setObjectForKey(
+ aSnapshot, new ReferenceKey( anObject ) );
+ }
+ }
+
+ public byte[] getCurrentSnapshot( Object anObject )
+ {
+ comparisonKey.set( anObject );
+ return (byte[]) objectsToCurrentSnapshots.objectForKey( comparisonKey );
+ }
+
+ public void setCurrentSnapshot( Object anObject, byte[] aSnapshot )
+ {
+ if ( aSnapshot == null )
+ {
+ comparisonKey.set( anObject );
+ objectsToCurrentSnapshots.removeObjectForKey( comparisonKey );
+ }
+ else
+ {
+ objectsToCurrentSnapshots.setObjectForKey(
+ aSnapshot, new ReferenceKey( anObject ) );
+ }
+ }
+
+ public void registerObject( Object anObject, EOGlobalID aGlobalID )
+ {
+ IDsToObjects.setObjectForKey( anObject, aGlobalID );
+ objectsToIDs.setObjectForKey( aGlobalID, new ReferenceKey( anObject ) );
+ EOObserverCenter.addObserver( context, anObject );
+ }
+
+ public void forgetObject( Object anObject )
+ {
+ comparisonKey.set( anObject );
+ Object id = objectsToIDs.objectForKey( comparisonKey );
+ IDsToObjects.removeObjectForKey( id );
+ objectsToIDs.removeObjectForKey( comparisonKey );
+ EOObserverCenter.removeObserver( context, anObject );
+ }
+
+ public void disposeSnapshots( Object anObject )
+ {
+ setCommitSnapshot( anObject, null );
+ setCurrentSnapshot( anObject, null );
+ }
+
+ }
+
+ /**
+ * Registrar that uses only WeakReferences.
+ * Used if retainsRegisteredObjects is false.
+ */
+ static private class WeakRegistrar extends Registrar
+ {
+ private WeakReferenceKey weakComparisonKey; //FIXME not thread safe!
+
+ public WeakRegistrar( EOEditingContext aContext )
+ {
+ super( aContext );
+ weakComparisonKey = new WeakReferenceKey();
+ }
+
+ public NSArray registeredObjects()
+ {
+ Object object;
+ WeakReferenceKey weakKey;
+ NSMutableArray result = new NSMutableArray();
+ Enumeration e = new NSArray( objectsToIDs.allKeys() ).objectEnumerator();
+ while ( e.hasMoreElements() )
+ {
+ weakKey = (WeakReferenceKey) e.nextElement();
+ object = weakKey.get();
+ if ( object != null )
+ {
+ result.addObject( object );
+ }
+ else
+ {
+ // object has been released: perform cleanup
+ disposeObject( null, weakKey );
+ }
+ }
+ return result;
+ }
+
+ public Object objectForGlobalID( EOGlobalID aGlobalID )
+ {
+ WeakReference ref = (WeakReference) super.objectForGlobalID( aGlobalID );
+ if ( ref == null ) return null;
+ Object result = ref.get();
+ if ( result == null )
+ {
+ // clean up manually
+ IDsToObjects.removeObjectForKey( aGlobalID );
+ Iterator i = new LinkedList( objectsToIDs.allKeysForObject( ref ) ).iterator();
+ while ( i.hasNext() )
+ {
+ objectsToIDs.removeObjectForKey( i.next() );
+ }
+ disposeSnapshots( aGlobalID );
+ }
+ return result;
+ }
+
+ public byte[] getCommitSnapshot( Object anObject )
+ {
+ weakComparisonKey.set( anObject );
+ return (byte[]) objectsToCommitSnapshots.objectForKey( weakComparisonKey );
+ }
+
+ public void setCommitSnapshot( Object anObject, byte[] aSnapshot )
+ {
+ if ( aSnapshot == null )
+ {
+ weakComparisonKey.set( anObject );
+ objectsToCommitSnapshots.removeObjectForKey( weakComparisonKey );
+ }
+ else
+ {
+ objectsToCommitSnapshots.setObjectForKey(
+ aSnapshot, new WeakReferenceKey( anObject ) );
+ }
+ }
+
+ public byte[] getCurrentSnapshot( Object anObject )
+ {
+ weakComparisonKey.set( anObject );
+ return (byte[]) objectsToCurrentSnapshots.objectForKey( weakComparisonKey );
+ }
+
+ public void setCurrentSnapshot( Object anObject, byte[] aSnapshot )
+ {
+ if ( aSnapshot == null )
+ {
+ weakComparisonKey.set( anObject );
+ objectsToCurrentSnapshots.removeObjectForKey( weakComparisonKey );
+ }
+ else
+ {
+ objectsToCurrentSnapshots.setObjectForKey(
+ aSnapshot, new WeakReferenceKey( anObject ) );
+ }
+ }
+
+ public void registerObject( Object anObject, EOGlobalID aGlobalID )
+ { // new net.wotonomy.ui.swing.ReferenceInspector( anObject );
+ IDsToObjects.setObjectForKey( new WeakReference( anObject ), aGlobalID );
+ objectsToIDs.setObjectForKey( aGlobalID, new WeakReferenceKey( anObject ) );
+ EOObserverCenter.addObserver( context, anObject );
+ }
+
+ public void forgetObject( Object anObject )
+ {
+ disposeObject( anObject, null );
+ }
+
+ // must specify one or the other
+ private void disposeObject( Object anObject, WeakReferenceKey key )
+ {
+ if ( key == null ) key = new WeakReferenceKey( anObject );
+ EOGlobalID id = (EOGlobalID) objectsToIDs.objectForKey( key );
+ if ( id != null ) IDsToObjects.removeObjectForKey( id );
+ objectsToIDs.removeObjectForKey( key );
+ disposeSnapshots( id );
+ if ( anObject != null )
+ {
+ EOObserverCenter.removeObserver( context, anObject );
+ }
+ }
+ }
+
+ /**
+ * Private class used to force a hashmap to
+ * perform key comparisons by reference.
+ */
+ static private class ReferenceKey
+ {
+ int hashCode;
+ Object referent;
+
+ public ReferenceKey()
+ {
+ referent = null;
+ hashCode = -1;
+ }
+
+ public ReferenceKey( Object anObject )
+ {
+ set( anObject );
+ }
+
+ public Object get()
+ {
+ return referent;
+ }
+
+ public void set( Object anObject )
+ {
+ referent = anObject;
+ hashCode = anObject.hashCode();
+ }
+
+ /**
+ * Returns the actual key's hash code.
+ */
+ public int hashCode()
+ {
+ return hashCode;
+ }
+
+ /**
+ * Compares by reference.
+ */
+ public boolean equals( Object anObject )
+ {
+ if ( anObject == this ) return true;
+ if ( anObject instanceof ReferenceKey )
+ {
+ return ((ReferenceKey)anObject).get() == referent;
+ }
+ return false;
+ }
+ }
+
+ /**
+ * Private class used to force a hashmap to
+ * perform key comparisons by reference.
+ */
+ static private class WeakReferenceKey extends ReferenceKey
+ {
+ public WeakReferenceKey()
+ {
+ super();
+ }
+
+ public WeakReferenceKey( Object anObject )
+ {
+ super( anObject );
+ }
+
+ public Object get()
+ {
+ return ((WeakReference)referent).get();
+ }
+
+ public void set( Object anObject )
+ {
+ referent = new WeakReference( anObject );
+ hashCode = anObject.hashCode();
+ }
+
+ /**
+ * Compares by reference.
+ */
+ public boolean equals( Object anObject )
+ {
+ if ( anObject == this ) return true;
+ if ( anObject instanceof ReferenceKey )
+ {
+ return ((ReferenceKey)anObject).get() == get();
+ }
+ return false;
+ }
+ }
+
+ /**
+ * Key combining an object with a string.
+ * Object is compared by reference.
+ */
+ static private class CompoundKey
+ {
+ private Object object;
+ private String string;
+ private int hashCode;
+
+ /**
+ * Creates compound key.
+ * Neither name nor object may be null.
+ */
+ public CompoundKey (
+ Object anObject, String aString )
+ {
+ object = anObject;
+ string = aString;
+ hashCode = object.hashCode() + string.hashCode();
+ }
+
+ public int hashCode()
+ {
+ return hashCode;
+ }
+
+ public boolean equals( Object anObject )
+ {
+ if ( anObject instanceof CompoundKey )
+ {
+ CompoundKey key = (CompoundKey) anObject;
+ return ( ( key.object == object ) && ( key.string.equals( string ) ) );
+ }
+ return false;
+ }
+
+ public String toString()
+ {
+ return "[CompoundKey:"+object+":"+string+"]";
+ }
+ }
+
+ /**
+ * Used by EditingContext to delegate behavior to another class.
+ * Note that EditingContext doesn't require its delegates to implement
+ * this interface: rather, this interface defines the methods that
+ * EditingContext will attempt to invoke dynamically on its delegate.
+ * The delegate may choose to implement only a subset of the methods
+ * on the interface.
+ */
+ public interface Delegate
+ {
+ /**
+ * Called after the editing context has completed merge operations
+ * on one or more objects after receiving an ObjectChangedInStore
+ * notification.
+ */
+ void editingContextDidMergeChanges(
+ EOEditingContext anEditingContext );
+
+ /**
+ * Called by objectsWithFetchSpecification.
+ * If null, the editing context will pass the fetch specification
+ * on to its parent store, as normal. Otherwise, the context
+ * will use the returned array to service the request.
+ */
+ NSArray editingContextShouldFetchObjects(
+ EOEditingContext anEditingContext,
+ EOFetchSpecification fetchSpecification );
+
+ /**
+ * Called to determine whether an object should be invalidated.
+ * Return false to prevent the object from being invalidated.
+ * Default is true.
+ */
+ boolean editingContextShouldInvalidateObject(
+ EOEditingContext anEditingContext,
+ Object anObject,
+ EOGlobalID aGlobalID );
+
+ /**
+ * Called to determine whether the editing context should attempt
+ * to merge changes in the specified object that the parent store
+ * says has changed via an ObjectChangedInStore notification.
+ * Default is true. Return false if you wish to handle the merge
+ * yourself, by extracting the values in the object now and comparing
+ * them to the values when editingContextDidMergeChanges is called.
+ */
+ boolean editingContextShouldMergeChangesForObject(
+ EOEditingContext anEditingContext,
+ Object anObject );
+
+ /**
+ * Returns whether the editing context should ask its message handler
+ * to display a message. Return false if the delegate will display the error.
+ * Default is true.
+ */
+ boolean editingContextShouldPresentException(
+ EOEditingContext anEditingContext,
+ Throwable exception );
+
+ /**
+ * Returns whether the editing context should undo the most
+ * recent set of changes that resulted in a validation failure.
+ * Default is true.
+ */
+ boolean editingContextShouldUndoUserActionsAfterFailure(
+ EOEditingContext anEditingContext );
+
+ /**
+ * Returns whether the editing context should validate the
+ * most recent set of changes. Default is true.
+ */
+ boolean editingContextShouldValidateChanges(
+ EOEditingContext anEditingContext );
+
+ /**
+ * Called before the editing context saves its changes
+ * to the parent object store.
+ */
+ void editingContextWillSaveChanges(
+ EOEditingContext anEditingContext );
+
+ }
+
+ /**
+ * Editors register themselves with the editing context
+ * so that they may receive notification before the context
+ * commits changes. This is useful for associations whose
+ * components do not immediately commit their changes to
+ * the object they are editing.
+ */
+ public interface Editor
+ {
+ /**
+ * Called before the editing context saves its changes
+ * to the parent object store.
+ */
+ void editingContextWillSaveChanges(
+ EOEditingContext anEditingContext );
+
+ /**
+ * Called to determine whether this editor has changes
+ * that have not been committed to the object in the context.
+ */
+ boolean editorHasChangesForEditingContext(
+ EOEditingContext anEditingContext );
+
+ }
+
+ /**
+ * Used by EditingContext to delegate messaging handling to another class,
+ * typically the display group that has the currently active association.
+ * Note that EditingContext doesn't require its message handlers to implement
+ * this interface: rather, this interface defines the methods that
+ * EditingContext will attempt to invoke dynamically on its delegate.
+ * The delegate may choose to implement only a subset of the methods
+ * on the interface.
+ */
+ public interface MessageHandler
+ {
+ /**
+ * Called to display a message for an error that occurred
+ * in the specified editing context.
+ */
+ void editingContextPresentErrorMessage(
+ EOEditingContext anEditingContext,
+ String aMessage );
+
+ /**
+ * Called by the specified object store to determine whether
+ * fetching should continue, where count is the current count
+ * and limit is the limit as specified by the fetch specification.
+ * Default is false.
+ */
+ boolean editingContextShouldContinueFetching(
+ EOEditingContext anEditingContext,
+ int count,
+ int limit,
+ EOObjectStore anObjectStore );
+
+ }
+
+}
+
+/*
+ * $Log$
+ * Revision 1.2 2006/02/16 16:47:14 cgruber
+ * Move some classes in to "internal" packages and re-work imports, etc.
+ *
+ * Also use UnsupportedOperationExceptions where appropriate, instead of WotonomyExceptions.
+ *
+ * Revision 1.1 2006/02/16 13:19:57 cgruber
+ * Check in all sources in eclipse-friendly maven-enabled packages.
+ *
+ * Revision 1.86 2003/12/18 15:37:38 mpowers
+ * Changes to retain ability to work with objects that don't necessarily
+ * implement EOEnterpriseObject. I would still like to preserve this case
+ * for general usage, however the access package is free to assume that
+ * those objects will be EOs and cast appropriately.
+ *
+ * Revision 1.85 2003/08/19 01:53:12 chochos
+ * EOObjectStore had some incompatible return types (Object instead of EOEnterpriseObject, in fault methods mostly). It's internally consistent but I hope it doesn't break anything based on this, even though fault methods mostly throw exceptions for now.
+ *
+ * Revision 1.84 2003/08/06 23:07:52 chochos
+ * general code cleanup (mostly, removing unused imports)
+ *
+ * Revision 1.83 2003/02/13 15:24:33 mpowers
+ * hasChanges is now derived, not tracked.
+ * refaultObject now more consistently removes object from updated list.
+ *
+ * Revision 1.82 2002/12/16 15:46:00 mpowers
+ * Major refactoring to implement setInstancesRetainRegisteredObjects().
+ *
+ * Revision 1.81 2002/11/18 22:10:58 mpowers
+ * Now resetting hasChanges flag on reset.
+ *
+ * Revision 1.80 2002/10/24 21:15:33 mpowers
+ * New implementations of NSArray and subclasses.
+ *
+ * Revision 1.79 2002/10/24 18:18:12 mpowers
+ * NSArray's are now considered read-only, so we can return our internal
+ * representation to reduce unnecessary object allocation.
+ *
+ * Revision 1.78 2002/06/21 21:44:33 mpowers
+ * No longer marking deleted objects as updated (thanks to dwang).
+ *
+ * Revision 1.77 2002/05/20 15:10:17 mpowers
+ * No longer refaulting if delegate does not wish to handle the merge.
+ *
+ * Revision 1.76 2002/03/26 21:46:06 mpowers
+ * Contributing EditingContext as a java-friendly convenience.
+ *
+ * Revision 1.75 2002/03/06 16:14:57 mpowers
+ * More attempts at ignoring update conflicts that come from ourself.
+ *
+ * Revision 1.74 2002/02/21 21:57:50 mpowers
+ * Implemented default merge behavior.
+ *
+ * Revision 1.73 2002/02/20 16:46:54 mpowers
+ * Implemented better support for EOEditingContext.Delegate.
+ *
+ * Revision 1.70 2002/02/19 22:26:05 mpowers
+ * Implemented EOEditingContext.MessageHandler support.
+ *
+ * Revision 1.69 2002/02/19 16:33:42 mpowers
+ * Implemented support for EditingContext.Editor
+ *
+ * Revision 1.68 2002/02/13 22:00:34 mpowers
+ * Fixed: invalidateAllObjects tries to invalidate inserted objects,
+ * typically causing class cast exceptions involving EOTemporaryGlobalID.
+ *
+ * Revision 1.67 2002/02/06 21:15:35 mpowers
+ * No longer refaulting a dirty object when we receive an invalidation notif.
+ *
+ * Revision 1.66 2002/01/08 19:31:03 mpowers
+ * refaultObject now correctly refaults the object.
+ *
+ * Revision 1.65 2001/12/20 18:56:15 mpowers
+ * Refinements to snapshotting and calling processRecentChanges.
+ *
+ * Revision 1.64 2001/12/10 15:11:41 mpowers
+ * Now only tracking a commit snapshot after an object has been modified.
+ *
+ * Revision 1.63 2001/11/14 00:08:10 mpowers
+ * Now marking context changed when objects are inserted or deleted
+ * and when child contexts save their changes into this context.
+ *
+ * Revision 1.62 2001/11/07 14:49:31 mpowers
+ * invalidateAllObjects now handles objects manually discarded in the course
+ * of invalidation.
+ *
+ * Revision 1.61 2001/10/26 20:02:49 mpowers
+ * No longer using NSNotificationQueue: all notifications are posted immed.
+ *
+ * Revision 1.60 2001/10/26 18:37:50 mpowers
+ * Now using NSRunLoop to correctly flush recent changes before delayed
+ * observers and AWT events.
+ *
+ * Revision 1.59 2001/10/23 22:29:59 mpowers
+ * Now posting notifications immediately.
+ * Recent changes are flushed at ObserverPrioritySixth, soon to change.
+ *
+ * Revision 1.58 2001/09/10 14:16:51 mpowers
+ * EditingContexts now relay InvalidatedAllObjectsInStore notifications.
+ *
+ * Revision 1.57 2001/06/18 14:11:15 mpowers
+ * Inserting a deleted object simply cancels the delete operation.
+ *
+ * Revision 1.56 2001/06/07 22:07:59 mpowers
+ * Now handling delete notifications before update notifications.
+ * Eliminated the case where deleted objects were also being put
+ * on the updated list when notifying child contexts.
+ *
+ * Revision 1.55 2001/05/18 21:04:33 mpowers
+ * Reimplemented EditingContext.initializeObject.
+ *
+ * Revision 1.54 2001/05/05 23:05:42 mpowers
+ * Implemented Array Faults.
+ *
+ * Revision 1.53 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.52 2001/05/05 14:11:48 mpowers
+ * Implemented registerClone.
+ *
+ * Revision 1.51 2001/05/04 16:57:56 mpowers
+ * Now correctly transposing references to editing contexts when
+ * cloning/copying between editing contexts.
+ *
+ * Revision 1.50 2001/05/02 17:58:41 mpowers
+ * Removed debugging code, added comments.
+ *
+ * Revision 1.49 2001/05/02 15:47:40 mpowers
+ * Fixed the pernicious problem with reverts: recordObject was recording
+ * a snapshot of the clone before the transposition-copy happened,
+ * so the revert object would lose all of its transposed relationships.
+ *
+ * Revision 1.48 2001/05/02 12:39:05 mpowers
+ * Fixed a nasty problem with transpose-cloning and faultForGlobalID.
+ * Now we're forced to create a deep clone, registered it, and then
+ * transpose it after it has been registered.
+ *
+ * Revision 1.47 2001/04/30 13:15:24 mpowers
+ * Child contexts re-initializing objects invalidated in parent now
+ * propery transpose relationships.
+ *
+ * Revision 1.46 2001/04/29 22:02:45 mpowers
+ * Work on id transposing between editing contexts.
+ *
+ * Revision 1.45 2001/04/29 02:29:31 mpowers
+ * Debugging relationship faulting.
+ *
+ * Revision 1.44 2001/04/28 16:18:44 mpowers
+ * Implementing relationships.
+ *
+ * Revision 1.43 2001/04/28 14:12:23 mpowers
+ * Refactored cloning/copying into KeyValueCodingUtilities.
+ *
+ * Revision 1.42 2001/04/27 00:27:11 mpowers
+ * Provided description to not-implemented exceptions.
+ *
+ * Revision 1.41 2001/04/26 01:16:44 mpowers
+ * Major bug fix so we no longer accumulate objects in the all objects
+ * list with every InvalidateAllObjectsInStore.
+ *
+ * Revision 1.40 2001/04/21 23:07:49 mpowers
+ * Now only broadcasts notifications if there's actually a change.
+ *
+ * Revision 1.39 2001/04/13 16:33:11 mpowers
+ * Corrected the refaulting behavior.
+ *
+ * Revision 1.38 2001/04/09 21:42:10 mpowers
+ * Debugging and optimizing notifications.
+ *
+ * Revision 1.37 2001/04/08 20:59:47 mpowers
+ * objectsForFetchSpecification now relies on faultForGlobalID.
+ *
+ * Revision 1.36 2001/04/03 20:36:01 mpowers
+ * Fixed refaulting/reverting/invalidating to be self-consistent.
+ *
+ * Revision 1.35 2001/03/29 03:29:49 mpowers
+ * Now using KeyValueCoding and Support instead of Introspector.
+ *
+ * Revision 1.34 2001/03/28 14:06:29 mpowers
+ * Implemented snapshots. Revert now uses snapshots.
+ *
+ * Revision 1.33 2001/03/20 23:20:33 mpowers
+ * invalidating all objects now sets the dirty flag to false.
+ *
+ * Revision 1.32 2001/03/19 21:44:36 mpowers
+ * Reverts reinitialize for now.
+ * Testing for inserted objects instead of temp id when invalidating object.
+ *
+ * Revision 1.31 2001/03/15 21:10:26 mpowers
+ * Implemented global id re-registration for newly saved inserts.
+ *
+ * Revision 1.30 2001/03/13 21:41:34 mpowers
+ * Broadcasting willChange for any change to hasChanges.
+ * Fixed major bug with inserted objects treated as updated objects
+ * in child display groups.
+ *
+ * Revision 1.29 2001/03/09 22:10:30 mpowers
+ * Fine tuned initializeObject.
+ *
+ * Revision 1.28 2001/03/06 23:23:55 mpowers
+ * objectForGlobalID now returns null if not found.
+ * objectsForFetchSpecification again does things the old way, for now.
+ *
+ * Revision 1.27 2001/03/02 16:31:45 mpowers
+ * Trying to better handle fetches from child contexts.
+ * No longer trying to invalidate temporary objects.
+ *
+ * Revision 1.26 2001/02/28 16:25:19 mpowers
+ * Now calling globalIDForObject internally.
+ *
+ * Revision 1.25 2001/02/27 17:36:55 mpowers
+ * Objects inserted from child now preserve the existing temporary id.
+ *
+ * Revision 1.24 2001/02/27 02:11:17 mpowers
+ * Now throwing exception when cloning fails.
+ * Removed debugging printlns.
+ *
+ * Revision 1.23 2001/02/26 22:41:51 mpowers
+ * Implemented null placeholder classes.
+ * Duplicator now uses NSNull.
+ * No longer catching base exception class.
+ *
+ * Revision 1.22 2001/02/26 21:18:45 mpowers
+ * Now marking edited objects from child contexts that were not already
+ * recorded in parent as changed in saveChangesInEditingContext.
+ *
+ * Revision 1.21 2001/02/26 15:53:22 mpowers
+ * Fine-tuning notification firing.
+ * Child display groups now update properly after parent save or invalidate.
+ *
+ * Revision 1.20 2001/02/24 17:03:22 mpowers
+ * Implemented the notification queue, and changed editing context to use it.
+ *
+ * Revision 1.19 2001/02/23 23:44:15 mpowers
+ * Fine-tuning notification handling.
+ *
+ * Revision 1.18 2001/02/22 22:56:57 mpowers
+ * Only refaulting edited objects on parent store invalidateAll.
+ *
+ * Revision 1.17 2001/02/22 20:54:39 mpowers
+ * Implemented notification handling.
+ *
+ * Revision 1.16 2001/02/21 22:10:55 mpowers
+ * Editing context is now posting appropriate notifications.
+ *
+ * Revision 1.15 2001/02/21 21:17:32 mpowers
+ * Now retaining a reference to the recent changes observer.
+ * Better documented need to retain reference.
+ * Started implementing notifications.
+ *
+ * Revision 1.14 2001/02/20 17:24:22 mpowers
+ * Now using reference keys in objectToID.
+ *
+ * Revision 1.13 2001/02/20 16:45:36 mpowers
+ * Child data sources now accept a data source instead of an editing context
+ * for more flexibility. Child data sources now forward relationship
+ * methods to parent source.
+ *
+ * Revision 1.12 2001/02/16 22:51:29 mpowers
+ * Now deep-cloning objects passed between editing contexts.
+ *
+ * Revision 1.11 2001/02/16 18:34:19 mpowers
+ * Implementing nested contexts.
+ *
+ * Revision 1.9 2001/02/15 21:13:30 mpowers
+ * First draft implementation is complete. Now on to debugging.
+ *
+ * Revision 1.7 2001/02/13 23:24:29 mpowers
+ * Implementing more of editing context.
+ *
+ * Revision 1.4 2001/02/09 22:09:34 mpowers
+ * Completed implementation of EOObjectStore.
+ *
+ * Revision 1.3 2001/02/06 15:24:11 mpowers
+ * Widened parameters on abstract method to fix build.
+ *
+ * Revision 1.2 2001/02/05 03:45:37 mpowers
+ * Starting work on EOEditingContext.
+ *
+ * Revision 1.1.1.1 2000/12/21 15:46:42 mpowers
+ * Contributing wotonomy.
+ *
+ */
+
+
diff --git a/projects/net.wotonomy.persistence/src/main/java/net/wotonomy/control/EOEnterpriseObject.java b/projects/net.wotonomy.persistence/src/main/java/net/wotonomy/control/EOEnterpriseObject.java
new file mode 100644
index 0000000..112531c
--- /dev/null
+++ b/projects/net.wotonomy.persistence/src/main/java/net/wotonomy/control/EOEnterpriseObject.java
@@ -0,0 +1,219 @@
+/*
+Wotonomy: OpenStep design patterns for pure Java applications.
+Copyright (C) 2001 Michael Powers
+
+This library is free software; you can redistribute it and/or
+modify it under the terms of the GNU Lesser General Public
+License as published by the Free Software Foundation; either
+version 2.1 of the License, or (at your option) any later version.
+
+This library is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+Lesser General Public License for more details.
+
+You should have received a copy of the GNU Lesser General Public
+License along with this library; if not, see http://www.gnu.org
+*/
+
+package net.wotonomy.control;
+
+import net.wotonomy.foundation.NSArray;
+import net.wotonomy.foundation.NSDictionary;
+
+/**
+* EOEnterpriseObject defines the required methods a data object
+* must implement to take full advantage of the control package.
+*
+* @author michael@mpowers.net
+* @author $Author: cgruber $
+* @version $Revision: 894 $
+*/
+public interface EOEnterpriseObject
+ extends EOKeyValueCodingAdditions,
+ EORelationshipManipulation,
+ EODeferredFaulting,
+ EOValidation
+{
+ /**
+ * Returns a List of all property keys defined on this object.
+ * This includes both attributes and relationships.
+ */
+ NSArray allPropertyKeys();
+
+ /**
+ * Returns a list of all attributes defined on this object.
+ * Attributes are all properties that are not relationships.
+ */
+ NSArray attributeKeys();
+
+ //void awakeFromClientUpdate(EOEditingContext aContext)
+
+ /**
+ * Called when the object has first been fetched into the
+ * specified editing context.
+ */
+ void awakeFromFetch(EOEditingContext anEditingContext);
+
+ /**
+ * Called when the object has been inserted into the
+ * specified editing context.
+ */
+ void awakeFromInsertion(EOEditingContext anEditingContext);
+
+ /**
+ * Returns a Map representing the delta of the current state
+ * from the state represented in the specified snapshot.
+ * The result will contain only the keys that have changed
+ * and their values. Relationship keys will map to an NSArray
+ * that contains an NSArray of added objects and an NSArray
+ * of removed objects, in that order.
+ */
+ NSDictionary changesFromSnapshot(NSDictionary snapshot);
+
+ /**
+ * Returns a class description for this object.
+ */
+ EOClassDescription classDescription();
+
+ /**
+ * Returns a class description for the object at the
+ * other end of the specified relationship key.
+ */
+ EOClassDescription classDescriptionForDestinationKey(String aKey);
+
+ /**
+ * Clears all property values for this object.
+ * This method is called to clean-up an object that
+ * will no longer be used, and implementations should
+ * ensure that all references are set to null to
+ * prevent problems with garbage-collection.
+ */
+ void clearProperties();
+
+ /**
+ * Returns the delete rule constant defined on EOClassDescription
+ * for the relationship defined by the specified key.
+ */
+ int deleteRuleForRelationshipKey(String aRelationshipKey);
+
+ /**
+ * Returns the editing context in which this object is registered.
+ */
+ EOEditingContext editingContext();
+
+ /**
+ * Returns the name of the entity that this object represents.
+ */
+ String entityName();
+
+ /**
+ * Returns a String containing all property keys and values for
+ * this object. Relationships should be represented by calling
+ * eoShallowDescription() on the object.
+ */
+ String eoDescription();
+
+ /**
+ * Returns a String containing all attribute keys and values for
+ * this object. Relationships are not included.
+ */
+ String eoShallowDescription();
+
+ /**
+ * Returns the key used to reference this object on the
+ * object at the other end of the specified relationship.
+ */
+ String inverseForRelationshipKey(String aRelationshipKey);
+
+ //Object invokeRemoteMethod(
+ // String aMethodName, Class[] aTypeArray Object[] anArgumentArray)
+
+ /**
+ * Returns whether the specified relationship key represents
+ * a to-many relationship.
+ */
+ boolean isToManyKey(String aKey);
+
+ /**
+ * Returns whether the objects at the other end of the specified
+ * relationship should be deleted when this object is deleted.
+ */
+ boolean ownsDestinationObjectsForRelationshipKey(String aKey);
+
+ //void prepareValuesForClient()
+
+ /**
+ * Called to perform the delete propagation for this object
+ * on the specified editing context. All relationships
+ * should be processed according to their corresponding
+ * delete rule.
+ */
+ void propagateDeleteWithEditingContext(EOEditingContext aContext);
+
+ /**
+ * Applies the changes from the specified snapshot to
+ * this object.
+ * @see #changesFromSnapshot(NSDictionary)
+ */
+ void reapplyChangesFromDictionary(NSDictionary aDeltaSnapshot);
+
+ /**
+ * Returns a snapshot of the current state of this object.
+ * All property keys are mapped to their values; nulls are
+ * represented by NSNull.
+ */
+ NSDictionary snapshot();
+
+ /**
+ * Returns a List of the to-many relationship keys
+ * for this object.
+ */
+ NSArray toManyRelationshipKeys();
+
+ /**
+ * Returns a List of the to-one relationship keys
+ * for this object.
+ */
+ NSArray toOneRelationshipKeys();
+
+ /**
+ * Applies the specified snapshot to this object,
+ * converting NSNulls to null and calling
+ * takeStoredValueForKey for each key in the Map.
+ */
+ void updateFromSnapshot(NSDictionary aSnapshot);
+
+ /**
+ * Returns a short, stateful string representation
+ * of this object.
+ */
+ String userPresentableDescription();
+
+ /**
+ * This method should be implemented to call
+ * EOObserverCenter.objectWillChange( this ),
+ * and it should be called by each setter method
+ * on this object before changes are made to the
+ * object's internal state.
+ */
+ void willChange();
+}
+
+/*
+ * $Log$
+ * Revision 1.2 2006/02/16 16:47:14 cgruber
+ * Move some classes in to "internal" packages and re-work imports, etc.
+ *
+ * Also use UnsupportedOperationExceptions where appropriate, instead of WotonomyExceptions.
+ *
+ * Revision 1.1 2006/02/16 13:19:57 cgruber
+ * Check in all sources in eclipse-friendly maven-enabled packages.
+ *
+ * Revision 1.1 2001/11/13 04:13:59 mpowers
+ * Added interfaces needed to begin work on EOCustomObject.
+ *
+ *
+ */
+
+
diff --git a/projects/net.wotonomy.persistence/src/main/java/net/wotonomy/control/EOFaultHandler.java b/projects/net.wotonomy.persistence/src/main/java/net/wotonomy/control/EOFaultHandler.java
new file mode 100644
index 0000000..19dc2df
--- /dev/null
+++ b/projects/net.wotonomy.persistence/src/main/java/net/wotonomy/control/EOFaultHandler.java
@@ -0,0 +1,102 @@
+/*
+Wotonomy: OpenStep design patterns for pure Java applications.
+Copyright (C) 2001 Michael Powers
+
+This library is free software; you can redistribute it and/or
+modify it under the terms of the GNU Lesser General Public
+License as published by the Free Software Foundation; either
+version 2.1 of the License, or (at your option) any later version.
+
+This library is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+Lesser General Public License for more details.
+
+You should have received a copy of the GNU Lesser General Public
+License along with this library; if not, see http://www.gnu.org
+*/
+
+package net.wotonomy.control;
+
+/**
+* EOFaultHandler defines the contract for objects that can
+* create and populate faults. In wotonomy, this interface is
+* currently only a marker interface.
+*
+* @author michael@mpowers.net
+* @author $Author: cgruber $
+* @version $Revision: 893 $
+*/
+public abstract class EOFaultHandler {
+
+ protected Class _targetClass;
+
+ public EOFaultHandler() {
+ super();
+ }
+
+ public static EOFaultHandler handlerForFault(Object obj) {
+ if (!(obj instanceof EOFaulting))
+ throw new IllegalArgumentException("Object must implement EOFaulting");
+ return ((EOFaulting)obj).faultHandler();
+ }
+
+ public static boolean isFault(Object obj) {
+ if (obj == null)
+ return false;
+ boolean isit = (obj instanceof EOFaulting);
+ if (isit)
+ isit = ((EOFaulting)obj).isFault();
+ return isit;
+ }
+
+ public static void makeObjectIntoFault(Object obj, EOFaultHandler handler) {
+ if (!(obj instanceof EOFaulting))
+ throw new IllegalArgumentException("Object must implement EOFaulting");
+ ((EOFaulting)obj).turnIntoFault(handler);
+ }
+
+ public static void clearFault(Object obj) {
+ if (!(obj instanceof EOFaulting))
+ throw new IllegalArgumentException("Object must implement EOFaulting");
+ ((EOFaulting)obj).clearFault();
+ }
+
+ public Class targetClass() {
+ return _targetClass;
+ }
+
+ public Object createFaultForDeferredFault(Object fault, EOEnterpriseObject source) {
+ return fault;
+ }
+
+ public String descriptionForObject(Object obj) {
+ if (obj == null)
+ return "<null>";
+ return obj.toString();
+ }
+
+ public String eoShallowDescription(Object obj) {
+ return null;
+ }
+
+ public abstract void completeInitializationOfObject(Object obj);
+
+ public abstract void faultWillFire(Object obj);
+
+}
+
+/*
+ * $Log$
+ * Revision 1.1 2006/02/16 13:19:57 cgruber
+ * Check in all sources in eclipse-friendly maven-enabled packages.
+ *
+ * Revision 1.2 2003/08/19 01:53:12 chochos
+ * EOObjectStore had some incompatible return types (Object instead of EOEnterpriseObject, in fault methods mostly). It's internally consistent but I hope it doesn't break anything based on this, even though fault methods mostly throw exceptions for now.
+ *
+ * Revision 1.1 2001/11/13 04:13:59 mpowers
+ * Added interfaces needed to begin work on EOCustomObject.
+ *
+ */
+
+
diff --git a/projects/net.wotonomy.persistence/src/main/java/net/wotonomy/control/EOFaulting.java b/projects/net.wotonomy.persistence/src/main/java/net/wotonomy/control/EOFaulting.java
new file mode 100644
index 0000000..95b3e35
--- /dev/null
+++ b/projects/net.wotonomy.persistence/src/main/java/net/wotonomy/control/EOFaulting.java
@@ -0,0 +1,79 @@
+/*
+Wotonomy: OpenStep design patterns for pure Java applications.
+Copyright (C) 2001 Michael Powers
+
+This library is free software; you can redistribute it and/or
+modify it under the terms of the GNU Lesser General Public
+License as published by the Free Software Foundation; either
+version 2.1 of the License, or (at your option) any later version.
+
+This library is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+Lesser General Public License for more details.
+
+You should have received a copy of the GNU Lesser General Public
+License along with this library; if not, see http://www.gnu.org
+*/
+
+package net.wotonomy.control;
+
+/**
+* EOFaulting defines the requirements for objects that
+* can be faulted by an EOFaultManager.
+*
+* @author michael@mpowers.net
+* @author $Author: cgruber $
+* @version $Revision: 893 $
+*/
+public interface EOFaulting
+{
+ /**
+ * Called by EOFaultHandler to prepare the object to be turned into a fault.
+ */
+ void clearFault();
+
+ /**
+ * Returns this object's EOFaultHandler.
+ */
+ EOFaultHandler faultHandler();
+
+ /**
+ * Returns whether this object is currently a fault.
+ * Returns true if this object has not yet retrieved any values.
+ */
+ boolean isFault();
+
+ /**
+ * Turns this object into a fault using the specified fault handler.
+ */
+ void turnIntoFault( EOFaultHandler aFaultHandler );
+
+ /**
+ * Called to completely fire the fault, reading all values.
+ * This method may be implemented to call willRead(null).
+ */
+ void willRead();
+
+ /**
+ * Called to fire the fault for the specified key.
+ * The fault manager is required to populate the specified key
+ * with a value, and may populate any or all of the other values
+ * on this object. A null key will populate all values on the object.
+ * NOTE: This method is not part of the specification.
+ */
+ void willRead( String aKey );
+}
+
+/*
+ * $Log$
+ * Revision 1.1 2006/02/16 13:19:57 cgruber
+ * Check in all sources in eclipse-friendly maven-enabled packages.
+ *
+ * Revision 1.1 2001/11/13 04:13:59 mpowers
+ * Added interfaces needed to begin work on EOCustomObject.
+ *
+ *
+ */
+
+
diff --git a/projects/net.wotonomy.persistence/src/main/java/net/wotonomy/control/EOFetchSpecification.java b/projects/net.wotonomy.persistence/src/main/java/net/wotonomy/control/EOFetchSpecification.java
new file mode 100644
index 0000000..71f3b78
--- /dev/null
+++ b/projects/net.wotonomy.persistence/src/main/java/net/wotonomy/control/EOFetchSpecification.java
@@ -0,0 +1,565 @@
+/*
+Wotonomy: OpenStep design patterns for pure Java applications.
+Copyright (C) 2001 Michael Powers
+
+This library is free software; you can redistribute it and/or
+modify it under the terms of the GNU Lesser General Public
+License as published by the Free Software Foundation; either
+version 2.1 of the License, or (at your option) any later version.
+
+This library is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+Lesser General Public License for more details.
+
+You should have received a copy of the GNU Lesser General Public
+License along with this library; if not, see http://www.gnu.org
+*/
+
+package net.wotonomy.control;
+
+import java.util.Collection;
+import java.util.List;
+import java.util.Map;
+
+import net.wotonomy.foundation.NSArray;
+import net.wotonomy.foundation.NSDictionary;
+import net.wotonomy.foundation.NSMutableArray;
+import net.wotonomy.foundation.NSMutableDictionary;
+import net.wotonomy.foundation.NSSelector;
+import net.wotonomy.foundation.internal.WotonomyException;
+
+/**
+* EOFetchSpecification defines the parameters used to request
+* objects from an EOObjectStore. They are commonly created
+* and passed to a EODataSource which fetches from its
+* EOEditingContext, which passes the call up to its root
+* EOObjectStore's objectsWithFetchSpecification method.
+*
+* @author michael@mpowers.net
+* @author $Author: cgruber $
+* @version $Revision: 894 $
+*/
+public class EOFetchSpecification implements EOKeyValueArchiving {
+ private boolean fetchesRawRows;
+ private String entityName;
+ private NSDictionary hints;
+ private boolean deep;
+ private int fetchLimit;
+ private boolean locksObjects;
+ private NSArray prefetchingRelationshipKeyPaths;
+ private boolean promptsAfterFetchLimit;
+ private EOQualifier qualifier;
+ private NSArray rawRowKeyPaths;
+ private boolean refreshesRefetchedObjects;
+ private boolean requiresAllQualifierBindingVariables;
+ private NSArray sortOrderings;
+ private boolean distinct;
+
+ /**
+ * Default constructor initializes internal state.
+ */
+ public EOFetchSpecification()
+ {
+ fetchesRawRows = false;
+ entityName = null;
+ hints = null;
+ deep = true;
+ fetchLimit = 0;
+ locksObjects = false;
+ prefetchingRelationshipKeyPaths = null;
+ promptsAfterFetchLimit = false;
+ qualifier = null;
+ rawRowKeyPaths = null;
+ refreshesRefetchedObjects = false;
+ requiresAllQualifierBindingVariables = false;
+ sortOrderings = null;
+ distinct = false;
+ }
+
+ /**
+ * Constructs a fetch specification for the specified entity type using
+ * the specified qualifier and sort ordering.
+ */
+ public EOFetchSpecification( String anEntityName, EOQualifier aQualifier, List aSortOrderingList )
+ {
+ this();
+ entityName = anEntityName;
+ qualifier = aQualifier;
+ sortOrderings = new NSArray( (Collection) aSortOrderingList );
+ }
+
+ /**
+ * Constructs a fetch specification for the specified entity type using
+ * the specified qualifier and sort ordering, distinct flag, deep flag,
+ * and hints dictionary.
+ */
+ public EOFetchSpecification( String anEntityName, EOQualifier aQualifier, NSArray aSortOrderingList,
+ boolean usesDistinct, boolean isDeep, Map aHintMap)
+ {
+ this();
+ entityName = anEntityName;
+ qualifier = aQualifier;
+ sortOrderings = new NSArray( (Collection) aSortOrderingList );
+ distinct = usesDistinct;
+ deep = isDeep;
+ hints = new NSMutableDictionary( (Map) aHintMap );
+ }
+
+ /**
+ * Convenience to return the named fetch specification from the class description
+ * corresponding to the specified entity name. Returns null if either entityName
+ * or spec name cannot be resolved.
+ */
+ public static EOFetchSpecification fetchSpecificationNamed( String name, String entityName)
+ {
+ EOClassDescription classDesc = EOClassDescription.classDescriptionForEntityName( entityName );
+ if ( classDesc == null ) return null;
+ return classDesc.fetchSpecificationNamed( name );
+ }
+
+ /**
+ * Implemented to return a new fetch specification that is a deep copy of this one.
+ */
+ public Object clone()
+ {
+ EOFetchSpecification clone = new EOFetchSpecification();
+
+ clone.fetchesRawRows = this.fetchesRawRows;
+ clone.entityName = this.entityName;
+ if ( this.hints != null )
+ clone.hints = new NSDictionary( (Map) this.hints );
+ clone.deep = this.deep;
+ clone.locksObjects = this.locksObjects;
+ if ( this.prefetchingRelationshipKeyPaths != null )
+ clone.prefetchingRelationshipKeyPaths =
+ new NSArray( (List) prefetchingRelationshipKeyPaths );
+ clone.promptsAfterFetchLimit = this.promptsAfterFetchLimit;
+ if ( this.qualifier != null )
+ clone.qualifier = this.qualifier; //FIXME: probably should clone?
+ if ( this.rawRowKeyPaths != null )
+ clone.rawRowKeyPaths = new NSArray( (List) this.rawRowKeyPaths );
+ clone.refreshesRefetchedObjects = this.refreshesRefetchedObjects;
+ clone.requiresAllQualifierBindingVariables =
+ this.requiresAllQualifierBindingVariables;
+ if ( this.sortOrderings != null )
+ clone.sortOrderings = new NSArray( (List) this.sortOrderings );
+ clone.distinct = this.distinct;
+
+ return clone;
+ }
+
+ /**
+ * Returns the name of the entity fetched by this fetch spec.
+ */
+ public String entityName()
+ {
+ return entityName;
+ }
+
+ /**
+ * Returns the current fetch limit.
+ * A fetch limit of zero indicates no fetch limit.
+ * Zero is the default.
+ */
+ public int fetchLimit()
+ {
+ return fetchLimit;
+ }
+
+ /**
+ * Returns whether this fetch spec will fetch raw rows.
+ * Default is false.
+ */
+ public boolean fetchesRawRows()
+ {
+ return fetchesRawRows;
+ }
+
+ /**
+ * Returns a fetch specification that resolves the bindings
+ * in the specified map.
+ */
+ public EOFetchSpecification
+ fetchSpecificationWithQualifierBindings(Map aBindingMap)
+ {
+ throw new WotonomyException( "Not implemented yet" );
+ }
+
+ /**
+ * Returns a Map containing the hints used by this fetch specification,
+ * or null if no hints have been specified.
+ */
+ public NSDictionary hints()
+ {
+ if ( hints == null ) return null;
+ return new NSDictionary( (NSDictionary) hints );
+ }
+
+ /**
+ * Returns whether entities related to the primary
+ * entities are fetched by this fetch spec. If true, all relationships
+ * whose destinations meet the qualifier criteria will be returned
+ * in addition to primary results. If false, only the primary entities
+ * will be returned. Default is true.
+ */
+ public boolean isDeep()
+ {
+ return deep;
+ }
+
+ /**
+ * Returns whether this data source should lock objects that
+ * are fetched. Default is false.
+ */
+ public boolean locksObjects()
+ {
+ return locksObjects;
+ }
+
+ /***
+ * Returns a List of relationships for the fetched objects that
+ * should also be fetched, or null if no such list has been specified.
+ * Use this to avoid additional calls to the server to fetch
+ * relationships that you know you will use.
+ * NOTE: wotonomy allows you to specify non-relational keys
+ * as well.
+ */
+ public NSArray prefetchingRelationshipKeyPaths()
+ {
+ return prefetchingRelationshipKeyPaths;
+ }
+
+ /**
+ * Returns whether the user should be prompted to continue
+ * when the fetch limit has been exceeded.
+ * Default is false.
+ */
+ public boolean promptsAfterFetchLimit()
+ {
+ return promptsAfterFetchLimit;
+ }
+
+ /**
+ * Returns the qualifier used by this fetch specification,
+ * or null if none has been specified.
+ */
+ public EOQualifier qualifier()
+ {
+ return qualifier;
+ }
+
+ /**
+ * Returns a List of keys or key paths for which
+ * values should be returned when fetching raw rows,
+ * or null if no raw row key paths have been specified.
+ */
+ public NSArray rawRowKeyPaths()
+ {
+ return rawRowKeyPaths;
+ }
+
+ /**
+ * Returns whether fetched objects should replace
+ * modified versions already fetched into an editing context.
+ * If true, those changes will be lost.
+ * Default is false.
+ */
+ public boolean refreshesRefetchedObjects()
+ {
+ return refreshesRefetchedObjects;
+ }
+
+ /**
+ * Returns whether all qualifier bindings must be specified
+ * in order to fetch. If true, an exception is thrown if
+ * unspecified bindings exist. If false, unspecified bindings
+ * will be removed from the qualifier. Default is false.
+ */
+ public boolean requiresAllQualifierBindingVariables()
+ {
+ return requiresAllQualifierBindingVariables;
+ }
+
+ /**
+ * Sets the name of the entity fetched by this spec.
+ */
+ public void setEntityName(String aName)
+ {
+ entityName = aName;
+ }
+
+ /**
+ * Sets whether this fetch spec will return raw rows.
+ */
+ public void setFetchesRawRows(boolean shouldFetchRawRows)
+ {
+ fetchesRawRows = shouldFetchRawRows;
+ }
+
+ /**
+ * Sets the limit on the number of records returned for this fetch spec.
+ * Zero indicates no limit on fetches.
+ */
+ public void setFetchLimit(int aLimit)
+ {
+ fetchLimit = aLimit;
+ }
+
+ /**
+ * Sets the hints passed by this fetch spec.
+ */
+ public void setHints(Map aHintMap)
+ {
+ if ( aHintMap == null )
+ {
+ hints = null;
+ }
+ else
+ {
+ hints = new NSDictionary( (Map) aHintMap );
+ }
+ }
+
+ /**
+ * Sets whether this fetch specification fetches deeply.
+ */
+ public void setIsDeep(boolean isDeep)
+ {
+ deep = isDeep;
+ }
+
+ /**
+ * Sets whether this fetch spec locks objects that
+ * are returned by the fetch.
+ */
+ public void setLocksObjects(boolean shouldLockObjects)
+ {
+ locksObjects = shouldLockObjects;
+ }
+
+ /**
+ * Sets the prefetch key paths that should be used as an optimization
+ * hint to the server. NOTE: wotonomy allows you to specify non-relationship
+ * keys as well.
+ */
+ public void setPrefetchingRelationshipKeyPaths(List aKeyPathList)
+ {
+ if ( aKeyPathList == null )
+ {
+ prefetchingRelationshipKeyPaths = null;
+ }
+ else
+ {
+ prefetchingRelationshipKeyPaths = new NSArray( (List) aKeyPathList );
+ }
+ }
+
+ /**
+ * Sets whether the user should be prompted when the fetch limit has been
+ * reached.
+ */
+ public void setPromptsAfterFetchLimit(boolean shouldPrompt)
+ {
+ promptsAfterFetchLimit = shouldPrompt;
+ }
+
+ /**
+ * Sets the qualifier used by this fetch specification.
+ */
+ public void setQualifier(EOQualifier aQualifier)
+ {
+ qualifier = aQualifier;
+ }
+
+ /**
+ * Sets the key paths to be returned if this fetch spec
+ * is returning raw rows.
+ */
+ public void setRawRowKeyPaths(List aKeyPathList)
+ {
+ if ( aKeyPathList == null )
+ {
+ rawRowKeyPaths = null;
+ }
+ else
+ {
+ rawRowKeyPaths = new NSArray( (List) aKeyPathList );
+ }
+ }
+
+ /**
+ * Sets whether modified objects in an editing context should
+ * be replaced by newer versions returned by this fetch spec.
+ */
+ public void setRefreshesRefetchedObjects(boolean shouldRefresh)
+ {
+ refreshesRefetchedObjects = shouldRefresh;
+ }
+
+ /**
+ * Sets whether this fetch spec should require all bindings to be
+ * resolved before executing.
+ */
+ public void setRequiresAllQualifierBindingVariables(boolean shouldRequireAll)
+ {
+ requiresAllQualifierBindingVariables = shouldRequireAll;
+ }
+
+ /**
+ * Sets the sort orderings used by this fetch spec.
+ */
+ public void setSortOrderings(List aSortList)
+ {
+ if ( aSortList == null )
+ {
+ sortOrderings = null;
+ }
+ else
+ {
+ sortOrderings = new NSArray( (List) aSortList );
+ }
+ }
+
+ /**
+ * Sets whether this fetch spec should return only distinct
+ * objects.
+ */
+ public void setUsesDistinct(boolean shouldUseDistinct)
+ {
+ distinct = shouldUseDistinct;
+ }
+
+ /**
+ * Returns a List of the sort orderings used by this fetch spec,
+ * or null if none have been specified.
+ */
+ public NSArray sortOrderings()
+ {
+ return sortOrderings;
+ }
+
+ /**
+ * Returns a string representation of this fetch specification.
+ */
+ public String toString()
+ {
+ return "[FetchSpecification:qualifier=("+qualifier+"),sortOrderings="+sortOrderings+"]";
+ }
+
+ /**
+ * Returns whether this fetch specification will return only one
+ * reference to each distinct object returned by the fetch.
+ * Default is false.
+ */
+ public boolean usesDistinct()
+ {
+ return distinct;
+ }
+
+ public void encodeWithKeyValueArchiver(EOKeyValueArchiver arch) {
+ arch.encodeObject("EOFetchSpecification", "class");
+ arch.encodeObject(entityName(), "entityName");
+ arch.encodeInt(fetchLimit(), "fetchLimit");
+
+ //flags
+ if (isDeep())
+ arch.encodeObject("YES", "isDeep");
+ arch.encodeObject(qualifier(), "qualifier");
+ if (refreshesRefetchedObjects())
+ arch.encodeObject("YES", "refreshesRefetchedObjects");
+ if (locksObjects())
+ arch.encodeObject("YES", "locksObjects");
+ if (fetchesRawRows())
+ arch.encodeObject("YES", "fetchesRawRows");
+ if (promptsAfterFetchLimit())
+ arch.encodeObject("YES", "promptsAfterFetchLimit");
+ if (requiresAllQualifierBindingVariables())
+ arch.encodeObject("YES", "requiresAllQualifierBindingVariables");
+ if (usesDistinct())
+ arch.encodeObject("YES", "usesDistinct");
+
+ //encode arrays
+ if (sortOrderings() != null) {
+ NSMutableArray arr = new NSMutableArray(sortOrderings().count());
+ for (int i = 0; i < sortOrderings.count(); i++) {
+ EOSortOrdering so = (EOSortOrdering)sortOrderings().objectAtIndex(i);
+ EOKeyValueArchiver ar2 = new EOKeyValueArchiver();
+ so.encodeWithKeyValueArchiver(ar2);
+ arr.addObject(ar2.dictionary());
+ }
+ arch.encodeObject(arr, "sortOrderings");
+ }
+ if (rawRowKeyPaths != null && rawRowKeyPaths.count() > 0)
+ arch.encodeObject(rawRowKeyPaths, "rawRowKeyPaths");
+ if (prefetchingRelationshipKeyPaths != null && prefetchingRelationshipKeyPaths.count() > 0)
+ arch.encodeObject(rawRowKeyPaths, "prefetchingRelationshipKeyPaths");
+ if (hints != null && hints.count() > 0)
+ arch.encodeObject(hints, "hints");
+ }
+
+ public static Object decodeWithKeyValueUnarchiver(EOKeyValueUnarchiver unarch) {
+ EOFetchSpecification fs = new EOFetchSpecification();
+ fs.setEntityName((String)unarch.decodeObjectForKey("entityName"));
+ fs.setFetchLimit(unarch.decodeIntForKey("fetchLimit"));
+ fs.setIsDeep(unarch.decodeBoolForKey("isDeep"));
+ fs.setRefreshesRefetchedObjects(unarch.decodeBoolForKey("refreshesRefetchedObjects"));
+
+ //Sort orderings
+ NSArray arr = (NSArray)unarch.decodeObjectForKey("sortOrderings");
+ if (arr != null && arr.count() > 0) {
+ NSMutableArray orderings = new NSMutableArray(arr.count());
+ for (int i = 0; i < arr.count(); i++) {
+ NSDictionary so = (NSDictionary)arr.objectAtIndex(i);
+ String selname = (String)so.objectForKey("selectorName");
+ NSSelector selector = EOSortOrdering.CompareAscending;
+ if (selname.startsWith("compareDescending"))
+ selector = EOSortOrdering.CompareDescending;
+ else if (selname.startsWith("compareCaseInsensitiveAscending"))
+ selector = EOSortOrdering.CompareCaseInsensitiveAscending;
+ else if (selname.startsWith("compareCaseInsensitiveDescending"))
+ selector = EOSortOrdering.CompareCaseInsensitiveDescending;
+ EOSortOrdering eoso = new EOSortOrdering((String)so.objectForKey("key"), selector);
+ orderings.addObject(eoso);
+ }
+ fs.setSortOrderings(orderings);
+ }
+ //raw rows
+ arr = (NSArray)unarch.decodeObjectForKey("rawRowKeyPaths");
+ if (arr != null && arr.count() > 0) {
+ fs.setFetchesRawRows(true);
+ fs.setRawRowKeyPaths(arr);
+ }
+ //qualifier
+ fs.setQualifier((EOQualifier)unarch.decodeObjectForKey("qualifier"));
+ return fs;
+ }
+
+}
+
+/*
+ * $Log$
+ * Revision 1.2 2006/02/16 16:47:14 cgruber
+ * Move some classes in to "internal" packages and re-work imports, etc.
+ *
+ * Also use UnsupportedOperationExceptions where appropriate, instead of WotonomyExceptions.
+ *
+ * Revision 1.1 2006/02/16 13:19:57 cgruber
+ * Check in all sources in eclipse-friendly maven-enabled packages.
+ *
+ * Revision 1.4 2003/08/11 18:19:01 chochos
+ * encoding/decoding with EOKeyValueArchiving now works properly
+ *
+ * Revision 1.3 2003/08/09 01:22:20 chochos
+ * implements EOKeyValueArchiving (and unarchiving)
+ *
+ * Revision 1.2 2001/11/24 17:32:57 mpowers
+ * We now have a real implementation.
+ *
+ * Revision 1.1 2001/02/05 03:45:37 mpowers
+ * Starting work on EOEditingContext.
+ *
+ *
+ */
+
+
diff --git a/projects/net.wotonomy.persistence/src/main/java/net/wotonomy/control/EOGenericRecord.java b/projects/net.wotonomy.persistence/src/main/java/net/wotonomy/control/EOGenericRecord.java
new file mode 100644
index 0000000..4358ecf
--- /dev/null
+++ b/projects/net.wotonomy.persistence/src/main/java/net/wotonomy/control/EOGenericRecord.java
@@ -0,0 +1,151 @@
+/*
+Wotonomy: OpenStep design patterns for pure Java applications.
+Copyright (C) 2001 Intersect Software Corporation
+
+This library is free software; you can redistribute it and/or
+modify it under the terms of the GNU Lesser General Public
+License as published by the Free Software Foundation; either
+version 2.1 of the License, or (at your option) any later version.
+
+This library is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+Lesser General Public License for more details.
+
+You should have received a copy of the GNU Lesser General Public
+License along with this library; if not, see http://www.gnu.org
+*/
+
+package net.wotonomy.control;
+
+import net.wotonomy.foundation.NSMutableDictionary;
+import net.wotonomy.foundation.NSNull;
+
+/**
+* EOGenericRecord extends EOCustomObject to provide a
+* general-purpose implementation, relying entirely on the
+* class description for entity-specific behavior, and
+* storing data in a dictionary.
+*
+* @author michael@mpowers.net
+* @author $Author: cgruber $
+* @version $Revision: 894 $
+*/
+public class EOGenericRecord extends EOCustomObject
+{
+ private EOClassDescription classDescription;
+ private NSMutableDictionary dataDictionary;
+
+ /**
+ * Default constructor.
+ */
+ protected EOGenericRecord()
+ {
+ classDescription = null;
+ dataDictionary = new NSMutableDictionary();
+ }
+
+ /**
+ * Preferred constructor.
+ */
+ public EOGenericRecord( EOClassDescription aDescription )
+ {
+ this();
+ classDescription = aDescription;
+ }
+
+ /**
+ * Overridden to return true so that deferred faults are used.
+ */
+ public static boolean usesDeferredFaultCreation()
+ {
+ return true;
+ }
+
+ /**
+ * Compatibility constructor: aContext and anID are ignored.
+ */
+ public EOGenericRecord(
+ EOEditingContext aContext, EOClassDescription aDescription, EOGlobalID anID )
+ {
+ this( aDescription );
+ }
+
+ /**
+ * Returns a class description for this object.
+ * Overridden to return the class description passed
+ * into the constructor.
+ */
+ public EOClassDescription classDescription()
+ {
+ return classDescription;
+ }
+
+ // interface EOKeyValueCoding
+
+ /**
+ * Calls storedValueForKey.
+ */
+ public Object valueForKey( String aKey )
+ {
+ return storedValueForKey( aKey );
+ }
+
+ /**
+ * Calls willChange and then calls takeStoredValueForKey.
+ */
+ public void takeValueForKey( Object aValue, String aKey )
+ {
+ willChange();
+ takeStoredValueForKey( aValue, aKey );
+ }
+
+ /**
+ * Calls willRead for the specified key,
+ * and returns the corresponding value from
+ * the data dictionary. Keys that do not
+ * exist will return null.
+ */
+ public Object storedValueForKey( String aKey )
+ {
+ willRead( aKey );
+ Object result = dataDictionary.objectForKey( aKey );
+ if ( NSNull.nullValue().equals( result ) ) result = null;
+ return result;
+ }
+
+ /**
+ * Writes the specified value into the data dictionary
+ * for the specified key. Nulls are stored as NSNulls
+ * in the dictionary.
+ * No checking is performed to determine whether the
+ * key is a valid attribute key.
+ */
+ public void takeStoredValueForKey( Object aValue, String aKey )
+ {
+ if ( aValue == null ) aValue = NSNull.nullValue();
+ dataDictionary.setObjectForKey( aValue, aKey );
+ }
+
+}
+
+/*
+ * $Log$
+ * Revision 1.2 2006/02/16 16:47:14 cgruber
+ * Move some classes in to "internal" packages and re-work imports, etc.
+ *
+ * Also use UnsupportedOperationExceptions where appropriate, instead of WotonomyExceptions.
+ *
+ * Revision 1.1 2006/02/16 13:19:57 cgruber
+ * Check in all sources in eclipse-friendly maven-enabled packages.
+ *
+ * Revision 1.2 2003/08/06 23:07:52 chochos
+ * general code cleanup (mostly, removing unused imports)
+ *
+ * Revision 1.1 2001/11/18 18:57:10 mpowers
+ * Implemented EOGenericRecord.
+ *
+ *
+ */
+
+
diff --git a/projects/net.wotonomy.persistence/src/main/java/net/wotonomy/control/EOGlobalID.java b/projects/net.wotonomy.persistence/src/main/java/net/wotonomy/control/EOGlobalID.java
new file mode 100644
index 0000000..1b4ccbe
--- /dev/null
+++ b/projects/net.wotonomy.persistence/src/main/java/net/wotonomy/control/EOGlobalID.java
@@ -0,0 +1,83 @@
+/*
+Wotonomy: OpenStep design patterns for pure Java applications.
+Copyright (C) 2001 Michael Powers
+
+This library is free software; you can redistribute it and/or
+modify it under the terms of the GNU Lesser General Public
+License as published by the Free Software Foundation; either
+version 2.1 of the License, or (at your option) any later version.
+
+This library is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+Lesser General Public License for more details.
+
+You should have received a copy of the GNU Lesser General Public
+License along with this library; if not, see http://www.gnu.org
+*/
+
+package net.wotonomy.control;
+
+import java.io.Serializable;
+
+/**
+* A pure java implementation of EOGlobalID.
+*
+* @author michael@mpowers.net
+* @author $Author: cgruber $
+* @version $Revision: 893 $
+*/
+public abstract class EOGlobalID implements Cloneable, Serializable
+{
+ /**
+ * ObjectStores broadcast this notification when they
+ * replace a temporary global id with a permanent one.
+ * EditingContexts listen for these notifications to
+ * update their mapping of global ids to objects.
+ * The object of the notification is null, and the user
+ * info contains a mapping of the old temporary ids to
+ * the new permanent ids.
+ */
+ public static final String GlobalIDChangedNotification
+ = "GlobalIDChangedNotification";
+
+ /**
+ * Returns whether this id is a temporary id.
+ * Temporary ids are generated for newly created
+ * objects that have not been persisted. When
+ * persisted, the temporary id is discarded in favor
+ * of the one generated by the object store.
+ */
+ public abstract boolean isTemporary();
+
+ /**
+ * Returns a copy of this object.
+ * This implementation calls super.clone().
+ */
+ public Object clone() throws CloneNotSupportedException
+ {
+ return super.clone();
+ }
+}
+
+/*
+ * $Log$
+ * Revision 1.1 2006/02/16 13:19:57 cgruber
+ * Check in all sources in eclipse-friendly maven-enabled packages.
+ *
+ * Revision 1.4 2001/04/29 22:02:45 mpowers
+ * Work on id transposing between editing contexts.
+ *
+ * Revision 1.3 2001/03/15 21:10:26 mpowers
+ * Implemented global id re-registration for newly saved inserts.
+ *
+ * Revision 1.2 2001/02/15 21:13:30 mpowers
+ * First draft implementation is complete. Now on to debugging.
+ *
+ * Revision 1.1 2001/02/05 03:45:37 mpowers
+ * Starting work on EOEditingContext.
+ *
+ *
+ */
+
+
diff --git a/projects/net.wotonomy.persistence/src/main/java/net/wotonomy/control/EOIntegralKeyGlobalID.java b/projects/net.wotonomy.persistence/src/main/java/net/wotonomy/control/EOIntegralKeyGlobalID.java
new file mode 100644
index 0000000..ee7aac1
--- /dev/null
+++ b/projects/net.wotonomy.persistence/src/main/java/net/wotonomy/control/EOIntegralKeyGlobalID.java
@@ -0,0 +1,72 @@
+/*
+ Wotonomy: OpenStep design patterns for pure Java applications.
+ Copyright (C) 2001 Michael Powers
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, see http://www.gnu.org
+ */
+package net.wotonomy.control;
+
+/**
+*
+* @author ezamudio@nasoft.com
+* @author $Author: cgruber $
+* @version $Revision: 893 $
+*/
+public class EOIntegralKeyGlobalID extends EOKeyGlobalID {
+
+ protected Number keyValue;
+
+ public EOIntegralKeyGlobalID(String entityName, Number value) {
+ super(entityName, 0);
+ keyValue = value;
+ }
+
+ /* (non-Javadoc)
+ * @see net.wotonomy.control.EOKeyGlobalID#keyValues()
+ */
+ public Object[] keyValues() {
+ return new Object[]{ keyValue };
+ }
+
+ /* (non-Javadoc)
+ * @see net.wotonomy.control.EOKeyGlobalID#_keyValuesNoCopy()
+ */
+ public Object[] _keyValuesNoCopy() {
+ return new Object[]{ keyValue };
+ }
+
+ /* (non-Javadoc)
+ * @see net.wotonomy.control.EOKeyGlobalID#keyCount()
+ */
+ public int keyCount() {
+ return 1;
+ }
+
+ /* (non-Javadoc)
+ * @see net.wotonomy.control.EOGlobalID#isTemporary()
+ */
+ public boolean isTemporary() {
+ return false;
+ }
+
+}
+/*
+ * $Log$
+ * Revision 1.1 2006/02/16 13:19:57 cgruber
+ * Check in all sources in eclipse-friendly maven-enabled packages.
+ *
+ * Revision 1.2 2003/08/19 01:59:01 chochos
+ * Added the wotonomy headers
+ *
+ */
diff --git a/projects/net.wotonomy.persistence/src/main/java/net/wotonomy/control/EOKeyComparisonQualifier.java b/projects/net.wotonomy.persistence/src/main/java/net/wotonomy/control/EOKeyComparisonQualifier.java
new file mode 100644
index 0000000..97baaab
--- /dev/null
+++ b/projects/net.wotonomy.persistence/src/main/java/net/wotonomy/control/EOKeyComparisonQualifier.java
@@ -0,0 +1,151 @@
+/*
+Wotonomy: OpenStep design patterns for pure Java applications.
+Copyright (C) 2000 Michael Powers
+
+This library is free software; you can redistribute it and/or
+modify it under the terms of the GNU Lesser General Public
+License as published by the Free Software Foundation; either
+version 2.1 of the License, or (at your option) any later version.
+
+This library is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+Lesser General Public License for more details.
+
+You should have received a copy of the GNU Lesser General Public
+License along with this library; if not, see http://www.gnu.org
+*/
+package net.wotonomy.control;
+
+import net.wotonomy.foundation.NSMutableDictionary;
+import net.wotonomy.foundation.NSSelector;
+import net.wotonomy.foundation.internal.WotonomyException;
+
+/**
+* @author ezamudio@nasoft.com
+* @author $Author: cgruber $
+* @version $Revision: 894 $
+*/
+public class EOKeyComparisonQualifier
+ extends EOQualifier
+ implements EOKeyValueArchiving, EOQualifierEvaluation {
+
+ private String _leftKey;
+ private String _rightKey;
+ private NSSelector _selector;
+
+ public EOKeyComparisonQualifier(String leftKey, NSSelector selector, String rightKey) {
+ super();
+ _leftKey = leftKey;
+ _rightKey = rightKey;
+ _selector = selector;
+ }
+
+ public String leftKey() {
+ return _leftKey;
+ }
+
+ public String rightKey() {
+ return _rightKey;
+ }
+
+ public NSSelector selector() {
+ return _selector;
+ }
+
+ /**
+ * Evaluates this qualifier for the specified object,
+ * and returns whether the object is qualified.
+ * selector() is invoked on the value for key() on the
+ * specified object, with value() as the parameter.
+ */
+ public boolean evaluateWithObject(Object eo) {
+ try {
+ Object lvalue, rvalue;
+ if ( eo instanceof EOKeyValueCoding) {
+ lvalue = ((EOKeyValueCoding)eo).valueForKey(leftKey());
+ rvalue = ((EOKeyValueCoding)eo).valueForKey(rightKey());
+ } else {
+ lvalue = EOKeyValueCodingSupport.valueForKey(eo, leftKey());
+ rvalue = EOKeyValueCodingSupport.valueForKey(eo, rightKey());
+ }
+ return ((Boolean)_selector.invoke( lvalue, rvalue)).booleanValue();
+ } catch (Exception exc) {
+ throw new WotonomyException( exc );
+ }
+ }
+
+ /* (non-Javadoc)
+ * @see net.wotonomy.control.EOKeyValueArchiving#encodeWithKeyValueArchiver(net.wotonomy.control.EOKeyValueArchiver)
+ */
+ public void encodeWithKeyValueArchiver(EOKeyValueArchiver arch) {
+ arch.encodeObject("EOKeyComparisonQualifier", "class");
+ arch.encodeObject(_leftKey, "key");
+ NSMutableDictionary d = new NSMutableDictionary(2);
+ arch.encodeObject(_rightKey, "value");
+ String selname = null;
+ if (_selector.equals(EOQualifier.QualifierOperatorCaseInsensitiveLike))
+ selname = "caseInsensitiveLike:";
+ else if (_selector.equals(EOQualifier.QualifierOperatorContains))
+ selname = "contains:";
+ else if (_selector.equals(EOQualifier.QualifierOperatorEqual))
+ selname = "isEqualTo:";
+ else if (_selector.equals(EOQualifier.QualifierOperatorGreaterThan))
+ selname = "greaterThan:";
+ else if (_selector.equals(EOQualifier.QualifierOperatorGreaterThanOrEqualTo))
+ selname = "greaterThanOrEqualTo:";
+ else if (_selector.equals(EOQualifier.QualifierOperatorLessThan))
+ selname = "lessThan:";
+ else if (_selector.equals(EOQualifier.QualifierOperatorLessThanOrEqualTo))
+ selname = "lessThanOrEqualTo:";
+ else if (_selector.equals(EOQualifier.QualifierOperatorLike))
+ selname = "like:";
+ else if (_selector.equals(EOQualifier.QualifierOperatorNotEqual))
+ selname = "isNotEqualTo:";
+ else
+ selname = _selector.name() + ":";
+ arch.encodeObject(selname, "selectorName");
+ }
+
+ public static Object decodeWithKeyValueArchiver(EOKeyValueUnarchiver arch) {
+ String k = (String)arch.decodeObjectForKey("key");
+ String v = (String)arch.decodeObjectForKey("value");
+ NSSelector sel = null;
+ String sname = (String)arch.decodeObjectForKey("selectorName");
+ if (sname.equals("isEqualTo:"))
+ sel = EOQualifier.QualifierOperatorEqual;
+ else if (sname.equals("isNotEqualTo:"))
+ sel = EOQualifier.QualifierOperatorNotEqual;
+ else if (sname.equals("caseInsensitiveLike:"))
+ sel = EOQualifier.QualifierOperatorCaseInsensitiveLike;
+ else if (sname.equals("contains:"))
+ sel = EOQualifier.QualifierOperatorContains;
+ else if (sname.equals("greaterThan:"))
+ sel = EOQualifier.QualifierOperatorGreaterThan;
+ else if (sname.equals("greaterThanOrEqualTo:"))
+ sel = EOQualifier.QualifierOperatorGreaterThanOrEqualTo;
+ else if (sname.equals("lessThan:"))
+ sel = EOQualifier.QualifierOperatorLessThan;
+ else if (sname.equals("lessThanOrEqualTo:"))
+ sel = EOQualifier.QualifierOperatorLessThanOrEqualTo;
+ else if (sname.equals("like:"))
+ sel = EOQualifier.QualifierOperatorLike;
+ EOKeyComparisonQualifier q = new EOKeyComparisonQualifier(k, sel, v);
+ return q;
+ }
+
+}
+/*
+ * $Log$
+ * Revision 1.2 2006/02/16 16:47:14 cgruber
+ * Move some classes in to "internal" packages and re-work imports, etc.
+ *
+ * Also use UnsupportedOperationExceptions where appropriate, instead of WotonomyExceptions.
+ *
+ * Revision 1.1 2006/02/16 13:19:57 cgruber
+ * Check in all sources in eclipse-friendly maven-enabled packages.
+ *
+ * Revision 1.1 2003/08/12 01:42:36 chochos
+ * this qualifier was missing
+ *
+ */
diff --git a/projects/net.wotonomy.persistence/src/main/java/net/wotonomy/control/EOKeyGlobalID.java b/projects/net.wotonomy.persistence/src/main/java/net/wotonomy/control/EOKeyGlobalID.java
new file mode 100644
index 0000000..76c4d05
--- /dev/null
+++ b/projects/net.wotonomy.persistence/src/main/java/net/wotonomy/control/EOKeyGlobalID.java
@@ -0,0 +1,146 @@
+/*
+Wotonomy: OpenStep design patterns for pure Java applications.
+Copyright (C) 2002 Israfil consulting Services Corporation
+
+This library is free software; you can redistribute it and/or
+modify it under the terms of the GNU Lesser General Public
+License as published by the Free Software Foundation; either
+version 2.1 of the License, or (at your option) any later version.
+
+This library is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+Lesser General Public License for more details.
+
+You should have received a copy of the GNU Lesser General Public
+License along with this library; if not, see http://www.gnu.org
+
+$Id: EOKeyGlobalID.java 894 2006-02-16 16:47:14Z cgruber $
+
+*/
+
+package net.wotonomy.control;
+
+import java.io.ObjectStreamException;
+
+import net.wotonomy.foundation.NSArray;
+import net.wotonomy.foundation.NSCoder;
+import net.wotonomy.foundation.NSCoding;
+
+/**
+* The model object which represents the mapping between database
+* fields and object properties, lists available entities, lists
+* connection dictionaries, and has fetch specifications.
+*
+* @author cgruber@israfil.net
+* @author $Author: cgruber $
+* @version $Revision: 894 $
+*/
+
+public abstract class EOKeyGlobalID extends EOGlobalID
+ implements NSCoding {
+
+ protected String _entityName;
+ protected int _hash;
+
+ protected EOKeyGlobalID(String entityName, int hashCode) {
+ super();
+ _entityName = entityName;
+ _hash = hashCode;
+ }
+
+ protected void _prepClone(EOKeyGlobalID gid) {
+ throw new UnsupportedOperationException("Not Yet Implemented");
+ }
+
+ public static EOKeyGlobalID globalIDWithEntityName(String s, Object aobj[]) {
+ throw new UnsupportedOperationException("Not Yet Implemented");
+ }
+
+ public static EOKeyGlobalID _defaultGlobalIDWithEntityName(String s, Object aobj[]) {
+ throw new UnsupportedOperationException("Not Yet Implemented");
+ }
+
+ public String entityName() {
+ return _entityName;
+ }
+
+ public String _literalEntityName() {
+ throw new UnsupportedOperationException("Not Yet Implemented");
+ }
+
+ public boolean _isFinal() {
+ throw new UnsupportedOperationException("Not Yet Implemented");
+ }
+
+ public void _setGuessedEntityName(String s) {
+ throw new UnsupportedOperationException("Not Yet Implemented");
+ }
+
+ public String _guessedEntityName() {
+ throw new UnsupportedOperationException("Not Yet Implemented");
+ }
+
+ public void _setSubEntityName(String s) {
+ throw new UnsupportedOperationException("Not Yet Implemented");
+ }
+
+ public String _subEntityName() {
+ throw new UnsupportedOperationException("Not Yet Implemented");
+ }
+
+ public int hashCode() {
+ return _hash;
+ }
+
+ public abstract Object[] keyValues();
+
+ public abstract Object[] _keyValuesNoCopy();
+
+ public abstract int keyCount();
+
+ public NSArray keyValuesArray() {
+ return new NSArray(keyValues());
+ }
+
+ public static Object decodeObject(NSCoder nscoder) {
+ throw new UnsupportedOperationException("Not Yet Implemented");
+ }
+
+ public static EOKeyGlobalID _adjustForInheritance(EOKeyGlobalID eokeyglobalid, String s, String s1) {
+ throw new UnsupportedOperationException("Not Yet Implemented");
+ }
+
+ public void encodeWithCoder(NSCoder nscoder) {
+ throw new UnsupportedOperationException("Not Yet Implemented");
+ }
+
+ public Class classForCoder() {
+ throw new UnsupportedOperationException("Not Yet Implemented");
+ }
+
+ protected Object readResolve() throws ObjectStreamException {
+ throw new UnsupportedOperationException("Not Yet Implemented");
+ }
+
+}
+/*
+ * $Log$
+ * Revision 1.2 2006/02/16 16:47:14 cgruber
+ * Move some classes in to "internal" packages and re-work imports, etc.
+ *
+ * Also use UnsupportedOperationExceptions where appropriate, instead of WotonomyExceptions.
+ *
+ * Revision 1.1 2006/02/16 13:19:57 cgruber
+ * Check in all sources in eclipse-friendly maven-enabled packages.
+ *
+ * Revision 1.2 2003/08/19 01:50:54 chochos
+ * added two concrete implementations of EOKeyGlobalID.
+ *
+ * Revision 1.1 2002/07/14 21:59:06 mpowers
+ * Contributions from cgruber.
+ *
+ * Revision 1.1 2002/06/25 00:11:09 cgruber
+ * Add EOKeyGlobalID, but it won't work without NSCoder.
+ *
+ */
diff --git a/projects/net.wotonomy.persistence/src/main/java/net/wotonomy/control/EOKeyValueArchiver.java b/projects/net.wotonomy.persistence/src/main/java/net/wotonomy/control/EOKeyValueArchiver.java
new file mode 100644
index 0000000..3e2f808
--- /dev/null
+++ b/projects/net.wotonomy.persistence/src/main/java/net/wotonomy/control/EOKeyValueArchiver.java
@@ -0,0 +1,106 @@
+/*
+Wotonomy: OpenStep design patterns for pure Java applications.
+Copyright (C) 2001 Michael Powers
+
+This library is free software; you can redistribute it and/or
+modify it under the terms of the GNU Lesser General Public
+License as published by the Free Software Foundation; either
+version 2.1 of the License, or (at your option) any later version.
+
+This library is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+Lesser General Public License for more details.
+
+You should have received a copy of the GNU Lesser General Public
+License along with this library; if not, see http://www.gnu.org
+*/
+package net.wotonomy.control;
+
+import net.wotonomy.foundation.NSArray;
+import net.wotonomy.foundation.NSDictionary;
+import net.wotonomy.foundation.NSMutableDictionary;
+import net.wotonomy.foundation.NSSelector;
+
+/**
+* @author ezamudio@nasoft.com
+* @author $Author: cgruber $
+* @version $Revision: 894 $
+*/
+public class EOKeyValueArchiver {
+
+ //for the delegate
+ public static final NSSelector sel = new NSSelector("referenceToEncodeForObject",
+ new Class[]{ Object.class });
+ NSMutableDictionary dict = new NSMutableDictionary();
+ private Object _delegate;
+
+ public EOKeyValueArchiver() {
+ super();
+ }
+
+ public void encodeBool(boolean flag, String key) {
+ String v = flag ? "true" : "false";
+ dict.setObjectForKey(v, key);
+ }
+
+ public void encodeInt(int value, String key) {
+ dict.setObjectForKey(new Integer(value), key);
+ }
+
+ public void encodeObject(Object obj, String key) {
+ if (obj == null)
+ return;
+ if (obj instanceof NSArray || obj instanceof NSDictionary) {
+ dict.setObjectForKey(obj, key);
+ return;
+ }
+ if (obj instanceof EOKeyValueArchiving) {
+ EOKeyValueArchiver arch = new EOKeyValueArchiver();
+ ((EOKeyValueArchiving)obj).encodeWithKeyValueArchiver(arch);
+ dict.setObjectForKey(arch.dictionary(), key);
+ return;
+ }
+ if (obj instanceof String)
+ dict.setObjectForKey(obj, key);
+ }
+
+ public void encodeReferenceToObject(Object obj, String key) {
+ if (_delegate != null) {
+ if (sel.implementedByObject(obj)) {
+ try {
+ Object ref = sel.invoke(_delegate, obj);
+ if (ref != null)
+ dict.setObjectForKey(ref, key);
+ } catch (Exception e) {
+ }
+ }
+ }
+ }
+
+ public NSDictionary dictionary() {
+ return new NSDictionary(dict);
+ }
+
+ public void setDelegate(Object delegate) {
+ _delegate = delegate;
+ }
+ public Object delegate() {
+ return _delegate;
+ }
+
+}
+/*
+ * $Log$
+ * Revision 1.2 2006/02/16 16:47:14 cgruber
+ * Move some classes in to "internal" packages and re-work imports, etc.
+ *
+ * Also use UnsupportedOperationExceptions where appropriate, instead of WotonomyExceptions.
+ *
+ * Revision 1.1 2006/02/16 13:19:57 cgruber
+ * Check in all sources in eclipse-friendly maven-enabled packages.
+ *
+ * Revision 1.1 2003/08/09 01:17:53 chochos
+ * Part of the EOKeyValueArchiving protocol (archives objects into NSDictionaries and vice-versa)
+ *
+ */ \ No newline at end of file
diff --git a/projects/net.wotonomy.persistence/src/main/java/net/wotonomy/control/EOKeyValueArchiving.java b/projects/net.wotonomy.persistence/src/main/java/net/wotonomy/control/EOKeyValueArchiving.java
new file mode 100644
index 0000000..0d9f78c
--- /dev/null
+++ b/projects/net.wotonomy.persistence/src/main/java/net/wotonomy/control/EOKeyValueArchiving.java
@@ -0,0 +1,41 @@
+/*
+Wotonomy: OpenStep design patterns for pure Java applications.
+Copyright (C) 2001 Michael Powers
+
+This library is free software; you can redistribute it and/or
+modify it under the terms of the GNU Lesser General Public
+License as published by the Free Software Foundation; either
+version 2.1 of the License, or (at your option) any later version.
+
+This library is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+Lesser General Public License for more details.
+
+You should have received a copy of the GNU Lesser General Public
+License along with this library; if not, see http://www.gnu.org
+*/
+package net.wotonomy.control;
+
+/**
+ * Remember that this method is also part of the interface:
+ * public static Object decodeWithKeyValueUnarchiver(EOKeyValueUnarchiver unarchiver)
+ *
+* @author ezamudio@nasoft.com
+* @author $Author: cgruber $
+* @version $Revision: 893 $
+*/
+public interface EOKeyValueArchiving {
+
+ public abstract void encodeWithKeyValueArchiver(EOKeyValueArchiver archiver);
+
+}
+/*
+ * $Log$
+ * Revision 1.1 2006/02/16 13:19:57 cgruber
+ * Check in all sources in eclipse-friendly maven-enabled packages.
+ *
+ * Revision 1.1 2003/08/09 01:17:53 chochos
+ * Part of the EOKeyValueArchiving protocol (archives objects into NSDictionaries and vice-versa)
+ *
+ */ \ No newline at end of file
diff --git a/projects/net.wotonomy.persistence/src/main/java/net/wotonomy/control/EOKeyValueCoding.java b/projects/net.wotonomy.persistence/src/main/java/net/wotonomy/control/EOKeyValueCoding.java
new file mode 100644
index 0000000..b3cf926
--- /dev/null
+++ b/projects/net.wotonomy.persistence/src/main/java/net/wotonomy/control/EOKeyValueCoding.java
@@ -0,0 +1,129 @@
+/*
+Wotonomy: OpenStep design patterns for pure Java applications.
+Copyright (C) 2001 Intersect Software Corporation
+
+This library is free software; you can redistribute it and/or
+modify it under the terms of the GNU Lesser General Public
+License as published by the Free Software Foundation; either
+version 2.1 of the License, or (at your option) any later version.
+
+This library is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+Lesser General Public License for more details.
+
+You should have received a copy of the GNU Lesser General Public
+License along with this library; if not, see http://www.gnu.org
+*/
+
+package net.wotonomy.control;
+
+import net.wotonomy.foundation.NSKeyValueCoding;
+
+/**
+* EOKeyValueCoding defines an interface for classes that
+* need to have more control over the wotonomy's property
+* introspection facilities. <br><br>
+*
+* On an object that implements this interface, wotonomy
+* will call these methods, and otherwise use the static
+* methods on EOKeyValueCodingSupport. <br><br>
+*
+* EOKeyValueCodingSupport implements the default behaviors
+* for each of these methods, so classes implementing this
+* interface can call those methods to acheive the same
+* behavior. <br><br>
+*
+* valueForKey and takeValueForKey are called in response
+* to user actions, like viewing an object or updating its
+* value in a user interface. These should call the public
+* getter and setter methods on the object itself and the
+* operations should be subject to validation. <br><br>
+*
+* storedValueForKey and takeStoredValueForKey are called
+* in response to wotonomy actions, like snapshotting,
+* faulting, commits, and reverts. These operations should
+* bypass the public methods and directly modify the internal
+* state of the object without validation.
+*
+* @author michael@mpowers.net
+* @author $Author: cgruber $
+* @version $Revision: 893 $
+*/
+public interface EOKeyValueCoding extends NSKeyValueCoding
+{
+ /**
+ * Returns the value for the specified property.
+ * If the property does not exist, this method should
+ * call handleQueryWithUnboundKey.
+ */
+ Object valueForKey( String aKey );
+
+ /**
+ * Sets the property to the specified value.
+ * If the property does not exist, this method should
+ * call handleTakeValueForUnboundKey.
+ * If the property is of a type that cannot allow
+ * null (e.g. primitive types) and aValue is null,
+ * this method should call unableToSetNullForKey.
+ */
+ void takeValueForKey( Object aValue, String aKey );
+
+ /**
+ * Returns the value for the private field that
+ * corresponds to the specified property.
+ */
+ Object storedValueForKey( String aKey );
+
+ /**
+ * Sets the the private field that corresponds to the
+ * specified property to the specified value.
+ */
+ void takeStoredValueForKey( Object aValue, String aKey );
+
+ /**
+ * Called by valueForKey when the specified key is
+ * not found on this object. Implementing classes
+ * should handle the specified value or otherwise
+ * throw an exception.
+ */
+ Object handleQueryWithUnboundKey( String aKey );
+
+ /**
+ * Called by takeValueForKey when the specified key
+ * is not found on this object. Implementing classes
+ * should handle the specified value or otherwise
+ * throw an exception.
+ */
+ void handleTakeValueForUnboundKey( Object aValue, String aKey );
+
+ /**
+ * Called by takeValueForKey when the type of the
+ * specified key is not allowed to be null, as is
+ * the case with primitive types. Implementing
+ * classes should handle this case appropriately
+ * or otherwise throw an exception.
+ */
+ void unableToSetNullForKey( String aKey );
+
+}
+
+/*
+ * $Log$
+ * Revision 1.1 2006/02/16 13:19:57 cgruber
+ * Check in all sources in eclipse-friendly maven-enabled packages.
+ *
+ * Revision 1.3 2003/01/16 22:47:30 mpowers
+ * Compatibility changes to support compiling woextensions source.
+ * (34 out of 56 classes compile!)
+ *
+ * Revision 1.2 2001/03/28 16:12:30 mpowers
+ * Documented interface.
+ *
+ * Revision 1.1 2001/03/27 23:25:05 mpowers
+ * Contributing interface, no docs yet.
+ *
+ *
+ */
+
+
diff --git a/projects/net.wotonomy.persistence/src/main/java/net/wotonomy/control/EOKeyValueCodingAdditions.java b/projects/net.wotonomy.persistence/src/main/java/net/wotonomy/control/EOKeyValueCodingAdditions.java
new file mode 100644
index 0000000..bc48f58
--- /dev/null
+++ b/projects/net.wotonomy.persistence/src/main/java/net/wotonomy/control/EOKeyValueCodingAdditions.java
@@ -0,0 +1,176 @@
+/*
+Wotonomy: OpenStep design patterns for pure Java applications.
+Copyright (C) 2001 Intersect Software Corporation
+
+This library is free software; you can redistribute it and/or
+modify it under the terms of the GNU Lesser General Public
+License as published by the Free Software Foundation; either
+version 2.1 of the License, or (at your option) any later version.
+
+This library is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+Lesser General Public License for more details.
+
+You should have received a copy of the GNU Lesser General Public
+License along with this library; if not, see http://www.gnu.org
+*/
+
+package net.wotonomy.control;
+
+import java.util.List;
+import java.util.Map;
+
+import net.wotonomy.foundation.NSDictionary;
+import net.wotonomy.foundation.NSKeyValueCodingAdditions;
+
+/**
+* EOKeyValueCodingAdditions defines an interface for classes
+* that need to have more control over the wotonomy's bulk
+* property copying and cloning facilities. <br><br>
+*
+* @author michael@mpowers.net
+* @author $Author: cgruber $
+* @version $Revision: 894 $
+*/
+public interface EOKeyValueCodingAdditions extends EOKeyValueCoding, NSKeyValueCodingAdditions
+{
+
+ /**
+ * Static utilities methods that
+ * call the appropriate method if the object implements
+ * NSKeyValueCodingAdditions, otherwise calls the method
+ * on DefaultImplementation.
+ */
+ public class Utility extends NSKeyValueCodingAdditions.Utility
+ {
+ /**
+ * Calls the appropriate method if the object implements
+ * NSKeyValueCodingAdditions, otherwise calls the method
+ * on DefaultImplementation.
+ */
+ public static void takeValuesFromDictionary(
+ Object object, Map dictionary)
+ {
+ if (object instanceof NSKeyValueCodingAdditions) {
+ ((NSKeyValueCodingAdditions)object).takeValuesFromDictionary(dictionary);
+ } else {
+ DefaultImplementation.takeValuesFromDictionary(object, dictionary);
+ }
+ }
+
+ /**
+ * Calls the appropriate method if the object implements
+ * NSKeyValueCodingAdditions, otherwise calls the method
+ * on DefaultImplementation.
+ */
+/*
+ public static void takeValuesFromDictionaryWithMapping(
+ Object object, NSDictionary dictionary, NSDictionary mapping)
+ {
+ if (object instanceof NSKeyValueCodingAdditions) {
+ ((NSKeyValueCodingAdditions)object).takeValuesFromDictionaryWithMapping(dictionary, mapping);
+ } else {
+ DefaultImplementation.takeValuesFromDictionaryWithMapping(object, dictionary, mapping);
+ }
+ }
+*/
+
+ /**
+ * Calls the appropriate method if the object implements
+ * NSKeyValueCodingAdditions, otherwise calls the method
+ * on DefaultImplementation.
+ */
+ public static NSDictionary valuesForKeys(
+ Object object, List keys)
+ {
+ if (object instanceof NSKeyValueCodingAdditions) {
+ return ((NSKeyValueCodingAdditions)object).valuesForKeys(keys);
+ } else {
+ return DefaultImplementation.valuesForKeys(object, keys);
+ }
+ }
+
+ /**
+ * Calls the appropriate method if the object implements
+ * NSKeyValueCodingAdditions, otherwise calls the method
+ * on DefaultImplementation.
+ */
+/*
+ public static NSDictionary valuesForKeysWithMapping(
+ Object object, NSDictionary mapping)
+ {
+ if (object instanceof NSKeyValueCodingAdditions) {
+ return ((NSKeyValueCodingAdditions)object).valuesForKeysWithMapping(mapping);
+ } else {
+ return DefaultImplementation.valuesForKeysWithMapping(object, mapping);
+ }
+ }
+*/
+ }
+
+ /**
+ * Provides a reflection-based implementation for classes that
+ * don't implement NSKeyValueCodingAdditions.
+ */
+ public class DefaultImplementation extends NSKeyValueCodingAdditions.DefaultImplementation
+ {
+ public static void takeValuesFromDictionary(
+ Object object, NSDictionary dictionary)
+ {
+ throw new RuntimeException( "Not implemented yet." );
+ }
+
+ public static void takeValuesFromDictionaryWithMapping(
+ Object object, NSDictionary dictionary, NSDictionary mapping)
+ {
+ throw new RuntimeException( "Not implemented yet." );
+ }
+
+ public static NSDictionary valuesForKeys(
+ Object object, List keys)
+ {
+ throw new RuntimeException( "Not implemented yet." );
+ }
+
+ public static NSDictionary valuesForKeysWithMapping(
+ Object object, Map mapping)
+ {
+ throw new RuntimeException( "Not implemented yet." );
+ }
+ }
+}
+
+/*
+ * $Log$
+ * Revision 1.2 2006/02/16 16:47:14 cgruber
+ * Move some classes in to "internal" packages and re-work imports, etc.
+ *
+ * Also use UnsupportedOperationExceptions where appropriate, instead of WotonomyExceptions.
+ *
+ * Revision 1.1 2006/02/16 13:19:57 cgruber
+ * Check in all sources in eclipse-friendly maven-enabled packages.
+ *
+ * Revision 1.4 2003/01/16 22:47:30 mpowers
+ * Compatibility changes to support compiling woextensions source.
+ * (34 out of 56 classes compile!)
+ *
+ * Revision 1.3 2001/12/10 15:25:11 mpowers
+ * Now properly extending EOKeyValueCoding.
+ *
+ * Revision 1.2 2001/04/28 14:12:23 mpowers
+ * Refactored cloning/copying into KeyValueCodingUtilities.
+ *
+ * Revision 1.1 2001/03/29 03:29:49 mpowers
+ * Now using KeyValueCoding and Support instead of Introspector.
+ *
+ * Revision 1.2 2001/03/28 16:12:30 mpowers
+ * Documented interface.
+ *
+ * Revision 1.1 2001/03/27 23:25:05 mpowers
+ * Contributing interface, no docs yet.
+ *
+ *
+ */
+
+
diff --git a/projects/net.wotonomy.persistence/src/main/java/net/wotonomy/control/EOKeyValueCodingSupport.java b/projects/net.wotonomy.persistence/src/main/java/net/wotonomy/control/EOKeyValueCodingSupport.java
new file mode 100644
index 0000000..89e3e91
--- /dev/null
+++ b/projects/net.wotonomy.persistence/src/main/java/net/wotonomy/control/EOKeyValueCodingSupport.java
@@ -0,0 +1,235 @@
+/*
+Wotonomy: OpenStep design patterns for pure Java applications.
+Copyright (C) 2001 Intersect Software Corporation
+
+This library is free software; you can redistribute it and/or
+modify it under the terms of the GNU Lesser General Public
+License as published by the Free Software Foundation; either
+version 2.1 of the License, or (at your option) any later version.
+
+This library is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+Lesser General Public License for more details.
+
+You should have received a copy of the GNU Lesser General Public
+License along with this library; if not, see http://www.gnu.org
+*/
+
+package net.wotonomy.control;
+
+import net.wotonomy.foundation.internal.Introspector;
+import net.wotonomy.foundation.internal.IntrospectorException;
+import net.wotonomy.foundation.internal.MissingPropertyException;
+import net.wotonomy.foundation.internal.NullPrimitiveException;
+import net.wotonomy.foundation.internal.WotonomyException;
+
+/**
+* EOKeyValueCodingSupport defines default behavior for
+* classes implementing EOKeyValueSupport. <br><br>
+*
+* On an object that does not implement EOKeyValueCoding,
+* wotonomy will call the methods on this class directly.
+*
+* @author michael@mpowers.net
+* @author $Author: cgruber $
+* @version $Revision: 894 $
+*/
+public class EOKeyValueCodingSupport
+{
+ /**
+ * Returns the value for the specified property key
+ * on the specified object. <br><br>
+ *
+ * If the property does not exist, this method calls
+ * handleQueryWithUnboundKey on the object if it
+ * implements EOKeyValueCoding, otherwise calls
+ * handleQueryWithUnboundKey on this class. <br><br>
+ */
+ static public Object valueForKey(
+ Object anObject, String aKey )
+ {
+ //TODO: may need to handle "." nesting here so
+ // that handleQueryWithUnboundKey gets called for
+ // for the nested object, not the parent object
+
+ //Correction: need to handle key paths in
+ // KeyValueCodingAdditionsSupport.
+
+ try
+ {
+ return Introspector.get( anObject, aKey );
+ }
+ catch ( IntrospectorException exc )
+ {
+ if ( anObject instanceof EOKeyValueCoding )
+ {
+ return ((EOKeyValueCoding)anObject).handleQueryWithUnboundKey( aKey );
+ }
+ return handleQueryWithUnboundKey( anObject, aKey );
+ }
+ }
+
+ /**
+ * Sets the property to the specified value on
+ * the specified object.
+ *
+ * If the property does not exist, this method calls
+ * handleTakeValueForUnboundKey on the object if it
+ * implements EOKeyValueCoding, otherwise calls
+ * handleTakeValueForUnboundKey on this class.
+ *
+ * If the property is of a type that cannot allow
+ * null (e.g. primitive types) and aValue is null,
+ * this method should call unableToSetNullForKey
+ * on the object if it implements EOKeyValueCoding,
+ * otherwise calls unableToSetNullForKey on this class.
+ */
+ static public void takeValueForKey(
+ Object anObject, Object aValue, String aKey )
+ {
+ //TODO: may need to handle "." nesting here so
+ // that handleTakeValueForUnboundKey gets called for
+ // for the nested object, not the parent object
+
+ try
+ {
+ Introspector.set( anObject, aKey, aValue );
+ }
+ catch ( NullPrimitiveException exc )
+ {
+ if ( anObject instanceof EOKeyValueCoding )
+ {
+ ((EOKeyValueCoding)anObject).unableToSetNullForKey( aKey );
+ }
+ else
+ {
+ unableToSetNullForKey( anObject, aKey );
+ }
+ }
+ catch ( MissingPropertyException exc )
+ {
+ if ( anObject instanceof EOKeyValueCoding )
+ {
+ ((EOKeyValueCoding)anObject).handleTakeValueForUnboundKey(
+ aValue, aKey );
+ }
+ else
+ {
+ handleTakeValueForUnboundKey( anObject, aValue, aKey );
+ }
+ }
+
+ }
+
+ /**
+ * Returns the value for the private field that
+ * corresponds to the specified property on
+ * the specified object.
+ *
+ * This implementation currently calls valueForKey,
+ * because java security currently prevents us from
+ * accessing the fields of another object.
+ */
+ static public Object storedValueForKey(
+ Object anObject, String aKey )
+ {
+ //TODO: this currently just calls valueForKey
+ return valueForKey( anObject, aKey );
+ }
+
+ /**
+ * Sets the the private field that corresponds to the
+ * specified property to the specified value on the
+ * specified object.
+ *
+ * This implementation currently calls takeValueForKey,
+ * because java security currently prevents us from
+ * accessing the fields of another object.
+ */
+ static public void takeStoredValueForKey(
+ Object anObject, Object aValue, String aKey )
+ {
+ //TODO: this currently just calls takeValueForKey
+ takeValueForKey( anObject, aValue, aKey );
+ }
+
+ /**
+ * Called by valueForKey when the specified key is
+ * not found on the specified object, if that object
+ * does not implement EOKeyValueCoding.
+ *
+ * This implementation throws a WotonomyException.
+ */
+ static public Object handleQueryWithUnboundKey(
+ Object anObject, String aKey )
+ {
+ throw new WotonomyException(
+ "Key not found for object: "
+ + aKey + " : " + anObject );
+ }
+
+ /**
+ * Called by takeValueForKey when the specified key
+ * is not found on the specified object, if that object
+ * does not implement EOKeyValueCoding.
+ *
+ * This implementation throws a WotonomyException.
+ */
+ static public void handleTakeValueForUnboundKey(
+ Object anObject, Object aValue, String aKey )
+ {
+ throw new WotonomyException(
+ "Key not found for object while setting value: "
+ + aKey + " : " + anObject + " : " + aValue );
+ }
+
+ /**
+ * Called by takeValueForKey when the type of the
+ * specified key is not allowed to be null, as is
+ * the case with primitive types, if the specified
+ * object does not implement EOKeyValueCoding.
+ *
+ * This implementation throws a WotonomyException.
+ */
+ static public void unableToSetNullForKey(
+ Object anObject, String aKey )
+ {
+ throw new WotonomyException(
+ "Tried to key on object to null: "
+ + aKey + " : " + anObject );
+ }
+
+}
+
+
+/*
+ * $Log$
+ * Revision 1.2 2006/02/16 16:47:14 cgruber
+ * Move some classes in to "internal" packages and re-work imports, etc.
+ *
+ * Also use UnsupportedOperationExceptions where appropriate, instead of WotonomyExceptions.
+ *
+ * Revision 1.1 2006/02/16 13:19:57 cgruber
+ * Check in all sources in eclipse-friendly maven-enabled packages.
+ *
+ * Revision 1.5 2003/01/16 22:47:30 mpowers
+ * Compatibility changes to support compiling woextensions source.
+ * (34 out of 56 classes compile!)
+ *
+ * Revision 1.4 2001/05/18 21:04:33 mpowers
+ * Reimplemented EditingContext.initializeObject.
+ *
+ * Revision 1.3 2001/04/27 00:28:29 mpowers
+ * Fixed a return value.
+ *
+ * Revision 1.2 2001/04/03 20:36:01 mpowers
+ * Fixed refaulting/reverting/invalidating to be self-consistent.
+ *
+ * Revision 1.1 2001/03/28 17:49:33 mpowers
+ * Implemented EOKeyValueCodingSupport.
+ *
+ *
+ */
+
+
diff --git a/projects/net.wotonomy.persistence/src/main/java/net/wotonomy/control/EOKeyValueQualifier.java b/projects/net.wotonomy.persistence/src/main/java/net/wotonomy/control/EOKeyValueQualifier.java
new file mode 100644
index 0000000..799955e
--- /dev/null
+++ b/projects/net.wotonomy.persistence/src/main/java/net/wotonomy/control/EOKeyValueQualifier.java
@@ -0,0 +1,233 @@
+/*
+Wotonomy: OpenStep design patterns for pure Java applications.
+Copyright (C) 2001 Michael Powers
+
+This library is free software; you can redistribute it and/or
+modify it under the terms of the GNU Lesser General Public
+License as published by the Free Software Foundation; either
+version 2.1 of the License, or (at your option) any later version.
+
+This library is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+Lesser General Public License for more details.
+
+You should have received a copy of the GNU Lesser General Public
+License along with this library; if not, see http://www.gnu.org
+*/
+
+package net.wotonomy.control;
+
+import net.wotonomy.foundation.NSKeyValueCoding;
+import net.wotonomy.foundation.NSMutableDictionary;
+import net.wotonomy.foundation.NSSelector;
+import net.wotonomy.foundation.internal.WotonomyException;
+
+/**
+* EOKeyValueQualifier performs a property-based
+* comparison against a specified value. The comparison
+* is specified in the form of a NSSelector. The
+* selector is expected to take two arguments, the
+* property value on an object and the comparison value,
+* and return a Boolean indicating whether the object
+* is qualified. EOQualifier defines selectors that
+* may be used in creating EOKeyValueQualifiers.
+*
+* @author michael@mpowers.net
+* @author yjcheung@intersectsoft.com
+* @author $Author: cgruber $
+* @version $Revision: 894 $
+*/
+public class EOKeyValueQualifier extends EOQualifier
+ implements EOKeyValueArchiving, EOQualifierEvaluation
+{
+ private String key;
+ private NSSelector selector;
+ private Object value;
+
+ /**
+ * Constructor specifying a property key, a selector,
+ * and a value for comparison. The selector may be
+ * one of the constant selectors defined on EOQualifier.
+ */
+ public EOKeyValueQualifier(
+ String aKey,
+ NSSelector aSelector,
+ Object aValue )
+ {
+ key = aKey;
+ selector = aSelector;
+ value = aValue;
+ }
+
+ /**
+ * Returns the key for this qualifier.
+ */
+ public String key()
+ {
+ return key;
+ }
+
+ /**
+ * Returns the selector for this qualifier.
+ */
+ public NSSelector selector()
+ {
+ return selector;
+ }
+
+ /**
+ * Returns the value for this qualifier.
+ */
+ public Object value()
+ {
+ return value;
+ }
+
+ /**
+ * Evaluates this qualifier for the specified object,
+ * and returns whether the object is qualified.
+ * selector() is invoked on the value for key() on the
+ * specified object, with value() as the parameter.
+ */
+ public boolean evaluateWithObject( Object anObject )
+ {
+ try
+ {
+ Object value;
+ if ( anObject instanceof EOKeyValueCoding )
+ {
+ value = ((EOKeyValueCoding)anObject).valueForKey( key() );
+ }
+ else
+ {
+ value = EOKeyValueCodingSupport.valueForKey( anObject, key() );
+ }
+ return ((Boolean)selector.invoke( value(), value)).booleanValue();
+ }
+ catch ( Exception exc )
+ {
+ throw new WotonomyException( exc );
+ }
+ }
+
+ /**
+ * Returns a string representation of this qualifier.
+ */
+ public String toString()
+ {
+ return "( " + key + " " + selector + " " + value + " )";
+ }
+
+ public void encodeWithKeyValueArchiver(EOKeyValueArchiver arch) {
+ arch.encodeObject("EOKeyValueQualifier", "class");
+ arch.encodeObject(key, "key");
+ NSMutableDictionary d = new NSMutableDictionary(2);
+ if (value instanceof String)
+ d.setObjectForKey("NSString", "class");
+ else if (value instanceof java.math.BigDecimal)
+ d.setObjectForKey("NSDecimalNumber", "class");
+ else if (value instanceof Number)
+ d.setObjectForKey("NSNumber", "class");
+ else if (value instanceof NSKeyValueCoding.Null)
+ d.setObjectForKey("EONull", "class");
+ if (value != null && !(value instanceof NSKeyValueCoding.Null))
+ d.setObjectForKey(value.toString(), "value");
+ arch.encodeObject(d, "value");
+ String selname = null;
+ if (selector.equals(EOQualifier.QualifierOperatorCaseInsensitiveLike))
+ selname = "caseInsensitiveLike:";
+ else if (selector.equals(EOQualifier.QualifierOperatorContains))
+ selname = "contains:";
+ else if (selector.equals(EOQualifier.QualifierOperatorEqual))
+ selname = "isEqualTo:";
+ else if (selector.equals(EOQualifier.QualifierOperatorGreaterThan))
+ selname = "greaterThan:";
+ else if (selector.equals(EOQualifier.QualifierOperatorGreaterThanOrEqualTo))
+ selname = "greaterThanOrEqualTo:";
+ else if (selector.equals(EOQualifier.QualifierOperatorLessThan))
+ selname = "lessThan:";
+ else if (selector.equals(EOQualifier.QualifierOperatorLessThanOrEqualTo))
+ selname = "lessThanOrEqualTo:";
+ else if (selector.equals(EOQualifier.QualifierOperatorLike))
+ selname = "like:";
+ else if (selector.equals(EOQualifier.QualifierOperatorNotEqual))
+ selname = "isNotEqualTo:";
+ else
+ selname = selector.name() + ":";
+ arch.encodeObject(selname, "selectorName");
+ }
+
+ public static Object decodeWithKeyValueUnarchiver(EOKeyValueUnarchiver arch) {
+ String k = (String)arch.decodeObjectForKey("key");
+ Object v = arch.decodeObjectForKey("value");
+ NSSelector sel = null;
+ String sname = (String)arch.decodeObjectForKey("selectorName");
+ if (sname.equals("isEqualTo:"))
+ sel = EOQualifier.QualifierOperatorEqual;
+ else if (sname.equals("isNotEqualTo:"))
+ sel = EOQualifier.QualifierOperatorNotEqual;
+ else if (sname.equals("caseInsensitiveLike:"))
+ sel = EOQualifier.QualifierOperatorCaseInsensitiveLike;
+ else if (sname.equals("contains:"))
+ sel = EOQualifier.QualifierOperatorContains;
+ else if (sname.equals("greaterThan:"))
+ sel = EOQualifier.QualifierOperatorGreaterThan;
+ else if (sname.equals("greaterThanOrEqualTo:"))
+ sel = EOQualifier.QualifierOperatorGreaterThanOrEqualTo;
+ else if (sname.equals("lessThan:"))
+ sel = EOQualifier.QualifierOperatorLessThan;
+ else if (sname.equals("lessThanOrEqualTo:"))
+ sel = EOQualifier.QualifierOperatorLessThanOrEqualTo;
+ else if (sname.equals("like:"))
+ sel = EOQualifier.QualifierOperatorLike;
+ EOKeyValueQualifier q = new EOKeyValueQualifier(k, sel, v);
+ return q;
+ }
+
+}
+
+/*
+ * $Log$
+ * Revision 1.2 2006/02/16 16:47:14 cgruber
+ * Move some classes in to "internal" packages and re-work imports, etc.
+ *
+ * Also use UnsupportedOperationExceptions where appropriate, instead of WotonomyExceptions.
+ *
+ * Revision 1.1 2006/02/16 13:19:57 cgruber
+ * Check in all sources in eclipse-friendly maven-enabled packages.
+ *
+ * Revision 1.10 2003/08/12 01:43:04 chochos
+ * formally implement EOQualifierEvaluation
+ *
+ * Revision 1.9 2003/08/11 19:39:30 chochos
+ * special conditions for NSKeyValueCoding.NullValue -> EONull
+ *
+ * Revision 1.8 2003/08/09 01:22:51 chochos
+ * qualifiers implement EOKeyValueArchiving
+ *
+ * Revision 1.7 2003/08/06 23:07:52 chochos
+ * general code cleanup (mostly, removing unused imports)
+ *
+ * Revision 1.6 2001/10/31 15:26:06 mpowers
+ * Fixed typo.
+ *
+ * Revision 1.5 2001/10/31 15:25:14 mpowers
+ * Cleanup of qualifiers.
+ *
+ * Revision 1.4 2001/10/30 22:57:28 mpowers
+ * EOQualifier framework is now working.
+ *
+ * Revision 1.3 2001/09/13 15:25:56 mpowers
+ * Started implementation of the EOQualifier framework.
+ *
+ * Revision 1.2 2001/03/29 03:29:49 mpowers
+ * Now using KeyValueCoding and Support instead of Introspector.
+ *
+ * Revision 1.1 2001/02/27 03:33:04 mpowers
+ * Initial draft of the key-value qualifier.
+ *
+ *
+ */
+
+
diff --git a/projects/net.wotonomy.persistence/src/main/java/net/wotonomy/control/EOKeyValueUnarchiver.java b/projects/net.wotonomy.persistence/src/main/java/net/wotonomy/control/EOKeyValueUnarchiver.java
new file mode 100644
index 0000000..caed8a4
--- /dev/null
+++ b/projects/net.wotonomy.persistence/src/main/java/net/wotonomy/control/EOKeyValueUnarchiver.java
@@ -0,0 +1,165 @@
+/*
+Wotonomy: OpenStep design patterns for pure Java applications.
+Copyright (C) 2001 Michael Powers
+
+This library is free software; you can redistribute it and/or
+modify it under the terms of the GNU Lesser General Public
+License as published by the Free Software Foundation; either
+version 2.1 of the License, or (at your option) any later version.
+
+This library is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+Lesser General Public License for more details.
+
+You should have received a copy of the GNU Lesser General Public
+License along with this library; if not, see http://www.gnu.org
+*/
+package net.wotonomy.control;
+
+import java.lang.reflect.Method;
+
+import net.wotonomy.foundation.NSDictionary;
+import net.wotonomy.foundation.NSKeyValueCoding;
+
+/** Creates objects from dictionaries that were created with
+ * EOKeyValueArchiver.
+* @author michael@mpowers.net
+* @author $Author: cgruber $
+* @version $Revision: 894 $
+*/
+public class EOKeyValueUnarchiver {
+
+ NSDictionary dict;
+ Object _delegate;
+ protected static final Class[] METHOD_ARGS = new Class[]{ EOKeyValueUnarchiver.class };
+
+ public EOKeyValueUnarchiver(NSDictionary archive) {
+ super();
+ dict = archive;
+ }
+
+ public void awakeObjects() {
+ }
+
+ public boolean decodeBoolForKey(String key) {
+ Object x = dict.objectForKey(key);
+ if (x == null)
+ return false;
+ return (x.equals("true") || x.equals("YES") || x.equals("Y"));
+ }
+
+ public int decodeIntForKey(String key) {
+ Object x = dict.objectForKey(key);
+ if (x == null)
+ return 0;
+ if (x instanceof Number)
+ return ((Number)x).intValue();
+ try {
+ int i = Integer.parseInt(x.toString());
+ return i;
+ } catch (NumberFormatException ex) {
+ return 0;
+ }
+ }
+
+ public Object decodeObjectForKey(String key) {
+ Object x = dict.objectForKey(key);
+ if (x == null)
+ return null;
+ if (x instanceof NSDictionary) {
+ NSDictionary d = (NSDictionary)x;
+ if (d.objectForKey("class") != null) {
+ String cname = d.objectForKey("class").toString();
+ Class _class = null;
+ if (cname.equals("NSNumber")) {
+ if (d.objectForKey("value") != null)
+ return new Long(d.objectForKey("value").toString());
+ } else if (cname.equals("NSDecimalNumber")) {
+ if (d.objectForKey("value") != null)
+ return new java.math.BigDecimal(d.objectForKey("value").toString());
+ } else if (cname.equals("NSString")) {
+ if (d.objectForKey("value") != null)
+ return d.objectForKey("value").toString();
+ } else if (cname.equals("EONull")) {
+ return NSKeyValueCoding.NullValue;
+ } else if (cname.equals("EOFetchSpecification")) {
+ _class = EOFetchSpecification.class;
+ } else if (cname.equals("EOKeyValueQualifier")) {
+ _class = EOKeyValueQualifier.class;
+ } else if (cname.equals("EONotQualifier")) {
+ _class = EONotQualifier.class;
+ } else if (cname.equals("EOAndQualifier")) {
+ _class = EOAndQualifier.class;
+ } else if (cname.equals("EOOrQualifier")) {
+ _class = EOOrQualifier.class;
+ } else if (cname.equals("EOSortOrdering")) {
+ _class = EOSortOrdering.class;
+ } else if (cname.indexOf(".") < 0) { //Load a class without package
+ try {
+ _class = Class.forName("net.wotonomy.control." + cname);
+ } catch (ClassNotFoundException ex) {
+ }
+ //search for the class in access
+ if (_class == null) {
+ try {
+ _class = Class.forName("net.wotonomy.access." + cname);
+ } catch (ClassNotFoundException ex) {
+ }
+ }
+ } else {
+ try {
+ _class = Class.forName(cname);
+ } catch (ClassNotFoundException ex) {
+ }
+ }
+ if (_class == null)
+ return x;
+ try {
+ Method met = _class.getMethod("decodeWithKeyValueUnarchiver", METHOD_ARGS);
+ return met.invoke(null, new Object[]{ new EOKeyValueUnarchiver(d) });
+ } catch (Exception ex) {
+ ex.printStackTrace();
+ return x;
+ }
+ }
+ }
+ return x;
+ }
+
+ public Object decodeObjectReferenceForKey(String key) {
+ return null;
+ }
+
+ public void ensureObjectAwake(Object obj) {
+ }
+
+ public void finishInitializationOfObjects() {
+ }
+
+ public Object parent() {
+ return null;
+ }
+
+ public void setDelegate(Object del) {
+ _delegate = del;
+ }
+ public Object delegate() {
+ return _delegate;
+ }
+
+}
+/*
+ * $Log$
+ * Revision 1.2 2006/02/16 16:47:14 cgruber
+ * Move some classes in to "internal" packages and re-work imports, etc.
+ *
+ * Also use UnsupportedOperationExceptions where appropriate, instead of WotonomyExceptions.
+ *
+ * Revision 1.1 2006/02/16 13:19:57 cgruber
+ * Check in all sources in eclipse-friendly maven-enabled packages.
+ *
+ * Revision 1.2 2003/08/11 18:17:41 chochos
+ * decoding of objects now works properly
+ *
+ */ \ No newline at end of file
diff --git a/projects/net.wotonomy.persistence/src/main/java/net/wotonomy/control/EONotQualifier.java b/projects/net.wotonomy.persistence/src/main/java/net/wotonomy/control/EONotQualifier.java
new file mode 100644
index 0000000..d086840
--- /dev/null
+++ b/projects/net.wotonomy.persistence/src/main/java/net/wotonomy/control/EONotQualifier.java
@@ -0,0 +1,129 @@
+/*
+Wotonomy: OpenStep design patterns for pure Java applications.
+Copyright (C) 2001 Intersect Software Corporation
+
+This library is free software; you can redistribute it and/or
+modify it under the terms of the GNU Lesser General Public
+License as published by the Free Software Foundation; either
+version 2.1 of the License, or (at your option) any later version.
+
+This library is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+Lesser General Public License for more details.
+
+You should have received a copy of the GNU Lesser General Public
+License along with this library; if not, see http://www.gnu.org
+*/
+
+package net.wotonomy.control;
+
+import net.wotonomy.foundation.internal.WotonomyException;
+
+/**
+* EONotQualifiier negates a specified qualifier,
+* evaluating to the opposite of the specified qualifier.
+*
+* @author michael@mpowers.net
+* @author yjcheung@intersectsoft.com
+* @author $Author: cgruber $
+* @version $Revision: 894 $
+*/
+public class EONotQualifier extends EOQualifier
+ implements EOKeyValueArchiving, EOQualifierEvaluation
+{
+ private EOQualifier qualifier;
+
+ public EONotQualifier(
+ EOQualifier aQualifier )
+ {
+ qualifier = aQualifier;
+ }
+
+ /**
+ * Returns the qualifier that this qualifier negates.
+ */
+ public EOQualifier qualifier()
+ {
+ return qualifier;
+ }
+
+ /**
+ * Evaluates this qualifier for the specified object,
+ * and returns whether the object is qualified.
+ * evaluateWithObject is invoked on qualifier
+ * and the result is negated and returned.
+ */
+ public boolean evaluateWithObject( Object anObject )
+ {
+ return !(qualifier.evaluateWithObject(anObject));
+ }
+
+ /**
+ * Returns a string representation of this qualifier.
+ */
+ public String toString()
+ {
+ return (new StringBuffer("Not ").append(qualifier.toString()).toString());
+ }
+
+ public static Object decodeWithKeyValueUnarchiver(EOKeyValueUnarchiver arch) {
+ EOQualifier q = (EOQualifier)arch.decodeObjectForKey("qualifier");
+ if (q == null)
+ return null;
+ return new EONotQualifier(q);
+ }
+
+ public void encodeWithKeyValueArchiver(EOKeyValueArchiver arch) {
+ arch.encodeObject("EONotQualifier", "class");
+ if (qualifier instanceof EOKeyValueArchiving) {
+ EOKeyValueArchiver ar2 = new EOKeyValueArchiver();
+ ((EOKeyValueArchiving)qualifier).encodeWithKeyValueArchiver(ar2);
+ arch.encodeObject(ar2.dictionary(), "qualifiers");
+ } else
+ throw new WotonomyException("Cannot archive instance of " + qualifier.getClass().getName());
+ }
+
+}
+
+/*
+ * $Log$
+ * Revision 1.2 2006/02/16 16:47:14 cgruber
+ * Move some classes in to "internal" packages and re-work imports, etc.
+ *
+ * Also use UnsupportedOperationExceptions where appropriate, instead of WotonomyExceptions.
+ *
+ * Revision 1.1 2006/02/16 13:19:57 cgruber
+ * Check in all sources in eclipse-friendly maven-enabled packages.
+ *
+ * Revision 1.9 2003/08/12 01:43:04 chochos
+ * formally implement EOQualifierEvaluation
+ *
+ * Revision 1.8 2003/08/11 19:39:30 chochos
+ * special conditions for NSKeyValueCoding.NullValue -> EONull
+ *
+ * Revision 1.7 2003/08/09 01:24:19 chochos
+ * implements EOKeyValueArchiving
+ *
+ * Revision 1.6 2003/08/06 23:07:52 chochos
+ * general code cleanup (mostly, removing unused imports)
+ *
+ * Revision 1.5 2001/10/31 15:25:14 mpowers
+ * Cleanup of qualifiers.
+ *
+ * Revision 1.4 2001/10/30 22:57:28 mpowers
+ * EOQualifier framework is now working.
+ *
+ * Revision 1.3 2001/09/13 15:42:20 mpowers
+ * Fixed another cut/paste typo.
+ *
+ * Revision 1.2 2001/09/13 15:41:34 mpowers
+ * Fixed typo.
+ *
+ * Revision 1.1 2001/09/13 15:38:19 mpowers
+ * Started implementation of the EOQualifier framework.
+ *
+ *
+ */
+
+
diff --git a/projects/net.wotonomy.persistence/src/main/java/net/wotonomy/control/EONullValue.java b/projects/net.wotonomy.persistence/src/main/java/net/wotonomy/control/EONullValue.java
new file mode 100644
index 0000000..3b4544e
--- /dev/null
+++ b/projects/net.wotonomy.persistence/src/main/java/net/wotonomy/control/EONullValue.java
@@ -0,0 +1,113 @@
+/*
+Wotonomy: OpenStep design patterns for pure Java applications.
+Copyright (C) 2001 Intersect Software Corporation
+
+This library is free software; you can redistribute it and/or
+modify it under the terms of the GNU Lesser General Public
+License as published by the Free Software Foundation; either
+version 2.1 of the License, or (at your option) any later version.
+
+This library is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+Lesser General Public License for more details.
+
+You should have received a copy of the GNU Lesser General Public
+License along with this library; if not, see http://www.gnu.org
+*/
+
+package net.wotonomy.control;
+
+import java.io.Serializable;
+
+import net.wotonomy.foundation.NSNull;
+
+/**
+* EONullValue is used to represent null in Collections classes
+* because List and Map do not specify whether null values
+* are allowed and because NSArray and NSDictionary explicitly
+* do not allow null values. <br><br>
+*
+* Use of the static singleton method nullValue() is required
+* by this implementation because Java cannot return a singleton
+* instance from a constructor. <br><br>
+*
+* This implementation duplicates NSNull, but the singleton instances
+* are of course different. Be careful. I have no idea why this
+* class was even created, given that NSNull exists.
+*
+* @author michael@mpowers.net
+* @author $Author: cgruber $
+* @version $Revision: 894 $
+*/
+public class EONullValue implements Serializable
+{
+ private static final EONullValue instance = new EONullValue();
+
+ /**
+ * Create a new instance of EONullValue.
+ */
+ private EONullValue ()
+ {
+ }
+
+ /**
+ * Constructor specifying name and object.
+ */
+ public static EONullValue nullValue ()
+ {
+ return instance;
+ }
+
+ /**
+ * Returns a human-readable string representation.
+ */
+ public String toString()
+ {
+ return "[null]";
+ }
+
+ /**
+ * Hashcode of all instances is zero.
+ */
+ public int hashCode()
+ {
+ return 0;
+ }
+
+ /**
+ * Implemented to return true for any instance of EONullValue
+ * and for any instance of NSNull.
+ */
+ public boolean equals( Object anObject )
+ {
+ if ( anObject instanceof EONullValue ) return true;
+ if ( anObject instanceof NSNull ) return true;
+ return false;
+ }
+}
+
+/*
+ * $Log$
+ * Revision 1.2 2006/02/16 16:47:14 cgruber
+ * Move some classes in to "internal" packages and re-work imports, etc.
+ *
+ * Also use UnsupportedOperationExceptions where appropriate, instead of WotonomyExceptions.
+ *
+ * Revision 1.1 2006/02/16 13:19:57 cgruber
+ * Check in all sources in eclipse-friendly maven-enabled packages.
+ *
+ * Revision 1.3 2003/08/06 23:07:52 chochos
+ * general code cleanup (mostly, removing unused imports)
+ *
+ * Revision 1.2 2001/03/01 20:35:38 mpowers
+ * Implemented equals and hashCode.
+ *
+ * Revision 1.1 2001/02/26 22:41:51 mpowers
+ * Implemented null placeholder classes.
+ * Duplicator now uses NSNull.
+ * No longer catching base exception class.
+ *
+ *
+ */
+
diff --git a/projects/net.wotonomy.persistence/src/main/java/net/wotonomy/control/EOObjectStore.java b/projects/net.wotonomy.persistence/src/main/java/net/wotonomy/control/EOObjectStore.java
new file mode 100644
index 0000000..cd18e36
--- /dev/null
+++ b/projects/net.wotonomy.persistence/src/main/java/net/wotonomy/control/EOObjectStore.java
@@ -0,0 +1,320 @@
+/*
+Wotonomy: OpenStep design patterns for pure Java applications.
+Copyright (C) 2000 Michael Powers
+
+This library is free software; you can redistribute it and/or
+modify it under the terms of the GNU Lesser General Public
+License as published by the Free Software Foundation; either
+version 2.1 of the License, or (at your option) any later version.
+
+This library is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+Lesser General Public License for more details.
+
+You should have received a copy of the GNU Lesser General Public
+License along with this library; if not, see http://www.gnu.org
+*/
+
+package net.wotonomy.control;
+
+import java.util.List;
+import java.util.Map;
+
+import net.wotonomy.foundation.NSArray;
+
+/**
+* EOObjectStore defines an object repository that tracks
+* object creations, deletions, and updates made by
+* EOEditingContexts. <br><br>
+*
+* A concrete implementation would probably write these
+* changes to some kind of persistent storage, like a
+* database. <br><br>
+*
+* EOEditingContext is itself a subclass of EOObjectStore
+* that requires an EOObjectStore parent for committing
+* its changes. This means that EOEditingContexts can
+* use other EOEditingContexts as their parent, but there
+* still must exist an EOObjectStore as the root of the
+* editing graph.
+*
+* @author michael@mpowers.net
+* @author $Author: cgruber $
+* @version $Revision: 894 $
+*/
+public abstract class EOObjectStore
+{
+ /**
+ * Key for the user info of ObjectsChangedInStoreNotifications.
+ * The key should retrieve an array of deleted EOGlobalIDs.
+ */
+ public static final String DeletedKey = "deleted";
+
+ /**
+ * Key for the user info of ObjectsChangedInStoreNotifications.
+ * The key should retrieve an array of inserted EOGlobalIDs.
+ */
+ public static final String InsertedKey = "inserted";
+
+ /**
+ * Key for the user info of ObjectsChangedInStoreNotifications.
+ * The key should retrieve an array of updated EOGlobalIDs.
+ * EOEditingContexts should refault their copies of these objects.
+ */
+ public static final String UpdatedKey = "updated";
+
+ /**
+ * Key for the user info of ObjectsChangedInStoreNotification.
+ * The key should retrieve an array of EOGlobalIDs.
+ */
+ public static final String InvalidatedKey = "invalidated";
+
+ /**
+ * Key for the NSNotification posted when this object store
+ * is asked to invalidate all objects. Object of the notification
+ * will be this object store, and user info will contain the
+ * InvalidatedKey.
+ */
+ public static final String
+ InvalidatedAllObjectsInStoreNotification =
+ "EOInvalidatedAllObjectsInStoreNotification";
+
+ /**
+ * Key for the NSNotification posted when this object store
+ * is changed. Object of the notification will be this object
+ * store, and user info will contain InsertedKey, UpdatedKey,
+ * DeletedKey, and InvalidatedKey.
+ */
+ public static final String
+ ObjectsChangedInStoreNotification =
+ "EOObjectsChangedInStoreNotification";
+
+ /**
+ * Default constructor is responsible for initializing
+ * internal state.
+ */
+ public EOObjectStore ()
+ {
+ }
+
+ /**
+ * Called by editing contexts when they no longer
+ * need to track the specified id. You will not need
+ * to call this method, but you use use it for a hint
+ * that the specified global id is not in use by that
+ * child editing context.
+ */
+ public void editingContextDidForgetObjectWithGlobalID (
+ EOEditingContext aContext,
+ EOGlobalID aGlobalID )
+ {
+ }
+
+ /**
+ * Returns a List of objects associated with the object
+ * with the specified id for the specified property
+ * relationship, or may return a placeholder array that
+ * will defer the fetch until accessed (an array fault).
+ * 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 abstract NSArray arrayFaultWithSourceGlobalID (
+ EOGlobalID aGlobalID,
+ String aRelationship,
+ EOEditingContext aContext );
+
+ /**
+ * Returns the object for the specified id.
+ * The returned object may be a fault.
+ * The object will be registered in the
+ * specified editing context.
+ */
+ public abstract /*EOEnterpriseObject*/ Object faultForGlobalID (
+ EOGlobalID aGlobalID,
+ EOEditingContext aContext );
+
+ /**
+ * 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 abstract /*EOEnterpriseObject*/ Object faultForRawRow (
+ Map aDictionary,
+ String anEntityName,
+ EOEditingContext aContext );
+
+ /**
+ * Given a newly instantiated object, this method
+ * initializes its properties to values appropriate
+ * for the specified id. The object should already
+ * belong to the specified editing context.
+ * This method is called to populate faults.
+ */
+ public abstract void initializeObject (
+ /*EOEnterpriseObject*/ Object eo,
+ EOGlobalID aGlobalID,
+ EOEditingContext aContext );
+
+ /**
+ * Remove all values from all objects in memory,
+ * turning them into faults, and posts an NSNotification
+ * that all objects have been invalidated.
+ * The notification should be named with the string
+ * constant InvalidatedAllObjectsInStoreNotification
+ * with this object store as the object and no user info.
+ */
+ public abstract void invalidateAllObjects ();
+
+ /**
+ * Removes values with the specified ids from memory,
+ * turning them into faults, and posts a notification
+ * that those objects have been invalidated.
+ * The notification should be named with the string
+ * constant ObjectsChangedInStoreNotification
+ * with this object store as the object and user info
+ * containing a key named InvalidateKey that returns
+ * a List of the EOGlobalIDs of the invalidated objects.
+ */
+ public abstract void invalidateObjectsWithGlobalIDs (
+ List aList );
+
+ /**
+ * Returns whether the object corresponding to the
+ * specified id is locked. The concept of object
+ * locking is implementation-specific.
+ */
+ public abstract boolean isObjectLockedWithGlobalID (
+ EOGlobalID aGlobalID,
+ EOEditingContext aContext );
+
+ /**
+ * Locks the object corresponding to the
+ * specified id is locked. The concept of object
+ * locking is implementation-specific.
+ * The lock may be released when objects are
+ * invalidated or commited, but this behavior
+ * is not required.
+ */
+ public abstract void lockObjectWithGlobalID (
+ EOGlobalID aGlobalID,
+ EOEditingContext aContext );
+
+ /**
+ * 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 abstract NSArray objectsForSourceGlobalID (
+ EOGlobalID aGlobalID,
+ String aRelationship,
+ EOEditingContext aContext );
+
+ /**
+ * 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 fetched in the specified context,
+ * it is not refetched and that object should be used in the array.
+ */
+ public abstract NSArray objectsWithFetchSpecification (
+ EOFetchSpecification aFetchSpec,
+ EOEditingContext aContext );
+
+ /**
+ * 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 abstract void refaultObject (
+ Object anObject,
+ EOGlobalID aGlobalID,
+ EOEditingContext aContext );
+
+ /**
+ * Writes all changes in the specified editing context
+ * to the respository. The object store is expected to
+ * post a notification that should be named with the string
+ * constant ObjectsChangedInStoreNotification
+ * with this object store as the object and user info
+ * containing keys named UpdatedKey, InsertedKey, and
+ * DeletedKey that return Lists of the EOGlobalIDs of the
+ * corresponding objects.
+ */
+ public abstract void saveChangesInEditingContext (
+ EOEditingContext aContext );
+}
+
+/*
+ * $Log$
+ * Revision 1.2 2006/02/16 16:47:14 cgruber
+ * Move some classes in to "internal" packages and re-work imports, etc.
+ *
+ * Also use UnsupportedOperationExceptions where appropriate, instead of WotonomyExceptions.
+ *
+ * Revision 1.1 2006/02/16 13:19:57 cgruber
+ * Check in all sources in eclipse-friendly maven-enabled packages.
+ *
+ * Revision 1.15 2003/12/18 15:37:38 mpowers
+ * Changes to retain ability to work with objects that don't necessarily
+ * implement EOEnterpriseObject. I would still like to preserve this case
+ * for general usage, however the access package is free to assume that
+ * those objects will be EOs and cast appropriately.
+ *
+ * Revision 1.14 2003/08/19 01:53:12 chochos
+ * EOObjectStore had some incompatible return types (Object instead of EOEnterpriseObject, in fault methods mostly). It's internally consistent but I hope it doesn't break anything based on this, even though fault methods mostly throw exceptions for now.
+ *
+ * Revision 1.13 2002/02/13 21:20:15 mpowers
+ * Updated comments.
+ *
+ * Revision 1.12 2001/05/05 23:05:42 mpowers
+ * Implemented Array Faults.
+ *
+ * Revision 1.11 2001/02/21 21:17:32 mpowers
+ * Now retaining a reference to the recent changes observer.
+ * Better documented need to retain reference.
+ * Started implementing notifications.
+ *
+ * Revision 1.10 2001/02/16 22:51:29 mpowers
+ * Now deep-cloning objects passed between editing contexts.
+ *
+ * Revision 1.9 2001/02/16 18:34:19 mpowers
+ * Implementing nested contexts.
+ *
+ * Revision 1.8 2001/02/15 21:13:30 mpowers
+ * First draft implementation is complete. Now on to debugging.
+ *
+ * Revision 1.7 2001/02/14 23:03:02 mpowers
+ * A near-complete first draft of EOEditingContext.
+ *
+ * Revision 1.6 2001/02/13 23:24:29 mpowers
+ * Implementing more of editing context.
+ *
+ * Revision 1.5 2001/02/12 20:36:36 mpowers
+ * Documented methods.
+ *
+ * Revision 1.4 2001/02/09 22:09:34 mpowers
+ * Completed implementation of EOObjectStore.
+ *
+ * Revision 1.3 2001/02/06 15:24:11 mpowers
+ * Widened parameters on abstract method to fix build.
+ *
+ * Revision 1.2 2001/02/06 14:57:42 mpowers
+ * Defined abstract methods.
+ *
+ * Revision 1.1.1.1 2000/12/21 15:46:42 mpowers
+ * Contributing wotonomy.
+ *
+ * Revision 1.2 2000/12/20 16:25:35 michael
+ * Added log to all files.
+ *
+ *
+ */
+
+
diff --git a/projects/net.wotonomy.persistence/src/main/java/net/wotonomy/control/EOObjectStoreCoordinator.java b/projects/net.wotonomy.persistence/src/main/java/net/wotonomy/control/EOObjectStoreCoordinator.java
new file mode 100644
index 0000000..e9a3a6a
--- /dev/null
+++ b/projects/net.wotonomy.persistence/src/main/java/net/wotonomy/control/EOObjectStoreCoordinator.java
@@ -0,0 +1,204 @@
+/*
+Wotonomy: OpenStep design patterns for pure Java applications.
+Copyright (C) 2002 Israfil consulting Services Corporation
+
+This library is free software; you can redistribute it and/or
+modify it under the terms of the GNU Lesser General Public
+License as published by the Free Software Foundation; either
+version 2.1 of the License, or (at your option) any later version.
+
+This library is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+Lesser General Public License for more details.
+
+You should have received a copy of the GNU Lesser General Public
+License along with this library; if not, see http://www.gnu.org
+
+$Id: EOObjectStoreCoordinator.java 894 2006-02-16 16:47:14Z cgruber $
+
+*/
+
+package net.wotonomy.control;
+
+import java.util.List;
+import java.util.Map;
+
+import net.wotonomy.foundation.NSArray;
+import net.wotonomy.foundation.NSDictionary;
+import net.wotonomy.foundation.NSNotification;
+/**
+* A representation of a channel of communication to the database.
+*
+* @author cgruber@israfil.net
+* @author $Author: cgruber $
+* @version $Revision: 894 $
+*/
+
+public class EOObjectStoreCoordinator extends EOObjectStore {
+
+ public static final String CooperatingObjectStoreWasAddedNotification = "EOCooperatingObjectStoreWasAddedNotification";
+ public static final String CooperatingObjectStoreWasRemovedNotification = "EOCooperatingObjectStoreWasRemovedNotification";
+ public static final String CooperatingObjectStoreNeededNotification = "EOCooperatingObjectStoreNeededNotification";
+ public static final String GlobalIDKey = "globalID";
+ public static final String FetchSpecificationKey = "fetchSpecification";
+ public static final String ObjectKey = "object";
+
+ public static synchronized EOObjectStoreCoordinator defaultCoordinator() {
+ throw new UnsupportedOperationException("Not Yet Implemented");
+ }
+
+ public static synchronized void setDefaultCoordinator(EOObjectStoreCoordinator eoobjectstorecoordinator) {
+ throw new UnsupportedOperationException("Not Yet Implemented");
+ }
+
+ public EOObjectStoreCoordinator() {
+ throw new UnsupportedOperationException("Not Yet Implemented");
+ }
+
+ public void dispose() {
+ throw new UnsupportedOperationException("Not Yet Implemented");
+ }
+
+ private NSArray _sources() {
+ throw new UnsupportedOperationException("Not Yet Implemented");
+ }
+
+ public NSArray cooperatingObjectStores() {
+ throw new UnsupportedOperationException("Not Yet Implemented");
+ }
+
+ public void addCooperatingObjectStore(EOCooperatingObjectStore eocooperatingobjectstore) {
+ throw new UnsupportedOperationException("Not Yet Implemented");
+ }
+
+ public void removeCooperatingObjectStore(EOCooperatingObjectStore eocooperatingobjectstore) {
+ throw new UnsupportedOperationException("Not Yet Implemented");
+ }
+
+ public EOCooperatingObjectStore objectStoreForGlobalID(EOGlobalID eoglobalid) {
+ throw new UnsupportedOperationException("Not Yet Implemented");
+ }
+
+ public EOCooperatingObjectStore objectStoreForObject(EOEnterpriseObject eoenterpriseobject) {
+ throw new UnsupportedOperationException("Not Yet Implemented");
+ }
+
+ public EOCooperatingObjectStore objectStoreForFetchSpecification(EOFetchSpecification eofetchspecification) {
+ throw new UnsupportedOperationException("Not Yet Implemented");
+ }
+
+ EOCooperatingObjectStore objectStoreForEntityNamed(String s) {
+ throw new UnsupportedOperationException("Not Yet Implemented");
+ }
+
+ public void forwardUpdateForObject(EOEnterpriseObject eoenterpriseobject, NSDictionary nsdictionary) {
+ throw new UnsupportedOperationException("Not Yet Implemented");
+ }
+
+ public NSDictionary valuesForKeys(NSArray nsarray, EOEnterpriseObject eoenterpriseobject) {
+ throw new UnsupportedOperationException("Not Yet Implemented");
+ }
+
+ public void saveChangesInEditingContext(EOEditingContext eoeditingcontext) {
+ throw new UnsupportedOperationException("Not Yet Implemented");
+ }
+
+ public NSArray objectsWithFetchSpecification(EOFetchSpecification eofetchspecification, EOEditingContext eoeditingcontext) {
+ throw new UnsupportedOperationException("Not Yet Implemented");
+ }
+
+ public boolean isObjectLockedWithGlobalID(EOGlobalID eoglobalid, EOEditingContext eoeditingcontext) {
+ throw new UnsupportedOperationException("Not Yet Implemented");
+ }
+
+ public void lockObjectWithGlobalID(EOGlobalID eoglobalid, EOEditingContext eoeditingcontext) {
+ throw new UnsupportedOperationException("Not Yet Implemented");
+ }
+
+ public /*EOEnterpriseObject*/ Object faultForGlobalID(EOGlobalID eoglobalid, EOEditingContext eoeditingcontext) {
+ throw new UnsupportedOperationException("Not Yet Implemented");
+ }
+
+ public /*EOEnterpriseObject*/ Object faultForRawRow(NSDictionary nsdictionary, String s, EOEditingContext eoeditingcontext) {
+ throw new UnsupportedOperationException("Not Yet Implemented");
+ }
+
+ public NSArray arrayFaultWithSourceGlobalID(EOGlobalID eoglobalid, String s, EOEditingContext eoeditingcontext) {
+ throw new UnsupportedOperationException("Not Yet Implemented");
+ }
+
+ public void editingContextDidForgetObjectWithGlobalID(EOEditingContext eoeditingcontext, EOGlobalID eoglobalid) {
+ throw new UnsupportedOperationException("Not Yet Implemented");
+ }
+
+ public NSArray objectsForSourceGlobalID(EOGlobalID eoglobalid, String s, EOEditingContext eoeditingcontext) {
+ throw new UnsupportedOperationException("Not Yet Implemented");
+ }
+
+ public void invalidateObjectsWithGlobalIDs(NSArray nsarray) {
+ throw new UnsupportedOperationException("Not Yet Implemented");
+ }
+
+ public void invalidateAllObjects() {
+ throw new UnsupportedOperationException("Not Yet Implemented");
+ }
+
+ public void _objectsChangedInSubStore(NSNotification nsnotification) {
+ throw new UnsupportedOperationException("Not Yet Implemented");
+ }
+
+ public void _invalidatedAllObjectsInSubStore(NSNotification nsnotification) {
+ throw new UnsupportedOperationException("Not Yet Implemented");
+ }
+
+ public void setUserInfo(NSDictionary nsdictionary) {
+ throw new UnsupportedOperationException("Not Yet Implemented");
+ }
+
+ public NSDictionary userInfo() {
+ throw new UnsupportedOperationException("Not Yet Implemented");
+ }
+
+ /**
+ * @see net.wotonomy.control.EOObjectStore#faultForRawRow(Map, String, EOEditingContext)
+ */
+ public /*EOEnterpriseObject*/ Object faultForRawRow(
+ Map aDictionary,
+ String anEntityName,
+ EOEditingContext aContext) {
+ throw new UnsupportedOperationException("Not Yet Implemented");
+ }
+
+
+ /**
+ * @see net.wotonomy.control.EOObjectStore#initializeObject(Object, EOGlobalID, EOEditingContext)
+ */
+ public void initializeObject(
+ Object anObject,
+ EOGlobalID aGlobalID,
+ EOEditingContext aContext) {
+ throw new UnsupportedOperationException("Not Yet Implemented");
+ }
+
+
+ /**
+ * @see net.wotonomy.control.EOObjectStore#invalidateObjectsWithGlobalIDs(List)
+ */
+ public void invalidateObjectsWithGlobalIDs(List aList) {
+ throw new UnsupportedOperationException("Not Yet Implemented");
+ }
+
+
+ /**
+ * @see net.wotonomy.control.EOObjectStore#refaultObject(Object, EOGlobalID, EOEditingContext)
+ */
+ public void refaultObject(
+ Object anObject,
+ EOGlobalID aGlobalID,
+ EOEditingContext aContext) {
+ throw new UnsupportedOperationException("Not Yet Implemented");
+ }
+
+
+}
diff --git a/projects/net.wotonomy.persistence/src/main/java/net/wotonomy/control/EOObserverCenter.java b/projects/net.wotonomy.persistence/src/main/java/net/wotonomy/control/EOObserverCenter.java
new file mode 100644
index 0000000..d42130c
--- /dev/null
+++ b/projects/net.wotonomy.persistence/src/main/java/net/wotonomy/control/EOObserverCenter.java
@@ -0,0 +1,547 @@
+/*
+Wotonomy: OpenStep design patterns for pure Java applications.
+Copyright (C) 2000 Intersect Software Corporation
+
+This library is free software; you can redistribute it and/or
+modify it under the terms of the GNU Lesser General Public
+License as published by the Free Software Foundation; either
+version 2.1 of the License, or (at your option) any later version.
+
+This library is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+Lesser General Public License for more details.
+
+You should have received a copy of the GNU Lesser General Public
+License along with this library; if not, see http://www.gnu.org
+*/
+
+package net.wotonomy.control;
+
+import java.lang.ref.Reference;
+import java.lang.ref.ReferenceQueue;
+import java.lang.ref.WeakReference;
+import java.util.ArrayList;
+import java.util.Hashtable;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.Observable;
+import java.util.Observer;
+
+import net.wotonomy.foundation.NSArray;
+import net.wotonomy.foundation.NSMutableArray;
+
+/**
+* EOObserverCenter is a static singleton-like class
+* that manages EOObservings that want to be notified
+* of changes to those objects that call
+* notifyObserversObjectWillChange() before their
+* properties change. <br><br>
+*
+* Implementation note: Because Java implements the
+* Observer pattern in java.util.Observable, this
+* class knows how to register itself as an Observer
+* with Observables as well. However, users should
+* note that Observables only notify their Observers
+* of changes after their property has changed.
+* EODelayedObservers would see no difference because
+* they always receive their notification after all
+* changes have taken place. <br><br>
+*
+* This implementation uses weak references for observers
+* and observables. The advantage to this approach is
+* that you do not need to explicitly unregister observers
+* or observables; they will be unregistered when they are
+* garbage-collected. Note that you will need to retain
+* a reference to any objects you register or they may
+* become unregistered if no other object references them.
+*
+* @author michael@mpowers.net
+* @author $Author: cgruber $
+* @version $Revision: 894 $
+*/
+public class EOObserverCenter implements Observer
+{
+ /**
+ * Would much rather use a WeakHashMap, but that class
+ * compares by value, and we need to compare by reference.
+ * This means we need to recreate a weak hashmap with
+ * the ReferenceKey class below. Using hashtable for
+ * thread safety.
+ */
+ private static Map observableToObservers = new Hashtable();
+
+ private static List omniscients = new LinkedList();
+
+ // suppression count
+ private static int suppressions = 0;
+
+ // singleton instance - needed for Observer
+ private static EOObserverCenter instance = null;
+
+ // optimization: remember last request and result
+ private static Object lastRequest;
+ private static NSArray lastResult;
+
+ /**
+ * Registers the specified EOObserving for
+ * notifications from the specified object.
+ * An EOObserving can only be registered
+ * once for a given object.
+ */
+ public static void addObserver(
+ EOObserving anObserver, Object anObject )
+ {
+ // atomic operation
+ synchronized ( instance() )
+ {
+ // find observer list
+ List observers = (List)
+ observableToObservers.get( new ReferenceKey( anObject ) );
+
+ // if observer list not found, create and add item
+ if ( observers == null )
+ {
+ observers = new ArrayList();
+ observers.add( new WeakReference( anObserver ) );
+ processQueue();
+ observableToObservers.put( new ReferenceKey( anObject, queue ), observers );
+
+ // support for java.util.Observable
+ if ( anObject instanceof Observable )
+ {
+ ((Observable)anObject).addObserver( instance() );
+ }
+ }
+ else // observer list found - scan for observer
+ if ( indexOf( observers, anObserver ) < 0 )
+ {
+ // observer not found, register it
+ observers.add( new WeakReference( anObserver ) );
+ }
+
+ lastRequest = null;
+ lastResult = null;
+ }
+ }
+
+ /**
+ * Registers the specified EOObserving for notifications
+ * on all object changes. An EOObserving can be
+ * registered as an omniscient observer at most once.
+ * Use omniscient observers with caution.
+ */
+ public static void addOmniscientObserver(
+ EOObserving anObserver )
+ {
+ if ( indexOf( omniscients, anObserver ) < 0 )
+ {
+ omniscients.add( anObserver );
+ }
+ }
+
+ /**
+ * Notifies all EOObservings registered for
+ * notifications for the specified object.
+ * This method is typically called by objects
+ * that wish to broadcast a notification before
+ * a property change takes place, passing itself
+ * as the argument.
+ */
+ public static void notifyObserversObjectWillChange(
+ Object anObject )
+ {
+ if ( observerNotificationSuppressCount() == 0 )
+ {
+ List observers = observersForObject( anObject );
+ EOObserving o;
+ Iterator it = observers.iterator();
+ while ( it.hasNext() )
+ {
+ o = (EOObserving) it.next();
+ o.objectWillChange( anObject );
+ }
+ }
+ }
+
+ /**
+ * Returns an observer that is an instance of
+ * the specified class and
+ * that is registered for notifications about
+ * the specified object. If more than one
+ * such observer exists, which observer is
+ * returned is undetermined - use
+ * observersForObject instead. If no observer
+ * exists, returns null.
+ */
+ public static EOObserving observerForObject(
+ Object anObject, Class aClass )
+ {
+ List result = observersForObject( anObject );
+ if ( result.size() == 0 ) return null;
+
+ Object o;
+ Iterator it = result.iterator();
+ while ( it.hasNext() )
+ {
+ o = it.next();
+ if ( aClass.isAssignableFrom( o.getClass() ) )
+ {
+ return (EOObserving) o;
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Returns the number of times that notifications
+ * have been suppressed. This is also the number
+ * of times that enableObserverNotification must
+ * be called to allow notifications to take place.
+ */
+ public static int observerNotificationSuppressCount()
+ {
+ return suppressions;
+ }
+
+ /**
+ * Returns a List of observers for the
+ * specified object. Returns an empty List
+ * if no observer has registered for that
+ * object.
+ */
+ public static NSArray observersForObject(
+ Object anObject )
+ {
+ synchronized ( instance() )
+ {
+ // optimization: this is called very frequently
+ // from the same object calling willChange() a
+ // number of times in a row as it is updating.
+ if ( lastRequest == anObject )
+ {
+ return lastResult;
+ }
+
+ NSArray result;
+
+ List references = observerListForObject( anObject );
+ if ( references == null )
+ {
+ result = NSArray.EmptyArray;
+ }
+ else
+ {
+ result = new NSMutableArray();
+ Object observer;
+ Iterator it = references.iterator();
+ while ( it.hasNext() )
+ {
+ observer = ((Reference)it.next()).get();
+ if ( observer != null )
+ {
+ result.add( observer );
+ }
+ else // reference has expired
+ {
+ processQueue();
+ it.remove(); // remove from list
+ // if last observer, unregister observable
+ if ( references.size() == 0 )
+ {
+ observableToObservers.remove( new ReferenceKey( anObject ) );
+ }
+ }
+ }
+ }
+
+ lastRequest = anObject;
+ lastResult = result;
+
+ return result;
+ }
+
+ }
+
+ /**
+ * Returns a reference to the actual list of
+ * observers for the given object, or null if it
+ * doesn't exist.
+ */
+ private static List observerListForObject( Object anObject )
+ {
+ return (List) observableToObservers.get( new ReferenceKey( anObject ) );
+ }
+
+ /**
+ * Unregisters the specified observer for
+ * notifications from the specified object.
+ */
+ public static void removeObserver(
+ EOObserving anObserver, Object anObject )
+ {
+ // atomic operation
+ synchronized ( instance() )
+ {
+ lastRequest = null;
+ lastResult = null;
+
+ List result = observerListForObject( anObject );
+ if ( result == null ) return;
+ int index = indexOf( result, anObserver );
+ if ( index == -1 ) return;
+
+ // remove observer from list
+ result.remove( index );
+
+ // if last observer, unregister observable
+ if ( result.size() == 0 )
+ {
+ processQueue();
+ observableToObservers.remove( new ReferenceKey( anObject ) );
+ }
+ }
+ }
+
+ /**
+ * Unregisters the specified omniscient observer.
+ */
+ public static void removeOmniscientObserver(
+ EOObserving anObserver )
+ {
+ int index = indexOf( omniscients, anObserver );
+ if ( index != -1 )
+ {
+ omniscients.remove( index );
+ }
+ }
+
+ /**
+ * Enables notifications after they have been
+ * suppressed by suppressObserverNotification.
+ * If notifications have been suppressed
+ * multiple times, this method must be called
+ * an equal number of times to resume notifications.
+ * If notifications are not currently suppressed,
+ * this method does nothing.
+ */
+ public static void enableObserverNotification()
+ {
+ if ( suppressions > 0 ) suppressions--;
+ }
+
+ /**
+ * Causes notifications to be suppressed until
+ * the next matching call to enableObserverNotification.
+ * If this method is called more than once,
+ * enableObserverNotification must be called an
+ * equal number of times for notifications to resume.
+ * This method always causes notifications to cease
+ * immediately.
+ */
+ public static void suppressObserverNotification()
+ {
+ suppressions++;
+ }
+
+ /**
+ * Because we're comparing by reference, we need to
+ * test for the existence of the object directly.
+ * @return the index or -1 if not found.
+ */
+ private static int indexOf( List aList, Object anObject )
+ {
+ if ( anObject == null ) return -1;
+
+ synchronized ( aList )
+ {
+ int len = aList.size();
+ for ( int i = 0; i < len; i++ )
+ {
+ // compare by reference
+ if ( anObject == ((Reference)aList.get(i)).get() )
+ {
+ return i;
+ }
+ }
+ }
+ return -1;
+ }
+
+ /**
+ * Private singleton instance, so we can be an observer.
+ */
+ private static EOObserverCenter instance()
+ {
+ if ( instance == null )
+ {
+ instance = new EOObserverCenter();
+ }
+ return instance;
+ }
+
+ /**
+ * Interface Observer
+ */
+ public void update( Observable o, Object arg )
+ {
+ notifyObserversObjectWillChange( o );
+ }
+
+ /* Reference queue for cleared WeakKeys */
+ private static ReferenceQueue queue = new ReferenceQueue();
+
+ /* Remove all invalidated entries from the map, that is, remove all entries
+ whose keys have been discarded. This method should be invoked once by
+ each public mutator in this class. We don't invoke this method in
+ public accessors because that can lead to surprising
+ ConcurrentModificationExceptions. */
+ private static void processQueue()
+ {
+ synchronized ( instance() )
+ {
+ ReferenceKey rk;
+ while ((rk = (ReferenceKey)queue.poll()) != null)
+ {
+ //System.out.println( "EOObserverCenter.processQueue: removing object" );
+ observableToObservers.remove(rk);
+ }
+ }
+ }
+
+ /**
+ * Private class used to force a hashmap to
+ * perform key comparisons by reference.
+ * Retains a weak reference just like WeakHashMap.
+ */
+ static private class ReferenceKey extends WeakReference
+ {
+ private int hashCode;
+
+ /**
+ * Called to create a disposable reference key,
+ * used for retrieving values from the hashtable.
+ */
+ public ReferenceKey( Object anObject )
+ {
+ super( anObject );
+ hashCode = anObject.hashCode();
+ }
+
+ /**
+ * Called to create a reference key that will be
+ * used as a key in the hashtable, so we need the
+ * reference queue to later remove the key from the
+ * table when referred object is no longer in use.
+ */
+ public ReferenceKey( Object anObject, ReferenceQueue aQueue )
+ {
+ super( anObject, aQueue );
+ hashCode = anObject.hashCode();
+ }
+
+ /**
+ * Passes through to actual key's hash code.
+ */
+ public int hashCode()
+ {
+ return hashCode;
+ }
+
+ /**
+ * Compares by reference.
+ */
+ public boolean equals( Object anObject )
+ {
+ if ( ! ( anObject instanceof ReferenceKey ) ) return false;
+ Object key = get();
+ if ( key == null ) return false;
+ return ( key == ((ReferenceKey)anObject).get() );
+ }
+ }
+
+ private static String debugString()
+ {
+ String result = "";
+ int count = 0;
+ synchronized ( instance() )
+ {
+ Object anObject;
+ Iterator it = observableToObservers.keySet().iterator();
+ while ( it.hasNext() )
+ {
+ result += ((Reference)it.next()).get() + " : ";
+ count++;
+ /*
+ Iterator values = ((List)it.next()).iterator();
+ while ( values.hasNext() )
+ {
+ anObject = ((Reference)values.next()).get();
+ if ( anObject != null )
+ {
+// if ( anObject instanceof net.wotonomy.ui.MasterDetailAssociation )
+ result += anObject.getClass().toString() + " : ";
+ count++;
+ }
+ }
+ */
+ }
+ result += "["+count+"]";
+ }
+ return result;
+ }
+
+}
+
+/*
+ * $Log$
+ * Revision 1.2 2006/02/16 16:47:14 cgruber
+ * Move some classes in to "internal" packages and re-work imports, etc.
+ *
+ * Also use UnsupportedOperationExceptions where appropriate, instead of WotonomyExceptions.
+ *
+ * Revision 1.1 2006/02/16 13:19:57 cgruber
+ * Check in all sources in eclipse-friendly maven-enabled packages.
+ *
+ * Revision 1.9 2002/10/24 18:18:12 mpowers
+ * NSArray's are now considered read-only, so we can return our internal
+ * representation to reduce unnecessary object allocation.
+ *
+ * Revision 1.8 2001/02/21 21:17:32 mpowers
+ * Now retaining a reference to the recent changes observer.
+ * Better documented need to retain reference.
+ * Started implementing notifications.
+ *
+ * Revision 1.7 2001/02/17 16:52:05 mpowers
+ * Changes in imports to support building with jdk1.1 collections.
+ *
+ * Revision 1.6 2001/02/05 18:45:45 mpowers
+ * Reduced access back to private for utility methods.
+ *
+ * Revision 1.5 2001/02/05 18:42:32 mpowers
+ * Updated documentation throughout project.
+ *
+ * Revision 1.4 2001/01/18 16:57:47 mpowers
+ * Added debug facility.
+ *
+ * Revision 1.3 2001/01/10 16:28:53 mpowers
+ * Implemented a compare-by-reference weak hashtable
+ * because WeakHashMap compared by value and we can't
+ * assume that display groups won't contain objects
+ * whose values are equivalent.
+ *
+ * Revision 1.2 2001/01/09 20:10:19 mpowers
+ * Now using weak references to track observables and their observers.
+ *
+ * Revision 1.1.1.1 2000/12/21 15:46:44 mpowers
+ * Contributing wotonomy.
+ *
+ * Revision 1.8 2000/12/20 16:25:35 michael
+ * Added log to all files.
+ *
+ *
+ */
+
+
diff --git a/projects/net.wotonomy.persistence/src/main/java/net/wotonomy/control/EOObserverProxy.java b/projects/net.wotonomy.persistence/src/main/java/net/wotonomy/control/EOObserverProxy.java
new file mode 100644
index 0000000..e616a33
--- /dev/null
+++ b/projects/net.wotonomy.persistence/src/main/java/net/wotonomy/control/EOObserverProxy.java
@@ -0,0 +1,104 @@
+/*
+Wotonomy: OpenStep design patterns for pure Java applications.
+Copyright (C) 2001 Intersect Software Corporation
+
+This library is free software; you can redistribute it and/or
+modify it under the terms of the GNU Lesser General Public
+License as published by the Free Software Foundation; either
+version 2.1 of the License, or (at your option) any later version.
+
+This library is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+Lesser General Public License for more details.
+
+You should have received a copy of the GNU Lesser General Public
+License along with this library; if not, see http://www.gnu.org
+*/
+
+package net.wotonomy.control;
+
+import net.wotonomy.foundation.NSSelector;
+
+/**
+* A convenience observer for objects that do not or cannot
+* subclass EODelayedObserver. EOObserverProxy will invoke
+* an NSSelector on an object when it receives a subjectChanged
+* message.
+*
+* @author michael@mpowers.net
+* @author $Author: cgruber $
+* @version $Revision: 893 $
+*/
+public class EOObserverProxy
+ extends EODelayedObserver
+{
+ protected Object target;
+ protected NSSelector selector;
+ protected int priority;
+
+ /**
+ * Constructs an EODelayedObserver that will invoke the specified selector
+ * on the specified object, and will run at the specified priority.
+ */
+ public EOObserverProxy (
+ Object anObject, NSSelector aSelector, int aPriority )
+ {
+ target = anObject;
+ selector = aSelector;
+ priority = aPriority;
+ }
+
+ /**
+ * Constructs an EODelayedObserver that will invoke the specified selector
+ * on the specified object, and will run at ObserverPriorityThird priority,
+ * which is the default.
+ */
+ public EOObserverProxy (
+ Object anObject, NSSelector aSelector )
+ {
+ this( anObject, aSelector, ObserverPriorityThird );
+ }
+
+ /**
+ * Returns the priority of this observer in the queue.
+ * This implementation returns the priority specified
+ * in the constructor.
+ */
+ public int priority ()
+ {
+ return priority;
+ }
+
+ /**
+ * Notifies observer that one or more objects that
+ * it is observing have changed. The observer should
+ * check all objects it is observing for changes.
+ */
+ public void subjectChanged ()
+ {
+ try
+ {
+ selector.invoke( target );
+ }
+ catch ( Exception exc )
+ {
+ System.out.println( "Error notifying observer: " );
+ exc.printStackTrace();
+ }
+ }
+
+}
+
+/*
+ * $Log$
+ * Revision 1.1 2006/02/16 13:19:57 cgruber
+ * Check in all sources in eclipse-friendly maven-enabled packages.
+ *
+ * Revision 1.1 2001/10/22 21:55:32 mpowers
+ * This turns out to be a really useful class.
+ *
+ *
+ */
+
+
diff --git a/projects/net.wotonomy.persistence/src/main/java/net/wotonomy/control/EOObserving.java b/projects/net.wotonomy.persistence/src/main/java/net/wotonomy/control/EOObserving.java
new file mode 100644
index 0000000..8ec6e5c
--- /dev/null
+++ b/projects/net.wotonomy.persistence/src/main/java/net/wotonomy/control/EOObserving.java
@@ -0,0 +1,51 @@
+/*
+Wotonomy: OpenStep design patterns for pure Java applications.
+Copyright (C) 2000 Michael Powers
+
+This library is free software; you can redistribute it and/or
+modify it under the terms of the GNU Lesser General Public
+License as published by the Free Software Foundation; either
+version 2.1 of the License, or (at your option) any later version.
+
+This library is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+Lesser General Public License for more details.
+
+You should have received a copy of the GNU Lesser General Public
+License along with this library; if not, see http://www.gnu.org
+*/
+
+package net.wotonomy.control;
+
+/**
+* A pure java implementation of EOObserving.
+*
+* @author michael@mpowers.net
+* @author $Author: cgruber $
+* @version $Revision: 893 $
+*/
+public interface EOObserving
+{
+ /**
+ * Called when the specified object is about to change.
+ */
+ void objectWillChange ( Object anObject );
+
+}
+
+/*
+ * $Log$
+ * Revision 1.1 2006/02/16 13:19:57 cgruber
+ * Check in all sources in eclipse-friendly maven-enabled packages.
+ *
+ * Revision 1.1.1.1 2000/12/21 15:46:44 mpowers
+ * Contributing wotonomy.
+ *
+ * Revision 1.2 2000/12/20 16:25:35 michael
+ * Added log to all files.
+ *
+ *
+ */
+
+
diff --git a/projects/net.wotonomy.persistence/src/main/java/net/wotonomy/control/EOOrQualifier.java b/projects/net.wotonomy.persistence/src/main/java/net/wotonomy/control/EOOrQualifier.java
new file mode 100644
index 0000000..7f4b1c6
--- /dev/null
+++ b/projects/net.wotonomy.persistence/src/main/java/net/wotonomy/control/EOOrQualifier.java
@@ -0,0 +1,169 @@
+/*
+Wotonomy: OpenStep design patterns for pure Java applications.
+Copyright (C) 2001 Intersect Software Corporation
+
+This library is free software; you can redistribute it and/or
+modify it under the terms of the GNU Lesser General Public
+License as published by the Free Software Foundation; either
+version 2.1 of the License, or (at your option) any later version.
+
+This library is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+Lesser General Public License for more details.
+
+You should have received a copy of the GNU Lesser General Public
+License along with this library; if not, see http://www.gnu.org
+*/
+
+package net.wotonomy.control;
+
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+
+import net.wotonomy.foundation.NSArray;
+import net.wotonomy.foundation.NSDictionary;
+import net.wotonomy.foundation.NSMutableArray;
+import net.wotonomy.foundation.internal.WotonomyException;
+
+/**
+* EOOrQualifier contains other EOQualifiers,
+* evaluating as true if any of the contained
+* qualifiers evaluate as true.
+*
+* @author michael@mpowers.net
+* @author yjcheung@intersectsoft.com
+* @author $Author: cgruber $
+* @version $Revision: 894 $
+*/
+public class EOOrQualifier extends EOQualifier
+ implements EOKeyValueArchiving, EOQualifierEvaluation
+{
+ private List qualifiers;
+
+ public EOOrQualifier(
+ List aQualifierList )
+ {
+ qualifiers = new LinkedList( aQualifierList );
+ }
+
+ /**
+ * Returns a List of qualifiers contained by this qualifier.
+ */
+ public NSArray qualifiers()
+ {
+ return new NSArray( qualifiers );
+ }
+
+ /**
+ * Add a new qualifier to the list.
+ */
+ public void addQualifier(EOQualifier qualifier)
+ {
+ qualifiers.add(qualifier);
+ }
+
+ /**
+ * Evaluates this qualifier for the specified object,
+ * and returns whether the object is qualified.
+ * selector() is invoked on the value for key() on the
+ * specified object, with value() as the parameter.
+ *
+ * Note: this has a lazy "or" implementation. Ex. Qal1 or Qal2.
+ * If Qal1 is evaluated to be true, then it returns true without
+ * further evaluate Qal2.
+ */
+ public boolean evaluateWithObject( Object anObject )
+ {
+ Iterator it = qualifiers.iterator();
+ while( it.hasNext() )
+ {
+ if ( ((EOQualifier) it.next()).evaluateWithObject(anObject) )
+ {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Returns a string representation of this qualifier.
+ */
+ public String toString()
+ {
+ StringBuffer myBuf = new StringBuffer("(");
+ Iterator it = qualifiers.iterator();
+ while (it.hasNext())
+ {
+ myBuf = myBuf.append(((EOQualifier) it.next()).toString()).append(" or ");
+ }
+ String myStr = myBuf.toString();
+ myStr = myStr.substring(0, myStr.lastIndexOf(" or ")).concat(")");
+ return myStr;
+ }
+
+ public static Object decodeWithKeyValueUnarchiver(EOKeyValueUnarchiver arch) {
+ NSArray a = (NSArray)arch.decodeObjectForKey("qualifiers");
+ if (a == null)
+ return null;
+ NSMutableArray l = new NSMutableArray();
+ for (int i = 0; i < a.count(); i++) {
+ NSDictionary d = (NSDictionary)a.objectAtIndex(i);
+ EOKeyValueUnarchiver ua = new EOKeyValueUnarchiver(d);
+ EOQualifier q = (EOQualifier)EOQualifier.decodeWithKeyValueUnarchiver(ua);
+ if (q != null)
+ l.addObject(q);
+ }
+ return new EOAndQualifier(l);
+ }
+
+ public void encodeWithKeyValueArchiver(EOKeyValueArchiver arch) {
+ arch.encodeObject("EOOrQualifier", "class");
+ NSMutableArray arr = new NSMutableArray(qualifiers.size());
+ for (int i = 0; i < qualifiers.size(); i++) {
+ EOQualifier q = (EOQualifier)qualifiers.get(i);
+ if (q instanceof EOKeyValueArchiving) {
+ EOKeyValueArchiver ar2 = new EOKeyValueArchiver();
+ ((EOKeyValueArchiving)q).encodeWithKeyValueArchiver(ar2);
+ arr.addObject(ar2.dictionary());
+ } else
+ throw new WotonomyException("Cannot archive instance of " + q.getClass().getName());
+ }
+ arch.encodeObject(arr, "qualifiers");
+ }
+
+}
+
+/*
+ * $Log$
+ * Revision 1.2 2006/02/16 16:47:14 cgruber
+ * Move some classes in to "internal" packages and re-work imports, etc.
+ *
+ * Also use UnsupportedOperationExceptions where appropriate, instead of WotonomyExceptions.
+ *
+ * Revision 1.1 2006/02/16 13:19:57 cgruber
+ * Check in all sources in eclipse-friendly maven-enabled packages.
+ *
+ * Revision 1.6 2003/08/12 01:43:04 chochos
+ * formally implement EOQualifierEvaluation
+ *
+ * Revision 1.5 2003/08/09 01:22:51 chochos
+ * qualifiers implement EOKeyValueArchiving
+ *
+ * Revision 1.4 2003/08/06 23:07:52 chochos
+ * general code cleanup (mostly, removing unused imports)
+ *
+ * Revision 1.3 2001/10/31 15:25:14 mpowers
+ * Cleanup of qualifiers.
+ *
+ * Revision 1.2 2001/10/30 22:57:28 mpowers
+ * EOQualifier framework is now working.
+ *
+ * Revision 1.1 2001/09/13 15:25:56 mpowers
+ * Started implementation of the EOQualifier framework.
+ *
+ *
+ */
+
+
diff --git a/projects/net.wotonomy.persistence/src/main/java/net/wotonomy/control/EOQualifier.java b/projects/net.wotonomy.persistence/src/main/java/net/wotonomy/control/EOQualifier.java
new file mode 100644
index 0000000..6fffea4
--- /dev/null
+++ b/projects/net.wotonomy.persistence/src/main/java/net/wotonomy/control/EOQualifier.java
@@ -0,0 +1,680 @@
+/*
+Wotonomy: OpenStep design patterns for pure Java applications.
+Copyright (C) 2000 Michael Powers
+
+This library is free software; you can redistribute it and/or
+modify it under the terms of the GNU Lesser General Public
+License as published by the Free Software Foundation; either
+version 2.1 of the License, or (at your option) any later version.
+
+This library is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+Lesser General Public License for more details.
+
+You should have received a copy of the GNU Lesser General Public
+License along with this library; if not, see http://www.gnu.org
+*/
+
+package net.wotonomy.control;
+
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.ListIterator;
+import java.util.Map;
+import java.util.Set;
+import java.util.StringTokenizer;
+
+import net.wotonomy.foundation.NSArray;
+import net.wotonomy.foundation.NSMutableArray;
+import net.wotonomy.foundation.NSMutableDictionary;
+import net.wotonomy.foundation.NSSelector;
+import net.wotonomy.foundation.NSSet;
+import net.wotonomy.foundation.internal.ValueConverter;
+import net.wotonomy.foundation.internal.WotonomyException;
+
+/**
+* EOQualifiers are used to perform property-based
+* qualifications on objects: for a set of criteria,
+* a qualifier either qualifies or disqualifies an
+* given object. EOKeyValueQualifiers can be joined
+* by EOAndQualifier and EOOrQualifier, and so can
+* form a tree of qualifications. <br><br>
+*
+* Certain qualifiers
+* can accept a variable in place of a value; variable
+* names are marked by a "$", as in "$name". Variables
+* are resolved with the qualifierWithBindings() method.
+*
+* @author michael@mpowers.net
+* @author yjcheung@intersectsoft.com
+* @author $Author: cgruber $
+* @version $Revision: 894 $
+*/
+public abstract class EOQualifier
+{
+ public static final NSSelector
+ QualifierOperatorCaseInsensitiveLike = new OperatorCaseInsensitiveLike();
+ public static final NSSelector
+ QualifierOperatorContains = new OperatorContains();
+ public static final NSSelector
+ QualifierOperatorEqual = new OperatorEqual();
+ public static final NSSelector
+ QualifierOperatorGreaterThan = new OperatorGreaterThan();
+ public static final NSSelector
+ QualifierOperatorGreaterThanOrEqualTo = new OperatorGreaterThanOrEqualTo();
+ public static final NSSelector
+ QualifierOperatorLessThan = new OperatorLessThan();
+ public static final NSSelector
+ QualifierOperatorLessThanOrEqualTo = new OperatorLessThanOrEqualTo();
+ public static final NSSelector
+ QualifierOperatorLike = new OperatorLike();
+ public static final NSSelector
+ QualifierOperatorNotEqual = new OperatorNotEqual();
+
+ /**
+ * Default constructor.
+ */
+ public EOQualifier ()
+ {
+ }
+
+ /**
+ * Adds all qualifier keys in this qualifier to
+ * the specified Set, which is expected to be
+ * mutable. The tree of qualifiers is traversed
+ * and the left-hand-side of each expression is
+ * added to the set.
+ */
+ public void addQualifierKeysToSet ( Set aSet )
+ {
+ throw new RuntimeException( "Not implemented yet." );
+ }
+
+ /**
+ * Returns a Set of all property names used for
+ * comparisons by this qualifier. The tree of
+ * qualifiers is traversed and the left-hand-side
+ * of each expression is added to the set.
+ */
+ public NSSet allQualifierKeys ()
+ {
+ throw new RuntimeException( "Not implemented yet." );
+ }
+
+ /**
+ * Returns a List containing the variables used
+ * at compare-time by this qualifier. Each variable
+ * will appear only once in the list.
+ */
+ public NSArray bindingKeys ()
+ {
+ throw new RuntimeException( "Not implemented yet." );
+ }
+
+ /**
+ * Returns whether the specified object meets the
+ * criteria defined by this qualifier.
+ */
+ public boolean evaluateWithObject ( Object anObject )
+ {
+ return true;
+ }
+
+ /**
+ * Returns the key (which can be a key path) that
+ * is tested against the specified binding variable.
+ * The tree is traversed looking for the first instance
+ * of the specified variable, and the corresponding
+ * left-hand-side of the expression is returned.
+ */
+ public String keyPathForBindingKey ( String aVariable )
+ {
+ throw new RuntimeException( "Not implemented yet." );
+ }
+
+ /**
+ * Returns a qualifier that is like this qualifier,
+ * except all variables will be replaced with values
+ * from the specified Map whose keys match the variable
+ * names. If requireAll is true, an exception will be
+ * thrown if there is no key that matches on of the
+ * variables in the tree; otherwise, the qualifier
+ * containing the unmatched variable is removed.
+ */
+ public EOQualifier qualifierWithBindings (
+ Map aMap,
+ boolean requireAll )
+ {
+ throw new WotonomyException( "Not implemented yet." );
+ }
+
+
+ /**
+ * Tests whether all the keys in this qualifier can
+ * be applied to an object of the specified class.
+ * @return A Throwable if the validation fails,
+ * otherwise returns null if the class can be used
+ * with this qualifier.
+ */
+ public Throwable validateKeysWithRootClassDescription (
+ Class aClass )
+ {
+ throw new WotonomyException( "Not implemented yet." );
+ }
+
+ // statics
+
+ /**
+ * Convenience to retain only those objects from the specified
+ * List that meet the specified qualifier's requirements.
+ */
+ public static void filterArrayWithQualifier (
+ List anObjectList, EOQualifier aQualifier )
+ {
+ ListIterator iterator = anObjectList.listIterator();
+ while ( iterator.hasNext() )
+ {
+ if ( ! aQualifier.evaluateWithObject( iterator.next() ) )
+ {
+ iterator.remove();
+ }
+ }
+ }
+
+ /**
+ * Convenience to return a List consisting only
+ * of those objects in the specified List that meet
+ * the specified qualifier's requirements.
+ */
+ public static NSArray filteredArrayWithQualifier (
+ List anObjectList, EOQualifier aQualifier )
+ {
+ Object o;
+ List result = new LinkedList();
+ Iterator iterator = anObjectList.iterator();
+ while ( iterator.hasNext() )
+ {
+ o = iterator.next();
+ if ( aQualifier.evaluateWithObject( o ) )
+ {
+ result.add( o );
+ }
+ }
+ return new NSArray( (Collection) result );
+ }
+
+ /**
+ * Convenience to create a set of EOKeyValueQualifiers
+ * joined by an EOAndQualifier. Each pair of keys and
+ * values are used to create EOKeyValueQualifiers.
+ */
+ public static EOQualifier
+ qualifierToMatchAllValues ( Map aMap )
+ {
+ Object key, value;
+ List qualifierList = new LinkedList();
+ Iterator iterator = aMap.keySet().iterator();
+ while ( iterator.hasNext() )
+ {
+ key = iterator.next();
+ value = aMap.get( key );
+ qualifierList.add( new EOKeyValueQualifier(
+ key.toString(), QualifierOperatorEqual, value ) );
+ }
+ return new EOAndQualifier( qualifierList );
+ }
+
+ /**
+ * Convenience to create a set of EOKeyValueQualifiers
+ * joined by an EOOrQualifier. Each pair of keys and
+ * values are used to create EOKeyValueQualifiers.
+ */
+ public static EOQualifier
+ qualifierToMatchAnyValue ( Map aMap )
+ {
+ Object key, value;
+ List qualifierList = new LinkedList();
+ Iterator iterator = aMap.keySet().iterator();
+ while ( iterator.hasNext() )
+ {
+ key = iterator.next();
+ value = aMap.get( key );
+ qualifierList.add( new EOKeyValueQualifier(
+ key.toString(), QualifierOperatorEqual, value ) );
+ }
+ return new EOOrQualifier( qualifierList );
+ }
+
+ /**
+ * Returns an EOQualifier that meets the criteria
+ * represented by the specified string and variable
+ * length argument list. This method parses the string
+ * and returns a tree of qualifiers. Each token beginning
+ * with "%" is replaced with the corresponding object
+ * from the argument list. The parser recognizes the
+ * operation tokens returned from the allQualifierOperators()
+ * method.
+ */
+ public static EOQualifier qualifierWithQualifierFormat (
+ String aString, List anArgumentList )
+ {
+ throw new RuntimeException( "Not implemented yet." );
+ }
+
+ /**
+ * Returns a List of operators that are supported for
+ * relational operations. This excludes string comparison
+ * operators.
+ */
+ public static NSArray relationalQualifierOperators ()
+ {
+ NSMutableArray result = new NSMutableArray();
+ BaseSelector selector;
+ Iterator iterator = allQualifierOperators().iterator();
+ while ( iterator.hasNext() )
+ {
+ selector = (BaseSelector) iterator.next();
+ if ( selector.isRelationalOperator() )
+ {
+ result.addObject( selector );
+ }
+ }
+ return result;
+ }
+
+ /**
+ * Returns a List of valid operators.
+ */
+ public static NSArray allQualifierOperators ()
+ {
+ return operators.allKeys();
+ }
+
+ /**
+ * Returns a selector the corresponds to the operation
+ * represented by the specified string. For example,
+ * ">" represents QualifierOperatorGreaterThan.
+ */
+ public static NSSelector operatorSelectorForString (
+ String anOperatorString )
+ {
+ return (NSSelector)
+ operators.objectForKey( anOperatorString );
+ }
+
+ /**
+ * Returns a string the corresponds to the operation
+ * represented by the specified selector. For example,
+ * QualifierOperatorGreaterThan is represented with ">".
+ */
+ public static String stringForOperatorSelector (
+ NSSelector aSelector )
+ {
+ return (String)
+ operators.allKeysForObject( aSelector ).lastObject();
+ }
+
+ /**
+ * Returns a string representation of this qualifier.
+ */
+ public String toString()
+ {
+ //TODO: implement this
+ return super.toString();
+ }
+
+ // built-in qualifiers
+
+ private static NSMutableDictionary operators;
+ static
+ {
+ operators = new NSMutableDictionary();
+ operators.setObjectForKey(
+ QualifierOperatorCaseInsensitiveLike,
+ QualifierOperatorCaseInsensitiveLike.toString() );
+ operators.setObjectForKey(
+ QualifierOperatorContains,
+ QualifierOperatorContains.toString() );
+ operators.setObjectForKey(
+ QualifierOperatorEqual,
+ QualifierOperatorEqual.toString() );
+ operators.setObjectForKey(
+ QualifierOperatorGreaterThan,
+ QualifierOperatorGreaterThan.toString() );
+ operators.setObjectForKey(
+ QualifierOperatorGreaterThanOrEqualTo,
+ QualifierOperatorGreaterThanOrEqualTo.toString() );
+ operators.setObjectForKey(
+ QualifierOperatorLessThan,
+ QualifierOperatorLessThan.toString() );
+ operators.setObjectForKey(
+ QualifierOperatorLessThanOrEqualTo,
+ QualifierOperatorLessThanOrEqualTo.toString() );
+ operators.setObjectForKey(
+ QualifierOperatorLike,
+ QualifierOperatorLike.toString() );
+ operators.setObjectForKey(
+ QualifierOperatorNotEqual,
+ QualifierOperatorNotEqual.toString() );
+ }
+
+ static private abstract class BaseSelector extends NSSelector
+ {
+ public String name ()
+ {
+ return "BaseSelector";
+ }
+
+ public Class[] parameterTypes ()
+ {
+ return new Class[] { Object.class, Object.class };
+ }
+
+ public Method methodOnClass (Class aClass)
+ throws NoSuchMethodException
+ {
+ throw new NoSuchMethodException();
+ }
+
+ public Method methodOnObject (Object anObject)
+ throws NoSuchMethodException
+ {
+ throw new NoSuchMethodException();
+ }
+
+ public boolean implementedByClass (Class aClass)
+ {
+ return true;
+ }
+
+ public boolean implementedByObject (Object anObject)
+ {
+ return true;
+ }
+
+ public boolean isRelationalOperator()
+ {
+ return true;
+ }
+
+ public Object invoke (Object anObject, Object[] parameters)
+ throws IllegalAccessException, IllegalArgumentException,
+ InvocationTargetException, NoSuchMethodException
+ {
+ return qualify( anObject, parameters[0] );
+ }
+
+ abstract protected Boolean qualify( Object target, Object parameter );
+
+ protected int doCompare(Object o1, Object o2)
+ {
+ Class firstClass = o1.getClass();
+ Class secondClass = o2.getClass();
+
+ if ( ! ( secondClass.equals( firstClass ) ) )
+ {
+ Object converted = null;
+ if ( o2 instanceof Comparable )
+ {
+ converted = ValueConverter.convertObjectToClass( o1, secondClass );
+ if (converted != null)
+ {
+ o1 = converted;
+ }
+ }
+
+ if (converted == null && (o1 instanceof Comparable))
+ {
+ converted = ValueConverter.convertObjectToClass( o2, firstClass );
+ if ( converted != null )
+ {
+ o2 = converted;
+ }
+ }
+
+ if (converted == null)
+ {
+ throw new WotonomyException("Qualifier: Not Comparable Objects");
+ // no way to compare
+ }
+ }
+
+ return ((Comparable)o2).compareTo( o1 );
+ }
+
+ }
+
+
+ static class OperatorCaseInsensitiveLike extends BaseSelector {
+
+ public boolean isRelationalOperator()
+ {
+ return false;
+ }
+
+ protected Boolean qualify( Object o1, Object o2 )
+ {
+ String myString1 = o1.toString();
+ String myString2 = o2.toString();
+ myString1 = myString1.toLowerCase();
+ myString2 = myString2.toLowerCase();
+ StringTokenizer st = new StringTokenizer(myString1, "%");
+
+ while (st.hasMoreTokens()) {
+ String part = st.nextToken();
+ int index = myString2.indexOf(part);
+ if (index > -1)
+ {
+ myString2 = myString2.substring(index + part.length());
+ }
+ else
+ {
+ return Boolean.FALSE;
+ }
+ }
+ return Boolean.TRUE;
+
+ }
+
+ public String toString()
+ {
+ return "caseInsensitiveLike";
+ }
+ }
+
+ static class OperatorContains extends BaseSelector {
+
+ public boolean isRelationalOperator()
+ {
+ return false;
+ }
+
+ protected Boolean qualify( Object o1, Object o2 )
+ {
+ String myString1 = o1.toString();
+ String myString2 = o2.toString();
+ return new Boolean(
+ myString2.indexOf(myString1) > -1 );
+ }
+
+ public String toString()
+ {
+ return "contains";
+ }
+ }
+
+ static class OperatorEqual extends BaseSelector {
+
+ protected Boolean qualify( Object o1, Object o2 )
+ {
+ return new Boolean( doCompare(o1, o2) == 0 );
+ }
+
+ public String toString()
+ {
+ return "=";
+ }
+ }
+
+ static class OperatorGreaterThan extends BaseSelector {
+
+ protected Boolean qualify( Object o1, Object o2 )
+ {
+ return new Boolean( doCompare(o1, o2) > 0 );
+ }
+
+ public String toString()
+ {
+ return ">";
+ }
+ }
+
+ static class OperatorGreaterThanOrEqualTo extends BaseSelector {
+
+ protected Boolean qualify( Object o1, Object o2 )
+ {
+ return new Boolean( doCompare(o1, o2) >= 0 );
+ }
+
+ public String toString()
+ {
+ return new String(" >= ");
+ }
+ }
+
+ static class OperatorLessThan extends BaseSelector {
+
+ protected Boolean qualify( Object o1, Object o2 )
+ {
+ return new Boolean( doCompare(o1, o2) < 0 );
+ }
+
+ public String toString()
+ {
+ return ">";
+ }
+ }
+
+ static class OperatorLessThanOrEqualTo extends BaseSelector {
+
+ protected Boolean qualify( Object o1, Object o2 )
+ {
+ return new Boolean (doCompare(o1, o2) <= 0);
+ }
+
+ public String toString()
+ {
+ return "<=";
+ }
+ }
+
+ static class OperatorLike extends BaseSelector {
+
+ public boolean isRelationalOperator()
+ {
+ return false;
+ }
+
+ protected Boolean qualify( Object o1, Object o2 )
+ {
+ String myString1 = o1.toString();
+ String myString2 = o2.toString();
+ StringTokenizer st = new StringTokenizer(myString1, "%");
+ while (st.hasMoreTokens()) {
+ String part = st.nextToken();
+ int index = myString2.indexOf(part);
+ if (index > -1)
+ {
+ myString2 = myString2.substring(index + part.length());
+ }
+ else
+ {
+ return Boolean.FALSE;
+ }
+ }
+ return Boolean.TRUE;
+ }
+
+ public String toString()
+ {
+ return "like";
+ }
+ }
+
+ static class OperatorNotEqual extends BaseSelector {
+
+ protected Boolean qualify( Object o1, Object o2 )
+ {
+ return new Boolean(!(o1.equals(o2)));
+ }
+
+ public String toString()
+ {
+ return "!=";
+ }
+ }
+
+
+ public static Object decodeWithKeyValueUnarchiver(EOKeyValueUnarchiver ua) {
+ String cname = (String)ua.decodeObjectForKey("class");
+ if (cname.equals("EOKeyValueQualifier"))
+ return (EOQualifier)EOKeyValueQualifier.decodeWithKeyValueUnarchiver(ua);
+ if (cname.equals("EOAndQualifier"))
+ return (EOQualifier)EOAndQualifier.decodeWithKeyValueUnarchiver(ua);
+ if (cname.equals("EOOrQualifier"))
+ return (EOQualifier)EOOrQualifier.decodeWithKeyValueUnarchiver(ua);
+ if (cname.equals("EONotQualifier"))
+ return (EOQualifier)EONotQualifier.decodeWithKeyValueUnarchiver(ua);
+ return null;
+ }
+
+}
+/*
+ * $Log$
+ * Revision 1.2 2006/02/16 16:47:14 cgruber
+ * Move some classes in to "internal" packages and re-work imports, etc.
+ *
+ * Also use UnsupportedOperationExceptions where appropriate, instead of WotonomyExceptions.
+ *
+ * Revision 1.1 2006/02/16 13:19:57 cgruber
+ * Check in all sources in eclipse-friendly maven-enabled packages.
+ *
+ * Revision 1.10 2003/08/09 01:22:51 chochos
+ * qualifiers implement EOKeyValueArchiving
+ *
+ * Revision 1.9 2001/11/04 18:29:11 mpowers
+ * Better handling for non-string types used with non-relational operators.
+ *
+ * Revision 1.8 2001/10/31 15:25:14 mpowers
+ * Cleanup of qualifiers.
+ *
+ * Revision 1.7 2001/10/30 22:57:28 mpowers
+ * EOQualifier framework is now working.
+ *
+ * Revision 1.6 2001/10/30 22:16:37 mpowers
+ * Implemented operators as selectors.
+ *
+ * Revision 1.5 2001/09/14 14:21:28 mpowers
+ * Updated javadoc.
+ *
+ * Revision 1.3 2001/09/13 15:25:56 mpowers
+ * Started implementation of the EOQualifier framework.
+ *
+ * Revision 1.2 2001/02/27 03:33:04 mpowers
+ * Initial draft of the key-value qualifier.
+ *
+ * Revision 1.1.1.1 2000/12/21 15:46:47 mpowers
+ * Contributing wotonomy.
+ *
+ * Revision 1.2 2000/12/20 16:25:35 michael
+ * Added log to all files.
+ *
+ *
+ */
+
+
diff --git a/projects/net.wotonomy.persistence/src/main/java/net/wotonomy/control/EOQualifierEvaluation.java b/projects/net.wotonomy.persistence/src/main/java/net/wotonomy/control/EOQualifierEvaluation.java
new file mode 100644
index 0000000..87769b8
--- /dev/null
+++ b/projects/net.wotonomy.persistence/src/main/java/net/wotonomy/control/EOQualifierEvaluation.java
@@ -0,0 +1,42 @@
+/*
+Wotonomy: OpenStep design patterns for pure Java applications.
+Copyright (C) 2000 Michael Powers
+
+This library is free software; you can redistribute it and/or
+modify it under the terms of the GNU Lesser General Public
+License as published by the Free Software Foundation; either
+version 2.1 of the License, or (at your option) any later version.
+
+This library is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+Lesser General Public License for more details.
+
+You should have received a copy of the GNU Lesser General Public
+License along with this library; if not, see http://www.gnu.org
+*/
+package net.wotonomy.control;
+
+/**
+* EOQualifiers that want to perform in-memory
+* evaluation should implement this interface. <br><br>
+*
+* @author ezamudio@nasoft.com
+* @author $Author: cgruber $
+* @version $Revision: 893 $
+*/
+public interface EOQualifierEvaluation {
+
+ public boolean evaluateWithObject(Object eo);
+
+}
+/*
+ * $Log$
+ * Revision 1.1 2006/02/16 13:19:57 cgruber
+ * Check in all sources in eclipse-friendly maven-enabled packages.
+ *
+ * Revision 1.1 2003/08/12 01:42:17 chochos
+ * formally declare this interface
+ *
+ */
+ \ No newline at end of file
diff --git a/projects/net.wotonomy.persistence/src/main/java/net/wotonomy/control/EORelationshipManipulation.java b/projects/net.wotonomy.persistence/src/main/java/net/wotonomy/control/EORelationshipManipulation.java
new file mode 100644
index 0000000..568b555
--- /dev/null
+++ b/projects/net.wotonomy.persistence/src/main/java/net/wotonomy/control/EORelationshipManipulation.java
@@ -0,0 +1,76 @@
+/*
+Wotonomy: OpenStep design patterns for pure Java applications.
+Copyright (C) 2001 Michael Powers
+
+This library is free software; you can redistribute it and/or
+modify it under the terms of the GNU Lesser General Public
+License as published by the Free Software Foundation; either
+version 2.1 of the License, or (at your option) any later version.
+
+This library is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+Lesser General Public License for more details.
+
+You should have received a copy of the GNU Lesser General Public
+License along with this library; if not, see http://www.gnu.org
+*/
+
+package net.wotonomy.control;
+
+/**
+* EORelationshipManipulation provides methods for generically
+* adding and removing relationships between objects, handling
+* both one-way and reciprocal relationships.
+*
+* @author michael@mpowers.net
+* @author $Author: cgruber $
+* @version $Revision: 893 $
+*/
+public interface EORelationshipManipulation
+{
+ /**
+ * Adds the specified object to the relationship on this
+ * object specified by the key. For to-one relationships,
+ * this operation is the same as valueForKey.
+ */
+ void addObjectToPropertyWithKey(
+ Object anObject, String aKey );
+
+ /**
+ * Removes the specified object from the relationship on
+ * this object specified by the key. For to-one relationships,
+ * this operation is the same as takeValueForKey with a null
+ * value.
+ */
+ void removeObjectFromPropertyWithKey(
+ Object anObject, String aKey );
+
+ /**
+ * As addObjectToProperty with key, but also performs the
+ * reciprocal operation on the other side of the relationship.
+ */
+ void addObjectToBothSidesOfRelationshipWithKey(
+ EORelationshipManipulation anObject, String aKey );
+
+ /**
+ * As removeObjectFromPropertyWithKey with key, but also performs the
+ * reciprocal operation on the other side of the relationship.
+ */
+ void removeObjectFromBothSidesOfRelationshipWithKey(
+ EORelationshipManipulation anObject, String aKey );
+
+}
+
+/*
+ * $Log$
+ * Revision 1.1 2006/02/16 13:19:57 cgruber
+ * Check in all sources in eclipse-friendly maven-enabled packages.
+ *
+ * Revision 1.1 2001/11/13 04:13:59 mpowers
+ * Added interfaces needed to begin work on EOCustomObject.
+ *
+ *
+ */
+
+
diff --git a/projects/net.wotonomy.persistence/src/main/java/net/wotonomy/control/EOSortOrdering.java b/projects/net.wotonomy.persistence/src/main/java/net/wotonomy/control/EOSortOrdering.java
new file mode 100644
index 0000000..789b6da
--- /dev/null
+++ b/projects/net.wotonomy.persistence/src/main/java/net/wotonomy/control/EOSortOrdering.java
@@ -0,0 +1,406 @@
+/*
+Wotonomy: OpenStep design patterns for pure Java applications.
+Copyright (C) 2000 Michael Powers
+
+This library is free software; you can redistribute it and/or
+modify it under the terms of the GNU Lesser General Public
+License as published by the Free Software Foundation; either
+version 2.1 of the License, or (at your option) any later version.
+
+This library is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+Lesser General Public License for more details.
+
+You should have received a copy of the GNU Lesser General Public
+License along with this library; if not, see http://www.gnu.org
+*/
+
+package net.wotonomy.control;
+
+import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.Iterator;
+import java.util.List;
+
+import net.wotonomy.foundation.NSArray;
+import net.wotonomy.foundation.NSMutableArray;
+import net.wotonomy.foundation.NSSelector;
+
+/**
+* EOSortOrdering defines a sort key and operation.
+* DisplayGroups use lists of EOSortOrdering to determine
+* how to order their items.
+*
+* @author michael@mpowers.net
+* @author $Author: cgruber $
+* @version $Revision: 894 $
+*/
+public class EOSortOrdering implements Serializable, EOKeyValueArchiving
+{
+ /**
+ * Sorts items in ascending order.
+ */
+ public static final
+ NSSelector CompareAscending = new CompareAscendingComparator();
+
+ /**
+ * Sorts items in descending order.
+ */
+ public static final
+ NSSelector CompareDescending = new CompareDescendingComparator();
+
+ /**
+ * Sorts items' string representations in ascending order
+ * in a case insensitive manner.
+ */
+ public static final
+ NSSelector CompareCaseInsensitiveAscending =
+ new CompareCaseInsensitiveAscendingComparator();
+
+ /**
+ * Sorts items' string representations in descending order
+ * in a case insensitive manner.
+ */
+ public static final
+ NSSelector CompareCaseInsensitiveDescending =
+ new CompareCaseInsensitiveDescendingComparator();
+
+ protected String key;
+ protected NSSelector selector;
+
+ /**
+ * Factory-style constructor returns a new EOSortOrdering instance
+ * with the specified key and selector. Neither may be null.
+ */
+ public static EOSortOrdering sortOrderingWithKey(String key, NSSelector selector)
+ {
+ return new EOSortOrdering( key, selector );
+ }
+
+ /**
+ * Constructor creates an EOSortOrdering that uses the
+ * specified key and selector. Neither may be null.
+ */
+ public EOSortOrdering( String aKey, NSSelector aSelector )
+ {
+ key = aKey;
+ selector = aSelector;
+ }
+
+ /**
+ * Constructor creates an EOSortOrdering that uses the
+ * specified key and comparator. Neither may be null.
+ * Not in the spec.
+ */
+ public EOSortOrdering( String aKey, Comparator aComparator )
+ {
+ key = aKey;
+ selector = new NSSelector( aKey, aComparator );
+ }
+
+ /**
+ * Returns the property key.
+ */
+ public String key()
+ {
+ return key;
+ }
+
+ /**
+ * Returns the selector.
+ */
+ public NSSelector selector()
+ {
+ return selector;
+ }
+
+ public String toString()
+ {
+ return "[EOSortOrdering: key='"+key+"' selector='"+selector+"']";
+ }
+
+ public boolean equals( Object anObject )
+ {
+ if ( anObject instanceof EOSortOrdering )
+ {
+ EOSortOrdering x = (EOSortOrdering) anObject;
+ if ( selector().equals( x.selector() ) )
+ {
+ if ( key().equals( x.key() ) )
+ {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Sorts the specified list in place according to the specified
+ * list of EOSortOrderings. The items will be sorted first by the
+ * first ordering, and items with equal values for that property
+ * will be sorted by the next ordering, and so on.
+ */
+ public static void sortArrayUsingKeyOrderArray(
+ List anObjectList, List aSortOrderingList )
+ {
+ List keys = new ArrayList( aSortOrderingList );
+ Collections.reverse( keys );
+ Iterator it = keys.iterator();
+ EOSortOrdering sortOrdering;
+ while ( it.hasNext() )
+ {
+ sortOrdering = (EOSortOrdering) it.next();
+ Collections.sort( anObjectList,
+ new DelegatingComparator(
+ sortOrdering.key(), sortOrdering.selector() ) );
+ }
+ }
+
+ /**
+ * Sorts the specified list in place according to the specified
+ * list of EOSortOrderings. The items will be sorted first by the
+ * first ordering, and items with equal values for that property
+ * will be sorted by the next ordering, and so on.
+ */
+ public static NSArray sortedArrayUsingKeyOrderArray(
+ List anObjectList, List aSortOrderingList )
+ {
+ NSArray result = new NSMutableArray();
+ result.addAll( anObjectList );
+ sortArrayUsingKeyOrderArray( result, aSortOrderingList );
+ return result;
+ }
+
+ public static Object decodeWithKeyValueUnarchiver(EOKeyValueUnarchiver arch) {
+ String k = (String)arch.decodeObjectForKey("key");
+ String sname = (String)arch.decodeObjectForKey("selectorName");
+ NSSelector sel = null;
+ if (sname.equals("compareAscending:"))
+ sel = CompareAscending;
+ else if (sname.equals("compareDescending:"))
+ sel = CompareDescending;
+ else if (sname.equals("compareCaseInsensitiveAscending:"))
+ sel = CompareCaseInsensitiveAscending;
+ else if (sname.equals("compareCaseInsensitiveDescending:"))
+ sel = CompareCaseInsensitiveAscending;
+ else {
+ if (sname.endsWith(":"))
+ sname = sname.substring(0, sname.length()-1);
+ sel = new NSSelector(sname, new Class[]{ Object.class });
+ }
+ return new EOSortOrdering(k, sel);
+ }
+
+ public void encodeWithKeyValueArchiver(EOKeyValueArchiver arch) {
+ arch.encodeObject("EOSortOrdering", "class");
+ arch.encodeObject(key(), "key");
+ if (selector.equals(CompareAscending))
+ arch.encodeObject("compareAscending:", "selectorName");
+ else if (selector.equals(CompareDescending))
+ arch.encodeObject("compareDescending:", "selectorName");
+ else if (selector.equals(CompareCaseInsensitiveAscending))
+ arch.encodeObject("compareCaseInsensitiveAscending:", "selectorName");
+ else if (selector.equals(CompareCaseInsensitiveAscending))
+ arch.encodeObject("compareCaseInsensitiveDescending:", "selectorName");
+ else
+ arch.encodeObject(selector.name() + ":", "selectorName");
+ }
+
+ private static class CompareAscendingComparator
+ extends NSSelector
+ {
+ public int compare(Object o1, Object o2)
+ {
+ if ( o1 instanceof Comparable )
+ {
+ if ( o2 instanceof Comparable )
+ {
+ return ((Comparable)o1).compareTo( o2 );
+ }
+ }
+
+ // null handling: null is less than any object
+
+ if ( o1 == null )
+ {
+ if ( o2 == null )
+ {
+ return 0;
+ }
+ else
+ {
+ return -1;
+ }
+ }
+ else // o1 != null
+ if ( o2 == null )
+ {
+ return 1;
+ }
+
+ // fall back on string representation comparison
+
+ return o1.toString().compareTo( o2.toString() );
+ }
+
+ public boolean equals(Object obj)
+ {
+ return ( this == obj );
+ }
+ }
+
+ private static class CompareDescendingComparator
+ extends CompareAscendingComparator
+ {
+ public int compare(Object o1, Object o2)
+ {
+ return -1 * super.compare( o1, o2 );
+ }
+ }
+
+ private static class CompareCaseInsensitiveAscendingComparator
+ extends NSSelector
+ {
+ public int compare(Object o1, Object o2)
+ {
+ // null handling: null is less than any object
+
+ if ( o1 == null )
+ {
+ if ( o2 == null )
+ {
+ return 0;
+ }
+ else
+ {
+ return -1;
+ }
+ }
+ else // o1 != null
+ if ( o2 == null )
+ {
+ return 1;
+ }
+
+ return o1.toString().toLowerCase().compareTo(
+ o2.toString().toLowerCase() );
+ }
+
+ public boolean equals(Object obj)
+ {
+ return ( this == obj );
+ }
+ }
+
+ private static class CompareCaseInsensitiveDescendingComparator
+ extends CompareCaseInsensitiveAscendingComparator
+ {
+ public int compare(Object o1, Object o2)
+ {
+ return -1 * super.compare( o1, o2 );
+ }
+ }
+
+ private static class DelegatingComparator implements Comparator
+ {
+ private String key;
+ private Comparator comparator;
+
+ public DelegatingComparator( String aKey, Comparator aComparator )
+ {
+ key = aKey;
+ comparator = aComparator;
+ }
+
+ public int compare(Object o1, Object o2)
+ {
+ Object v1, v2;
+ if ( o1 instanceof EOKeyValueCoding )
+ {
+ v1 = ((EOKeyValueCoding)o1).valueForKey( key );
+ }
+ else
+ {
+ v1 = EOKeyValueCodingSupport.valueForKey( o1, key );
+ }
+ if ( o2 instanceof EOKeyValueCoding )
+ {
+ v2 = ((EOKeyValueCoding)o2).valueForKey( key );
+ }
+ else
+ {
+ v2 = EOKeyValueCodingSupport.valueForKey( o2, key );
+ }
+ return comparator.compare( v1, v2 );
+ }
+
+ public boolean equals(Object obj)
+ {
+ return ( this == obj );
+ }
+ }
+
+}
+
+/*
+ * $Log$
+ * Revision 1.2 2006/02/16 16:47:14 cgruber
+ * Move some classes in to "internal" packages and re-work imports, etc.
+ *
+ * Also use UnsupportedOperationExceptions where appropriate, instead of WotonomyExceptions.
+ *
+ * Revision 1.1 2006/02/16 13:19:57 cgruber
+ * Check in all sources in eclipse-friendly maven-enabled packages.
+ *
+ * Revision 1.14 2003/08/11 19:39:52 chochos
+ * now encodes/decodes correctly.
+ *
+ * Revision 1.13 2003/08/09 01:29:56 chochos
+ * implements EOKeyValueArchiving
+ *
+ * Revision 1.12 2003/08/06 23:07:52 chochos
+ * general code cleanup (mostly, removing unused imports)
+ *
+ * Revision 1.11 2003/02/07 20:23:23 mpowers
+ * Provided backwards compatibility for comparators.
+ *
+ * Revision 1.10 2003/01/18 23:46:58 mpowers
+ * EOSortOrdering is now correctly using NSSelectors.
+ *
+ * Revision 1.9 2003/01/16 22:47:30 mpowers
+ * Compatibility changes to support compiling woextensions source.
+ * (34 out of 56 classes compile!)
+ *
+ * Revision 1.8 2002/03/01 20:23:27 mpowers
+ * Implemented equals.
+ *
+ * Revision 1.7 2002/02/06 21:15:04 mpowers
+ * Added null handling to CompareCaseInsensitiveAscendingComparator.
+ *
+ * Revision 1.6 2001/12/01 23:51:24 mpowers
+ * Made serializable.
+ *
+ * Revision 1.5 2001/03/29 03:29:49 mpowers
+ * Now using KeyValueCoding and Support instead of Introspector.
+ *
+ * Revision 1.4 2001/01/24 22:15:52 mpowers
+ * Fixed npe when comparing nulls.
+ *
+ * Revision 1.3 2001/01/12 19:11:56 mpowers
+ * Fixed table column click sorting.
+ *
+ * Revision 1.2 2001/01/12 17:23:46 mpowers
+ * Inner classes are now private.
+ *
+ * Revision 1.1 2001/01/11 20:34:26 mpowers
+ * Implemented EOSortOrdering and added support in framework.
+ * Added header-click to sort table columns.
+ *
+ *
+ */
+
+
diff --git a/projects/net.wotonomy.persistence/src/main/java/net/wotonomy/control/EOTemporaryGlobalID.java b/projects/net.wotonomy.persistence/src/main/java/net/wotonomy/control/EOTemporaryGlobalID.java
new file mode 100644
index 0000000..44ba855
--- /dev/null
+++ b/projects/net.wotonomy.persistence/src/main/java/net/wotonomy/control/EOTemporaryGlobalID.java
@@ -0,0 +1,219 @@
+/*
+Wotonomy: OpenStep design patterns for pure Java applications.
+Copyright (C) 2001 Intersect Software Corporation
+
+This library is free software; you can redistribute it and/or
+modify it under the terms of the GNU Lesser General Public
+License as published by the Free Software Foundation; either
+version 2.1 of the License, or (at your option) any later version.
+
+This library is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+Lesser General Public License for more details.
+
+You should have received a copy of the GNU Lesser General Public
+License along with this library; if not, see http://www.gnu.org
+*/
+
+package net.wotonomy.control;
+
+import java.net.InetAddress;
+
+/**
+* EOTemporaryGlobalID is a network-wide unique key.
+* This is used by EOEditingContext to construct temporary
+* ids when new objects are created. <br><br>
+*
+* The specified format of the key is a byte array:
+* &lt; Sequence [2], ProcessID [2], Time [4], IP Addr [4] &gt;,
+* but because java does not allow access to the process id,
+* the timestamp of when this class is first loaded is used
+* to simulate a process id.
+*
+*
+* @author michael@mpowers.net
+* @author $Author: cgruber $
+* @version $Revision: 893 $
+*/
+public class EOTemporaryGlobalID
+ extends EOGlobalID
+{
+ /**
+ * Holds the length in bytes of the key that is generated.
+ */
+ public static final int UniqueBinaryKeyLength = 12;
+
+ private static int sequence;
+ private static byte[] processid; // 2 bytes
+ private static byte[] ipaddr; // 4 bytes
+
+ static // static initializer
+ {
+ // init sequence
+ sequence = 0;
+
+ // init processid
+ processid = new byte[2];
+ long time = System.currentTimeMillis();
+ processid[1] = (byte) time;
+ time = time >> 8;
+ processid[0] = (byte) time;
+
+ // init ipaddr
+ ipaddr = new byte[4];
+ try
+ {
+ ipaddr = InetAddress.getLocalHost().getAddress();
+ }
+ catch ( Exception exc )
+ {
+ // could not obtain ip address - use pid twice
+ ipaddr[0] = processid[0];
+ ipaddr[1] = processid[1];
+ ipaddr[2] = processid[0];
+ ipaddr[3] = processid[1];
+ }
+ }
+
+ private byte[] key;
+ private int hashCode;
+
+ /**
+ * Generates a new id with a unique key.
+ */
+ public EOTemporaryGlobalID()
+ {
+ key = new byte[UniqueBinaryKeyLength];
+
+ // init sequence (important byte first)
+ key[0] = (byte) ( sequence );
+ key[1] = (byte) ( sequence >> 8 );
+ sequence++;
+
+ // populate pid (important byte first)
+ key[2] = processid[1];
+ key[3] = processid[0];
+
+ // init time (important byte first)
+ long time = System.currentTimeMillis();
+ key[4] = (byte) time;
+ time = time >> 8;
+ key[5] = (byte) time;
+ time = time >> 8;
+ key[6] = (byte) time;
+ time = time >> 8;
+ key[7] = (byte) time;
+
+ // populate ipaddr
+ key[8] = ipaddr[0];
+ key[9] = ipaddr[1];
+ key[10] = ipaddr[2];
+ key[11] = ipaddr[3];
+
+ // use string's hash code
+ hashCode = new String( key ).hashCode();
+ }
+
+ /**
+ * Private constructor for cloning.
+ */
+ private EOTemporaryGlobalID( byte[] aKey )
+ {
+// key = aKey; // this might be faster
+
+ // make copy of key - might be safer
+ key = new byte[ UniqueBinaryKeyLength ];
+ for ( int i = 0; i < UniqueBinaryKeyLength; i++ )
+ {
+ key[i] = aKey[i];
+ }
+
+ // use string's hash code
+ hashCode = new String( aKey ).hashCode();
+ }
+
+ /**
+ * Returns true.
+ */
+ public boolean isTemporary()
+ {
+ return true;
+ }
+
+ /**
+ * Returns whether the keys are equal.
+ */
+ public boolean equals( Object anObject )
+ {
+ if ( ! ( anObject instanceof EOTemporaryGlobalID ) )
+ return false;
+
+ byte[] otherKey = ((EOTemporaryGlobalID)anObject).key;
+
+ for ( int i = 0; i < UniqueBinaryKeyLength; i++ )
+ {
+ if ( key[i] != otherKey[i] ) return false;
+ }
+ return true;
+ }
+
+ /**
+ * Returns a copy of this object.
+ */
+ public Object clone()
+ {
+ // faster than super.clone()
+ return new EOTemporaryGlobalID( key );
+ }
+
+ public int hashCode()
+ {
+ return hashCode;
+ }
+
+ /**
+ * Returns a string representation of this key.
+ * This is a 24-character string with each pair
+ * of characters holding a hexadecimal value that
+ * is 128 more than the value of the corresponding
+ * byte (to account for two's complement).
+ */
+ public String toString()
+ {
+ String hex;
+ StringBuffer buffer = new StringBuffer();
+ for ( int i = 0; i < key.length; i++ )
+ {
+ // get string: adjust for two's complement
+ hex = Integer.toHexString( key[i]+128 );
+ // pad with zero so we take two characters
+ if ( hex.length() == 1 ) hex = "0" + hex;
+ // append hex code
+ buffer.append( hex );
+ }
+ return buffer.toString();
+ }
+}
+
+/*
+ * $Log$
+ * Revision 1.1 2006/02/16 13:19:57 cgruber
+ * Check in all sources in eclipse-friendly maven-enabled packages.
+ *
+ * Revision 1.4 2001/04/29 22:02:45 mpowers
+ * Work on id transposing between editing contexts.
+ *
+ * Revision 1.3 2001/02/15 21:13:30 mpowers
+ * First draft implementation is complete. Now on to debugging.
+ *
+ * Revision 1.2 2001/02/14 23:03:02 mpowers
+ * A near-complete first draft of EOEditingContext.
+ *
+ * Revision 1.1 2001/02/13 23:24:29 mpowers
+ * Implementing more of editing context.
+ *
+ *
+ */
+
+
diff --git a/projects/net.wotonomy.persistence/src/main/java/net/wotonomy/control/EOValidation.java b/projects/net.wotonomy.persistence/src/main/java/net/wotonomy/control/EOValidation.java
new file mode 100644
index 0000000..a0aa4db
--- /dev/null
+++ b/projects/net.wotonomy.persistence/src/main/java/net/wotonomy/control/EOValidation.java
@@ -0,0 +1,72 @@
+/*
+Wotonomy: OpenStep design patterns for pure Java applications.
+Copyright (C) 2001 Michael Powers
+
+This library is free software; you can redistribute it and/or
+modify it under the terms of the GNU Lesser General Public
+License as published by the Free Software Foundation; either
+version 2.1 of the License, or (at your option) any later version.
+
+This library is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+Lesser General Public License for more details.
+
+You should have received a copy of the GNU Lesser General Public
+License along with this library; if not, see http://www.gnu.org
+*/
+
+package net.wotonomy.control;
+
+/**
+* EOValidation provides methods for validating a operation
+* on an object as a whole, rather than on an individual property.
+*
+* @author michael@mpowers.net
+* @author $Author: cgruber $
+* @version $Revision: 893 $
+*/
+public interface EOValidation
+{
+ /**
+ * Validates this object for delete.
+ * Throws an exception if this object cannot be deleted.
+ */
+ void validateForDelete();
+
+ /**
+ * Validates this object for insertion into the external store.
+ * Throws an exception if this object cannot be inserted.
+ * Validations here should be specific to insertion.
+ * Implementations may call validateForSave().
+ */
+ void validateForInsert();
+
+ /**
+ * Validates this object for a commit to the external store.
+ * Throws an exception if this object cannot be committed.
+ * Validations here are not specific to either inserts or updates.
+ */
+ void validateForSave();
+
+ /**
+ * Validates this object for update to the external store.
+ * Throws an exception if this object cannot be updated.
+ * Validations here should be specific to updates.
+ * Implementations may call validateForSave().
+ */
+ void validateForUpdate();
+}
+
+/*
+ * $Log$
+ * Revision 1.1 2006/02/16 13:19:57 cgruber
+ * Check in all sources in eclipse-friendly maven-enabled packages.
+ *
+ * Revision 1.1 2001/11/13 04:13:59 mpowers
+ * Added interfaces needed to begin work on EOCustomObject.
+ *
+ *
+ */
+
+
diff --git a/projects/net.wotonomy.persistence/src/main/java/net/wotonomy/control/EOVectorKeyGlobalID.java b/projects/net.wotonomy.persistence/src/main/java/net/wotonomy/control/EOVectorKeyGlobalID.java
new file mode 100644
index 0000000..cc91fd7
--- /dev/null
+++ b/projects/net.wotonomy.persistence/src/main/java/net/wotonomy/control/EOVectorKeyGlobalID.java
@@ -0,0 +1,79 @@
+/*
+ Wotonomy: OpenStep design patterns for pure Java applications.
+ Copyright (C) 2001 Michael Powers
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, see http://www.gnu.org
+ */
+package net.wotonomy.control;
+
+/**
+*
+* @author ezamudio@nasoft.com
+* @author $Author: cgruber $
+* @version $Revision: 893 $
+*/
+public class EOVectorKeyGlobalID extends EOKeyGlobalID {
+
+ protected Object[] _keyValues;
+
+ public EOVectorKeyGlobalID(String entityName, Object[] values) {
+ super(entityName, 0);
+ _keyValues = new Object[values.length];
+ for (int i = 0; i < values.length; i++) {
+ _keyValues[i] = values[i];
+ }
+ }
+
+ /* (non-Javadoc)
+ * @see net.wotonomy.control.EOKeyGlobalID#keyValuesNoCopy()
+ */
+ public Object[] _keyValuesNoCopy() {
+ return _keyValues;
+ }
+
+ /* (non-Javadoc)
+ * @see net.wotonomy.control.EOKeyGlobalID#_keyValues()
+ */
+ public Object[] keyValues() {
+ Object[] v = new Object[_keyValues.length];
+ for (int i = 0; i < _keyValues.length; i++) {
+ v[i] = _keyValues[i];
+ }
+ return v;
+ }
+
+ /* (non-Javadoc)
+ * @see net.wotonomy.control.EOKeyGlobalID#keyCount()
+ */
+ public int keyCount() {
+ return _keyValues.length;
+ }
+
+ /* (non-Javadoc)
+ * @see net.wotonomy.control.EOGlobalID#isTemporary()
+ */
+ public boolean isTemporary() {
+ return false;
+ }
+
+}
+/*
+ * $Log$
+ * Revision 1.1 2006/02/16 13:19:57 cgruber
+ * Check in all sources in eclipse-friendly maven-enabled packages.
+ *
+ * Revision 1.2 2003/08/19 01:59:01 chochos
+ * Added the wotonomy headers
+ *
+ */
diff --git a/projects/net.wotonomy.persistence/src/main/java/net/wotonomy/control/EditingContext.java b/projects/net.wotonomy.persistence/src/main/java/net/wotonomy/control/EditingContext.java
new file mode 100644
index 0000000..ee40f23
--- /dev/null
+++ b/projects/net.wotonomy.persistence/src/main/java/net/wotonomy/control/EditingContext.java
@@ -0,0 +1,283 @@
+/*
+Wotonomy: OpenStep design patterns for pure Java applications.
+Copyright (C) 2001 Michael Powers
+
+This library is free software; you can redistribute it and/or
+modify it under the terms of the GNU Lesser General Public
+License as published by the Free Software Foundation; either
+version 2.1 of the License, or (at your option) any later version.
+
+This library is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+Lesser General Public License for more details.
+
+You should have received a copy of the GNU Lesser General Public
+License along with this library; if not, see http://www.gnu.org
+*/
+
+package net.wotonomy.control;
+
+import java.util.List;
+import java.util.Map;
+
+// swing dependency for undo manager
+//import javax.swing.undo.UndoManager;
+
+/**
+* EditingContext provides transactional support for
+* fetching, editing, and committing changes made on a
+* collection of objects to a parent object store.
+* This subclasses EOEditingContext to provide
+* java-friendly conveniences.
+*
+* @author michael@mpowers.net
+* @author $Author: cgruber $
+* @version $Revision: 894 $
+*/
+public class EditingContext extends EOEditingContext
+{
+ /**
+ * Default constructor creates a new editing context
+ * that uses the default object store. If the default
+ * object store has not been set, an exception is thrown.
+ */
+ public EditingContext()
+ {
+ this( defaultParentObjectStore() );
+ }
+
+ /**
+ * Creates a new editing context that uses the specified
+ * object store as its parent object store.
+ */
+ public EditingContext( EOObjectStore anObjectStore )
+ {
+ super( anObjectStore );
+ }
+
+ /**
+ * Returns a List of objects associated with the object
+ * with the specified id for the specified property
+ * relationship, or may return a placeholder array that
+ * will defer the fetch until needed (aka an array fault).
+ * All objects must be registered in the specified editing context.
+ * This implementation calls to its parent object store's
+ * implementation if the requested source object is not
+ * registered in this editing context.
+ * The specified relationship key must produce a result of
+ * type Collection for the source object or an exception is thrown.
+ */
+ public List getArrayFaultWithSourceGlobalID (
+ EOGlobalID aGlobalID,
+ String aRelationshipKey,
+ EOEditingContext aContext )
+ {
+ return arrayFaultWithSourceGlobalID(
+ aGlobalID, aRelationshipKey, aContext );
+ }
+
+ /**
+ * Returns a snapshot of the specified object as it
+ * existed when it was last read or committed to the
+ * parent object store.
+ */
+ public Map getCommittedSnapshotForObject (
+ Object anObject )
+ {
+ return committedSnapshotForObject( anObject );
+ }
+
+ /**
+ * Returns a snapshot of the specified object as it
+ * existed before the edits triggered by the current
+ * event loop were processed.
+ */
+ public Map getCurrentEventSnapshotForObject (
+ Object anObject )
+ {
+ return currentEventSnapshotForObject( anObject );
+ }
+
+ /**
+ * Returns the delegate for this editing context,
+ * or null if no delegate has been set.
+ */
+ public Object getDelegate ()
+ {
+ return delegate();
+ }
+
+ /**
+ * Returns a List of all objects marked as deleted
+ * in this editing context.
+ */
+ public List getDeletedObjects ()
+ {
+ return deletedObjects();
+ }
+
+ /**
+ * Returns a List of registered editors of this
+ * editing context.
+ */
+ public List getEditors()
+ {
+ return editors();
+ }
+
+ /**
+ * Returns the object for the specified id.
+ * If the object's data has not been fetched,
+ * it will be fetched when needed.
+ */
+ public Object getFaultForGlobalID (
+ EOGlobalID aGlobalID )
+ {
+ return faultForGlobalID( aGlobalID, this );
+ }
+
+ /**
+ * Returns a fault representing an object of
+ * the specified entity type with values from
+ * the specified dictionary.
+ */
+ public Object getFaultForRawRow (
+ Map aDictionary,
+ String anEntityName )
+ {
+ return faultForRawRow( aDictionary, anEntityName );
+ }
+
+ /**
+ * Returns the fetch timestamp for this editing context.
+ */
+ public double getFetchTimestamp()
+ {
+ return fetchTimestamp();
+ }
+
+ /**
+ * Returns the id for the specified object, or null
+ * if the object is not registered in this context.
+ */
+ public EOGlobalID getGlobalIDForObject (
+ Object anObject )
+ {
+ return globalIDForObject( anObject );
+ }
+
+ /**
+ * Returns a List of the objects that have been
+ * inserted into this editing context.
+ */
+ public List getInsertedObjects ()
+ {
+ return insertedObjects();
+ }
+
+ /**
+ * Returns the message handler for this editing context,
+ * or null if no message handler has been set.
+ */
+ public Object getMessageHandler ()
+ {
+ return messageHandler();
+ }
+
+ /**
+ * Returns the object registered in this editing context
+ * for the specified id, or null if that id is not
+ * registered.
+ */
+ public Object getObjectForGlobalID (
+ EOGlobalID aGlobalID )
+ {
+ return objectForGlobalID( aGlobalID );
+ }
+
+ /**
+ * Returns a List of objects the meet the criteria of
+ * the supplied specification.
+ */
+ public List getObjectsWithFetchSpecification (
+ EOFetchSpecification aFetchSpec )
+ {
+ return objectsWithFetchSpecification( aFetchSpec );
+ }
+
+ /**
+ * Returns the parent object store for this editing context.
+ * The result will not be null.
+ */
+ public EOObjectStore getParentObjectStore ()
+ {
+ return parentObjectStore();
+ }
+
+ /**
+ * Returns a List of all objects registered in this
+ * editing context.
+ */
+ public List geRegisteredObjects ()
+ {
+ return registeredObjects();
+ }
+
+ /**
+ * Returns the root object store, which is the parent
+ * of all parent object stores of this editing context.
+ */
+ public EOObjectStore getRootObjectStore ()
+ {
+ return rootObjectStore();
+ }
+
+ /**
+ * Returns a list of all objects marked as modified,
+ * but not inserted or deleted, in this editing context.
+ */
+ public List getUpdatedObjects ()
+ {
+ return updatedObjects();
+ }
+
+ // static methods
+
+ public static double getDefaultFetchTimestampLag()
+ {
+ return defaultFetchTimestampLag();
+ }
+
+ /**
+ * Returns the default parent object store for all
+ * object stores created with the parameterless
+ * constructor.
+ */
+ public static EOObjectStore getDefaultParentObjectStore()
+ {
+ return defaultParentObjectStore();
+ }
+
+}
+
+/*
+ * $Log$
+ * Revision 1.2 2006/02/16 16:47:14 cgruber
+ * Move some classes in to "internal" packages and re-work imports, etc.
+ *
+ * Also use UnsupportedOperationExceptions where appropriate, instead of WotonomyExceptions.
+ *
+ * Revision 1.1 2006/02/16 13:19:57 cgruber
+ * Check in all sources in eclipse-friendly maven-enabled packages.
+ *
+ * Revision 1.2 2003/08/06 23:07:52 chochos
+ * general code cleanup (mostly, removing unused imports)
+ *
+ * Revision 1.1 2002/03/26 21:46:36 mpowers
+ * Contributing EditingContext as a java-friendly convenience.
+ *
+ *
+ */
+
+
diff --git a/projects/net.wotonomy.persistence/src/main/java/net/wotonomy/control/KeyValueCodingUtilities.java b/projects/net.wotonomy.persistence/src/main/java/net/wotonomy/control/KeyValueCodingUtilities.java
new file mode 100644
index 0000000..1aa2147
--- /dev/null
+++ b/projects/net.wotonomy.persistence/src/main/java/net/wotonomy/control/KeyValueCodingUtilities.java
@@ -0,0 +1,740 @@
+/*
+Wotonomy: OpenStep design patterns for pure Java applications.
+Copyright (C) 2001 Intersect Software Corporation
+
+This library is free software; you can redistribute it and/or
+modify it under the terms of the GNU Lesser General Public
+License as published by the Free Software Foundation; either
+version 2.1 of the License, or (at your option) any later version.
+
+This library is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+Lesser General Public License for more details.
+
+You should have received a copy of the GNU Lesser General Public
+License along with this library; if not, see http://www.gnu.org
+*/
+
+package net.wotonomy.control;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.io.ObjectStreamClass;
+import java.io.OutputStream;
+import java.io.Serializable;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+
+import net.wotonomy.foundation.NSDictionary;
+import net.wotonomy.foundation.NSMutableDictionary;
+import net.wotonomy.foundation.internal.Duplicator;
+import net.wotonomy.foundation.internal.WotonomyException;
+
+/**
+* KeyValueCodingUtilities implements what
+* EOKeyValueCodingSupport leaves out. Importantly,
+* this class implements the deep clone and deep copy
+* operations that are essential to the functioning of
+* nested editing contexts.
+*
+* @author michael@mpowers.net
+* @author $Author: cgruber $
+* @version $Revision: 900 $
+*/
+public class KeyValueCodingUtilities
+{
+ /**
+ * Returns a Map of the specified keys to their values,
+ * each of which is obtained by calling valueForKey
+ * on the specified object if it implements EOKeyValueCoding,
+ * and otherwise falling back on EOKeyValueCodingSupport.
+ * Null values must be represented by NSNull.nullValue().
+ */
+ static public NSDictionary valuesForKeys(
+ Object anObject, List aKeyList )
+ {
+ return valuesForKeys( anObject, aKeyList, false );
+ }
+
+ /**
+ * Returns a Map of the specified keys to their values,
+ * each of which is obtained by calling storedValueForKey
+ * on the specified object if it implements EOKeyValueCoding,
+ * and otherwise falling back on EOKeyValueCodingSupport.
+ * Null values must be represented by NSNull.nullValue().
+ */
+ static public NSDictionary storedValuesForKeys(
+ Object anObject, List aKeyList )
+ {
+ return valuesForKeys( anObject, aKeyList, true );
+ }
+
+ /**
+ * Called by valuesForKeys and storedValuesForKeys.
+ * This uses storedValueForKey if isStored is true,
+ * otherwise uses valueForKey.
+ */
+ static private NSDictionary valuesForKeys(
+ Object anObject, List aKeyList, boolean isStored )
+ {
+ EOKeyValueCoding coding;
+ if ( anObject instanceof EOKeyValueCoding )
+ {
+ coding = (EOKeyValueCoding) anObject;
+ }
+ else
+ {
+ coding = null;
+ }
+
+ String key;
+ Object value;
+ NSMutableDictionary result = new NSMutableDictionary();
+ Iterator it = aKeyList.iterator();
+ while ( it.hasNext() )
+ {
+ //TODO: get rid of this try/catch - exceptions should be fatal (?)
+ try
+ {
+ key = it.next().toString();
+ if ( coding != null )
+ {
+ if ( isStored )
+ value = coding.storedValueForKey( key );
+ else
+ value = coding.valueForKey( key );
+ }
+ else
+ {
+ if ( isStored )
+ value = EOKeyValueCodingSupport.storedValueForKey( anObject, key );
+ else
+ value = EOKeyValueCodingSupport.valueForKey( anObject, key );
+ }
+ if ( value == null )
+ {
+ value = EONullValue.nullValue();
+ }
+ result.setObjectForKey( value, key );
+ }
+ catch ( RuntimeException exc )
+ {
+ System.out.println(
+ "KeyValueCodingUtilities.valuesForKeys: "
+ + isStored + " : " + exc );
+ }
+ }
+ return result;
+ }
+
+ /**
+ * Takes the keys from the specified Map as properties
+ * and applies the corresponding values, each of which
+ * might be set by calling takeValueForKey on the
+ * specified object if it implements EOKeyValueCoding,
+ * and otherwise falling back on EOKeyValueCodingSupport.
+ * Null values must be represented by NSNull.nullValue().
+ */
+ static public void takeValuesFromDictionary(
+ Object anObject, Map aMap )
+ {
+ takeStoredValuesFromDictionary( anObject, aMap, false );
+ }
+
+ /**
+ * Takes the keys from the specified Map as properties
+ * and applies the corresponding values, each of which
+ * might be set by calling takeStoredValueForKey on the
+ * specified object if it implements EOKeyValueCoding,
+ * and otherwise falling back on EOKeyValueCodingSupport.
+ * Null values must be represented by NSNull.nullValue().
+ */
+ static public void takeStoredValuesFromDictionary(
+ Object anObject, Map aMap )
+ {
+ takeStoredValuesFromDictionary( anObject, aMap, true );
+ }
+
+ /**
+ * Called by takeValuesFromDictionary and takeStoredValuesFromDictionary.
+ * This uses takeStoredValueForKey if isStored is true,
+ * otherwise uses takeValueForKey.
+ */
+ static private void takeStoredValuesFromDictionary(
+ Object anObject, Map aMap, boolean isStored )
+ {
+ EOKeyValueCoding coding;
+ if ( anObject instanceof EOKeyValueCoding )
+ {
+ coding = (EOKeyValueCoding) anObject;
+ }
+ else
+ {
+ coding = null;
+ }
+
+ String key;
+ Object value;
+ NSMutableDictionary result = new NSMutableDictionary();
+ Iterator it = aMap.keySet().iterator();
+ while ( it.hasNext() )
+ {
+ //TODO: get rid of this try/catch - exceptions should be fatal (?)
+ try
+ {
+ key = it.next().toString();
+ value = aMap.get( key );
+ if ( value instanceof EONullValue )
+ // can't use == nullValue() because of cloning/serialization
+ {
+ value = null;
+ }
+ if ( coding != null )
+ {
+ if ( isStored )
+ coding.takeStoredValueForKey( value, key );
+ else
+ coding.takeValueForKey( value, key );
+ }
+ else
+ {
+ if ( isStored )
+ EOKeyValueCodingSupport.takeStoredValueForKey(
+ anObject, value, key );
+ else
+ EOKeyValueCodingSupport.takeValueForKey(
+ anObject, value, key );
+ }
+ }
+ catch ( WotonomyException exc )
+ {
+ System.out.println(
+ "KeyValueCodingUtilities.takeStoredValuesFromDictionary: "
+ + isStored + " : " + exc );
+ }
+ }
+ }
+
+ /**
+ * Creates a deep clone of the specified object.
+ * (Object.clone() only creates a shallow clone.)
+ * Returns null if operation fails.
+ */
+ static public Object clone( Object aSource )
+ {
+ return Duplicator.deepClone( aSource );
+ }
+
+ /**
+ * Creates a deep clone of the specified object,
+ * registered in the specified source editing context,
+ * transposing it into the specified destination
+ * editing context.
+ * Returns null if operation fails.
+ */
+ static public Object clone(
+ EOEditingContext aSourceContext, Object aSource,
+ EOEditingContext aDestinationContext )
+ {
+ return clone( aSourceContext, aSource, aDestinationContext, aSource );
+ }
+
+ /**
+ * Called by clone and copy.
+ * The specified root object will not be replaced
+ * by an object in the destination editing context:
+ * this should be the same as the source object for
+ * cloning, but should be null for copying.
+ * Returns null if operation fails.
+ */
+ static private Object clone(
+ EOEditingContext aSourceContext, Object aSource,
+ EOEditingContext aDestinationContext,
+ Object aRootObject )
+ {
+
+//System.out.println();
+//System.out.println( "clone: " + aSourceContext );
+//System.out.println( " : " + aSource );
+//System.out.println( " : " + aDestinationContext );
+//System.out.println();
+
+ // the only known way to deep copy in
+ // java without native code is serialization
+
+ return thaw(
+ freeze( aSource, aSourceContext, aRootObject, true ),
+ aDestinationContext, true );
+ }
+
+ /**
+ * Serializes an object to a byte array containing
+ * GlobalIDMarkers in place of references to other objects
+ * registered in the specified context.
+ * The specified root object will be serialized,
+ * even if it is registered in the specified context:
+ * this is typically the root object you're trying to
+ * serialize.
+ * Package access, as this method is used by editing
+ * context for snapshots.
+ */
+ static public byte[] freeze(
+ Object anObject, EOEditingContext aContext, Object aRootObject, boolean transpose )
+ {
+ try
+ {
+//long t = System.currentTimeMillis();
+ ByteArrayOutputStream byteOutput =
+ new ByteArrayOutputStream();// CloneBufferSize );
+ ObjectOutputStream objectOutput;
+ if ( transpose )
+ {
+ objectOutput =
+ new TransposingContextObjectOutputStream(
+ byteOutput, aContext, aRootObject );
+ }
+ else
+ {
+ objectOutput =
+ new ContextObjectOutputStream(
+ byteOutput, aContext );
+ }
+
+ objectOutput.writeObject( anObject );
+ objectOutput.flush();
+ objectOutput.close();
+
+ return byteOutput.toByteArray();
+
+// profiling
+/*
+byte[] result = byteOutput.toByteArray();
+long size = result.length;
+long time = ( System.currentTimeMillis() - t );
+maxSize = Math.max( size, maxSize );
+minSize = Math.min( size, minSize );
+totSize += size;
+maxTime = Math.max( time, maxTime );
+minTime = Math.min( time, minTime );
+totTime += time;
+nTime++;
+System.out.println( "freeze: size = [ " + size + " : " + minSize + " : " + ( (float)totSize / (float)nTime ) + " : " + maxSize
++ " ] time = [ " + time + " : " + minTime + " : " + ( (float)totTime / (float)nTime ) + " : " + maxTime + " ]" );
+return result;
+*/
+// end profiling
+
+ }
+ catch ( Exception exc )
+ {
+ throw new WotonomyException( exc );
+ }
+ }
+
+//static long maxTime, minTime, totTime, nTime, maxSize, minSize, totSize;
+//static long maxTimeThaw, minTimeThaw, totTimeThaw, nTimeThaw;
+
+ /**
+ * De-serializes an object from the specified byte
+ * array, replacing GlobalIDMarkers with reference
+ * to objects registered in the specified editing
+ * context.
+ * Package access, as this method is used by editing
+ * context for snapshots.
+ */
+ static public Object thaw(
+ byte[] aByteArray, EOEditingContext aContext, boolean transpose )
+ {
+ return thaw( aByteArray, aContext, null, transpose );
+ }
+
+ /**
+ * De-serializes an object from the specified byte
+ * array, replacing GlobalIDMarkers with reference
+ * to objects registered in the specified editing
+ * context.
+ * Package access, as this method is used by editing
+ * context for snapshots.
+ */
+ static public Object thaw(
+ byte[] aByteArray, EOEditingContext aContext, ClassLoader aLoader, boolean transpose )
+ {
+ try
+ {
+//long t = System.currentTimeMillis();
+ ByteArrayInputStream byteInput =
+ new ByteArrayInputStream( aByteArray );
+ ObjectInputStream objectInput;
+ if ( transpose )
+ {
+ objectInput =
+ new TransposingContextObjectInputStream(
+ byteInput, aContext, aLoader );
+ }
+ else
+ {
+ objectInput =
+ new ContextObjectInputStream(
+ byteInput, aContext, aLoader );
+ }
+
+ return objectInput.readObject();
+// profiling
+/*
+Object result = objectInput.readObject();
+long timeThaw = ( System.currentTimeMillis() - t );
+maxTimeThaw = Math.max( timeThaw, maxTimeThaw );
+minTimeThaw = Math.min( timeThaw, minTimeThaw );
+totTimeThaw += timeThaw;
+nTimeThaw++;
+System.out.println( "thaw: size = " + aByteArray.length + ", time = [ " + timeThaw + " : " + minTimeThaw + " : " + ( (float)totTimeThaw / (float)nTimeThaw ) + " : " + maxTimeThaw + " ]" );
+return result;
+*/
+// end profiling
+ }
+ catch ( Exception exc )
+ {
+ throw new WotonomyException( exc );
+ }
+ }
+
+ /**
+ * Copies values from one object registered in the
+ * specified origin context to the specified destination
+ * object
+ * The values themselves are cloned, so this is a deep copy.
+ * Returns the destination object, or throws exception
+ * if operation fails.
+ */
+ static public Object copy( Object aSource, Object aDestination )
+ {
+ NSDictionary values = (NSDictionary)
+ clone( valuesForKeys( aSource,
+ EOClassDescription.classDescriptionForClass(
+ aSource.getClass() ).attributeKeys() ) );
+
+ takeStoredValuesFromDictionary( aDestination, values );
+ return aDestination;
+ }
+
+ /**
+ * Copies values from one object registered in the
+ * specified origin context to the specified destination
+ * object
+ * The values themselves are cloned, so this is a deep copy.
+ * Returns the destination object, or throws exception
+ * if operation fails.
+ */
+ static public Object copy(
+ EOEditingContext aSourceContext, Object aSource,
+ EOEditingContext aDestinationContext, Object aDestination )
+ {
+ // get all keys for this object
+ EOClassDescription classDesc =
+ EOClassDescription.classDescriptionForClass( aSource.getClass() );
+ List keys = new LinkedList();
+ keys.addAll( classDesc.attributeKeys() );
+ keys.addAll( classDesc.toOneRelationshipKeys() );
+ keys.addAll( classDesc.toManyRelationshipKeys() );
+
+ // transpose all objects registered in source context
+ NSDictionary values = storedValuesForKeys( aSource, keys );
+ values = (NSDictionary)
+ clone( aSourceContext, values, aDestinationContext, null );
+
+ // apply to destination object
+ takeStoredValuesFromDictionary( aDestination, values );
+ return aDestination;
+ }
+
+ // inner classes
+
+ /**
+ * An ObjectOutputStream that serializes objects with references
+ * to an editing context. The specified context will not be
+ * serialized but referenced, so that a ContextObjectInputStream
+ * can replace the reference with another editing context.
+ */
+ static private class ContextObjectOutputStream extends ObjectOutputStream
+ {
+ private EditingContextMarker marker = new EditingContextMarker();
+ protected EOEditingContext editingContext;
+
+ /**
+ * Specifies the output stream to wrap,
+ * and the source context that should be
+ * referenced but not serialized.
+ */
+ public ContextObjectOutputStream(
+ OutputStream anOutputStream,
+ EOEditingContext aContext )
+ throws IOException
+ {
+ super( anOutputStream );
+ editingContext = aContext;
+ try
+ {
+ enableReplaceObject(true);
+ }
+ catch ( Exception exc )
+ {
+ exc.printStackTrace();
+ }
+ }
+
+ protected Object replaceObject(Object anObject) throws IOException
+ {
+// if ( anObject == editingContext ) return marker;
+//FIXME: this should be more strict as above
+ if ( anObject instanceof EOEditingContext ) return marker;
+ return anObject;
+ }
+
+ }
+
+ /**
+ * A ContextObjectOutputStream that replaces any objects registered
+ * in the source editing context with markers to be used in
+ * ContextObjectInputStream.
+ */
+ static private class TransposingContextObjectOutputStream
+ extends ContextObjectOutputStream
+ {
+ protected Object rootObject;
+
+ /**
+ * Specifies the output stream to wrap,
+ * the source context containing objects that
+ * should be replaced if found,
+ * and the object which should not be re-registered,
+ * which is typically the object being cloned, but
+ * may be null.
+ */
+ public TransposingContextObjectOutputStream(
+ OutputStream anOutputStream,
+ EOEditingContext aContext,
+ Object anObject )
+ throws IOException
+ {
+ super( anOutputStream, aContext );
+ rootObject = anObject;
+ }
+
+ protected Object replaceObject(Object anObject) throws IOException
+ {
+ if ( anObject == rootObject ) return anObject;
+ if ( editingContext != null )
+ {
+ EOGlobalID id = editingContext.globalIDForObject( anObject );
+ if ( id != null )
+ {
+ Object result = new GlobalIDMarker( id );
+ //System.out.println( "KeyValueCodingUtilities.replaceObject: returning: " + result );
+ return result;
+ }
+ }
+ return super.replaceObject( anObject );
+ }
+
+ }
+
+ /**
+ * A marker class so references to objects registered in editing
+ * contexts get transposed rather than cloned.
+ */
+ static private class GlobalIDMarker implements Serializable
+ {
+ private EOGlobalID id;
+
+ public GlobalIDMarker( EOGlobalID anID )
+ {
+ id = anID;
+ }
+
+ public EOGlobalID getID()
+ {
+ return id;
+ }
+
+ public String toString()
+ {
+ return "[GlobalIDMarker:"+id+"]";
+ }
+ }
+
+ /**
+ * A marker class so references an object's editing context
+ * gets transposed rather than cloned.
+ */
+ static private class EditingContextMarker implements Serializable
+ {
+ // just a marker class - no implementation necessary
+ }
+
+ /**
+ * An ObjectInputStream that replaces any markers from
+ * ContextObjectOutputStream with objects registered
+ * in the destination editing context.
+ */
+ static private class ContextObjectInputStream extends ObjectInputStream
+ {
+ protected EOEditingContext editingContext;
+ protected ClassLoader classLoader;
+
+ /**
+ * Specifies the output stream to wrap,
+ * the source context containing objects that
+ * should be to replace any markers.
+ * The class loader may be null.
+ */
+ public ContextObjectInputStream(
+ InputStream anInputStream,
+ EOEditingContext aContext,
+ ClassLoader aClassLoader )
+ throws IOException
+ {
+ super( anInputStream );
+ editingContext = aContext;
+ classLoader = aClassLoader;
+ if ( classLoader == null )
+ {
+ classLoader =
+ KeyValueCodingUtilities.class.getClassLoader();
+ }
+ try
+ {
+ enableResolveObject(true);
+ }
+ catch ( Exception exc )
+ {
+ exc.printStackTrace();
+ }
+ }
+
+ protected Object resolveObject(Object anObject) throws IOException
+ {
+ if ( anObject instanceof EditingContextMarker )
+ {
+ return editingContext;
+ }
+ return anObject;
+ }
+
+ protected Class resolveClass(ObjectStreamClass v)
+ throws IOException, ClassNotFoundException
+ {
+ return classLoader.loadClass( v.getName() );
+ }
+ }
+
+ /**
+ * A ContextObjectInputStream that replaces any markers from
+ * TransposingContextObjectOutputStream with objects registered
+ * in the destination editing context.
+ */
+ static private class TransposingContextObjectInputStream
+ extends ContextObjectInputStream
+ {
+ /**
+ * Specifies the output stream to wrap,
+ * the source context containing objects that
+ * should be to replace any markers.
+ */
+ public TransposingContextObjectInputStream(
+ InputStream anInputStream,
+ EOEditingContext aContext,
+ ClassLoader aClassLoader )
+ throws IOException
+ {
+ super( anInputStream, aContext, aClassLoader );
+ }
+
+ protected Object resolveObject(Object anObject) throws IOException
+ {
+ if ( anObject instanceof GlobalIDMarker )
+ {
+ return editingContext.faultForGlobalID(
+ ((GlobalIDMarker)anObject).getID(), editingContext );
+ }
+ return super.resolveObject( anObject );
+ }
+ }
+
+}
+
+/*
+ * $Log$
+ * Revision 1.3 2006/02/18 22:46:44 cgruber
+ * Add Surrogate map from .util into control's internal package, and fix imports.
+ *
+ * Revision 1.2 2006/02/16 16:47:14 cgruber
+ * Move some classes in to "internal" packages and re-work imports, etc.
+ *
+ * Also use UnsupportedOperationExceptions where appropriate, instead of WotonomyExceptions.
+ *
+ * Revision 1.1 2006/02/16 13:19:57 cgruber
+ * Check in all sources in eclipse-friendly maven-enabled packages.
+ *
+ * Revision 1.15 2003/01/21 22:30:10 mpowers
+ * thaw() now allows you to pass in a class loader.
+ *
+ * Revision 1.14 2002/05/15 13:46:35 mpowers
+ * Exposed freeze and thaw as public.
+ *
+ * Revision 1.13 2001/08/22 19:25:13 mpowers
+ * Added (and commented out) profiling code for freeze.
+ *
+ * Revision 1.12 2001/05/06 18:27:10 mpowers
+ * More broadly catching editing contexts for now.
+ *
+ * Revision 1.11 2001/05/05 13:18:49 mpowers
+ * Fixed: transposing output stream was not returning the object to replace.
+ *
+ * Revision 1.10 2001/05/04 16:57:56 mpowers
+ * Now correctly transposing references to editing contexts when
+ * cloning/copying between editing contexts.
+ *
+ * Revision 1.9 2001/05/04 14:42:58 mpowers
+ * Now getting stored values in KeyValueCoding.
+ * MasterDetail now marks dirty based on whether it's an attribute
+ * or relation.
+ * Implemented editing context marker.
+ *
+ * Revision 1.8 2001/05/02 15:47:40 mpowers
+ * Fixed the pernicious problem with reverts: recordObject was recording
+ * a snapshot of the clone before the transposition-copy happened,
+ * so the revert object would lose all of its transposed relationships.
+ *
+ * Revision 1.7 2001/04/30 12:33:17 mpowers
+ * Fixed problem with use of EONullValue.nullValue(), which can't be used
+ * when we're serializably duplicating objects.
+ *
+ * Revision 1.6 2001/04/30 02:14:25 mpowers
+ * Copying should call takeStoredValueForKeys.
+ *
+ * Revision 1.5 2001/04/29 22:02:45 mpowers
+ * Work on id transposing between editing contexts.
+ *
+ * Revision 1.4 2001/04/29 02:29:31 mpowers
+ * Debugging relationship faulting.
+ *
+ * Revision 1.3 2001/04/28 16:18:44 mpowers
+ * Implementing relationships.
+ *
+ * Revision 1.2 2001/04/28 14:12:23 mpowers
+ * Refactored cloning/copying into KeyValueCodingUtilities.
+ *
+ * Revision 1.1 2001/04/27 23:41:12 mpowers
+ * Contributing file for KeyValueCodingUtilities.
+ *
+ *
+ */
+
+
diff --git a/projects/net.wotonomy.persistence/src/main/java/net/wotonomy/control/ObservableArray.java b/projects/net.wotonomy.persistence/src/main/java/net/wotonomy/control/ObservableArray.java
new file mode 100644
index 0000000..19d39ff
--- /dev/null
+++ b/projects/net.wotonomy.persistence/src/main/java/net/wotonomy/control/ObservableArray.java
@@ -0,0 +1,346 @@
+/*
+Wotonomy: OpenStep design patterns for pure Java applications.
+Copyright (C) 2001 Michael Powers
+
+This library is free software; you can redistribute it and/or
+modify it under the terms of the GNU Lesser General Public
+License as published by the Free Software Foundation; either
+version 2.1 of the License, or (at your option) any later version.
+
+This library is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+Lesser General Public License for more details.
+
+You should have received a copy of the GNU Lesser General Public
+License along with this library; if not, see http://www.gnu.org
+*/
+
+package net.wotonomy.control;
+
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.List;
+
+import net.wotonomy.foundation.NSMutableArray;
+import net.wotonomy.foundation.NSRange;
+
+/**
+* A package class that extends NSMutableArray but makes use
+* of the fact that wotonomy's implementation extends ArrayList
+* to intercept insertions and deletion and register and
+* unregister objects for change notifications as appropriate.
+* Since we can't be sure of ArrayList's implementation, we're
+* forced to override each and every add and remove method,
+* some of which probably call each other. However,
+* EOObserverCenter will only register us once per object.
+*/
+class ObservableArray extends NSMutableArray
+{
+ EOObserving observer;
+
+ ObservableArray( EOObserving anObserver )
+ {
+ observer = anObserver;
+ }
+
+ /**
+ * Removes the last object from the array.
+ */
+ public void removeLastObject ()
+ {
+ remove( count() - 1 );
+ }
+
+ /**
+ * Removes the object at the specified index.
+ */
+ public void removeObjectAtIndex (int index)
+ {
+ remove( index );
+ }
+
+ /**
+ * Adds all objects in the specified collection.
+ */
+ public void addObjectsFromArray (Collection aCollection)
+ {
+ addAll( aCollection );
+ }
+
+ /**
+ * Removes all objects from the array.
+ */
+ public void removeAllObjects ()
+ {
+ clear();
+ }
+
+ /**
+ * Removes all objects equivalent to the specified object
+ * within the range of specified indices.
+ */
+ public void removeObject (Object anObject, NSRange aRange)
+ {
+ if ( ( anObject == null ) || ( aRange == null ) ) return;
+
+ int loc = aRange.location();
+ int max = aRange.maxRange();
+ for ( int i = loc; i < max; i++ )
+ {
+ if ( anObject.equals( get( i ) ) )
+ {
+ remove( i );
+ i = i - 1;
+ max = max - 1;
+ }
+ }
+ }
+
+ /**
+ * Removes all instances of the specified object within the
+ * range of specified indices, comparing by reference.
+ */
+ public void removeIdenticalObject (Object anObject, NSRange aRange)
+ {
+ if ( ( anObject == null ) || ( aRange == null ) ) return;
+
+ int loc = aRange.location();
+ int max = aRange.maxRange();
+ for ( int i = loc; i < max; i++ )
+ {
+ if ( anObject == get( i ) )
+ {
+ remove( i );
+ i = i - 1;
+ max = max - 1;
+ }
+ }
+ }
+
+ /**
+ * Removes all objects in the specified collection from the array.
+ */
+ public void removeObjectsInArray (Collection aCollection)
+ {
+ removeAll( aCollection );
+ }
+
+ /**
+ * Removes all objects in the indices within the specified range
+ * from the array.
+ */
+ public void removeObjectsInRange (NSRange aRange)
+ {
+ if ( aRange == null ) return;
+
+ for ( int i = 0; i < aRange.length(); i++ )
+ {
+ remove( aRange.location() );
+ }
+ }
+
+ /**
+ * Replaces objects in the current range with objects from
+ * the specified range of the specified array. If currentRange
+ * is larger than otherRange, the extra objects are removed.
+ * If otherRange is larger than currentRange, the extra objects
+ * are added.
+ */
+ public void replaceObjectsInRange (NSRange currentRange,
+ List otherArray, NSRange otherRange)
+ {
+ if ( ( currentRange == null ) || ( otherArray == null ) ||
+ ( otherRange == null ) ) return;
+
+ // transform otherRange if out of bounds for array
+ if ( otherRange.maxRange() > otherArray.size() )
+ {
+ // TODO: Test this logic.
+ int loc = Math.min( otherRange.location(), otherArray.size() - 1 );
+ otherRange = new NSRange( loc, otherArray.size() - loc );
+ }
+
+ Object o;
+ List subList = subList(
+ currentRange.location(), currentRange.maxRange() );
+ int otherIndex = otherRange.location();
+ // TODO: Test this logic.
+ for ( int i = 0; i < subList.size(); i++ )
+ {
+ if ( otherIndex < otherRange.maxRange() )
+ { // set object
+ subList.set( i, otherArray.get( otherIndex ) );
+ }
+ else
+ { // remove extra elements from currentRange
+ subList.remove( i );
+ i--;
+ }
+ otherIndex++;
+ }
+ // TODO: Test this logic.
+ for ( int i = otherIndex; i < otherRange.maxRange(); i++ )
+ {
+ add( otherArray.get( i ) );
+ }
+ }
+
+ /**
+ * Clears the current array and then populates it with the
+ * contents of the specified collection.
+ */
+ public void setArray (Collection aCollection)
+ {
+ clear();
+ addAll( aCollection );
+ }
+
+ /**
+ * Removes all objects equivalent to the specified object.
+ */
+ public void removeObject (Object anObject)
+ {
+ remove( anObject );
+ }
+
+ /**
+ * Removes all occurences of the specified object,
+ * comparing by reference.
+ */
+ public void removeIdenticalObject (Object anObject)
+ {
+ EOObserverCenter.removeObserver( observer, anObject );
+ super.removeIdenticalObject( anObject );
+ }
+
+ /**
+ * Inserts the specified object into this array at the
+ * specified index.
+ */
+ public void insertObjectAtIndex (Object anObject, int anIndex)
+ {
+ add( anIndex, anObject );
+ }
+
+ /**
+ * Replaces the object at the specified index with the
+ * specified object.
+ */
+ public void replaceObjectAtIndex (int anIndex, Object anObject)
+ {
+ set( anIndex, anObject );
+ }
+
+ /**
+ * Adds the specified object to the end of this array.
+ */
+ public void addObject (Object anObject)
+ {
+ add( anObject );
+ }
+
+ // interface List: mutators
+
+ public void add(int index, Object element)
+ {
+ EOObserverCenter.addObserver( observer, element );
+ super.add( index, element );
+ }
+
+ public boolean add(Object o)
+ {
+ EOObserverCenter.addObserver( observer, o );
+ return super.add(o);
+ }
+
+ public boolean addAll(Collection coll)
+ {
+ Iterator it = coll.iterator();
+ while ( it.hasNext() )
+ {
+ EOObserverCenter.addObserver( observer, it.next() );
+ }
+ return super.addAll(coll);
+ }
+
+ public boolean addAll(int index, Collection c)
+ {
+ Iterator it = c.iterator();
+ while ( it.hasNext() )
+ {
+ EOObserverCenter.addObserver( observer, it.next() );
+ }
+ return super.addAll( index, c );
+ }
+
+ public void clear()
+ {
+ Iterator it = iterator();
+ while ( it.hasNext() )
+ {
+ EOObserverCenter.removeObserver( observer, it.next() );
+ }
+ super.clear();
+ }
+
+ public Object remove(int index)
+ {
+ EOObserverCenter.removeObserver( observer, get(index) );
+ return super.remove( index );
+ }
+
+ public boolean remove(Object o)
+ {
+ EOObserverCenter.removeObserver( observer, o );
+ return super.remove(o);
+ }
+
+ public boolean removeAll(Collection coll)
+ {
+ Iterator it = coll.iterator();
+ while ( it.hasNext() )
+ {
+ EOObserverCenter.removeObserver( observer, it.next() );
+ }
+ return super.removeAll(coll);
+ }
+
+ public boolean retainAll(Collection coll)
+ {
+ throw new UnsupportedOperationException();
+ }
+
+ public Object set(int index, Object element)
+ {
+ EOObserverCenter.removeObserver( observer, get(index) );
+ EOObserverCenter.addObserver( observer, element );
+ return super.set( index, element );
+ }
+}
+
+/*
+ * $Log$
+ * Revision 1.2 2006/02/16 16:47:14 cgruber
+ * Move some classes in to "internal" packages and re-work imports, etc.
+ *
+ * Also use UnsupportedOperationExceptions where appropriate, instead of WotonomyExceptions.
+ *
+ * Revision 1.1 2006/02/16 13:19:57 cgruber
+ * Check in all sources in eclipse-friendly maven-enabled packages.
+ *
+ * Revision 1.1 2002/10/24 21:15:35 mpowers
+ * New implementations of NSArray and subclasses.
+ *
+ * Revision 1.1 2001/02/20 16:38:55 mpowers
+ * MasterDetailAssociations now observe their controlled display group's
+ * objects for changes to that the parent object will be marked as updated.
+ * Before, only inserts and deletes to an object's items are registered.
+ * Also, moved ObservableArray to package access.
+ *
+ * Revision 1.1 2001/01/24 14:37:24 mpowers
+ * Contributing a delegate useful for debugging.
+ *
+ *
+ */
+
diff --git a/projects/net.wotonomy.persistence/src/main/java/net/wotonomy/control/OrderedDataSource.java b/projects/net.wotonomy.persistence/src/main/java/net/wotonomy/control/OrderedDataSource.java
new file mode 100644
index 0000000..8b88615
--- /dev/null
+++ b/projects/net.wotonomy.persistence/src/main/java/net/wotonomy/control/OrderedDataSource.java
@@ -0,0 +1,57 @@
+/*
+Wotonomy: OpenStep design patterns for pure Java applications.
+Copyright (C) 2001 Intersect Software Corporation
+
+This library is free software; you can redistribute it and/or
+modify it under the terms of the GNU Lesser General Public
+License as published by the Free Software Foundation; either
+version 2.1 of the License, or (at your option) any later version.
+
+This library is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+Lesser General Public License for more details.
+
+You should have received a copy of the GNU Lesser General Public
+License along with this library; if not, see http://www.gnu.org
+*/
+
+package net.wotonomy.control;
+
+/**
+* A simple extension of EODataSource that
+* allows for indexed insertion. The wotonomy
+* implementation of EODisplayGroup supports
+* this and will use it if possible. This is
+* useful for classes like the PropertyDataSource,
+* where the ordering of items in an indexed
+* property may be important.
+*
+* @author michael@mpowers.net
+* @author $Author: cgruber $
+* @version $Revision: 893 $
+*/
+public abstract class OrderedDataSource extends EODataSource
+{
+ /**
+ * Inserts the specified object into this data source,
+ * at the specified index.
+ */
+ public abstract void insertObjectAtIndex (
+ Object anObject, int anIndex );
+}
+
+/*
+ * $Log$
+ * Revision 1.1 2006/02/16 13:19:57 cgruber
+ * Check in all sources in eclipse-friendly maven-enabled packages.
+ *
+ * Revision 1.2 2003/08/06 23:07:52 chochos
+ * general code cleanup (mostly, removing unused imports)
+ *
+ * Revision 1.1 2001/01/24 14:10:53 mpowers
+ * Contributing OrderedDataSource, and PropertyDataSource extends it.
+ *
+ *
+ */
+
diff --git a/projects/net.wotonomy.persistence/src/main/java/net/wotonomy/control/PropertyDataSource.java b/projects/net.wotonomy.persistence/src/main/java/net/wotonomy/control/PropertyDataSource.java
new file mode 100644
index 0000000..76f7219
--- /dev/null
+++ b/projects/net.wotonomy.persistence/src/main/java/net/wotonomy/control/PropertyDataSource.java
@@ -0,0 +1,549 @@
+/*
+Wotonomy: OpenStep design patterns for pure Java applications.
+Copyright (C) 2000 Intersect Software Corporation
+
+This library is free software; you can redistribute it and/or
+modify it under the terms of the GNU Lesser General Public
+License as published by the Free Software Foundation; either
+version 2.1 of the License, or (at your option) any later version.
+
+This library is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+Lesser General Public License for more details.
+
+You should have received a copy of the GNU Lesser General Public
+License along with this library; if not, see http://www.gnu.org
+*/
+
+package net.wotonomy.control;
+
+import java.lang.reflect.Array;
+import java.lang.reflect.Method;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.TreeSet;
+
+import net.wotonomy.foundation.NSArray;
+import net.wotonomy.foundation.NSMutableArray;
+import net.wotonomy.foundation.internal.Introspector;
+import net.wotonomy.foundation.internal.WotonomyException;
+
+/**
+* A data source that reads and writes to an indexed
+* property of a java object. This class is used by
+* MasterDetailAssociation to retreive objects from
+* the master display group.
+*
+* @author michael@mpowers.net
+* @author $Author: cgruber $
+* @version $Revision: 894 $
+*/
+public class PropertyDataSource extends OrderedDataSource
+{
+ protected Object source;
+ protected String key;
+ protected Class lastKnownType; // for best-guessing
+ protected EOClassDescription classDesc;
+ protected EOEditingContext context;
+
+ /**
+ * Creates a new PropertyDataSource with no editing context
+ * and will try to guess the appropriate class description
+ * when trying to create objects.
+ */
+ public PropertyDataSource()
+ {
+ this( null, (EOClassDescription) null );
+ }
+
+ /**
+ * Creates a new PropertyDataSource that uses the specified
+ * editing context, but will try to guess the appropriate
+ * class description when trying to create objects.
+ */
+ public PropertyDataSource( EOEditingContext aContext )
+ {
+ this( aContext, (EOClassDescription) null );
+ }
+
+ /**
+ * Creates a new PropertyDataSource that uses the specified
+ * editing context and vends objects of the specified class.
+ */
+ public PropertyDataSource(
+ EOEditingContext aContext, Class aClass )
+ {
+ this( aContext, EOClassDescription.classDescriptionForClass( aClass ) );
+ }
+
+ /**
+ * Creates a new PropertyDataSource that uses the specified
+ * editing context and vends objects of the specified
+ * class description.
+ */
+ public PropertyDataSource(
+ EOEditingContext aContext, EOClassDescription aClassDesc )
+ {
+ source = null;
+ key = null;
+ lastKnownType = null;
+ classDesc = aClassDesc;
+ context = aContext;
+ }
+
+ /**
+ * Provides the master object for detail display groups.
+ */
+ public Object source()
+ {
+ return source;
+ }
+
+ /**
+ * Allows a detail display group to set the master object.
+ */
+ public void setSource( Object anObject )
+ {
+ source = anObject;
+ }
+
+ /**
+ * Provides the detail key for detail display groups.
+ */
+ public String key()
+ {
+ return key;
+ }
+
+ /**
+ * Allows a detail display group to set the detail key.
+ */
+ public void setKey( String aKey )
+ {
+ key = aKey;
+ }
+
+ /**
+ * Inserts the specified object into this data source.
+ * Calls insertObjectAtIndex and appends to the end
+ * of the list.
+ */
+ public void insertObject ( Object anObject )
+ {
+ insertObjectAtIndex( anObject, -1 ); // trick to force to end
+ }
+
+ /**
+ * Inserts the specified object into this data source,
+ * at the specified index.
+ */
+ public void insertObjectAtIndex (
+ Object anObject, int anIndex )
+ {
+ if ( source == null ) return;
+ List list = readAsList();
+ if ( anIndex == -1 ) anIndex = list.size(); // force to end
+ if ( anIndex > list.size() ) anIndex = list.size(); // force to end
+ list.add( anIndex, anObject );
+ writeAsList( list );
+ }
+
+ /**
+ * Deletes the specified object from this data source.
+ */
+ public void deleteObject ( Object anObject )
+ {
+ if ( source == null ) return;
+ List list = readAsList();
+ list.remove( anObject );
+ writeAsList( list );
+ }
+
+ public EOEditingContext editingContext ()
+ {
+ return context;
+ }
+
+ /**
+ * Returns a List containing the objects in this
+ * data source.
+ */
+ public NSArray fetchObjects ()
+ {
+ if ( source == null ) return NSArray.EmptyArray;
+ return readAsList();
+ }
+
+ /**
+ * Returns a new instance of this class.
+ */
+ public EODataSource
+ dataSourceQualifiedByKey ( String aKey )
+ {
+ // determine the target class desc if possible
+ EOClassDescription keyClassDesc = null;
+ if ( classDesc != null )
+ {
+ keyClassDesc = classDesc.classDescriptionForDestinationKey( aKey );
+ }
+ return new PropertyDataSource( editingContext(), keyClassDesc );
+ }
+
+ /**
+ * 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 )
+ {
+ source = anObject;
+ key = aKey;
+ }
+
+ /**
+ * Returns the class description passed to the
+ * constructor, if any. If no class description and
+ * if the bound property is an indexed property,
+ * the type of the array is returned, otherwise
+ * this method returns null. This method is called
+ * by createObject().
+ */
+ public EOClassDescription
+ classDescriptionForObjects ()
+ {
+ // just return the class description if we have one
+ if ( classDesc != null ) return classDesc;
+
+ // otherwise, try to do some guesswork
+ EOClassDescription result = null;
+
+ // lastKnownType is not updated here
+ Class type = lastKnownType;
+
+ // if no last known type
+ if ( type == null )
+ {
+ // if source and key were specified
+ if ( ( source != null ) && ( key != null ) )
+ {
+ // try to get an array type
+ Method m = Introspector.getPropertyReadMethod(
+ source.getClass(), key, new Class[0] );
+ if ( m != null )
+ {
+ Class returnType = m.getReturnType();
+ if ( returnType.isArray() )
+ {
+ type = returnType.getComponentType();
+ }
+ }
+ else
+ {
+ throw new WotonomyException( "Key does not exist for object: " + key + " : " + source );
+ }
+ }
+
+ // does not update lastKnownType because
+ // we prefer to get that info from a fetch.
+ }
+
+ // if type has been determined
+ if ( type != null )
+ {
+ result =
+ EOClassDescription.classDescriptionForClass( type );
+ }
+
+ return result;
+ }
+
+ /**
+ * Calls getValue() and returns the result as a List.
+ * Sets lastKnownType to the retrieved type.
+ */
+ protected NSMutableArray readAsList()
+ {
+ Object value = getValue();
+ if ( value == null )
+ {
+ return new NSMutableArray();
+ }
+
+ Object o;
+ NSMutableArray result = new NSMutableArray();
+ boolean hasReadType = false;
+ lastKnownType = null;
+
+ // if instance of array, convert to list
+ if ( value.getClass().isArray() )
+ {
+ int count = Array.getLength( value );
+ for ( int i = 0; i < count; i++ )
+ {
+ o = Array.get( value, i );
+ if ( o != null )
+ {
+ // we've already found a type
+ if ( hasReadType )
+ {
+ // check that this matches the last known type
+ if ( o.getClass() != lastKnownType )
+ {
+ // not all of the same type: set to null
+ lastKnownType = null;
+ }
+ }
+ else // this is the first type we've found
+ {
+ // remember it
+ hasReadType = true;
+ lastKnownType = o.getClass();
+ }
+ }
+ result.add( o );
+ }
+ }
+ else
+ if ( value instanceof Collection )
+ {
+ // convert to list so we handle sets, etc.
+ Iterator i = ((Collection)value).iterator();
+ while ( i.hasNext() )
+ {
+ o = i.next();
+ if ( o != null )
+ {
+ // we've already found a type
+ if ( hasReadType )
+ {
+ // check that this matches the last known type
+ if ( o.getClass() != lastKnownType )
+ {
+ // not all of the same type: set to null
+ lastKnownType = null;
+ }
+ }
+ else // this is the first type we've found
+ {
+ // remember it
+ hasReadType = true;
+ lastKnownType = o.getClass();
+ }
+ }
+ result.add( o );
+ }
+ }
+ else
+ {
+ lastKnownType = null;
+ throw new WotonomyException( "PropertyDataSource: " +
+ "bound property was not an indexed property: " + key );
+ }
+
+ return result;
+ }
+
+ /**
+ * Converts the specified List to lastKnownType
+ * and calls setValue().
+ */
+ protected void writeAsList( List anObjectList )
+ {
+ if ( source == null )
+ {
+ throw new WotonomyException( "PropertyDataSource: " +
+ "no source object: " + key );
+ }
+
+ Class c = source.getClass();
+ Method m = Introspector.getPropertyReadMethod( c, key, new Class[0] );
+ if ( m == null )
+ {
+ throw new WotonomyException( "Could not read property for object: "
+ + key + " : " + source + " (" + c + ")" );
+ }
+
+ Class returnType = m.getReturnType();
+
+ int count = anObjectList.size();
+ Object result = null;
+
+ if ( returnType.isArray() )
+ {
+ Class type = returnType.getComponentType();
+ result = Array.newInstance( type, count );
+ for ( int i = 0; i < count; i++ )
+ {
+ Array.set( result, i, anObjectList.get( i ) );
+ }
+ }
+ else
+ {
+ Collection collection = null;
+
+ if ( ! returnType.isInterface() )
+ {
+ try
+ {
+ collection = (Collection) returnType.newInstance();
+ }
+ catch ( Exception exc )
+ {
+ // no default constructor, leave null
+ }
+ }
+
+ // try to find an acceptable collections type
+ if ( collection == null )
+ {
+ if ( returnType.isAssignableFrom( NSMutableArray.class ) )
+ {
+ collection = new NSMutableArray();
+ }
+ else
+ if ( returnType.isAssignableFrom( LinkedList.class ) )
+ {
+ collection = new LinkedList();
+ }
+ else
+ if ( returnType.isAssignableFrom( ArrayList.class ) )
+ {
+ collection = new ArrayList();
+ }
+ else
+ if ( returnType.isAssignableFrom( HashSet.class ) )
+ {
+ collection = new HashSet();
+ }
+ else
+ if ( returnType.isAssignableFrom( TreeSet.class ) )
+ {
+ collection = new TreeSet();
+ }
+ }
+
+ if ( collection == null )
+ {
+ throw new WotonomyException( "Could not create a collection of type: " + returnType );
+ }
+
+ collection.addAll( anObjectList );
+ result = collection;
+ }
+
+ setValue( result );
+ }
+
+ /**
+ * Returns the value of the indexed property
+ * specified by qualifyWithRelationshipKey.
+ */
+ protected Object getValue()
+ {
+ if ( source instanceof EOKeyValueCoding )
+ {
+ return ((EOKeyValueCoding)source).valueForKey( key );
+ }
+ return EOKeyValueCodingSupport.valueForKey( source, key );
+ }
+
+ /**
+ * Sets the value of the indexed property
+ * specified by qualifyWithRelationshipKey.
+ * The argument is assumed to be of appropriate
+ * type for the property. EOObserverCenter is
+ * notified that the object will change.
+ */
+ protected void setValue( Object aValue )
+ {
+ EOClassDescription sourceDesc =
+ EOClassDescription.classDescriptionForClass( source.getClass() );
+
+
+ // if we're not editing a relationship (?)
+// if ( ! sourceDesc.toManyRelationshipKeys().containsObject( key ) )
+ {
+ // mark the parent as changed
+ EOObserverCenter.notifyObserversObjectWillChange( source );
+ }
+
+
+ if ( source instanceof EOKeyValueCoding )
+ {
+ ((EOKeyValueCoding)source).takeValueForKey( aValue, key );
+ }
+ else
+ {
+ EOKeyValueCodingSupport.takeValueForKey( source, aValue, key );
+ }
+ }
+
+}
+
+/*
+ * $Log$
+ * Revision 1.2 2006/02/16 16:47:14 cgruber
+ * Move some classes in to "internal" packages and re-work imports, etc.
+ *
+ * Also use UnsupportedOperationExceptions where appropriate, instead of WotonomyExceptions.
+ *
+ * Revision 1.1 2006/02/16 13:19:57 cgruber
+ * Check in all sources in eclipse-friendly maven-enabled packages.
+ *
+ * Revision 1.14 2003/01/18 23:30:42 mpowers
+ * WODisplayGroup now compiles.
+ *
+ * Revision 1.13 2002/10/24 21:15:36 mpowers
+ * New implementations of NSArray and subclasses.
+ *
+ * Revision 1.12 2002/10/24 18:18:12 mpowers
+ * NSArray's are now considered read-only, so we can return our internal
+ * representation to reduce unnecessary object allocation.
+ *
+ * Revision 1.11 2002/04/15 21:55:33 mpowers
+ * Catching a condition where the get may not return the value passed to set.
+ *
+ * Revision 1.10 2002/03/08 23:20:37 mpowers
+ * insertObject now calls insertObjectAtIndex.
+ *
+ * Revision 1.9 2001/06/05 19:10:41 mpowers
+ * Better handling of null properties.
+ *
+ * Revision 1.8 2001/05/21 14:03:35 mpowers
+ * Added a convenience constructor for java classes.
+ *
+ * Revision 1.7 2001/04/30 13:15:24 mpowers
+ * Child contexts re-initializing objects invalidated in parent now
+ * propery transpose relationships.
+ *
+ * Revision 1.6 2001/04/29 02:29:31 mpowers
+ * Debugging relationship faulting.
+ *
+ * Revision 1.5 2001/04/28 22:17:51 mpowers
+ * Revised PropertyDataSource to be EOClassDescription-aware.
+ *
+ * Revision 1.4 2001/04/27 23:37:20 mpowers
+ * Now using EOClassDescription in the EODataSource class, as we should.
+ *
+ * Revision 1.3 2001/03/29 03:29:49 mpowers
+ * Now using KeyValueCoding and Support instead of Introspector.
+ *
+ * Revision 1.2 2001/01/24 14:10:53 mpowers
+ * Contributing OrderedDataSource, and PropertyDataSource extends it.
+ *
+ * Revision 1.1.1.1 2000/12/21 15:46:50 mpowers
+ * Contributing wotonomy.
+ *
+ * Revision 1.3 2000/12/20 16:25:35 michael
+ * Added log to all files.
+ *
+ *
+ */
+
diff --git a/projects/net.wotonomy.persistence/src/main/java/net/wotonomy/control/internal/Surrogate.java b/projects/net.wotonomy.persistence/src/main/java/net/wotonomy/control/internal/Surrogate.java
new file mode 100644
index 0000000..e12fda0
--- /dev/null
+++ b/projects/net.wotonomy.persistence/src/main/java/net/wotonomy/control/internal/Surrogate.java
@@ -0,0 +1,259 @@
+/*
+Wotonomy: OpenStep design patterns for pure Java applications.
+Copyright (C) 2000 Intersect Software Corporation
+
+This library is free software; you can redistribute it and/or
+modify it under the terms of the GNU Lesser General Public
+License as published by the Free Software Foundation; either
+version 2.1 of the License, or (at your option) any later version.
+
+This library is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+Lesser General Public License for more details.
+
+You should have received a copy of the GNU Lesser General Public
+License along with this library; if not, see http://www.gnu.org
+*/
+
+package net.wotonomy.control.internal;
+
+import net.wotonomy.control.EOObserverCenter;
+import net.wotonomy.foundation.NSMutableDictionary;
+import net.wotonomy.foundation.internal.Introspector;
+
+/**
+* A Surrogate is a special object that can be used in a display
+* group when you wish to emulate other objects or modify their
+* behaviors. Because it is a Map, it makes use of Introspector's
+* ability to treat keys in a map as if they were properties to
+* implement the following features.
+* <ul>
+* <li>By default, Surrogate works like a Map, and reading and
+* writing properties to a Surrogate gets and puts values in the
+* Map.</li>
+* <li>If one or more delegate objects are specified, property keys
+* that do not exist in the map are read from and written to the
+* delegate object.</li>
+* <li>If a default value is specified, that value will be returned
+* for all property reads that do not exist in the map or in the
+* delegate object. (Subsequent writes to those properties will
+* create a key in the map and then subsequent reads will read not
+* read the default object.)</li>
+* <li>Subclasses can override the get(Object) method to further
+* customize the behavior of a Surrogate.
+* </ul>
+*
+* @author michael@mpowers.net
+* @date $Date: 2006-02-18 17:46:44 -0500 (Sat, 18 Feb 2006) $
+* @revision $Revision: 900 $
+*/
+public class Surrogate extends NSMutableDictionary
+{
+ protected Object[] delegates;
+ protected Object defaultValue;
+
+ /**
+ * Default constructor with no delegate object and no default value.
+ */
+ public Surrogate()
+ {
+ delegates = null;
+ defaultValue = null;
+ }
+
+ /**
+ * Constructor specifying a delegate object.
+ */
+ public Surrogate( Object[] aDelegateArray )
+ {
+ setDelegates( aDelegateArray );
+ }
+
+ /**
+ * Constructor specifying a default value.
+ */
+ public Surrogate( Object aDefault )
+ {
+ setDefaultValue( aDefault );
+ }
+
+ /**
+ * Constructor specifying a delegate object and a default value.
+ */
+ public Surrogate( Object[] aDelegateArray, Object aDefault )
+ {
+ setDelegates( aDelegateArray );
+ setDefaultValue( aDefault );
+ }
+
+ /**
+ * Returns the first delegate object, or null if no delegates exist.
+ */
+ public Object getDelegate()
+ {
+ if ( delegates == null ) return null;
+ if ( delegates.length == 0 ) return null;
+ return delegates[0];
+ }
+
+ /**
+ * Sets the delegate object list to contain only the
+ * specified object.
+ */
+ public void setDelegate( Object aDelegate )
+ {
+ setDelegates( new Object[] { aDelegate } );
+ }
+
+ /**
+ * Returns the list of delegates in the order in which
+ * they are consulted.
+ */
+ public Object[] getDelegates()
+ {
+ if ( delegates == null ) delegates = new Object[0];
+ return delegates;
+ }
+
+ /**
+ * Sets the list of delegates in the order in which they
+ * will be consulted.
+ */
+ public void setDelegates( Object[] aDelegateArray )
+ {
+ delegates = aDelegateArray;
+ }
+
+ /**
+ * Returns the current default value, or null if no default exists.
+ */
+ public Object getDefaultValue()
+ {
+ return defaultValue;
+ }
+
+ /**
+ * Sets the default value.
+ */
+ public void setDefaultValue( Object aDefault )
+ {
+ defaultValue = aDefault;
+ }
+
+ /**
+ * Called by get to retrieve a value from the internal map.
+ * This implementation simply calls super.get().
+ */
+ public Object directGet( Object aKey )
+ {
+ return super.get( aKey );
+ }
+
+ /**
+ * Called by put to retrieve a value from the internal map.
+ * This implementation simply calls super.put().
+ */
+ public Object directPut( Object aKey, Object aValue )
+ {
+ return super.put( aKey, aValue );
+ }
+
+ /**
+ * Overridden to consult each delegate before
+ * checking the internal list of keys. No matching
+ * key is found, returns the default object, or
+ * null if no default object exists.
+ */
+ public Object get( Object aKey )
+ {
+ // check all delegates in order
+ int i, j;
+ Object[] list = getDelegates();
+ String[] properties;
+ for ( i = 0; i < list.length; i++ )
+ {
+ // for each delegate
+ properties =
+ Introspector.getReadPropertiesForObject( list[i] );
+ for ( j = 0; j < properties.length; j++ )
+ {
+ // if delegate has property
+ if ( properties[j].equals( aKey ) )
+ {
+ // use this delegate
+ return Introspector.get( list[i], aKey.toString() );
+ }
+ }
+ }
+
+ // return from internal map
+ Object result = directGet( aKey );
+ if ( result == null )
+ {
+ // if not in map, return default object
+ result = getDefaultValue();
+ }
+ return result;
+ }
+
+ /**
+ * Overridden to attempt to write each delegate, writing to
+ * only the first successful delegate, before storing the
+ * value in the internal map.
+ */
+ public Object put( Object aKey, Object aValue )
+ {
+ // check all delegates in order
+ int i, j;
+ Object[] list = getDelegates();
+ String[] properties;
+ for ( i = 0; i < list.length; i++ )
+ {
+ // for each delegate
+ properties =
+ Introspector.getWritePropertiesForObject( list[i] );
+ for ( j = 0; j < properties.length; j++ )
+ {
+ // if delegate has property
+ if ( properties[j].equals( aKey ) )
+ {
+ // use this delegate
+ EOObserverCenter.notifyObserversObjectWillChange( list[i] );
+ return Introspector.set( list[i], aKey.toString(), aValue );
+ }
+ }
+ }
+
+ // set on internal map
+ EOObserverCenter.notifyObserversObjectWillChange( this );
+ return directPut( aKey, aValue );
+ }
+
+ /**
+ * Overridden to compare by reference.
+ */
+ public boolean equals( Object anObject )
+ {
+ return ( this == anObject );
+ }
+
+}
+
+/*
+ * $Log$
+ * Revision 1.1 2006/02/18 22:46:44 cgruber
+ * Add Surrogate map from .util into control's internal package, and fix imports.
+ *
+ * Revision 1.1 2006/02/16 13:22:22 cgruber
+ * Check in all sources in eclipse-friendly maven-enabled packages.
+ *
+ * Revision 1.1.1.1 2000/12/21 15:52:21 mpowers
+ * Contributing wotonomy.
+ *
+ * Revision 1.2 2000/12/20 16:25:48 michael
+ * Added log to all files.
+ *
+ *
+ */
+
diff --git a/projects/net.wotonomy.persistence/src/main/java/net/wotonomy/control/package.html b/projects/net.wotonomy.persistence/src/main/java/net/wotonomy/control/package.html
new file mode 100644
index 0000000..c32d3c7
--- /dev/null
+++ b/projects/net.wotonomy.persistence/src/main/java/net/wotonomy/control/package.html
@@ -0,0 +1,6 @@
+<body>
+<p>
+Support classes needed by the ui and web packages.
+This package roughly corresponds to the eocontrol package.
+</p>
+</body>
diff --git a/projects/net.wotonomy.persistence/src/test/java/net/wotonomy/access/EOEntityTest.java b/projects/net.wotonomy.persistence/src/test/java/net/wotonomy/access/EOEntityTest.java
new file mode 100644
index 0000000..84f0721
--- /dev/null
+++ b/projects/net.wotonomy.persistence/src/test/java/net/wotonomy/access/EOEntityTest.java
@@ -0,0 +1,14 @@
+package net.wotonomy.access;
+
+import junit.framework.TestCase;
+
+public class EOEntityTest extends TestCase {
+
+ /*
+ * Test method for 'net.wotonomy.access.EOEntity.addAttribute(EOAttribute)'
+ */
+ public void testAddAttribute() {
+
+ }
+
+}