/*
 * 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.spi;

import java.util.Collection;
import java.util.Properties;

import javax.jdo.Constants;
import javax.jdo.JDOHelper;
import javax.jdo.JDOUserException;
import javax.jdo.pc.PCPoint;
import javax.jdo.util.AbstractTest;
import javax.jdo.util.BatchTestRunner;

/** 
 * Tests class javax.jdo.spi.JDOImplHelper.
 * <p>
 * Missing: testNewInstance + testNewObjectIdInstance
 * Missing: tests for JDOImplHelper methods: copyKeyFieldsToObjectId and 
 * copyKeyFieldsFromObjectId.
 */
public class JDOImplHelperTest extends AbstractTest {
    
    /** */
    private RegisterClassEvent event;

    /** */
    public static void main(String args[]) {
        BatchTestRunner.run(JDOImplHelperTest.class);
    }

    /** */
    public void setUp() {
        // make sure PCClass is loaded before any tests are run
        PCPoint p = new PCPoint(1, new Integer(1));        
    }

    /** */
    public void testGetFieldNames() {
        JDOImplHelper implHelper = JDOImplHelper.getInstance();
        String[] fieldNames = implHelper.getFieldNames(PCPoint.class);
        if (fieldNames == null) {
            fail("array of field names is null");
        }
        if (fieldNames.length != 2) {
            fail("Unexpected length of fieldNames; expected 2, got " + 
                 fieldNames.length);
        }
        if (!fieldNames[0].equals("x")) {
            fail("Unexpected field; expected x, got " + fieldNames[0]);
        }
        if (!fieldNames[1].equals("y")) {
            fail("Unexpected field; expected y, got " + fieldNames[1]);
        }
    }

    /** */
    public void testGetFieldTypes() {
        JDOImplHelper implHelper = JDOImplHelper.getInstance();
        Class[] fieldTypes = implHelper.getFieldTypes(PCPoint.class);
        if (fieldTypes == null) {
            fail("array of field types is null");
        }
        if (fieldTypes.length != 2) {
            fail("Unexpected length of fieldTypes; expected 2, got " + 
                 fieldTypes.length);
        }
        if (fieldTypes[0] != int.class) {
            fail("Unexpected field type; expected int, got " + 
                 fieldTypes[0]);
        }
        if (fieldTypes[1] != Integer.class) {
            fail("Unexpected field type; expected Integer, got " + 
                 fieldTypes[1]);
        }
    }
    
    /** */
    public void testGetFieldFlags() {
        byte expected = (byte) (PersistenceCapable.CHECK_READ +
            PersistenceCapable.CHECK_WRITE + PersistenceCapable.SERIALIZABLE);
            
        JDOImplHelper implHelper = JDOImplHelper.getInstance();
        byte[] fieldFlags = implHelper.getFieldFlags(PCPoint.class);
        if (fieldFlags == null) {
            fail("array of field flags is null");
        }
        if (fieldFlags.length != 2) {
            fail("Unexpected length of fieldFlags; expected 2, got " + 
                 fieldFlags.length);
        }
        if (fieldFlags[0] != expected) {
            fail("Unexpected field flag; expected " + expected + 
                 ", got " + fieldFlags[0]);
        }
        if (fieldFlags[1] != expected) {
            fail("Unexpected field flag; expected " + expected + 
                 ", got " + fieldFlags[1]);
        }
    }

    /** */
    public void testGetPCSuperclass() {
        JDOImplHelper implHelper = JDOImplHelper.getInstance();
        Class pcSuper = 
            implHelper.getPersistenceCapableSuperclass(PCPoint.class);
        if (pcSuper != null) {
            fail("Wrong pc superclass of PCPoint; expected null, got " + 
                 pcSuper);
        }
    }

    /** */
    public void testNewInstance() {
        // TBD: test JDOImplHelper.newInstance(pcClass, sm) and
        // JDOImplHelper.newInstance(pcClass, sm, oid)  
    }

    /** */
    public void testNewObjectIdInstance() {
        // TBD: test JDOImplHelper.newObjectIdInstance(pcClass)
    }
    
    /** */
    public void testClassRegistration() {
        JDOImplHelper implHelper = JDOImplHelper.getInstance();

        Collection registeredClasses = implHelper.getRegisteredClasses();
        // test whether PCPoint is registered
        if (!registeredClasses.contains(PCPoint.class)) {
            fail("Missing registration of pc class PCPoint");
        }

        // Save registered meta data for restoring
        String[] fieldNames = implHelper.getFieldNames(PCPoint.class);
        Class[] fieldTypes = implHelper.getFieldTypes(PCPoint.class);
        byte[] fieldFlags = implHelper.getFieldFlags(PCPoint.class);
        Class pcSuperclass = implHelper.getPersistenceCapableSuperclass(PCPoint.class);
        
        // test unregisterClass with null parameter
        try {
            implHelper.unregisterClass(null);
            fail("Missing exception when calling unregisterClass(null)");
        }
        catch (NullPointerException ex) {
            // expected exception => OK
        }

        // test unregister PCPoint class
        implHelper.unregisterClass(PCPoint.class);
        registeredClasses = implHelper.getRegisteredClasses();
        if (registeredClasses.contains(PCPoint.class)) {
            fail("PCPoint still registered");
        }

        // register PCPoint again
        JDOImplHelper.registerClass(PCPoint.class, fieldNames, fieldTypes, 
                                    fieldFlags, pcSuperclass, new PCPoint());
    }

