/*
 * Copyright (c) 2017, 2020 Oracle and/or its affiliates. All rights reserved.
 */

package javasoft.sqe.tests.bind.binder;

import java.io.IOException;
import java.net.URL;
import javasoft.sqe.tests.api.jakarta.xml.bind.ErrorCollector;
import jakarta.xml.bind.annotation.*;
import jakarta.xml.bind.Binder;
import jakarta.xml.bind.JAXBContext;
import jakarta.xml.bind.JAXBException;
import jakarta.xml.bind.UnmarshalException;
import jakarta.xml.bind.MarshalException;
import jakarta.xml.bind.JAXBElement;
import javax.xml.namespace.QName;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.xml.sax.SAXException;
import javasoft.sqe.javatest.Status;
import java.io.PrintWriter;
import javasoft.sqe.tests.api.jakarta.xml.bind.CTTest;

public class MarshalUnmarshal_CTTests extends CTTest {

    /**
     * Command line starter.
     */
    public static void main(String[] args) {
        MarshalUnmarshal_CTTests test = new MarshalUnmarshal_CTTests();
        PrintWriter err = new PrintWriter(System.err, true);
        PrintWriter out = new PrintWriter(System.out, true);
        Status status = test.run(args, err, out);
        err.flush();
        out.flush();
        status.exit();
    }

    @XmlAccessorType(XmlAccessType.FIELD)
    @XmlType(name = "MyClass", propOrder = { "name", "street", "city", "country" })
    static public class MyClass {
        public String name = "name", street = "street", city = null, country;
    }

    public Binder<Node> getBinder() throws JAXBException{
        JAXBContext jaxbContext = getJAXBContext();
        return jaxbContext.createBinder();
    }

    public Binder<Node> getBinderObject(JAXBContext jaxbContext) {
        try {
            if(jaxbContext == null)
                 jaxbContext = getJAXBContext();
            return jaxbContext.createBinder();
        } catch( JAXBException x ){
            x.printStackTrace(ref);
            throw new RuntimeException(x);
        }
    }

    public Document getDocument(String doc) throws ParserConfigurationException, SAXException, IOException{
        URL documentURL = getDocumentURL(doc);
        DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance();
        documentBuilderFactory.setNamespaceAware(true);
        DocumentBuilder documentBuilder = documentBuilderFactory.newDocumentBuilder();
        return documentBuilder.parse(documentURL.openStream());
    }

    public Document getDocumentObject(String doc) {
        try {
            DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance();
            documentBuilderFactory.setNamespaceAware(true);
            DocumentBuilder documentBuilder = documentBuilderFactory.newDocumentBuilder();
            URL documentURL = getDocumentURL(doc);
            return documentBuilder.parse(documentURL.openStream());
        } catch( Exception x ){
            x.printStackTrace(ref);
            throw new RuntimeException(x);
        }
    }

    Document createNewDocument() {
        DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance();
        documentBuilderFactory.setNamespaceAware(true);
        try {
            return documentBuilderFactory.newDocumentBuilder().newDocument();
        } catch ( ParserConfigurationException x ){
            x.printStackTrace(ref);
            throw new RuntimeException(x);
        }
    }

    public Status unmarshal001( ) {
        ErrorCollector eh = new ErrorCollector();
        Binder<Node> binder;
        try{
            binder = getBinder();
        }catch(JAXBException e){
            e.printStackTrace(ref);
            return Status.failed("Unable to create valid context or binder");
        }
        try{
            eh.setParent(binder.getEventHandler());
            binder.setEventHandler(eh);
        }catch(JAXBException e){
            e.printStackTrace(ref);
            return Status.failed("Error on setting event handler");
        }
        try{
            Object o = binder.unmarshal(getDocument("Binder.xml"));
            if(o == null){
                return Status.failed("unmarshal(\"Binder.xml\") returns null");
            }
            if(!eh.hasEvents()){
                return Status.passed("OK");
            }
            return Status.failed("No exception is thrown but some error events are handled");
        }catch(JAXBException jaxbe){
            jaxbe.printStackTrace(ref);
            if (!eh.hasEvents()) {
                return Status.failed("no error events are handled but " + jaxbe + " is thrown");
            }
            return Status.failed(jaxbe.toString());
        }catch(Exception e){
            e.printStackTrace(ref);
            return Status.failed("Exception is thrown: "+e);
        }finally{
            eh.printEvents(ref);
        }
    }

