Author: ppoddar
Date: Wed Feb 11 16:57:26 2009
New Revision: 743396

URL: http://svn.apache.org/viewvc?rev=743396&view=rev
Log:
OPENJPA-900: Consolidate Query Hint related processing. As a byproduct, support 
new JPA 2.0 getHins() and getSupportedHints().

Added:
    
openjpa/trunk/openjpa-lib/src/main/java/org/apache/openjpa/lib/util/Reflectable.java
    
openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/conf/TestQueryHints.java
    
openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/HintHandler.java
Modified:
    
openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/conf/JDBCProductDerivation.java
    
openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/enhance/Reflection.java
    
openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/Filters.java
    
openjpa/trunk/openjpa-lib/src/main/java/org/apache/openjpa/lib/conf/AbstractProductDerivation.java
    
openjpa/trunk/openjpa-lib/src/main/java/org/apache/openjpa/lib/conf/ProductDerivation.java
    
openjpa/trunk/openjpa-lib/src/main/java/org/apache/openjpa/lib/conf/ProductDerivations.java
    
openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/FetchPlan.java
    
openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/QueryImpl.java
    
openjpa/trunk/openjpa-persistence/src/main/resources/org/apache/openjpa/persistence/localizer.properties
    
openjpa/trunk/openjpa-slice/src/main/java/org/apache/openjpa/slice/ProductDerivation.java

Modified: 
openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/conf/JDBCProductDerivation.java
URL: 
http://svn.apache.org/viewvc/openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/conf/JDBCProductDerivation.java?rev=743396&r1=743395&r2=743396&view=diff
==============================================================================
--- 
openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/conf/JDBCProductDerivation.java
 (original)
+++ 
openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/conf/JDBCProductDerivation.java
 Wed Feb 11 16:57:26 2009
@@ -18,11 +18,14 @@
  */
 package org.apache.openjpa.jdbc.conf;
 
+import java.util.Collections;
 import java.util.Map;
+import java.util.Set;
 
 import org.apache.openjpa.conf.BrokerFactoryValue;
 import org.apache.openjpa.conf.OpenJPAProductDerivation;
 import org.apache.openjpa.jdbc.kernel.JDBCBrokerFactory;
+import org.apache.openjpa.jdbc.sql.OracleDictionary;
 import org.apache.openjpa.lib.conf.AbstractProductDerivation;
 import org.apache.openjpa.lib.conf.ConfigurationProvider;
 
@@ -48,4 +51,9 @@
         }
         return false;
     }
+    
+    @Override
+    public Set<String> getSupportedQueryHints() {
+        return Collections.singleton(OracleDictionary.SELECT_HINT);
+    }
 }

Modified: 
openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/enhance/Reflection.java
URL: 
http://svn.apache.org/viewvc/openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/enhance/Reflection.java?rev=743396&r1=743395&r2=743396&view=diff
==============================================================================
--- 
openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/enhance/Reflection.java
 (original)
+++ 
openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/enhance/Reflection.java
 Wed Feb 11 16:57:26 2009
@@ -21,13 +21,18 @@
 import java.lang.reflect.AccessibleObject;
 import java.lang.reflect.Field;
 import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Member;
 import java.lang.reflect.Method;
 import java.lang.reflect.Modifier;
 import java.security.AccessController;
+import java.util.Collections;
+import java.util.Set;
+import java.util.TreeSet;
 
 import org.apache.commons.lang.StringUtils;
 import org.apache.openjpa.lib.util.J2DoPrivHelper;
 import org.apache.openjpa.lib.util.Localizer;
+import org.apache.openjpa.lib.util.Reflectable;
 import org.apache.openjpa.util.GeneralException; 
 import org.apache.openjpa.util.UserException; 
 
@@ -754,4 +759,166 @@
     public static void set(Object target, Method setter, short value) {
         set(target, setter, new Short(value));
     }
