/*
 * Copyright 2008 Tobias Hochwallner
 *
 * Licensed 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.exolab.castor.builder.info.nature;

import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;

import junit.framework.TestCase;

import org.exolab.castor.builder.factory.FieldInfoFactory;
import org.exolab.castor.builder.info.ClassInfo;
import org.exolab.castor.builder.info.FieldInfo;
import org.exolab.castor.builder.info.NodeType;
import org.exolab.castor.builder.types.XSClass;
import org.exolab.castor.mapping.AccessMode;
import org.exolab.javasource.JClass;

/**
 * Tests access to {@link ClassInfo} properties via {@link JDOClassInfoNature}. Remember that
 * behavior of properties not set before is specified in the {@link BaseNature}
 * class and tested in {@link BaseNatureTest}. Property implementation will not
 * be tested in here.
 * 
 * @author Tobias Hochwallner, Lukas Lang
 * @since 1.2.1
 */
public final class JDOClassNatureTest extends TestCase {

    /**
     * Constructor.
     * 
     * @param name
     *            of the test case.
     */
    public JDOClassNatureTest(final String name) {
        super(name);
    }

    /**
     * Shows the usage of JDOClassNature.
     */
    public void testUsage() {
        ClassInfo classInfo = new ClassInfo(new JClass("test"));
        if (!classInfo.hasNature(JDOClassInfoNature.class.getName())) {
            classInfo.addNature(JDOClassInfoNature.class.getName());
            JDOClassInfoNature jdo = new JDOClassInfoNature(classInfo);
            jdo.setTableName("BOOK");
            jdo.addPrimaryKey("ISBN");
            // TODO Tobias jdo.addPrimaryKey("ISBN","Generator");
            assertEquals("BOOK", jdo.getTableName());
            assertEquals(1, jdo.getPrimaryKeys().size());
            assertTrue(jdo.getPrimaryKeys().contains("ISBN"));
        }
    }

    /**
     * Tests set and get JDO table name.
     */
    public void testTableName() {
        ClassInfo classInfo = new ClassInfo(new JClass("test"));
        classInfo.addNature(JDOClassInfoNature.class.getName());
        JDOClassInfoNature jdo = new JDOClassInfoNature(classInfo);
        jdo.setTableName("BOOK");
        assertEquals("BOOK", jdo.getTableName());
    }

    /**
     * Tests set and get primary keys. Adding the author column to the primary
     * key in reality would net really make sense.
     */
    public void testPrimaryKeys() {
        ClassInfo classInfo = new ClassInfo(new JClass("test"));
        classInfo.addNature(JDOClassInfoNature.class.getName());
        JDOClassInfoNature jdo = new JDOClassInfoNature(classInfo);

        List columns = new LinkedList();
        columns.add("ISBN");
        columns.add("AUTHOR");

        jdo.addPrimaryKey(columns.get(0).toString());
        assertEquals(1, jdo.getPrimaryKeys().size());

        assertTrue(jdo.getPrimaryKeys().contains(columns.get(0)));

        jdo.addPrimaryKey(columns.get(1).toString());

        List primaryKey = jdo.getPrimaryKeys();
        assertEquals(2, jdo.getPrimaryKeys().size());

        assertTrue(primaryKey.containsAll(columns));
    }

    /**
     * Tests set and get {@link AccessMode} of the JDO entity.
     */
    public void testAccessMode() {
        ClassInfo classInfo = new ClassInfo(new JClass("test"));
        classInfo.addNature(JDOClassInfoNature.class.getName());
        JDOClassInfoNature jdo = new JDOClassInfoNature(classInfo);
        jdo.setAccessMode(AccessMode.Shared);
        assertNotNull(jdo.getAccessMode());
        assertEquals(AccessMode.Shared, jdo.getAccessMode());
    }