    public Status unmarshal002( ) {
        ErrorCollector eh = new ErrorCollector();
        Binder<Node> binder;
        try{
            binder = getBinder();
        }catch(JAXBException e){
            e.printStackTrace(ref);
            return Status.failed("Unable to create valid context or binder");
        }

        binder.setSchema(schema);

        try{
            eh.setParent(binder.getEventHandler());
            binder.setEventHandler(eh);
        }catch(JAXBException e){
            e.printStackTrace(ref);
            return Status.failed("Error on setting event handler");
        }
        try{
            Object o = binder.unmarshal(getDocument("Binder.xml"));
            if(o == null){
                return Status.failed("unmarshal(\"Binder.xml\") returns null");
            }
            if(!eh.hasEvents()){
                return Status.passed("OK");
            }
            return Status.failed("No exception is thrown but some error events are handled");
        }catch(JAXBException jaxbe){
            jaxbe.printStackTrace(ref);
            if (!eh.hasEvents()) {
                return Status.failed("no error events are handled but " + jaxbe + " is thrown");
            }
            return Status.failed(jaxbe.toString());
        }catch(Exception e){
            e.printStackTrace(ref);
            return Status.failed("Exception is thrown: "+e);
        }finally{
            eh.printEvents(ref);
        }
    }

    public Status unmarshal003( ) {
        MyValidationEventHandler validationEventHandler = new MyValidationEventHandler();
        Binder<Node> binder;
        try{
            binder = getBinder();
        }catch(JAXBException e){
            e.printStackTrace(ref);
            return Status.failed("Unable to create valid context or binder");
        }

        binder.setSchema(schema);

        try{
            binder.setEventHandler(validationEventHandler);
        }catch(JAXBException e){
            e.printStackTrace(ref);
            return Status.failed("Error on setting event handler");
        }
        try{
            Object o = binder.unmarshal(getDocument("Binder_invalid.xml"));
            if(o == null){
                return Status.failed("unmarshal(\"Binder_invalid.xml\") returns null");
            }
            if(!validationEventHandler.hasEvents()){
                return Status.failed("No validation events are handled");
            }
            if(o instanceof JAXBElement){
                JAXBElement jaxbElement = (JAXBElement) o;
                if(jaxbElement.getValue() instanceof PurchaseOrder){
                    return Status.passed("Ok.");
                }
            }
            return Status.failed("Result unrecognized.");
        }catch(JAXBException jaxbe){
            jaxbe.printStackTrace(ref);
            if (!validationEventHandler.hasEvents()) {
                return Status.failed("no error events are handled but " + jaxbe + " is thrown");
            }
            return Status.failed(jaxbe.toString());
        }catch(Exception e){
            e.printStackTrace(ref);
            return Status.failed("Exception is thrown: "+e);
        }finally{
            validationEventHandler.printEvents(ref);
        }
    }

    public Status unmarshal004( ) {
        try {
            getBinderObject(null).unmarshal(getDocumentObject("unmarshal004.xml"));
            return Status.failed("expected exception UnmarshalException was not thrown");
        } catch(UnmarshalException e){
            return Status.passed("OK");
        } catch(JAXBException x){
            x.printStackTrace(ref);
            throw new RuntimeException(x);
        }
    }

    public Status unmarshal005( ) {
        try {
            getBinderObject(null).unmarshal(null);
            return Status.failed("expected exception IllegalArgumentException was not thrown");
        } catch(IllegalArgumentException e){
            return Status.passed("OK");
        } catch(JAXBException x){
            x.printStackTrace(ref);
            throw new RuntimeException(x);
        }
    }

