/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements. See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership. The ASF licenses this file
 * to you 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.apache.axis2.saaj;

import junit.framework.Assert;

import javax.xml.namespace.QName;
import javax.xml.soap.MessageFactory;
import javax.xml.soap.Name;
import javax.xml.soap.SOAPBody;
import javax.xml.soap.SOAPConstants;
import javax.xml.soap.SOAPElement;
import javax.xml.soap.SOAPEnvelope;
import javax.xml.soap.SOAPException;
import javax.xml.soap.SOAPFactory;
import javax.xml.soap.SOAPHeader;
import javax.xml.soap.SOAPHeaderElement;
import javax.xml.soap.SOAPMessage;
import javax.xml.soap.SOAPPart;
import javax.xml.soap.Text;

import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.w3c.dom.Document;
import org.w3c.dom.Element;

import java.util.ArrayList;
import java.util.Iterator;

@RunWith(SAAJTestRunner.class)
public class SOAPHeaderTest extends Assert {
    private MessageFactory mf = null;
    private SOAPMessage msg = null;
    private SOAPPart sp = null;
    private SOAPEnvelope envelope = null;
    private SOAPHeader hdr = null;
    private SOAPHeaderElement she1 = null;
    private SOAPHeaderElement she2 = null;

    @Validated @Test
    public void testAddHeaderElements() throws Exception {
        javax.xml.soap.SOAPMessage soapMessage =
                javax.xml.soap.MessageFactory.newInstance().createMessage();
        javax.xml.soap.SOAPEnvelope soapEnv =
                soapMessage.getSOAPPart().getEnvelope();
        javax.xml.soap.SOAPHeader header = soapEnv.getHeader();
        try {
            header.addChildElement("ebxmlms1");
        } catch (Exception e) {
            assertTrue(e instanceof SOAPException);
        }

        assertTrue(header.addChildElement("ebxmlms1", "ns-prefix",
                                          "http://test.apache.org") instanceof SOAPHeaderElement);
        ((SOAPHeaderElement)header.getFirstChild()).addTextNode("test add");


        assertTrue(header.addHeaderElement(
                soapEnv.createName("ebxmlms2", "ns-prefix", "http://test2.apache.org")) != null);
        assertTrue(header.addHeaderElement(
                new PrefixedQName("http://test3.apache.org", "ebxmlms3", "ns-prefix")) != null);


        SOAPHeaderElement firstChild = (SOAPHeaderElement)header.getFirstChild();
        assertEquals("ebxmlms1", firstChild.getLocalName());
        assertEquals("ns-prefix", firstChild.getPrefix());
        assertEquals("http://test.apache.org", firstChild.getNamespaceURI());

        SOAPHeaderElement secondChild = (SOAPHeaderElement)firstChild.getNextSibling();
        assertEquals("ebxmlms2", secondChild.getLocalName());
        assertEquals("ns-prefix", secondChild.getPrefix());
        assertEquals("http://test2.apache.org", secondChild.getNamespaceURI());

        SOAPHeaderElement lastChild = (SOAPHeaderElement)header.getLastChild();
        assertEquals("ebxmlms3", lastChild.getLocalName());
        assertEquals("ns-prefix", lastChild.getPrefix());
        assertEquals("http://test3.apache.org", lastChild.getNamespaceURI());

        SOAPHeaderElement fourthChild = (SOAPHeaderElement)lastChild.getPreviousSibling();
        assertEquals("ebxmlms2", fourthChild.getLocalName());
        assertEquals("ns-prefix", fourthChild.getPrefix());
        assertEquals("http://test2.apache.org", fourthChild.getNamespaceURI());

        Iterator it = header.getChildElements();
        int numOfHeaderElements = 0;
        while (it.hasNext()) {
            Object o = it.next();
            assertTrue(o instanceof SOAPHeaderElement);
            SOAPHeaderElement el = (SOAPHeaderElement)o;
            String lName = el.getLocalName();
            assertTrue(lName.equals("ebxmlms" + ++numOfHeaderElements));
        }
        assertEquals(3, numOfHeaderElements);
    }

