Track #76: XML and CSV report output now also works for structures without groups.
diff --git a/org.jacoco.doc/docroot/doc/changes.html b/org.jacoco.doc/docroot/doc/changes.html
index c035df3..50516c1 100644
--- a/org.jacoco.doc/docroot/doc/changes.html
+++ b/org.jacoco.doc/docroot/doc/changes.html
@@ -24,8 +24,14 @@
<li>New HTML report option to directly create a zip file containing the report
(Trac #12).</li>
<li>Code coverage for static initializers in interfaces (Trac #21).</li>
- <li>Better error handling for <code>report</code> Ant task. (Trac #71).</li>
- <li>Classes without instructions are excluded from reports. (Trac #73).</li>
+ <li>Better error handling for <code>report</code> Ant task (Trac #71).</li>
+ <li>Classes without instructions are excluded from reports (Trac #73).</li>
+</ul>
+
+<h3>Fixed Bugs</h3>
+<ul>
+ <li>XML and CSV report output now also works for structures without groups
+ (Track #76).</li>
</ul>
<h3>API Changes</h3>
diff --git a/org.jacoco.report.test/src/org/jacoco/report/xml/XMLElementTest.java b/org.jacoco.report.test/src/org/jacoco/report/xml/XMLElementTest.java
index db1c648..fa4d050 100644
--- a/org.jacoco.report.test/src/org/jacoco/report/xml/XMLElementTest.java
+++ b/org.jacoco.report.test/src/org/jacoco/report/xml/XMLElementTest.java
@@ -105,13 +105,20 @@
}
@Test
- public void testAttributes() throws IOException {
+ public void testStringAttributes() throws IOException {
root.attr("id", "12345").attr("quote", "<\">");
root.close();
assertEquals("<root id=\"12345\" quote=\"<">\"/>", buffer
.toString());
}
+ @Test
+ public void testIntAttributes() throws IOException {
+ root.attr("missed", 0).attr("total", 123);
+ root.close();
+ assertEquals("<root missed=\"0\" total=\"123\"/>", buffer.toString());
+ }
+
@Test(expected = IOException.class)
public void testInvalidAttributeOutput1() throws IOException {
root.text("text");
diff --git a/org.jacoco.report.test/src/org/jacoco/report/xml/XMLFormatterTest.java b/org.jacoco.report.test/src/org/jacoco/report/xml/XMLFormatterTest.java
index 5c2228b..8e0ca96 100644
--- a/org.jacoco.report.test/src/org/jacoco/report/xml/XMLFormatterTest.java
+++ b/org.jacoco.report.test/src/org/jacoco/report/xml/XMLFormatterTest.java
@@ -18,7 +18,6 @@
import org.jacoco.report.ReportStructureTestDriver;
import org.junit.After;
import org.junit.Before;
-import org.junit.Ignore;
import org.junit.Test;
import org.w3c.dom.Document;
@@ -52,6 +51,7 @@
@Test
public void testStructureWithGroup() throws Exception {
driver.sendGroup(formatter);
+ assertPathMatches("group", "/report/@name");
assertPathMatches("bundle", "/report/group/@name");
assertPathMatches("org/jacoco/example", "/report/group/package/@name");
assertPathMatches("org/jacoco/example/FooClass",
@@ -60,12 +60,10 @@
"/report/group/package/class/method/@name");
}
- // TODO: Trac #67
- @Ignore
@Test
public void testStructureWithBundleOnly() throws Exception {
driver.sendBundle(formatter);
- assertPathMatches("bundle", "/report/group/@name");
+ assertPathMatches("bundle", "/report/@name");
assertPathMatches("org/jacoco/example", "/report/package/@name");
assertPathMatches("org/jacoco/example/FooClass",
"/report/package/class/@name");
@@ -74,7 +72,7 @@
private void assertPathMatches(String expected, String path)
throws Exception {
- XMLSupport support = new XMLSupport(XMLReportFile.class);
+ XMLSupport support = new XMLSupport(XMLFormatter.class);
Document document = support.parse(output.getFile());
assertEquals(expected, support.findStr(document, path));
}
diff --git a/org.jacoco.report.test/src/org/jacoco/report/xml/XMLReportFileTest.java b/org.jacoco.report.test/src/org/jacoco/report/xml/XMLReportFileTest.java
deleted file mode 100644
index f5ff259..0000000
--- a/org.jacoco.report.test/src/org/jacoco/report/xml/XMLReportFileTest.java
+++ /dev/null
@@ -1,113 +0,0 @@
-/*******************************************************************************
- * Copyright (c) 2009, 2010 Mountainminds GmbH & Co. KG and Contributors
- * All rights reserved. This program and the accompanying materials
- * are made available under the terms of the Eclipse Public License v1.0
- * which accompanies this distribution, and is available at
- * http://www.eclipse.org/legal/epl-v10.html
- *
- * Contributors:
- * Brock Janiczak - initial API and implementation
- *
- * $Id: $
- *******************************************************************************/
-package org.jacoco.report.xml;
-
-import static org.junit.Assert.assertEquals;
-
-import java.io.IOException;
-import java.io.Reader;
-import java.util.Arrays;
-import java.util.Collections;
-
-import org.jacoco.core.analysis.BundleCoverage;
-import org.jacoco.core.analysis.ClassCoverage;
-import org.jacoco.core.analysis.MethodCoverage;
-import org.jacoco.core.analysis.PackageCoverage;
-import org.jacoco.core.analysis.SourceFileCoverage;
-import org.jacoco.report.IReportVisitor;
-import org.jacoco.report.ISourceFileLocator;
-import org.jacoco.report.MemorySingleReportOutput;
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Test;
-import org.w3c.dom.Document;
-
-/**
- * Unit tests for {@link XMLReportFile}
- *
- * @author Brock Janiczak
- * @version $Revision: $
- *
- */
-public class XMLReportFileTest {
-
- private MemorySingleReportOutput output;
-
- private XMLReportFile report;
-
- private final ISourceFileLocator nullSourceLocator = new ISourceFileLocator() {
-
- public Reader getSourceFile(String packageName, String fileName)
- throws IOException {
- return null;
- }
- };
-
- @Before
- public void setUp() throws Exception {
- output = new MemorySingleReportOutput();
- report = new XMLReportFile("UTF-8", output.createFile());
- }
-
- @After
- public void teardown() {
- output.assertClosed();
- }
-
- @Test
- public void testEmptyReport() throws Exception {
- IReportVisitor groupElement = report.visitChild(new BundleCoverage(
- "test", Arrays.<PackageCoverage> asList()));
- groupElement.visitEnd(nullSourceLocator);
- report.visitEnd(nullSourceLocator);
-
- assertPathMatches("test", "/report/group/@name");
- }
-
- @Test
- public void testReportWithNoGroupsOrBundles() throws Exception {
-
- MethodCoverage methodCoverage = new MethodCoverage("foo", "V", "V");
- ClassCoverage classCoverage = new ClassCoverage("org/jacoco/test/Test",
- null, "java/lang/Object", new String[0], "Test.java", Arrays
- .asList(methodCoverage));
- PackageCoverage packageCoverage = new PackageCoverage(
- "org/jacoco/test", Arrays.asList(classCoverage), Collections
- .<SourceFileCoverage> emptyList());
- BundleCoverage bundleCoverage = new BundleCoverage("test", Arrays
- .asList(packageCoverage));
-
- IReportVisitor sessionElement = report.visitChild(bundleCoverage);
- IReportVisitor packageElement = sessionElement
- .visitChild(packageCoverage);
- IReportVisitor classElement = packageElement.visitChild(classCoverage);
- IReportVisitor methodElement = classElement.visitChild(methodCoverage);
-
- methodElement.visitEnd(nullSourceLocator);
- classElement.visitEnd(nullSourceLocator);
- packageElement.visitEnd(nullSourceLocator);
- sessionElement.visitEnd(nullSourceLocator);
- report.visitEnd(nullSourceLocator);
-
- assertPathMatches("BLOCK",
- "/report/group/package/class/method[@name='foo']/counter/@type");
- }
-
- private void assertPathMatches(String expected, String path)
- throws Exception {
- XMLSupport support = new XMLSupport(XMLReportFile.class);
- Document document = support.parse(output.getFile());
- assertEquals(expected, support.findStr(document, path));
-
- }
-}
diff --git a/org.jacoco.report/src/org/jacoco/report/html/HTMLElement.java b/org.jacoco.report/src/org/jacoco/report/html/HTMLElement.java
index bdc3b68..d0f247b 100644
--- a/org.jacoco.report/src/org/jacoco/report/html/HTMLElement.java
+++ b/org.jacoco.report/src/org/jacoco/report/html/HTMLElement.java
@@ -331,7 +331,7 @@
td.attr("class", classattr);
}
if (colspanattr > 1) {
- td.attr("colspan", String.valueOf(colspanattr));
+ td.attr("colspan", colspanattr);
}
return td;
}
@@ -354,8 +354,8 @@
final int heightattr, final String titleattr) throws IOException {
final HTMLElement img = element("img");
img.attr("src", srcattr);
- img.attr("width", String.valueOf(widthattr));
- img.attr("height", String.valueOf(heightattr));
+ img.attr("width", widthattr);
+ img.attr("height", heightattr);
img.attr("title", titleattr);
img.attr("alt", titleattr);
img.close();
diff --git a/org.jacoco.report/src/org/jacoco/report/xml/ClassNode.java b/org.jacoco.report/src/org/jacoco/report/xml/ClassNode.java
deleted file mode 100644
index 2d43e8c..0000000
--- a/org.jacoco.report/src/org/jacoco/report/xml/ClassNode.java
+++ /dev/null
@@ -1,79 +0,0 @@
-/*******************************************************************************
- * Copyright (c) 2009, 2010 Mountainminds GmbH & Co. KG and Contributors
- * All rights reserved. This program and the accompanying materials
- * are made available under the terms of the Eclipse Public License v1.0
- * which accompanies this distribution, and is available at
- * http://www.eclipse.org/legal/epl-v10.html
- *
- * Contributors:
- * Brock Janiczak -initial API and implementation
- *
- * $Id: $
- *******************************************************************************/
-package org.jacoco.report.xml;
-
-import java.io.IOException;
-
-import org.jacoco.core.analysis.ClassCoverage;
-import org.jacoco.core.analysis.ICoverageNode;
-import org.jacoco.core.analysis.MethodCoverage;
-import org.jacoco.core.analysis.ICoverageNode.CounterEntity;
-import org.jacoco.report.IReportVisitor;
-
-/**
- * Wrapper for an {@link XMLElement} that contains class coverage data
- *
- * @author Brock Janiczak
- * @version $Revision: $
- */
-public class ClassNode extends NodeWithCoverage {
- private static final CounterEntity[] CLASS_COUNTERS = {
- CounterEntity.METHOD, CounterEntity.BLOCK, CounterEntity.LINE,
- CounterEntity.INSTRUCTION, };
-
- /**
- * Creates a new Class coverage element for the supplied package and class
- * coverage node
- *
- * @param parent
- * Parent element that will own this class element
- * @param classNode
- * Class coverage node
- * @throws IOException
- * IO Error creating the element
- */
- public ClassNode(final PackageNode parent, final ClassCoverage classNode)
- throws IOException {
- super(parent, "class", classNode);
- if (classNode.getSignature() != null) {
- this.attr("signature", classNode.getSignature());
- }
- if (classNode.getSuperName() != null) {
- this.attr("superclass", classNode.getSuperName());
- }
- if (classNode.getInterfaceNames() != null) {
- boolean first = true;
- final StringBuilder builder = new StringBuilder();
- for (final String iface : classNode.getInterfaceNames()) {
- if (first) {
- first = false;
- } else {
- builder.append(' ');
- }
- builder.append(iface);
- }
- this.attr("interfaces", builder.toString());
- }
- }
-
- public IReportVisitor visitChild(final ICoverageNode node)
- throws IOException {
- return new MethodNode(this, (MethodCoverage) node);
- }
-
- @Override
- protected CounterEntity[] getCounterEntities() {
- return CLASS_COUNTERS;
- }
-
-}
diff --git a/org.jacoco.report/src/org/jacoco/report/xml/GroupNode.java b/org.jacoco.report/src/org/jacoco/report/xml/GroupNode.java
deleted file mode 100644
index d33beed..0000000
--- a/org.jacoco.report/src/org/jacoco/report/xml/GroupNode.java
+++ /dev/null
@@ -1,78 +0,0 @@
-/*******************************************************************************
- * Copyright (c) 2009, 2010 Mountainminds GmbH & Co. KG and Contributors
- * All rights reserved. This program and the accompanying materials
- * are made available under the terms of the Eclipse Public License v1.0
- * which accompanies this distribution, and is available at
- * http://www.eclipse.org/legal/epl-v10.html
- *
- * Contributors:
- * Brock Janiczak -initial API and implementation
- *
- * $Id: $
- *******************************************************************************/
-package org.jacoco.report.xml;
-
-import java.io.IOException;
-
-import org.jacoco.core.analysis.ICoverageNode;
-import org.jacoco.core.analysis.ICoverageNode.ElementType;
-import org.jacoco.report.IReportVisitor;
-
-/**
- * Wrapper for an {@link XMLElement} that contains 'groups' of coverage data.
- * Group Nodes can represent either be Bundle or Group coverage data
- *
- * @author Brock Janiczak
- * @version $Revision: $
- */
-public class GroupNode extends NodeWithCoverage {
-
- /**
- * Creates a new top level Group coverage element for the supplied session
- * coverage node
- *
- * @param file
- * Root element to attach to
- * @param coverageNode
- * Coverage node
- * @throws IOException
- * IO Error creating the element
- */
- public GroupNode(final XMLReportFile file, final ICoverageNode coverageNode)
- throws IOException {
- this((XMLElement) file, coverageNode);
- }
-
- /**
- * Creates a new Group coverage element under an existing group element for
- * the supplied coverage node
- *
- * @param parent
- * Element to attach to
- * @param node
- * Coverage node
- * @throws IOException
- * IO Error creating the element
- */
- public GroupNode(final GroupNode parent, final ICoverageNode node)
- throws IOException {
- this((XMLElement) parent, node);
- }
-
- private GroupNode(final XMLElement parent, final ICoverageNode node)
- throws IOException {
- super(parent, "group", node);
- }
-
- public IReportVisitor visitChild(final ICoverageNode node)
- throws IOException {
-
- if (node.getElementType() == ElementType.GROUP
- || node.getElementType() == ElementType.BUNDLE) {
- return new GroupNode(this, node);
- }
-
- return new PackageNode(this, node);
- }
-
-}
diff --git a/org.jacoco.report/src/org/jacoco/report/xml/MethodNode.java b/org.jacoco.report/src/org/jacoco/report/xml/MethodNode.java
deleted file mode 100644
index 05fe829..0000000
--- a/org.jacoco.report/src/org/jacoco/report/xml/MethodNode.java
+++ /dev/null
@@ -1,64 +0,0 @@
-/*******************************************************************************
- * Copyright (c) 2009, 2010 Mountainminds GmbH & Co. KG and Contributors
- * All rights reserved. This program and the accompanying materials
- * are made available under the terms of the Eclipse Public License v1.0
- * which accompanies this distribution, and is available at
- * http://www.eclipse.org/legal/epl-v10.html
- *
- * Contributors:
- * Brock Janiczak -initial API and implementation
- *
- * $Id: $
- *******************************************************************************/
-package org.jacoco.report.xml;
-
-import java.io.IOException;
-
-import org.jacoco.core.analysis.ICoverageNode;
-import org.jacoco.core.analysis.MethodCoverage;
-import org.jacoco.core.analysis.ICoverageNode.CounterEntity;
-import org.jacoco.report.IReportVisitor;
-
-/**
- * Wrapper for an {@link XMLElement} that contains method coverage data
- *
- * @author Brock Janiczak
- * @version $Revision: $
- */
-public class MethodNode extends NodeWithCoverage {
-
- private static final CounterEntity[] METHOD_COUNTERS = {
- CounterEntity.BLOCK, CounterEntity.LINE, CounterEntity.INSTRUCTION };
-
- /**
- * Creates a new Method coverage element for the supplied package and class
- * coverage node
- *
- * @param parent
- * Parent element that will own this class element
- * @param methodNode
- * Method coverage node
- * @throws IOException
- * IO Error creating the element
- */
- public MethodNode(final ClassNode parent, final MethodCoverage methodNode)
- throws IOException {
- super(parent, "method", methodNode);
- this.attr("desc", methodNode.getDesc());
- final String signature = methodNode.getSignature();
- if (signature != null) {
- this.attr("signature", signature);
- }
- }
-
- public IReportVisitor visitChild(final ICoverageNode node)
- throws IOException {
- throw new IllegalStateException("Methods must not have child nodes.");
- }
-
- @Override
- protected CounterEntity[] getCounterEntities() {
- return METHOD_COUNTERS;
- }
-
-}
diff --git a/org.jacoco.report/src/org/jacoco/report/xml/NodeWithCoverage.java b/org.jacoco.report/src/org/jacoco/report/xml/NodeWithCoverage.java
deleted file mode 100644
index 4ec8c5f..0000000
--- a/org.jacoco.report/src/org/jacoco/report/xml/NodeWithCoverage.java
+++ /dev/null
@@ -1,89 +0,0 @@
-/*******************************************************************************
- * Copyright (c) 2009, 2010 Mountainminds GmbH & Co. KG and Contributors
- * All rights reserved. This program and the accompanying materials
- * are made available under the terms of the Eclipse Public License v1.0
- * which accompanies this distribution, and is available at
- * http://www.eclipse.org/legal/epl-v10.html
- *
- * Contributors:
- * Brock Janiczak -initial API and implementation
- *
- * $Id: $
- *******************************************************************************/
-package org.jacoco.report.xml;
-
-import java.io.IOException;
-
-import org.jacoco.core.analysis.ICounter;
-import org.jacoco.core.analysis.ICoverageNode;
-import org.jacoco.core.analysis.ICoverageNode.CounterEntity;
-import org.jacoco.report.IReportVisitor;
-import org.jacoco.report.ISourceFileLocator;
-
-/**
- * Base class for implementing XML Elements that contain coverage elements
- *
- * @author Brock Janiczak
- * @version $Revision: $
- */
-public abstract class NodeWithCoverage extends XMLElement implements
- IReportVisitor {
- private static final CounterEntity[] DEFAULT_COUNTERS = {
- CounterEntity.CLASS, CounterEntity.METHOD, CounterEntity.BLOCK,
- CounterEntity.LINE, CounterEntity.INSTRUCTION, };
-
- private final ICoverageNode node;
-
- /**
- * Creates a new Coverage node under the supplied parent
- *
- * @param parent
- * Parent element
- * @param elementName
- * Name of this element
- * @param node
- * Coverage node
- * @throws IOException
- * IO Error creating this element
- */
- public NodeWithCoverage(final XMLElement parent, final String elementName,
- final ICoverageNode node) throws IOException {
- super(parent.writer, elementName);
- parent.addChildElement(this);
- this.node = node;
- this.attr("name", node.getName());
- }
-
- public final void visitEnd(final ISourceFileLocator sourceFileLocator)
- throws IOException {
-
- for (final CounterEntity counterEntity : getCounterEntities()) {
- createCounterElement(counterEntity);
- }
-
- this.close();
- }
-
- /**
- * Retrieves the list of counters supported by this element
- *
- * @return Counters supported by this element
- */
- protected CounterEntity[] getCounterEntities() {
- return DEFAULT_COUNTERS;
- }
-
- private void createCounterElement(final CounterEntity counterEntity)
- throws IOException {
- final ICounter counter = node.getCounter(counterEntity);
-
- final XMLElement counterNode = this.element("counter");
- counterNode.attr("type", counterEntity.name());
- counterNode
- .attr("covered", Integer.toString(counter.getCoveredCount()));
- counterNode.attr("missed", Integer.toString(counter.getMissedCount()));
-
- counterNode.close();
- }
-
-}
diff --git a/org.jacoco.report/src/org/jacoco/report/xml/PackageNode.java b/org.jacoco.report/src/org/jacoco/report/xml/PackageNode.java
deleted file mode 100644
index c6e5008..0000000
--- a/org.jacoco.report/src/org/jacoco/report/xml/PackageNode.java
+++ /dev/null
@@ -1,59 +0,0 @@
-/*******************************************************************************
- * Copyright (c) 2009, 2010 Mountainminds GmbH & Co. KG and Contributors
- * All rights reserved. This program and the accompanying materials
- * are made available under the terms of the Eclipse Public License v1.0
- * which accompanies this distribution, and is available at
- * http://www.eclipse.org/legal/epl-v10.html
- *
- * Contributors:
- * Brock Janiczak -initial API and implementation
- *
- * $Id: $
- *******************************************************************************/
-package org.jacoco.report.xml;
-
-import static java.lang.String.format;
-
-import java.io.IOException;
-
-import org.jacoco.core.analysis.ClassCoverage;
-import org.jacoco.core.analysis.ICoverageNode;
-import org.jacoco.core.analysis.ICoverageNode.ElementType;
-import org.jacoco.report.IReportVisitor;
-
-/**
- * Wrapper for an {@link XMLElement} that contains package coverage data
- *
- * @author Brock Janiczak
- * @version $Revision: $
- */
-public class PackageNode extends NodeWithCoverage {
-
- /**
- * Creates a new Package coverage element under the supplied group element
- *
- * @param parent
- * Parent element that will own this class element
- * @param packageNode
- * Package coverage node
- * @throws IOException
- * IO Error creating the element
- */
- public PackageNode(final GroupNode parent, final ICoverageNode packageNode)
- throws IOException {
- super(parent, "package", packageNode);
- }
-
- public IReportVisitor visitChild(final ICoverageNode node)
- throws IOException {
- final ElementType type = node.getElementType();
- switch (type) {
- case CLASS:
- return new ClassNode(this, (ClassCoverage) node);
- case SOURCEFILE:
- return IReportVisitor.NOP;
- }
- throw new IllegalStateException(format("Unexpected child node %s.",
- type));
- }
-}
diff --git a/org.jacoco.report/src/org/jacoco/report/xml/XMLElement.java b/org.jacoco.report/src/org/jacoco/report/xml/XMLElement.java
index 1468441..5e55bc1 100644
--- a/org.jacoco.report/src/org/jacoco/report/xml/XMLElement.java
+++ b/org.jacoco.report/src/org/jacoco/report/xml/XMLElement.java
@@ -161,6 +161,25 @@
}
/**
+ * Adds an attribute to this element. May only be called before an child
+ * element is added or this element has been closed. The attribute value is
+ * the decimal representation of the given int value.
+ *
+ * @param name
+ * attribute name
+ * @param value
+ * attribute value
+ *
+ * @return this element
+ * @throws IOException
+ * in case of problems with the writer
+ */
+ public XMLElement attr(final String name, final int value)
+ throws IOException {
+ return attr(name, String.valueOf(value));
+ }
+
+ /**
* Adds the given text as a child to this node. The text will be quoted.
*
* @param text
diff --git a/org.jacoco.report/src/org/jacoco/report/xml/XMLFormatter.java b/org.jacoco.report/src/org/jacoco/report/xml/XMLFormatter.java
index 10056f9..4fa71af 100644
--- a/org.jacoco.report/src/org/jacoco/report/xml/XMLFormatter.java
+++ b/org.jacoco.report/src/org/jacoco/report/xml/XMLFormatter.java
@@ -27,13 +27,19 @@
*/
public class XMLFormatter implements IReportFormatter {
+ private static final String PUBID = "-//JACOCO//DTD Report 1.0//EN";
+
+ private static final String SYSTEM = "report.dtd";
+
private ISingleReportOutput output;
private String outputEncoding = "UTF-8";
public IReportVisitor createReportVisitor(final ICoverageNode session)
throws IOException {
- return new XMLReportFile(outputEncoding, output.createFile());
+ final XMLElement root = new XMLDocument("report", PUBID, SYSTEM,
+ outputEncoding, true, output.createFile());
+ return new XMLReportNodeHandler(root, session);
}
/**
diff --git a/org.jacoco.report/src/org/jacoco/report/xml/XMLReportFile.java b/org.jacoco.report/src/org/jacoco/report/xml/XMLReportFile.java
deleted file mode 100644
index 4ca31bf..0000000
--- a/org.jacoco.report/src/org/jacoco/report/xml/XMLReportFile.java
+++ /dev/null
@@ -1,61 +0,0 @@
-/*******************************************************************************
- * Copyright (c) 2009, 2010 Mountainminds GmbH & Co. KG and Contributors
- * All rights reserved. This program and the accompanying materials
- * are made available under the terms of the Eclipse Public License v1.0
- * which accompanies this distribution, and is available at
- * http://www.eclipse.org/legal/epl-v10.html
- *
- * Contributors:
- * Brock Janiczak -initial API and implementation
- *
- * $Id: $
- *******************************************************************************/
-package org.jacoco.report.xml;
-
-import java.io.IOException;
-import java.io.OutputStream;
-
-import org.jacoco.core.analysis.ICoverageNode;
-import org.jacoco.report.IReportVisitor;
-import org.jacoco.report.ISourceFileLocator;
-
-/**
- * Report visitor that will generate an XML report of the coverage data
- *
- * @author Brock Janiczak
- * @version $Revision: $
- */
-public class XMLReportFile extends XMLDocument implements IReportVisitor {
-
- private static final String ROOT = "report";
-
- private static final String PUBID = "-//JACOCO//DTD Report 1.0//EN";
-
- private static final String SYSTEM = "report.dtd";
-
- /**
- * Creates a new Report file
- *
- * @param output
- * Report output
- * @param encoding
- * Encoding of the XML file
- * @throws IOException
- * IO Error creating report file
- */
- public XMLReportFile(final String encoding, final OutputStream output)
- throws IOException {
- super(ROOT, PUBID, SYSTEM, encoding, true, output);
- }
-
- public IReportVisitor visitChild(final ICoverageNode node)
- throws IOException {
- return new GroupNode(this, node);
- }
-
- public void visitEnd(final ISourceFileLocator sourceFileLocator)
- throws IOException {
- this.close();
- }
-
-}
diff --git a/org.jacoco.report/src/org/jacoco/report/xml/XMLReportNodeHandler.java b/org.jacoco.report/src/org/jacoco/report/xml/XMLReportNodeHandler.java
new file mode 100644
index 0000000..5dc7cd8
--- /dev/null
+++ b/org.jacoco.report/src/org/jacoco/report/xml/XMLReportNodeHandler.java
@@ -0,0 +1,130 @@
+/*******************************************************************************
+ * Copyright (c) 2009, 2010 Mountainminds GmbH & Co. KG and Contributors
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Brock Janiczak - initial API and implementation
+ * Marc R. Hoffmann - generalized structure
+ *
+ * $Id: $
+ *******************************************************************************/
+package org.jacoco.report.xml;
+
+import java.io.IOException;
+
+import org.jacoco.core.analysis.ClassCoverage;
+import org.jacoco.core.analysis.ICounter;
+import org.jacoco.core.analysis.ICoverageNode;
+import org.jacoco.core.analysis.MethodCoverage;
+import org.jacoco.core.analysis.ICoverageNode.CounterEntity;
+import org.jacoco.core.analysis.ICoverageNode.ElementType;
+import org.jacoco.report.IReportVisitor;
+import org.jacoco.report.ISourceFileLocator;
+
+/**
+ * Report visitor that transforms the report structure into XML elements.
+ *
+ * @author Brock Janiczak
+ * @version $Revision: $
+ */
+class XMLReportNodeHandler implements IReportVisitor {
+
+ protected final XMLElement element;
+
+ protected final ICoverageNode node;
+
+ /**
+ * New handler for the given coverage node.
+ *
+ * @param element
+ * XML-Element representing this coverage node. The start tag
+ * must not be closed yet to allow adding additional attributes.
+ * @param node
+ * corresponding coverage node
+ * @throws IOException
+ * in case of problems with the underlying writer
+ */
+ public XMLReportNodeHandler(final XMLElement element,
+ final ICoverageNode node) throws IOException {
+ this.element = element;
+ this.node = node;
+ element.attr("name", node.getName());
+ }
+
+ public IReportVisitor visitChild(final ICoverageNode node)
+ throws IOException {
+ final ElementType type = node.getElementType();
+ switch (type) {
+ case GROUP:
+ case BUNDLE:
+ return new XMLReportNodeHandler(element.element("group"), node);
+ case PACKAGE:
+ return new XMLReportNodeHandler(element.element("package"), node);
+ case CLASS:
+ final XMLElement classChild = element.element("class");
+ addClassAttributes(classChild, (ClassCoverage) node);
+ return new XMLReportNodeHandler(classChild, node);
+ case METHOD:
+ final XMLElement methodChild = element.element("method");
+ addMethodAttributes(methodChild, (MethodCoverage) node);
+ return new XMLReportNodeHandler(methodChild, node);
+ }
+ return IReportVisitor.NOP;
+ }
+
+ public void visitEnd(final ISourceFileLocator sourceFileLocator)
+ throws IOException {
+ for (final CounterEntity counterEntity : CounterEntity.values()) {
+ createCounterElement(counterEntity);
+ }
+ this.element.close();
+ }
+
+ private void createCounterElement(final CounterEntity counterEntity)
+ throws IOException {
+ final ICounter counter = node.getCounter(counterEntity);
+ if (counter.getTotalCount() > 0) {
+ final XMLElement counterNode = this.element.element("counter");
+ counterNode.attr("type", counterEntity.name());
+ counterNode.attr("covered", counter.getCoveredCount());
+ counterNode.attr("missed", counter.getMissedCount());
+ counterNode.close();
+ }
+ }
+
+ private static void addClassAttributes(final XMLElement element,
+ final ClassCoverage node) throws IOException {
+ if (node.getSignature() != null) {
+ element.attr("signature", node.getSignature());
+ }
+ if (node.getSuperName() != null) {
+ element.attr("superclass", node.getSuperName());
+ }
+ if (node.getInterfaceNames() != null) {
+ boolean first = true;
+ final StringBuilder builder = new StringBuilder();
+ for (final String iface : node.getInterfaceNames()) {
+ if (first) {
+ first = false;
+ } else {
+ builder.append(' ');
+ }
+ builder.append(iface);
+ }
+ element.attr("interfaces", builder.toString());
+ }
+ }
+
+ private static void addMethodAttributes(final XMLElement element,
+ final MethodCoverage node) throws IOException {
+ element.attr("desc", node.getDesc());
+ final String signature = node.getSignature();
+ if (signature != null) {
+ element.attr("signature", signature);
+ }
+ }
+
+}
diff --git a/org.jacoco.report/src/org/jacoco/report/xml/report.dtd b/org.jacoco.report/src/org/jacoco/report/xml/report.dtd
index 4d6b7d8..11164d9 100644
--- a/org.jacoco.report/src/org/jacoco/report/xml/report.dtd
+++ b/org.jacoco.report/src/org/jacoco/report/xml/report.dtd
@@ -7,28 +7,31 @@
Contributors:
Brock Janiczak - initial API and implementation
+ Marc R. Hoffmann - generalized report structure
$Id: $
-->
-<!ELEMENT report (group)>
+<!ELEMENT report ((group* , package*) , counter*)>
+<!ATTLIST report
+ name CDATA #REQUIRED>
-<!ELEMENT group ((group* , package*) , counter+)>
+<!ELEMENT group ((group* , package*) , counter*)>
<!ATTLIST group
name CDATA #REQUIRED>
-<!ELEMENT package ((class*) , counter+)>
+<!ELEMENT package ((class*) , counter*)>
<!ATTLIST package
name CDATA #REQUIRED>
-<!ELEMENT class ((method*), counter+)>
+<!ELEMENT class ((method*), counter*)>
<!ATTLIST class
name CDATA #REQUIRED
signature CDATA #IMPLIED
superclass CDATA #IMPLIED
interfaces CDATA #IMPLIED>
-<!ELEMENT method (counter+)>
+<!ELEMENT method (counter*)>
<!ATTLIST method
name CDATA #REQUIRED
desc CDATA #REQUIRED