You are viewing a plain text version of this content. The canonical link for it is here.
Posted to jdo-commits@db.apache.org by ti...@apache.org on 2017/01/06 20:05:47 UTC

svn commit: r1777710 - in /db/jdo/trunk/tck/src/java/org/apache/jdo/tck: pc/query/OptionalSample.java query/jdoql/methods/SupportedOptionalMethods.java

Author: tilmannz
Date: Fri Jan  6 20:05:47 2017
New Revision: 1777710

URL: http://svn.apache.org/viewvc?rev=1777710&view=rev
Log:
Patch for JDO-751 - Support for Java 8 Optional

Added:
    db/jdo/trunk/tck/src/java/org/apache/jdo/tck/pc/query/OptionalSample.java   (with props)
    db/jdo/trunk/tck/src/java/org/apache/jdo/tck/query/jdoql/methods/SupportedOptionalMethods.java   (with props)

Added: db/jdo/trunk/tck/src/java/org/apache/jdo/tck/pc/query/OptionalSample.java
URL: http://svn.apache.org/viewvc/db/jdo/trunk/tck/src/java/org/apache/jdo/tck/pc/query/OptionalSample.java?rev=1777710&view=auto
==============================================================================
--- db/jdo/trunk/tck/src/java/org/apache/jdo/tck/pc/query/OptionalSample.java (added)
+++ db/jdo/trunk/tck/src/java/org/apache/jdo/tck/pc/query/OptionalSample.java Fri Jan  6 20:05:47 2017
@@ -0,0 +1,128 @@
+/*
+ * 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 org.apache.jdo.tck.pc.query;
+
+import java.util.Date;
+import java.util.Optional;
+
+public class OptionalSample {
+    long id;
+
+    Optional<OptionalSample> optionalPC;
+
+    Optional<Date> optionalDate;
+    Optional<Integer> optionalInteger;
+    Optional<String> optionalString;
+
+
+    public long getId() {
+        return id;
+    }
+
+    public void setId(long id) {
+        this.id = id;
+    }
+
+    public Optional<OptionalSample> getOptionalPC() {
+        return optionalPC;
+    }
+
+    public void setOptionalPC(Optional<OptionalSample> opt) {
+        this.optionalPC = opt;
+    }
+
+    public Optional<Date> getOptionalDate() {
+        return optionalDate;
+    }
+
+    public void setOptionalDate(Optional<Date> opt) {
+        this.optionalDate = opt;
+    }
+
+    public Optional<Integer> getOptionalInteger() {
+        return optionalInteger;
+    }
+
+    public void setOptionalInteger(Optional<Integer> opt) {
+        this.optionalInteger = opt;
+    }
+
+    public Optional<String> getOptionalString() {
+        return optionalString;
+    }
+
+    public void setOptionalString(Optional<String> opt) {
+        this.optionalString = opt;
+    }
+
+    @Override
+    public int hashCode() {
+        final int prime = 31;
+        int result = 1;
+        result = prime * result + ((optionalPC == null) ? 0 : optionalPC.hashCode());
+        result = prime * result + ((optionalString == null) ? 0 : optionalString.hashCode());
+        result = prime * result + ((optionalInteger == null) ? 0 : optionalInteger.hashCode());
+        result = prime * result + ((optionalDate == null) ? 0 : optionalDate.hashCode());
+        result = prime * result + (int) (id ^ (id >>> 32));
+        return result;
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (this == obj)
+            return true;
+        if (obj == null)
+            return false;
+        if (getClass() != obj.getClass())
+            return false;
+        OptionalSample other = (OptionalSample) obj;
+        if (id != other.id)
+            return false;
+
+        if (optionalPC == null) {
+            if (other.optionalPC != null)
+                return false;
+        } else if (!optionalPC.equals(other.optionalPC))
+            return false;
+
+        if (optionalDate == null) {
+            if (other.optionalDate != null)
+                return false;
+        } else if (!optionalDate.equals(other.optionalDate))
+            return false;
+
+        if (optionalInteger == null) {
+            if (other.optionalInteger != null)
+                return false;
+        } else if (!optionalInteger.equals(other.optionalInteger))
+            return false;
+
+        if (optionalString == null) {
+            if (other.optionalString != null)
+                return false;
+        } else if (!optionalString.equals(other.optionalString))
+            return false;
+
+        return true;
+    }
+
+    @Override
+    public String toString() {
+        return "id=" + id + " " + super.toString();
+    }
+}

Propchange: db/jdo/trunk/tck/src/java/org/apache/jdo/tck/pc/query/OptionalSample.java
------------------------------------------------------------------------------
    svn:executable = *

Added: db/jdo/trunk/tck/src/java/org/apache/jdo/tck/query/jdoql/methods/SupportedOptionalMethods.java
URL: http://svn.apache.org/viewvc/db/jdo/trunk/tck/src/java/org/apache/jdo/tck/query/jdoql/methods/SupportedOptionalMethods.java?rev=1777710&view=auto
==============================================================================
--- db/jdo/trunk/tck/src/java/org/apache/jdo/tck/query/jdoql/methods/SupportedOptionalMethods.java (added)
+++ db/jdo/trunk/tck/src/java/org/apache/jdo/tck/query/jdoql/methods/SupportedOptionalMethods.java Fri Jan  6 20:05:47 2017
@@ -0,0 +1,574 @@
+/*
+ * 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 org.apache.jdo.tck.query.jdoql.methods;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Date;
+import java.util.Optional;
+
+import javax.jdo.PersistenceManager;
+import javax.jdo.Query;
+import javax.jdo.Transaction;
+
+import org.apache.jdo.tck.JDO_Test;
+import org.apache.jdo.tck.pc.query.OptionalSample;
+import org.apache.jdo.tck.query.QueryElementHolder;
+import org.apache.jdo.tck.query.QueryTest;
+import org.apache.jdo.tck.util.BatchTestRunner;
+
+/**
+ *<B>Title:</B> Optional Fields.
+ *<BR>
+ *<B>Keywords:</B> query
+ *<BR>
+ *<B>Assertion ID:</B> A14.6.2-9
+ *<BR>
+ *<B>Assertion Description: </B>
+ *Queries on fields of type java.util.Optional .
+ */
+public class SupportedOptionalMethods extends QueryTest {
+
+    /** */
+    private static final String ASSERTION_FAILED = 
+            "Assertion A14.6.2-9 (OptionalFields) failed: ";
+
+    private static final int PC_ID = 1;
+    private static final int PC_EMPTY_ID = 3;
+    private static final int PC_NULL_ID = 4;
+    private static final int REFERENCED_PC1_ID = 88;
+    private static final int REFERENCED_PC2_ID = 99;
+    private static final String STRING = "Hello JDO";
+    private static final Integer INTEGER = 2016;
+    private static final Date DATE = new Date(1000000000);
+    private Object oidReferencedPC1;
+    private Object oidReferencedPC2;
+    private Object oidPC;
+    private Object oidEmpty;
+    private Object oidNull;
+
+    /**
+     * The <code>main</code> is called when the class
+     * is directly executed from the command line.
+     * @param args The arguments passed to the program.
+     */
+    public static void main(String[] args) {
+        BatchTestRunner.run(SupportedOptionalMethods.class);
+    }
+
+    /** */
+    public void testQueriesWithPresence() {
+        //Matches 'optionalPC.isPresent() == true'
+        checkQuery("optionalPC != null", oidPC, oidReferencedPC1); 
+
+        //matches !isPresent() but does NOT match Java 'optionalPC==null'
+        checkQuery("optionalPC == null", oidEmpty, oidNull, oidReferencedPC2);
+
+        //matches isPresent() 
+        checkQuery("!(optionalPC == null)", oidReferencedPC1, oidPC);
+
+        //matches !isPresent() 
+        checkQuery("!(optionalPC != null)", oidReferencedPC2, oidEmpty, oidNull);
+
+        checkQuery("optionalPC.get() != null", oidPC, oidReferencedPC1);
+        checkQuery("optionalPC.get() == null", oidEmpty, oidNull, oidReferencedPC2);
+        checkQuery("optionalPC.isPresent()", oidPC, oidReferencedPC1);
+        checkQuery("!optionalPC.isPresent()", oidReferencedPC2, oidEmpty, oidNull);
+
+        //querying non-PC 'Optional' fields
+        checkOptionalForPresence("optionalString");
+        checkOptionalForPresence("optionalDate");
+        checkOptionalForPresence("optionalInteger");
+    }
+
+    private void checkOptionalForPresence(String fieldName) {
+        checkQuery(fieldName + " != null", 			oidPC);
+        checkQuery(fieldName + " == null", 			
+                oidEmpty, oidNull, oidReferencedPC1, oidReferencedPC2);
+        checkQuery("!(" + fieldName + " == null)", 	oidPC);
+        checkQuery("!("+ fieldName + " != null)", 	
+                oidEmpty, oidNull, oidReferencedPC1, oidReferencedPC2);
+
+        checkQuery(fieldName + ".get() != null", 	oidPC);
+        checkQuery(fieldName + ".get() == null", 	
+                oidEmpty, oidNull, oidReferencedPC1, oidReferencedPC2);
+        checkQuery(fieldName + ".isPresent()", 		oidPC);
+        checkQuery("!"+ fieldName + ".isPresent()", 
+                oidEmpty, oidNull, oidReferencedPC1, oidReferencedPC2);
+    }
+
+    public void testQueriesWithNavigation() {
+        checkQuery("optionalPC.id == " + REFERENCED_PC1_ID, oidPC);
+        checkQuery("optionalPC.id != " + REFERENCED_PC1_ID, oidReferencedPC1);
+        checkQuery("!(optionalPC.id == " + REFERENCED_PC1_ID + ")", oidReferencedPC1);
+        checkQuery("optionalPC.get().id == " + REFERENCED_PC1_ID, oidPC);
+        checkQuery("optionalPC.get().id != " + REFERENCED_PC1_ID, oidReferencedPC1);
+        checkQuery("!(optionalPC.get().id == " + REFERENCED_PC1_ID + ")", oidReferencedPC1);
+        checkQuery("optionalPC.optionalPC.isPresent()", oidPC);
+
+        //The following reflects the changed behavior in JDO 3.2 in  the sense that
+        //all instances are returned where either 'optionalPC.optionalPC==null' (not present)
+        //or 'optionalPC==null' (the 'null' evaluates to 'null', which is followed until it is 
+        //evaluated in the 'isPresent()'). In other words, the query also returns all
+        //objects that match '!(optionalPC.isPresent())'.
+        checkQuery("!(optionalPC.optionalPC.isPresent())", 
+                oidReferencedPC1, oidReferencedPC2, oidEmpty, oidNull);
+
+        //A query where 'optionalPC!=null' and 'optionalPC.optionalPC==null; 
+        //can be done as follows:
+        checkQuery("optionalPC.isPresent() && !(optionalPC.optionalPC.isPresent())", 
+                oidReferencedPC1);
+
+        checkQuery("optionalPC.optionalPC.id == " + REFERENCED_PC2_ID, oidPC);
+        checkQuery("optionalPC.get().optionalPC.get().id == " + REFERENCED_PC2_ID, oidPC);
+
+        //test with && operator
+        checkQuery("!(optionalPC.isPresent() && optionalPC.id == " + REFERENCED_PC1_ID + ")", 
+                oidReferencedPC1, oidReferencedPC2, oidEmpty, oidNull);
+    }
+
+    private void checkQuery(String filter, Object ... resultOids) {
+        QueryElementHolder qeh = new QueryElementHolder(
+                /*UNIQUE*/      null,
+                /*RESULT*/      null, 
+                /*INTO*/        null, 
+                /*FROM*/        OptionalSample.class,
+                /*EXCLUDE*/     null,
+                /*WHERE*/       filter,
+                /*VARIABLES*/   null,
+                /*PARAMETERS*/  null,
+                /*IMPORTS*/     null,
+                /*GROUP BY*/    null,
+                /*ORDER BY*/    null,
+                /*FROM*/        null,
+                /*TO*/          null);
+
+        ArrayList<Object> expectedResults = new ArrayList<>();
+        Transaction tx = pm.currentTransaction();
+        try {
+            tx.begin();
+            for (int i = 0; i < resultOids.length; i++) {
+                expectedResults.add(pm.getObjectById(resultOids[i]));
+            }
+        } finally {
+            if (tx.isActive()) {
+                tx.rollback();
+            }
+        }
+
+        executeAPIQuery(ASSERTION_FAILED, qeh, expectedResults);
+        executeSingleStringQuery(ASSERTION_FAILED, qeh, expectedResults);
+    }
+
+    /**
+     * This methods tests Optional fields and parameters.
+     */
+    public void testParameterOptional() {
+        OptionalSample osReferencedPC1 = getOptionalSampleById(oidReferencedPC1);
+        String paramDecl = "java.util.Optional op";
+        checkQuery("this.optionalPC == op", paramDecl, 
+                new Object[]{Optional.of(osReferencedPC1)}, 
+                new Object[]{oidPC});
+        checkQuery("this.optionalDate == op", paramDecl,  
+                new Object[]{Optional.of(DATE)}, 
+                new Object[]{oidPC});
+        checkQuery("this.optionalInteger == op", paramDecl,  
+                new Object[]{Optional.of(INTEGER)}, 
+                new Object[]{oidPC});
+        checkQuery("this.optionalString == op", paramDecl,  
+                new Object[]{Optional.of(STRING)}, 
+                new Object[]{oidPC});
+    }
+
+    /**
+     * This methods tests Optional fields and parameters with auto
+     * de-referencing.
+     */
+    public void testParameterOptionalAutoDeref() {
+        OptionalSample osReferencedPC1 = getOptionalSampleById(oidReferencedPC1);
+        checkQuery("this.optionalPC == op", 
+                OptionalSample.class.getName() + " op", 
+                new Object[]{osReferencedPC1}, new Object[]{oidPC});
+        checkQuery("this.optionalDate == op", 
+                "java.util.Date op",  
+                new Object[]{DATE}, new Object[]{oidPC});
+        checkQuery("this.optionalInteger == op", 
+                "Integer op",  
+                new Object[]{INTEGER}, new Object[]{oidPC});
+        checkQuery("this.optionalString == op", 
+                "String op",  
+                new Object[]{STRING}, new Object[]{oidPC});
+    }
+
+    private OptionalSample getOptionalSampleById(Object id) {
+        Transaction tx = pm.currentTransaction();
+        try {
+            tx.begin();
+            return (OptionalSample) pm.getObjectById(id);
+        } finally {
+            if (tx.isActive()) {
+                tx.rollback();
+            }
+        }
+    }
+
+    /**
+     * This methods tests that empty Optional fields and parameters matches with
+     * Optional.empty().
+     */
+    public void testParameterOptionalWithEmptyFields() {
+        String paramDecl = "java.util.Optional op";
+        Object[] params = new Object[]{Optional.empty()}; 
+        checkQuery("this.optionalPC == op", paramDecl, params, 
+                new Object[]{oidEmpty, oidNull, oidReferencedPC2});
+        checkQuery("this.optionalDate == op", paramDecl, params, 
+                new Object[]{oidEmpty, oidNull, oidReferencedPC1, oidReferencedPC2});
+        checkQuery("this.optionalInteger == op", paramDecl, params, 
+                new Object[]{oidEmpty, oidNull, oidReferencedPC1, oidReferencedPC2});
+        checkQuery("this.optionalString == op", paramDecl, params, 
+                new Object[]{oidEmpty, oidNull, oidReferencedPC1, oidReferencedPC2});
+    }
+
+    /**
+     * This methods tests that Optional fields and parameters matches with 
+     * (Optional)null.
+     */
+    public void testParameterOptionalWithNull() {
+        String paramDecl = "java.util.Optional op";
+        Object[] params = new Object[]{null}; 
+        checkQuery("this.optionalPC == op", paramDecl, params, 
+                new Object[]{oidEmpty, oidNull, oidReferencedPC2});
+        checkQuery("this.optionalDate == op", paramDecl, params, 
+                new Object[]{oidEmpty, oidNull, oidReferencedPC1, oidReferencedPC2});
+        checkQuery("this.optionalInteger == op", paramDecl, params, 
+                new Object[]{oidEmpty, oidNull, oidReferencedPC1, oidReferencedPC2});
+        checkQuery("this.optionalString == op", paramDecl, params, 
+                new Object[]{oidEmpty, oidNull, oidReferencedPC1, oidReferencedPC2});
+    }
+
+    /**
+     * This methods tests that Optional fields and parameters matches with
+     * (Object)null.
+     */
+    public void testParameterOptionalNull() {
+        Object[] params = new Object[]{null}; 
+        checkQuery("this.optionalPC == op", OptionalSample.class.getName() + " op", params, 
+                new Object[]{oidEmpty, oidNull, oidReferencedPC2});
+        checkQuery("this.optionalDate == op", "java.util.Date op", params, 
+                new Object[]{oidEmpty, oidNull, oidReferencedPC1, oidReferencedPC2});
+        checkQuery("this.optionalInteger == op", "java.lang.Integer op", params, 
+                new Object[]{oidEmpty, oidNull, oidReferencedPC1, oidReferencedPC2});
+        checkQuery("this.optionalString == op", "java.lang.String op", params, 
+                new Object[]{oidEmpty, oidNull, oidReferencedPC1, oidReferencedPC2});
+
+    }
+
+    /**
+     * This methods tests that Optional fields can be accessed in subqueries.
+     */
+    public void testSubqueries() {
+        String queryStr1 = 
+                "SELECT FROM " + OptionalSample.class.getName() + " WHERE " + 
+                "(select max(a.id) from " + OptionalSample.class.getName() + " a " +
+                "where a.optionalPC.isPresent() ) == id";
+        Object[] expectedResult1 = new Object[]{oidReferencedPC1};
+        checkSingleStringQuery(queryStr1, expectedResult1);
+        
+        String queryStr2 = 
+                "SELECT FROM " + OptionalSample.class.getName() + " WHERE " + 
+                "(select max(a.id) from " + OptionalSample.class.getName() + " a " +
+                "where a.optionalPC.get() != null) == id";
+        Object[] expectedResult2 = new Object[]{oidReferencedPC1};
+        checkSingleStringQuery(queryStr2, expectedResult2);
+    }
+
+    /**
+     * This methods tests that Optional fields can be accessed in subqueries.
+     */
+    public void testOptionalAggregation() {
+        String clsName = OptionalSample.class.getName();
+        
+        String queryStr1 = "SELECT AVG(optionalInteger) FROM " + clsName;
+        Query<?> q1 = pm.newQuery(queryStr1);
+        executeJDOQuery(ASSERTION_FAILED, q1, queryStr1, false, null, (double)INTEGER, true);
+
+        String queryStr2 = "SELECT AVG(optionalInteger.get()) FROM " + clsName;
+        Query<?> q2 = pm.newQuery(queryStr2);
+        executeJDOQuery(ASSERTION_FAILED, q2, queryStr2, false, null, (double)INTEGER, true);
+
+        //return the object whose Integer is the same as the AVG of all
+        //objects that have the Integer present.
+        String queryStrSub1 = "SELECT FROM " + clsName + " WHERE " + 
+                "(select avg(a.optionalInteger) from " + clsName + " a " +
+                "where a.optionalInteger.isPresent() ) == optionalInteger";
+        Object[] expectedResult1 = new Object[]{oidPC};
+        checkSingleStringQuery(queryStrSub1, expectedResult1);
+
+        String queryStrSub2 = "SELECT FROM " + clsName + " WHERE " + 
+                "(select avg(a.optionalInteger.get()) from " + clsName + " a " +
+                "where a.optionalInteger.isPresent() ) == optionalInteger";
+        Object[] expectedResult2 = new Object[]{oidPC};
+        checkSingleStringQuery(queryStrSub2, expectedResult2);
+
+        //Find all where the average is the same as the integer value itself.
+        //This returns ALL objects!!!
+        String queryStrSub3 = "SELECT FROM " + clsName + " WHERE " + 
+                "(select avg(a.optionalInteger) from " + clsName + " a " +
+                " ) == optionalInteger";
+        Object[] expectedResult3 = new Object[]{oidPC};
+        checkSingleStringQuery(queryStrSub3, expectedResult3);
+    }
+
+    private void checkSingleStringQuery(String singleStringJDOQL, Object ... resultOids) {
+        ArrayList<Object> expectedResults = new ArrayList<>();
+        Transaction tx = pm.currentTransaction();
+        try {
+            tx.begin();
+            for (int i = 0; i < resultOids.length; i++) {
+                expectedResults.add(pm.getObjectById(resultOids[i]));
+            }
+        } finally {
+            if (tx.isActive())
+                tx.rollback();
+        }
+
+        Query<?> singleStringQuery = pm.newQuery(singleStringJDOQL);
+        executeJDOQuery(ASSERTION_FAILED, singleStringQuery, singleStringJDOQL, 
+                            false, null, expectedResults, true);
+    }
+
+    private void checkQuery(String filter, String paramDecl, Object[] params, Object[] result) {
+        QueryElementHolder qeh = new QueryElementHolder(
+                /*UNIQUE*/      null,
+                /*RESULT*/      null, 
+                /*INTO*/        null, 
+                /*FROM*/        OptionalSample.class,
+                /*EXCLUDE*/     null,
+                /*WHERE*/       filter,
+                /*VARIABLES*/   null,
+                /*PARAMETERS*/  paramDecl,
+                /*IMPORTS*/     null,
+                /*GROUP BY*/    null,
+                /*ORDER BY*/    null,
+                /*FROM*/        null,
+                /*TO*/          null);
+
+        ArrayList<Object> expectedResults = new ArrayList<>();
+        PersistenceManager pm  = getPM();
+        Transaction tx = pm.currentTransaction();
+        try {
+            tx.begin();
+            for (int i = 0; i < result.length; i++) {
+                Object o = result[i];
+                if (o instanceof String || o instanceof Date || o instanceof Integer) {
+                    expectedResults.add(o);
+                } else {
+                    expectedResults.add(pm.getObjectById(result[i]));
+                }
+            }
+        } finally {
+            if (tx.isActive()) {
+                tx.rollback();
+            }
+        }
+
+        executeAPIQuery(ASSERTION_FAILED, qeh, params, expectedResults);
+        executeSingleStringQuery(ASSERTION_FAILED, qeh, params, expectedResults);
+    }
+
+
+    /**
+     * Result class for queries on OptionalSample. 
+     */
+    public static class ResultInfo {
+        public long id;
+        public String optionalString;
+        public ResultInfo() {};
+        public ResultInfo(long id, String optionalString) {
+            this.id = id;
+            this.optionalString = optionalString;
+        }
+    }
+
+
+    /**
+     * Test Optional.orElse() in the SELECT clause of JDOQL queries.
+     */
+    @SuppressWarnings("unchecked")
+    public void testOrElseInSELECT() {
+        Transaction tx = pm.currentTransaction();
+        try {
+            tx.begin();
+            Query<?> q = pm.newQuery("SELECT id, optionalString.orElse('NotPresent') FROM " + 
+                    OptionalSample.class.getName());
+            q.setResultClass(ResultInfo.class);
+            Collection<ResultInfo> c = (Collection<ResultInfo>) q.execute();
+            if (c.size() != 5) {
+            	fail(ASSERTION_FAILED, "Wrong result count: " + c.size());
+            }
+            for (ResultInfo i: c) {
+                switch ((int)i.id) {
+                case PC_ID:
+                	if (!STRING.equals(i.optionalString)) {
+                		fail(ASSERTION_FAILED, "Wrong string value: " + i.optionalString);
+                	}
+                    break;
+                case PC_EMPTY_ID:
+                case PC_NULL_ID:
+                case REFERENCED_PC1_ID:
+                case REFERENCED_PC2_ID:
+                    assertEquals("NotPresent", i.optionalString);
+                    break;
+                default: fail(ASSERTION_FAILED, "Wrong object id: " + i.id);
+                }
+            }
+        } finally {
+            if (tx.isActive()) {
+                tx.rollback();
+            }
+        }
+    }
+
+
+    /**
+     * This test assert that null-references are converted to Optional.empty() when
+     * loaded from the database.
+     */
+    public void testPersistenceNotNull() {
+        OptionalSample osNotNull = getOptionalSampleById(oidNull);
+
+        Transaction tx = pm.currentTransaction();
+        try {
+            tx.begin();
+
+            if (osNotNull.getOptionalDate() == null) {
+            	fail(ASSERTION_FAILED, "Date field was 'null'");
+            }
+            if (osNotNull.getOptionalInteger() == null) {
+            	fail(ASSERTION_FAILED, "Integer field was 'null'");
+            }
+            if (osNotNull.getOptionalPC() == null) {
+            	fail(ASSERTION_FAILED, "optionalPC field was 'null'");
+            }
+            if (osNotNull.getOptionalString() == null) {
+            	fail(ASSERTION_FAILED, "String field was 'null'");
+            }
+
+            if (osNotNull.getOptionalDate().isPresent()) {
+            	fail(ASSERTION_FAILED, "Date field was present");
+            }
+            if (osNotNull.getOptionalInteger().isPresent()) {
+            	fail(ASSERTION_FAILED, "Integer field was present");
+            }
+            if (osNotNull.getOptionalPC().isPresent()) {
+            	fail(ASSERTION_FAILED, "optionalPC field was present");
+            }
+            if (osNotNull.getOptionalString().isPresent()) {
+            	fail(ASSERTION_FAILED, "String field was present");
+            }
+        } finally {
+            if (tx.isActive()) {
+                tx.rollback();
+            }
+        }
+    }
+
+
+    /**
+     * @see JDO_Test#localSetUp()
+     */
+    @Override
+    protected void localSetUp() {
+        addTearDownClass(OptionalSample.class);
+        insertOptionalSample(getPM());
+    }
+
+
+    private void insertOptionalSample(PersistenceManager pm) {
+        Transaction tx = pm.currentTransaction();
+        try {
+            tx.begin();
+
+            //Create two objects that are referenced by other objects, this allows 
+            //testing of navigation in queries.
+            //The referencedPC1 will be referenced by 'osPC',
+            //The referencedPC2 will be referenced by referencedPC1.
+            OptionalSample referencedPC1 = new OptionalSample(); 
+            referencedPC1.setId(REFERENCED_PC1_ID);
+            pm.makePersistent(referencedPC1);
+            oidReferencedPC1 = pm.getObjectId(referencedPC1);
+
+            OptionalSample referencedPC2 = new OptionalSample(); 
+            referencedPC2.setId(REFERENCED_PC2_ID);
+            pm.makePersistent(referencedPC2);
+            oidReferencedPC2 = pm.getObjectId(referencedPC2);
+
+            referencedPC1.setOptionalPC(Optional.of(referencedPC2));
+
+            OptionalSample osPC = new OptionalSample();
+            osPC.setId(PC_ID);
+            osPC.setOptionalPC(Optional.of(referencedPC1));
+            osPC.setOptionalDate(Optional.of(DATE));
+            osPC.setOptionalInteger(Optional.of(INTEGER));
+            osPC.setOptionalString(Optional.of(STRING));
+            pm.makePersistent(osPC);
+            oidPC = pm.getObjectId(osPC);
+
+            //use empty optionals
+            OptionalSample osEmpty = new OptionalSample();
+            osEmpty.setId(PC_EMPTY_ID);
+            osEmpty.setOptionalPC(Optional.empty());
+            osEmpty.setOptionalDate(Optional.empty());
+            osEmpty.setOptionalInteger(Optional.empty());
+            osEmpty.setOptionalString(Optional.empty());
+            pm.makePersistent(osEmpty);
+            oidEmpty = pm.getObjectId(osEmpty);
+
+            //use null for optional fields
+            OptionalSample osNull = new OptionalSample();
+            osNull.setId(PC_NULL_ID);
+            osNull.setOptionalPC(null);
+            osNull.setOptionalDate(null);
+            osNull.setOptionalInteger(null);
+            osNull.setOptionalString(null);
+            pm.makePersistent(osNull);
+            oidNull = pm.getObjectId(osNull);
+
+            tx.commit();
+        } finally {
+            if (tx.isActive()) {
+                tx.rollback();
+            }
+        }
+    }
+
+    @Override
+    protected void localTearDown() {
+        //set all references to null to allow deletion of (otherwise) referenced objects
+        Transaction tx = getPM().currentTransaction();
+        tx.begin();
+        Query<OptionalSample> q = pm.newQuery(OptionalSample.class);
+        for (OptionalSample os: q.executeList()) {
+            os.setOptionalPC(Optional.empty());
+        }
+        tx.commit();
+
+        super.localTearDown();
+    }
+
+}

Propchange: db/jdo/trunk/tck/src/java/org/apache/jdo/tck/query/jdoql/methods/SupportedOptionalMethods.java
------------------------------------------------------------------------------
    svn:executable = *