    @Validated @Test
    public void testHeaders() {
        try {
            // Create message factory and SOAP factory
            MessageFactory messageFactory = MessageFactory.newInstance();
            SOAPFactory soapFactory = SOAPFactory.newInstance();

            // Create a message
            SOAPMessage message = messageFactory.createMessage();

            // Get the SOAP header from the message and
            //  add headers to it
            SOAPHeader header = message.getSOAPHeader();

            String nameSpace = "ns";
            String nameSpaceURI = "http://gizmos.com/NSURI";

            Name order =
                    soapFactory.createName("orderDesk", nameSpace, nameSpaceURI);
            SOAPHeaderElement orderHeader = header.addHeaderElement(order);
            orderHeader.setActor("http://gizmos.com/orders");

            Name shipping =
                    soapFactory.createName("shippingDesk", nameSpace, nameSpaceURI);
            SOAPHeaderElement shippingHeader =
                    header.addHeaderElement(shipping);
            shippingHeader.setActor("http://gizmos.com/shipping");

            Name confirmation =
                    soapFactory.createName("confirmationDesk", nameSpace,
                                           nameSpaceURI);
            SOAPHeaderElement confirmationHeader =
                    header.addHeaderElement(confirmation);
            confirmationHeader.setActor("http://gizmos.com/confirmations");

            Name billing =
                    soapFactory.createName("billingDesk", nameSpace, nameSpaceURI);
            SOAPHeaderElement billingHeader = header.addHeaderElement(billing);
            billingHeader.setActor("http://gizmos.com/billing");

            // Add header with mustUnderstand attribute
            Name tName =
                    soapFactory.createName("Transaction", "t",
                                           "http://gizmos.com/orders");

            SOAPHeaderElement transaction = header.addHeaderElement(tName);
            transaction.setMustUnderstand(true);
            transaction.addTextNode("5");

            // Get the SOAP body from the message but leave
            // it empty
            SOAPBody body = message.getSOAPBody();

            message.saveChanges();

            // Display the message that would be sent
            //System.out.println("\n----- Request Message ----\n");
            //message.writeTo(System.out);

            // Look at the headers
            Iterator allHeaders = header.examineAllHeaderElements();

            while (allHeaders.hasNext()) {
                SOAPHeaderElement headerElement =
                        (SOAPHeaderElement)allHeaders.next();
                Name headerName = headerElement.getElementName();
                //System.out.println("\nHeader name is " +
                //                   headerName.getQualifiedName());
                //System.out.println("Actor is " + headerElement.getActor());
                //System.out.println("mustUnderstand is " +
                //                   headerElement.getMustUnderstand());
            }
        } catch (Exception e) {
            fail("Enexpected Exception " + e);
        }
    }

    @Before
    public void setUp() throws Exception {
        msg = MessageFactory.newInstance().createMessage();
        sp = msg.getSOAPPart();
        envelope = sp.getEnvelope();
        hdr = envelope.getHeader();
    }

    @Validated @Test
    public void testExamineHeader() {
        SOAPHeaderElement she = null;

        try {
            she1 = hdr.addHeaderElement(envelope.createName("foo1", "f1", "foo1-URI"));
            she1.setActor("actor-URI");
            Iterator iterator = hdr.examineAllHeaderElements();
            int cnt = 0;
            while (iterator.hasNext()) {
                cnt++;
                she = (SOAPHeaderElement)iterator.next();
                if (!she.equals(she1)) {
                    fail("SOAP Header Elements do not match");
                }
            }

            if (cnt != 1) {
                fail("SOAPHeaderElement count mismatch: expected 1, received " + cnt);
            }

            iterator = hdr.examineAllHeaderElements();
            if (!iterator.hasNext()) {
                fail("no elements in iterator - unexpected");
            }

        } catch (Exception e) {
            fail("Unexpected Exception: " + e);
        }
    }

    @Validated @Test
    public void testAddNotUnderstoodHeaderElement() throws Exception {
        javax.xml.soap.SOAPMessage soapMessage =
                javax.xml.soap.MessageFactory.newInstance(
                        SOAPConstants.SOAP_1_2_PROTOCOL).createMessage();

        javax.xml.soap.SOAPEnvelope soapEnv =
                soapMessage.getSOAPPart().getEnvelope();
        javax.xml.soap.SOAPHeader header = soapEnv.getHeader();

        SOAPElement soapElement = header.addNotUnderstoodHeaderElement(
                new QName("http://foo.org", "foo", "f"));

        assertNotNull(soapElement);
        Name name = soapElement.getElementName();
        String uri = name.getURI();
        String localName = name.getLocalName();
        assertEquals(SOAPConstants.URI_NS_SOAP_1_2_ENVELOPE, uri);
        //Validate the LocalName which must be NotUnderstood
        assertEquals("NotUnderstood", localName);
    }


