Submitted by: Stephane Bailliez <sbailliez@imediation.com> git-svn-id: https://svn.apache.org/repos/asf/ant/core/trunk@268562 13f79535-47bb-0310-9956-ffa450edef68master
@@ -22,7 +22,7 @@ Other changes: | |||||
* A GUI Frontend: Antidote. This is currently in development. | * A GUI Frontend: Antidote. This is currently in development. | ||||
* New tasks: stylebook, propertyfile, depend, antlr, telnet, csc, | * New tasks: stylebook, propertyfile, depend, antlr, telnet, csc, | ||||
ilasm, apply, javah, several clearcase tasks | |||||
ilasm, apply, javah, several clearcase tasks, junitreport | |||||
* Added output attribute to <java>. | * Added output attribute to <java>. | ||||
@@ -193,6 +193,10 @@ | |||||
<exclude name="${optional.package}/ide/VAJ*.java" unless="vaj.present" /> | <exclude name="${optional.package}/ide/VAJ*.java" unless="vaj.present" /> | ||||
<exclude name="${optional.package}/perforce/*.java" unless="jakarta.oro.present" /> | <exclude name="${optional.package}/perforce/*.java" unless="jakarta.oro.present" /> | ||||
<exclude name="${optional.package}/sound/*.java" unless="jmf.present" /> | <exclude name="${optional.package}/sound/*.java" unless="jmf.present" /> | ||||
<exclude name="${optional.package}/junit/XMLResultAggregator.java" | |||||
unless="trax.present" /> | |||||
<exclude name="${optional.package}/junit/AggregateTransformer.java" | |||||
unless="trax.present" /> | |||||
</javac> | </javac> | ||||
<copy todir="${build.classes}"> | <copy todir="${build.classes}"> | ||||
@@ -212,6 +216,14 @@ | |||||
<include name="**/defaultManifest.mf" /> | <include name="**/defaultManifest.mf" /> | ||||
</fileset> | </fileset> | ||||
</copy> | </copy> | ||||
<copy todir="${build.classes}/${optional.package}/junit"> | |||||
<fileset dir="${java.dir}/${optional.package}/junit"> | |||||
<include name="html/**" /> | |||||
<include name="xsl/**" /> | |||||
</fileset> | |||||
</copy> | |||||
</target> | </target> | ||||
<!-- | <!-- | ||||
@@ -87,6 +87,7 @@ cccheckout=org.apache.tools.ant.taskdefs.optional.clearcase.CCCheckout | |||||
cccheckin=org.apache.tools.ant.taskdefs.optional.clearcase.CCCheckin | cccheckin=org.apache.tools.ant.taskdefs.optional.clearcase.CCCheckin | ||||
ccuncheckout=org.apache.tools.ant.taskdefs.optional.clearcase.CCUnCheckout | ccuncheckout=org.apache.tools.ant.taskdefs.optional.clearcase.CCUnCheckout | ||||
sound=org.apache.tools.ant.taskdefs.optional.sound.SoundTask | sound=org.apache.tools.ant.taskdefs.optional.sound.SoundTask | ||||
junitreport=org.apache.tools.ant.taskdefs.optional.junit.XMLResultAggregator | |||||
# deprecated ant tasks (kept for back compatibility) | # deprecated ant tasks (kept for back compatibility) | ||||
javadoc2=org.apache.tools.ant.taskdefs.Javadoc | javadoc2=org.apache.tools.ant.taskdefs.Javadoc | ||||
@@ -0,0 +1,528 @@ | |||||
/* | |||||
* The Apache Software License, Version 1.1 | |||||
* | |||||
* Copyright (c) 2000 The Apache Software Foundation. All rights | |||||
* reserved. | |||||
* | |||||
* Redistribution and use in source and binary forms, with or without | |||||
* modification, are permitted provided that the following conditions | |||||
* are met: | |||||
* | |||||
* 1. Redistributions of source code must retain the above copyright | |||||
* notice, this list of conditions and the following disclaimer. | |||||
* | |||||
* 2. Redistributions in binary form must reproduce the above copyright | |||||
* notice, this list of conditions and the following disclaimer in | |||||
* the documentation and/or other materials provided with the | |||||
* distribution. | |||||
* | |||||
* 3. The end-user documentation included with the redistribution, if | |||||
* any, must include the following acknowlegement: | |||||
* "This product includes software developed by the | |||||
* Apache Software Foundation (http://www.apache.org/)." | |||||
* Alternately, this acknowlegement may appear in the software itself, | |||||
* if and wherever such third-party acknowlegements normally appear. | |||||
* | |||||
* 4. The names "The Jakarta Project", "Ant", and "Apache Software | |||||
* Foundation" must not be used to endorse or promote products derived | |||||
* from this software without prior written permission. For written | |||||
* permission, please contact apache@apache.org. | |||||
* | |||||
* 5. Products derived from this software may not be called "Apache" | |||||
* nor may "Apache" appear in their names without prior written | |||||
* permission of the Apache Group. | |||||
* | |||||
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED | |||||
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES | |||||
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | |||||
* DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR | |||||
* ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | |||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | |||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF | |||||
* USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND | |||||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, | |||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT | |||||
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF | |||||
* SUCH DAMAGE. | |||||
* ==================================================================== | |||||
* | |||||
* This software consists of voluntary contributions made by many | |||||
* individuals on behalf of the Apache Software Foundation. For more | |||||
* information on the Apache Software Foundation, please see | |||||
* <http://www.apache.org/>. | |||||
*/ | |||||
package org.apache.tools.ant.taskdefs.optional.junit; | |||||
import org.apache.tools.ant.Task; | |||||
import org.apache.tools.ant.Project; | |||||
import org.apache.tools.ant.BuildException; | |||||
import java.io.File; | |||||
import java.io.FileInputStream; | |||||
import java.io.FileOutputStream; | |||||
import java.io.InputStream; | |||||
import java.io.OutputStream; | |||||
import java.io.FileNotFoundException; | |||||
import java.io.IOException; | |||||
import java.util.Enumeration; | |||||
import java.util.Hashtable; | |||||
import javax.xml.transform.Transformer; | |||||
import javax.xml.transform.TransformerFactory; | |||||
import javax.xml.transform.stream.StreamSource; | |||||
import javax.xml.transform.stream.StreamResult; | |||||
import javax.xml.transform.dom.DOMSource; | |||||
import javax.xml.transform.TransformerConfigurationException; | |||||
import javax.xml.transform.TransformerException; | |||||
import javax.xml.parsers.DocumentBuilderFactory; | |||||
import javax.xml.parsers.DocumentBuilder; | |||||
import javax.xml.parsers.ParserConfigurationException; | |||||
import org.w3c.dom.Document; | |||||
import org.w3c.dom.Element; | |||||
import org.w3c.dom.Node; | |||||
import org.w3c.dom.NodeList; | |||||
import org.w3c.dom.NamedNodeMap; | |||||
import org.xml.sax.SAXException; | |||||
/** | |||||
* Transform a JUnit xml report. | |||||
* The default transformation generates an html report in either framed or non-framed | |||||
* style. The non-framed style is convenient to have a concise report via mail, the | |||||
* framed report is much more convenient if you want to browse into different | |||||
* packages or testcases since it is a Javadoc like report. | |||||
* In the framed report, there are 3 frames: | |||||
* <ul> | |||||
* <li>packageListFrame - list of all packages. | |||||
* <li>classListFrame - summary of all testsuites belonging to a package or tests list. | |||||
* <li>classFrame - details of all tests made for a package or for a testsuite. | |||||
* </ul> | |||||
* As a default, the transformer will use its default stylesheets, they may not be | |||||
* be appropriate for users who wants to customize their reports, so you can indicates | |||||
* your own stylesheets by using <tt>setStyleDir()</tt>. | |||||
* Stylesheets must be as follows: | |||||
* <ul> | |||||
* <li><b>all-packages.xsl</b> create the package list. It creates | |||||
* all-packages.html file in the html folder and it is load in the packageListFrame</li> | |||||
* <li><b>all-classes.xsl</b> creates the class list. It creates the all-classes.html | |||||
* file in the html folder is loaded by the 'classListFrame' frame</li> | |||||
* <li><b>overview-packages.xsl</b> allows to get summary on all tests made | |||||
* for each packages and each class that not include in a package. The filename | |||||
* is overview-packages.html</li> | |||||
* <li><b>class-detail.xsl</b> is the style for the detail of the tests made on a class. | |||||
* the Html resulting page in write in the directory of the package and the name | |||||
* of this page is the name of the class with "-detail" element. For instance, | |||||
* the style is applied on the MyClass testsuite, the resulting filename is | |||||
* <u>MyClass-detail.html</u>. This file is load in the "classFrame" frame.</li> | |||||
* <li><b>package-summary.xsl</b> allows to create a summary on the package. | |||||
* The resulting html file is write in the package directory. The name of this | |||||
* file is <u>package-summary.html</u> This file is load in the "classFrame" frame.</li> | |||||
* <li><b>classes-list.xsl</b> create the list of the class in this package. | |||||
* The resulting html file is write in the package directory and it is load in | |||||
* the 'classListFrame' frame. The name of the resulting file is <u>class-list.html</u></li> | |||||
* <li> | |||||
* | |||||
* @author <a href="mailto:sbailliez@imediation.com">Stephane Bailliez</a> | |||||
* @author <a href="mailto:ndelahaye@imediation.com">Nicolas Delahaye</a> | |||||
*/ | |||||
public class AggregateTransformer { | |||||
public final static String ALLPACKAGES = "all-packages"; | |||||
public final static String ALLCLASSES = "all-classes"; | |||||
public final static String OVERVIEW_PACKAGES = "overview-packages"; | |||||
public final static String CLASS_DETAILS = "class-details"; | |||||
public final static String CLASSES_LIST = "classes-list"; | |||||
public final static String PACKAGE_SUMMARY = "package-summary"; | |||||
public final static String OVERVIEW_SUMMARY = "overview-summary"; | |||||
public final static String FRAMES = "frames"; | |||||
public final static String NOFRAMES = "noframes"; | |||||
/** Task */ | |||||
protected Task task; | |||||
/** the xml document to process */ | |||||
protected Document document; | |||||
/** the style directory. XSLs should be read from here if necessary */ | |||||
protected File styleDir; | |||||
/** the destination directory, this is the root from where html should be generated */ | |||||
protected File toDir; | |||||
/** the format to use for the report. Must be <tt>FRAMES</tt> or <tt>NOFRAMES</tt> */ | |||||
protected String format; | |||||
/** the file extension of the generated files. As a default it will be <tt>.html</tt> */ | |||||
protected String extension; | |||||
/** XML Parser factory */ | |||||
protected static final DocumentBuilderFactory dbfactory = DocumentBuilderFactory.newInstance(); | |||||
/** XSL Parser factory */ | |||||
protected static final TransformerFactory tfactory = TransformerFactory.newInstance(); | |||||
public AggregateTransformer(Task task){ | |||||
this.task = task; | |||||
} | |||||
public void setFormat(String format){ | |||||
this.format = format; | |||||
} | |||||
public void setXmlDocument(Document doc){ | |||||
this.document = doc; | |||||
} | |||||
/** | |||||
* Set the xml file to be processed. This is a helper if you want | |||||
* to set the file directly. Much more for testing purposes. | |||||
* @param xmlfile xml file to be processed | |||||
*/ | |||||
void setXmlfile(File xmlfile) throws BuildException { | |||||
try { | |||||
setXmlDocument(readDocument(xmlfile)); | |||||
} catch (Exception e){ | |||||
throw new BuildException("Error while parsing document: " + xmlfile, e); | |||||
} | |||||
} | |||||
/** | |||||
* set the style directory. It is optional and will override the | |||||
* default xsl used. | |||||
* @param styledir the directory containing the xsl files if the user | |||||
* would like to override with its own style. | |||||
*/ | |||||
public void setStyledir(File styledir){ | |||||
this.styleDir = styledir; | |||||
} | |||||
/** set the destination directory */ | |||||
public void setTodir(File todir){ | |||||
this.toDir = todir; | |||||
} | |||||
/** set the extension of the output files */ | |||||
public void setExtension(String ext){ | |||||
this.extension = ext; | |||||
} | |||||
/** get the extension, if it is null, it will use .html as the default */ | |||||
protected String getExtension(){ | |||||
if (extension == null) { | |||||
extension = ".html"; | |||||
} | |||||
return extension; | |||||
} | |||||
public void transform() throws BuildException { | |||||
checkOptions(); | |||||
try { | |||||
Element root = document.getDocumentElement(); | |||||
if (NOFRAMES.equals(format)) { | |||||
//createCascadingStyleSheet(); | |||||
createSinglePageSummary(root); | |||||
} else { | |||||
createFrameStructure(); | |||||
createCascadingStyleSheet(); | |||||
createPackageList(root); | |||||
createClassList(root); | |||||
createPackageOverview(root); | |||||
createAllTestSuiteDetails(root); | |||||
createAllPackageDetails(root); | |||||
} | |||||
} catch (Exception e){ | |||||
e.printStackTrace(); | |||||
throw new BuildException("Errors while applying transformations", e); | |||||
} | |||||
} | |||||
/** check for invalid options */ | |||||
protected void checkOptions() throws BuildException { | |||||
if ( !FRAMES.equals(format) && !NOFRAMES.equals(format)) { | |||||
throw new BuildException("Invalid format. Must be 'frames' or 'noframes' but was: '" + format + "'"); | |||||
} | |||||
// set the destination directory relative from the project if needed. | |||||
if (toDir == null) { | |||||
toDir = task.getProject().resolveFile("."); | |||||
} else if ( !toDir.isAbsolute() ) { | |||||
toDir = task.getProject().resolveFile(toDir.getPath()); | |||||
} | |||||
// create the directories if needed | |||||
if (!toDir.exists()) { | |||||
if (!toDir.mkdirs()){ | |||||
throw new BuildException("Could not create directory " + toDir); | |||||
} | |||||
} | |||||
} | |||||
/** create a single page summary */ | |||||
protected void createSinglePageSummary(Element root) throws IOException, TransformerException { | |||||
transform(root, OVERVIEW_SUMMARY + ".xsl", OVERVIEW_SUMMARY + getExtension()); | |||||
} | |||||
/** | |||||
* read the xml file that should be the resuiting file of the testcase. | |||||
* @param filename name of the xml resulting file of the testcase. | |||||
*/ | |||||
protected Document readDocument(File file) throws IOException, SAXException, ParserConfigurationException { | |||||
DocumentBuilder builder = dbfactory.newDocumentBuilder(); | |||||
InputStream in = new FileInputStream(file); | |||||
try { | |||||
return builder.parse(in); | |||||
} finally { | |||||
in.close(); | |||||
} | |||||
} | |||||
protected void createCascadingStyleSheet() throws IOException, TransformerException { | |||||
if (styleDir == null) { | |||||
InputStream in = getResourceAsStream("html/stylesheet.css"); | |||||
OutputStream out = new FileOutputStream( new File(toDir, "stylesheet.css")); | |||||
copy(in, out); | |||||
} | |||||
} | |||||
protected void createFrameStructure() throws IOException, TransformerException{ | |||||
if (styleDir == null) { | |||||
InputStream in = getResourceAsStream("html/index.html"); | |||||
OutputStream out = new FileOutputStream( new File(toDir, "index.html") ); | |||||
copy(in, out); | |||||
} | |||||
} | |||||
/** | |||||
* Create the list of all packages. | |||||
* @param root root of the xml document. | |||||
*/ | |||||
protected void createPackageList(Node root) throws TransformerException { | |||||
transform(root, ALLPACKAGES + ".xsl", ALLPACKAGES + getExtension()); | |||||
} | |||||
/** | |||||
* Create the list of all classes. | |||||
* @param root root of the xml document. | |||||
*/ | |||||
protected void createClassList(Node root) throws TransformerException { | |||||
transform(root, ALLCLASSES + ".xsl", ALLCLASSES + getExtension()); | |||||
} | |||||
/** | |||||
* Create the summary used in the overview. | |||||
* @param root root of the xml document. | |||||
*/ | |||||
protected void createPackageOverview(Node root) throws TransformerException { | |||||
transform(root, OVERVIEW_PACKAGES + ".xsl", OVERVIEW_PACKAGES + getExtension()); | |||||
} | |||||
/** | |||||
* @return the list of all packages that exists defined in testsuite nodes | |||||
*/ | |||||
protected Enumeration getPackages(Element root){ | |||||
Hashtable map = new Hashtable(); | |||||
NodeList testsuites = root.getElementsByTagName(XMLConstants.TESTSUITE); | |||||
final int size = testsuites.getLength(); | |||||
for (int i = 0; i < size; i++){ | |||||
Element testsuite = (Element) testsuites.item(i); | |||||
String packageName = testsuite.getAttribute(XMLConstants.ATTR_PACKAGE); | |||||
if (packageName == null){ | |||||
//@todo replace the exception by something else | |||||
throw new IllegalStateException("Invalid 'testsuite' node: should contains 'package' attribute"); | |||||
} | |||||
map.put(packageName, packageName); | |||||
} | |||||
return map.keys(); | |||||
} | |||||
/** | |||||
* create all resulting html pages for all testsuites. | |||||
* @param root should be 'testsuites' node. | |||||
*/ | |||||
protected void createAllTestSuiteDetails(Element root) throws TransformerException { | |||||
NodeList testsuites = root.getElementsByTagName(XMLConstants.TESTSUITE); | |||||
final int size = testsuites.getLength(); | |||||
for (int i = 0; i < size; i++){ | |||||
Element testsuite = (Element) testsuites.item(i); | |||||
createTestSuiteDetails(testsuite); | |||||
} | |||||
} | |||||
/** | |||||
* create the html resulting page of one testsuite. | |||||
* @param root should be 'testsuite' node. | |||||
*/ | |||||
protected void createTestSuiteDetails(Element testsuite) throws TransformerException { | |||||
String packageName = testsuite.getAttribute(XMLConstants.ATTR_PACKAGE); | |||||
String pkgPath = packageToPath(packageName); | |||||
// get the class name | |||||
String name = testsuite.getAttribute(XMLConstants.ATTR_NAME); | |||||
// get the name of the testsuite and create the filename of the ouput html page | |||||
String filename = name + "-details" + getExtension(); | |||||
String fullpathname = pkgPath + filename; // there's already end path separator to pkgPath | |||||
// apply the style on the document. | |||||
transform(testsuite, CLASS_DETAILS + ".xsl", fullpathname); | |||||
} | |||||
/** | |||||
* create the html resulting page of the summary of each package of the root element. | |||||
* @param root should be 'testsuites' node. | |||||
*/ | |||||
protected void createAllPackageDetails(Element root) throws TransformerException, ParserConfigurationException { | |||||
Enumeration packages = getPackages(root); | |||||
while ( packages.hasMoreElements() ){ | |||||
String pkgname = (String)packages.nextElement(); | |||||
// for each package get the list of its testsuite. | |||||
DOMUtil.NodeFilter pkgFilter = new PackageFilter(pkgname); | |||||
NodeList testsuites = DOMUtil.listChildNodes(root, pkgFilter, false); | |||||
Element doc = buildDocument(testsuites); | |||||
// skip package details if the package does not exist (root package) | |||||
if( !pkgname.equals("") ){ | |||||
createPackageDetails(doc, pkgname); | |||||
} | |||||
} | |||||
} | |||||
protected String packageToPath(String pkgname){ | |||||
if (!pkgname.equals("")) { | |||||
return pkgname.replace('.', File.separatorChar) + File.separatorChar; | |||||
} | |||||
return "." + File.separatorChar; | |||||
} | |||||
/** | |||||
* create the html resulting page of the summary of a package . | |||||
* @param root should be 'testsuites' node. | |||||
* @param pkgname Name of the package that we want a summary. | |||||
*/ | |||||
protected void createPackageDetails(Node root, String pkgname) throws TransformerException { | |||||
String path = packageToPath(pkgname); | |||||
// apply style to get the list of the classes of this package and | |||||
// display it in the classListFrame. | |||||
transform(root, CLASSES_LIST + ".xsl", path + CLASSES_LIST + getExtension()); | |||||
// apply style to get a summary on this package. | |||||
transform(root, PACKAGE_SUMMARY + ".xsl", path + PACKAGE_SUMMARY + getExtension()); | |||||
} | |||||
/** | |||||
* Create an element root ("testsuites") | |||||
* and import all nodes as children of this element. | |||||
* | |||||
*/ | |||||
protected Element buildDocument(NodeList list) throws ParserConfigurationException { | |||||
DocumentBuilder builder = dbfactory.newDocumentBuilder(); | |||||
Document doc = builder.newDocument(); | |||||
Element elem = doc.createElement(XMLConstants.TESTSUITES); | |||||
final int len = list.getLength(); | |||||
for(int i=0 ; i < len ; i++) { | |||||
DOMUtil.importNode(elem, list.item(i)); | |||||
} | |||||
return elem; | |||||
} | |||||
/** | |||||
* Apply a template on a part of the xml document. | |||||
* | |||||
* @param root root of the document fragment | |||||
* @param xslfile style file | |||||
* @param outfilename filename of the result of the style applied on the Node | |||||
* | |||||
* @throws TransformerException SAX Parsing Error on the style Sheet. | |||||
*/ | |||||
protected void transform(Node root, String xslname, String htmlname) throws TransformerException { | |||||
try{ | |||||
final long t0 = System.currentTimeMillis(); | |||||
StreamSource xsl_source = getXSLStreamSource(xslname); | |||||
Transformer transformer = tfactory.newTransformer(xsl_source); | |||||
File htmlfile = new File(toDir, htmlname); | |||||
// create the directory if it does not exist | |||||
File dir = new File(htmlfile.getParent()); // getParentFile is in JDK1.2+ | |||||
if (!dir.exists()) { | |||||
dir.mkdirs(); | |||||
} | |||||
task.log("Applying '" + xslname + "'. Generating '" + htmlfile + "'", Project.MSG_VERBOSE); | |||||
transformer.transform( new DOMSource(root), new StreamResult(htmlfile)); | |||||
final long dt = System.currentTimeMillis() - t0; | |||||
task.log("Transform time: " + dt + "ms"); | |||||
} catch (IOException e){ | |||||
task.log(e.getMessage(), Project.MSG_ERR); | |||||
e.printStackTrace(); //@todo bad, change this | |||||
throw new TransformerException(e.getMessage()); | |||||
} | |||||
} | |||||
/** | |||||
* default xsls are embedded in the distribution jar. As a default we will use | |||||
* them, otherwise we will get the one supplied by the client in a given | |||||
* directory. It must have the same name. | |||||
*/ | |||||
protected StreamSource getXSLStreamSource(String name) throws IOException { | |||||
InputStream in; | |||||
String systemId; //we need this because there are references in xsls | |||||
if (styleDir == null){ | |||||
in = getResourceAsStream("xsl/" + name); | |||||
systemId = getClass().getResource("xsl/" + name).toString(); | |||||
} else { | |||||
File f = new File(styleDir, name); | |||||
in= new FileInputStream(f); | |||||
systemId = f.getAbsolutePath(); | |||||
} | |||||
StreamSource ss = new StreamSource(in); | |||||
ss.setSystemId(systemId); | |||||
return ss; | |||||
} | |||||
private InputStream getResourceAsStream(String name) throws FileNotFoundException { | |||||
InputStream in = getClass().getResourceAsStream(name); | |||||
if (in == null) { | |||||
throw new FileNotFoundException("Could not find resource '" + name + "'"); | |||||
} | |||||
return in; | |||||
} | |||||
/** Do some raw stream copying */ | |||||
private static void copy(InputStream in, OutputStream out) throws IOException { | |||||
int size = -1; | |||||
byte[] buffer = new byte[1024]; | |||||
// Make the copy | |||||
while( (size = in.read(buffer)) != -1){ | |||||
out.write(buffer,0,size); | |||||
} | |||||
} | |||||
/** | |||||
* allow us to check if the node is a object of a specific package. | |||||
*/ | |||||
protected static class PackageFilter implements DOMUtil.NodeFilter { | |||||
private final String pkgName; | |||||
PackageFilter(String pkgname) { | |||||
this.pkgName = pkgname; | |||||
} | |||||
/** | |||||
* if the node receive is not a element then return false | |||||
* check if the node is a class of this package. | |||||
*/ | |||||
public boolean accept(Node node) { | |||||
String pkgname = DOMUtil.getNodeAttribute(node, XMLConstants.ATTR_PACKAGE); | |||||
return pkgName.equals(pkgname); | |||||
} | |||||
} | |||||
} |
@@ -0,0 +1,310 @@ | |||||
/* | |||||
* The Apache Software License, Version 1.1 | |||||
* | |||||
* Copyright (c) 2001 The Apache Software Foundation. All rights | |||||
* reserved. | |||||
* | |||||
* Redistribution and use in source and binary forms, with or without | |||||
* modification, are permitted provided that the following conditions | |||||
* are met: | |||||
* | |||||
* 1. Redistributions of source code must retain the above copyright | |||||
* notice, this list of conditions and the following disclaimer. | |||||
* | |||||
* 2. Redistributions in binary form must reproduce the above copyright | |||||
* notice, this list of conditions and the following disclaimer in | |||||
* the documentation and/or other materials provided with the | |||||
* distribution. | |||||
* | |||||
* 3. The end-user documentation included with the redistribution, if | |||||
* any, must include the following acknowlegement: | |||||
* "This product includes software developed by the | |||||
* Apache Software Foundation (http://www.apache.org/)." | |||||
* Alternately, this acknowlegement may appear in the software itself, | |||||
* if and wherever such third-party acknowlegements normally appear. | |||||
* | |||||
* 4. The names "The Jakarta Project", "Ant", and "Apache Software | |||||
* Foundation" must not be used to endorse or promote products derived | |||||
* from this software without prior written permission. For written | |||||
* permission, please contact apache@apache.org. | |||||
* | |||||
* 5. Products derived from this software may not be called "Apache" | |||||
* nor may "Apache" appear in their names without prior written | |||||
* permission of the Apache Group. | |||||
* | |||||
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED | |||||
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES | |||||
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | |||||
* DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR | |||||
* ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | |||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | |||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF | |||||
* USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND | |||||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, | |||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT | |||||
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF | |||||
* SUCH DAMAGE. | |||||
* ==================================================================== | |||||
* | |||||
* This software consists of voluntary contributions made by many | |||||
* individuals on behalf of the Apache Software Foundation. For more | |||||
* information on the Apache Software Foundation, please see | |||||
* <http://www.apache.org/>. | |||||
*/ | |||||
package org.apache.tools.ant.taskdefs.optional.junit; | |||||
import java.io.File; | |||||
import java.io.IOException; | |||||
import java.io.OutputStream; | |||||
import java.io.PrintWriter; | |||||
import java.io.FileOutputStream; | |||||
import java.util.Enumeration; | |||||
import java.util.Vector; | |||||
import org.w3c.dom.Element; | |||||
import org.w3c.dom.Document; | |||||
import org.w3c.dom.Node; | |||||
import org.w3c.dom.*; | |||||
import org.xml.sax.SAXException; | |||||
import javax.xml.parsers.DocumentBuilder; | |||||
import javax.xml.parsers.DocumentBuilderFactory; | |||||
import org.apache.tools.ant.Project; | |||||
import org.apache.tools.ant.Task; | |||||
import org.apache.tools.ant.DirectoryScanner; | |||||
import org.apache.tools.ant.BuildException; | |||||
import org.apache.tools.ant.types.FileSet; | |||||
import org.apache.tools.ant.util.DOMElementWriter; | |||||
/** | |||||
* This is an helper class that will aggregate all testsuites under a specific | |||||
* directory and create a new single document. It is not particulary clean but | |||||
* should be helpful while I am thinking about another technique. | |||||
* | |||||
* The main problem is due to the fact that a JVM can be forked for a testcase | |||||
* thus making it impossible to aggregate all testcases since the listener is | |||||
* (obviously) in the forked JVM. A solution could be to write a | |||||
* TestListener that will receive events from the TestRunner via sockets. This | |||||
* is IMHO the simplest way to do it to avoid this file hacking thing. | |||||
* | |||||
* @author <a href="mailto:sbailliez@imediation.com">Stephane Bailliez</a> | |||||
*/ | |||||
public class XMLResultAggregator extends Task implements XMLConstants { | |||||
/** the list of all filesets, that should contains the xml to aggregate */ | |||||
protected Vector filesets = new Vector(); | |||||
/** the name of the result file */ | |||||
protected String toFile; | |||||
/** the directory to write the file to */ | |||||
protected File toDir; | |||||
protected Vector transformers = new Vector(); | |||||
/** the default directory: <tt>.</tt>. It is resolved from the project directory */ | |||||
public final static String DEFAULT_DIR = "."; | |||||
/** the default file name: <tt>TESTS-TestSuites.xml</tt> */ | |||||
public final static String DEFAULT_FILENAME = "TESTS-TestSuites.xml"; | |||||
public AggregateTransformer createReport(){ | |||||
AggregateTransformer transformer = new AggregateTransformer(this); | |||||
transformers.add(transformer); | |||||
return transformer; | |||||
} | |||||
/** | |||||
* Set the name of the file aggregating the results. It must be relative | |||||
* from the <tt>todir</tt> attribute. If not set it will use {@link DEFAULT_FILENAME} | |||||
* @param value the name of the file. | |||||
* @see #setTodir(File) | |||||
*/ | |||||
public void setTofile(String value){ | |||||
toFile = value; | |||||
} | |||||
/** | |||||
* Set the destination directory where the results should be written. If not | |||||
* set if will use {@link DEFAULT_DIR}. When given a relative directory | |||||
* it will resolve it from the project directory. | |||||
* @param value the directory where to write the results, absolute or | |||||
* relative. | |||||
*/ | |||||
public void setTodir(File value){ | |||||
toDir = value; | |||||
} | |||||
/** | |||||
* Add a new fileset containing the xml results to aggregate | |||||
* @param fs the new fileset of xml results. | |||||
*/ | |||||
public void addFileSet(FileSet fs) { | |||||
filesets.addElement(fs); | |||||
} | |||||
/** | |||||
* Aggregate all testsuites into a single document and write it to the | |||||
* specified directory and file. | |||||
* @throws BuildException thrown if there is a serious error while writing | |||||
* the document. | |||||
*/ | |||||
public void execute() throws BuildException { | |||||
Element rootElement = createDocument(); | |||||
File destFile = getDestinationFile(); | |||||
// write the document | |||||
try { | |||||
writeDOMTree(rootElement.getOwnerDocument(), destFile ); | |||||
} catch (IOException e){ | |||||
throw new BuildException("Unable to write test aggregate to '" + destFile + "'", e); | |||||
} | |||||
// apply transformation | |||||
Enumeration enum = transformers.elements(); | |||||
while (enum.hasMoreElements()) { | |||||
AggregateTransformer transformer = | |||||
(AggregateTransformer) enum.nextElement(); | |||||
transformer.setXmlDocument(rootElement.getOwnerDocument()); | |||||
transformer.transform(); | |||||
} | |||||
} | |||||
/** | |||||
* get the full destination file where to write the result. It is made of | |||||
* the <tt>todir</tt> and <tt>tofile</tt> attributes. | |||||
* @return the destination file where should be written the result file. | |||||
*/ | |||||
protected File getDestinationFile(){ | |||||
if (toFile == null){ | |||||
toFile = DEFAULT_FILENAME; | |||||
} | |||||
if (toDir == null){ | |||||
toDir = project.resolveFile(DEFAULT_DIR); | |||||
} | |||||
return new File(toDir, toFile); | |||||
} | |||||
/** | |||||
* @return all files in the fileset that end with a '.xml'. | |||||
*/ | |||||
protected File[] getFiles() { | |||||
Vector v = new Vector(); | |||||
final int size = filesets.size(); | |||||
for (int i = 0; i < size; i++) { | |||||
FileSet fs = (FileSet) filesets.elementAt(i); | |||||
DirectoryScanner ds = fs.getDirectoryScanner(project); | |||||
ds.scan(); | |||||
String[] f = ds.getIncludedFiles(); | |||||
for (int j = 0; j < f.length; j++) { | |||||
String pathname = f[j]; | |||||
if ( pathname.endsWith(".xml") ) { | |||||
File file = new File(ds.getBasedir(), pathname); | |||||
file = project.resolveFile(file.getPath()); | |||||
v.addElement( file ); | |||||
} | |||||
} | |||||
} | |||||
File[] files = new File[v.size()]; | |||||
v.copyInto(files); | |||||
return files; | |||||
} | |||||
//----- from now, the methods are all related to DOM tree manipulation | |||||
/** | |||||
* Write the DOM tree to a file. | |||||
* @param doc the XML document to dump to disk. | |||||
* @param file the filename to write the document to. Should obviouslly be a .xml file. | |||||
* @throws IOException thrown if there is an error while writing the content. | |||||
*/ | |||||
protected void writeDOMTree(Document doc, File file) throws IOException { | |||||
OutputStream out = new FileOutputStream( file ); | |||||
PrintWriter wri = new PrintWriter(out); | |||||
wri.write("<?xml version=\"1.0\"?>\n"); | |||||
(new DOMElementWriter()).write(doc.getDocumentElement(), wri, 0, " "); | |||||
wri.flush(); | |||||
wri.close(); | |||||
// writers do not throw exceptions, so check for them. | |||||
if (wri.checkError()){ | |||||
throw new IOException("Error while writing DOM content"); | |||||
} | |||||
} | |||||
/** | |||||
* Create a DOM tree with firstchild as 'testsuites' and aggregates all | |||||
* testsuite results that exists in the base directory. | |||||
* @return the root element of DOM tree that aggregates all testsuites. | |||||
*/ | |||||
protected Element createDocument() { | |||||
// create the dom tree | |||||
DocumentBuilder builder = getDocumentBuilder(); | |||||
Document doc = builder.newDocument(); | |||||
Element rootElement = doc.createElement(TESTSUITES); | |||||
doc.appendChild(rootElement); | |||||
// get all files and add them to the document | |||||
File[] files = getFiles(); | |||||
for (int i = 0; i < files.length; i++) { | |||||
try { | |||||
log("Parsing file: '" + files[i] + "'", Project.MSG_VERBOSE); | |||||
Document testsuiteDoc = builder.parse( files[i] ); | |||||
Element elem = testsuiteDoc.getDocumentElement(); | |||||
// make sure that this is REALLY a testsuite. | |||||
if ( TESTSUITE.equals(elem.getNodeName()) ) { | |||||
addTestSuite(rootElement, elem); | |||||
} else { | |||||
// issue a warning. | |||||
log("the file " + files[i] + " is not a valid testsuite XML document", Project.MSG_WARN); | |||||
} | |||||
} catch (SAXException e){ | |||||
// a testcase might have failed and write a zero-length document, | |||||
// It has already failed, but hey.... mm. just put a warning | |||||
log("The file " + files[i] + " is not a valid XML document. It is possibly corrupted.", Project.MSG_WARN); | |||||
} catch (IOException e){ | |||||
log("Error while accessing file " + files[i] + ": " + e.getMessage(), Project.MSG_ERR); | |||||
} | |||||
} | |||||
return rootElement; | |||||
} | |||||
/** | |||||
* Add a new testsuite node to the document, the main difference is that it | |||||
* split the previous fully qualified name into a package and a name. | |||||
* For example: <tt>org.apache.Whatever</tt> will be splitted in | |||||
* <tt>org.apache</tt> and <tt>Whatever</tt>. | |||||
* @param root the root element to which the <tt>testsuite</tt> node should | |||||
* be appended. | |||||
* @param testsuite the element to append to the given root. It will slightly | |||||
* modify the original node to change the name attribute and add | |||||
* a package one. | |||||
*/ | |||||
protected void addTestSuite(Element root, Element testsuite){ | |||||
String fullclassname = testsuite.getAttribute(ATTR_NAME); | |||||
int pos = fullclassname.lastIndexOf('.'); | |||||
// a missing . might imply no package at all. Don't get fooled. | |||||
String pkgName = (pos == -1) ? "" : fullclassname.substring(0, pos); | |||||
String classname = (pos == -1) ? fullclassname : fullclassname.substring(pos + 1); | |||||
Element copy = (Element)DOMUtil.importNode(root, testsuite); | |||||
// modify the name attribute and set the package | |||||
copy.setAttribute(ATTR_NAME, classname); | |||||
copy.setAttribute(ATTR_PACKAGE, pkgName); | |||||
} | |||||
/** | |||||
* Create a new document builder. Will issue an <tt>ExceptionInitializerError</tt> | |||||
* if something is going wrong. It is fatal anyway. | |||||
* @return a new document builder to create a DOM | |||||
* @todo factorize this somewhere else. It is duplicated code. | |||||
*/ | |||||
private static DocumentBuilder getDocumentBuilder() { | |||||
try { | |||||
return DocumentBuilderFactory.newInstance().newDocumentBuilder(); | |||||
} catch(Exception exc) { | |||||
throw new ExceptionInInitializerError(exc); | |||||
} | |||||
} | |||||
} |
@@ -0,0 +1,21 @@ | |||||
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN""http://www.w3.org/TR/REC-html40/loose.dtd> | |||||
<HTML> | |||||
<HEAD> | |||||
<TITLE> | |||||
Unit Tests Results. | |||||
</TITLE> | |||||
</HEAD> | |||||
<FRAMESET cols="20%,80%"> | |||||
<FRAMESET rows="30%,70%"> | |||||
<FRAME src="all-packages.html" name="packageListFrame"> | |||||
<FRAME src="all-classes.html" name="classListFrame"> | |||||
</FRAMESET> | |||||
<FRAME src="overview-packages.html" name="classFrame"> | |||||
</FRAMESET> | |||||
<NOFRAMES> | |||||
<H2>Frame Alert</H2> | |||||
<P> | |||||
This document is designed to be viewed using the frames feature. If you see this message, you are using a non-frame-capable web client. | |||||
</P> | |||||
</HTML> |
@@ -0,0 +1,35 @@ | |||||
BODY { | |||||
font:normal 68% verdana,arial,helvetica; | |||||
color:#000000; | |||||
} | |||||
TD { | |||||
FONT-SIZE: 68% | |||||
} | |||||
P { | |||||
line-height:1.5em; | |||||
margin-top:0.5em; margin-bottom:1.0em; | |||||
} | |||||
H1 { | |||||
MARGIN: 0px 0px 5px; FONT: 165% verdana,arial,helvetica | |||||
} | |||||
H2 { | |||||
MARGIN-TOP: 1em; MARGIN-BOTTOM: 0.5em; FONT: bold 125% verdana,arial,helvetica | |||||
} | |||||
H3 { | |||||
MARGIN-BOTTOM: 0.5em; FONT: bold 115% verdana,arial,helvetica | |||||
} | |||||
H4 { | |||||
MARGIN-BOTTOM: 0.5em; FONT: bold 100% verdana,arial,helvetica | |||||
} | |||||
H5 { | |||||
MARGIN-BOTTOM: 0.5em; FONT: bold 100% verdana,arial,helvetica | |||||
} | |||||
H6 { | |||||
MARGIN-BOTTOM: 0.5em; FONT: bold 100% verdana,arial,helvetica | |||||
} | |||||
.Error { | |||||
font-weight:bold; color:red; | |||||
} | |||||
.Failure { | |||||
font-weight:bold; color:purple; | |||||
} |
@@ -0,0 +1,40 @@ | |||||
<?xml version="1.0" encoding="ISO-8859-1"?> | |||||
<!-- This style sheet should contain just a named templates that used in the other specific templates --> | |||||
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> | |||||
<xsl:include href="toolkit.xsl"/> | |||||
<xsl:template match="testsuites"> | |||||
<HTML> | |||||
<HEAD> | |||||
<LINK REL ="stylesheet" TYPE="text/css" HREF="./stylesheet.css" TITLE="Style"/> | |||||
</HEAD> | |||||
<BODY onload="open('overview-packages.html','classFrame')"> | |||||
<H2>Classes</H2> | |||||
<p> | |||||
<TABLE WIDTH="100%"> | |||||
<xsl:apply-templates select="testsuite"> | |||||
<xsl:sort select="@name"/> | |||||
</xsl:apply-templates> | |||||
</TABLE> | |||||
</p> | |||||
</BODY> | |||||
</HTML> | |||||
</xsl:template> | |||||
<xsl:template match="testsuite"> | |||||
<tr> | |||||
<td nowrap="nowrap"> | |||||
<a target="classFrame"> | |||||
<xsl:attribute name="href"> | |||||
<xsl:if test="not(@package='')"> | |||||
<xsl:value-of select="translate(@package,'.','/')"/><xsl:text>/</xsl:text> | |||||
</xsl:if><xsl:value-of select="@name"/><xsl:text>-details.html</xsl:text> | |||||
</xsl:attribute> | |||||
<xsl:value-of select="@name"/></a> | |||||
</td> | |||||
</tr> | |||||
</xsl:template> | |||||
</xsl:stylesheet> |
@@ -0,0 +1,42 @@ | |||||
<?xml version="1.0" encoding="ISO-8859-1"?> | |||||
<!-- This style sheet should contain just a named templates that used in the other specific templates --> | |||||
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> | |||||
<!-- import the commun templates --> | |||||
<xsl:include href="toolkit.xsl"/> | |||||
<xsl:template match="testsuites"> | |||||
<HTML> | |||||
<HEAD> | |||||
<LINK REL ="stylesheet" TYPE="text/css" HREF="./stylesheet.css" TITLE="Style"/> | |||||
</HEAD> | |||||
<BODY> | |||||
<H2><a href="all-classes.html" target="classListFrame">Home</a></H2> | |||||
<!-- create a summary on this testcase--> | |||||
<!--xsl:call-template name="SummaryTableHeadRootPackage"/--> | |||||
<H2>Packages</H2> | |||||
<!-- Get the list of the subpackage --> | |||||
<p> | |||||
<table width="100%"> | |||||
<!-- For each packages node apply the style describe in the below template--> | |||||
<xsl:apply-templates select="testsuite[not(./@package = preceding-sibling::testsuite/@package)]"> | |||||
<xsl:sort select="@package"/> | |||||
</xsl:apply-templates> | |||||
</table> | |||||
</p> | |||||
</BODY> | |||||
</HTML> | |||||
</xsl:template> | |||||
<xsl:template match="testsuite"> | |||||
<tr> | |||||
<td nowrap="nowrap"> | |||||
<a href="{translate(@package,'.','/')}/package-summary.html" target="classFrame"><xsl:value-of select="@package"/></a> | |||||
</td> | |||||
</tr> | |||||
</xsl:template> | |||||
</xsl:stylesheet> |
@@ -0,0 +1,37 @@ | |||||
<?xml version="1.0" encoding="ISO-8859-1"?> | |||||
<!-- This style sheet should contain just a named templates that used in the other specific templates --> | |||||
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> | |||||
<!-- import the commun templates --> | |||||
<xsl:include href="toolkit.xsl"/> | |||||
<xsl:template match="testsuite"> | |||||
<HTML> | |||||
<HEAD> | |||||
<LINK REL ="stylesheet" TYPE="text/css" TITLE="Style"> | |||||
<xsl:attribute name="href"><xsl:call-template name="path"><xsl:with-param name="path" select="@package"/></xsl:call-template>stylesheet.css</xsl:attribute> | |||||
</LINK> | |||||
</HEAD> | |||||
<BODY> | |||||
<xsl:call-template name="header"> | |||||
<xsl:with-param name="useFrame">yes</xsl:with-param> | |||||
<xsl:with-param name="path" select="@package"/> | |||||
</xsl:call-template> | |||||
<H2>Class <xsl:if test="not(@package = '')"><xsl:value-of select="@package"/>.</xsl:if><xsl:value-of select="@name"/></H2> | |||||
<p> | |||||
<h3>TestCase <xsl:value-of select="@name"/></h3> | |||||
<table border="0" cellpadding="5" cellspacing="2" width="95%"> | |||||
<!-- Header --> | |||||
<xsl:call-template name="classesSummaryHeader"/> | |||||
<!-- match the testcases of this package --> | |||||
<xsl:apply-templates select="testcase"/> | |||||
</table> | |||||
</p> | |||||
</BODY> | |||||
</HTML> | |||||
</xsl:template> | |||||
</xsl:stylesheet> |
@@ -0,0 +1,46 @@ | |||||
<?xml version="1.0" encoding="ISO-8859-1"?> | |||||
<!-- This style sheet should contain just a named templates that used in the other specific templates --> | |||||
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> | |||||
<!-- import the commun templates --> | |||||
<xsl:include href="toolkit.xsl"/> | |||||
<xsl:template match="testsuites"> | |||||
<HTML> | |||||
<HEAD> | |||||
<LINK REL ="stylesheet" TYPE="text/css" TITLE="Style"> | |||||
<xsl:attribute name="href"><xsl:call-template name="path"><xsl:with-param name="path" select="testsuite[position() = 1]/@package"/></xsl:call-template>stylesheet.css</xsl:attribute> | |||||
</LINK> | |||||
</HEAD> | |||||
<BODY> | |||||
<table width="100%"> | |||||
<tr> | |||||
<td nowrap="nowrap"> | |||||
<H2><a href="package-summary.html" target="classFrame"><xsl:value-of select="testsuite/@package"/></a></H2> | |||||
</td> | |||||
</tr> | |||||
</table> | |||||
<H2>Classes</H2> | |||||
<p> | |||||
<TABLE WIDTH="100%"> | |||||
<xsl:apply-templates select="testsuite"> | |||||
<xsl:sort select="@name"/> | |||||
</xsl:apply-templates> | |||||
</TABLE> | |||||
</p> | |||||
</BODY> | |||||
</HTML> | |||||
</xsl:template> | |||||
<xsl:template match="testsuite"> | |||||
<tr> | |||||
<td nowrap="nowrap"> | |||||
<a href="{@name}-details.html" target="classFrame"><xsl:value-of select="@name"/></a> | |||||
</td> | |||||
</tr> | |||||
</xsl:template> | |||||
</xsl:stylesheet> |
@@ -0,0 +1,90 @@ | |||||
<?xml version="1.0" encoding="ISO-8859-1"?> | |||||
<!-- This style sheet should contain just a named templates that used in the other specific templates --> | |||||
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> | |||||
<xsl:include href="toolkit.xsl"/> | |||||
<!-- Calculate all summary values --> | |||||
<xsl:variable name="testCount" select="sum(//testsuite/@tests)"/> | |||||
<xsl:variable name="errorCount" select="sum(//testsuite/@errors)"/> | |||||
<xsl:variable name="failureCount" select="sum(//testsuite/@failures)"/> | |||||
<xsl:variable name="timeCount" select="sum(//testsuite/@time)"/> | |||||
<xsl:variable name="successRate" select="($testCount - $failureCount - $errorCount) div $testCount"/> | |||||
<xsl:template match="testsuites"> | |||||
<HTML> | |||||
<HEAD> | |||||
<LINK REL ="stylesheet" TYPE="text/css" HREF="stylesheet.css" TITLE="Style"/> | |||||
</HEAD> | |||||
<BODY> | |||||
<xsl:call-template name="header"> | |||||
<xsl:with-param name="useFrame">yes</xsl:with-param> | |||||
</xsl:call-template> | |||||
<xsl:call-template name="summary"/> | |||||
<xsl:if test="count(testsuite[not(./@package = preceding-sibling::testsuite/@package)])>0"> | |||||
<h2>Packages</h2> | |||||
<table border="0" cellpadding="5" cellspacing="2" width="95%"> | |||||
<!--Header--> | |||||
<xsl:call-template name="packageSummaryHeader"/> | |||||
<!-- write a summary for the package --> | |||||
<xsl:apply-templates select="testsuite[not(./@package = preceding-sibling::testsuite/@package) and not(./@package = '') ]" mode="package"> | |||||
<xsl:sort select="@package"/> | |||||
</xsl:apply-templates> | |||||
</table> | |||||
<br/> | |||||
</xsl:if> | |||||
<xsl:if test="count(testsuite[./@package = ''])>0"> | |||||
<h2>Classes</h2> | |||||
<table border="0" cellpadding="5" cellspacing="2" width="95%"> | |||||
<!--Header--> | |||||
<xsl:call-template name="packageSummaryHeader"/> | |||||
<!-- write a summary for the package --> | |||||
<xsl:apply-templates select="testsuite[./@package = '']" mode="class"> | |||||
<xsl:sort select="@name"/> | |||||
</xsl:apply-templates> | |||||
</table> | |||||
</xsl:if> | |||||
</BODY> | |||||
</HTML> | |||||
</xsl:template> | |||||
<xsl:template match="testsuite" mode="package"> | |||||
<xsl:variable name="isError" select="(sum(//testsuite[@package = current()/@package]/@errors) + sum(//testsuite[@package = current()/@package]/@failures))>0"/> | |||||
<!-- write a summary for the package --> | |||||
<tr bgcolor="#EEEEE" valign="top"> | |||||
<td><xsl:if test="$isError"><xsl:attribute name="class">Error</xsl:attribute></xsl:if><a href="{translate(@package,'.','/')}/package-summary.html"><xsl:value-of select="@package"/></a></td> | |||||
<xsl:call-template name="statistics"> | |||||
<xsl:with-param name="isError" select="$isError"/> | |||||
</xsl:call-template> | |||||
</tr> | |||||
</xsl:template> | |||||
<xsl:template match="testsuite" mode="class"> | |||||
<xsl:variable name="isError" select="(@errors + @failures)>0"/> | |||||
<!-- write a summary for the package --> | |||||
<tr bgcolor="#EEEEE" valign="top"> | |||||
<td><xsl:if test="$isError"><xsl:attribute name="class">Error</xsl:attribute></xsl:if><a href="{translate(@package,'.','/')}/summary.html"><xsl:value-of select="@name"/></a></td> | |||||
<xsl:call-template name="statistics"> | |||||
<xsl:with-param name="isError" select="$isError"/> | |||||
</xsl:call-template> | |||||
</tr> | |||||
</xsl:template> | |||||
<xsl:template name="statistics"> | |||||
<xsl:variable name="isError"/> | |||||
<td><xsl:if test="$isError"><xsl:attribute name="class">Error</xsl:attribute></xsl:if><xsl:value-of select="sum(//testsuite[@package = current()/@package]/@tests)"/></td> | |||||
<td><xsl:if test="$isError"><xsl:attribute name="class">Error</xsl:attribute></xsl:if><xsl:value-of select="sum(//testsuite[@package = current()/@package]/@errors)"/></td> | |||||
<td><xsl:if test="$isError"><xsl:attribute name="class">Error</xsl:attribute></xsl:if><xsl:value-of select="sum(//testsuite[@package = current()/@package]/@failures)"/></td> | |||||
<td><xsl:if test="$isError"><xsl:attribute name="class">Error</xsl:attribute></xsl:if><xsl:value-of select="format-number(sum(//testsuite[@package = current()/@package]/@time),'#,###0.000')"/></td> | |||||
</xsl:template> | |||||
</xsl:stylesheet> |
@@ -0,0 +1,184 @@ | |||||
<?xml version="1.0" encoding="ISO-8859-1"?> | |||||
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0" xmlns:html="http://www.w3.org/Profiles/XHTML-transitional"> | |||||
<xsl:include href="toolkit.xsl"/> | |||||
<!-- | |||||
==================================================== | |||||
Create the page structure | |||||
==================================================== | |||||
--> | |||||
<xsl:template match="testsuites"> | |||||
<HTML> | |||||
<HEAD> | |||||
<!--LINK REL ="stylesheet" TYPE="text/css" HREF="stylesheet.css" TITLE="Style"/--> | |||||
<!-- put the style in the html so that we can mail it w/o problem --> | |||||
<style type="text/css"> | |||||
BODY { | |||||
font:normal 68% verdana,arial,helvetica; | |||||
color:#000000; | |||||
} | |||||
TD { | |||||
FONT-SIZE: 68% | |||||
} | |||||
P { | |||||
line-height:1.5em; | |||||
margin-top:0.5em; margin-bottom:1.0em; | |||||
} | |||||
H1 { | |||||
MARGIN: 0px 0px 5px; FONT: 165% verdana,arial,helvetica | |||||
} | |||||
H2 { | |||||
MARGIN-TOP: 1em; MARGIN-BOTTOM: 0.5em; FONT: bold 125% verdana,arial,helvetica | |||||
} | |||||
H3 { | |||||
MARGIN-BOTTOM: 0.5em; FONT: bold 115% verdana,arial,helvetica | |||||
} | |||||
H4 { | |||||
MARGIN-BOTTOM: 0.5em; FONT: bold 100% verdana,arial,helvetica | |||||
} | |||||
H5 { | |||||
MARGIN-BOTTOM: 0.5em; FONT: bold 100% verdana,arial,helvetica | |||||
} | |||||
H6 { | |||||
MARGIN-BOTTOM: 0.5em; FONT: bold 100% verdana,arial,helvetica | |||||
} | |||||
.Error { | |||||
font-weight:bold; color:red; | |||||
} | |||||
.Failure { | |||||
font-weight:bold; color:purple; | |||||
} | |||||
</style> | |||||
</HEAD> | |||||
<body text="#000000" bgColor="#ffffff"> | |||||
<a name="#top"></a> | |||||
<xsl:call-template name="header"/> | |||||
<!-- Summary part --> | |||||
<xsl:call-template name="summary"/> | |||||
<hr size="1" width="95%" align="left"/> | |||||
<!-- Package List part --> | |||||
<xsl:call-template name="packagelist"/> | |||||
<hr size="1" width="95%" align="left"/> | |||||
<!-- For each package create its part --> | |||||
<xsl:call-template name="packages"/> | |||||
<hr size="1" width="95%" align="left"/> | |||||
<!-- For each class create the part --> | |||||
<xsl:call-template name="classes"/> | |||||
</body> | |||||
</HTML> | |||||
</xsl:template> | |||||
<!-- ================================================================== --> | |||||
<!-- Write a list of all packages with an hyperlink to the anchor of --> | |||||
<!-- of the package name. --> | |||||
<!-- ================================================================== --> | |||||
<xsl:template name="packagelist"> | |||||
<h2>Packages</h2> | |||||
Note: package statistics are not computed recursively, they only sum up all of its testsuites numbers. | |||||
<table border="0" cellpadding="5" cellspacing="2" width="95%"> | |||||
<xsl:call-template name="packageSummaryHeader"/> | |||||
<!-- list all packages recursively --> | |||||
<xsl:for-each select="./testsuite[not(./@package = preceding-sibling::testsuite/@package)]"> | |||||
<xsl:sort select="@package"/> | |||||
<xsl:variable name="testCount" select="sum(../testsuite[./@package = current()/@package]/@tests)"/> | |||||
<xsl:variable name="errorCount" select="sum(../testsuite[./@package = current()/@package]/@errors)"/> | |||||
<xsl:variable name="failureCount" select="sum(../testsuite[./@package = current()/@package]/@failures)"/> | |||||
<xsl:variable name="timeCount" select="sum(../testsuite[./@package = current()/@package]/@time)"/> | |||||
<!-- write a summary for the package --> | |||||
<tr bgcolor="#EEEEE" valign="top"> | |||||
<!-- set a nice color depending if there is an error/failure --> | |||||
<xsl:attribute name="class"> | |||||
<xsl:choose> | |||||
<xsl:when test="$errorCount > 0">Error</xsl:when> | |||||
<xsl:when test="$failureCount > 0">Failure</xsl:when> | |||||
</xsl:choose> | |||||
</xsl:attribute> | |||||
<td><a href="#{@package}"><xsl:value-of select="@package"/></a></td> | |||||
<td><xsl:value-of select="$testCount"/></td> | |||||
<td><xsl:value-of select="$errorCount"/></td> | |||||
<td><xsl:value-of select="$failureCount"/></td> | |||||
<td><xsl:value-of select="format-number($timeCount,'#,###0.000')"/></td> | |||||
</tr> | |||||
</xsl:for-each> | |||||
</table> | |||||
</xsl:template> | |||||
<!-- ================================================================== --> | |||||
<!-- Write a package level report --> | |||||
<!-- It creates a table with values from the document: --> | |||||
<!-- Name | Tests | Errors | Failures | Time --> | |||||
<!-- ================================================================== --> | |||||
<xsl:template name="packages"> | |||||
<!-- create an anchor to this package name --> | |||||
<xsl:for-each select="./testsuite[not(./@package = preceding-sibling::testsuite/@package)]"> | |||||
<xsl:sort select="@package"/> | |||||
<a name="#{@package}"></a> | |||||
<h3>Package <xsl:value-of select="@package"/></h3> | |||||
<table border="0" cellpadding="5" cellspacing="2" width="95%"> | |||||
<xsl:call-template name="packageSummaryHeader"/> | |||||
<!-- match the testsuites of this package --> | |||||
<xsl:apply-templates select="../testsuite[./@package = current()/@package]"/> | |||||
</table> | |||||
<a href="#top">Back to top</a> | |||||
<p/> | |||||
<p/> | |||||
</xsl:for-each> | |||||
</xsl:template> | |||||
<!-- ================================================================== --> | |||||
<!-- Process a testsuite node --> | |||||
<!-- It creates a table with values from the document: --> | |||||
<!-- Name | Tests | Errors | Failures | Time --> | |||||
<!-- It must match the table definition at the package level --> | |||||
<!-- ================================================================== --> | |||||
<xsl:template match="testsuite"> | |||||
<tr bgcolor="#EEEEE" valign="top"> | |||||
<!-- set a nice color depending if there is an error/failure --> | |||||
<xsl:attribute name="class"> | |||||
<xsl:choose> | |||||
<xsl:when test="@errors[.> 0]">Error</xsl:when> | |||||
<xsl:when test="@failures[.> 0]">Failure</xsl:when> | |||||
</xsl:choose> | |||||
</xsl:attribute> | |||||
<!-- print testsuite information --> | |||||
<td><a href="#{@name}"><xsl:value-of select="@name"/></a></td> | |||||
<td><xsl:value-of select="@tests"/></td> | |||||
<td><xsl:value-of select="@errors"/></td> | |||||
<td><xsl:value-of select="@failures"/></td> | |||||
<td><xsl:value-of select="format-number(@time,'#,###0.000')"/></td> | |||||
</tr> | |||||
</xsl:template> | |||||
<xsl:template name="classes"> | |||||
<xsl:for-each select="./testsuite"> | |||||
<xsl:sort select="@name"/> | |||||
<!-- create an anchor to this class name --> | |||||
<a name="#{@name}"></a> | |||||
<h3>TestCase <xsl:value-of select="@name"/></h3> | |||||
<table border="0" cellpadding="5" cellspacing="2" width="95%"> | |||||
<!-- Header --> | |||||
<xsl:call-template name="classesSummaryHeader"/> | |||||
<!-- match the testcases of this package --> | |||||
<xsl:apply-templates select="testcase"/> | |||||
</table> | |||||
<a href="#top">Back to top</a> | |||||
</xsl:for-each> | |||||
</xsl:template> | |||||
</xsl:stylesheet> |
@@ -0,0 +1,73 @@ | |||||
<?xml version="1.0" encoding="ISO-8859-1"?> | |||||
<!-- This style sheet should contain just a named templates that used in the other specific templates --> | |||||
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> | |||||
<!-- import the commun templates --> | |||||
<xsl:include href="toolkit.xsl"/> | |||||
<xsl:template match="testsuites"> | |||||
<HTML> | |||||
<HEAD> | |||||
<LINK REL ="stylesheet" TYPE="text/css" TITLE="Style"> | |||||
<xsl:attribute name="href"><xsl:call-template name="path"><xsl:with-param name="path" select="testsuite[position() = 1]/@package"/></xsl:call-template>stylesheet.css</xsl:attribute> | |||||
</LINK> | |||||
</HEAD> | |||||
<BODY><xsl:attribute name="onload">open('classes-list.html','classListFrame')</xsl:attribute> | |||||
<xsl:call-template name="header"> | |||||
<xsl:with-param name="useFrame">yes</xsl:with-param> | |||||
<xsl:with-param name="path" select="testsuite/@package"/> | |||||
</xsl:call-template> | |||||
<!-- create an anchor to this package name --> | |||||
<h3>Package <xsl:value-of select="testsuite/@package"/></h3> | |||||
<table border="0" cellpadding="5" cellspacing="2" width="95%"> | |||||
<!--Header--> | |||||
<xsl:call-template name="packageSummaryHeader"/> | |||||
<!-- write a summary for the package --> | |||||
<tr bgcolor="#EEEEE" valign="top"> | |||||
<td><xsl:value-of select="testsuite/@package"/></td> | |||||
<td><xsl:value-of select="sum(testsuite/@tests)"/></td> | |||||
<td><xsl:value-of select="sum(testsuite/@errors)"/></td> | |||||
<td><xsl:value-of select="sum(testsuite/@failures)"/></td> | |||||
<td><xsl:value-of select="format-number(sum(testsuite/@time),'#,###0.000')"/></td> | |||||
</tr> | |||||
</table> | |||||
<H2>Classes</H2> | |||||
<p> | |||||
<table border="0" cellpadding="5" cellspacing="2" width="95%"> | |||||
<!--Header--> | |||||
<xsl:call-template name="packageSummaryHeader"/> | |||||
<!--Value--> | |||||
<xsl:apply-templates select="testsuite"> | |||||
<xsl:sort select="@name"/> | |||||
</xsl:apply-templates> | |||||
</table> | |||||
</p> | |||||
</BODY> | |||||
</HTML> | |||||
</xsl:template> | |||||
<xsl:template match="testsuite"> | |||||
<tr bgcolor="#EEEEE" valign="top"> | |||||
<!-- set a nice color depending if there is an error/failure --> | |||||
<xsl:attribute name="class"> | |||||
<xsl:choose> | |||||
<xsl:when test="@errors[.> 0]">Error</xsl:when> | |||||
<xsl:when test="@failures[.> 0]">Failure</xsl:when> | |||||
</xsl:choose> | |||||
</xsl:attribute> | |||||
<!-- print testsuite information --> | |||||
<td><a href="{@name}-details.html" target="classFrame"><xsl:value-of select="@name"/></a></td> | |||||
<td><xsl:value-of select="@tests"/></td> | |||||
<td><xsl:value-of select="@errors"/></td> | |||||
<td><xsl:value-of select="@failures"/></td> | |||||
<td><xsl:value-of select="format-number(@time,'#,###0.000')"/></td> | |||||
</tr> | |||||
</xsl:template> | |||||
</xsl:stylesheet> |
@@ -0,0 +1,204 @@ | |||||
<?xml version="1.0" encoding="ISO-8859-1"?> | |||||
<!-- This style sheet should contain just a named templates that used in the other specific templates --> | |||||
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> | |||||
<!-- transform string like a.b.c to ../../../ --> | |||||
<xsl:template name="path"> | |||||
<xsl:param name="path"/> | |||||
<xsl:if test="contains($path,'.')"> | |||||
<xsl:text>../</xsl:text> | |||||
<xsl:call-template name="path"> | |||||
<xsl:with-param name="path"><xsl:value-of select="substring-after($path,'.')"/></xsl:with-param> | |||||
</xsl:call-template> | |||||
</xsl:if> | |||||
<xsl:if test="not(contains($path,'.')) and not($path = '')"> | |||||
<xsl:text>../</xsl:text> | |||||
</xsl:if> | |||||
</xsl:template> | |||||
<!-- | |||||
template that will convert a carriage return into a br tag | |||||
@param word the text from which to convert CR to BR tag | |||||
--> | |||||
<xsl:template name="br-replace"> | |||||
<xsl:param name="word"/> | |||||
<xsl:choose> | |||||
<xsl:when test="contains($word,'
')"> | |||||
<xsl:value-of select="substring-before($word,'
')"/> | |||||
<br/> | |||||
<xsl:call-template name="br-replace"> | |||||
<xsl:with-param name="word" select="substring-after($word,'
')"/> | |||||
</xsl:call-template> | |||||
</xsl:when> | |||||
<xsl:otherwise> | |||||
<xsl:value-of select="$word"/> | |||||
</xsl:otherwise> | |||||
</xsl:choose> | |||||
</xsl:template> | |||||
<!-- | |||||
===================================================================== | |||||
classes summary header | |||||
===================================================================== | |||||
--> | |||||
<xsl:template name="header"> | |||||
<xsl:param name="useFrame">no</xsl:param> | |||||
<xsl:param name="path"/> | |||||
<h1>Unit Tests Results</h1> | |||||
<table width="100%"> | |||||
<tr> | |||||
<td align="left"> | |||||
<!--xsl:choose> | |||||
<xsl:when test="$useFrame='yes'">Frames  | |||||
<a target="_top"> | |||||
<xsl:attribute name="href"><xsl:call-template name="path"><xsl:with-param name="path" select="$path"/></xsl:call-template>noframes.html</xsl:attribute>No frames</a></xsl:when> | |||||
<xsl:when test="$useFrame='no'"><a target="_top"><xsl:attribute name="href"><xsl:call-template name="path"><xsl:with-param name="path" select="$path"/></xsl:call-template>index.html</xsl:attribute>Frames</a> No frames</xsl:when> | |||||
<xsl:otherwise><code>ERROR : useFrame must have 'no' or 'yes' as value.</code></xsl:otherwise> | |||||
</xsl:choose--> | |||||
</td> | |||||
<td align="right">Designed for use with <a href='http://www.junit.org'>JUnit</a> and <a href='http://jakarta.apache.org'>Ant</a>.</td> | |||||
</tr> | |||||
</table> | |||||
<hr size="1"/> | |||||
</xsl:template> | |||||
<xsl:template name="summaryHeader"> | |||||
<tr bgcolor="#A6CAF0" valign="top"> | |||||
<td><b>Tests</b></td> | |||||
<td><b>Failures</b></td> | |||||
<td><b>Errors</b></td> | |||||
<td><b>Success Rate</b></td> | |||||
<td nowrap="nowrap"><b>Time(s)</b></td> | |||||
</tr> | |||||
</xsl:template> | |||||
<!-- | |||||
===================================================================== | |||||
package summary header | |||||
===================================================================== | |||||
--> | |||||
<xsl:template name="packageSummaryHeader"> | |||||
<tr bgcolor="#A6CAF0" valign="top"> | |||||
<td width="75%"><b>Name</b></td> | |||||
<td width="5%"><b>Tests</b></td> | |||||
<td width="5%"><b>Errors</b></td> | |||||
<td width="5%"><b>Failures</b></td> | |||||
<td width="10%" nowrap="nowrap"><b>Time(s)</b></td> | |||||
</tr> | |||||
</xsl:template> | |||||
<!-- | |||||
===================================================================== | |||||
classes summary header | |||||
===================================================================== | |||||
--> | |||||
<xsl:template name="classesSummaryHeader"> | |||||
<tr bgcolor="#A6CAF0" valign="top"> | |||||
<td width="18%"><b>Name</b></td> | |||||
<td width="7%"><b>Status</b></td> | |||||
<td width="70%"><b>Type</b></td> | |||||
<td width="5%" nowrap="nowrap"><b>Time(s)</b></td> | |||||
</tr> | |||||
</xsl:template> | |||||
<!-- | |||||
===================================================================== | |||||
Write the summary report | |||||
It creates a table with computed values from the document: | |||||
User | Date | Environment | Tests | Failures | Errors | Rate | Time | |||||
Note : this template must call at the testsuites level | |||||
===================================================================== | |||||
--> | |||||
<xsl:template name="summary"> | |||||
<h2>Summary</h2> | |||||
<xsl:variable name="testCount" select="sum(./testsuite/@tests)"/> | |||||
<xsl:variable name="errorCount" select="sum(./testsuite/@errors)"/> | |||||
<xsl:variable name="failureCount" select="sum(./testsuite/@failures)"/> | |||||
<xsl:variable name="timeCount" select="sum(./testsuite/@time)"/> | |||||
<xsl:variable name="successRate" select="($testCount - $failureCount - $errorCount) div $testCount"/> | |||||
<table border="0" cellpadding="5" cellspacing="2" width="95%"> | |||||
<xsl:call-template name="summaryHeader"/> | |||||
<tr bgcolor="#EEEEE" valign="top"> | |||||
<xsl:attribute name="class"> | |||||
<xsl:choose> | |||||
<xsl:when test="./failure | ./error">Error</xsl:when> | |||||
<xsl:otherwise>TableRowColor</xsl:otherwise> | |||||
</xsl:choose> | |||||
</xsl:attribute> | |||||
<td><xsl:value-of select="$testCount"/></td> | |||||
<td><xsl:value-of select="$failureCount"/></td> | |||||
<td><xsl:value-of select="$errorCount"/></td> | |||||
<td><xsl:value-of select="format-number($successRate,'#,##0.00%')"/></td> | |||||
<td><xsl:value-of select="format-number($timeCount,'#,###0.000')"/></td> | |||||
</tr> | |||||
</table> | |||||
Note: <i>failures</i> are anticipated and checked for with assertions while <i>errors</i> are unanticipated. | |||||
</xsl:template> | |||||
<!-- | |||||
===================================================================== | |||||
testcase report | |||||
===================================================================== | |||||
--> | |||||
<xsl:template match="testcase"> | |||||
<TR bgcolor="#EEEEE" valign="top"><xsl:attribute name="class"> | |||||
<xsl:choose> | |||||
<xsl:when test="./failure | ./error">Error</xsl:when> | |||||
<xsl:otherwise>TableRowColor</xsl:otherwise> | |||||
</xsl:choose> | |||||
</xsl:attribute> | |||||
<TD><xsl:value-of select="./@name"/></TD> | |||||
<xsl:choose> | |||||
<xsl:when test="./failure"> | |||||
<td>Failure</td> | |||||
<td><xsl:apply-templates select="./failure"/></td> | |||||
</xsl:when> | |||||
<xsl:when test="./error"> | |||||
<TD>Error</TD> | |||||
<td><xsl:apply-templates select="./error"/></td> | |||||
</xsl:when> | |||||
<xsl:otherwise> | |||||
<TD>Success</TD> | |||||
<TD></TD> | |||||
</xsl:otherwise> | |||||
</xsl:choose> | |||||
<td><xsl:value-of select="format-number(@time,'#,###0.000')"/></td> | |||||
</TR> | |||||
</xsl:template> | |||||
<!-- Note : the below template error and failure are the same style | |||||
so just call the same style store in the toolkit template --> | |||||
<xsl:template match="failure"> | |||||
<xsl:call-template name="display-failures"/> | |||||
</xsl:template> | |||||
<xsl:template match="error"> | |||||
<xsl:call-template name="display-failures"/> | |||||
</xsl:template> | |||||
<!-- Style for the error and failure in the tescase template --> | |||||
<xsl:template name="display-failures"> | |||||
<xsl:choose> | |||||
<xsl:when test="not(@message)">N/A</xsl:when> | |||||
<xsl:otherwise> | |||||
<xsl:value-of select="@message"/> | |||||
</xsl:otherwise> | |||||
</xsl:choose> | |||||
<!-- display the stacktrace --> | |||||
<code> | |||||
<p/> | |||||
<xsl:call-template name="br-replace"> | |||||
<xsl:with-param name="word" select="."/> | |||||
</xsl:call-template> | |||||
</code> | |||||
<!-- the later is better but might be problematic for non-21" monitors... --> | |||||
<!--pre><xsl:value-of select="."/></pre--> | |||||
</xsl:template> | |||||
<!-- I am sure that all nodes are called --> | |||||
<xsl:template match="*"> | |||||
<xsl:apply-templates/> | |||||
</xsl:template> | |||||
</xsl:stylesheet> |
@@ -67,6 +67,7 @@ import org.w3c.dom.*; | |||||
* | * | ||||
* @author The original author of XmlLogger | * @author The original author of XmlLogger | ||||
* @author <a href="mailto:stefan.bodewig@epost.de">Stefan Bodewig</a> | * @author <a href="mailto:stefan.bodewig@epost.de">Stefan Bodewig</a> | ||||
* @author <a href="mailto:bailliez@noos.fr">Stephane Bailliez</tt> | |||||
*/ | */ | ||||
public class DOMElementWriter { | public class DOMElementWriter { | ||||
@@ -119,18 +120,39 @@ public class DOMElementWriter { | |||||
for (int i = 0; i < children.getLength(); i++) { | for (int i = 0; i < children.getLength(); i++) { | ||||
Node child = children.item(i); | Node child = children.item(i); | ||||
if (child.getNodeType() == Node.ELEMENT_NODE) { | |||||
switch (child.getNodeType()) { | |||||
case Node.ELEMENT_NODE: | |||||
if (!hasChildren) { | if (!hasChildren) { | ||||
out.write(lSep); | out.write(lSep); | ||||
hasChildren = true; | hasChildren = true; | ||||
} | } | ||||
write((Element)child, out, indent + 1, indentWith); | write((Element)child, out, indent + 1, indentWith); | ||||
} | |||||
if (child.getNodeType() == Node.TEXT_NODE) { | |||||
break; | |||||
case Node.TEXT_NODE: | |||||
case Node.CDATA_SECTION_NODE: | |||||
out.write("<![CDATA["); | out.write("<![CDATA["); | ||||
out.write(((Text)child).getData()); | out.write(((Text)child).getData()); | ||||
out.write("]]>"); | out.write("]]>"); | ||||
break; | |||||
case Node.ENTITY_REFERENCE_NODE: | |||||
out.write('&'); | |||||
out.write(child.getNodeName()); | |||||
out.write(';'); | |||||
break; | |||||
case Node.PROCESSING_INSTRUCTION_NODE: | |||||
out.write("<?"); | |||||
out.write(child.getNodeName()); | |||||
String data = child.getNodeValue(); | |||||
if ( data != null && data.length() > 0 ) { | |||||
out.write(' '); | |||||
out.write(data); | |||||
} | |||||
out.write("?>"); | |||||
break; | |||||
} | } | ||||
} | } | ||||