/*
 * Copyright (c) 1998, 2021 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:
//     Oracle - initial API and implementation from Oracle TopLink
package org.eclipse.persistence.testing.jaxb.xmlmarshaller;

import java.io.File;
import java.io.InputStream;

import java.lang.reflect.Method;

import java.net.URL;
import java.net.URLClassLoader;

import jakarta.xml.bind.JAXBContext;
import jakarta.xml.bind.PropertyException;
import jakarta.xml.bind.Unmarshaller;
import jakarta.xml.bind.UnmarshalException;
import jakarta.xml.bind.util.JAXBSource;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.transform.Source;
import javax.xml.transform.stream.StreamSource;

import junit.framework.TestCase;
import org.eclipse.persistence.jaxb.JAXBUnmarshaller;

import org.w3c.dom.Document;
import org.w3c.dom.Node;

import org.xml.sax.InputSource;

public class UnmarshallerTestCases extends TestCase  {

    private static final String SCHEMA_LANGUAGE = "http://java.sun.com/xml/jaxp/properties/schemaLanguage";
    private static final String XML_SCHEMA = "http://www.w3.org/2001/XMLSchema";

    private final static String CONTROL_XML_FILE_NAME = "org/eclipse/persistence/testing/jaxb/xmlmarshaller/Employee.xml";
    private final static String CONTROL_EMPLOYEE_NAME = "Jane Doe";

    private JAXBContext jaxbContext;
    private JAXBUnmarshaller unmarshaller;
    private DocumentBuilder parser;
    private String contextPath;

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

    @Override
    public void setUp() throws Exception {
        contextPath = System.getProperty("jaxb.test.contextpath", JAXBSAXTestSuite.CONTEXT_PATH);

        jaxbContext = JAXBContext.newInstance(contextPath, getClass().getClassLoader());
        unmarshaller = (JAXBUnmarshaller) jaxbContext.createUnmarshaller();
        DocumentBuilderFactory builderFactory = DocumentBuilderFactory.newInstance();
        builderFactory.setIgnoringElementContentWhitespace(true);
        parser = builderFactory.newDocumentBuilder();
    }

    /**
    * Since JAXB delegates all of its functionality to XMLMarshaller, there is no need to
    * re-test all of the various marshal/unmarshal methods.
    */
    public void testUnmarshalFile() throws Exception {
        File file = new File(ClassLoader.getSystemResource(CONTROL_XML_FILE_NAME).getFile());
        unmarshaller.setValidating(true);
        Object testObject = unmarshaller.unmarshal(file);
        assertEquals(getControlObject(), testObject);
    }

    public void testUnmarshalFileNotValidating() throws Exception {
        File file = new File(ClassLoader.getSystemResource(CONTROL_XML_FILE_NAME).getFile());
        unmarshaller.setValidating(false);
        Object testObject = unmarshaller.unmarshal(file);
        assertEquals(getControlObject(), testObject);
    }

    public void testUnmarshalFromJAXBSource() throws Exception {
        File file = new File(ClassLoader.getSystemResource(CONTROL_XML_FILE_NAME).getFile());
        Object unmarshalledObject = unmarshaller.unmarshal(file);

        JAXBSource jaxbSource = new JAXBSource(jaxbContext.createMarshaller(), unmarshalledObject);

        Object objectFromSource = unmarshaller.unmarshal(jaxbSource);
        assertEquals(getControlObject(), objectFromSource);
    }

    // Invalid sources tests=============================================================

    public void testUnmarshalFromInvalidFile() throws Exception {
        File file = new File("blah.txt");
        try {
            Object unmarshalledObject = unmarshaller.unmarshal(file);
        } catch (UnmarshalException e) {
            assertTrue(true);
            return;
        }
        assertTrue("An XMLValidation should have been caught but wasn't.", false);
    }

    public void testUnmarshalFromInvalidURL() throws Exception {
        URL url = new URL("http://");
        try {
            Object unmarshalledObject = unmarshaller.unmarshal(url);
        } catch (UnmarshalException e) {
            assertTrue(true);
            return;
        }
        assertTrue("An UnmarshalException should have been caught but wasn't.", false);
    }

    public void testUnmarshalFromInvalidNode()  throws Exception {
        Document doc = parser.newDocument();
        Node node = doc.createAttribute("aaa");
        try {
            Object unmarshalledObject = unmarshaller.unmarshal(node);
        } catch (UnmarshalException e) {
            assertTrue(true);
            return;
        }
        assertTrue("An UnmarshalException should have been caught but wasn't.", false);
    }

    public void testUnmarshalFromInvalidInputStream() throws Exception {
        InputStream stream = ClassLoader.getSystemResourceAsStream(CONTROL_XML_FILE_NAME);
        stream.close();
        try {
            Object unmarshalledObject = unmarshaller.unmarshal(stream);
        } catch (UnmarshalException e) {
            assertTrue(true);
            return;
        }
        assertTrue("An UnmarshalException should have been caught but wasn't.", false);
    }

    public void testUnmarshalFromInvalidSource() throws Exception {
        InputStream inputStream = ClassLoader.getSystemResourceAsStream(CONTROL_XML_FILE_NAME);
        StreamSource source = new StreamSource(inputStream);
        source.getInputStream().close();
        try {
            Object unmarshalledObject = unmarshaller.unmarshal(source);
        } catch (UnmarshalException e) {
            assertTrue(true);
            return;
        }
        assertTrue("An UnmarshalException should have been caught but wasn't.", false);
    }

    public void testUnmarshalFromInvalidInputSource() throws Exception {
        InputStream stream = ClassLoader.getSystemResourceAsStream(CONTROL_XML_FILE_NAME);
        InputSource source = new InputSource(stream);
        source.getByteStream().close();
        try {
            Object unmarshalledObject = unmarshaller.unmarshal(source);
        } catch (UnmarshalException e) {
            assertTrue(true);
            return;
        }
        assertTrue("An UnmarshalException should have been caught but wasn't.", false);
    }

    public void testUnmarshalFileException() {
        boolean caughtException = false;
        try {
            unmarshaller.unmarshal((File) null);
        } catch (IllegalArgumentException e) {
            caughtException = true;
        } catch (Exception e) {
        }
        assertTrue("JAXBUnmarshaller did not throw IllegalArgumentException as expected.", caughtException);
    }

    public void testUnmarshalInputStreamException() {
        boolean caughtException = false;
        try {
            unmarshaller.unmarshal((InputStream) null);
        } catch (IllegalArgumentException e) {
            caughtException = true;
        } catch (Exception e) {
        }
        assertTrue("JAXBUnmarshaller did not throw IllegalArgumentException as expected.", caughtException);
    }

    public void testUnmarshalInputSourceException() {
        boolean caughtException = false;
        try {
            unmarshaller.unmarshal((InputSource) null);
        } catch (IllegalArgumentException e) {
            caughtException = true;
        } catch (Exception e) {
        }
        assertTrue("JAXBUnmarshaller did not throw IllegalArgumentException as expected.", caughtException);
    }

    public void testUnmarshalSourceException() {
        boolean caughtException = false;
        try {
            unmarshaller.unmarshal((Source) null);
        } catch (IllegalArgumentException e) {
            caughtException = true;
        } catch (Exception e) {
        }
        assertTrue("JAXBUnmarshaller did not throw IllegalArgumentException as expected.", caughtException);
    }

    public void testUnmarshalURLException() {
        boolean caughtException = false;
        try {
            unmarshaller.unmarshal((URL) null);
        } catch (IllegalArgumentException e) {
            caughtException = true;
        } catch (Exception e) {
        }
        assertTrue("JAXBUnmarshaller did not throw IllegalArgumentException as expected.", caughtException);
    }

    public void testUnmarshalNodeException() {
        boolean caughtException = false;
        try {
            unmarshaller.unmarshal((Node) null);
        } catch (IllegalArgumentException e) {
            caughtException = true;
        } catch (Exception e) {
        }
        assertTrue("JAXBUnmarshaller did not throw IllegalArgumentException as expected.", caughtException);
    }

    public void testJAXBClassLoader() throws Exception {
        // Determine the directory that Employee.class is in
        URL classURL = ClassLoader.getSystemClassLoader().getResource("org/eclipse/persistence/testing/jaxb/xmlmarshaller/Employee.class");
        URL directoryURL = new File(classURL.getFile()).getParentFile().getParentFile().getParentFile().getParentFile().getParentFile().getParentFile().toURI().toURL();

        // Create a URLClassLoader specifically for this class
        URLClassLoader classLoader = new URLClassLoader(new URL[] { directoryURL });

        // Set up a control object using this ClassLoader
        Object controlObject = Class.forName("org.eclipse.persistence.testing.jaxb.xmlmarshaller.Employee", true, classLoader).getConstructor().newInstance();
        Method setMethod = controlObject.getClass().getMethod("setName", String.class);
        setMethod.invoke(controlObject, CONTROL_EMPLOYEE_NAME);

        // Unmarshall the control file, passing JAXB this classLoader
        JAXBContext jaxbContext = JAXBContext.newInstance(contextPath, classLoader);
        Unmarshaller unmarshaller = jaxbContext.createUnmarshaller();
        File file = new File(ClassLoader.getSystemResource(CONTROL_XML_FILE_NAME).getFile());
        Object testObject = unmarshaller.unmarshal(file);

        assertEquals(controlObject, testObject);
    }

    public void testSetNullPropertyException() {
        boolean caughtException = false;
        try {
            unmarshaller.setProperty(null, null);
        } catch (IllegalArgumentException e) {
            caughtException = true;
        } catch (Exception e) {
        }
        assertTrue("JAXBUnmarshaller did not throw IllegalArgumentException as expected.", caughtException);
    }

    public void testGetNullPropertyException() {
        boolean caughtException = false;
        try {
            unmarshaller.getProperty(null);
        } catch (IllegalArgumentException e) {
            caughtException = true;
        } catch (Exception e) {
        }
        assertTrue("JAXBUnmarshaller did not throw IllegalArgumentException as expected.", caughtException);
    }

    public void testSetInvalidPropertyException() {
        boolean caughtException = false;
        try {
            unmarshaller.setProperty("thisIsAnInvalidProperty", "thisIsAnInvalidValue");
        } catch (PropertyException e) {
            caughtException = true;
        } catch (Exception e) {
        }
        assertTrue("JAXBUnmarshaller did not throw PropertyException as expected.", caughtException);
    }

    public void testGetInvalidPropertyException() {
        boolean caughtException = false;
        try {
            unmarshaller.getProperty("thisIsAnInvalidProperty");
        } catch (PropertyException e) {
            caughtException = true;
        } catch (Exception e) {
        }
        assertTrue("JAXBUnmarshaller did not throw PropertyException as expected.", caughtException);
    }

    // =============================================================

    private Object getControlObject() {
        Employee employee = new Employee();
        employee.setName(CONTROL_EMPLOYEE_NAME);
        return employee;
    }

}