    @Validated @Test
    public void testAddUpgradeHeaderElement() throws Exception {
        javax.xml.soap.SOAPMessage soapMessage =
                javax.xml.soap.MessageFactory.newInstance(
                        SOAPConstants.SOAP_1_2_PROTOCOL).createMessage();

        javax.xml.soap.SOAPEnvelope soapEnv =
                soapMessage.getSOAPPart().getEnvelope();
        javax.xml.soap.SOAPHeader header = soapEnv.getHeader();

        // create a list of supported URIs.
        ArrayList supported = new ArrayList();
        supported.add(SOAPConstants.URI_NS_SOAP_1_2_ENVELOPE);
        supported.add(SOAPConstants.URI_NS_SOAP_ENVELOPE);

        SOAPElement soapElement = header.addUpgradeHeaderElement(supported.iterator());
        assertNotNull(soapElement);
        Name name = soapElement.getElementName();
        String uri = name.getURI();
        String localName = name.getLocalName();

        //Validate the URI which must be SOAPConstants.URI_NS_SOAP_1_2_ENVELOPE);
        assertTrue(uri.equals(SOAPConstants.URI_NS_SOAP_1_2_ENVELOPE));
        assertTrue(localName.equals("Upgrade"));
    }

    @Validated @Test
    public void testExamineHeaderElements() throws Exception {
        javax.xml.soap.SOAPMessage soapMessage =
                javax.xml.soap.MessageFactory.newInstance(
                        SOAPConstants.SOAP_1_2_PROTOCOL).createMessage();

        javax.xml.soap.SOAPEnvelope soapEnv =
                soapMessage.getSOAPPart().getEnvelope();
        javax.xml.soap.SOAPHeader header = soapEnv.getHeader();

        SOAPHeaderElement soapHeaderElement =
                header.addHeaderElement(envelope.createName("foo1", "f1", "foo1-URI"));

        Iterator iterator = null;
        soapHeaderElement.setRole("role-URI");

        iterator = header.examineHeaderElements("role1-URI");

        int count = 0;
        while (iterator.hasNext()) {
            count++;
            iterator.next();
        }
        assertEquals(0, count);
    }

    @Validated @Test
    public void testExamineHeaderElements2() throws Exception {
        javax.xml.soap.SOAPMessage soapMessage =
                javax.xml.soap.MessageFactory.newInstance().createMessage();

        javax.xml.soap.SOAPEnvelope soapEnv =
                soapMessage.getSOAPPart().getEnvelope();
        javax.xml.soap.SOAPHeader header = soapEnv.getHeader();
        SOAPHeaderElement soapHeaderElement = null;

        try {
            // Add some soap header elements
            SOAPElement se = header.addHeaderElement(
                    envelope.createName("Header1", "prefix", "http://myuri"))
                    .addTextNode("This is Header1");
            soapHeaderElement = (SOAPHeaderElement)se;
            soapHeaderElement.setMustUnderstand(true);

            se = header.addHeaderElement(
                    envelope.createName("Header2", "prefix", "http://myuri"))
                    .addTextNode("This is Header2");
            soapHeaderElement = (SOAPHeaderElement)se;
            soapHeaderElement.setMustUnderstand(false);

            se = header.addHeaderElement(
                    envelope.createName("Header3", "prefix", "http://myuri"))
                    .addTextNode("This is Header3");
            soapHeaderElement = (SOAPHeaderElement)se;
            soapHeaderElement.setMustUnderstand(true);

            se = header.addHeaderElement(
                    envelope.createName("Header4", "prefix", "http://myuri"))
                    .addTextNode("This is Header4");
            soapHeaderElement = (SOAPHeaderElement)se;
            soapHeaderElement.setMustUnderstand(false);

            Iterator iterator = header.examineAllHeaderElements();

            //validating Iterator count .... should be 4");
            int cnt = 0;
            while (iterator.hasNext()) {
                cnt++;
                soapHeaderElement = (SOAPHeaderElement)iterator.next();
            }
            assertEquals(cnt, 4);
            iterator = header.examineHeaderElements(SOAPConstants.URI_SOAP_ACTOR_NEXT);
            cnt = 0;
            while (iterator.hasNext()) {
                cnt++;
                soapHeaderElement = (SOAPHeaderElement)iterator.next();
            }
            assertEquals(cnt, 0);
        } catch (Exception e) {
            fail("Unexpected Exception: " + e);
        }
    }