    public Status unmarshal006( ) {
        try {
            Binder<Node> binder = getBinderObject(null);
            Document document = getDocumentObject("Binder_invalid.xml");
            binder.setSchema( null ); // disable validation (for sure)
            binder.unmarshal( document );

            binder.setSchema(schema);
            try {
                binder.unmarshal( document );
            } catch( UnmarshalException x ){
                return Status.passed("OK");
            }
            return Status.failed("expected exception UnmarshalException was not thrown");
        } catch(JAXBException x){
            x.printStackTrace(ref);
            throw new RuntimeException(x);
        }
    }

    public Status unmarshal007( ) {
        try {
            // JAXBElement<PurchaseOrder> elem =
            getBinderObject(null).unmarshal( getDocumentObject("unmarshal004.xml"),
                                            PurchaseOrder.class );
            return Status.passed("OK");
        } catch(JAXBException x){
            x.printStackTrace(ref);
            throw new RuntimeException(x);
        }
    }

    public Status unmarshal008( ) {
        try {
            Binder<Node> binder = getBinderObject(null);
            try {
                binder.unmarshal( null, PurchaseOrder.class );
                return Status.failed("expected exception IllegalArgumentException was not thrown. (null, class)");
            } catch(IllegalArgumentException e){}
            try {
                binder.unmarshal( getDocumentObject("Binder.xml"), null );
                return Status.failed("expected exception IllegalArgumentException was not thrown. (doc, null)");
            } catch(IllegalArgumentException e){}
            try {
                binder.unmarshal( null, null );
                return Status.failed("expected exception IllegalArgumentException was not thrown. (null, null)");
            } catch(IllegalArgumentException e){}
            return Status.passed("OK");
        } catch(JAXBException x){
            x.printStackTrace(ref);
            throw new RuntimeException(x);
        }
    }

    public Status unmarshal009( ) {
        try {
            Binder<Node> binder = getBinderObject(null);
            Document document = getDocumentObject("unmarshal004.xml");
            binder.setSchema( null ); // disable validation (for sure)
            binder.unmarshal( document, PurchaseOrder.class );

            binder.setSchema(schema);
            try {
                binder.unmarshal( document, PurchaseOrder.class );
            } catch( UnmarshalException x ){
                return Status.passed("OK");
            }
            return Status.failed("expected exception UnmarshalException was not thrown");
        } catch(JAXBException x){
            x.printStackTrace(ref);
            throw new RuntimeException(x);
        }
    }

    public Status marshal001( ) {
        Binder<Node> binder;
        try {
            binder = getBinder();
        } catch (JAXBException e) {
            e.printStackTrace(ref);
            return Status.failed("Unable to create valid context or binder");
        }
        //Create PurchaseOrder object
        Address shipTo = new Address();

        shipTo.setName("Alice Smith");
        shipTo.setStreet("123 Maple Street");
        shipTo.setCity("Mill Valley,CA");
        shipTo.setCountry("US");

        Items items = new Items();
        items.getItem().add("Item 1");
        items.getItem().add("Item 2");

        PurchaseOrder po = new PurchaseOrder();
        po.setShipTo(shipTo);
        po.setItems(items);

        //Create document
        DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance();
        documentBuilderFactory.setNamespaceAware(true);
        Document doc;
        try {
            doc = documentBuilderFactory.newDocumentBuilder().newDocument();
        } catch (ParserConfigurationException e) {
            e.printStackTrace(ref);
            return Status.failed("Cannot create Document due to exception: "+e);
        }
        try {
            binder.marshal((new ObjectFactory()).createPurchaseOrder(po),doc);
        } catch (JAXBException e) {
            e.printStackTrace(ref);
            return Status.failed("Exception on marshal: "+e);
        }

        Element purchaseOrderElement = doc.getDocumentElement();
        //check PurchaseOrder
        if(purchaseOrderElement == null || !"purchaseOrder".equals(purchaseOrderElement.getLocalName())){
            return Status.failed("Result unrecognized");
        }
        Node n = purchaseOrderElement.getFirstChild();
        while(n != null && n.getNodeType() != Node.ELEMENT_NODE){
            n = n.getNextSibling();
        }
        Element elem = (Element)n;
        if(elem == null || !"shipTo".equals(elem.getLocalName())){
            return Status.failed("Result unrecognized");
        }
        n = n.getNextSibling();
        while(n != null && n.getNodeType() != Node.ELEMENT_NODE){
            n = n.getNextSibling();
        }
        elem = (Element)n;
        if(elem == null || !"items".equals(elem.getLocalName())){
            return Status.failed("Result unrecognized");
        }
        return Status.passed("Ok");
    }

