package org.apache.torque.map;

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

import java.util.List;

import org.apache.torque.BaseDatabaseTestCase;
import org.apache.torque.Torque;
import org.apache.torque.TorqueException;

/**
 * Test code for Database Map functions
 *
 * @author <a href="mailto:greg.monroe@dukece.com">Greg Monroe</a>
 * @version $Id
 */
public class DatabaseMapTest extends BaseDatabaseTestCase
{

    public static final String TABLE_NAME1 = "NOT_USED_BEFORE_MAP_INIT";

    public static final String DATABASE_NAME = "databaseMap";

    public static final String INVALID_DATABASE_NAME = "erotskoob";

    public static final String TABLE_NAME2 = "SECOND_MAP_INIT_TABLE";

    public static final String PROTECTED_TABLE = "PROTECTED_COLUMNS";

    public static final String[] COLUMN_NAMES =
    {
            "id", "one", "two", "three"
    };

    /**
     * Test initializing the default database. <p> Assumptions: <ul> A table
     * called NotUsedBeforeMapInit table exists in the default schema
     * (bookstore)<br> This table has not been used by any other test prior to
     * this test.<br> </ul>
     */
    public void testDatabaseMapInitialize() throws Exception
    {
        // Get default schema DB
        DatabaseMap map = Torque.getDatabaseMap(DATABASE_NAME);
        TableMap tMap = map.getTable(TABLE_NAME1);
        assertTrue(
                "Did not expect to find NotUsedBeforeMapInit table before " +
                "initialize!",
                tMap == null);
        map.initialize();
        tMap = map.getTable(TABLE_NAME1);
        assertTrue(
                "Did not find table named NotUsedBeforeMapInit after " +
                "initialization!",
                tMap != null);
    }

    /**
     * Test that XML column order is preserved in TableMap objects.
     * <p>
     * Assumptions:
     * <ul>
     * The default database is bookstore<br>
     * The book table has columns ordered as in COLUMN_NAMES property<br>
     * </ul>
     *
     * @throws TorqueException
     */
    public void testColumnOrder() throws TorqueException
    {
        DatabaseMap map = Torque.getDatabaseMap(DATABASE_NAME);
        map.initialize();
        TableMap tMap = map.getTable(TABLE_NAME1);
        ColumnMap[] columns = tMap.getColumns();
        assertTrue("TestColumnOrder: Did not get enough columns from table!",
                columns.length >= COLUMN_NAMES.length);
        for (int i = 0; i < COLUMN_NAMES.length; i++)
        {
            String cName = columns[i].getColumnName();
            String expected = COLUMN_NAMES[i];
            // Handle torque.deprecated.uppercasePeer=true problems
            String upperCaseExpected = expected.toUpperCase();
            assertTrue("TableMap for " + TABLE_NAME1
                    + " did not preserve XML column order!  Expected "
                    + expected + " but found " +
                    cName, cName.equals(expected) ||
                        cName.equals(upperCaseExpected));
        }
    }

    /**
     * Test that XML table order is preserved in DatabaseMap objects.
     * <p>
     * Assumptions:
     * <ul>
     * The default database is bookstore<br>
     * TABLE_NAME1 is followed by TABLE_NAME2 in the array<br>
     * </ul>
     *
     * @throws TorqueException
     */
    public void testTableOrder() throws TorqueException
    {
        DatabaseMap map = Torque.getDatabaseMap(DATABASE_NAME);
        map.initialize();
        TableMap[] tables = map.getTables();
        assertEquals("XML Table order not preserved!\nDid not find table '"
                + TABLE_NAME1 + "' in DatabaseMap.getTables() array!",
            TABLE_NAME1,
            tables[0].getName());
        assertEquals("XML Table order not preserved!\nDid not find table '"
                + TABLE_NAME2 + "' after '" + TABLE_NAME1
                + " in DatabaseMap.getTables() array!",
            TABLE_NAME2,
            tables[1].getName());
    }

    /**
     * Check that the external schema tables are added to the database map.
     * <p>
     * @throws TorqueException
     */
    public void testExternalSchemaTables() throws TorqueException
    {
        DatabaseMap map = Torque.getDatabaseMap();
        map.initialize();

        TableMap table = map.getTable("ext");
        assertTrue("Did not find external schema table, 'ext'!",
                    table != null);

        table = map.getTable("extext");
        assertTrue("Did not find external schema table, 'extext'!",
                table != null);
    }

    /**
     * Test that various table properties get set correctly from the XML
     */
    public void testTableAttributes() throws TorqueException
    {
        DatabaseMap map = Torque.getDatabaseMap(DATABASE_NAME);
        map.initialize();
        TableMap table = map.getTable(TABLE_NAME1);

        validateAttribute("TableMap Javaname", "NotUsedBeforeMapInit", table
                .getJavaName());
        validateAttribute(
                "TableMap description",
                "Table used for database map initialisation checks",
                table.getDescription());
        assertTrue("TableMap OM Class was null!", table.getOMClass() != null);
        assertTrue("TableMap Peer Class was null!",
                table.getPeerClass() != null);

        table = map.getTable(TABLE_NAME2);
        validateAttribute(
                "TableMap Javaname",
                "SecondMapInit",
                table.getJavaName());
    }