    /**
     * Tests if getFields returns a list of {@link JDOFieldInfoNature}s.
     */
    public void testGetElementFields() {
        ClassInfo classInfo = new ClassInfo(new JClass("test"));
        FieldInfoFactory factory = new FieldInfoFactory();
        FieldInfo field = factory.createFieldInfo(new XSClass(
                new JClass("Book")), "isbn");
        field.addNature(JDOFieldInfoNature.class.getName());
        JDOFieldInfoNature jdoField = new JDOFieldInfoNature(field);
        jdoField.setColumnName("isbn");
        jdoField.setColumnType("integer");
        classInfo.addFieldInfo(field);
        classInfo.addNature(JDOClassInfoNature.class.getName());
        JDOClassInfoNature jdo = new JDOClassInfoNature(classInfo);

        List jdoFields = jdo.getFields();
        assertEquals(1, jdoFields.size());
        JDOFieldInfoNature jdoFieldReceived = (JDOFieldInfoNature) jdoFields
                .get(0);
        assertEquals("integer", jdoFieldReceived.getColumnType());
        assertEquals("isbn", jdoFieldReceived.getColumnName());
    }
    
    /**
     * Tests if getFields returns a list of {@link JDOFieldInfoNature}s.
     */
    public void testGetElementFieldsWithMoreThanOneField() {
        ClassInfo classInfo = new ClassInfo(new JClass("test"));
        FieldInfoFactory factory = new FieldInfoFactory();
        FieldInfo isbn = factory.createFieldInfo(new XSClass(
                new JClass("Book")), "isbn");
        FieldInfo title = factory.createFieldInfo(new XSClass(
                new JClass("Book")), "title");
        isbn.addNature(JDOFieldInfoNature.class.getName());
        title.addNature(JDOFieldInfoNature.class.getName());
        JDOFieldInfoNature jdoIsbn = new JDOFieldInfoNature(isbn);
        JDOFieldInfoNature jdoTitle = new JDOFieldInfoNature(title);
        jdoIsbn.setColumnName("isbn");
        jdoIsbn.setColumnType("integer");
        jdoTitle.setColumnName("title");
        jdoTitle.setColumnType("varchar");
        
        classInfo.addFieldInfo(isbn);
        classInfo.addFieldInfo(title);
        classInfo.addNature(JDOClassInfoNature.class.getName());
        JDOClassInfoNature jdo = new JDOClassInfoNature(classInfo);

        List jdoFields = jdo.getFields();
        assertEquals(2, jdoFields.size());
        
        JDOFieldInfoNature jdoFieldReceived = (JDOFieldInfoNature) jdoFields
                .get(0);
        
        // We can not depend on the order of the received Natures.
        if (jdoFieldReceived.getColumnName().equals("isbn")) {
            assertEquals("integer", jdoFieldReceived.getColumnType());
            // Now check the second field
            jdoFieldReceived = (JDOFieldInfoNature) jdoFields.get(1);
            assertEquals("title", jdoFieldReceived.getColumnName());
            assertEquals("varchar", jdoFieldReceived.getColumnType());
        } else if (jdoFieldReceived.getColumnName().equals("title")) {
            assertEquals("varchar", jdoFieldReceived.getColumnType());
            // Now check the second field
            jdoFieldReceived = (JDOFieldInfoNature) jdoFields.get(1);
            assertEquals("isbn", jdoFieldReceived.getColumnName());
            assertEquals("integer", jdoFieldReceived.getColumnType());
        } else {
            fail("Fields not found!");
        }
    }

    /**
     * Test getFields with a ClassInfo having a FieldInfo but without
     * JDOFieldInfoNature.
     */
    public void testGetElementFieldsWithoutNatures() {
        ClassInfo classInfo = new ClassInfo(new JClass("test"));
        FieldInfoFactory factory = new FieldInfoFactory();
        FieldInfo field = factory.createFieldInfo(new XSClass(
                new JClass("Book")), "isbn");
        classInfo.addFieldInfo(field);
        classInfo.addNature(JDOClassInfoNature.class.getName());
        JDOClassInfoNature jdo = new JDOClassInfoNature(classInfo);

        List jdoFields = jdo.getFields();
        assertEquals(0, jdoFields.size());
    }

    /**
     * Test getFields with a ClassInfo no fields added before.
     */
    public void testGetElementFieldsNoFieldsAdded() {
        ClassInfo classInfo = new ClassInfo(new JClass("test"));
        classInfo.addNature(JDOClassInfoNature.class.getName());
        JDOClassInfoNature jdo = new JDOClassInfoNature(classInfo);

        List jdoFields = jdo.getFields();
        assertEquals(0, jdoFields.size());
    }
    
