Index: test/java/javax/jdo/ObjectStateTest.java =================================================================== --- test/java/javax/jdo/ObjectStateTest.java (revision 0) +++ test/java/javax/jdo/ObjectStateTest.java (revision 0) @@ -0,0 +1,237 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + */ + +package javax.jdo; + +import java.lang.reflect.InvocationHandler; +import java.lang.reflect.Method; +import java.lang.reflect.Proxy; + +import javax.jdo.spi.PersistenceCapable; + +import javax.jdo.util.AbstractTest; +import javax.jdo.util.BatchTestRunner; + +/* + * ObjectStateTest.java + * This test class verifies the proper returned ObjectState for + * each life cycle state. See Table 3 in section 7.4. + * + * @since 2.1 + */ +public class ObjectStateTest extends AbstractTest { + + static final int PERSISTENT = 1; + static final int TRANSACTIONAL = 2; + static final int DIRTY = 4; + static final int NEW = 8; + static final int DELETED = 16; + static final int DETACHED = 32; + + private static final Method jdoIsPersistent = getDeclaredMethod( + PersistenceCapable.class, + "jdoIsPersistent", + null); + private static final Method jdoIsTransactional = getDeclaredMethod( + PersistenceCapable.class, + "jdoIsTransactional", + null); + private static final Method jdoIsDirty = getDeclaredMethod( + PersistenceCapable.class, + "jdoIsDirty", + null); + private static final Method jdoIsNew = getDeclaredMethod( + PersistenceCapable.class, + "jdoIsNew", + null); + private static final Method jdoIsDeleted = getDeclaredMethod( + PersistenceCapable.class, + "jdoIsDeleted", + null); + private static final Method jdoIsDetached = getDeclaredMethod( + PersistenceCapable.class, + "jdoIsDetached", + null); + + /** */ + public static void main(String args[]) { + BatchTestRunner.run(ObjectStateTest.class); + } + + public void testNull() { + PersistenceCapable mock = null; + assertObjectState("null", null, mock); + } + + public void testTransient() { + PersistenceCapable mock = newMock(0); + assertObjectState("transient", ObjectState.TRANSIENT, mock); + } + + public void testTransientClean() { + PersistenceCapable mock = newMock(TRANSACTIONAL); + assertObjectState("transient-clean", ObjectState.TRANSIENT_CLEAN, mock); + } + + public void testTransientDirty() { + PersistenceCapable mock = newMock(TRANSACTIONAL+DIRTY); + assertObjectState("transient-dirty", ObjectState.TRANSIENT_DIRTY, mock); + } + + public void testPersistentNew() { + PersistenceCapable mock = newMock(PERSISTENT+TRANSACTIONAL+NEW+DIRTY); + assertObjectState("persistent-new", ObjectState.PERSISTENT_NEW, mock); + } + + public void testPersistentNontransactional() { + PersistenceCapable mock = newMock(PERSISTENT); + assertObjectState("persistent-nontransactional", ObjectState.HOLLOW_PERSISTENT_NONTRANSACTIONAL, mock); + } + + public void testPersistentNontransactionalDirty() { + PersistenceCapable mock = newMock(PERSISTENT+DIRTY); + assertObjectState("persistent-nontransactional-dirty", ObjectState.PERSISTENT_NONTRANSACTIONAL_DIRTY, mock); + } + + public void testPersistentClean() { + PersistenceCapable mock = newMock(PERSISTENT+TRANSACTIONAL); + assertObjectState("persistent-clean", ObjectState.PERSISTENT_CLEAN, mock); + } + + public void testPersistentDirty() { + PersistenceCapable mock = newMock(PERSISTENT+TRANSACTIONAL+DIRTY); + assertObjectState("persistent-dirty", ObjectState.PERSISTENT_DIRTY, mock); + } + + public void testPersistentDeleted() { + PersistenceCapable mock = newMock(PERSISTENT+TRANSACTIONAL+DIRTY+DELETED); + assertObjectState("persistent-deleted", ObjectState.PERSISTENT_DELETED, mock); + } + + public void testPersistentNewDeleted() { + PersistenceCapable mock = newMock(PERSISTENT+TRANSACTIONAL+NEW+DIRTY+DELETED); + assertObjectState("persistent-new-deleted", ObjectState.PERSISTENT_NEW_DELETED, mock); + } + + public void testDetachedClean() { + PersistenceCapable mock = newMock(DETACHED); + assertObjectState("detached-clean", ObjectState.DETACHED_CLEAN, mock); + } + + public void testDetachedDirty() { + PersistenceCapable mock = newMock(DETACHED+DIRTY); + assertObjectState("detached-dirty", ObjectState.DETACHED_DIRTY, mock); + } + + private void assertObjectState(String string, + ObjectState expected, + PersistenceCapable pc) { + ObjectState actual = JDOHelper.getObjectState(pc); + // test for == here because enums should be singleton + if (actual == expected) + return; + fail("ObjectState failure for " + string + NL + + "expected: " + expected + + ", actual: " + actual); + } + + /** + * Construct a new mock instance of PersistenceCapable that responds + * to methods jdoIsXXX by returning the bit value of its constructor + * masked with the value of XXX. + * @param i the sum of bit masks representing the life cycle state + * @return a mock instance of PersistenceCapable + */ + private PersistenceCapable newMock(final int i) { + return (PersistenceCapable) + Proxy.newProxyInstance( + PersistenceCapable.class.getClassLoader(), + new Class[] {PersistenceCapable.class}, + new MockInvocationHandler(i)); + } + + private class MockInvocationHandler implements InvocationHandler { + /** States is the sum of all life cycle interrogatives. + */ + private int states; + + /** + * Constructs an invocation handler with the specified bit fields set + * according to the sum of values of PERSISTENT, TRANSACTIONAL, DIRTY, + * NEW, DELETED, and DETACHED. + * @param i the bit field values for the life cycle interrogatives + */ + private MockInvocationHandler(int i) { + states = i; + } + + /** + * + * @param object the PersistenceCapable instance + * @param method the method being invoked + * @param parameters parameters (should be null) + * @throws java.lang.Throwable unused + * @return for jdoIsXXX methods only, returns whether the + * bit field selected by the method is set in the + * mock handler + */ + public Object invoke(Object object, Method method, Object[] parameters) + throws Throwable { + if (method.equals(jdoIsPersistent)) { + return (0 != (states & PERSISTENT)); + } + if (method.equals(jdoIsTransactional)) { + return (0 != (states & TRANSACTIONAL)); + } + if (method.equals(jdoIsDirty)) { + return (0 != (states & DIRTY)); + } + if (method.equals(jdoIsNew)) { + return (0 != (states & NEW)); + } + if (method.equals(jdoIsDeleted)) { + return (0 != (states & DELETED)); + } + if (method.equals(jdoIsDetached)) { + return (0 != (states & DETACHED)); + } + fail("Unexpected method called: " + method.getName()); + return Boolean.FALSE; // not reached + } + } + + private static Method getDeclaredMethod + (Class clazz, String methodName, Class[] parameters) { + try { + Method result = clazz.getDeclaredMethod(methodName, parameters); + return result; + } catch (Exception ex) { + // human-readable class.methodName(parameter[,parameter]) + StringBuffer sb = new StringBuffer(methodName); + String delimiter = "("; + for (Class parameter: parameters) { + sb.append(delimiter); + sb.append(parameter.getName()); + delimiter = ","; + } + sb.append(")"); + throw new RuntimeException + ("getDeclaredMethod for " + clazz.getName() + "." + + sb + " threw..." + ex); + } + } +} Property changes on: test/java/javax/jdo/ObjectStateTest.java ___________________________________________________________________ Name: svn:eol-style + LF Index: src/java/javax/jdo/ObjectState.java =================================================================== --- src/java/javax/jdo/ObjectState.java (revision 0) +++ src/java/javax/jdo/ObjectState.java (revision 0) @@ -0,0 +1,55 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + */ + +/* + * ObjectState.java + * + */ +package javax.jdo; + +/** + * This class defines the object states for JDO instances. + * + * @version 2.1 + */ +public enum ObjectState { + + TRANSIENT("transient"), + TRANSIENT_CLEAN("transient-clean"), + TRANSIENT_DIRTY("transient-dirty"), + PERSISTENT_NEW("persistent-new"), + HOLLOW_PERSISTENT_NONTRANSACTIONAL("hollow/persistent-nontransactional"), + PERSISTENT_NONTRANSACTIONAL_DIRTY("persistent-nontransactional-dirty"), + PERSISTENT_CLEAN("persistent-clean"), + PERSISTENT_DIRTY("persistent-dirty"), + PERSISTENT_DELETED("persistent-deleted"), + PERSISTENT_NEW_DELETED("persistent-new-deleted"), + DETACHED_CLEAN("detached-clean"), + DETACHED_DIRTY("detached-dirty"); + + private final String value; + + private ObjectState(String value) { + this.value = value; + } + + public String toString() { + return value; + } +} + + Property changes on: src/java/javax/jdo/ObjectState.java ___________________________________________________________________ Name: svn:eol-style + LF Index: src/java/javax/jdo/JDOHelper.java =================================================================== --- src/java/javax/jdo/JDOHelper.java (revision 577824) +++ src/java/javax/jdo/JDOHelper.java (working copy) @@ -591,7 +591,86 @@ return implHelper.nonBinaryCompatibleIs(pc, isDetached); } } - + + /** Accessor for the state of the passed object. + * @param pc The object + * @return The object state + * @since 2.1 + */ + public static ObjectState getObjectState(Object pc) { + if (pc == null) { + return null; + } + + if (isDetached(pc)) { + // Detached + if (isDirty(pc)) { + return ObjectState.DETACHED_DIRTY; + } + else { + return ObjectState.DETACHED_CLEAN; + } + } + else { + if (isPersistent(pc)) { + // Persistent + if (isTransactional(pc)) { + // Transactional + if (isDirty(pc)) { + // Dirty + if (isNew(pc)) { + // New + if (isDeleted(pc)) { + // New Deleted + return ObjectState.PERSISTENT_NEW_DELETED; + } else { + // New Not Deleted + return ObjectState.PERSISTENT_NEW; + } + } else { + // Not New + if (isDeleted(pc)) { + // Deleted + return ObjectState.PERSISTENT_DELETED; + } else { + // Not Deleted + return ObjectState.PERSISTENT_DIRTY; + } + } + } else { + // Not dirty + return ObjectState.PERSISTENT_CLEAN; + } + } + else { + // Nontransactional + if (isDirty(pc)) { + return ObjectState.PERSISTENT_NONTRANSACTIONAL_DIRTY; + } + else { + return ObjectState.HOLLOW_PERSISTENT_NONTRANSACTIONAL; + } + } + } + else { + // Not Persistent + if (isTransactional(pc)) { + // Transactional + if (isDirty(pc)) { + // Dirty + return ObjectState.TRANSIENT_DIRTY; + } else { + // Not Dirty + return ObjectState.TRANSIENT_CLEAN; + } + } + else { + return ObjectState.TRANSIENT; + } + } + } + } + /** Get the anonymous PersistenceManagerFactory configured via * the standard configuration file resource "META-INF/jdoconfig.xml", using * the current thread's context class loader