    @Validated @Test
    public void testQNamesOnHeader() {
        SOAPHeaderElement headerElement = null;
        try {
            //SOAP1.1 and SOAP1.2 requires all HeaderElements to be namespace qualified
            //Try adding HeaderElement with unqualified QName not belonging to any namespace
            //(expect SOAPException)
            headerElement = hdr.addHeaderElement(envelope.createName("Transaction"));
            fail("Did not throw expected SOAPException");
        } catch (SOAPException e) {
            //Did throw expected SOAPException
        } catch (Exception e) {
            fail("Unexpected Exception: " + e.getMessage());
        }
    }
    
    @Validated @Test
    public void testAppendChild() throws Exception {
        MessageFactory fact = MessageFactory.newInstance();
        SOAPMessage message = fact.createMessage();
        SOAPHeader soapHeader = message.getSOAPHeader();
        
        assertEquals(0, soapHeader.getChildNodes().getLength());
        assertFalse(soapHeader.getChildElements().hasNext());
        
        Document doc = soapHeader.getOwnerDocument();        
        String namespace = "http://example.com";
        String localName = "GetLastTradePrice";
        Element getLastTradePrice = doc.createElementNS(namespace, localName);        
        Element symbol = doc.createElement("symbol");
        symbol.setAttribute("foo", "bar");
        getLastTradePrice.appendChild(symbol);
        org.w3c.dom.Text def = doc.createTextNode("DEF");
        symbol.appendChild(def);
                        
        soapHeader.appendChild(getLastTradePrice);
                
        assertEquals(1, soapHeader.getChildNodes().getLength());
        Iterator iter = soapHeader.getChildElements();
        assertTrue(iter.hasNext()); 
        Object obj = iter.next();
        // must be SOAPHeaderElement
        assertTrue(obj instanceof SOAPHeaderElement);  
        SOAPElement soapElement = (SOAPElement)obj;
        assertEquals(namespace, soapElement.getNamespaceURI());
        assertEquals(localName, soapElement.getLocalName());
        
        iter = soapElement.getChildElements();
        assertTrue(iter.hasNext()); 
        obj = iter.next();
        assertTrue(obj instanceof SOAPElement);  
        soapElement = (SOAPElement)obj;
        assertEquals(null, soapElement.getNamespaceURI());
        assertEquals("symbol", soapElement.getLocalName());
        assertFalse(iter.hasNext());
        
        iter = soapElement.getChildElements();
        assertTrue(iter.hasNext()); 
        obj = iter.next();
        assertTrue(obj instanceof Text);  
        Text text = (Text)obj;
        assertEquals("DEF", text.getData());
        assertFalse(iter.hasNext());   
    }
    
    @Validated @Test
    public void testAXIS2_5006() throws Exception {
        MessageFactory fact = MessageFactory.newInstance();
        SOAPMessage message = fact.createMessage();
        SOAPHeader header = message.getSOAPHeader();
        
        Document doc = message.getSOAPPart();
        Element orgElement = doc.createElementNS("urn:ns", "ns:test");
        orgElement.appendChild(doc.createElementNS(null, "child1"));
        orgElement.appendChild(doc.createElementNS(null, "child2"));
        orgElement.appendChild(doc.createElementNS(null, "child3"));
        header.appendChild(orgElement);
        
        SOAPElement element = (SOAPElement)header.getChildElements().next();
        assertTrue(element instanceof SOAPHeaderElement);
        assertEquals(3, element.getChildNodes().getLength());
    }
    
    @Validated @Test
    public void testExtractAllHeaderElements() throws Exception {
        MessageFactory fact = MessageFactory.newInstance();
        SOAPMessage message = fact.createMessage();
        SOAPHeader header = message.getSOAPHeader();
        SOAPHeaderElement headerElement1 = header.addHeaderElement(new QName("urn:ns1", "test1", "p1"));
        SOAPHeaderElement headerElement2 = header.addHeaderElement(new QName("urn:ns2", "test2", "p2"));
        Iterator it = header.extractAllHeaderElements();
        assertTrue(it.hasNext());
        assertSame(headerElement1, it.next());
        assertTrue(it.hasNext());
        assertSame(headerElement2, it.next());
        assertFalse(it.hasNext());
        assertEquals(0, header.getChildNodes().getLength());
    }
}