+    
+    /**
+     * Gets all bean-style property names of the given Class or its superclass.
+     * A bean-style property 'abc' exists in Class C iff C has declared 
+     * following pair of methods:
+     *   public void setAbc(Y y) or public C setAbc(Y y)
+     *   public Y getAbc();
+     *   
+     * If a getter property is annotated with {...@link Reflectable}, then
+     * it is ignored.
+     *   
+     */
+    public static Set<String> getBeanStylePropertyNames(Class c) {
+        if (c == null)
+            return Collections.EMPTY_SET;
+        Method[] methods = c.getMethods();
+        if (methods == null || methods.length < 2)
+            return Collections.EMPTY_SET;
+        Set<String> result = new TreeSet<String>();
+        for (Method m : methods) {
+            if (m.getName().startsWith("get")) {
+                if (!canReflect(m))
+                    continue;
+                String prop = StringUtils.capitalize(m.getName()
+                    .substring("get".length()));
+                Class rtype = m.getReturnType();
+                try {
+                  Method setter = c.getMethod("set"+prop, new Class[]{rtype});
+                  if (setter.getReturnType() == void.class || 
+                      setter.getReturnType().isAssignableFrom(c))
+                  result.add(prop);
+                } catch (NoSuchMethodException e) {
+                    
+                }
+            }
+        }
+        return result;
+    }
+    
+    /**
+     * Gets all public field names of the given Class.
+     *   
+     */
+    public static Set<String> getPublicFieldNames(Class c) {
+        if (c == null)
+            return Collections.EMPTY_SET;
+        Field[] fields = c.getFields();
+        if (fields == null || fields.length == 0)
+            return Collections.EMPTY_SET;
+        Set<String> result = new TreeSet<String>();
+        for (Field f : fields) {
+            if (canReflect(f))
+                result.add(f.getName());
+        }
+        return result;
+    }
+    
+    /**
+     * Gets values of all field f the given class such that f exactly 
+     * match the given modifiers and are of given type (Object implies any 
type)
+     * unless f is annotated as {...@link Reflectable}. 
+     *   
+     */
+    public static <T> Set<T> getFieldValues(Class c, int mods, Class<T> t){
+        if (c == null)
+            return Collections.EMPTY_SET;
+        Field[] fields = c.getFields();
+        if (fields == null || fields.length == 0)
+            return Collections.EMPTY_SET;
+        Set<T> result = new TreeSet<T>();
+        for (Field f : fields) {
+            if (mods == f.getModifiers() 
+            && (t == Object.class || t.isAssignableFrom(f.getType()))
+            && canReflect(f)) {
+                try {
+                    result.add((T)f.get(null));
+                } catch (IllegalArgumentException e) {
+                } catch (IllegalAccessException e) {
+                }
+            }
+        }
+        return result;
+    }
+
+    /**
+     * Affirms if the given member is selected for reflection. The decision is
+     * based on the following truth table on both the class-level and 
+     * member-level annotation (null annotation represents MAYBE) 
+     * 
+     * Class   member  
+     * MAYBE   MAYBE   YES
+     * MAYBE   YES     YES
+     * MAYBE   NO      NO
+     *
+     * YES     MAYBE   YES
+     * YES     YES     YES
+     * YES     NO      NO
+     *
+     * NO      YES     YES
+     * NO      MAYBE   NO
+     * NO      NO      NO 
+     * 
+    */   
+    static boolean canReflect(Reflectable cls, Reflectable member) {
+        if (cls == null || cls.value()) {
+            return member == null || member.value() == true;
+        } else {
+            return member != null && member.value() == true;
+        }
+    }
+    
+    /**
+     * Affirms if the original declaration the given field is annotated
+     * for reflection. 
+     */
+    static boolean canReflect(Field field) {
+        Class cls = field.getDeclaringClass();
+        return canReflect((Reflectable)cls.getAnnotation(Reflectable.class), 
+            field.getAnnotation(Reflectable.class));
+    }
+    
+    /**
+     * Affirms if the original declaration the given method is annotated
+     * for reflection. 
+     */
+    static boolean canReflect(Method method) {
+        Class cls = getDeclaringClass(method);
+        if (cls != method.getDeclaringClass())
+            method = getDeclaringMethod(cls, method);
+        return canReflect((Reflectable)cls.getAnnotation(Reflectable.class), 
+            method.getAnnotation(Reflectable.class));
+    }
+    
+    /**
+     * Gets the declaring class of the given method signature but also checks
+     * if the method is declared in an interface. If yes, then returns the
+     * interface. 
+     */
+    public static Class getDeclaringClass(Method m) {
+        if (m == null)
+            return null;
+        Class cls = m.getDeclaringClass();
+        Class[] intfs =  cls.getInterfaces();
+        for (Class intf : intfs) {
+            if (getDeclaringMethod(intf, m) != null)
+                cls = intf;
+        }
+        return cls;
+    }
+    
+    /**
+     * Gets the method in the given class that has the same signature of the
+     * given method, if exists. Otherwise, null.
+     */
+    public static Method getDeclaringMethod(Class c, Method m) {
+        try {
+            Method m0 = c.getMethod(m.getName(), m.getParameterTypes());
+            return m0;
+        } catch (Exception e) {
+            return null;
+        }
+    }    
 }

Modified: 
openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/Filters.java
URL: 
http://svn.apache.org/viewvc/openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/Filters.java?rev=743396&r1=743395&r2=743396&view=diff
==============================================================================
--- 
openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/Filters.java
 (original)
+++ 
openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/Filters.java
 Wed Feb 11 16:57:26 2009
@@ -247,12 +247,23 @@
         if (type.isAssignableFrom(o.getClass()))
             return o;
 
-        // the only non-numeric conversions we do are to string, or from
+        // the non-numeric conversions we do are to string, or from
         // string/char to number, or calendar/date
+        // String to Boolean
+        // String to Integer
         boolean num = o instanceof Number;
         if (!num) {
             if (type == String.class)
                 return o.toString();
+            else if (type == Boolean.class && o instanceof String) 
+                return Boolean.valueOf(o.toString());
+            else if (type == Integer.class && o instanceof String)
+                try {
+                    return new Integer(o.toString());
+                } catch (NumberFormatException e) {
+                    throw new ClassCastException(_loc.get("cant-convert", o,
+                        o.getClass(), type).getMessage());
+                }
             else if (type == Character.class) {
                 String str = o.toString();
                 if (str != null && str.length() == 1)

Modified: 
openjpa/trunk/openjpa-lib/src/main/java/org/apache/openjpa/lib/conf/AbstractProductDerivation.java
URL: 
http://svn.apache.org/viewvc/openjpa/trunk/openjpa-lib/src/main/java/org/apache/openjpa/lib/conf/AbstractProductDerivation.java?rev=743396&r1=743395&r2=743396&view=diff
==============================================================================
--- 
openjpa/trunk/openjpa-lib/src/main/java/org/apache/openjpa/lib/conf/AbstractProductDerivation.java
 (original)
+++ 
openjpa/trunk/openjpa-lib/src/main/java/org/apache/openjpa/lib/conf/AbstractProductDerivation.java
 Wed Feb 11 16:57:26 2009
@@ -19,7 +19,9 @@
 package org.apache.openjpa.lib.conf;
 
 import java.io.File;
+import java.util.Collections;
 import java.util.List;
+import java.util.Set;
 
 /**
  * Abstract no-op product derivation for easy extension.
@@ -85,4 +87,8 @@
     
     public void beforeConfigurationClose(Configuration conf) {
     }
+        
+    public Set<String> getSupportedQueryHints() {
+        return Collections.EMPTY_SET;
+    }
 }

Modified: 
openjpa/trunk/openjpa-lib/src/main/java/org/apache/openjpa/lib/conf/ProductDerivation.java
URL: 
http://svn.apache.org/viewvc/openjpa/trunk/openjpa-lib/src/main/java/org/apache/openjpa/lib/conf/ProductDerivation.java?rev=743396&r1=743395&r2=743396&view=diff
==============================================================================
--- 
openjpa/trunk/openjpa-lib/src/main/java/org/apache/openjpa/lib/conf/ProductDerivation.java
 (original)
+++ 
openjpa/trunk/openjpa-lib/src/main/java/org/apache/openjpa/lib/conf/ProductDerivation.java
 Wed Feb 11 16:57:26 2009
@@ -21,6 +21,7 @@
 import java.io.File;
 import java.io.IOException;
 import java.util.List;
+import java.util.Set;
 
 /**
  * Hooks for deriving products with additional functionality.
@@ -157,4 +158,13 @@
      * @since 0.9.7
      */
     public void beforeConfigurationClose(Configuration conf);
+    
+       
+    /**
+     * Return set of Query hint keys recognized by this receiver. 
+     * 
+     * @since 2.0.0
+     */
+    public Set<String> getSupportedQueryHints();
+
 }

Modified: 
openjpa/trunk/openjpa-lib/src/main/java/org/apache/openjpa/lib/conf/ProductDerivations.java
URL: 
http://svn.apache.org/viewvc/openjpa/trunk/openjpa-lib/src/main/java/org/apache/openjpa/lib/conf/ProductDerivations.java?rev=743396&r1=743395&r2=743396&view=diff
==============================================================================
--- 
openjpa/trunk/openjpa-lib/src/main/java/org/apache/openjpa/lib/conf/ProductDerivations.java
 (original)
+++ 
openjpa/trunk/openjpa-lib/src/main/java/org/apache/openjpa/lib/conf/ProductDerivations.java
 Wed Feb 11 16:57:26 2009
@@ -29,6 +29,8 @@
 import java.util.List;
 import java.util.Map;
 import java.util.MissingResourceException;
+import java.util.Set;
+import java.util.TreeSet;
 
 import org.apache.commons.lang.StringUtils;
 import org.apache.openjpa.lib.util.J2DoPrivHelper;
@@ -434,6 +436,19 @@
                 collection.add(fqLoc);
         }
     }
