/*
* JBoss, Home of Professional Open Source.
* See the COPYRIGHT.txt file distributed with this work for information
* regarding copyright ownership. Some portions may be licensed
* to Red Hat, Inc. under one or more contributor license agreements.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301 USA.
*/
package org.teiid.query.function.source;
import static org.junit.Assert.*;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.io.StringReader;
import java.nio.charset.Charset;
import java.sql.SQLException;
import java.sql.SQLXML;
import java.util.TimeZone;
import javax.sql.rowset.serial.SerialBlob;
import javax.sql.rowset.serial.SerialClob;
import javax.sql.rowset.serial.SerialException;
import javax.xml.stream.EventFilter;
import javax.xml.stream.XMLEventReader;
import javax.xml.stream.XMLEventWriter;
import javax.xml.stream.XMLOutputFactory;
import javax.xml.stream.events.EndElement;
import javax.xml.stream.events.XMLEvent;
import net.sf.saxon.trans.XPathException;
import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.junit.Test;
import org.teiid.common.buffer.BufferManagerFactory;
import org.teiid.core.TeiidComponentException;
import org.teiid.core.TeiidProcessingException;
import org.teiid.core.types.SQLXMLImpl;
import org.teiid.core.types.XMLType;
import org.teiid.core.util.ObjectConverterUtil;
import org.teiid.core.util.UnitTestUtil;
import org.teiid.query.unittest.TimestampUtil;
import org.teiid.query.util.CommandContext;
@SuppressWarnings("nls")
public class TestXMLSystemFunctions {
public String getContentOfTestFile( final String testFilePath ) throws IOException {
final File file = UnitTestUtil.getTestDataFile(testFilePath);
return ObjectConverterUtil.convertFileToString(file);
}
public String helpTestXpathValue(final String xmlFilePath, final String xpath, final String expected) throws XPathException, TeiidProcessingException, IOException {
final String actual = helpGetNode(xmlFilePath,xpath);
assertEquals(expected,actual);
return actual;
}
public String helpGetNode(final String xmlFilePath, final String xpath ) throws XPathException, TeiidProcessingException, IOException {
final String xmlContent = getContentOfTestFile(xmlFilePath);
return XMLSystemFunctions.xpathValue(xmlContent,xpath);
}
@Test public void testElement() throws Exception {
String doc = "<?xml version=\"1.0\" encoding=\"utf-8\" ?><a><b><c>test</c></b></a>"; //$NON-NLS-1$
String xpath = "a/b/c"; //$NON-NLS-1$
String value = XMLSystemFunctions.xpathValue(doc, xpath);
assertEquals("test", value); //$NON-NLS-1$
}
@Test public void testAttribute() throws Exception {
String doc = "<?xml version=\"1.0\" encoding=\"utf-8\" ?><a><b c=\"test\"></b></a>"; //$NON-NLS-1$
String xpath = "a/b/@c"; //$NON-NLS-1$
String value = XMLSystemFunctions.xpathValue(doc, xpath);
assertEquals("test", value); //$NON-NLS-1$
}
@Test public void testText() throws Exception {
String doc = "<?xml version=\"1.0\" encoding=\"utf-8\" ?><a><b><c>test</c></b></a>"; //$NON-NLS-1$
String xpath = "a/b/c/text()"; //$NON-NLS-1$
String value = XMLSystemFunctions.xpathValue(doc, xpath);
assertEquals("test", value); //$NON-NLS-1$
}
@Test public void testNoMatch() throws Exception {
String doc = "<?xml version=\"1.0\" encoding=\"utf-8\" ?><a><b><c>test</c></b></a>"; //$NON-NLS-1$
String xpath = "x"; //$NON-NLS-1$
String value = XMLSystemFunctions.xpathValue(doc, xpath);
assertEquals(null, value);
}
@Test public void testNoXMLHeader() throws Exception {
String doc = "<a><b><c>test</c></b></a>"; //$NON-NLS-1$
String xpath = "a/b/c/text()"; //$NON-NLS-1$
String value = XMLSystemFunctions.xpathValue(doc, xpath);
assertEquals("test", value); //$NON-NLS-1$
}
// simulate what would happen if someone passed the output of an XML query to the xpathvalue function
@Test public void testXMLInput() throws Exception {
XMLType doc = new XMLType(new SQLXMLImpl("<foo/>"));//$NON-NLS-1$
String xpath = "a/b/c"; //$NON-NLS-1$
String value = XMLSystemFunctions.xpathValue(doc, xpath);
assertNull(value);
}
@Test(expected=XPathException.class) public void testBadXPath() throws Exception {
String doc = "<?xml version=\"1.0\" encoding=\"utf-8\" ?><a><b><c>test</c></b></a>"; //$NON-NLS-1$
String xpath = ":BOGUS:"; //$NON-NLS-1$
XMLSystemFunctions.xpathValue(doc, xpath);
}
@Test(expected=XPathException.class) public void testValidateXpath_Defect15088() throws Exception {
// Mismatched tick and quote
final String xpath = "//*[local-name()='bookName\"]"; //$NON-NLS-1$
XMLSystemFunctions.validateXpath(xpath);
}
@Test public void testValidateXpath_null() throws Exception {
XMLSystemFunctions.validateXpath(null);
}
@Test public void testValidateXpath_valid() throws Exception {
XMLSystemFunctions.validateXpath("//shipTo/@country"); //$NON-NLS-1$
}
@Test public void testGetSingleMatch_01_001() throws Exception {
final String xmlFilePath = "testdoc.xml"; //$NON-NLS-1$
final String xpath = "//shipTo/@country"; //$NON-NLS-1$
final String expectedValue = "US"; //$NON-NLS-1$
helpTestXpathValue(xmlFilePath,xpath, expectedValue);
}
@Test public void testGetSingleMatch_01_002() throws Exception {
final String xmlFilePath = "testdoc.xml"; //$NON-NLS-1$
final String xpath = "//@partNum"; //$NON-NLS-1$
final String expectedValue = "872-AA"; //$NON-NLS-1$
helpTestXpathValue(xmlFilePath,xpath, expectedValue);
}
@Test public void testGetSingleMatch_01_003() throws Exception {
final String xmlFilePath = "testdoc.xml"; //$NON-NLS-1$
final String xpath = "//productName"; //$NON-NLS-1$
final String expectedValue = "Lawnmower"; //$NON-NLS-1$
helpTestXpathValue(xmlFilePath,xpath, expectedValue);
}
@Test public void testGetSingleMatch_03() throws Exception {
final String xmlFilePath = "testdoc.xml"; //$NON-NLS-1$
final String xpath = "//*[local-name()=\"ReadOnly\"]"; //$NON-NLS-1$
helpTestXpathValue(xmlFilePath,xpath, "false"); //$NON-NLS-1$
}
/**
* * is no longer valid to match the namespace
*/
@Test public void testGetSingleMatch_04() throws Exception {
final String xmlFilePath = "testdoc.xml"; //$NON-NLS-1$
final String xpath = "//*:ReadOnly"; //$NON-NLS-1$
helpTestXpathValue(xmlFilePath,xpath, "false"); //$NON-NLS-1$
}
@Test public void testAtomicValueTimestamp() throws Exception {
assertEquals("1910-04-01T07:01:02.000055Z", XMLSystemFunctions.convertToAtomicValue(TimestampUtil.createTimestamp(10, 3, 1, 1, 1, 2, 55001)).getStringValue());
}
@Test public void testAtomicValueTime() throws Exception {
assertEquals("16:03:01Z", XMLSystemFunctions.convertToAtomicValue(TimestampUtil.createTime(10, 3, 1)).getStringValue());
}
@Test public void testAtomicValueDate() throws Exception {
assertEquals("1920-03-03Z", XMLSystemFunctions.convertToAtomicValue(TimestampUtil.createDate(20, 2, 3)).getStringValue());
}
@Test public void testNameEscaping() throws Exception {
assertEquals("_u003A_b", XMLSystemFunctions.escapeName(":b", true));
}
@Test public void testNameEscaping1() throws Exception {
assertEquals("a_u005F_x", XMLSystemFunctions.escapeName("a_x", true));
}
@Test public void testNameEscaping2() throws Exception {
assertEquals("_u000A_", XMLSystemFunctions.escapeName(new String(new char[] {10}), true));
}
@Test public void testJsonToXml() throws Exception {
String json = "[0,{\"1\":{\"2\":{\"3\":{\"4\":[5,{\"6\":7}]}}}}]";
String expected = "<?xml version=\"1.0\" encoding=\"UTF-8\"?><Array xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"><Array xsi:type=\"decimal\">0</Array><Array><_u0031_><_u0032_><_u0033_><_u0034_ xsi:type=\"decimal\">5</_u0034_><_u0034_><_u0036_ xsi:type=\"decimal\">7</_u0036_></_u0034_></_u0033_></_u0032_></_u0031_></Array></Array>";
helpTestJson(json, "Array", expected);
}
private void helpTestJson(String json, String rootName, String expected)
throws SQLException, TeiidComponentException,
TeiidProcessingException, SerialException, IOException {
CommandContext cc = new CommandContext();
cc.setBufferManager(BufferManagerFactory.getStandaloneBufferManager());
SQLXML xml = XMLSystemFunctions.jsonToXml(cc, rootName, new SerialClob(json.toCharArray()));
assertEquals(expected, xml.getString());
xml = XMLSystemFunctions.jsonToXml(cc, rootName, new SerialBlob(json.getBytes(Charset.forName("UTF-8"))));
assertEquals(expected, xml.getString());
xml = XMLSystemFunctions.jsonToXml(cc, rootName, new SerialBlob(json.getBytes(Charset.forName("UTF-32BE"))));
assertEquals(expected, xml.getString());
}
@Test public void testJsonToXml1() throws Exception {
String json = "{ \"firstName\": \"John\", \"lastName\": \"Smith\", \"age\": 25, \"address\": { \"streetAddress\": \"21 2nd Street\", \"city\": \"New York\", \"state\": \"NY\", "+
"\"postalCode\": \"10021\" }, \"phoneNumber\": [ { \"type\": \"home\", \"number\": \"212 555-1234\" }, { \"type\": \"fax\", \"number\": \"646 555-4567\" } ] }";
String expected = "<?xml version=\"1.0\" encoding=\"UTF-8\"?><Person xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"><firstName>John</firstName><lastName>Smith</lastName><age xsi:type=\"decimal\">25</age><address><streetAddress>21 2nd Street</streetAddress><city>New York</city><state>NY</state><postalCode>10021</postalCode></address><phoneNumber><type>home</type><number>212 555-1234</number></phoneNumber><phoneNumber><type>fax</type><number>646 555-4567</number></phoneNumber></Person>";
helpTestJson(json, "Person", expected);
}
@Test public void testJsonToXml2() throws Exception {
String json = "{ \"firstName\": null }";
String expected = "<?xml version=\"1.0\" encoding=\"UTF-8\"?><Person xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"><firstName xsi:nil=\"true\"></firstName></Person>";
helpTestJson(json, "Person", expected);
}
@Test public void testJsonToXml3() throws Exception {
String json = "{ \"kids\":[{ \"firstName\" : \"George\" }, { \"firstName\" : \"Jerry\" }]}";
String expected = "<?xml version=\"1.0\" encoding=\"UTF-8\"?><Person xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"><kids><firstName>George</firstName></kids><kids><firstName>Jerry</firstName></kids></Person>";
helpTestJson(json, "Person", expected);
}
@Test public void testJsonToXml4() throws Exception {
String json = "{ \"kids\":[[{ \"firstName\" : \"George\" }, { \"firstName\" : \"Jerry\" }]]}";
String expected = "<?xml version=\"1.0\" encoding=\"UTF-8\"?><Person xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"><kids><kids><firstName>George</firstName></kids><kids><firstName>Jerry</firstName></kids></kids></Person>";
helpTestJson(json, "Person", expected);
}
@Test public void testJsonToXml4a() throws Exception {
String json = "{ \"kids\":[[{ \"firstName\" : \"George\" }], [{ \"firstName\" : \"Jerry\" }]]}";
String expected = "<?xml version=\"1.0\" encoding=\"UTF-8\"?><Person xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"><kids><kids><firstName>George</firstName></kids></kids><kids><kids><firstName>Jerry</firstName></kids></kids></Person>";
helpTestJson(json, "Person", expected);
}
/**
* This shows an ambiguity with the approach in that array/object children of an array cannot be distinguished
* @throws Exception
*/
@Test public void testJsonToXml5() throws Exception {
String json = "[[],{\"x\": 1},[]]";
String expected = "<?xml version=\"1.0\" encoding=\"UTF-8\"?><Person xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"><Person></Person><Person><x xsi:type=\"decimal\">1</x></Person><Person></Person></Person>";
helpTestJson(json, "Person", expected);
}
@Test public void testRepairingNamespaces() throws Exception {
XMLOutputFactory factory = XMLSystemFunctions.getOutputFactory(true);
ByteArrayOutputStream baos = new ByteArrayOutputStream();
XMLEventWriter writer = factory.createXMLEventWriter(baos);
XMLEventReader reader = XMLType.getXmlInputFactory().createXMLEventReader(new StringReader("<a xmlns:x=\"http://foo\"><b x:y=\"1\"/></a>"));
reader.nextTag();
reader = XMLType.getXmlInputFactory().createFilteredReader(reader, new EventFilter() {
@Override
public boolean accept(XMLEvent arg0) {
if (arg0.isStartDocument() || arg0.isEndDocument()) {
return false;
}
if (arg0.isEndElement() && ((EndElement)arg0).getName().getLocalPart().equals("a")) {
return false;
}
return true;
}
});
writer.add(reader);
writer.close();
assertEquals("<b xmlns=\"\" xmlns:x=\"http://foo\" x:y=\"1\"></b>", new String(baos.toByteArray(), "UTF-8"));
}
@BeforeClass static public void setUpOnce() {
TimeZone.setDefault(TimeZone.getTimeZone("GMT-6:00"));
}
@AfterClass static public void tearDownOnce() {
TimeZone.setDefault(null);
}
}