/******************************************************************************* * Copyright (c) 1998, 2015 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 v1.0 and Eclipse Distribution License v. 1.0 * which accompanies this distribution. * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v10.html * and the Eclipse Distribution License is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * 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 javax.activation.DataHandler; import javax.xml.bind.JAXBContext; import javax.xml.bind.JAXBElement; import javax.xml.bind.Marshaller; import javax.xml.bind.SchemaOutputResolver; import javax.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); } 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); } 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 = new 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(factory.IS_REPAIRING_NAMESPACES, new 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(factory.IS_REPAIRING_NAMESPACES, new 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()); } 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. * @return * @throws Exception */ 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. * @param xmlResource * @throws Exception */ 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); } 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. * * @param xmlelement * @return * @throws Exception */ 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>(); } 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; } } }