/*
 * Copyright (c) 1998, 2018 Oracle and/or its affiliates. All rights reserved.
 *
 * This program and the accompanying materials are made available under the
 * terms of the Eclipse Public License v. 2.0 which is available at
 * http://www.eclipse.org/legal/epl-2.0,
 * or the Eclipse Distribution License v. 1.0 which is available at
 * http://www.eclipse.org/org/documents/edl-v10.php.
 *
 * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
 */

// Contributors:
// dmccann - Mar 2/2009 - 2.0 - Initial implementation
package org.eclipse.persistence.testing.oxm.schemamodelgenerator;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileWriter;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.StringWriter;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Vector;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;

import org.eclipse.persistence.descriptors.ClassDescriptor;
import org.eclipse.persistence.internal.oxm.Namespace;
import org.eclipse.persistence.internal.oxm.mappings.Descriptor;
import org.eclipse.persistence.internal.oxm.schema.SchemaModelGenerator;
import org.eclipse.persistence.internal.oxm.schema.SchemaModelGeneratorProperties;
import org.eclipse.persistence.internal.oxm.schema.SchemaModelProject;
import org.eclipse.persistence.internal.oxm.schema.model.Schema;
import org.eclipse.persistence.logging.SessionLog;
import org.eclipse.persistence.oxm.XMLContext;
import org.eclipse.persistence.oxm.XMLDescriptor;
import org.eclipse.persistence.oxm.XMLLogin;
import org.eclipse.persistence.oxm.XMLMarshaller;
import org.eclipse.persistence.oxm.platform.SAXPlatform;
import org.eclipse.persistence.oxm.platform.XMLPlatform;
import org.eclipse.persistence.sessions.DatabaseSession;
import org.eclipse.persistence.sessions.Project;
import org.eclipse.persistence.testing.jaxb.JAXBXMLComparer;
import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

import junit.framework.TestCase;

/**
 * These tests are designed to test basic schema model generation functionality.  The
 * associated project (TestProject) has three descriptors - Employee, Address and
 * PhoneNumber - that contain XMLDirect, XMLCompositeDirectCollection,
 * XMLCompositeObject and XMLCompositeCollection mappings.  Different combinations of
 * element form default, schema context and default root element settings are tested.
 * Note that these tests will set the schema context and/or default root elements on
 * every descriptor, and NOT on a per descriptor basis.
 *
 */
public class GenerateSingleSchemaTestCases extends GenerateSchemaTestCases {
    protected static final String MYNS = "myns:examplenamespace";
    protected static final String MYEMPTYNS = "";
    protected static final String ELEMENT_FORM_QUALIFIED_RESOURCE = "org/eclipse/persistence/testing/oxm/schemamodelgenerator/eltFrmQualified.xsd";
    protected static final String ELEMENT_FORM_QUALIFIED_NO_CTX_RESOURCE = "org/eclipse/persistence/testing/oxm/schemamodelgenerator/eltFrmQualifiedNoCtx.xsd";
    protected static final String ELEMENT_FORM_QUALIFIED_NO_DRE_RESOURCE = "org/eclipse/persistence/testing/oxm/schemamodelgenerator/eltFrmQualifiedNoDRE.xsd";
    protected static final String ELEMENT_FORM_UNQUALIFIED_RESOURCE = "org/eclipse/persistence/testing/oxm/schemamodelgenerator/eltFrmUnqualified.xsd";
    protected static final String ELEMENT_FORM_UNQUALIFIED_NO_CTX_RESOURCE = "org/eclipse/persistence/testing/oxm/schemamodelgenerator/eltFrmUnqualifiedNoCtx.xsd";
    protected static final String ELEMENT_FORM_UNQUALIFIED_NO_DRE_RESOURCE = "org/eclipse/persistence/testing/oxm/schemamodelgenerator/eltFrmUnqualifiedNoDRE.xsd";

    public GenerateSingleSchemaTestCases(String name) throws Exception {
        super(name);
    }