+    
+    
+    public static Set<String> getSupportedQueryHints() {
+        Set<String> result = new TreeSet<String>();
+        // most specific to least
+        for (int i = _derivations.length - 1; i >= 0; i--) {
+            Set<String> members = _derivations[i].getSupportedQueryHints();
+            if (members != null || !members.isEmpty())
+                result.addAll(members);
+        }
+        return result;
+    }
+
 
     /**
      * Compare {...@link ProductDerivation}s.

Added: 
openjpa/trunk/openjpa-lib/src/main/java/org/apache/openjpa/lib/util/Reflectable.java
URL: 
http://svn.apache.org/viewvc/openjpa/trunk/openjpa-lib/src/main/java/org/apache/openjpa/lib/util/Reflectable.java?rev=743396&view=auto
==============================================================================
--- 
openjpa/trunk/openjpa-lib/src/main/java/org/apache/openjpa/lib/util/Reflectable.java
 (added)
+++ 
openjpa/trunk/openjpa-lib/src/main/java/org/apache/openjpa/lib/util/Reflectable.java
 Wed Feb 11 16:57:26 2009
@@ -0,0 +1,25 @@
+package org.apache.openjpa.lib.util;
+
+import static java.lang.annotation.ElementType.FIELD;
+import static java.lang.annotation.ElementType.METHOD;
+import static java.lang.annotation.ElementType.TYPE;
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+
+/**
+ * Annotates a getter method or field so {...@link Reflection reflection 
+ * utility} to control whether the annotated member is recorded during 
scanning 
+ * for bean-style method or field.
+ * 
+ * @author Pinaki Poddar
+ * 
+ * @since 2.0.0
+ *
+ */
+...@target({TYPE, METHOD, FIELD})
+...@retention(RUNTIME)
+public @interface Reflectable {
+    boolean value() default true;
+}

Added: 
openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/conf/TestQueryHints.java
URL: 
http://svn.apache.org/viewvc/openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/conf/TestQueryHints.java?rev=743396&view=auto
==============================================================================
--- 
openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/conf/TestQueryHints.java
 (added)
+++ 
openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/conf/TestQueryHints.java
 Wed Feb 11 16:57:26 2009