    /**
     * Test that various column properties get set correctly from the XML
     */
    public void testColumnAttributes() throws TorqueException
    {
        DatabaseMap map = Torque.getDatabaseMap(DATABASE_NAME);
        map.initialize();
        TableMap table = map.getTable(TABLE_NAME1);

        ColumnMap column = table.getColumn("id");
        // Handle torque.deprecated.uppercasePeer=true problems
        if ( column == null ) {
            column = table.getColumn("ID");
        }

        validateAttribute("Column JavaName", "Id", column.getJavaName());
        validateAttribute("Column description", "id column", column
                .getDescription());

        assertTrue(
            "Column isPrimaryKey attribute returned false instead of true!",
            column.isPrimaryKey());
        assertTrue(
            "Column isAutoIncrement attribute returned false instead of true!",
            column.isAutoIncrement());
        assertTrue(
            "Column isNotNull attribute returned false instead of true!",
            column.isNotNull());
        assertTrue(
            "Column isUsePrimitive attribute returned false instead of true!",
            column.isUsePrimitive());
        assertTrue("Column type attribute was not Integer!",
                column.getType() instanceof Integer);

        column = table.getColumn("one");
        // Handle torque.deprecated.uppercasePeer=true problems
        if ( column == null ) {
            column = table.getColumn("ONE");
        }
        assertFalse(
            "Column isProtected attribute returned true instead of false!",
            column.isProtected());
        assertTrue("Column size != 50", column.getSize() == 50);
        validateAttribute("Column default", "unknown", column
                .getDefault());

        column = table.getColumn("three");
        // Handle torque.deprecated.uppercasePeer=true problems
        if ( column == null ) {
            column = table.getColumn("THREE");
        }
        assertTrue("Column position attribute != 4",
                    column.getPosition() == 4);

        TableMap tableWithProtectedColumn = map.getTable(PROTECTED_TABLE);
        column = tableWithProtectedColumn.getColumn("id");
        assertTrue(
                "Column isProtected attribute returned false instead of true!",
                column.isProtected());
        column = tableWithProtectedColumn.getColumn("name");
        assertTrue(
                "Column isProtected attribute returned false instead of true!",
                column.isProtected());
    }

    /**
     * Test that the foreign key properties get set correctly from the XML
     */
    public void testForeignKeyAttributes() throws TorqueException
    {
        DatabaseMap map = Torque.getDatabaseMap(DATABASE_NAME);
        map.initialize();
        TableMap table = map.getTable(TABLE_NAME1);
        List<ForeignKeyMap> foreignKeys = table.getForeignKeys();
        assertEquals(1, foreignKeys.size());
        ForeignKeyMap foreignKey = foreignKeys.get(0);
        assertEquals(TABLE_NAME2, foreignKey.getForeignTableName());
        assertEquals(1, foreignKey.getColumns().size());
        ForeignKeyMap.ColumnPair columnPair = foreignKey.getColumns().get(0);
        assertEquals(
                "three",
                columnPair.getLocal().getColumnName());
        assertEquals(
                "id",
                columnPair.getForeign().getColumnName());
    }

    /**
     * Test that Inheritance info is stored correctly
     */
    public void testInheritanceMapping() throws TorqueException
    {
        DatabaseMap map = Torque.getDatabaseMap(DATABASE_NAME);
        map.initialize();
        TableMap table = map.getTable(TABLE_NAME1);
        assertTrue("Table isUseInheritance returned false!", table
                .isUseInheritance());

        ColumnMap column = table.getColumn("CLASS_NAME");

        assertTrue("Column isUseInheritance returned false!", column
                .isUseInheritance());

        InheritanceMap[] inhArray = column.getInheritanceMaps();

        assertEquals("Did not get 3 mappings back!", 3, inhArray.length);
        InheritanceMap inh = column.getInheritanceMap("C");
        assertTrue("Inheritance map did not preserve XML order!", inh.getKey()
                .equals(inhArray[1].getKey()));
        validateAttribute("Inheritance key", "C", inh.getKey());
        validateAttribute("Inheritance className", "MapInheritanceChildC", inh
                .getClassName());
        validateAttribute("Inheritance extends",
                "org.apache.torque.test.dbobject.NotUsedBeforeMapInit",
                inh.getExtends());
    }

    /**
     * Test for controlled error on getting invalid database
     */
    public void testInvalidDatabaseName() throws TorqueException
    {
        DatabaseMap map = Torque.getDatabaseMap(INVALID_DATABASE_NAME);
        try
        {
            map.initialize();
        }
        catch (TorqueException e)
        {
            return;
        }
        fail("DatabaseMap.initialize() should fail if invalid DB name used!");
    }

    /**
     * Tests whether all options are present
     */
    public void testOptions() throws TorqueException
    {
        DatabaseMap databaseMap = Torque.getDatabaseMap(DATABASE_NAME);
        databaseMap.initialize();
        assertEquals(2, databaseMap.getOptions().size());
        assertEquals(
                "databaseOptionValue1",
                databaseMap.getOption("databaseOptionKey1"));
        assertEquals(
                "databaseOptionValue2",
                databaseMap.getOption("databaseOptionKey2"));

        TableMap tableMap = databaseMap.getTable("OPTION_TABLE");
        assertEquals(2, tableMap.getOptions().size());
        assertEquals(
                "tableOptionValue1",
                tableMap.getOption("tableOptionKey1"));
        assertEquals(
                "tableOptionValue2",
                tableMap.getOption("tableOptionKey2"));

        ColumnMap columnMap = tableMap.getColumn("ID");
        assertEquals(2, columnMap.getOptions().size());
        assertEquals(
                "columnOptionValue1",
                columnMap.getOption("columnOptionKey1"));
        assertEquals(
                "columnOptionValue2",
                columnMap.getOption("columnOptionKey2"));
    }

    /**
     * Validate that the attribute value matches
     * @param name
     */
    protected void validateAttribute(String name,
                                     String expected,
                                     String result)
    {
        assertTrue(name + " attribute not set correctly!\n Expected '"
                + expected + "' and got '" + result + "'", expected
                .equals(result));
    }
}