    /**
     * Test global complex type and global element generation.  Element form default
     * is qualified.
     *
     * @throws Exception
     */
    public void testElementFormQualified() throws Exception {
        Schema generatedSchema = null;
        Document tDoc = null;
        Document cDoc = null;
        try {
            boolean setSchemaContext = true;
            boolean setDefaultRootElement = true;

            SchemaModelGeneratorProperties props = new SchemaModelGeneratorProperties();
            props.addProperty(MYNS, SchemaModelGeneratorProperties.ELEMENT_FORM_QUALIFIED_KEY, true);

            Project prj = new TestProject(setSchemaContext, setDefaultRootElement, MYNS);
            loginProject(prj);
            List<Descriptor> descriptorsToProcess = setupDescriptorList(prj);
            Map<String, Schema> generatedSchemas = sg.generateSchemas(descriptorsToProcess, props);
            generatedSchema = generatedSchemas.get(MYNS);

            // debugging
            //writeSchema(generatedSchema);

            assertNotNull("No schema was generated for namespace ["+MYNS+"]", generatedSchema);

            tDoc = getDocument(generatedSchema);
            cDoc = getDocument(ELEMENT_FORM_QUALIFIED_RESOURCE);
        } catch (Exception ex) {
            ex.printStackTrace();
            fail();
        }
        assertNotNull("Schema to Document conversion failed", tDoc);
        assertNotNull("A problem occurred loading the control schema", cDoc);
        assertTrue("Schema comparsion failed", comparer.isSchemaEqual(cDoc, tDoc));
    }

    /**
     * Test global element and anonymous complex type generation. Element form
     * default is qualified.
     *
     * @throws Exception
     */
    public void testElementFormQualifiedNoSchemaContext() throws Exception {
        Schema generatedSchema = null;
        Document tDoc = null;
        Document cDoc = null;
        try {
            boolean setSchemaContext = false;
            boolean setDefaultRootElement = true;

            SchemaModelGeneratorProperties props = new SchemaModelGeneratorProperties();
            props.addProperty(MYNS, SchemaModelGeneratorProperties.ELEMENT_FORM_QUALIFIED_KEY, true);

            Project prj = new TestProject(setSchemaContext, setDefaultRootElement, MYNS);
            loginProject(prj);
            List<Descriptor> descriptorsToProcess = setupDescriptorList(prj);
            Map<String, Schema> generatedSchemas = sg.generateSchemas(descriptorsToProcess, props);
            generatedSchema = generatedSchemas.get(MYNS);
            assertNotNull("No schema was generated for namespace ["+MYNS+"]", generatedSchema);

            // debugging
            //writeSchema(generatedSchema);

            tDoc = getDocument(generatedSchema);
            cDoc = getDocument(ELEMENT_FORM_QUALIFIED_NO_CTX_RESOURCE);
        } catch (Exception ex) {
            ex.printStackTrace();
            fail();
        }
        assertNotNull("Schema to Document conversion failed", tDoc);
        assertNotNull("A problem occurred loading the control schema", cDoc);
        assertTrue("Schema comparsion failed", comparer.isSchemaEqual(cDoc, tDoc));
    }

    /**
     * Test global complex type generation.  Element form default is qualified.
     *
     * @throws Exception
     */
    public void testElementFormQualifiedNoDefaultRootElement() throws Exception {
        Schema generatedSchema = null;
        Document tDoc = null;
        Document cDoc = null;
        try {
            boolean setSchemaContext = true;
            boolean setDefaultRootElement = false;

            SchemaModelGeneratorProperties props = new SchemaModelGeneratorProperties();
            props.addProperty(MYNS, SchemaModelGeneratorProperties.ELEMENT_FORM_QUALIFIED_KEY, true);

            Project prj = new TestProject(setSchemaContext, setDefaultRootElement, MYNS);
            loginProject(prj);
            List<Descriptor> descriptorsToProcess = setupDescriptorList(prj);
            Map<String, Schema> generatedSchemas = sg.generateSchemas(descriptorsToProcess, props);
            generatedSchema = generatedSchemas.get(MYNS);
            assertNotNull("No schema was generated for namespace ["+MYNS+"]", generatedSchema);

            // debugging
            //writeSchema(generatedSchema);

            tDoc = getDocument(generatedSchema);
            cDoc = getDocument(ELEMENT_FORM_QUALIFIED_NO_DRE_RESOURCE);
        } catch (Exception ex) {
            ex.printStackTrace();
            fail();
        }
        assertNotNull("Schema to Document conversion failed", tDoc);
        assertNotNull("A problem occurred loading the control schema", cDoc);
        assertTrue("Schema comparsion failed", comparer.isSchemaEqual(cDoc, tDoc));
    }