@@ -0,0 +1,167 @@
+/*
+ * 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.openjpa.conf;
+
+import javax.persistence.EntityManager;
+import javax.persistence.Query;
+
+import org.apache.openjpa.jdbc.sql.OracleDictionary;
+import org.apache.openjpa.kernel.QueryHints;
+import org.apache.openjpa.persistence.HintHandler;
+import org.apache.openjpa.persistence.OpenJPAPersistence;
+import org.apache.openjpa.persistence.OpenJPAQuery;
+import org.apache.openjpa.persistence.test.SingleEMFTestCase;
+
+/**
+ * Tests JPA 2.0 API methods {...@link Query#getSupportedHints()} and 
+ * {...@link Query#getHints()}. 
+ * 
+ * @author Pinaki Poddar
+ *
+ */
+public class TestQueryHints extends SingleEMFTestCase {
+    EntityManager em;
+    OpenJPAQuery query;
+    
+    public void setUp() {
+       super.setUp((Object[])null);
+       em = emf.createEntityManager();
+       String sql = "select * from Person";
+       query = OpenJPAPersistence.cast(em.createNativeQuery(sql));
+    }
+    
+    public void testSupportedHintsContainProductDerivationHints() {
+        assertSupportedHint(OracleDictionary.SELECT_HINT, true);
+    }
+    
+    public void testSupportedHintsContainFetchPlanHints() {
+        assertSupportedHint("openjpa.FetchPlan.LockTimeout", true);
+    }
+
+    public void testSupportedHintsIgnoresSomeFetchPlanBeanStyleProperty() {
+        assertSupportedHint("openjpa.FetchPlan.QueryResultCache", false);
+    }
+    
+    public void testSupportedHintsContainQueryProperty() {
+        assertSupportedHint("openjpa.Subclasses", true);
+    }
+    
+    public void testSupportedHintsContainKernelQueryHints() {
+        assertSupportedHint(QueryHints.HINT_IGNORE_PREPARED_QUERY, true);
+    }
+    
+    public void testSupportedHintsContainJPAQueryHints() {
+        assertSupportedHint("javax.persistence.query.timeout", true);
+    }
+    
+    public void testUnrecognizedKeyIsIgnored() {
+        String unrecognizedKey = "acme.org.hint.SomeThingUnknown";
+        query.setHint(unrecognizedKey, "xyz");
+        assertFalse(query.getHints().containsKey(unrecognizedKey));
+        
assertNull(query.getFetchPlan().getDelegate().getHint(unrecognizedKey));
+     }
+    
+    public void testRecognizedKeyIsNotRecordedButAvailable() {
+        String recognizedKey = "openjpa.some.derivation.hint";
+        query.setHint(recognizedKey, "abc");
+        assertFalse(query.getHints().containsKey(recognizedKey));
+        assertEquals("abc", 
query.getFetchPlan().getDelegate().getHint(recognizedKey));
+    }
+
+    public void testSupportedKeyIsRecordedAndAvailable() {
+        String supportedKey = "openjpa.FetchPlan.FetchBatchSize";
+        query.setHint(supportedKey, 42);
+        assertTrue(query.getHints().containsKey(supportedKey));
+        assertEquals(42, query.getFetchPlan().getFetchBatchSize());
+    }
+    
+    public void testSupportedKeyWrongValue() {
+        String supportedKey = "openjpa.FetchPlan.FetchBatchSize";
+        short goodValue = (short)42;
+        float badValue = 57.9f;
+        query.setHint(supportedKey, goodValue);
+        assertTrue(query.getHints().containsKey(supportedKey));
+        assertEquals(goodValue, query.getFetchPlan().getFetchBatchSize());
+        
+        try {
+            query.setHint(supportedKey, badValue);
+            fail("Expected to fail to set " + supportedKey + " hint to " + 
badValue);
+        } catch (IllegalArgumentException e) {
+            // good
+        }
+    }
+    
+    public void testSupportedKeyIntegerValueConversion() {
+        String supportedKey = "openjpa.hint.OptimizeResultCount";
+        String goodValue = "57";
+        int badValue = -3;
+        query.setHint(supportedKey, goodValue);
+        assertTrue(query.getHints().containsKey(supportedKey));
+        assertEquals(57, 
query.getFetchPlan().getDelegate().getHint(supportedKey));
+        
+        try {
+            query.setHint(supportedKey, badValue);
+            fail("Expected to fail to set " + supportedKey + " hint to " + 
badValue);
+        } catch (IllegalArgumentException e) {
+            // good
+        }
+    }
+
+    public void testSupportedKeyBooleanValueConversion() {
+        String supportedKey = QueryHints.HINT_IGNORE_PREPARED_QUERY;
+        String goodValue = "true";
+        query.setHint(supportedKey, goodValue);
+        assertTrue(query.getHints().containsKey(supportedKey));
+        assertEquals(true, 
query.getFetchPlan().getDelegate().getHint(supportedKey));
+        
+        goodValue = "false";
+        query.setHint(supportedKey, goodValue);
+        assertTrue(query.getHints().containsKey(supportedKey));
+        assertEquals(false, 
query.getFetchPlan().getDelegate().getHint(supportedKey));
+    }
+    
+    public void testJPAHintSetsFetchPlan() {
+        String jpaKey = "javax.persistence.query.timeout";
+        query.setHint(jpaKey, 5671);
+        assertEquals(5671, query.getFetchPlan().getLockTimeout());
+    }
+    
+    public void testParts() {
+        HintHandler.HintKeyComparator test = new 
HintHandler.HintKeyComparator();
+        assertEquals(0, test.countDots("a"));
+        assertEquals(1, test.countDots("a.b"));
+        assertEquals(2, test.countDots("a.b.c"));
+        assertEquals(3, test.countDots("a.b.c.d"));
+        assertEquals(4, test.countDots("a.b.c.d."));
+        assertEquals(1, test.countDots("."));
+        assertEquals(0, test.countDots(""));
+        assertEquals(0, test.countDots(null));
+    }
+    
+    void assertSupportedHint(String hint, boolean contains) {
+        if (contains)
+            assertTrue("Expeceted suppoerted hint [" + hint + "]",
+                query.getSupportedHints().contains(hint));
+        else
+            assertFalse("Unexpeceted suppoerted hint [" + hint + "]",
+                query.getSupportedHints().contains(hint));
+    }
+    
+    
+}

Modified: 
openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/FetchPlan.java
URL: 
http://svn.apache.org/viewvc/openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/FetchPlan.java?rev=743396&r1=743395&r2=743396&view=diff
==============================================================================
--- 
openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/FetchPlan.java
 (original)
+++ 
openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/FetchPlan.java
 Wed Feb 11 16:57:26 2009
@@ -22,6 +22,7 @@
 import javax.persistence.LockModeType;
 
 import org.apache.openjpa.kernel.FetchConfiguration;
+import org.apache.openjpa.lib.util.Reflectable;
 import org.apache.openjpa.meta.FetchGroup;
 
 /**
@@ -88,6 +89,7 @@
      *
      * @since 1.0.0
      */
+    @Reflectable(false)
     public boolean getQueryResultCacheEnabled();
 
     /**
@@ -102,6 +104,7 @@
     /**
      * @deprecated use {...@link #getQueryResultCacheEnabled()} instead.
      */
+    @Reflectable(false)
     public boolean getQueryResultCache();
 
     /**
@@ -293,5 +296,6 @@
      * @deprecated cast to {...@link FetchPlanImpl} instead. This
      * method pierces the published-API boundary, as does the SPI cast.
      */
+    @Reflectable(false)
     public org.apache.openjpa.kernel.FetchConfiguration getDelegate();
 }

Added: 
openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/HintHandler.java
URL: 
http://svn.apache.org/viewvc/openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/HintHandler.java?rev=743396&view=auto
==============================================================================
--- 
openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/HintHandler.java
 (added)
+++ 
openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/HintHandler.java
 Wed Feb 11 16:57:26 2009