    /** */
    public void testClassListenerRegistration() {
        JDOImplHelper implHelper = JDOImplHelper.getInstance();

        // add listener and check event
        event = null;
        RegisterClassListener listener = new SimpleListener();
        implHelper.addRegisterClassListener(listener);
        JDOImplHelper.registerClass(JDOImplHelperTest.class, new String[0], 
                                    new Class[0], new byte[0], null, null);
        if (event == null) {
            fail("Missing event "); 
        }

        // remove listener and check event
        event = null;
        implHelper.removeRegisterClassListener(listener);
        JDOImplHelper.registerClass(JDOImplHelperTest.class, new String[0], 
                                    new Class[0], new byte[0], null, null);
        if (event != null) {
            fail("Unexpected event " + event);
        }
    }

    /**
     * Test that an unknown standard property causes JDOUserException.
     */
    public void testUnknownStandardProperty() {
        Properties p = new Properties();
        p.setProperty("javax.jdo.unknown.standard.property", "value");

        JDOUserException x = null;

        try {
            JDOImplHelper.assertOnlyKnownStandardProperties(p);
            fail("testUnknownStandardProperty should result in JDOUserException. "
                    + "No exception was thrown.");
        } catch (JDOUserException thrown) {
            if (verbose)
                println("Caught expected exception " + thrown);
            x = thrown;
        }
        assertNull("should have had no nested exceptions",
                x.getNestedExceptions());
    }

    /**
     * Test that unknown standard properties cause JDOUserException w/nested
     * exceptions.
     */
    public void testUnknownStandardProperties() {
        Properties p = new Properties();
        p.setProperty("javax.jdo.unknown.standard.property.1", "value");
        p.setProperty("javax.jdo.unknown.standard.property.2", "value");

        JDOUserException x = null;

        try {
            JDOImplHelper.assertOnlyKnownStandardProperties(p);
            fail("testUnknownStandardProperties should result in JDOUserException. "
                    + "No exception was thrown.");
        } catch (JDOUserException thrown) {
            if (verbose)
                println("Caught expected exception " + thrown);
            x = thrown;
        }

        Throwable[] nesteds = x.getNestedExceptions();

        assertNotNull(nesteds);
        assertEquals("should have been 2 nested exceptions", 2, nesteds.length);
        for (int i = 0; i < nesteds.length; i++) {
            Throwable t = nesteds[i];
            assertTrue("nested exception " + i
                    + " should have been JDOUserException",
                    t instanceof JDOUserException);
        }
    }

    /**
     * Test that unknown non-standard properties & well-formed listener
     * properties don't cause JDOUserException.
     */
    public void testUnknownNonStandardPropertiesAndListeners() {
        Properties p = new Properties();
        p.put("unknown.property", "value");
        p.put(new Object(), "value");
        p.put(Constants.PROPERTY_PREFIX_INSTANCE_LIFECYCLE_LISTENER
                + "unknown.listener", "value");
        JDOImplHelper.assertOnlyKnownStandardProperties(p);
    }

    /**
     * Test that all JDO standard properties don't cause JDOUserException.
     */
    public void testOnlyStandardProperties() {
        Properties props = new Properties();
        for (String p : JDOImplHelper.USER_CONFIGURABLE_STANDARD_PROPERTIES) {
            props.setProperty(p, p);
        }
        JDOImplHelper.assertOnlyKnownStandardProperties(props);
    }
    
    /**
     * Test that an known standard property in mixed case succeeds.
     */
    public void testKnownStandardPropertyThatDiffersInCaseOnly() {
        Properties p = new Properties();
        p.setProperty("JaVaX.jDo.oPtIoN.CoNNectionDRiVerNamE", "value");
        p.setProperty("jAvAx.JdO.lIsTeNeR.InstaNceLifeCycleLisTener.foo.Bar", "");
        JDOImplHelper.assertOnlyKnownStandardProperties(p);
    }

    /** */
    class SimpleListener implements RegisterClassListener {

        /** */
        public void registerClass(RegisterClassEvent event) {
            JDOImplHelperTest.this.event = event;
        }
        
    }
    
}