    /**
     * No schema should be generated since there is no default root element
     * or schema context set on any descriptors
     */
    public void testElementFormQualifiedNoCtxNoDRE() throws Exception {
        Schema generatedSchema = null;
        try {
            boolean setSchemaContext = false;
            boolean setDefaultRootElement = false;

            SchemaModelGeneratorProperties props = new SchemaModelGeneratorProperties();
            props.addProperty(MYNS, SchemaModelGeneratorProperties.ELEMENT_FORM_QUALIFIED_KEY, false);

            Project prj = new TestProject(setSchemaContext, setDefaultRootElement, MYNS);
            loginProject(prj);
            List<Descriptor> descriptorsToProcess = setupDescriptorList(prj);
            Map<String, Schema> generatedSchemas = sg.generateSchemas(descriptorsToProcess, props);
            generatedSchema = generatedSchemas.get(MYNS);
        } catch (Exception ex) {
            ex.printStackTrace();
            fail();
        }
        assertNull("A schema should not have been generated", generatedSchema);
    }

    /**
     * Test global complex type and global element generation.  Element form default
     * is unqualified.  Element refs will be generated for local elements whose
     * type is that of a global complex type.
     *
     * @throws Exception
     */
    public void testElementFormUnqualified() throws Exception {
        Schema generatedSchema = null;
        Document tDoc = null;
        Document cDoc = null;
        try {
            boolean setSchemaContext = true;
            boolean setDefaultRootElement = true;
            Project prj = new TestProject(setSchemaContext, setDefaultRootElement);
            loginProject(prj);
            List<Descriptor> descriptorsToProcess = setupDescriptorList(prj);

            SchemaModelGeneratorProperties props = new SchemaModelGeneratorProperties();
            props.addProperty(MYEMPTYNS, SchemaModelGeneratorProperties.ELEMENT_FORM_QUALIFIED_KEY, false);

            Map<String, Schema> generatedSchemas = sg.generateSchemas(descriptorsToProcess, props);
            generatedSchema = generatedSchemas.get(MYEMPTYNS);
            assertNotNull("No schema was generated for namespace ["+MYEMPTYNS+"]", generatedSchema);

            // debugging
            //writeSchema(generatedSchema);

            tDoc = getDocument(generatedSchema);
            cDoc = getDocument(ELEMENT_FORM_UNQUALIFIED_RESOURCE);
        } catch (Exception ex) {
            ex.printStackTrace();
            fail();
        }
        assertNotNull("Schema to Document conversion failed", tDoc);
        assertNotNull("A problem occurred loading the control schema", cDoc);
        assertTrue("Schema comparsion failed", comparer.isSchemaEqual(cDoc, tDoc));
    }

    /**
     * Test global element generation.  Element form default is unqualified.
     * Element refs will be generated for local elements - the global
     * elements they refer to will have anonymous complex types generated.
     *
     * @throws Exception
     */
    public void testElementFormUnqualifiedNoSchemaContext() throws Exception {
        Schema generatedSchema = null;
        Document tDoc = null;
        Document cDoc = null;
        try {
            boolean setSchemaContext = false;
            boolean setDefaultRootElement = true;

            SchemaModelGeneratorProperties props = new SchemaModelGeneratorProperties();
            props.addProperty(MYEMPTYNS, SchemaModelGeneratorProperties.ELEMENT_FORM_QUALIFIED_KEY, false);

            Project prj = new TestProject(setSchemaContext, setDefaultRootElement);
            loginProject(prj);
            List<Descriptor> descriptorsToProcess = setupDescriptorList(prj);
            Map<String, Schema> generatedSchemas = sg.generateSchemas(descriptorsToProcess, props);
            generatedSchema = generatedSchemas.get(MYEMPTYNS);
            assertNotNull("No schema was generated for namespace ["+MYEMPTYNS+"]", generatedSchema);

            // debugging
            //writeSchema(generatedSchema);

            tDoc = getDocument(generatedSchema);
            cDoc = getDocument(ELEMENT_FORM_UNQUALIFIED_NO_CTX_RESOURCE);
        } catch (Exception ex) {
            ex.printStackTrace();
            fail();
        }
        assertNotNull("Schema to Document conversion failed", tDoc);
        assertNotNull("A problem occurred loading the control schema", cDoc);
        assertTrue("Schema comparsion failed", comparer.isSchemaEqual(cDoc, tDoc));
    }