    /**
     * Tests if getFields returns a list of {@link JDOFieldInfoNature}s.
     */
    public void testGetElementFieldsOnlyOneHasNature() {
        ClassInfo classInfo = new ClassInfo(new JClass("test"));
        FieldInfoFactory factory = new FieldInfoFactory();
        FieldInfo field = factory.createFieldInfo(new XSClass(
                new JClass("Book")), "isbn");
        FieldInfo field2 = factory.createFieldInfo(new XSClass(
                new JClass("Book")), "abc");
        field.addNature(JDOFieldInfoNature.class.getName());
        JDOFieldInfoNature jdoField = new JDOFieldInfoNature(field);
        jdoField.setColumnName("isbn");
        jdoField.setColumnType("integer");
        classInfo.addFieldInfo(field);
        classInfo.addFieldInfo(field2);
        classInfo.addNature(JDOClassInfoNature.class.getName());
        JDOClassInfoNature jdo = new JDOClassInfoNature(classInfo);

        List jdoFields = jdo.getFields();
        assertEquals(1, jdoFields.size());
        JDOFieldInfoNature jdoFieldReceived = (JDOFieldInfoNature) jdoFields
                .get(0);
        assertEquals("integer", jdoFieldReceived.getColumnType());
        assertEquals("isbn", jdoFieldReceived.getColumnName());
    }
    
    /**
     * Tests if getFields returns a list of {@link JDOFieldInfoNature}s.
     */
    public void testGetAttributeTextElementFields() {
        ClassInfo classInfo = new ClassInfo(new JClass("test"));
        FieldInfoFactory factory = new FieldInfoFactory();
        
        FieldInfo elementField = factory.createFieldInfo(new XSClass(
                new JClass("Book")), "isbn");
        // Set node type
        elementField.addNature(XMLInfoNature.class.getName());
        new XMLInfoNature(elementField).setNodeType(NodeType.ELEMENT);
        // Set column name
        elementField.addNature(JDOFieldInfoNature.class.getName());
        JDOFieldInfoNature jdoField = new JDOFieldInfoNature(elementField);
        jdoField.setColumnName("isbn");
        classInfo.addFieldInfo(elementField);
        
        FieldInfo attributeField = factory.createFieldInfo(new XSClass(
                new JClass("Book")), "title");
        // Set node type
        attributeField.addNature(XMLInfoNature.class.getName());
        new XMLInfoNature(attributeField).setNodeType(NodeType.ATTRIBUTE);
        // Set column name
        attributeField.addNature(JDOFieldInfoNature.class.getName());
        jdoField = new JDOFieldInfoNature(attributeField);
        jdoField.setColumnName("title");
        classInfo.addFieldInfo(attributeField);
        
        FieldInfo textField = factory.createFieldInfo(new XSClass(
                new JClass("Book")), "price");
        // Set node type
        textField.addNature(XMLInfoNature.class.getName());
        new XMLInfoNature(textField).setNodeType(NodeType.TEXT);
        // Set column name
        textField.addNature(JDOFieldInfoNature.class.getName());
        jdoField = new JDOFieldInfoNature(textField);
        jdoField.setColumnName("price");
        classInfo.addFieldInfo(textField);
        
        // Add JDO Nature to ClassInfo.
        classInfo.addNature(JDOClassInfoNature.class.getName());
        JDOClassInfoNature jdo = new JDOClassInfoNature(classInfo);

        List jdoFields = jdo.getFields();
        assertEquals(3, jdoFields.size());

        boolean containsElementField = false;
        boolean containsAttributeField = false;
        boolean containsTextField = false;
        
        for (Iterator fieldIt = jdoFields.iterator(); fieldIt.hasNext();) {
            JDOFieldInfoNature nature = (JDOFieldInfoNature) fieldIt.next();
            if (nature.getColumnName().equals("isbn")) {
                containsElementField = true;
            } else if (nature.getColumnName().equals("title")) {
                containsAttributeField = true;
            } else if (nature.getColumnName().equals("price")) {
                containsTextField = true;
            }
        }
        
        assertTrue(containsElementField);
        assertTrue(containsAttributeField);
        assertTrue(containsTextField);
    }
    
    
    
    
}
