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. | |||
| * 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>. | |||
| @@ -193,6 +193,10 @@ | |||
| <exclude name="${optional.package}/ide/VAJ*.java" unless="vaj.present" /> | |||
| <exclude name="${optional.package}/perforce/*.java" unless="jakarta.oro.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> | |||
| <copy todir="${build.classes}"> | |||
| @@ -212,6 +216,14 @@ | |||
| <include name="**/defaultManifest.mf" /> | |||
| </fileset> | |||
| </copy> | |||
| <copy todir="${build.classes}/${optional.package}/junit"> | |||
| <fileset dir="${java.dir}/${optional.package}/junit"> | |||
| <include name="html/**" /> | |||
| <include name="xsl/**" /> | |||
| </fileset> | |||
| </copy> | |||
| </target> | |||
| <!-- | |||
| @@ -87,6 +87,7 @@ cccheckout=org.apache.tools.ant.taskdefs.optional.clearcase.CCCheckout | |||
| cccheckin=org.apache.tools.ant.taskdefs.optional.clearcase.CCCheckin | |||
| ccuncheckout=org.apache.tools.ant.taskdefs.optional.clearcase.CCUnCheckout | |||
| 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) | |||
| 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 <a href="mailto:stefan.bodewig@epost.de">Stefan Bodewig</a> | |||
| * @author <a href="mailto:bailliez@noos.fr">Stephane Bailliez</tt> | |||
| */ | |||
| public class DOMElementWriter { | |||
| @@ -119,18 +120,39 @@ public class DOMElementWriter { | |||
| for (int i = 0; i < children.getLength(); i++) { | |||
| Node child = children.item(i); | |||
| if (child.getNodeType() == Node.ELEMENT_NODE) { | |||
| switch (child.getNodeType()) { | |||
| case Node.ELEMENT_NODE: | |||
| if (!hasChildren) { | |||
| out.write(lSep); | |||
| hasChildren = true; | |||
| } | |||
| 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(((Text)child).getData()); | |||
| 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; | |||
| } | |||
| } | |||