blob: 272458bc2d86777512498c4c3221b7dcc63d5793 [file] [log] [blame]
/*
* Copyright (c) 1998, 2021 Oracle and/or its affiliates. All rights reserved.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v. 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0,
* or the Eclipse Distribution License v. 1.0 which is available at
* http://www.eclipse.org/org/documents/edl-v10.php.
*
* SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
*/
// Contributors:
// Denise Smith - November, 2009
package org.eclipse.persistence.testing.jaxb.typemappinginfo;
import java.awt.Image;
import java.io.IOException;
import java.io.InputStream;
import java.io.StringReader;
import java.io.StringWriter;
import java.io.Writer;
import java.lang.reflect.Array;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import jakarta.activation.DataHandler;
import jakarta.xml.bind.JAXBContext;
import jakarta.xml.bind.JAXBElement;
import jakarta.xml.bind.Marshaller;
import jakarta.xml.bind.SchemaOutputResolver;
import jakarta.xml.bind.Unmarshaller;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.stream.XMLEventWriter;
import javax.xml.stream.XMLOutputFactory;
import javax.xml.stream.XMLStreamReader;
import javax.xml.stream.XMLStreamWriter;
import javax.xml.transform.Result;
import javax.xml.transform.Source;
import javax.xml.transform.stream.StreamResult;
import javax.xml.transform.stream.StreamSource;
import javax.xml.validation.Schema;
import javax.xml.validation.SchemaFactory;
import javax.xml.validation.Validator;
import org.eclipse.persistence.internal.helper.ClassConstants;
import org.eclipse.persistence.jaxb.JAXBContextFactory;
import org.eclipse.persistence.jaxb.JAXBMarshaller;
import org.eclipse.persistence.jaxb.JAXBUnmarshaller;
import org.eclipse.persistence.jaxb.MarshallerProperties;
import org.eclipse.persistence.jaxb.TypeMappingInfo;
import org.eclipse.persistence.oxm.NamespaceResolver;
import org.eclipse.persistence.oxm.XMLConstants;
import org.eclipse.persistence.oxm.XMLDescriptor;
import org.eclipse.persistence.oxm.XMLRoot;
import org.eclipse.persistence.testing.jaxb.JAXBXMLComparer;
import org.eclipse.persistence.testing.oxm.OXTestCase;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.xml.sax.InputSource;
import static org.eclipse.persistence.testing.jaxb.JAXBTestCases.ECLIPSELINK_OXM_XSD;
public abstract class TypeMappingInfoTestCases extends OXTestCase {
protected JAXBContext jaxbContext;
protected Marshaller jaxbMarshaller;
protected Unmarshaller jaxbUnmarshaller;
public String resourceName;
protected Document controlDocument;
protected Document writeControlDocument;
protected DocumentBuilder parser;
protected Source bindingsFileXSDSource;
protected String controlDocumentLocation;
protected String writeControlDocumentLocation;
protected TypeMappingInfo[] typeMappingInfos;
public TypeMappingInfoTestCases(String name) throws Exception {
super(name);
}
@Override
public void setUp() throws Exception {
setupParser();
setupControlDocs();
InputStream bindingsFileXSDInputStream = getClass().getClassLoader().getResourceAsStream(ECLIPSELINK_OXM_XSD);
if(bindingsFileXSDInputStream == null){
bindingsFileXSDInputStream = getClass().getClassLoader().getResourceAsStream("org/eclipse/persistence/jaxb/" + ECLIPSELINK_OXM_XSD);
}
if(bindingsFileXSDInputStream == null){
fail("ERROR LOADING " + ECLIPSELINK_OXM_XSD);
}
bindingsFileXSDSource = new StreamSource(bindingsFileXSDInputStream);
}
@Override
public void tearDown() throws Exception{
super.tearDown();
jaxbContext = null;
jaxbMarshaller = null;
jaxbUnmarshaller = null;
}
public void setTypeMappingInfos(TypeMappingInfo[] newTypes) throws Exception {
typeMappingInfos = newTypes;
Map props = getProperties();
if(props != null){
Map overrides = (Map) props.get(JAXBContextFactory.ECLIPSELINK_OXM_XML_KEY);
if(overrides != null){
Iterator valuesIter = overrides.values().iterator();
while(valuesIter.hasNext()){
Source theSource = (Source) valuesIter.next();
validateBindingsFileAgainstSchema(theSource);
}
}
}
jaxbContext = org.eclipse.persistence.jaxb.JAXBContextFactory.createContext(newTypes, props, Thread.currentThread().getContextClassLoader());
jaxbMarshaller = jaxbContext.createMarshaller();
jaxbUnmarshaller = jaxbContext.createUnmarshaller();
}
public TypeMappingInfo getTypeMappingInfo(){
return typeMappingInfos[0];
}
protected Map getProperties() {
return null;
}
public void testXMLToObjectFromXMLEventReaderWithTypeMappingInfo() throws Exception {
if(null != XML_INPUT_FACTORY) {
InputStream instream = ClassLoader.getSystemResourceAsStream(resourceName);
javax.xml.stream.XMLEventReader reader = XML_INPUT_FACTORY.createXMLEventReader(instream);
Object obj = ((org.eclipse.persistence.jaxb.JAXBUnmarshaller)jaxbUnmarshaller).unmarshal(reader, getTypeMappingInfo());
Object controlObj = getReadControlObject();
xmlToObjectTest(obj, controlObj);
}
}
public void testXMLToObjectFromXMLStreamReaderWithTypeMappingInfo() throws Exception {
if(null != XML_INPUT_FACTORY) {
InputStream instream = ClassLoader.getSystemResourceAsStream(resourceName);
XMLStreamReader xmlStreamReader = XML_INPUT_FACTORY.createXMLStreamReader(instream);
Object testObject = ((JAXBUnmarshaller)jaxbUnmarshaller).unmarshal(xmlStreamReader, getTypeMappingInfo());
instream.close();
Object controlObj = getReadControlObject();
xmlToObjectTest(testObject, controlObj);
}
}
public void testXMLToObjectFromSourceWithTypeMappingInfoXML() throws Exception {
InputStream instream = ClassLoader.getSystemResourceAsStream(resourceName);
StreamSource ss = new StreamSource(instream);
Object testObject = ((JAXBUnmarshaller)jaxbUnmarshaller).unmarshal(ss, getTypeMappingInfo());
instream.close();
Object controlObj = getReadControlObject();
xmlToObjectTest(testObject, controlObj);
}
public void testObjectToResultWithTypeMappingInfoXML() throws Exception {
Object objectToWrite = getWriteControlObject();
XMLDescriptor desc = null;
if (objectToWrite instanceof XMLRoot) {
desc = (XMLDescriptor)((org.eclipse.persistence.jaxb.JAXBContext)jaxbContext).getXMLContext().getSession(0).getProject().getDescriptor(((XMLRoot)objectToWrite).getObject().getClass());
} else {
desc = (XMLDescriptor)((org.eclipse.persistence.jaxb.JAXBContext)jaxbContext).getXMLContext().getSession(0).getProject().getDescriptor(objectToWrite.getClass());
}
int sizeBefore = getNamespaceResolverSize(desc);
StringWriter stringWriter = new StringWriter();
StreamResult result = new StreamResult(stringWriter);
jaxbMarshaller.setProperty(MarshallerProperties.MEDIA_TYPE, "application/xml");
((JAXBMarshaller)jaxbMarshaller).marshal(objectToWrite, result, getTypeMappingInfo());
int sizeAfter = getNamespaceResolverSize(desc);
assertEquals(sizeBefore, sizeAfter);
StringReader reader = new StringReader(stringWriter.toString());
InputSource inputSource = new InputSource(reader);
Document testDocument = parser.parse(inputSource);
stringWriter.close();
reader.close();
objectToXMLDocumentTest(testDocument);
}
public void testObjectToXMLStreamWriterWithTypeMappingInfo() throws Exception {
if(XML_OUTPUT_FACTORY != null) {
StringWriter writer = new StringWriter();
XMLOutputFactory factory = XMLOutputFactory.newInstance();
factory.setProperty(XMLOutputFactory.IS_REPAIRING_NAMESPACES, Boolean.FALSE);
XMLStreamWriter streamWriter= factory.createXMLStreamWriter(writer);
Object objectToWrite = getWriteControlObject();
XMLDescriptor desc = null;
if (objectToWrite instanceof XMLRoot) {
desc = (XMLDescriptor)((org.eclipse.persistence.jaxb.JAXBContext)jaxbContext).getXMLContext().getSession(0).getProject().getDescriptor(((XMLRoot)objectToWrite).getObject().getClass());
} else {
desc = (XMLDescriptor)((org.eclipse.persistence.jaxb.JAXBContext)jaxbContext).getXMLContext().getSession(0).getProject().getDescriptor(objectToWrite.getClass());
}
int sizeBefore = getNamespaceResolverSize(desc);
((JAXBMarshaller)jaxbMarshaller).marshal(objectToWrite, streamWriter, getTypeMappingInfo());
streamWriter.flush();
int sizeAfter = getNamespaceResolverSize(desc);
assertEquals(sizeBefore, sizeAfter);
StringReader reader = new StringReader(writer.toString());
InputSource inputSource = new InputSource(reader);
Document testDocument = parser.parse(inputSource);
writer.close();
reader.close();
objectToXMLDocumentTest(testDocument);
}
}
public void testObjectToXMLEventWriterWithTypeMappingInfo() throws Exception {
if(XML_OUTPUT_FACTORY != null) {
StringWriter writer = new StringWriter();
XMLOutputFactory factory = XMLOutputFactory.newInstance();
factory.setProperty(XMLOutputFactory.IS_REPAIRING_NAMESPACES, Boolean.FALSE);
XMLEventWriter eventWriter= factory.createXMLEventWriter(writer);
Object objectToWrite = getWriteControlObject();
XMLDescriptor desc = null;
if (objectToWrite instanceof XMLRoot) {
desc = (XMLDescriptor)((org.eclipse.persistence.jaxb.JAXBContext)jaxbContext).getXMLContext().getSession(0).getProject().getDescriptor(((XMLRoot)objectToWrite).getObject().getClass());
} else {
desc = (XMLDescriptor)((org.eclipse.persistence.jaxb.JAXBContext)jaxbContext).getXMLContext().getSession(0).getProject().getDescriptor(objectToWrite.getClass());
}
int sizeBefore = getNamespaceResolverSize(desc);
((JAXBMarshaller)jaxbMarshaller).marshal(objectToWrite, eventWriter, getTypeMappingInfo());
eventWriter.flush();
int sizeAfter = getNamespaceResolverSize(desc);
assertEquals(sizeBefore, sizeAfter);
StringReader reader = new StringReader(writer.toString());
InputSource inputSource = new InputSource(reader);
Document testDocument = parser.parse(inputSource);
writer.close();
reader.close();
objectToXMLDocumentTest(testDocument);
}
}
public void xmlToObjectTest(Object testObject, Object controlObject) throws Exception {
log("\n**xmlToObjectTest**");
log("Expected:");
log(controlObject.toString());
log("Actual:");
log(testObject.toString());
JAXBElement controlObj = (JAXBElement)controlObject;
JAXBElement testObj = (JAXBElement)testObject;
compareJAXBElementObjects(controlObj, testObj);
}
public abstract Map<String, InputStream> getControlSchemaFiles();
public void testSchemaGen() throws Exception {
testSchemaGen(getControlSchemaFiles());
}
@Override
protected void compareValues(Object controlValue, Object testValue){
if(controlValue instanceof Node && testValue instanceof Node) {
assertXMLIdentical(((Node)controlValue).getOwnerDocument(), ((Node)testValue).getOwnerDocument());
} else if(controlValue instanceof DataHandler && testValue instanceof DataHandler){
compareDataHandlers((DataHandler)controlValue, (DataHandler)testValue);
} else if(controlValue instanceof Image && testValue instanceof Image) {
compareImages((Image)controlValue, (Image) testValue);
} else if (controlValue instanceof Byte[] && testValue instanceof Byte[]){
compareByteArrays((Byte[])controlValue, (Byte[])testValue);
} else if (controlValue instanceof byte[] && testValue instanceof byte[]){
compareByteArrays((byte[])controlValue, (byte[])testValue);
} else {
assertEquals(controlValue, testValue);
}
}
private void compareDataHandlers(DataHandler controlValue, DataHandler testValue){
assertEquals(controlValue.getContentType(), testValue.getContentType());
try{
assertEquals(controlValue.getContent(), testValue.getContent());
}catch(Exception e){
e.printStackTrace();
fail();
}
}
private void compareImages(Image controlImage, Image testImage) {
assertEquals(controlImage.getWidth(null), testImage.getWidth(null));
assertEquals(controlImage.getHeight(null), testImage.getHeight(null));
}
protected void setupParser() {
try {
DocumentBuilderFactory builderFactory = DocumentBuilderFactory.newInstance();
builderFactory.setNamespaceAware(true);
builderFactory.setIgnoringElementContentWhitespace(true);
parser = builderFactory.newDocumentBuilder();
} catch (Exception e) {
e.printStackTrace();
fail("An exception occurred during setup");
}
}
public void setupControlDocs() throws Exception{
if(this.controlDocumentLocation != null) {
InputStream inputStream = ClassLoader.getSystemResourceAsStream(controlDocumentLocation);
resourceName = controlDocumentLocation;
controlDocument = parser.parse(inputStream);
removeEmptyTextNodes(controlDocument);
inputStream.close();
}
if(this.writeControlDocumentLocation != null) {
InputStream inputStream = ClassLoader.getSystemResourceAsStream(writeControlDocumentLocation);
writeControlDocument = parser.parse(inputStream);
removeEmptyTextNodes(writeControlDocument);
inputStream.close();
}
}
protected int getNamespaceResolverSize(XMLDescriptor desc){
int size = -1;
if (desc != null) {
NamespaceResolver nr = desc.getNamespaceResolver();
if (nr != null) {
size = nr.getNamespaces().size();
}else{
size =0;
}
}
return size;
}
protected Document getControlDocument() {
return controlDocument;
}
/**
* Override this function to implement different read/write control documents.
*/
protected Document getWriteControlDocument() throws Exception {
if(writeControlDocument != null){
return writeControlDocument;
}
return getControlDocument();
}
protected void setControlDocument(String xmlResource) throws Exception {
this.controlDocumentLocation = xmlResource;
}
/**
* Provide an alternative write version of the control document when rountrip is not enabled.
* If this function is not called and getWriteControlDocument() is not overridden then the write and read control documents are the same.
*/
protected void setWriteControlDocument(String xmlResource) throws Exception {
writeControlDocumentLocation = xmlResource;
}
abstract protected Object getControlObject();
/*
* Returns the object to be used in a comparison on a read
* This will typically be the same object used to write
*/
public Object getReadControlObject() {
return getControlObject();
}
/*
* Returns the object to be written to XML which will be compared
* to the control document.
*/
public Object getWriteControlObject() {
return getControlObject();
}
public void objectToXMLDocumentTest(Document testDocument) throws Exception {
log("**objectToXMLDocumentTest**");
log("Expected:");
log(getWriteControlDocument());
log("\nActual:");
log(testDocument);
assertXMLIdentical(getWriteControlDocument(), testDocument);
}
@Override
public void compareJAXBElementObjects(JAXBElement controlObj, JAXBElement testObj) {
assertEquals(controlObj.getName().getLocalPart(), testObj.getName().getLocalPart());
assertEquals(controlObj.getName().getNamespaceURI(), testObj.getName().getNamespaceURI());
assertEquals(controlObj.getDeclaredType(), testObj.getDeclaredType());
Object controlValue = controlObj.getValue();
Object testValue = testObj.getValue();
if(controlValue == null) {
if(testValue == null){
return;
}
fail("Test value should have been null");
}else{
if(testValue == null){
fail("Test value should not have been null");
}
}
if(controlValue.getClass() == ClassConstants.ABYTE && testValue.getClass() == ClassConstants.ABYTE ||
controlValue.getClass() == ClassConstants.APBYTE && testValue.getClass() == ClassConstants.APBYTE){
compareValues(controlValue, testValue);
}else if(controlValue.getClass().isArray()){
if(testValue.getClass().isArray()){
if(controlValue.getClass().getComponentType().isPrimitive()){
comparePrimitiveArrays(controlValue, testValue);
}else{
compareObjectArrays(controlValue, testValue);
}
}else{
fail("Expected an array value but was an " + testValue.getClass().getName());
}
}
else if (controlValue instanceof Collection){
Collection controlCollection = (Collection)controlValue;
Collection testCollection = (Collection)testValue;
Iterator<Object> controlIter = controlCollection.iterator();
Iterator<Object> testIter = testCollection.iterator();
assertEquals(controlCollection.size(), testCollection.size());
while(controlIter.hasNext()){
Object nextControl = controlIter.next();
Object nextTest = testIter.next();
compareValues(nextControl, nextTest);
}
}else{
compareValues(controlValue, testValue);
}
}
protected void comparePrimitiveArrays(Object controlValue, Object testValue) {
if (controlValue == null && testValue == null) {
// equal
return;
}
assertEquals(controlValue.getClass(), testValue.getClass());
assertNotNull(controlValue);
assertNotNull(testValue);
int controlLength = Array.getLength(controlValue);
int testLength = Array.getLength(controlValue);
assertEquals(controlLength, testLength);
for (int i = 0; i < controlLength; i++) {
Object controlObject = Array.get(controlValue, i);
Object testObject = Array.get(testValue, i);
assertEquals(controlObject, testObject);
}
}
protected void compareObjectArrays(Object controlValue, Object testValue){
assertEquals(((Object[])controlValue).length,((Object[])testValue).length);
for(int i=0; i<((Object[])controlValue).length-1; i++){
assertEquals(((Object[])controlValue)[i], ((Object[])testValue)[i]);
}
}
public void testSchemaGen(Map<String, InputStream> controlSchemas) throws Exception {
MyStreamSchemaOutputResolver outputResolver = new MyStreamSchemaOutputResolver();
jaxbContext.generateSchema(outputResolver);
Map<String, Writer> generatedSchemas = outputResolver.schemaFiles;
assertEquals(controlSchemas.size(), generatedSchemas.size());
for(String next:controlSchemas.keySet()){
InputStream nextControlValue = controlSchemas.get(next);
Writer sw = generatedSchemas.get(next);
InputSource nextGeneratedValue = new InputSource(new StringReader(sw.toString()));
assertNotNull("Generated Schema not found.", nextGeneratedValue);
Document control = parser.parse(nextControlValue);
Document test = parser.parse(nextGeneratedValue);
JAXBXMLComparer xmlComparer = new JAXBXMLComparer();
boolean isEqual = xmlComparer.isSchemaEqual(control, test);
if(!isEqual){
log("Expected Schema\n");
log(control);
log("ActualSchema\n");
log(test);
}
assertTrue("generated schema did not match control schema", isEqual);
}
}
/**
* Return an Element for a given xml-element snippet.
*
*/
protected Element getXmlElement(String xmlelement) throws Exception {
StringReader str = new StringReader(xmlelement);
InputSource is = new InputSource(str);
try {
return parser.parse(is).getDocumentElement();
} catch (Exception e) {
throw e;
}
}
private boolean compareByteArrays(Byte[] first, Byte[] second){
if(first.length != second.length){
return false;
}
for(int i=0; i<first.length; i++){
if (first[i] != second[i]){
return false;
}
}
return true;
}
private boolean compareByteArrays(byte[] first, byte[] second){
if(first.length != second.length){
return false;
}
for(int i=0; i<first.length; i++){
if (first[i] != second[i]){
return false;
}
}
return true;
}
protected void validateBindingsFileAgainstSchema(Source src) {
String result = null;
SchemaFactory sFact = SchemaFactory.newInstance(javax.xml.XMLConstants.W3C_XML_SCHEMA_NS_URI);
Schema theSchema;
try {
theSchema = sFact.newSchema(bindingsFileXSDSource);
Validator validator = theSchema.newValidator();
validator.validate(src);
} catch (Exception e) {
e.printStackTrace();
if (e.getMessage() == null) {
result = "An unknown exception occurred.";
}
result = e.getMessage();
}
assertTrue("Schema validation failed unxepectedly: " + result, result == null);
}
public static class MyStreamSchemaOutputResolver extends SchemaOutputResolver {
// keep a list of processed schemas for the validation phase of the test(s)
public Map<String, Writer> schemaFiles;
public MyStreamSchemaOutputResolver() {
schemaFiles = new HashMap<String, Writer>();
}
@Override
public Result createOutput(String namespaceURI, String suggestedFileName) throws IOException {
//return new StreamResult(System.out);
if (namespaceURI == null) {
namespaceURI = "";
}
StringWriter sw = new StringWriter();
schemaFiles.put(namespaceURI, sw);
Result res = new StreamResult(sw);
res.setSystemId(suggestedFileName);
return res;
}
}
}