@@ -0,0 +1,365 @@
+package org.apache.openjpa.persistence;
+
+import static org.apache.openjpa.kernel.QueryHints.HINT_IGNORE_PREPARED_QUERY;
+import static 
org.apache.openjpa.kernel.QueryHints.HINT_INVALIDATE_PREPARED_QUERY;
+import static org.apache.openjpa.kernel.QueryHints.HINT_RESULT_COUNT;
+
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.Map;
+import java.util.Set;
+import java.util.TreeMap;
+import java.util.TreeSet;
+
+import org.apache.openjpa.conf.OpenJPAConfiguration;
+import org.apache.openjpa.enhance.Reflection;
+import org.apache.openjpa.kernel.FetchConfiguration;
+import org.apache.openjpa.kernel.Filters;
+import org.apache.openjpa.kernel.exps.AggregateListener;
+import org.apache.openjpa.kernel.exps.FilterListener;
+import org.apache.openjpa.lib.conf.ProductDerivation;
+import org.apache.openjpa.lib.conf.ProductDerivations;
+import org.apache.openjpa.lib.log.Log;
+import org.apache.openjpa.lib.util.Localizer;
+import org.apache.openjpa.lib.util.StringDistance;
+
+
+/**
+ * Manages query hint keys and handles their values on behalf of a owning
+ * {...@link QueryImpl}. Uses specific knowledge of hint keys declared in
+ * different parts of the system.
+ * 
+ * This receiver collects hint keys from different parts of the system. The
+ * keys are implicitly or explicitly declared by several different mechanics.
+ * This receiver sets the values on behalf of a owning {...@link QueryImpl}
+ * based on the its specific knowledge of these keys.
+ * 
+ * The hint keys from following sources are collected and handled: 
+ * 
+ * 1. {...@link org.apache.openjpa.kernel.QueryHints} interface declares hint 
keys
+ *    as public static final fields. These fields are collected by reflection.
+ *    The values are handled by invoking methods on the owning {...@link 
QueryImpl}
+ *    
+ * 2. Some hint keys are collected from bean-style property names of {...@link 
+ *    JDBCFetchPlan} by {...@link Reflection#getBeanStylePropertyNames(Class) 
+ *    reflection} and prefixed with <code>openjpa.FetchPlan</code>. 
+ *    Their values are used to set the corresponding property of {...@link 
+ *    FetchPlan} via {...@link #hintToSetter(FetchPlan, String, Object) 
reflection}
+ *      
+ * 3. Currently defined <code>javax.persistence.*</code> hint keys have  
+ *    a equivalent counterpart to one of these FetchPlan keys. 
+ *    The JPA keys are mapped to equivalent FetchPlan hint keys.
+ *    
+ * 4. Some keys directly invoke setters or add listeners to the owning 
+ *    {...@link QueryImpl}. These hint keys are statically declared in 
+ *    this receiver itself. 
+ *    
+ * 5. ProductDerivation may introduce their own query hint keys via {...@link 
+ *    ProductDerivation#getSupportedQueryHints()}. Their values are set in the 
+ *    {...@link FetchConfiguration#setHint(String, Object)}
+ *     
+ *  A hint key is classified into one of the following three categories:
+ *  
+ *  1. Supported: A key is known to this receiver as collected from different 
+ *     parts of the system. The value of a supported key is recorded and 
+ *     available via {...@link #getHints()} method. 
+ *  2. Recognized: A key is not known to this receiver but has a prefix which
+ *     is known to this receiver. The value of a recognized key is not 
recorded 
+ *     but its value is available via {...@link 
FetchConfiguration#getHint(String)}
+ *  3. Unrecognized: A key is neither supported nor recognized. The value of a 
+ *     unrecognized key is neither recorded nor set anywhere.
+ *  
+ *  If an incompatible value is supplied for a supported key, a non-fatal
+ *  {...@link ArgumentException} is raised.
+ *  
+ * @author Pinaki Poddar
+ *
+ * @since 2.0.0
+ * 
+ * @nojavadoc
+ */
+public class HintHandler {
+    private final QueryImpl owner;
+    private Map<String, Object> _hints;
+    private Set<String> _supportedKeys;
+    private Set<String> _supportedPrefixes;
+    
+    static final String PREFIX_JPA = "javax.persistence.";
+    static final String PREFIX_FETCHPLAN = "openjpa.FetchPlan.";
+    
+    // These keys are directly handled in {...@link QueryImpl} class.
+    // Declaring a public static final String variable in this class will 
+    // make it register as a supported hint key
+    // if you do not want that then annotate as {...@link Reflectable(false)}.
+    public static final String HINT_SUBCLASSES = "openjpa.Subclasses";
+    public static final String HINT_FILTER_LISTENER = "openjpa.FilterListener";
+    public static final String HINT_FILTER_LISTENERS = 
+        "openjpa.FilterListeners";
+    public static final String HINT_AGGREGATE_LISTENER = 
+        "openjpa.AggregateListener";
+    public static final String HINT_AGGREGATE_LISTENERS = 
+        "openjpa.AggregateListeners";
+    
+    // JPA Specification 2.0 keys are mapped to equivalent FetchPlan keys
+    public static Map<String,String> _jpaKeys = new TreeMap<String, String>();
+    static {
+        _jpaKeys.put(addPrefix(PREFIX_JPA, "query.timeout"), 
+            addPrefix(PREFIX_FETCHPLAN, "LockTimeout"));
+    }
+    
+    private static final String DOT = ".";
+    private static final String BLANK = "";
+    private static final Localizer _loc = Localizer.forPackage(
+        HintHandler.class);
+    
+    public HintHandler(QueryImpl impl) {
+        owner = impl;
+    }
+    
+    /**
+     * Gets all the recorded hint keys and their values.
+     */
+    @SuppressWarnings("unchecked")
+    public Map<String, Object> getHints() {
+        return _hints == null ? Collections.EMPTY_MAP 
+            : Collections.unmodifiableMap(_hints);
+    }
+    
+    /**
+     * Record a key-value pair only only if the given key is supported.
+     * 
+     * @return FALSE if the key is unrecognized. 
+     *         null (i.e. MAY BE) if the key is recognized, but not supported.
+     *         TRUE if the key is supported.
+     */
+    public Boolean record(String hint, Object value) {
+        if (hint == null)
+            return Boolean.FALSE;
+        if (isSupported(hint)) {
+            if (_hints == null)
+                _hints = new TreeMap<String, Object>();
+            _hints.put(hint, value);
+            return Boolean.TRUE;
+        }
+        
+        Log log = owner.getDelegate().getBroker().getConfiguration()
+            .getLog(OpenJPAConfiguration.LOG_RUNTIME);
+        String possible = StringDistance.getClosestLevenshteinDistance(hint, 
+            getSupportedHints());
+        if (log.isWarnEnabled()) {
+            log.warn(_loc.get("bad-query-hint", hint, possible));
+        }
+        return (isKnownHintPrefix(hint)) ? null : Boolean.FALSE;
+    }
+    
+    /**
+     * Gets all the supported hint keys. The set of supported hint keys is
+     * statically determined by collecting hint keys from the 
ProductDerivations
+     * and reflecting upon some of the known classes.
+     */
+    public Set<String> getSupportedHints() {
+        if (_supportedKeys == null) {
+            _supportedKeys = new TreeSet<String>(new HintKeyComparator());
+            _supportedPrefixes = new TreeSet<String>();
+            
+            _supportedKeys.addAll(Reflection.getFieldValues(
+                org.apache.openjpa.kernel.QueryHints.class, 
+                Modifier.PUBLIC | Modifier.STATIC | Modifier.FINAL, 
+                String.class));
+
+            _supportedKeys.addAll(addPrefix(PREFIX_FETCHPLAN, 
+                Reflection.getBeanStylePropertyNames(
+                    owner.getFetchPlan().getClass())));
+
+            _supportedKeys.addAll(_jpaKeys.keySet());
+
+            _supportedKeys.addAll(Reflection.getFieldValues(
+                HintHandler.class, 
+                Modifier.PUBLIC | Modifier.STATIC | Modifier.FINAL, 
+                String.class));
+
+            
_supportedKeys.addAll(ProductDerivations.getSupportedQueryHints());    
+            
+            for (String key : _supportedKeys) {
+                _supportedPrefixes.add(getPrefixOf(key));
+            }
+        }
+        return _supportedKeys;
+    }
+    
+    /**
+     * Add a hint key to the set of supported hint keys.
+     */
+    public void addHintKey(String key) {
+        getSupportedHints().add(key);
+        _supportedPrefixes.add(getPrefixOf(key));
+    }
+    
+    public Set<String> getKnownPrefixes() {
+        getSupportedHints();
+        return _supportedPrefixes;
+    }
+    
+    /**
+     * Affirms the given key matches one of the supported keys.
+     */
+    public boolean isSupported(String key) {
+        return getSupportedHints().contains(key);
+    }
+    
+    /**
+     * Affirms the given key has a prefix that matches with any of the 
+     * supported prefixes.
+     */
+    public boolean isSupportedPrefix(String key) {
+        return getKnownPrefixes().contains(getPrefixOf(key));
+    }
+    
+    static Set<String> addPrefix(String prefix, Set<String> original) {
+        Set<String> result = new TreeSet<String>();
+        String join = prefix.endsWith(DOT) ? BLANK : DOT;
+        for (String o : original)
+            result.add(prefix + join + o);
+        return result;
+    }
+    
+    static String addPrefix(String prefix, String original) {
+        String join = prefix.endsWith(DOT) ? BLANK : DOT;
+        return prefix + join + original;
+    }
+    
+    public static String removePrefix(String key, String prefix) {
+        if (prefix == null)
+            return key;
+        if (!prefix.endsWith(DOT))
+            prefix = prefix + DOT;
+        if (key != null && key.startsWith(prefix))
+            return key.substring(prefix.length());
+        return key;
+    }
+    
+    static String getPrefixOf(String key) {
+        int index = key == null ? -1 : key.indexOf(DOT);
+        return (index != -1) ? key.substring(0,index) : key;
+    }
+    
+    boolean isKnownHintPrefix(String key) {
+        String prefix = getPrefixOf(key);
+        return getKnownPrefixes().contains(prefix);
+    }
+    
+    public static boolean hasPrefix(String key, String prefix) {
+        if (key == null || prefix == null)
+            return false;
+        if (!prefix.endsWith(DOT))
+            prefix = prefix + DOT;
+        return key.startsWith(prefix);
+    }
+    
+    public void setHint(String key, Object value) {
+        Boolean record = record(key, value);
+        FetchConfiguration fetch = owner.getDelegate().getFetchConfiguration();
+        ClassLoader loader = owner.getDelegate().getBroker().getClassLoader();
+        if (record == Boolean.FALSE)
+            return;
+        if (record == null) {
+            fetch.setHint(key, value);
+            return;
+        }
+        try {
+            if (HINT_SUBCLASSES.equals(key)) {
+                if (value instanceof String)
+                    value = Boolean.valueOf((String) value);
+                owner.setSubclasses(((Boolean) value).booleanValue());
+            } else if (HINT_FILTER_LISTENER.equals(key))
+                owner.addFilterListener(Filters.hintToFilterListener(value, 
+                    loader));
+            else if (HINT_FILTER_LISTENERS.equals(key)) {
+                FilterListener[] arr = Filters.hintToFilterListeners(value, 
+                    loader);
+                for (int i = 0; i < arr.length; i++)
+                    owner.addFilterListener(arr[i]);
+            } else if (HINT_AGGREGATE_LISTENER.equals(key))
+                owner.addAggregateListener(Filters.hintToAggregateListener(
+                    value, loader));
+            else if (HINT_AGGREGATE_LISTENERS.equals(key)) {
+                AggregateListener[] arr = Filters.hintToAggregateListeners(
+                        value, loader);
+                for (int i = 0; i < arr.length; i++)
+                    owner.addAggregateListener(arr[i]);
+            } else if (isFetchPlanHint(key)) {
+                hintToSetter(owner.getFetchPlan(), 
+                    getFetchPlanProperty(key), value);
+            } else if (HINT_RESULT_COUNT.equals(key)) {
+                int v = (Integer)Filters.convert(value, Integer.class);
+                if (v < 0)
+                    throw new 
ArgumentException(_loc.get("bad-query-hint-value", 
+                        key, value), null,  null, false);
+                    fetch.setHint(key, v);
+            }  else if (HINT_INVALIDATE_PREPARED_QUERY.equals(key)) {
+                fetch.setHint(key, Filters.convert(value, Boolean.class));
+                owner.invalidatePreparedQuery();
+            } else if (HINT_IGNORE_PREPARED_QUERY.equals(key)) {
+                fetch.setHint(key, Filters.convert(value, Boolean.class));
+                owner.ignorePreparedQuery();
+            } else { // default 
+                fetch.setHint(key, value);
+            }
+            return;
+        } catch (IllegalArgumentException iae) {
+            throw new ArgumentException(_loc.get("bad-query-hint-value", 
+                key, value), null,  null, false);
+        } catch (ClassCastException ce) {
+            throw new ArgumentException(_loc.get("bad-query-hint-value", 
+                key, ce.getMessage()), null,  null, false);
+        } catch (Exception e) {
+            throw PersistenceExceptions.toPersistenceException(e);
+        }
+    }
+    
+    private boolean isFetchPlanHint(String key) {
+        return key.startsWith(PREFIX_FETCHPLAN) 
+           || (_jpaKeys.containsKey(key) && 
isFetchPlanHint(_jpaKeys.get(key)));
+    }
+    
+    private String getFetchPlanProperty(String key) {
+        if (key.startsWith(PREFIX_FETCHPLAN))
+            return removePrefix(key, PREFIX_FETCHPLAN);
+        else if (_jpaKeys.containsKey(key))
+            return getFetchPlanProperty(_jpaKeys.get(key));
+        else
+            return key;
+    }
+    
+    private void hintToSetter(FetchPlan fetchPlan, String k, Object value) {
+        if (fetchPlan == null || k == null)
+            return;
+
+        Method setter = Reflection.findSetter(fetchPlan.getClass(), k, true);
+        Class paramType = setter.getParameterTypes()[0];
+        if (Enum.class.isAssignableFrom(paramType) && value instanceof String)
+            value = Enum.valueOf(paramType, (String) value);
+
+        Filters.hintToSetter(fetchPlan, k, value);
+    }
+    
+    public static class HintKeyComparator implements Comparator<String> {
+        public int compare(String s1, String s2) {
+            if (getPrefixOf(s1).equals(getPrefixOf(s2))) {
+                int n1 = countDots(s1);
+                int n2 = countDots(s2);
+                return (n1 == n2) ? s1.compareTo(s2) : (n1 - n2);
+            } else
+                return s1.compareTo(s2);
+        }
+        
+        public int countDots(String s) {
+            if (s == null || s.length() == 0)
+                return 0;
+            int index = s.indexOf(DOT);
+            return (index == -1) ? 0 : countDots(s.substring(index+1)) + 1;
+        }
+    }
+}

