XMLReportNodeHandler.java
1/*******************************************************************************
2 * Copyright (c) 2009, 2010 Mountainminds GmbH & Co. KG and Contributors
3 * All rights reserved. This program and the accompanying materials
4 * are made available under the terms of the Eclipse Public License v1.0
5 * which accompanies this distribution, and is available at
6 * http://www.eclipse.org/legal/epl-v10.html
7 *
8 * Contributors:
9 * Brock Janiczak - initial API and implementation
10 * Marc R. Hoffmann - generalized structure, line info
11 *
12 *******************************************************************************/
13package org.jacoco.report.xml;
14
15import java.io.IOException;
16
17import org.jacoco.core.analysis.ICounter;
18import org.jacoco.core.analysis.ICoverageNode;
19import org.jacoco.core.analysis.ICoverageNode.CounterEntity;
20import org.jacoco.core.analysis.ICoverageNode.ElementType;
21import org.jacoco.core.analysis.ILines;
22import org.jacoco.core.analysis.MethodCoverage;
23import org.jacoco.report.IReportVisitor;
24import org.jacoco.report.ISourceFileLocator;
25
26/**
27 * Report visitor that transforms the report structure into XML elements.
28 *
29 * @author Brock Janiczak
30 * @version 0.4.1.20101007204400
31 */
32class XMLReportNodeHandler implements IReportVisitor {
33
34 private final XMLElement element;
35
36 private final ICoverageNode node;
37
38 /**
39 * New handler for the given coverage node.
40 *
41 * @param element
42 * XML-Element representing this coverage node. The start tag
43 * must not be closed yet to allow adding additional attributes.
44 * @param node
45 * corresponding coverage node
46 * @throws IOException
47 * in case of problems with the underlying writer
48 */
49 public XMLReportNodeHandler(final XMLElement element,
50 final ICoverageNode node) throws IOException {
51 this.element = element;
52 this.node = node;
53 element.attr("name", node.getName());
54 insertElementsBefore(element);
55 }
56
57 /**
58 * Hook to add XML elements before the child elements created by default.
59 *
60 * @param element
61 * this element
62 * @throws IOException
63 */
64 void insertElementsBefore(final XMLElement element) throws IOException {
65 }
66
67 /**
68 * Hook to add XML elements before the child elements created by default.
69 *
70 * @param element
71 * this element
72 * @throws IOException
73 */
74 void insertElementsAfter(final XMLElement element) throws IOException {
75 }
76
77 public IReportVisitor visitChild(final ICoverageNode node)
78 throws IOException {
79 final ElementType type = node.getElementType();
80 switch (type) {
81 case GROUP:
82 case BUNDLE:
83 return new XMLReportNodeHandler(element.element("group"), node);
84 case PACKAGE:
85 return new XMLReportNodeHandler(element.element("package"), node);
86 case CLASS:
87 return new XMLReportNodeHandler(element.element("class"), node);
88 case METHOD:
89 final XMLElement methodChild = element.element("method");
90 final MethodCoverage methodNode = (MethodCoverage) node;
91 methodChild.attr("desc", methodNode.getDesc());
92 final int line = methodNode.getLines().getFirstLine();
93 if (line != -1) {
94 methodChild.attr("line", line);
95 }
96 return new XMLReportNodeHandler(methodChild, node);
97 case SOURCEFILE:
98 return new XMLReportNodeHandler(element.element("sourcefile"), node) {
99 @Override
100 protected void insertElementsAfter(final XMLElement element)
101 throws IOException {
102 writeLines(node.getLines(), element);
103 }
104 };
105 default:
106 throw new AssertionError(type);
107 }
108 }
109
110 public final void visitEnd(final ISourceFileLocator sourceFileLocator)
111 throws IOException {
112 insertElementsAfter(element);
113 for (final CounterEntity counterEntity : CounterEntity.values()) {
114 createCounterElement(counterEntity);
115 }
116 element.close();
117 }
118
119 private void createCounterElement(final CounterEntity counterEntity)
120 throws IOException {
121 final ICounter counter = node.getCounter(counterEntity);
122 if (counter.getTotalCount() > 0) {
123 final XMLElement counterNode = this.element.element("counter");
124 counterNode.attr("type", counterEntity.name());
125 counterNode.attr("covered", counter.getCoveredCount());
126 counterNode.attr("missed", counter.getMissedCount());
127 counterNode.close();
128 }
129 }
130
131 private static void writeLines(final ILines lines, final XMLElement parent)
132 throws IOException {
133 final int last = lines.getLastLine();
134 for (int nr = lines.getFirstLine(); nr <= last; nr++) {
135 final byte status = lines.getStatus(nr);
136 if (status != ILines.NO_CODE) {
137 final XMLElement line = parent.element("line");
138 line.attr("nr", nr);
139 switch (status) {
140 case ILines.NOT_COVERED:
141 line.attr("status", "N");
142 break;
143 case ILines.PARTLY_COVERED:
144 line.attr("status", "P");
145 break;
146 case ILines.FULLY_COVERED:
147 line.attr("status", "F");
148 break;
149 }
150 }
151 }
152 }
153
154}