Hi, This is still a work in progress, pending inclusion of the JDONullIdentityFIeldException. But the ObjectIdentity class is mostly done. Craig |
Index: test/java/javax/jdo/identity/StringIdentityTest.java =================================================================== --- test/java/javax/jdo/identity/StringIdentityTest.java (revision 209512) +++ test/java/javax/jdo/identity/StringIdentityTest.java (working copy) @@ -70,4 +70,10 @@ assertFalse ("Not equal StringIdentity instances compare equal.", sc1.equals(sc3)); assertFalse ("Not equal StringIdentity instances compare equal.", sc3.equals(sc1)); } + + public void testGetKeyAsObject() { + StringIdentity c1 = new StringIdentity(Object.class, "1"); + assertEquals("keyAsObject doesn't match.", c1.getKeyAsObject(), "1"); + } + } Index: test/java/javax/jdo/identity/ObjectIdentityTest.java =================================================================== --- test/java/javax/jdo/identity/ObjectIdentityTest.java (revision 0) +++ test/java/javax/jdo/identity/ObjectIdentityTest.java (revision 0) @@ -0,0 +1,384 @@ +/* + * Copyright 2005 The Apache Software Foundation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * ObjectIdentityTest.java + * + */ + +package javax.jdo.identity; + +import java.lang.reflect.Constructor; +import java.lang.reflect.InvocationTargetException; + +import java.io.Serializable; + +import java.math.BigDecimal; +import java.util.Currency; +import java.util.Date; +import java.util.Locale; + +import javax.jdo.JDOUserException; + +import javax.jdo.util.BatchTestRunner; + +/** + * + */ +public class ObjectIdentityTest extends SingleFieldIdentityTest { + + /** Creates a new instance of ObjectIdentityTest */ + public ObjectIdentityTest() { + } + + /** + * @param args the command line arguments + */ + public static void main(String[] args) { + BatchTestRunner.run(ObjectIdentityTest.class); + } + + public void testConstructor() { + ObjectIdentity c1 = new ObjectIdentity(Object.class, new IdClass(1)); + ObjectIdentity c2 = new ObjectIdentity(Object.class, new IdClass(1)); + ObjectIdentity c3 = new ObjectIdentity(Object.class, new IdClass(2)); + assertEquals("Equal ObjectIdentity instances compare not equal.", c1, c2); + assertFalse ("Not equal ObjectIdentity instances compare equal", c1.equals(c3)); + } + + public void testIntegerConstructor() { + ObjectIdentity c1 = new ObjectIdentity(Object.class, new Integer(1)); + ObjectIdentity c2 = new ObjectIdentity(Object.class, new Integer(1)); + ObjectIdentity c3 = new ObjectIdentity(Object.class, new Integer(2)); + assertEquals("Equal ObjectIdentity instances compare not equal.", c1, c2); + assertFalse ("Not equal ObjectIdentity instances compare equal", c1.equals(c3)); + } + + public void testLongConstructor() { + ObjectIdentity c1 = new ObjectIdentity(Object.class, new Long(1)); + ObjectIdentity c2 = new ObjectIdentity(Object.class, new Long(1)); + ObjectIdentity c3 = new ObjectIdentity(Object.class, new Long(2)); + assertEquals("Equal ObjectIdentity instances compare not equal.", c1, c2); + assertFalse ("Not equal ObjectIdentity instances compare equal", c1.equals(c3)); + } + + public void testDateConstructor() { + ObjectIdentity c1 = new ObjectIdentity(Object.class, new Date(1)); + ObjectIdentity c2 = new ObjectIdentity(Object.class, new Date(1)); + ObjectIdentity c3 = new ObjectIdentity(Object.class, new Date(2)); + assertEquals("Equal ObjectIdentity instances compare not equal.", c1, c2); + assertFalse ("Not equal ObjectIdentity instances compare equal", c1.equals(c3)); + } + + public void testLocaleConstructor() { + ObjectIdentity c1 = new ObjectIdentity(Object.class, Locale.US); + ObjectIdentity c2 = new ObjectIdentity(Object.class, Locale.US); + ObjectIdentity c3 = new ObjectIdentity(Object.class, Locale.GERMANY); + assertEquals("Equal ObjectIdentity instances compare not equal.", c1, c2); + assertFalse ("Not equal ObjectIdentity instances compare equal", c1.equals(c3)); + } + + public void testCurrencyConstructor() { + ObjectIdentity c1 = new ObjectIdentity(Object.class, + Currency.getInstance(Locale.US)); + ObjectIdentity c2 = new ObjectIdentity(Object.class, + Currency.getInstance(Locale.US)); + ObjectIdentity c3 = new ObjectIdentity(Object.class, + Currency.getInstance(Locale.GERMANY)); + assertEquals("Equal ObjectIdentity instances compare not equal.", c1, c2); + assertFalse ("Not equal ObjectIdentity instances compare equal", c1.equals(c3)); + } + + public void testStringConstructor() { + ObjectIdentity c1 = new ObjectIdentity(Object.class, + "javax.jdo.identity.ObjectIdentityTest$IdClass:1"); + ObjectIdentity c2 = new ObjectIdentity(Object.class, + "javax.jdo.identity.ObjectIdentityTest$IdClass:1"); + ObjectIdentity c3 = new ObjectIdentity(Object.class, + "javax.jdo.identity.ObjectIdentityTest$IdClass:2"); + assertEquals("Equal ObjectIdentity instances compare not equal.", c1, c2); + assertFalse ("Not equal ObjectIdentity instances compare equal", c1.equals(c3)); + } + + public void testToStringConstructor() { + ObjectIdentity c1 = new ObjectIdentity(Object.class, new IdClass(1)); + ObjectIdentity c2 = new ObjectIdentity(Object.class, c1.toString()); + assertEquals ("Equal ObjectIdentity instances compare not equal.", c1, c2); + } + + public void testBadStringConstructorNullClass() { + try { + ObjectIdentity c1 = new ObjectIdentity(null, "1"); + } catch (NullPointerException ex) { + return; + } + fail ("Failed to catch expected exception."); + } + + public void testBadStringConstructorNullParam() { + try { + ObjectIdentity c1 = new ObjectIdentity(Object.class, null); + } catch (NullPointerException ex) { + return; + } + fail ("Failed to catch expected exception."); + } + + public void testBadStringConstructorTooShort() { + try { + ObjectIdentity c1 = new ObjectIdentity(Object.class, "xx"); + } catch (JDOUserException ex) { + return; + } + fail ("Failed to catch expected exception."); + } + + public void testBadStringConstructorNoDelimiter() { + try { + ObjectIdentity c1 = new ObjectIdentity(Object.class, "xxxxxxxxx"); + } catch (JDOUserException ex) { + return; + } + fail ("Failed to catch expected exception."); + } + + public void testBadStringConstructorBadClassName() { + try { + ObjectIdentity c1 = new ObjectIdentity(Object.class, "xx:yy"); + } catch (JDOUserException ex) { + validateNestedException(ex, ClassNotFoundException.class); + return; + } + fail ("Failed to catch expected ClassNotFoundException."); + } + + public void testBadStringConstructorNoStringConstructor() { + try { + ObjectIdentity c1 = new ObjectIdentity(Object.class, + "javax.jdo.identity.ObjectIdentityTest$BadIdClassNoStringConstructor:yy"); + } catch (JDOUserException ex) { + validateNestedException(ex, NoSuchMethodException.class); + return; + } + fail ("Failed to catch expected NoSuchMethodException."); + } + + public void testBadStringConstructorNoPublicStringConstructor() { + try { + ObjectIdentity c1 = new ObjectIdentity(Object.class, + "javax.jdo.identity.ObjectIdentityTest$BadIdClassNoPublicStringConstructor:yy"); + } catch (JDOUserException ex) { + validateNestedException(ex, NoSuchMethodException.class); + return; + } + fail ("Failed to catch expected NoSuchMethodException."); + } + + public void testBadStringConstructorIllegalArgument() { + try { + ObjectIdentity c1 = new ObjectIdentity(Object.class, + "javax.jdo.identity.ObjectIdentityTest$IdClass:yy"); + } catch (JDOUserException ex) { + validateNestedException(ex, InvocationTargetException.class); + return; + } + fail ("Failed to catch expected InvocationTargetException."); + } + + public void testStringDateConstructor() { + Object c1; + try { + c1 = new ObjectIdentity(Object.class, + "java.util.Date:Jan 01, 1970 00:00:00 AM"); + } catch (JDOUserException ex) { + fail ("Unexpected Exception " + ex); + } + } + + public void testBadStringDateConstructor() { + try { + ObjectIdentity c1 = new ObjectIdentity(Object.class, + "java.util.Date:Jop 1, 1970 00:00:00"); + } catch (JDOUserException ex) { + return; + } + fail ("Failed to catch expected Exception."); + } + + public void testStringLocaleConstructor() { + Object c1; + try { + c1 = new ObjectIdentity(Object.class, + "java.util.Locale:en_us"); + } catch (JDOUserException ex) { + fail ("Unexpected Exception " + ex); + } + } + + public void testStringCurrencyConstructor() { + Object c1; + try { + c1 = new ObjectIdentity(Object.class, + "java.util.Currency:USD"); + } catch (JDOUserException ex) { + fail ("Unexpected Exception " + ex); + } + } + + public void testBadStringCurrencyConstructor() { + try { + ObjectIdentity c1 = new ObjectIdentity(Object.class, + "java.util.Currency:NowhereInTheWorld"); + } catch (JDOUserException ex) { + validateNestedException(ex, IllegalArgumentException.class); + return; + } + fail ("Failed to catch expected IllegalArgumentException."); + } + + public void testSerializedIdClass() { + ObjectIdentity c1 = new ObjectIdentity(Object.class, new IdClass(1)); + ObjectIdentity c2 = new ObjectIdentity(Object.class, new IdClass(1)); + ObjectIdentity c3 = new ObjectIdentity(Object.class, new IdClass(2)); + Object[] scis = writeReadSerialized(new Object[] {c1, c2, c3}); + Object sc1 = scis[0]; + Object sc2 = scis[1]; + Object sc3 = scis[2]; + assertEquals ("Equal ObjectIdentity instances compare not equal.", c1, sc1); + assertEquals ("Equal ObjectIdentity instances compare not equal.", c2, sc2); + assertEquals ("Equal ObjectIdentity instances compare not equal.", sc1, c2); + assertEquals ("Equal ObjectIdentity instances compare not equal.", sc2, c1); + assertFalse ("Not equal ObjectIdentity instances compare equal.", c1.equals(sc3)); + assertFalse ("Not equal ObjectIdentity instances compare equal.", sc1.equals(c3)); + assertFalse ("Not equal ObjectIdentity instances compare equal.", sc1.equals(sc3)); + assertFalse ("Not equal ObjectIdentity instances compare equal.", sc3.equals(sc1)); + } + + public void testSerializedBigDecimal() { + ObjectIdentity c1 = new ObjectIdentity(Object.class, new BigDecimal("123456789.012")); + ObjectIdentity c2 = new ObjectIdentity(Object.class, new BigDecimal("123456789.012")); + ObjectIdentity c3 = new ObjectIdentity(Object.class, new BigDecimal("123456789.01")); + Object[] scis = writeReadSerialized(new Object[] {c1, c2, c3}); + Object sc1 = scis[0]; + Object sc2 = scis[1]; + Object sc3 = scis[2]; + assertEquals ("Equal ObjectIdentity instances compare not equal.", c1, sc1); + assertEquals ("Equal ObjectIdentity instances compare not equal.", c2, sc2); + assertEquals ("Equal ObjectIdentity instances compare not equal.", sc1, c2); + assertEquals ("Equal ObjectIdentity instances compare not equal.", sc2, c1); + assertFalse ("Not equal ObjectIdentity instances compare equal.", c1.equals(sc3)); + assertFalse ("Not equal ObjectIdentity instances compare equal.", sc1.equals(c3)); + assertFalse ("Not equal ObjectIdentity instances compare equal.", sc1.equals(sc3)); + assertFalse ("Not equal ObjectIdentity instances compare equal.", sc3.equals(sc1)); + } + + public void testSerializedCurrency() { + ObjectIdentity c1 = new ObjectIdentity(Object.class, Currency.getInstance(Locale.US)); + ObjectIdentity c2 = new ObjectIdentity(Object.class, Currency.getInstance(Locale.US)); + ObjectIdentity c3 = new ObjectIdentity(Object.class, Currency.getInstance(Locale.GERMANY)); + Object[] scis = writeReadSerialized(new Object[] {c1, c2, c3}); + Object sc1 = scis[0]; + Object sc2 = scis[1]; + Object sc3 = scis[2]; + assertEquals ("Equal ObjectIdentity instances compare not equal.", c1, sc1); + assertEquals ("Equal ObjectIdentity instances compare not equal.", c2, sc2); + assertEquals ("Equal ObjectIdentity instances compare not equal.", sc1, c2); + assertEquals ("Equal ObjectIdentity instances compare not equal.", sc2, c1); + assertFalse ("Not equal ObjectIdentity instances compare equal.", c1.equals(sc3)); + assertFalse ("Not equal ObjectIdentity instances compare equal.", sc1.equals(c3)); + assertFalse ("Not equal ObjectIdentity instances compare equal.", sc1.equals(sc3)); + assertFalse ("Not equal ObjectIdentity instances compare equal.", sc3.equals(sc1)); + } + + public void testSerializedDate() { + ObjectIdentity c1 = new ObjectIdentity(Object.class, new Date(1)); + ObjectIdentity c2 = new ObjectIdentity(Object.class, new Date(1)); + ObjectIdentity c3 = new ObjectIdentity(Object.class, new Date(2)); + Object[] scis = writeReadSerialized(new Object[] {c1, c2, c3}); + Object sc1 = scis[0]; + Object sc2 = scis[1]; + Object sc3 = scis[2]; + assertEquals ("Equal ObjectIdentity instances compare not equal.", c1, sc1); + assertEquals ("Equal ObjectIdentity instances compare not equal.", c2, sc2); + assertEquals ("Equal ObjectIdentity instances compare not equal.", sc1, c2); + assertEquals ("Equal ObjectIdentity instances compare not equal.", sc2, c1); + assertFalse ("Not equal ObjectIdentity instances compare equal.", c1.equals(sc3)); + assertFalse ("Not equal ObjectIdentity instances compare equal.", sc1.equals(c3)); + assertFalse ("Not equal ObjectIdentity instances compare equal.", sc1.equals(sc3)); + assertFalse ("Not equal ObjectIdentity instances compare equal.", sc3.equals(sc1)); + } + + public void testSerializedLocale() { + ObjectIdentity c1 = new ObjectIdentity(Object.class, new Locale("EN_US")); + ObjectIdentity c2 = new ObjectIdentity(Object.class, new Locale("EN_US")); + ObjectIdentity c3 = new ObjectIdentity(Object.class, new Locale("EN_GB")); + Object[] scis = writeReadSerialized(new Object[] {c1, c2, c3}); + Object sc1 = scis[0]; + Object sc2 = scis[1]; + Object sc3 = scis[2]; + assertEquals ("Equal ObjectIdentity instances compare not equal.", c1, sc1); + assertEquals ("Equal ObjectIdentity instances compare not equal.", c2, sc2); + assertEquals ("Equal ObjectIdentity instances compare not equal.", sc1, c2); + assertEquals ("Equal ObjectIdentity instances compare not equal.", sc2, c1); + assertFalse ("Not equal ObjectIdentity instances compare equal.", c1.equals(sc3)); + assertFalse ("Not equal ObjectIdentity instances compare equal.", sc1.equals(c3)); + assertFalse ("Not equal ObjectIdentity instances compare equal.", sc1.equals(sc3)); + assertFalse ("Not equal ObjectIdentity instances compare equal.", sc3.equals(sc1)); + } + + public void testGetKeyAsObject() { + ObjectIdentity c1 = new ObjectIdentity(Object.class, new IdClass(1)); + assertEquals("keyAsObject doesn't match.", c1.getKeyAsObject(), new IdClass(1)); + } + + private void validateNestedException(JDOUserException ex, Class expected) { + Throwable[] nesteds = ex.getNestedExceptions(); + if (nesteds == null || nesteds.length == 0) { + fail ("Nested exception is null or length 0"); + } + Throwable nested = nesteds[0]; + if (!(expected.isAssignableFrom(nested.getClass()))) { + fail ("Wrong nested exception. Expected ClassNotFoundException, got " + + nested.toString()); + } + return; + } + public static class IdClass implements Serializable { + public int value; + public IdClass() {value = 0;} + public IdClass(int value) {this.value = value;} + public IdClass(String str) {this.value = Integer.parseInt(str);} + public String toString() {return Integer.toString(value);} + public int hashCode() { + return value; + } + public boolean equals (Object obj) { + if (this == obj) { + return true; + } else { + IdClass other = (IdClass) obj; + return value == other.value; + } + } + } + + public static class BadIdClassNoStringConstructor { + } + + public static class BadIdClassNoPublicStringConstructor { + private BadIdClassNoPublicStringConstructor(String str) {} + } +} Index: test/java/javax/jdo/identity/IntIdentityTest.java =================================================================== --- test/java/javax/jdo/identity/IntIdentityTest.java (revision 209512) +++ test/java/javax/jdo/identity/IntIdentityTest.java (working copy) @@ -95,4 +95,14 @@ assertFalse ("Not equal IntIdentity instances compare equal.", sc1.equals(sc3)); assertFalse ("Not equal IntIdentity instances compare equal.", sc3.equals(sc1)); } + public void testGetKeyAsObjectPrimitive() { + IntIdentity c1 = new IntIdentity(Object.class, 1); + assertEquals("keyAsObject doesn't match.", c1.getKeyAsObject(), new Integer(1)); + } + + public void testGetKeyAsObject() { + IntIdentity c1 = new IntIdentity(Object.class, new Integer(1)); + assertEquals("keyAsObject doesn't match.", c1.getKeyAsObject(), new Integer(1)); + } + } Index: test/java/javax/jdo/identity/CharIdentityTest.java =================================================================== --- test/java/javax/jdo/identity/CharIdentityTest.java (revision 209512) +++ test/java/javax/jdo/identity/CharIdentityTest.java (working copy) @@ -105,4 +105,14 @@ assertFalse ("Not equal CharIdentity instances compare equal.", sc1.equals(sc3)); assertFalse ("Not equal CharIdentity instances compare equal.", sc3.equals(sc1)); } + public void testGetKeyAsObjectPrimitive() { + CharIdentity c1 = new CharIdentity(Object.class, '1'); + assertEquals("keyAsObject doesn't match.", c1.getKeyAsObject(), new Character('1')); + } + + public void testGetKeyAsObject() { + CharIdentity c1 = new CharIdentity(Object.class, new Character('1')); + assertEquals("keyAsObject doesn't match.", c1.getKeyAsObject(), new Character('1')); + } + } Index: test/java/javax/jdo/identity/ShortIdentityTest.java =================================================================== --- test/java/javax/jdo/identity/ShortIdentityTest.java (revision 209512) +++ test/java/javax/jdo/identity/ShortIdentityTest.java (working copy) @@ -95,4 +95,14 @@ assertFalse ("Not equal ShortIdentity instances compare equal.", sc1.equals(sc3)); assertFalse ("Not equal ShortIdentity instances compare equal.", sc3.equals(sc1)); } + public void testGetKeyAsObjectPrimitive() { + ShortIdentity c1 = new ShortIdentity(Object.class, (short)1); + assertEquals("keyAsObject doesn't match.", c1.getKeyAsObject(), new Short((short)1)); + } + + public void testGetKeyAsObject() { + ShortIdentity c1 = new ShortIdentity(Object.class, new Short((short)1)); + assertEquals("keyAsObject doesn't match.", c1.getKeyAsObject(), new Short((short)1)); + } + } Index: test/java/javax/jdo/identity/LongIdentityTest.java =================================================================== --- test/java/javax/jdo/identity/LongIdentityTest.java (revision 209512) +++ test/java/javax/jdo/identity/LongIdentityTest.java (working copy) @@ -95,4 +95,15 @@ assertFalse ("Not equal LongIdentity instances compare equal.", sc1.equals(sc3)); assertFalse ("Not equal LongIdentity instances compare equal.", sc3.equals(sc1)); } + + public void testGetKeyAsObjectPrimitive() { + LongIdentity c1 = new LongIdentity(Object.class, 1L); + assertEquals("keyAsObject doesn't match.", c1.getKeyAsObject(), new Long(1L)); + } + + public void testGetKeyAsObject() { + LongIdentity c1 = new LongIdentity(Object.class, new Long(1L)); + assertEquals("keyAsObject doesn't match.", c1.getKeyAsObject(), new Long(1L)); + } + } Index: test/java/javax/jdo/identity/ByteIdentityTest.java =================================================================== --- test/java/javax/jdo/identity/ByteIdentityTest.java (revision 209512) +++ test/java/javax/jdo/identity/ByteIdentityTest.java (working copy) @@ -95,4 +95,15 @@ assertFalse ("Not equal ByteIdentity instances compare equal.", sc1.equals(sc3)); assertFalse ("Not equal ByteIdentity instances compare equal.", sc3.equals(sc1)); } + + public void testGetKeyAsObjectPrimitive() { + ByteIdentity c1 = new ByteIdentity(Object.class, (byte)1); + assertEquals("keyAsObject doesn't match.", c1.getKeyAsObject(), new Byte((byte)1)); + } + + public void testGetKeyAsObject() { + ByteIdentity c1 = new ByteIdentity(Object.class, new Byte((byte)1)); + assertEquals("keyAsObject doesn't match.", c1.getKeyAsObject(), new Byte((byte)1)); + } + } Index: src/java/javax/jdo/identity/SingleFieldIdentity.java =================================================================== --- src/java/javax/jdo/identity/SingleFieldIdentity.java (revision 209512) +++ src/java/javax/jdo/identity/SingleFieldIdentity.java (working copy) @@ -26,6 +26,10 @@ import java.io.ObjectInput; import java.io.ObjectOutput; +import javax.jdo.JDOFatalInternalException; + +import javax.jdo.spi.I18NHelper; + /** This class is the abstract base class for all single field identity * classes. A common case of application identity uses exactly one * persistent field in the class to represent identity. In this case, @@ -36,6 +40,10 @@ public abstract class SingleFieldIdentity implements Externalizable { + /** The Internationalization message helper. + */ + protected static I18NHelper msg = I18NHelper.getInstance ("javax.jdo.Bundle"); //NOI18N + /** The class of the target object. */ transient private Class targetClass; @@ -47,6 +55,10 @@ /** The hashCode. */ protected int hashCode; + + /** The key as an Object. + */ + protected Object keyAsObject; /** Constructor with target class. * @param pcClass the class of the target @@ -80,6 +92,27 @@ return targetClassName; } + /** Return the key as an Object. The method is synchronized to avoid + * race conditions in multi-threaded environments. + * @return the key as an Object. + * @since 2.0 + */ + public synchronized Object getKeyAsObject() { + if (keyAsObject == null) { + keyAsObject = createKeyAsObject(); + } + return keyAsObject; + } + + /** Create the key as an Object. + * @return the key as an Object; + * @since 2.0 + */ + protected Object createKeyAsObject() { + throw new JDOFatalInternalException + (msg.msg("EXC_CreateKeyAsObjectMustNotBeCalled")); + } + /** Check the class and class name and object type. If restored * from serialization, class will be null so compare class name. * @param obj the other object Index: src/java/javax/jdo/identity/StringIdentity.java =================================================================== --- src/java/javax/jdo/identity/StringIdentity.java (revision 209512) +++ src/java/javax/jdo/identity/StringIdentity.java (working copy) @@ -30,11 +30,9 @@ */ public class StringIdentity extends SingleFieldIdentity { - /** The key. + /** The key is stored in the superclass field keyAsObject. */ - private String key; - - + /** Constructor with class and key. * @param pcClass the class * @param key the key @@ -43,8 +41,8 @@ super (pcClass); if (key == null) throw new NullPointerException (); - this.key = key; - hashCode = hashClassName() ^ key.hashCode(); + this.keyAsObject = key; + hashCode = hashClassName() ^ keyAsObject.hashCode(); } /** Constructor only for Externalizable. @@ -56,14 +54,14 @@ * @return the key */ public String getKey () { - return key; + return (String)keyAsObject; } /** Return the String form of the key. * @return the String form of the key */ public String toString () { - return key; + return (String)keyAsObject; } /** Determine if the other object represents the same object id. @@ -77,7 +75,7 @@ return false; } else { StringIdentity other = (StringIdentity) obj; - return key.equals(other.key); + return keyAsObject.equals(other.keyAsObject); } } @@ -86,7 +84,7 @@ */ public void writeExternal(ObjectOutput out) throws IOException { super.writeExternal (out); - out.writeObject(key); + out.writeObject(keyAsObject); } /** Read this object. Read the superclass first. @@ -95,7 +93,6 @@ public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException { super.readExternal (in); - key = (String)in.readObject(); - hashCode = hashClassName() ^ key.hashCode(); + keyAsObject = (String)in.readObject(); } } Index: src/java/javax/jdo/identity/ObjectIdentity.java =================================================================== --- src/java/javax/jdo/identity/ObjectIdentity.java (revision 0) +++ src/java/javax/jdo/identity/ObjectIdentity.java (revision 0) @@ -0,0 +1,172 @@ +/* + * Copyright 2005 The Apache Software Foundation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * ObjectIdentity.java + * + */ + +package javax.jdo.identity; + +import java.io.IOException; +import java.io.ObjectInput; +import java.io.ObjectOutput; + +import java.lang.reflect.Constructor; +import java.lang.reflect.InvocationTargetException; + +import java.util.Currency; +import java.util.Date; +import java.util.HashMap; +import java.util.Locale; +import java.util.Map; + +import javax.jdo.JDOException; +import javax.jdo.JDOUserException; +import javax.jdo.spi.JDOImplHelper; +/** This class is for identity with a single Object type field. + * @version 2.0 + */ +public class ObjectIdentity extends SingleFieldIdentity { + + /** The key is stored in the superclass field keyAsObject. + */ + + + /** The JDOImplHelper instance used for parsing the String to an Object. + */ + JDOImplHelper helper = JDOImplHelper.getInstance(); + + /** The delimiter for String constructor. + */ + public String STRING_DELIMITER = ":"; + + /** Constructor with class and key. + * @param pcClass the class + * @param param the key + */ + public ObjectIdentity (Class pcClass, Object param) { + super (pcClass); + String paramString = null; + String keyString = null; + String className = null; + if (param == null | pcClass == null) + throw new NullPointerException (); + if (param instanceof String) { + /* The paramString is of the form "<className>:<keyString>" */ + paramString = (String)param; + if (paramString.length() < 3) { + throw new JDOUserException( + msg.msg("EXC_ObjectIdentityStringConstructionTooShort") + //NOI18N + msg.msg("EXC_ObjectIdentityStringConstructionUsage", //NOI18N + paramString)); + } + int indexOfDelimiter = paramString.indexOf(STRING_DELIMITER); + if (indexOfDelimiter < 0) { + throw new JDOUserException( + msg.msg("EXC_ObjectIdentityStringConstructionNoDelimiter") + //NOI18N + msg.msg("EXC_ObjectIdentityStringConstructionUsage", //NOI18N + paramString)); + } + keyString = paramString.substring(indexOfDelimiter+1); + className = paramString.substring(0, indexOfDelimiter); + try { + Class keyClass = Class.forName(className); + keyAsObject = helper.construct(keyClass, keyString); + if (keyAsObject == null) { + Constructor keyConstructor = keyClass.getConstructor(new Class[]{String.class}); + keyAsObject = keyConstructor.newInstance(new Object[]{keyString}); + } + } catch (JDOUserException ex) { + throw ex; + } catch (Exception ex) { + /* ClassNotFoundException, + NoSuchMethodException, + InstantiationException, + IllegalAccessException, + InvocationTargetException */ + throw new JDOUserException( + msg.msg("EXC_ObjectIdentityStringConstruction", //NOI18N + new Object[] + {paramString, ex.toString(), className, keyString}), ex); + } + } else { + keyAsObject = param; + } + hashCode = hashClassName() ^ keyAsObject.hashCode(); + } + + /** Constructor only for Externalizable. + */ + public ObjectIdentity () { + } + + /** Return the key. + * @return the key + */ + public Object getKey () { + return keyAsObject; + } + + /** Return the String form of the object id. The class of the + * object id is written as the first part of the result so that + * the class can be reconstructed later. Then the toString + * of the key instance is appended. During construction, + * this process is reversed. The class is extracted from + * the first part of the String, and the String constructor + * of the key is used to construct the key itself. + * @return the String form of the key + */ + public String toString () { + return keyAsObject.getClass().getName() + + STRING_DELIMITER + + keyAsObject.toString(); + } + + /** Determine if the other object represents the same object id. + * @param obj the other object + * @return true if both objects represent the same object id + */ + public boolean equals (Object obj) { + if (this == obj) { + return true; + } else if (!super.equals (obj)) { + return false; + } else { + ObjectIdentity other = (ObjectIdentity) obj; + return keyAsObject.equals(other.keyAsObject); + } + } + + /** Write this object. Write the superclass first. + * @param out the output + */ + public void writeExternal(ObjectOutput out) throws IOException { + super.writeExternal (out); + out.writeObject(keyAsObject); + } + + /** Read this object. Read the superclass first. + * @param in the input + */ + public void readExternal(ObjectInput in) + throws IOException, ClassNotFoundException { + super.readExternal (in); + keyAsObject = in.readObject(); +// hashCode = hashClassName() ^ keyAsObject.hashCode(); + } + +} Index: src/java/javax/jdo/identity/IntIdentity.java =================================================================== --- src/java/javax/jdo/identity/IntIdentity.java (revision 209512) +++ src/java/javax/jdo/identity/IntIdentity.java (working copy) @@ -47,6 +47,7 @@ */ public IntIdentity (Class pcClass, Integer key) { this (pcClass, key.intValue ()); + keyAsObject = key; } @@ -92,6 +93,14 @@ } } + /** Create the key as an Object. + * @return the key as an Object + * @since 2.0 + */ + protected Object createKeyAsObject() { + return new Integer(key); + } + /** Write this object. Write the superclass first. * @param out the output */ @@ -107,6 +116,5 @@ throws IOException, ClassNotFoundException { super.readExternal (in); key = in.readInt(); - hashCode = hashClassName() ^ key; } } Index: src/java/javax/jdo/identity/CharIdentity.java =================================================================== --- src/java/javax/jdo/identity/CharIdentity.java (revision 209512) +++ src/java/javax/jdo/identity/CharIdentity.java (working copy) @@ -56,6 +56,7 @@ */ public CharIdentity (Class pcClass, Character key) { this (pcClass, key.charValue ()); + keyAsObject = key; } /** Constructor with class and key. The String must have exactly one @@ -106,6 +107,14 @@ } } + /** Create the key as an Object. + * @return the key as an Object + * @since 2.0 + */ + protected Object createKeyAsObject() { + return new Character(key); + } + /** Write this object. Write the superclass first. * @param out the output */ Index: src/java/javax/jdo/identity/ShortIdentity.java =================================================================== --- src/java/javax/jdo/identity/ShortIdentity.java (revision 209512) +++ src/java/javax/jdo/identity/ShortIdentity.java (working copy) @@ -50,6 +50,7 @@ */ public ShortIdentity (Class pcClass, Short key) { this (pcClass, key.shortValue ()); + keyAsObject = key; } /** Constructor with class and key. @@ -94,6 +95,14 @@ } } + /** Create the key as an Object. + * @return the key as an Object + * @since 2.0 + */ + protected Object createKeyAsObject() { + return new Short(key); + } + /** Write this object. Write the superclass first. * @param out the output */ @@ -109,6 +118,5 @@ throws IOException, ClassNotFoundException { super.readExternal (in); key = in.readShort(); - hashCode = hashClassName() ^ key; } } Index: src/java/javax/jdo/identity/LongIdentity.java =================================================================== --- src/java/javax/jdo/identity/LongIdentity.java (revision 209512) +++ src/java/javax/jdo/identity/LongIdentity.java (working copy) @@ -50,6 +50,7 @@ */ public LongIdentity (Class pcClass, Long key) { this (pcClass, key.longValue ()); + keyAsObject = key; } /** Constructor with class and key. @@ -94,6 +95,14 @@ } } + /** Create the key as an Object. + * @return the key as an Object + * @since 2.0 + */ + protected Object createKeyAsObject() { + return new Long(key); + } + /** Write this object. Write the superclass first. * @param out the output */ @@ -109,6 +118,6 @@ throws IOException, ClassNotFoundException { super.readExternal (in); key = in.readLong(); - hashCode = hashClassName() ^ (int)key; } + } Index: src/java/javax/jdo/identity/ByteIdentity.java =================================================================== --- src/java/javax/jdo/identity/ByteIdentity.java (revision 209512) +++ src/java/javax/jdo/identity/ByteIdentity.java (working copy) @@ -50,6 +50,7 @@ */ public ByteIdentity(Class pcClass, Byte key) { this (pcClass, key.byteValue()); + keyAsObject = key; } /** Constructor with class and key. @@ -94,6 +95,14 @@ } } + /** Create the key as an Object. + * @return the key as an Object + * @since 2.0 + */ + protected Object createKeyAsObject() { + return new Byte(key); + } + /** Write this object. Write the superclass first. * @param out the output */ @@ -109,6 +118,5 @@ throws IOException, ClassNotFoundException { super.readExternal (in); key = in.readByte (); - hashCode = super.hashCode() ^ key; } } Index: src/java/javax/jdo/spi/JDOImplHelper.java =================================================================== --- src/java/javax/jdo/spi/JDOImplHelper.java (revision 209536) +++ src/java/javax/jdo/spi/JDOImplHelper.java (working copy) @@ -21,18 +21,26 @@ package javax.jdo.spi; +import java.text.DateFormat; +import java.text.ParsePosition; +import java.text.SimpleDateFormat; + import java.util.ArrayList; import java.util.Collection; import java.util.Collections; +import java.util.Currency; +import java.util.Date; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.List; +import java.util.Locale; import java.util.Map; import java.util.WeakHashMap; import javax.jdo.JDOFatalInternalException; import javax.jdo.JDOFatalUserException; +import javax.jdo.JDOUserException; import javax.jdo.spi.JDOPermission; /** This class is a helper class for JDO implementations. It contains methods @@ -267,7 +275,7 @@ byte[] fieldFlags, Class persistenceCapableSuperclass, PersistenceCapable pc) { if (pcClass == null) - throw new NullPointerException(msg.msg("ERR_NullClass")); + throw new NullPointerException(msg.msg("ERR_NullClass")); //NOI18N Meta meta = new Meta (fieldNames, fieldTypes, fieldFlags, persistenceCapableSuperclass, pc); registeredClasses.put (pcClass, meta); @@ -413,7 +421,7 @@ public static void registerAuthorizedStateManagerClass (Class smClass) throws SecurityException { if (smClass == null) - throw new NullPointerException(msg.msg("ERR_NullClass")); + throw new NullPointerException(msg.msg("ERR_NullClass")); //NOI18N SecurityManager sm = System.getSecurityManager(); if (sm != null) { sm.checkPermission(JDOPermission.SET_STATE_MANAGER); @@ -442,7 +450,7 @@ Object smClass = it.next(); if (!(smClass instanceof Class)) { throw new ClassCastException( - msg.msg("ERR_StateManagerClassCast", + msg.msg("ERR_StateManagerClassCast", //NOI18N smClass.getClass().getName())); } registerAuthorizedStateManagerClass((Class)it.next()); @@ -489,6 +497,106 @@ scm.checkPermission(JDOPermission.SET_STATE_MANAGER); } + /** The default DateFormat instance. + */ + static DateFormat dateFormat = DateFormat.getDateTimeInstance(); + + /** The DateFormat pattern, set to the default. + */ + static String dateFormatPattern = "MMM d, yyyy hh:mm:ss a"; //NOI18N + + /** Register a DateFormat instance for use with constructing Date + * instances. The default is the default DateFormat instance. + * If the new instance implements SimpleDateFormat, set the pattern + * for error messages. + */ + synchronized void registerDateFormat(DateFormat df) { + dateFormat = df; + if (df instanceof SimpleDateFormat) { + dateFormatPattern = ((SimpleDateFormat)df).toPattern(); + } + } + + /** + * Construct an instance of a key class using a String as input. + * This is a helper interface for use with ObjectIdentity. + * Classes without a String constructor (such as those in java.lang + * and java.util) will use this interface for constructing new instances. + */ + public interface StringConstructor { + public Object construct(String s); + } + + /** + * Special StringConstructor instances for use with specific + * classes that have no public String constructor. The Map is + * keyed on class instance and the value is an instance of + * StringConstructor. + */ + static Map stringConstructorMap = new HashMap(); + + /** + * Register special StringConstructor instances. These instances + * are for constructing instances from String parameters where there + * is no String constructor for them. + */ + public Object registerStringConstructor(Class cls, StringConstructor sc) { + return stringConstructorMap.put(cls, sc); + } + + /** Register the default special StringConstructor instances. + */ + static { + JDOImplHelper helper = getInstance(); + helper.registerStringConstructor(Currency.class, new StringConstructor() { + public Object construct(String s) { + try { + return Currency.getInstance(s); + } catch (IllegalArgumentException ex) { + throw new javax.jdo.JDOUserException( + msg.msg("EXC_CurrencyStringConstructorIllegalArgument", s), ex); //NOI18N + } catch (Exception ex) { + throw new JDOUserException( + msg.msg("EXC_CurrencyStringConstructorException"), ex); //NOI18N + } + } + }); + helper.registerStringConstructor(Locale.class, new StringConstructor() { + public Object construct(String s) { + try { + return new Locale(s); + } catch (Exception ex) { + throw new JDOUserException( + msg.msg("EXC_LocaleStringConstructorException"), ex); //NOI18N + } + } + }); + helper.registerStringConstructor(Date.class, new StringConstructor() { + public synchronized Object construct(String s) { + ParsePosition pp = new ParsePosition(0); + Date result = dateFormat.parse(s, pp); + if (result == null) { + throw new JDOUserException ( + msg.msg("EXC_DateStringConstructor", new Object[] //NOI18N + {s, new Integer(pp.getErrorIndex()), dateFormatPattern})); + } + return result; + } + }); + } + + public Object construct(Class cls, String s) { + synchronized(stringConstructorMap) { + StringConstructor sc = (StringConstructor) stringConstructorMap.get(cls); + if (sc == null) { + // no special treatment for this class + return null; + } else { + return sc.construct(s); + } + } + } + /** This is a helper class to manage metadata per persistence-capable * class. The information is used at runtime to provide field names and * field types to the JDO Model. Index: src/java/javax/jdo/Bundle.properties =================================================================== --- src/java/javax/jdo/Bundle.properties (revision 209514) +++ src/java/javax/jdo/Bundle.properties (working copy) @@ -66,3 +66,11 @@ \nThe parameter String is of the form "<className>:<keyString>". EXC_CreateKeyAsObjectMustNotBeCalled: The method createKeyAsObject must not be called \ because the keyAsObject field must never be null for this class. +EXC_CurrencyStringConstructorIllegalArgument: The instance could not be constructed \ +with the argument "{0}". Try "USD". +EXC_CurrencyStringConstructorException: An exception was thrown during construction \ +of the Currency instance. +EXC_LocaleStringConstructorException: An exception was thrown during construction \ +of the Locale instance. +EXC_DateStringConstructor: Error parsing Date string "{0}" at position {1} \ +using date format "{2}". \ No newline at end of file
Craig Russell Architect, Sun Java Enterprise System http://java.sun.com/products/jdo 408 276-5638 mailto:[EMAIL PROTECTED] P.S. A good JDO? O, Gasp! |
smime.p7s
Description: S/MIME cryptographic signature