    public Status marshal002( ) {
        Binder<Node> binder;
        try {
            binder = getBinder();
        } catch (JAXBException e) {
            e.printStackTrace(ref);
            return Status.failed("Unable to create valid context or binder");
        }

        //Create document
        DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance();
        documentBuilderFactory.setNamespaceAware(true);
        Document doc;
        try {
            doc = documentBuilderFactory.newDocumentBuilder().newDocument();
        } catch (ParserConfigurationException e) {
            e.printStackTrace(ref);
            return Status.failed("Cannot create Document due to exception: "+e);
        }
        try {
            binder.marshal(null,doc);
            return Status.failed("Binder.marshal(null,somenode) must throw an IllegalArgumentException");
        } catch (JAXBException e) {
            e.printStackTrace(ref);
            return Status.failed("Binder.marshal(null,somenode) has thrown an JAXBException instead of IllegalArgumentException: "+e);
        } catch (IllegalArgumentException x){
            return Status.passed("Ok");
        } catch (NullPointerException npe){
            npe.printStackTrace(ref);
            return Status.failed("Binder.marshal(null,somenode) has thrown a NullPointerException instead of IllegalArgumentException: "+npe);
        }
    }

    public Status marshal003( ) {
        Binder<Node> binder;
        try {
            binder = getBinder();
        } catch (JAXBException e) {
            e.printStackTrace(ref);
            return Status.failed("Unable to create valid context or binder");
        }
        //Create PurchaseOrder object
        Address shipTo = new Address();

        shipTo.setName("Alice Smith");
        shipTo.setStreet("123 Maple Street");
        shipTo.setCity("Mill Valley,CA");
        shipTo.setCountry("US");

        Items items = new Items();
        items.getItem().add("Item 1");
        items.getItem().add("Item 2");

        PurchaseOrder po = new PurchaseOrder();
        po.setShipTo(shipTo);
        po.setItems(items);

        try {
            binder.marshal((new ObjectFactory()).createPurchaseOrder(po),null);
            return Status.failed("Binder.marshal(jaxbObject,null) must throw an IllegalArgumentException");
        } catch (JAXBException e) {
            e.printStackTrace(ref);
            return Status.failed("Binder.marshal(jaxbObject,null) has thrown an JAXBException instead of IllegalArgumentException: "+e);
        } catch (IllegalArgumentException x){
            return Status.passed("Ok");
        }  catch (NullPointerException npe){
            npe.printStackTrace(ref);
            return Status.failed("Binder.marshal(jaxbObject,null) has thrown a NullPointerException instead of IllegalArgumentException: "+npe);
        }
    }

    public Status marshal004( ) {
        MyClass my = new MyClass();

        try {
            Binder<Node> binder = getBinderObject( JAXBContext.newInstance(MyClass.class) );
            binder.marshal(new JAXBElement<MyClass>( new QName("jck-jaxb-test/Binder", "MyClass"), MyClass.class, my ),
                                            createNewDocument());
            binder.setSchema(schema);
            try {
                binder.marshal(new JAXBElement<MyClass>( new QName("jck-jaxb-test/Binder", "MyClass"), MyClass.class, my ),
                                                createNewDocument());
                return Status.failed( "expected exception MarshalException was not thrown." );
            } catch( MarshalException x ){
                return Status.passed("OK");
            }
        } catch (JAXBException e) {
            e.printStackTrace(ref);
            throw new RuntimeException(e);
        }
    }


}