Modified: 
openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/QueryImpl.java
URL: 
http://svn.apache.org/viewvc/openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/QueryImpl.java?rev=743396&r1=743395&r2=743396&view=diff
==============================================================================
--- 
openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/QueryImpl.java
 (original)
+++ 
openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/QueryImpl.java
 Wed Feb 11 16:57:26 2009
@@ -18,8 +18,9 @@
  */
 package org.apache.openjpa.persistence;
 
+import static org.apache.openjpa.kernel.QueryLanguages.LANG_PREPARED_SQL;
+
 import java.io.Serializable;
-import java.lang.reflect.Method;
 import java.sql.Time;
 import java.sql.Timestamp;
 import java.util.ArrayList;
@@ -41,15 +42,12 @@
 import javax.persistence.TemporalType;
 
 import org.apache.openjpa.conf.OpenJPAConfiguration;
-import org.apache.openjpa.enhance.Reflection;
 import org.apache.openjpa.kernel.Broker;
 import org.apache.openjpa.kernel.DelegatingQuery;
 import org.apache.openjpa.kernel.DelegatingResultList;
 import org.apache.openjpa.kernel.FetchConfiguration;
-import org.apache.openjpa.kernel.Filters;
 import org.apache.openjpa.kernel.PreparedQuery;
 import org.apache.openjpa.kernel.PreparedQueryCache;