    /**
     * Test global complex type generation.  Element form default is unqualified.
     * Element refs will be generated for local elements whose type is that of
     * a global complex type.  In these cases, global elements will be
     * generated for the refs.
     *
     * @throws Exception
     */
    public void testElementFormUnqualifiedNoDefaultRootElement() throws Exception {
        Schema generatedSchema = null;
        Document tDoc = null;
        Document cDoc = null;
        try {
            boolean setSchemaContext = true;
            boolean setDefaultRootElement = false;

            SchemaModelGeneratorProperties props = new SchemaModelGeneratorProperties();
            props.addProperty(MYEMPTYNS, SchemaModelGeneratorProperties.ELEMENT_FORM_QUALIFIED_KEY, false);

            Project prj = new TestProject(setSchemaContext, setDefaultRootElement);
            loginProject(prj);
            List<Descriptor> descriptorsToProcess = setupDescriptorList(prj);
            Map<String, Schema> generatedSchemas = sg.generateSchemas(descriptorsToProcess, props);
            generatedSchema = generatedSchemas.get(MYEMPTYNS);
            assertNotNull("No schema was generated for namespace ["+MYEMPTYNS+"]", generatedSchema);

            // debugging
            //writeSchema(generatedSchema);

            tDoc = getDocument(generatedSchema);
            cDoc = getDocument(ELEMENT_FORM_UNQUALIFIED_NO_DRE_RESOURCE);
        } catch (Exception ex) {
            ex.printStackTrace();
            fail();
        }
        assertNotNull("Schema to Document conversion failed", tDoc);
        assertNotNull("A problem occurred loading the control schema", cDoc);
        assertTrue("Schema comparsion failed", comparer.isSchemaEqual(cDoc, tDoc));
    }

    /**
     * No schema should be generated since there is no default root element
     * or schema context set on any descriptors
     */
    public void testElementFormUnqualifiedNoCtxNoDRE() throws Exception {
        Schema generatedSchema = null;
        try {
            boolean setSchemaContext = false;
            boolean setDefaultRootElement = false;
            Project prj = new TestProject(setSchemaContext, setDefaultRootElement);
            loginProject(prj);
            List<Descriptor> descriptorsToProcess = setupDescriptorList(prj);

            SchemaModelGeneratorProperties props = new SchemaModelGeneratorProperties();
            props.addProperty(MYEMPTYNS, SchemaModelGeneratorProperties.ELEMENT_FORM_QUALIFIED_KEY, false);

            Map<String, Schema> generatedSchemas = sg.generateSchemas(descriptorsToProcess, props);
            generatedSchema = generatedSchemas.get(MYEMPTYNS);
        } catch (Exception ex) {
            ex.printStackTrace();
            fail();
        }
        assertNull("A schema should not have been generated", generatedSchema);
    }

    /**
     * Add the Employee, Address and PhoneNumber descriptors to the List of descriptors
     * to be processed by the schema model generator.
     *
     * @param prj
     * @return
     */
    private List<Descriptor> setupDescriptorList(Project prj) {
        List<Descriptor> descriptorsToProcess = new ArrayList<Descriptor>();
        descriptorsToProcess.add((Descriptor) prj.getDescriptorForAlias("Employee"));
        descriptorsToProcess.add((Descriptor) prj.getDescriptorForAlias("Address"));
        descriptorsToProcess.add((Descriptor) prj.getDescriptorForAlias("PhoneNumber"));
        return descriptorsToProcess;
    }
}
