/*
 * Copyright (c) 2011, 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:
//     Blaise Doughan - 2.2 - initial implementation
package org.eclipse.persistence.testing.jaxb.xmlmarshaller;

import java.io.File;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.lang.reflect.Type;
import java.net.URL;

import org.eclipse.persistence.oxm.XMLConstants;
import jakarta.xml.bind.JAXBContext;
import jakarta.xml.bind.UnmarshalException;
import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;
import javax.xml.stream.XMLEventReader;
import javax.xml.stream.XMLStreamException;
import javax.xml.stream.XMLStreamReader;
import javax.xml.transform.Source;
import javax.xml.transform.sax.SAXSource;
import javax.xml.transform.stream.StreamSource;
import javax.xml.validation.Schema;
import javax.xml.validation.SchemaFactory;

import org.eclipse.persistence.jaxb.JAXBContextFactory;
import org.eclipse.persistence.jaxb.JAXBUnmarshaller;
import org.eclipse.persistence.platform.xml.XMLParser;
import org.eclipse.persistence.platform.xml.XMLPlatform;
import org.eclipse.persistence.platform.xml.XMLPlatformFactory;
import org.eclipse.persistence.testing.oxm.OXTestCase;
import org.w3c.dom.Node;
import org.xml.sax.InputSource;
import org.xml.sax.XMLReader;

public class UnmarshalSchemaValidationTestCases extends OXTestCase {

    static String SCHEMA = "org/eclipse/persistence/testing/jaxb/xmlmarshaller/Employee.xsd";
    static String DOUBLE_ERROR_XML = "org/eclipse/persistence/testing/jaxb/xmlmarshaller/Employee_TwoError.xml";

    private JAXBUnmarshaller unmarshaller;

    private Schema schema;

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

    @Override
    protected void setUp() throws Exception {
        Class<?>[] classes = {Employee.class};
        JAXBContext jc = JAXBContextFactory.createContext(classes, null);
        unmarshaller = (JAXBUnmarshaller) jc.createUnmarshaller();

        SchemaFactory sf = SchemaFactory.newInstance(javax.xml.XMLConstants.W3C_XML_SCHEMA_NS_URI);
        InputStream stream = ClassLoader.getSystemResourceAsStream(SCHEMA);
        Schema schema = sf.newSchema(new StreamSource(stream));
        stream.close();
        this.schema = schema;
    }

    public void testFailOnSecondErrorFile() throws Exception {
        CustomErrorValidationEventHandler eventHandler = new CustomErrorValidationEventHandler();
        unmarshaller.setEventHandler(eventHandler);
        try {
            unmarshaller.setSchema(this.schema);
            unmarshaller.unmarshal(Thread.currentThread().getContextClassLoader().getResource(DOUBLE_ERROR_XML));
        } catch (UnmarshalException ex) {
            assertEquals(2, eventHandler.getErrorCount());
            return;
        }
        fail("No Exceptions thrown.");
    }

    public void testFailOnSecondErrorInputSource() throws Exception {
        CustomErrorValidationEventHandler eventHandler = new CustomErrorValidationEventHandler();
        unmarshaller.setEventHandler(eventHandler);
        InputStream stream = ClassLoader.getSystemResourceAsStream(DOUBLE_ERROR_XML);
        InputSource inputSource = new InputSource(stream);
        try {
            unmarshaller.setSchema(this.schema);
            unmarshaller.unmarshal(inputSource);
        } catch (UnmarshalException ex) {
            assertEquals(2,eventHandler.getErrorCount());
            return;
        }
        fail("No Exceptions thrown.");
    }

    public void testFailOnSecondErrorNode() throws Exception {
        CustomErrorValidationEventHandler eventHandler = new CustomErrorValidationEventHandler();
        unmarshaller.setEventHandler(eventHandler);
        InputStream stream = ClassLoader.getSystemResourceAsStream(DOUBLE_ERROR_XML);
        XMLPlatform xmlPlatform = XMLPlatformFactory.getInstance().getXMLPlatform();
        XMLParser xmlParser = xmlPlatform.newXMLParser();
        xmlParser.setNamespaceAware(true);
        Node node = xmlParser.parse(stream);
        try {
            unmarshaller.setSchema(this.schema);
            unmarshaller.unmarshal(node);
        } catch (UnmarshalException ex) {
            assertEquals(2, eventHandler.getErrorCount());
            return;
        }
        fail("No Exceptions thrown.");
    }

    public void testFailOnSecondErrorNodeWithClass() throws Exception {
        CustomErrorValidationEventHandler eventHandler = new CustomErrorValidationEventHandler();
        unmarshaller.setEventHandler(eventHandler);
        InputStream stream = ClassLoader.getSystemResourceAsStream(DOUBLE_ERROR_XML);
        XMLPlatform xmlPlatform = XMLPlatformFactory.getInstance().getXMLPlatform();
        XMLParser xmlParser = xmlPlatform.newXMLParser();
        xmlParser.setNamespaceAware(true);
        Node node = xmlParser.parse(stream);
        try {
            unmarshaller.setSchema(this.schema);
            unmarshaller.unmarshal(node, Employee.class);
        } catch (UnmarshalException ex) {
            assertEquals(2, eventHandler.getErrorCount());
            return;
        }
        fail("No Exceptions thrown.");
    }

    public void testFailOnSecondErrorReader() throws Exception {
        CustomErrorValidationEventHandler eventHandler = new CustomErrorValidationEventHandler();
        unmarshaller.setEventHandler(eventHandler);
        InputStream stream = ClassLoader.getSystemResourceAsStream(DOUBLE_ERROR_XML);
        InputStreamReader reader = new InputStreamReader(stream);
        try {
            unmarshaller.setSchema(this.schema);
            unmarshaller.unmarshal(reader);
        } catch (UnmarshalException ex) {
            assertEquals(eventHandler.getErrorCount(), 2);
            return;
        }
        fail("No Exceptions thrown.");
    }

    public void testFailOnSecondErrorSource() throws Exception {
        CustomErrorValidationEventHandler eventHandler = new CustomErrorValidationEventHandler();
        unmarshaller.setEventHandler(eventHandler);
        InputStream stream = ClassLoader.getSystemResourceAsStream(DOUBLE_ERROR_XML);
        Source source = new StreamSource(stream);
        try {
            unmarshaller.setSchema(this.schema);
            unmarshaller.unmarshal(source);
        } catch (UnmarshalException ex) {
            assertEquals(eventHandler.getErrorCount(), 2);
            return;
        }
        fail("No Exceptions thrown.");
    }

    public void testFailOnSecondErrorSAXSource() throws Exception {
        CustomErrorValidationEventHandler eventHandler = new CustomErrorValidationEventHandler();
        unmarshaller.setEventHandler(eventHandler);

        SAXParserFactory spf = SAXParserFactory.newInstance();
        spf.setNamespaceAware(true);
        SAXParser sp = spf.newSAXParser();
        XMLReader xr = sp.getXMLReader();
        InputStream stream = ClassLoader.getSystemResourceAsStream(DOUBLE_ERROR_XML);
        Source source = new SAXSource(xr, new InputSource(stream));
        try {
            unmarshaller.setSchema(this.schema);
            unmarshaller.unmarshal(source);
        } catch (UnmarshalException ex) {
            assertEquals(2, eventHandler.getErrorCount());
            return;
        }
        fail("No Exceptions thrown.");
    }

    public void testFailOnSecondErrorSAXSourceWithClass() throws Exception {
        CustomErrorValidationEventHandler eventHandler = new CustomErrorValidationEventHandler();
        unmarshaller.setEventHandler(eventHandler);
        SAXParserFactory spf = SAXParserFactory.newInstance();
        spf.setNamespaceAware(true);
        SAXParser sp = spf.newSAXParser();
        XMLReader xr = sp.getXMLReader();
        InputStream stream = ClassLoader.getSystemResourceAsStream(DOUBLE_ERROR_XML);
        Source source = new SAXSource(xr, new InputSource(stream));
        try {
            unmarshaller.setSchema(this.schema);
            unmarshaller.unmarshal(source, Employee.class);
        } catch (UnmarshalException ex) {
            assertEquals(2, eventHandler.getErrorCount());
            return;
        }
        fail("No Exceptions thrown.");
    }

    public void testFailOnSecondErrorSAXSourceWithType() throws Exception {
        CustomErrorValidationEventHandler eventHandler = new CustomErrorValidationEventHandler();
        unmarshaller.setEventHandler(eventHandler);
        SAXParserFactory spf = SAXParserFactory.newInstance();
        spf.setNamespaceAware(true);
        SAXParser sp = spf.newSAXParser();
        XMLReader xr = sp.getXMLReader();
        InputStream stream = ClassLoader.getSystemResourceAsStream(DOUBLE_ERROR_XML);
        Source source = new SAXSource(xr, new InputSource(stream));
        try {
            unmarshaller.setSchema(this.schema);
            unmarshaller.unmarshal(source, (Type) Employee.class);
        } catch (UnmarshalException ex) {
            assertEquals(2, eventHandler.getErrorCount());
            return;
        }
        fail("No Exceptions thrown.");
    }

    public void testFailOnSecondErrorURL() throws Exception {
        CustomErrorValidationEventHandler eventHandler = new CustomErrorValidationEventHandler();
        unmarshaller.setEventHandler(eventHandler);
        URL url = ClassLoader.getSystemResource(DOUBLE_ERROR_XML);
        try {
            unmarshaller.setSchema(this.schema);
            unmarshaller.unmarshal(url);
        } catch (UnmarshalException ex) {
            assertEquals(2, eventHandler.getErrorCount());
            return;
        }
        fail("No Exceptions thrown.");
    }

    public void testFailOnSecondErrorSourceWithClass() throws Exception {
        CustomErrorValidationEventHandler eventHandler = new CustomErrorValidationEventHandler();
        unmarshaller.setEventHandler(eventHandler);
        InputStream stream = ClassLoader.getSystemResourceAsStream(DOUBLE_ERROR_XML);
        Source source = new StreamSource(stream);
        try {
            unmarshaller.setSchema(this.schema);
            unmarshaller.unmarshal(source, Employee.class);
        } catch (UnmarshalException ex) {
            assertEquals(2, eventHandler.getErrorCount());
            return;
        }
        fail("No Exceptions thrown.");
    }

    public void testFailOnSecondErrorSourceWithType() throws Exception {
        CustomErrorValidationEventHandler eventHandler = new CustomErrorValidationEventHandler();
        unmarshaller.setEventHandler(eventHandler);
        InputStream stream = ClassLoader.getSystemResourceAsStream(DOUBLE_ERROR_XML);
        Source source = new StreamSource(stream);
        try {
            unmarshaller.setSchema(this.schema);
            unmarshaller.unmarshal(source, (Type) Employee.class);
        } catch (UnmarshalException ex) {
            assertEquals(2, eventHandler.getErrorCount());
            return;
        }
        fail("No Exceptions thrown.");
    }

    public void testFailOnSecondErrorXMLEventReader() throws Exception {
        InputStream stream = ClassLoader.getSystemResourceAsStream(DOUBLE_ERROR_XML);
        XMLEventReader xmlEventReader = createXMLEventReader(stream);
        if(null == xmlEventReader) {
            return;
        }
        CustomErrorValidationEventHandler eventHandler = new CustomErrorValidationEventHandler();
        unmarshaller.setEventHandler(eventHandler);
        try {
            unmarshaller.setSchema(this.schema);
            unmarshaller.unmarshal(xmlEventReader);
        } catch (UnmarshalException ex) {
            assertEquals(2, eventHandler.getErrorCount());
            return;
        }
        fail("No Exceptions thrown.");
    }

    public void testFailOnSecondErrorXMLEventReaderWithClass() throws Exception {
        InputStream stream = ClassLoader.getSystemResourceAsStream(DOUBLE_ERROR_XML);
        XMLEventReader xmlEventReader = createXMLEventReader(stream);
        if(null == xmlEventReader) {
            return;
        }
        CustomErrorValidationEventHandler eventHandler = new CustomErrorValidationEventHandler();
        unmarshaller.setEventHandler(eventHandler);
        try {
            unmarshaller.setSchema(this.schema);
            unmarshaller.unmarshal(xmlEventReader, Employee.class);
        } catch (UnmarshalException ex) {
            assertEquals(2, eventHandler.getErrorCount());
            return;
        }
        fail("No Exceptions thrown.");
    }

    public void testFailOnSecondErrorXMLEventReaderWithType() throws Exception {
        InputStream stream = ClassLoader.getSystemResourceAsStream(DOUBLE_ERROR_XML);
        XMLEventReader xmlEventReader = createXMLEventReader(stream);
        if(null == xmlEventReader) {
            return;
        }
        CustomErrorValidationEventHandler eventHandler = new CustomErrorValidationEventHandler();
        unmarshaller.setEventHandler(eventHandler);
        try {
            unmarshaller.setSchema(this.schema);
            unmarshaller.unmarshal(xmlEventReader, (Type) Employee.class);
        } catch (UnmarshalException ex) {
            assertEquals(2, eventHandler.getErrorCount());
            return;
        }
        fail("No Exceptions thrown.");
    }

    public void testFailOnSecondErrorXMLStreamReader() throws Exception {
        InputStream stream = ClassLoader.getSystemResourceAsStream(DOUBLE_ERROR_XML);
        XMLStreamReader xmlStreamReader = createXMLStreamReader(stream);
        if(null == xmlStreamReader) {
            return;
        }
        CustomErrorValidationEventHandler eventHandler = new CustomErrorValidationEventHandler();
        unmarshaller.setEventHandler(eventHandler);
        try {
            unmarshaller.setSchema(this.schema);
            unmarshaller.unmarshal(xmlStreamReader);
        } catch (UnmarshalException ex) {
            assertEquals(2, eventHandler.getErrorCount());
            return;
        }
        fail("No Exceptions thrown.");
    }

    public void testFailOnSecondErrorXMLStreamReaderWithClass() throws Exception {
        InputStream stream = ClassLoader.getSystemResourceAsStream(DOUBLE_ERROR_XML);
        XMLStreamReader xmlStreamReader = createXMLStreamReader(stream);
        if(null == xmlStreamReader) {
            return;
        }
        CustomErrorValidationEventHandler eventHandler = new CustomErrorValidationEventHandler();
        unmarshaller.setEventHandler(eventHandler);
        try {
            unmarshaller.setSchema(this.schema);
            unmarshaller.unmarshal(xmlStreamReader, Employee.class);
        } catch (UnmarshalException ex) {
            assertEquals(2, eventHandler.getErrorCount());
            return;
        }
        fail("No Exceptions thrown.");
    }

    public void testFailOnSecondErrorXMLStreamReaderWithType() throws Exception {
        InputStream stream = ClassLoader.getSystemResourceAsStream(DOUBLE_ERROR_XML);
        XMLStreamReader xmlStreamReader = createXMLStreamReader(stream);
        if(null == xmlStreamReader) {
            return;
        }
        CustomErrorValidationEventHandler eventHandler = new CustomErrorValidationEventHandler();
        unmarshaller.setEventHandler(eventHandler);
        try {
            unmarshaller.setSchema(this.schema);
            unmarshaller.unmarshal(xmlStreamReader, (Type) Employee.class);
        } catch (UnmarshalException ex) {
            assertEquals(2, eventHandler.getErrorCount());
            return;
        }
        fail("No Exceptions thrown.");
    }

    public void testFailOnSecondErrorInputStream() throws Exception {
        CustomErrorValidationEventHandler eventHandler = new CustomErrorValidationEventHandler();
        unmarshaller.setEventHandler(eventHandler);
        InputStream stream = ClassLoader.getSystemResourceAsStream(DOUBLE_ERROR_XML);
        try {
            unmarshaller.setSchema(this.schema);
            unmarshaller.unmarshal(stream);
        } catch (UnmarshalException ex) {
            assertEquals(2, eventHandler.getErrorCount());
            return;
        }
        fail("No Exceptions thrown.");
    }

    private XMLEventReader createXMLEventReader(InputStream stream) {
        try {
            if(null == XML_INPUT_FACTORY) {
                return null;
            }
            XMLEventReader xmlEventReader = XML_INPUT_FACTORY.createXMLEventReader(stream);
            return xmlEventReader;
        } catch(XMLStreamException e) {
            return null;
        }
    }

    private XMLStreamReader createXMLStreamReader(InputStream stream) {
        try {
            if(null == XML_INPUT_FACTORY) {
                return null;
            }
            XMLStreamReader xmlStreamReader = XML_INPUT_FACTORY.createXMLStreamReader(stream);
            return xmlStreamReader;
        } catch(XMLStreamException e) {
            return null;
        }
    }

}
