package com.bagri.xqj; import static org.junit.Assert.*; import java.io.ByteArrayOutputStream; import java.io.OutputStream; import java.io.StringWriter; import java.io.UnsupportedEncodingException; import java.io.Writer; import java.util.Properties; import javax.xml.namespace.QName; import javax.xml.xquery.XQConnection; import javax.xml.xquery.XQConstants; import javax.xml.xquery.XQDataSource; import javax.xml.xquery.XQException; import javax.xml.xquery.XQExpression; import javax.xml.xquery.XQItem; import javax.xml.xquery.XQItemType; import javax.xml.xquery.XQMetaData; import javax.xml.xquery.XQPreparedExpression; import javax.xml.xquery.XQSequence; import javax.xml.xquery.XQStaticContext; import org.junit.After; import org.junit.Before; import org.junit.BeforeClass; import org.junit.Test; public class BagriXQSequenceTest { private XQConnection xqc; @Before public void setUp() throws Exception { XQDataSource xqds = new BagriXQDataSource(); xqds.setProperty(BagriXQDataSource.ADDRESS, "localhost:10500"); xqds.setProperty(BagriXQDataSource.SCHEMA, "default"); xqds.setProperty(BagriXQDataSource.USER, "guest"); xqds.setProperty(BagriXQDataSource.PASSWORD, "password"); xqds.setProperty(BagriXQDataSource.XQ_PROCESSOR, "com.bagri.xquery.saxon.XQProcessorClient"); xqds.setProperty(BagriXQDataSource.XDM_REPOSITORY, "com.bagri.client.hazelcast.impl.SchemaRepositoryImpl"); xqc = xqds.getConnection(); } @After public void tearDown() throws Exception { if (xqc != null) xqc.close(); } @Test public void testGetItem() throws XQException { XQExpression xqe = xqc.createExpression(); XQSequence xqs = xqe.executeQuery("1,2,3,4"); xqs.next(); xqs.close(); try { xqs.getItem(); fail("A-XQS-1.2: closed sequence supports getItem()"); } catch (XQException e) { // Expect an XQException } xqe.close(); } @Test public void testWriteSequence() throws XQException { XQExpression xqe = xqc.createExpression(); XQSequence xqs = xqe.executeQuery("<e>Hello world!</e>"); StringWriter result = new StringWriter(); Properties prop = new Properties(); prop.setProperty("encoding", "UTF-8"); try { xqs.writeSequence(result, prop); } catch (XQException e) { fail("A-XQS-24.1: writeSequence failed with message: " + e.getMessage()); } assertTrue("A-XQS-24.1: Expects serialized result contains '<e>Hello world!</e>', but it is '" + result.toString() + "'", result.toString().indexOf("<e>Hello world!</e>") != -1); xqe.close(); } @Test public void testGetBoolean() throws XQException { XQExpression xqe = xqc.createExpression(); XQSequence xqs = xqe.executeQuery("xs:boolean('true')"); xqs.next(); xqs.getItem(); try { xqs.getBoolean(); fail("A-XQIA-1.4: SCROLLTYPE_FORWARD_ONLY sequence supports getting item twice()"); } catch (XQException e) { // Expect an XQException } xqe.close(); } @Test public void testBindSequence() throws XQException { XQExpression xqe = xqc.createExpression(); boolean failed = false; try { XQSequence xqs = xqe.executeQuery("<e>Hello world!</e>"); XQItem xqitem = xqc.createItemFromDocument(xqs.getSequenceAsStream(), xqc.createAtomicType(XQItemType.XQBASETYPE_BOOLEAN)); // conversion succeeded, we're having implementation defined behaviour // but at least the XDM instance must be of the right type. if (xqitem.getItemType().getItemKind() != XQItemType.XQITEMKIND_ATOMIC) failed = true; if (xqitem.getItemType().getBaseType() != XQItemType.XQBASETYPE_BOOLEAN) failed = true; } catch (XQException e) { // Expect an XQException } if (failed) fail("A-XQDF-1.3: The conversion is subject to the following constraints. Either it fails with an XQException, either it is successful in which case it must result in an instance of XDT."); XQItem xqi = null; try { XQSequence xqs = xqe.executeQuery("<e>Hello world!</e>"); xqi = xqc.createItemFromDocument(xqs.getSequenceAsStream(), null); } catch (XQException e) { fail("A-XQDF-1.5: createItemFromDocument() failed with message: " + e.getMessage()); } String result = xqi.getItemAsString(null); assertTrue("A-XQDF-1.5: Expects serialized result contains '<e>Hello world!</e>', but it is '" + result + "'", result.indexOf("<e>Hello world!</e>") != -1); xqe.close(); } @Test public void testBindSequence2() throws XQException { XQPreparedExpression xqpe; // Create an XQSequence, which we will use subsequently to test bindSequence() XQExpression xqe = xqc.createExpression(); XQSequence xqs = xqc.createSequence(xqe.executeQuery("'Hello world!'")); xqe.close(); xqs.beforeFirst(); xqpe = xqc.prepareExpression("declare variable $v external; $v"); xqpe.close(); try { xqpe.bindSequence(new QName("v"), xqs); fail("A-XQDC-1.1: bindSequence() throws an XQException when the dynamic context is in closed state."); } catch (XQException e) { // Expect an XQException } xqs.beforeFirst(); xqpe = xqc.prepareExpression("declare variable $v external; $v"); try { xqpe.bindSequence(null, xqs); fail("A-XQDC-1.2: null argument is invalid and throws an XQException."); } catch (XQException e) { // Expect an XQException } xqpe.close(); xqs.beforeFirst(); xqpe = xqc.prepareExpression("declare variable $v external; $v"); try { xqpe.bindSequence(new QName("foo"), xqs); fail("A-XQDC-1.5: The bound variable must be declared external in the prepared expression."); } catch (XQException e) { // Expect an XQException } xqpe.close(); xqs.beforeFirst(); xqpe = xqc.prepareExpression("declare variable $v as xs:decimal external; $v"); try { xqpe.bindSequence(new QName("v"), xqs); xqpe.executeQuery().getSequenceAsString(null); fail("A-XQDC-1.6: The dynamic type of the bound value is not compatible with the static type of the variable and must fail."); } catch (XQException e) { // Expect an XQException } xqpe.close(); xqs.beforeFirst(); xqpe = xqc.prepareExpression("declare variable $v external; $v"); try { xqpe.bindSequence(new QName("v"),xqs); } catch (XQException e) { fail("A-XQDC-1.7: bindSequence() failed with message: " + e.getMessage()); } XQSequence xqs2 = xqpe.executeQuery(); xqs2.next(); assertEquals("A-XQDC-1.7: Successful bindXXX.", "Hello world!", xqs2.getAtomicValue()); xqpe.close(); xqs.close(); xqpe = xqc.prepareExpression("declare variable $v as xs:decimal external; $v"); try { xqpe.bindSequence(new QName("v"), xqs); fail("A-XQDC-1.8: Passing a closed XQItem or XQSequence object as argument must result in an XQException."); } catch (XQException e) { // Expect an XQException } xqpe.close(); } @Test public void testCreateSequence() throws XQException { XQPreparedExpression xqpe = xqc.prepareExpression("'Hello world!'"); XQSequence xqsresult = null; XQSequence xqs = xqpe.executeQuery(); try { xqsresult = xqc.createSequence(xqs); } catch (XQException e) { fail("A-XQDF-1.5: createSequence() failed with message: " + e.getMessage()); } String result = xqsresult.getSequenceAsString(null); assertTrue("A-XQDF-1.5: Expects serialized result contains 'Hello world!', but it is '" + result + "'", result.indexOf("Hello world!") != -1); } @Test public void testFirst() throws XQException { XQExpression xqe; XQSequence xqs; boolean b = false; xqe = xqc.createExpression(); xqs = xqe.executeQuery("1,2,3,4"); try { xqs.first(); fail("A-XQS-1.1: SCROLLTYPE_FORWARD_ONLY sequence supports first()"); } catch (XQException e) { // Expect an XQException } xqe.close(); XQStaticContext xqsc = xqc.getStaticContext(); xqsc.setScrollability(XQConstants.SCROLLTYPE_SCROLLABLE); xqc.setStaticContext(xqsc); xqe = xqc.createExpression(); xqs = xqe.executeQuery("1,2,3,4"); xqs.close(); try { xqs.first(); fail("A-XQS-1.2: closed sequence supports first()"); } catch (XQException e) { // Expect an XQException } xqe.close(); xqe = xqc.createExpression(); xqs = xqe.executeQuery("1,2,3,4"); try { b = xqs.first(); } catch (XQException e) { fail("A-XQS-11.1: first() failed with message: " + e.getMessage()); } assertEquals("A-XQS-11.1: first() failed", true, b); assertEquals("A-XQS-11.1: first() failed", 1, xqs.getInt()); xqe.close(); xqe = xqc.createExpression(); xqs = xqe.executeQuery("()"); try { b = xqs.first(); } catch (XQException e) { fail("A-XQS-11.2: first() failed with message: " + e.getMessage()); } assertEquals("A-XQS-11.2: first() failed", false, b); xqe.close(); } @Test public void testBindDocument() throws XQException { XQPreparedExpression xqpe; xqpe = xqc.prepareExpression("declare variable $v external; $v"); xqpe.close(); try { xqpe.bindDocument(new QName("v"), "<e>Hello world!</e>", null, null); fail("A-XQDC-1.1: bindDocument() throws an XQException when the dynamic context is in closed state."); } catch (XQException e) { // Expect an XQException } xqpe = xqc.prepareExpression("declare variable $v external; $v"); try { xqpe.bindDocument(null, "<e>Hello world!</e>", null, null); fail("A-XQDC-1.2: null argument is invalid and throws an XQException."); } catch (XQException e) { // Expect an XQException } xqpe.close(); boolean bindFailed = false; xqpe = xqc.prepareExpression("declare variable $v external; $v"); try { xqpe.bindDocument(new QName("v"), "<e>Hello world!</e>", null, xqc.createAtomicType(XQItemType.XQBASETYPE_BOOLEAN)); } catch (XQException e) { bindFailed = true; // Expect an XQException } if (!bindFailed) { XQSequence xqs = xqpe.executeQuery(); xqs.next(); if (xqs.getItemType().getItemKind() != XQItemType.XQITEMKIND_ATOMIC) { fail("A-XQDC-1.3: The conversion is subject to the following constraints. Either it fails with an XQException, either it is successful in which case it must result in an instance of XDT."); } if (xqs.getItemType().getBaseType() != XQItemType.XQBASETYPE_BOOLEAN) { fail("A-XQDC-1.3: The conversion is subject to the following constraints. Either it fails with an XQException, either it is successful in which case it must result in an instance of XDT."); } } xqpe.close(); xqpe = xqc.prepareExpression("declare variable $v external; $v"); try { xqpe.bindDocument(new QName("v"), "<e>", null, null); xqpe.executeQuery().getSequenceAsString(null); fail("A-XQDC-1.4: The conversion of the value to an XDM instance must fail."); } catch (XQException e) { // Expect an XQException } xqpe.close(); xqpe = xqc.prepareExpression("declare variable $v external; $v"); try { xqpe.bindDocument(new QName("foo"), "<e>Hello world!</e>", null, null); fail("A-XQDC-1.5: The bound variable must be declared external in the prepared expression."); } catch (XQException e) { // Expect an XQException } xqpe.close(); xqpe = xqc.prepareExpression("declare variable $v as xs:decimal external; $v"); try { xqpe.bindDocument(new QName("v"), "<e>Hello world!</e>", null, null); xqpe.executeQuery().getSequenceAsString(null); fail("A-XQDC-1.6: The dynamic type of the bound value is not compatible with the static type of the variable and must fail."); } catch (XQException e) { // Expect an XQException } xqpe.close(); xqpe = xqc.prepareExpression("declare variable $v external; $v"); try { xqpe.bindDocument(new QName("v"), "<e>Hello world!</e>", null, null); } catch (XQException e) { fail("A-XQDC-1.7: bindDocument() failed with message: " + e.getMessage()); } String result = xqpe.executeQuery().getSequenceAsString(null); assertTrue("A-XQDC-1.7: Expects serialized result contains '<e>Hello world!</e>', but it is '" + result + "'", result.indexOf("<e>Hello world!</e>") != -1); xqpe.close(); } @Test public void testStaticTypingSupport() throws XQException { XQMetaData xqmd = xqc.getMetaData(); boolean supportsStaticTyping = true; try { supportsStaticTyping = xqmd.isStaticTypingFeatureSupported(); } catch (XQException e) { fail("A-XQMD-1.2: XQMetaData method failed with message: " + e.getMessage()); } if (supportsStaticTyping) { try { // According to the XQuery Formal Semantics, the following query // must result in a static type error. xqc.prepareExpression("declare variable $v as item() external; $v + 1"); fail("A-XQMD-4.1: XQMetaData reports support for static typing feature, successfully generate a static type error."); } catch (XQException e) { // Expect an xQException } } } @Test public void testWriteSequence_OutputStream() throws XQException, UnsupportedEncodingException { // We don't expect this method ever to throw // UnsupportedEncodingException, as we only request the "UTF-8" // encoding. // However, in order to make the compiler happy, and to play it safe, // add UnsupportedEncodingException to the throws clause. XQExpression xqe; XQSequence xqs; Properties prop = new Properties(); prop.setProperty("encoding", "UTF-8"); xqe = xqc.createExpression(); xqs = xqe.executeQuery("<e>Hello world!</e>"); xqs.close(); try { xqs.writeSequence(new ByteArrayOutputStream(), prop); fail("A-XQS-1.2: closed sequence supports writeSequence()"); } catch (XQException e) { // Expect an XQException } xqe.close(); xqe = xqc.createExpression(); xqs = xqe.executeQuery("<e>Hello world!</e>"); xqs.next(); xqs.getItem(); try { xqs.writeSequence(new ByteArrayOutputStream(), prop); fail("A-XQS-21.2: SCROLLTYPE_FORWARD_ONLY sequence, getXXX() or write() method has been invoked already on the current item"); } catch (XQException e) { // Expect an XQException } xqe.close(); xqe = xqc.createExpression(); xqs = xqe.executeQuery("<e a='Hello world!'/>/@*"); try { xqs.writeSequence(new ByteArrayOutputStream(), prop); fail("A-XQS-21.1: serialization process fails when sequence contains a top-level attribute"); } catch (XQException xq) { // Expect an XQException } xqe.close(); xqe = xqc.createExpression(); xqs = xqe.executeQuery("<e>Hello world!</e>"); try { xqs.writeSequence((OutputStream) null, prop); fail("A-XQS-24.3: writeSequence accepts a null buffer as first argument"); } catch (XQException e) { // Expect an XQException } xqe.close(); xqe = xqc.createExpression(); xqs = xqe.executeQuery("<e>Hello world!</e>"); ByteArrayOutputStream result = new ByteArrayOutputStream(); try { xqs.writeSequence(result, prop); } catch (XQException e) { fail("A-XQS-24.1: writeSequence failed with message: " + e.getMessage()); } assertTrue("A-XQS-24.1: Expects serialized result contains '<e>Hello world!</e>', but it is '" + result.toString("UTF-8") + "'", result.toString("UTF-8").indexOf("<e>Hello world!</e>") != -1); xqe.close(); xqe = xqc.createExpression(); xqs = xqe.executeQuery("<e>Hello world!</e>"); ByteArrayOutputStream otherResult = new ByteArrayOutputStream(); try { xqs.writeSequence(otherResult, prop); } catch (XQException e) { fail("A-XQS-24.2: writeSequence failed with message: " + e.getMessage()); } assertEquals("A-XQS-24.2: null properties argument is equivalent to empty properties argument", result.toString("UTF-8"), otherResult.toString("UTF-8")); xqe.close(); } @Test public void testWriteSequence_Writer() throws XQException { XQExpression xqe; XQSequence xqs; Properties prop = new Properties(); prop.setProperty("encoding", "UTF-8"); xqe = xqc.createExpression(); xqs = xqe.executeQuery("<e>Hello world!</e>"); xqs.close(); try { xqs.writeSequence(new StringWriter(), prop); fail("A-XQS-1.2: closed sequence supports writeSequence()"); } catch (XQException e) { // Expect an XQException } xqe.close(); xqe = xqc.createExpression(); xqs = xqe.executeQuery("<e>Hello world!</e>"); xqs.next(); xqs.getItem(); try { xqs.writeSequence(new StringWriter(), prop); fail("A-XQS-21.2: SCROLLTYPE_FORWARD_ONLY sequence, getXXX() or write() method has been invoked already on the current item"); } catch (XQException e) { // Expect an XQException } xqe.close(); xqe = xqc.createExpression(); xqs = xqe.executeQuery("<e a='Hello world!'/>/@*"); try { xqs.writeSequence(new StringWriter(), prop); fail("A-XQS-21.1: serialization process fails when sequence contains a top-level attribute"); } catch (XQException xq) { // Expect an XQException } xqe.close(); xqe = xqc.createExpression(); xqs = xqe.executeQuery("<e>Hello world!</e>"); try { xqs.writeSequence((Writer) null, prop); fail("A-XQS-24.3: writeSequence accepts a null buffer as first argument"); } catch (XQException e) { // Expect an XQException } xqe.close(); xqe = xqc.createExpression(); xqs = xqe.executeQuery("<e>Hello world!</e>"); StringWriter result = new StringWriter(); try { xqs.writeSequence(result, prop); } catch (XQException e) { fail("A-XQS-24.1: writeSequence failed with message: " + e.getMessage()); } assertTrue("A-XQS-24.1: Expects serialized result contains '<e>Hello world!</e>', but it is '" + result.toString() + "'", result.toString().indexOf("<e>Hello world!</e>") != -1); xqe.close(); xqe = xqc.createExpression(); xqs = xqe.executeQuery("<e>Hello world!</e>"); StringWriter otherResult = new StringWriter(); try { xqs.writeSequence(otherResult, prop); } catch (XQException e) { fail("A-XQS-24.2: writeSequence failed with message: " + e.getMessage()); } assertEquals("A-XQS-24.2: null properties argument is equivalent to empty properties argument", result.toString(), otherResult.toString()); xqe.close(); } }