/*
 * TestQuotedNumbersInFilters.java
 *
 * Created on October 18, 2006, 2:29 PM
 *
 * To change this template, choose Tools | Template Manager
 * and open the template in the editor.
 */
/*
 * 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.persistence.query;

import java.util.Collection;
import java.util.HashMap;
import java.util.Map;

import javax.persistence.Query;

import org.apache.openjpa.persistence.OpenJPAEntityManager;
import org.apache.openjpa.persistence.OpenJPAQuery;
import org.apache.openjpa.persistence.query.common.apps.RuntimeTest1;

public class TestQuotedNumbersInFilters2 extends BaseQueryTest {

    public TestQuotedNumbersInFilters2(String name) {
        super(name);
    }

    @Override
    public void setUp() {
        deleteAll(RuntimeTest1.class);
        OpenJPAEntityManager pm = getEM();
        startTx(pm);
        pm.persist(new RuntimeTest1("foo", 3));
        pm.persist(new RuntimeTest1("bar", 15));
        pm.persist(new RuntimeTest1("baz", -8));
        pm.persist(new RuntimeTest1("baz2", 45)); // 45 is '-'
        pm.persist(new RuntimeTest1("3", (int) '4'));
        endTx(pm);
        endEm(pm);

        // make sure everything is working as expected for the base case.
        assertEquals(1, helper("intField = -8"));
        assertEquals(1, helper("intField = 15"));
        assertEquals(1, helper("intField = 3"));
        assertEquals(0, helper("intField = 51")); // the int value of '3'
        assertEquals(0, helper("intField = 4"));
        assertEquals(1, helper("intField = 52")); // the int value of '4'
        assertEquals(1, helper("stringField = \'foo\'"));
        assertEquals(1, helper("stringField = \'bar\'"));
    }

    public void testUnquotedNumbersWithExtraPrecision() {
        assertEquals(1, helper("intField = 15"));
        assertEquals(1, helper("intField = -8"));
        assertEquals(1, helper("intField = 3"));
        assertEquals(1, helper("intField = 45"));

//    try {
//        // test without casting ... some DBs don't like this
////        assertEquals(1, helper("intField = 15.0"));
////        assertEquals(1, helper("intField = -8.0"));
//        assertEquals(1, helper("intField = 3.0"));
//        assertEquals(1, helper("intField = 45.0"));
//    } catch (Exception jdoe) {
//        bug(AbstractTestCase.Platform.HYPERSONIC, 414, jdoe,
//            "Some databases require explicit casts");
//    }
    }

    public void testSingleQuotedStrings() {
        assertEquals(1, helper("stringField = 'foo'"));
        assertEquals(1, helper("stringField = '3'"));
    }

    public void testDoubleQuotedStrings() {
        assertEquals(1, helper("stringField = \'foo\'"));
        assertEquals(1, helper("stringField = \'3\'"));
    }

    /**
     * Kodo 3.1 and prior treated single-quoted numbers as character literals,
     * to the degree that prepared statement setInt() calls were made.
     * Only the first digit of multiple-digit single-quoted numbers was used.
     * FIX ME: aokeke - commenting this --> applies to kodo 3.1 and prior
     */
    public void testKodo31SingleQuotedMultipleCharacterBehavior() {
        assertEquals(0, helper31("intField = '15'", true)); // looks like '1'
        assertEquals(0, helper31("intField = '52'", true)); // looks like '5'
        assertEquals(1, helper31("intField = '49'", true)); // looks like '4'
        assertEquals(1, helper31("intField = '-8'", true)); // looks like '-'

        assertEquals(0, helper31("intField = '15'", false));
        assertEquals(0, helper31("intField = '52'", false));
    }

    /**
     * Kodo 3.1 and prior did not match negative numbers of different types
     * in in-mem queries.
     */
    public void testKodo31UnquotedInMemBehavior() {
        assertEquals(1, helper31("intField = 3", false));
        assertEquals(1, helper31("intField = -8", false));
        assertEquals(1, helper31("intField = 15", false));
        assertEquals(1, helper31("intField = 45", false));
    }

    public void testKodo31UnquotedDatastoreBehavior() {
        assertEquals(1, helper31("intField = 3", false));
        assertEquals(1, helper31("intField = -8", false));
        assertEquals(1, helper31("intField = 15", false));
        assertEquals(1, helper31("intField = 45", false));
    }

    /**
     * Returns the # of matches to the query.
     */
    private long helper(String filter) {
        return helper(filter, false);
    }

    /**
     * Returns the # of matches to the query. Returns -1 if shouldFail
     * is true and the query raised an exception in both in-mem and datastore
     * queries.
     */
    private long helper(String filter, boolean shouldFail) {
        OpenJPAEntityManager pm = getEM();

        OpenJPAQuery q =
            pm.createQuery("SELECT r FROM RuntimeTest1 r WHERE r." + filter);

        long datastore = getResults(q, shouldFail);

        q.setCandidateCollection((Collection) q.getResultList());
        long inmem = getResults(q, shouldFail);

        if (datastore != inmem)
            fail("datastore query returned " + datastore + " values; " +
                "inmem query returned " + inmem);

        endEm(pm);
        return datastore;
    }

    /**
     * Returns the # of matches to the query. Performs the query in datastore
     * or memory as appropriate.
     */
    private long helper31(String filter, boolean datastore) {
        Map props = new HashMap();
        props.put("openjpa.Compatibility", "QuotedNumbersInQueries=true");
        OpenJPAEntityManager pm = getEmf(props).createEntityManager();

        try {
            OpenJPAQuery q = pm.createQuery(
                "SELECT r FROM RuntimeTest1 r WHERE r." + filter);

            if (!datastore)
                q.setCandidateCollection((Collection) q.getResultList());
            return getResults(q, false);
        }
        finally {
            endEm(pm);
        }
    }

    private long getResults(Query q, boolean shouldFail) {
        try {

            Integer result = q.getResultList().size();
            if (shouldFail) {
                fail("should have failed");
            }

            return ((Number) result).longValue();
        } catch (IllegalArgumentException e) {
            if (!shouldFail)
                throw e;
            return -1;
        }
    }
}
