/**
*
* 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 test.sdo21.tests.general;
import commonj.sdo.*;
import commonj.sdo.helper.*;
import org.junit.After;
import static org.junit.Assert.*;
import org.junit.Before;
import org.junit.Test;
import test.sdo21.framework.CTSTestCase;
import test.sdo21.tests.util.XMLEqualityChecker;
import java.io.*;
import java.math.BigDecimal;
import java.net.URL;
/**
* Tests various concepts against the XMLHelper, including ...
*
* - Verification of change summary serialization before and after undoChanges()
*
- Round tripping of serialization
*
- Definition of types using dynamic API
*
- Handling of sequenced types
*
- Handling of open sequenced types
*
* @see 2.1 spec section 3.11
*/
public class XMLHelperTest extends CTSTestCase {
private static final String CUSTOMER1_XML = "/customer1.xml";
private static final String CUSTOMER2_XML = "/customer2.xml";
private static final String MIXED_XML = "/mixed2.xml";
private static final String MIXEDOPEN_XML = "/mixedopen.xml";
/**
* Verify how the XMLHelper handles serialization of a DataObject which includes a ChangeSummary.
* Checks are made before and after an {@link ChangeSummary#undoChanges()} call
* @see 2.1 spec section 3.3.5
* @see XMLHelper#save(DataObject, String, String)
*/
@Test
public void testChangeSummary() throws Exception {
final String TEST_MODEL = "/simpleWithChangeSummary.xsd";
final String TEST_NAMESPACE = "http://www.example.com/simpleCS";
final String TEST_DATA_BEFORE_UNDO = "/simpleWithChangeSummary.xml";
final String TEST_DATA_AFTER_UNDO = "/simpleWithChangeSummaryUndone.xml";
// Populate the meta data for the test (Stock Quote) model
URL url = getClass().getResource(TEST_MODEL);
InputStream inputStream = url.openStream();
getScope().getXSDHelper().define(inputStream, url.toString());
inputStream.close();
Type quoteType = getScope().getTypeHelper().getType(TEST_NAMESPACE, "RootQuote");
DataObject quote = getScope().getDataFactory().create(quoteType);
ChangeSummary cs = quote.getChangeSummary();
ChangeSummary csp = (ChangeSummary)quote.get("changes");
assertSame("The ChangeSummary Property should be returned by getChangeSummary.", cs, csp);
quote.setString("symbol", "fbnt");
quote.setString("companyName", "FlyByNightTechnology");
quote.setBigDecimal("price", new BigDecimal("1000.0"));
DataObject child = quote.createDataObject("quotes");
child.setBigDecimal("price", new BigDecimal("1500.0"));
child = quote.createDataObject("quotes");
child.setBigDecimal("price", new BigDecimal("2000.0"));
child = child.createDataObject("quotes");
child.setBigDecimal("price", new BigDecimal("2000.99"));
child = quote.createDataObject("quotes");
child.setBigDecimal("price", new BigDecimal("2500.0"));
// Begin logging changes
//
cs.beginLogging();
// Modify the data graph in various ways
//
quote.setString("symbol", "FBNT");
quote.setBigDecimal("price", new BigDecimal("999.0"));
quote.setDouble("volume", 1000);
child = quote.createDataObject("quotes");
child.setBigDecimal("price", new BigDecimal("3000.0"));
child = quote.createDataObject("quotes");
child.setBigDecimal("price", new BigDecimal("4000.0"));
quote.getDataObject("quotes[2]").delete();
// Stop logging changes and serialize the resulting data graph
cs.endLogging();
ByteArrayOutputStream baos = new ByteArrayOutputStream();
getScope().getXMLHelper().save(quote, TEST_NAMESPACE, "stockQuote", baos);
try {
XMLEqualityChecker.compareXmlFiles(
new ByteArrayInputStream(baos.toByteArray()),
getClass().getResource(TEST_DATA_BEFORE_UNDO)
);
}
catch (Exception e) {
fail("XMLHelper did not accurately reflect the ChangeSummary: " + e.getMessage());
}
// Undo all changes and then serialize the resulting data graph again
cs.undoChanges();
baos = new ByteArrayOutputStream();
getScope().getXMLHelper().save(quote, TEST_NAMESPACE, "stockQuote", baos);
try {
XMLEqualityChecker.compareXmlFiles(
new ByteArrayInputStream(baos.toByteArray()),
getClass().getResource(TEST_DATA_AFTER_UNDO)
);
}
catch (Exception e) {
fail( "XMLHelper did not accurately reflect the ChangeSummary after undoChanges(): " + e.getMessage() );
}
}
/**
* Verify how the XMLHelper handles open content.
* Ensures that the open content is serialized properly.
* Uses global properties from the XSD, and open content properties
* defined by the dynamic API.
* TODO -- is this primarily a TypeHelper test? Consider moving.
*
* @see 2.1 spec section 3.6.6
* @see 2.1 spec section 3.8.3
*/
@Test
public void testDefineOpenContentProperty() throws IOException {
final String TEST_MODEL = "/open.xsd";
final String TEST_NAMESPACE = "http://www.example.com/open";
final String TEST_DATA = "/openContentProperty.xml";
TypeHelper typeHelper;
XSDHelper xsdHelper;
XMLHelper xmlHelper;
DataFactory dataFactory;
typeHelper = getScope().getTypeHelper();
dataFactory = getScope().getDataFactory();
xsdHelper = getScope().getXSDHelper();
xmlHelper = getScope().getXMLHelper();
// Populate the meta data for the test (Stock Quote) model
URL url = getClass().getResource(TEST_MODEL);
InputStream inputStream = url.openStream();
xsdHelper.define(inputStream, url.toString());
inputStream.close();
Type quoteType = typeHelper.getType(TEST_NAMESPACE, "OpenQuote");
DataObject quote = dataFactory.create(quoteType);
assertNotNull( quote );
quote.setString("symbol", "s1");
Property companyProperty = typeHelper.getOpenContentProperty(TEST_NAMESPACE, "company");
DataObject company = quote.createDataObject(companyProperty);
company.setString("name", "FlyByNightTechnology");
Property priceProperty = typeHelper.getOpenContentProperty(TEST_NAMESPACE, "price");
quote.getList(priceProperty).add(new BigDecimal("1000.0"));
// Define a new SDO open content property with simple type
DataObject p = dataFactory.create("commonj.sdo", "Property");
p.set("type", typeHelper.getType("commonj.sdo", "Decimal"));
p.set("name", "highPrice");
Property highPrice = typeHelper.defineOpenContentProperty(TEST_NAMESPACE, p);
quote.setBigDecimal(highPrice, new BigDecimal("1100.0"));
// Define a new SDO open content property with complex type
DataObject mutualFundQuotePropertyDef = dataFactory.create("commonj.sdo", "Property");
mutualFundQuotePropertyDef.set("type", quoteType);
mutualFundQuotePropertyDef.set("name", "mutualFundQuote");
mutualFundQuotePropertyDef.setBoolean("containment", true);
Property mutualFundQuoteProperty =
typeHelper.defineOpenContentProperty(TEST_NAMESPACE, mutualFundQuotePropertyDef);
DataObject mutualFundQuote = quote.createDataObject(mutualFundQuoteProperty);
mutualFundQuote.setString("symbol", "mutual-1");
ByteArrayOutputStream baos = new ByteArrayOutputStream();
xmlHelper.save(quote, TEST_NAMESPACE, "openStockQuote", baos);
try {
XMLEqualityChecker.compareXmlFiles(
new ByteArrayInputStream(baos.toByteArray()),
getClass().getResource(TEST_DATA)
);
}
catch (Exception e) {
fail("XMLHelper did not accurately reflect open content." + e.getMessage());
}
// validate existing property condition
Property duplicateProp = typeHelper.defineOpenContentProperty(TEST_NAMESPACE, p);
assertTrue("The expected Property equality was not true", highPrice.equals(duplicateProp));
// validate error condition, where new property exists with different
// type
boolean errorCondition = false;
try {
p = dataFactory.create("commonj.sdo", "Property");
p.set("type", typeHelper.getType("commonj.sdo", "String"));
p.set("name", "highPrice");
highPrice = typeHelper.defineOpenContentProperty(TEST_NAMESPACE, p);
} catch (IllegalArgumentException ex) {
errorCondition = true;
}
assertTrue("Should not be able to redefine a Property with a different Type.", errorCondition);
}
/**
* Verify how the XMLHelper handles Types defined using the TypeHelper. Save
* and load the Dataobject with the XMLHelper as an intermediate step.
* TODO this is a hybrid test that needs some refactoring. It implicitly tests
* dynamic type creation, creation of dynamic types, xml round tripping and the
* dataObject API without a great deal of structure. Some assertions are
* irrelevant since the tests would throw NPEs before performing the
* tests. Other assertions could be stronger than checking for non-null. See also
* testDefineTypes for similar issues.
* @see SDO spec 2..1 section 3.8.4
* @see SDO spec 2..1 section 3.11.3
* @see TypeHelper#define(DataObject)
* @see XMLHelper#save(DataObject, String, String, java.io.OutputStream)
* @see XMLHelper#load(InputStream, String, Object)
*
*/
@Test
public void testDefineTypeRoundTrip() throws Exception {
TypeHelper types = getScope().getTypeHelper();
DataFactory factory = getScope().getDataFactory();
XMLHelper xmlHelper = getScope().getXMLHelper();
Type intType = types.getType("commonj.sdo", "Int");
assertNotNull( intType );
Type stringType = types.getType("commonj.sdo", "String");
// create a new Type for Customers
DataObject customerType = factory.create("commonj.sdo", "Type");
customerType.set("uri", "http://example.com/customer");
customerType.set("name", "Customer");
Property xmlElementProp = getScope().getXSDHelper().getGlobalProperty("commonj.sdo/xml", "xmlElement", false);
// create a customer number property
DataObject custNumProperty = customerType.createDataObject("property");
custNumProperty.set("name", "custNum");
custNumProperty.set("type", intType);
custNumProperty.setBoolean(xmlElementProp, false);
// create a first name property
DataObject firstNameProperty = customerType.createDataObject("property");
firstNameProperty.set("name", "firstName");
firstNameProperty.set("type", stringType);
firstNameProperty.setBoolean(xmlElementProp, false);
// create a last name property
DataObject lastNameProperty = customerType.createDataObject("property");
lastNameProperty.set("name", "lastName");
lastNameProperty.set("type", stringType);
lastNameProperty.setBoolean(xmlElementProp, false);
ByteArrayOutputStream baos = new ByteArrayOutputStream();
xmlHelper.save(customerType, "commonj.sdo", "type", baos);
ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
XMLDocument xdoc = xmlHelper.load(bais);
customerType = xdoc.getRootObject();
// now define the Customer type so that customers can be made
types.define(customerType);
DataObject customer1 = factory.create("http://example.com/customer", "Customer");
customer1.setInt("custNum", 1);
customer1.set("firstName", "John");
customer1.set("lastName", "Adams");
DataObject customer2 = factory.create("http://example.com/customer", "Customer");
customer2.setInt("custNum", 2);
customer2.set("firstName", "Jeremy");
customer2.set("lastName", "Pavick");
assertNotNull("Customer1 should not be null", customer1);
Type type = customer1.getType();
assertNotNull("Customer1.custNum should not be null", type.getProperty("custNum"));
assertNotNull("Customer1.firstName should not be null", type.getProperty("firstName"));
assertNotNull("Customer1.lastName should not be null", type.getProperty("lastName"));
assertEquals("Customer1.custNum should be intType", type.getProperty("custNum").getType(), intType);
assertEquals("Customer1.firstName should be stringType", type.getProperty("firstName").getType(), stringType);
assertEquals("Customer1.lastName should be stringType", type.getProperty("lastName").getType(), stringType);
assertNotNull("Customer2 should not be null", customer2);
type = customer2.getType();
assertNotNull("Customer1.custNum should not be null", type.getProperty("custNum"));
assertNotNull("Customer1.custNum should not be null", type.getProperty("firstName"));
assertNotNull("Customer1.custNum should not be null", type.getProperty("lastName"));
assertEquals("Customer1.custNum should be intType", type.getProperty("custNum").getType(), intType);
assertEquals("Customer1.firstName should be stringType", type.getProperty("firstName").getType(), stringType);
assertEquals("Customer1.lastName should be stringType", type.getProperty("lastName").getType(), stringType);
baos = new ByteArrayOutputStream();
xmlHelper.save(customer1, "http://example.com/customer", "Customer", baos);
try {
XMLEqualityChecker.compareXmlFiles(
new ByteArrayInputStream(baos.toByteArray()),
getClass().getResource(CUSTOMER1_XML)
);
}
catch (Exception e) {
fail("XMLHelper did not accurately reflect customer1: " + e.getMessage());
}
baos = new ByteArrayOutputStream();
xmlHelper.save(customer2, "http://example.com/customer", "Customer", baos);
try {
XMLEqualityChecker.compareXmlFiles(
new ByteArrayInputStream(baos.toByteArray()),
getClass().getResource(CUSTOMER2_XML)
);
}
catch (Exception e) {
fail("XMLHelper did not accurately reflect customer2: " + e.getMessage());
}
}
/**
* Verify how the XMLHelper handles Types defined using the TypeHelper.
* TODO this is the same as {@link XMLHelperTest#testDefineTypeRoundTrip()} without
* the round tripping of the metametadata. See the issues described in the doc for that method.
* @see SDO spec 2..1 section 3.8.4
* @see TypeHelper#define(DataObject)
*/
@Test
public void testDefineType() throws Exception {
TypeHelper types = getScope().getTypeHelper();
DataFactory factory = getScope().getDataFactory();
XMLHelper xmlHelper = getScope().getXMLHelper();
Type intType = types.getType("commonj.sdo", "Int");
Type stringType = types.getType("commonj.sdo", "String");
// create a new Type for Customers
DataObject customerType = factory.create("commonj.sdo", "Type");
customerType.set("uri", "http://example.com/customer");
customerType.set("name", "Customer");
Property xmlElementProp = getScope().getXSDHelper().getGlobalProperty("commonj.sdo/xml", "xmlElement", false);
// create a customer number property
DataObject custNumProperty = customerType.createDataObject("property");
custNumProperty.set("name", "custNum");
custNumProperty.set("type", intType);
custNumProperty.setBoolean(xmlElementProp, false);
// create a first name property
DataObject firstNameProperty = customerType.createDataObject("property");
firstNameProperty.set("name", "firstName");
firstNameProperty.set("type", stringType);
firstNameProperty.setBoolean(xmlElementProp, false);
// create a last name property
DataObject lastNameProperty = customerType.createDataObject("property");
lastNameProperty.set("name", "lastName");
lastNameProperty.set("type", stringType);
lastNameProperty.setBoolean(xmlElementProp, false);
// now define the Customer type so that customers can be made
types.define(customerType);
DataObject customer1 = factory.create("http://example.com/customer", "Customer");
customer1.setInt("custNum", 1);
customer1.set("firstName", "John");
customer1.set("lastName", "Adams");
DataObject customer2 = factory.create("http://example.com/customer", "Customer");
customer2.setInt("custNum", 2);
customer2.set("firstName", "Jeremy");
customer2.set("lastName", "Pavick");
assertNotNull("Customer1 should not be null", customer1);
Type type = customer1.getType();
assertNotNull("Customer1.custNum should not be null", type.getProperty("custNum"));
assertNotNull("Customer1.firstName should not be null", type.getProperty("firstName"));
assertNotNull("Customer1.lastName should not be null", type.getProperty("lastName"));
assertEquals("Customer1.custNum should be intType", type.getProperty("custNum").getType(), intType);
assertEquals("Customer1.firstName should be stringType", type.getProperty("firstName").getType(), stringType);
assertEquals("Customer1.lastName should be stringType", type.getProperty("lastName").getType(), stringType);
assertNotNull("Customer2 should not be null", customer2);
type = customer2.getType();
assertNotNull("Customer1.custNum should not be null", type.getProperty("custNum"));
assertNotNull("Customer1.custNum should not be null", type.getProperty("firstName"));
assertNotNull("Customer1.custNum should not be null", type.getProperty("lastName"));
assertEquals("Customer1.custNum should be intType", type.getProperty("custNum").getType(), intType);
assertEquals("Customer1.firstName should be stringType", type.getProperty("firstName").getType(), stringType);
assertEquals("Customer1.lastName should be stringType", type.getProperty("lastName").getType(), stringType);
ByteArrayOutputStream baos = new ByteArrayOutputStream();
xmlHelper.save(customer1, "http://example.com/customer", "Customer", baos);
try {
XMLEqualityChecker.compareXmlFiles(
new ByteArrayInputStream(baos.toByteArray()),
getClass().getResource(CUSTOMER1_XML)
);
}
catch (Exception e) {
fail("XMLHelper did not accurately reflect customer1: " + e.getMessage());
}
baos = new ByteArrayOutputStream();
xmlHelper.save(customer2, "http://example.com/customer", "Customer", baos);
try {
XMLEqualityChecker.compareXmlFiles(
new ByteArrayInputStream(baos.toByteArray()),
getClass().getResource(CUSTOMER2_XML)
);
}
catch (Exception e) {
fail("XMLHelper did not accurately reflect customer2: " + e.getMessage());
}
}
/**
* Verify how the XMLHelper handles defined Sequence types.
* @see SDO spec 2..1 section 3.1.8
* @see DataObject#getSequence()
* @see Sequence#addText(String)
* @see Sequence#add(Property, Object)
*/
@Test
public void testDefineSequencedType() throws Exception {
TypeHelper types = getScope().getTypeHelper();
DataFactory factory = getScope().getDataFactory();
XMLHelper xmlHelper = getScope().getXMLHelper();
Type stringType = types.getType("commonj.sdo", "String");
Type decimalType = types.getType("commonj.sdo", "Decimal");
// Define a new mixed type - MixedQuote
DataObject mixedQuoteType = factory.create("commonj.sdo", "Type");
mixedQuoteType.set("uri", "http://www.example.com/mixed");
mixedQuoteType.set("name", "MixedQuote");
mixedQuoteType.set("sequenced", Boolean.TRUE);
DataObject symbolProperty = mixedQuoteType.createDataObject("property");
symbolProperty.set("name", "symbol");
symbolProperty.set("type", stringType);
DataObject companyNameProperty = mixedQuoteType.createDataObject("property");
companyNameProperty.set("name", "companyName");
companyNameProperty.set("type", stringType);
DataObject priceProperty = mixedQuoteType.createDataObject("property");
priceProperty.set("name", "price");
priceProperty.set("type", decimalType);
DataObject quotesProperty = mixedQuoteType.createDataObject("property");
quotesProperty.set("name", "quotes");
quotesProperty.set("type", mixedQuoteType);
quotesProperty.set("many", Boolean.TRUE);
quotesProperty.set("containment", Boolean.TRUE);
types.define(mixedQuoteType);
DataObject quote = factory.create("http://www.example.com/mixed", "MixedQuote");
assertTrue("Quote type should be Sequenced", quote.getType().isSequenced());
Sequence sequence = quote.getSequence();
sequence.addText("\n ");
quote.setString("symbol", "fbnt");
sequence.addText("\n ");
quote.setString("companyName", "FlyByNightTechnology");
sequence.addText("\n some text\n ");
DataObject child = quote.createDataObject("quotes");
child.setBigDecimal("price", new BigDecimal("2000.0"));
sequence.addText("\n more text\n ");
// quote.setBigDecimal("price", new BigDecimal("1000.0"));
sequence.add("price", new BigDecimal("1000.0"));
sequence.addText("\n");
ByteArrayOutputStream baos = new ByteArrayOutputStream();
xmlHelper.save(quote, "http://www.example.com/mixed", "mixedStockQuote", baos);
try {
XMLEqualityChecker.compareXmlFiles(
new ByteArrayInputStream(baos.toByteArray()),
getClass().getResource(MIXED_XML)
);
}
catch (Exception e) {
fail("XMLHelper did not accurately reflect quote: " + e.getMessage());
}
}
/**
* Verify how the XMLHelper handles Types which are Sequenced and Open.
* @see SDO spec 2..1 section 3.1.8
* @see DataObject#getSequence()
* @see Sequence#addText(String)
* @see Sequence#add(Property, Object)
* @see TypeHelper#defineOpenContentProperty(String, DataObject)
* @see TypeHelper#getOpenContentProperty(String, String)
*/
@Test
public void testDefineSequencedOpenType() throws Exception {
TypeHelper types = getScope().getTypeHelper();
DataFactory factory = getScope().getDataFactory();
XMLHelper xmlHelper = getScope().getXMLHelper();
Type stringType = types.getType("commonj.sdo", "String");
Type decimalType = types.getType("commonj.sdo", "Decimal");
// Define a new mixed type - MixedQuote
DataObject mixedQuoteType = factory.create("commonj.sdo", "Type");
mixedQuoteType.set("uri", "http://www.example.com/mixed");
mixedQuoteType.set("name", "MixedOpenQuote");
mixedQuoteType.set("sequenced", Boolean.TRUE);
mixedQuoteType.set("open", Boolean.TRUE);
// DataObject symbolProperty =
// mixedQuoteType.createDataObject("property");
// symbolProperty.set("name", "symbol");
// symbolProperty.set("type", stringType);
DataObject companyNameProperty = mixedQuoteType.createDataObject("property");
companyNameProperty.set("name", "companyName");
companyNameProperty.set("type", stringType);
DataObject priceProperty = mixedQuoteType.createDataObject("property");
priceProperty.set("name", "price");
priceProperty.set("type", decimalType);
DataObject quotesProperty = mixedQuoteType.createDataObject("property");
quotesProperty.set("name", "quotes");
quotesProperty.set("type", mixedQuoteType);
quotesProperty.set("many", Boolean.TRUE);
quotesProperty.set("containment", Boolean.TRUE);
types.define(mixedQuoteType);
// Define a global property
DataObject symbolProperty = factory.create("commonj.sdo", "Property");
symbolProperty.set("name", "symbol");
symbolProperty.set("type", stringType);
symbolProperty.set("containment", Boolean.TRUE);
types.defineOpenContentProperty("http://www.example.com/open", symbolProperty);
DataObject quote = factory.create("http://www.example.com/mixed", "MixedOpenQuote");
assertTrue("Quote type should be Sequenced.", quote.getType().isSequenced());
Sequence sequence = quote.getSequence();
sequence.addText("\n ");
Property definedSymbolProperty = types.getOpenContentProperty("http://www.example.com/open", "symbol");
quote.setString(definedSymbolProperty, "fbnt");
sequence.addText("\n ");
quote.setString("companyName", "FlyByNightTechnology");
sequence.addText("\n some text\n ");
DataObject child = quote.createDataObject("quotes");
child.setBigDecimal("price", new BigDecimal("2000.0"));
sequence.addText("\n more text\n ");
// quote.setBigDecimal("price", new BigDecimal("1000.0"));
sequence.add("price", new BigDecimal("1000.0"));
sequence.addText("\n");
ByteArrayOutputStream baos = new ByteArrayOutputStream();
xmlHelper.save(quote, "http://www.example.com/mixed", "mixedOpenStockQuote", baos);
FileOutputStream fos = new FileOutputStream("XMLTCOutput");
xmlHelper.save(quote, "http://www.example.com/mixed", "mixedOpenStockQuote", fos);
try {
XMLEqualityChecker.compareXmlFiles(
new ByteArrayInputStream(baos.toByteArray()),
getClass().getResource(MIXEDOPEN_XML)
);
}
catch (Exception e) {
fail("XMLHelper did not accurately reflect quote: " + e.getMessage());
}
}
@Before
public void setUp() throws Exception {
super.setUp();
}
@After
public void tearDown() throws Exception {
super.tearDown();
}
}