-import org.apache.openjpa.kernel.QueryHints;
 import org.apache.openjpa.kernel.QueryLanguages;
 import org.apache.openjpa.kernel.QueryOperations;
 import org.apache.openjpa.kernel.QueryStatistics;
@@ -63,8 +61,6 @@
 import org.apache.openjpa.util.RuntimeExceptionTranslator;
 import org.apache.openjpa.util.UserException;
 
-import static org.apache.openjpa.kernel.QueryLanguages.LANG_PREPARED_SQL;
-
 /**
  * Implementation of {...@link Query} interface.
  * 
@@ -85,6 +81,8 @@
        private Map<String, Object> _named;
        private Map<Integer, Object> _positional;
        private String _id;
+       
+       private final HintHandler _hintHandler;
 
        /**
         * Constructor; supply factory exception translator and delegate.
@@ -97,6 +95,7 @@
                        org.apache.openjpa.kernel.Query query) {
                _em = em;
                _query = new DelegatingQuery(query, ret);
+               _hintHandler = new HintHandler(this);
        }
 
        /**
@@ -254,7 +253,7 @@
        }
        
        private Object execute() {
-               if (_query.getOperation() != QueryOperations.OP_SELECT)
+               if (!isNative() && _query.getOperation() != 
QueryOperations.OP_SELECT)
                        throw new 
InvalidStateException(_loc.get("not-select-query", _query
                                        .getQueryString()), null, null, false);
                
@@ -351,81 +350,8 @@
 
        public OpenJPAQuery setHint(String key, Object value) {
                _em.assertNotCloseInvoked();
-               if (key == null)
-                       return this;
-               if (!key.startsWith("openjpa.")) {
-                       _query.getFetchConfiguration().setHint(key, value);
-                       return this;
-               }
-               String k = key.substring("openjpa.".length());
-
-               try {
-                       if ("Subclasses".equals(k)) {
-                               if (value instanceof String)
-                                       value = Boolean.valueOf((String) value);
-                               setSubclasses(((Boolean) value).booleanValue());
-                       } else if ("FilterListener".equals(k))
-                               
addFilterListener(Filters.hintToFilterListener(value, _query
-                                               .getBroker().getClassLoader()));
-                       else if ("FilterListeners".equals(k)) {
-                               FilterListener[] arr = 
Filters.hintToFilterListeners(value,
-                                               
_query.getBroker().getClassLoader());
-                               for (int i = 0; i < arr.length; i++)
-                                       addFilterListener(arr[i]);
-                       } else if ("AggregateListener".equals(k))
-                               
addAggregateListener(Filters.hintToAggregateListener(value,
-                                               
_query.getBroker().getClassLoader()));
-                       else if ("FilterListeners".equals(k)) {
-                               AggregateListener[] arr = 
Filters.hintToAggregateListeners(
-                                               value, 
_query.getBroker().getClassLoader());
-                               for (int i = 0; i < arr.length; i++)
-                                       addAggregateListener(arr[i]);
-                       } else if (k.startsWith("FetchPlan.")) {
-                               k = k.substring("FetchPlan.".length());
-                               hintToSetter(getFetchPlan(), k, value);
-                       } else if (k.startsWith("hint.")) {
-                               if ("hint.OptimizeResultCount".equals(k)) {
-                                       if (value instanceof String) {
-                                               try {
-                                                       value = new 
Integer((String) value);
-                                               } catch (NumberFormatException 
nfe) {
-                                               }
-                                       }
-                                       if (!(value instanceof Number)
-                                                       || ((Number) 
value).intValue() < 0)
-                                               throw new 
ArgumentException(_loc.get(
-                                                               
"bad-query-hint-value", key, value), null,
-                                                               null, false);
-                    _query.getFetchConfiguration().setHint(key, value);
-                               }  else if 
(QueryHints.HINT_INVALIDATE_PREPARED_QUERY.equals
-                    (key)) {
-                    _query.getFetchConfiguration().setHint(key, 
(Boolean)value);
-                    invalidatePreparedQuery();
-                } else if (QueryHints.HINT_IGNORE_PREPARED_QUERY.equals(key)) {
-                    _query.getFetchConfiguration().setHint(key, 
(Boolean)value);
-                    ignorePreparedQuery();
-                } else {
-                    _query.getFetchConfiguration().setHint(key, value);
-                }
-            } else
-                               throw new 
ArgumentException(_loc.get("bad-query-hint", key),
-                                               null, null, false);
-                       return this;
-               } catch (Exception e) {
-                       throw PersistenceExceptions.toPersistenceException(e);
-               }
-       }
-
-       private void hintToSetter(FetchPlan fetchPlan, String k, Object value) {
-               if (fetchPlan == null || k == null)
-                       return;
-
-               Method setter = Reflection.findSetter(fetchPlan.getClass(), k, 
true);
-               Class paramType = setter.getParameterTypes()[0];
-               if (Enum.class.isAssignableFrom(paramType) && value instanceof 
String)
-                       value = Enum.valueOf(paramType, (String) value);
-
-               Filters.hintToSetter(fetchPlan, k, value);
+               _hintHandler.setHint(key, value);
+               return this;
        }
 
        public OpenJPAQuery setParameter(int position, Calendar value,
@@ -613,7 +539,7 @@
         */
     //TODO: JPA 2.0 Hints that are not set to FetchConfiguration 
     public Map<String, Object> getHints() {
-        return _query.getFetchConfiguration().getHints();
+        return _hintHandler.getHints();
     }
 
     public LockModeType getLockMode() {
@@ -622,8 +548,7 @@
     }
 
     public Set<String> getSupportedHints() {
-        throw new UnsupportedOperationException(
-            "JPA 2.0 - Method not yet implemented");
+        return _hintHandler.getSupportedHints();
     }
 
     public Query setLockMode(LockModeType lockMode) {
@@ -704,7 +629,7 @@
     /**
      * Remove this query from PreparedQueryCache. 
      */
-    private boolean invalidatePreparedQuery() {
+    boolean invalidatePreparedQuery() {
         PreparedQueryCache cache = _em.getPreparedQueryCache();
         if (cache == null)
             return false;
@@ -716,7 +641,7 @@
      * Ignores this query from PreparedQueryCache by recreating the original
      * query if it has been cached. 
      */
-    private void ignorePreparedQuery() {
+    void ignorePreparedQuery() {
         PreparedQuery cached = _em.getPreparedQuery(_id);
         if (cached == null)
             return;

Modified: 
openjpa/trunk/openjpa-persistence/src/main/resources/org/apache/openjpa/persistence/localizer.properties
URL: 
http://svn.apache.org/viewvc/openjpa/trunk/openjpa-persistence/src/main/resources/org/apache/openjpa/persistence/localizer.properties?rev=743396&r1=743395&r2=743396&view=diff
==============================================================================
--- 
openjpa/trunk/openjpa-persistence/src/main/resources/org/apache/openjpa/persistence/localizer.properties
 (original)
+++ 
openjpa/trunk/openjpa-persistence/src/main/resources/org/apache/openjpa/persistence/localizer.properties
 Wed Feb 11 16:57:26 2009
@@ -70,7 +70,7 @@
 mult-results: Query returned multiple results: "{0}".
 no-pos-named-params-mix: Cannot mix named and positional parameters in query \
        "{0}".
-bad-query-hint: "{0}" is not a recognized query hint.
+bad-query-hint: "{0}" is not a supported query hint. May be you meant "{1}"?
 bad-query-hint-value: Invalid value specified for query hint "{0}": {1}
 detached: Cannot perform this operation on detached entity "{0}".
 removed: Cannot perform this operation on removed entity "{0}".

Modified: 
openjpa/trunk/openjpa-slice/src/main/java/org/apache/openjpa/slice/ProductDerivation.java
URL: 
http://svn.apache.org/viewvc/openjpa/trunk/openjpa-slice/src/main/java/org/apache/openjpa/slice/ProductDerivation.java?rev=743396&r1=743395&r2=743396&view=diff
==============================================================================
--- 
openjpa/trunk/openjpa-slice/src/main/java/org/apache/openjpa/slice/ProductDerivation.java
 (original)
+++ 
openjpa/trunk/openjpa-slice/src/main/java/org/apache/openjpa/slice/ProductDerivation.java
 Wed Feb 11 16:57:26 2009
@@ -18,7 +18,9 @@
  */
 package org.apache.openjpa.slice;
 
+import java.util.Collections;
 import java.util.Map;
+import java.util.Set;
 
 import org.apache.openjpa.conf.OpenJPAProductDerivation;
 import org.apache.openjpa.lib.conf.AbstractProductDerivation;
@@ -98,4 +100,8 @@
                log.warn(_loc.get("forced-set-config", 
                        prefix+"."+v.getProperty(), forced));
     }
+    
+    public Set<String> getSupportedQueryHints() {
+        return Collections.singleton(HINT_TARGET);
+    }
 }


Reply via email to