blob: f3ac0ae76bfd8cf9b5517b9ec4ff201ecd94784e [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2010-2014 SAP AG and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* SAP AG - initial API and implementation
*******************************************************************************/
package org.eclipse.skalli.testutil;
import java.io.Reader;
import java.io.StringReader;
import java.io.StringWriter;
import java.net.URL;
import java.util.List;
import javax.xml.XMLConstants;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.transform.OutputKeys;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import javax.xml.transform.stream.StreamSource;
import javax.xml.validation.Schema;
import javax.xml.validation.SchemaFactory;
import org.eclipse.skalli.model.ExtensibleEntityBase;
import org.eclipse.skalli.model.ExtensionEntityBase;
import org.eclipse.skalli.services.extension.ExtensionService;
import org.eclipse.skalli.services.extension.ExtensionServices;
import org.eclipse.skalli.services.extension.rest.RestConverter;
import org.eclipse.skalli.services.extension.rest.RestUtils;
import org.junit.Assert;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.ErrorHandler;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import org.xml.sax.SAXParseException;
import com.thoughtworks.xstream.converters.MarshallingContext;
import com.thoughtworks.xstream.io.HierarchicalStreamWriter;
@SuppressWarnings("nls")
public class SchemaValidationUtils {
public static void validate(Object o, RestConverter<?> converter, String xsdFile)
throws Exception {
DocumentBuilderFactory dbf = getDocumentBuilderFactory(xsdFile);
StringBufferHierarchicalStreamWriter writer = new StringBufferHierarchicalStreamWriter();
SchemaValidationUtils.marshal(o, converter, writer);
validate(o, writer, dbf);
}
public static void validate(List<?> list, RestConverter<?> converter, String xsdFile)
throws Exception {
DocumentBuilderFactory dbf = getDocumentBuilderFactory(xsdFile);
for (Object o : list) {
StringBufferHierarchicalStreamWriter writer = new StringBufferHierarchicalStreamWriter();
SchemaValidationUtils.marshal(o, converter, writer);
validate(o, writer, dbf);
}
}
public static <T extends ExtensionEntityBase>
void validate(List<? extends ExtensibleEntityBase> extensibles, Class<T> extensionClass, String xsdFile)
throws Exception
{
DocumentBuilderFactory dbf = getDocumentBuilderFactory(xsdFile);
for (ExtensibleEntityBase extensible : extensibles) {
StringBufferHierarchicalStreamWriter writer = new StringBufferHierarchicalStreamWriter();
T extension = extensible.getExtension(extensionClass);
if (extension != null) {
boolean done = SchemaValidationUtils.marshalExtension(extensible, extension, writer);
if (!done) {
Assert.fail("Failed to marshal extension " + extensionClass + " for entity " + extensible);
}
validate(extensible, writer, dbf);
}
}
}
private static DocumentBuilderFactory getDocumentBuilderFactory(String xsdFile) throws Exception {
URL schemaFile = RestUtils.findSchemaResource(xsdFile);
String mergedSchema = SchemaValidationUtils.resolveIncludes(schemaFile);
SchemaFactory xsFact = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
Schema schema = xsFact.newSchema(new StreamSource(new StringReader(mergedSchema)));
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
dbf.setNamespaceAware(true);
dbf.setValidating(false);
dbf.setSchema(schema);
return dbf;
}
private static void validate(final Object o, HierarchicalStreamWriter writer, DocumentBuilderFactory dbf)
throws Exception {
final String xml = writer.toString();
Reader reader = new StringReader(xml);
DocumentBuilder db = dbf.newDocumentBuilder();
db.setErrorHandler(new ErrorHandler() {
@Override
public void warning(SAXParseException exception) throws SAXException {
Assert.fail(o.toString() + ": " + exception.getMessage() + "\n" + xml);
}
@Override
public void error(SAXParseException exception) throws SAXException {
Assert.fail(o.toString() + ": " + exception.getMessage() + "\n" + xml);
}
@Override
public void fatalError(SAXParseException exception) throws SAXException {
Assert.fail(o.toString() + ": " + exception.getMessage() + "\n" + xml);
}
});
db.parse(new InputSource(reader));
}
private static String resolveIncludes(URL schema) throws Exception {
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
Document schemaDOM = dbf.newDocumentBuilder().parse(new InputSource(schema.openStream()));
NodeList includes = schemaDOM.getElementsByTagName("xsd:include");
while (includes.getLength() > 0) {
for (int i = 0; i < includes.getLength(); ++i) {
Node includeNode = includes.item(i);
Node includeParent = includeNode.getParentNode();
NamedNodeMap attributes = includeNode.getAttributes();
String schemaLocation = attributes.getNamedItem("schemaLocation").getTextContent();
URL includeFile = RestUtils.findSchemaResource(schemaLocation);
Document includeDOM = dbf.newDocumentBuilder().parse(new InputSource(includeFile.openStream()));
Element schemaRoot = schemaDOM.getDocumentElement();
Element includeRoot = includeDOM.getDocumentElement();
NodeList children = includeRoot.getChildNodes();
for (int j = 0; j < children.getLength(); ++j) {
schemaRoot.insertBefore(schemaDOM.importNode(children.item(j), true), includeNode);
}
includeParent.removeChild(includeNode);
}
includes = schemaDOM.getElementsByTagName("xsd:include");
}
StringWriter out = new StringWriter();
Transformer xform = TransformerFactory.newInstance().newTransformer();
xform.setOutputProperty(OutputKeys.INDENT, "yes");
xform.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "no");
xform.transform(new DOMSource(schemaDOM), new StreamResult(out));
return out.toString();
}
private static <T extends ExtensionEntityBase>
boolean marshalExtension(ExtensibleEntityBase extensible, T extension, HierarchicalStreamWriter writer) {
boolean done = false;
for (ExtensionService<?> extensionService : ExtensionServices.getAll()) {
RestConverter<?> converter = extensionService.getRestConverter("https://localhost");
Class<? extends ExtensionEntityBase> extensionClass = extension.getClass();
if (converter != null && extensionClass.equals(converter.getConversionClass())) {
marshal(extension,
new ConverterWrapper("https://localhost", converter, extensionService.getShortName(),
extensible.isInherited(extensionClass)), writer);
done = true;
break;
}
}
return done;
}
private static void marshal(Object o, RestConverter<?> converter, HierarchicalStreamWriter writer) {
MarshallingContext context = new MarshallingContextMock(writer);
converter.marshal(o, writer, context);
}
}