/*
 * 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.servicemix.jbi.jaxp;

import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.io.StringWriter;

import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.stream.XMLInputFactory;
import javax.xml.stream.XMLStreamReader;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMResult;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.sax.SAXSource;
import javax.xml.transform.stream.StreamResult;
import javax.xml.transform.stream.StreamSource;

import org.w3c.dom.Document;
import org.w3c.dom.NodeList;
import org.w3c.dom.Text;

import junit.framework.TestCase;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

public class StaxSourceTest extends TestCase {

    private static final Log LOG = LogFactory.getLog(StaxSourceTest.class);

    public void testStaxSourceOnStream() throws Exception {
        InputStream is = getClass().getResourceAsStream("test.xml");
        XMLInputFactory factory = XMLInputFactory.newInstance();
        factory.setProperty(XMLInputFactory.IS_COALESCING, Boolean.FALSE);
        XMLStreamReader xsr = factory.createXMLStreamReader(is);
        StaxSource ss = new StaxSource(xsr);
        StringWriter buffer = new StringWriter();
        Transformer transformer = TransformerFactory.newInstance().newTransformer();
        transformer.transform(ss, new StreamResult(buffer));
        LOG.info(buffer.toString());

        /*
         * Attribute ordering is not preserved, so we can not compare the
         * strings
         * 
         * is = getClass().getResourceAsStream("test.xml");
         * ByteArrayOutputStream baos = new ByteArrayOutputStream();
         * FileUtil.copyInputStream(is, baos);
         * compare(baos.toString().replaceAll("\r", ""),
         * buffer.toString().replaceAll("\r", ""));
         */

        DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
        dbf.setNamespaceAware(true);
        Document doc = dbf.newDocumentBuilder().parse(new ByteArrayInputStream(buffer.toString().getBytes()));
        checkDomResult(doc);

        StringWriter buffer2 = new StringWriter();
        transformer.transform(new DOMSource(doc), new StreamResult(buffer2));
        LOG.info(buffer2.toString());
    }

    public void testStaxSourceOnDOM() throws Exception {
        InputStream is = getClass().getResourceAsStream("test.xml");
        XMLStreamReader xsr = XMLInputFactory.newInstance().createXMLStreamReader(is);
        StaxSource ss = new StaxSource(xsr);
        Transformer transformer = TransformerFactory.newInstance().newTransformer();
        DOMResult result = new DOMResult();
        transformer.transform(ss, result);
        assertNotNull(result.getNode());
        checkDomResult((Document) result.getNode());
    }

    public void testStaxToDOM() throws Exception {
        InputStream is = getClass().getResourceAsStream("test.xml");
        XMLStreamReader xsr = XMLInputFactory.newInstance().createXMLStreamReader(is);
        StaxSource ss = new StaxSource(xsr);
        DOMSource src = new SourceTransformer().toDOMSource(ss);
        assertNotNull(src);
        assertNotNull(src.getNode());
        checkDomResult((Document) src.getNode());
    }

    public void testEncoding() throws Exception {
        final String msg = "<?xml version=\"1.0\" encoding=\"ISO-8859-1\"?><hello>едц</hello>";
        StringSource src = new StringSource(msg);
        DOMSource dom = new SourceTransformer().toDOMSource(src);
        StreamSource stream = new SourceTransformer().toStreamSource(dom);
        LOG.info(new SourceTransformer().toString(stream));
        SAXSource sax = new SourceTransformer().toSAXSource(dom);
        LOG.info(new SourceTransformer().toString(sax));
    }

    protected void checkDomResult(Document doc) {
        // Whitespace only elements must be preserved
        NodeList l = doc.getElementsByTagName("child4");
        assertEquals(1, l.getLength());
        assertEquals(1, l.item(0).getChildNodes().getLength());
        Text txt = (Text) l.item(0).getFirstChild();
        assertEquals("   ", txt.getTextContent());

        // Check long string
        l = doc.getDocumentElement().getElementsByTagName("long");
        assertEquals(1, l.getLength());
        assertEquals(1, l.item(0).getChildNodes().getLength());
        txt = (Text) l.item(0).getFirstChild();
        StringBuffer expected = new StringBuffer();
        for (int i = 0; i < 4; i++) {
            for (int j = 0; j < 10; j++) {
                for (int k = 0; k < 10; k++) {
                    expected.append((char) ('0' + j));
                    expected.append((char) ('0' + k));
                    if (k != 9) {
                        expected.append(' ');
                    }
                }
                expected.append("\n");
            }
        }
        assertEquals(expected.toString(), txt.getTextContent());
    }

    protected void compare(String s1, String s2) {
        char[] c1 = s1.toCharArray();
        char[] c2 = s2.toCharArray();
        for (int i = 0; i < c1.length; i++) {
            if (c1[i] != c2[i]) {
                fail("Expected '" + (int) c2[i] + "' but found '" + (int) c1[i] + "' at index " + i + ". Expected '" + build(c2, i)
                                + "' but found '" + build(c1, i) + "'.");
            }
        }
    }

    protected String build(char[] c, int i) {
        int min = Math.max(0, i - 10);
        int cnt = Math.min(20, c.length - min);
        return new String(c, min, cnt);
    }

}
