diff --git a/de.gebit.integrity.dsl.runner.experimentation/ConsoleTestExecutor XML.launch b/de.gebit.integrity.dsl.runner.experimentation/ConsoleTestExecutor XML.launch index 6590a08cd..388752340 100644 --- a/de.gebit.integrity.dsl.runner.experimentation/ConsoleTestExecutor XML.launch +++ b/de.gebit.integrity.dsl.runner.experimentation/ConsoleTestExecutor XML.launch @@ -19,6 +19,7 @@ + diff --git a/de.gebit.integrity.dsl.runner.experimentation/ConsoleTestExecutor.launch b/de.gebit.integrity.dsl.runner.experimentation/ConsoleTestExecutor.launch index 99897b253..a5a3fd2db 100644 --- a/de.gebit.integrity.dsl.runner.experimentation/ConsoleTestExecutor.launch +++ b/de.gebit.integrity.dsl.runner.experimentation/ConsoleTestExecutor.launch @@ -19,6 +19,8 @@ + + diff --git a/de.gebit.integrity.runner/.classpath b/de.gebit.integrity.runner/.classpath index b9a5b1ec6..8940e61e2 100644 --- a/de.gebit.integrity.runner/.classpath +++ b/de.gebit.integrity.runner/.classpath @@ -3,5 +3,6 @@ + diff --git a/de.gebit.integrity.runner/META-INF/MANIFEST.MF b/de.gebit.integrity.runner/META-INF/MANIFEST.MF index 35a33ba03..0d9fa52b2 100644 --- a/de.gebit.integrity.runner/META-INF/MANIFEST.MF +++ b/de.gebit.integrity.runner/META-INF/MANIFEST.MF @@ -3,7 +3,6 @@ Bundle-ManifestVersion: 2 Bundle-Name: Integrity Test Framework - Test Runner Bundle-SymbolicName: de.gebit.integrity.runner Bundle-Version: 0.16.0.qualifier -Bundle-ClassPath: . Require-Bundle: org.eclipse.xtext;bundle-version="2.8.3", de.gebit.integrity.dsl, org.eclipse.xtext.common.types;bundle-version="2.8.3", @@ -41,3 +40,5 @@ Import-Package: org.apache.log4j;version="1.2.15", org.jdom.xpath;version="1.0.0", org.slf4j;version="1.4.0" Bundle-Vendor: GEBIT Solutions GmbH +Bundle-ClassPath: ., + saxon/saxon-6.5.5-patched.jar diff --git a/de.gebit.integrity.runner/build.properties b/de.gebit.integrity.runner/build.properties index 5113c5fa5..4121fd857 100644 --- a/de.gebit.integrity.runner/build.properties +++ b/de.gebit.integrity.runner/build.properties @@ -1,3 +1,4 @@ bin.includes = META-INF/,\ - . + .,\ + saxon/ source.. = src/ diff --git a/de.gebit.integrity.runner/pom.xml b/de.gebit.integrity.runner/pom.xml index 991712f1e..0d7ef6724 100644 --- a/de.gebit.integrity.runner/pom.xml +++ b/de.gebit.integrity.runner/pom.xml @@ -18,6 +18,94 @@ ${basedir}/src + + ${project.build.directory}/saxon + + + + + org.codehaus.mojo + truezip-maven-plugin + 1.2 + + + copy-package + + copy + + validate + + true + + ${basedir}/saxon/saxon-6.5.5-patched.jar/ + ${project.build.directory}/saxon + **/*.class + + + + + + + + org.eclipse.tycho + tycho-compiler-plugin + + + default-compile + compile + + compile + + + + + de.gebit.integrity.runner.internaldep + saxon + 6.5.5 + ${basedir}/saxon/saxon-6.5.5-patched.jar + + + + + + + + + + + + + org.eclipse.m2e + lifecycle-mapping + 1.0.0 + + + + + + + org.codehaus.mojo + + + truezip-maven-plugin + + + [1.2,) + + + copy + + + + + + + + + + + + diff --git a/de.gebit.integrity.runner/saxon/com.icl.saxon.output.HTMLEmitter.diff b/de.gebit.integrity.runner/saxon/com.icl.saxon.output.HTMLEmitter.diff new file mode 100644 index 000000000..f7da21de0 --- /dev/null +++ b/de.gebit.integrity.runner/saxon/com.icl.saxon.output.HTMLEmitter.diff @@ -0,0 +1,13 @@ +diff --git a/HTMLEmitter.java b/HTMLEmitter.java +index 033b585..754b892 100644 +--- a/HTMLEmitter.java ++++ b/HTMLEmitter.java +@@ -326,7 +326,7 @@ public class HTMLEmitter extends XMLEmitter { + + if (inAttribute) { + if (ch[i]=='<') { +- writer.write('<'); // not escaped ++ writer.write("<"); // escaped + } else if (ch[i]=='>') { + writer.write(">"); // recommended for older browsers + } else if (ch[i]=='&') { diff --git a/de.gebit.integrity.runner/saxon/saxon-6.5.5-patched.jar b/de.gebit.integrity.runner/saxon/saxon-6.5.5-patched.jar new file mode 100644 index 000000000..c8705990c Binary files /dev/null and b/de.gebit.integrity.runner/saxon/saxon-6.5.5-patched.jar differ diff --git a/de.gebit.integrity.runner/src/de/gebit/integrity/runner/callbacks/xml/XmlWriterTestCallback.java b/de.gebit.integrity.runner/src/de/gebit/integrity/runner/callbacks/xml/XmlWriterTestCallback.java index cbeb63d5f..790b44a9a 100644 --- a/de.gebit.integrity.runner/src/de/gebit/integrity/runner/callbacks/xml/XmlWriterTestCallback.java +++ b/de.gebit.integrity.runner/src/de/gebit/integrity/runner/callbacks/xml/XmlWriterTestCallback.java @@ -11,7 +11,6 @@ import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileOutputStream; -import java.io.FilterOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.PrintWriter; @@ -558,6 +557,11 @@ public class XmlWriterTestCallback extends AbstractTestRunnerCallback { protected static final String XSLT_RESOURCE_NAME = System.getProperty(SYSPARAM_XSLT_RESOURCE, "resource/xhtml.xslt"); + /** + * The XSLT transformer factory property. + */ + protected static final String XSLT_TRANSFORMER_FACTORY_PROPERTY = "javax.xml.transform.TransformerFactory"; + /** * Creates a new instance. * @@ -1654,13 +1658,26 @@ protected int determineTransformThreadStackSize() { */ protected void transformResult(FileOutputStream aTargetStream) { try { - if (System.getProperty("javax.xml.transform.TransformerFactory") == null) { - // Explicitly specify the JRE-bundled XSLT transformer if nothing else was specified via the - // system property, so we at least know for sure what to expect - System.setProperty("javax.xml.transform.TransformerFactory", - "com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl"); - } + /* + * Explicitly specify the bundled, patched Saxon XSLT transformer. There is a problem with the XML source + * data that is copied to the transformed HTML result (into the "xmldata" element): since the output method + * for the serializer is set to HTML, Saxon assumes it is okay to not escape the < char in attributes, which + * is no problem for HTML, but strict XML requires those to be replaced by their corresponding entities. I + * could solve this by outputting strict XML, but that renders the output unrenderable by browsers :-( well, + * for some reason I don't fully understand at least, I'm no browser developer. To solve this, I have + * patched the HTMLEmitter inside the bundled Saxon XSLT transformer to escape this character as well, which + * seems to work just fine with browsers as well as XML parsers (which should then only parse until the end + * of the xmldata element - afterwards there's a lot of non-well-formed XML, which actually is HTML, + * coming). The mentioned patch is provided in the file com.icl.saxon.output.HTMLEmitter.diff. + */ + String tempOldProperty = System.getProperty(XSLT_TRANSFORMER_FACTORY_PROPERTY); + System.setProperty(XSLT_TRANSFORMER_FACTORY_PROPERTY, "com.icl.saxon.TransformerFactoryImpl"); TransformerFactory tempTransformerFactory = TransformerFactory.newInstance(); + if (tempOldProperty != null) { + System.setProperty(XSLT_TRANSFORMER_FACTORY_PROPERTY, tempOldProperty); + } else { + System.clearProperty(XSLT_TRANSFORMER_FACTORY_PROPERTY); + } Transformer tempTransformer = tempTransformerFactory.newTransformer(new StreamSource(getXsltStream())); tempTransformer.setOutputProperty(OutputKeys.METHOD, "html"); @@ -1668,107 +1685,7 @@ protected void transformResult(FileOutputStream aTargetStream) { Source tempSource = new JDOMSource(document); - /* - * There is a problem with the XML source data that is copied to the transformed HTML result (into the - * "xmldata" element): since the output method for the serializer is set to HTML, it seems to inevitably - * output '<' and '>' in attributes as characters, which is no problem for HTML, but strict XML requires - * those to be replaced by their corresponding entities. I could solve this by outputting strict XML, but - * that renders the output unrenderable by browsers :-( well, for some reason I don't fully understand at - * least, I'm no browser developer. To solve this, the following very ugly hack replaces the characters by - * their entities in all attribute values inside the xmldata section on a character stream level. That makes - * the xmldata content valid XML and thus conveniently parseable for example by a SAX parser (just be sure - * to stop the parsing when reaching the end of that section, because the HTML afterwards definitely doesn't - * parse as XML!), while keeping viewability on all browsers. It should not have any negative side-effects, - * apart from being disgusting and everything, but well, this can still be replaced by a more elegant - * solution if someone comes up with one. - */ - StreamResult tempResult = new StreamResult(new FilterOutputStream(aTargetStream) { - - private final char[] triggerOpenTagName = new char[] { 'x', 'm', 'l', 'd', 'a', 't', 'a' }; - - private final char[] triggerCloseTagName = new char[] { '/', 'x', 'm', 'l', 'd', 'a', 't', 'a' }; - - private static final char TRIGGER_TAG_START = '<'; - - private static final char TRIGGER_TAG_END = '<'; - - private static final char TRIGGER_ATTRIBUTE = '"'; - - private boolean insideXmlPart; - - private boolean insideAttribute; - - private boolean pastXmlPart; - - private int tagPosition; - - @Override - public void write(int aByte) throws IOException { - char tempChar = (char) aByte; - - if (!pastXmlPart) { - if (!insideAttribute) { - if (tempChar == TRIGGER_TAG_START) { - tagPosition = 0; - } else if (tempChar == TRIGGER_TAG_END) { - tagPosition = -1; - } else if (tagPosition >= 0) { - if (insideXmlPart && tempChar == TRIGGER_ATTRIBUTE) { - insideAttribute = true; - } else { - tagPosition++; - if (insideXmlPart) { - if (tagPosition < triggerCloseTagName.length - 1) { - if (tempChar != triggerCloseTagName[tagPosition]) { - tagPosition = 0; - } - } else if (tagPosition == triggerCloseTagName.length - 1) { - insideXmlPart = false; - pastXmlPart = true; - tagPosition = 0; - } - } else { - if (tagPosition < triggerOpenTagName.length - 1) { - if (tempChar != triggerOpenTagName[tagPosition]) { - tagPosition = 0; - } - } else if (tagPosition == triggerOpenTagName.length - 1) { - insideXmlPart = true; - pastXmlPart = false; - tagPosition = 0; - } - } - } - } - } else { - if (insideXmlPart) { - if (tempChar == TRIGGER_ATTRIBUTE) { - insideAttribute = false; - } else { - if (tempChar == '<') { - super.write("<".getBytes("UTF-8")); - return; - } else if (tempChar == '>') { - super.write(">".getBytes("UTF-8")); - return; - } - } - } - } - } - - super.write(aByte); - } - - @Override - public void write(byte[] someBytes, int anOffset, int aLength) throws IOException { - if (!pastXmlPart) { - super.write(someBytes, anOffset, aLength); - } else { - out.write(someBytes, anOffset, aLength); - } - } - }); + StreamResult tempResult = new StreamResult(aTargetStream); tempTransformer.transform(tempSource, tempResult); } catch (TransformerConfigurationException exc) {