1/*******************************************************************************
2 * Copyright (c) 2009, 2018 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 *    Marc R. Hoffmann - initial API and implementation
10 *
11 *******************************************************************************/
12package org.jacoco.report.internal.html.page;
13
14import java.io.BufferedReader;
15import java.io.IOException;
16import java.io.Reader;
17import java.util.Locale;
18
19import org.jacoco.core.analysis.ICounter;
20import org.jacoco.core.analysis.ILine;
21import org.jacoco.core.analysis.ISourceNode;
22import org.jacoco.report.internal.html.HTMLElement;
23import org.jacoco.report.internal.html.resources.Styles;
24
25/**
26 * Creates a highlighted output of a source file.
27 */
28final class SourceHighlighter {
29
30	private final Locale locale;
31
32	private String lang;
33
34	/**
35	 * Creates a new highlighter with default settings.
36	 *
37	 * @param locale
38	 *            locale for tooltip rendering
39	 */
40	public SourceHighlighter(final Locale locale) {
41		this.locale = locale;
42		lang = "java";
43	}
44
45	/**
46	 * Specifies the source language. This value might be used for syntax
47	 * highlighting. Default is "java".
48	 *
49	 * @param lang
50	 *            source language identifier
51	 */
52	public void setLanguage(final String lang) {
53		this.lang = lang;
54	}
55
56	/**
57	 * Highlights the given source file.
58	 *
59	 * @param parent
60	 *            parent HTML element
61	 * @param source
62	 *            highlighting information
63	 * @param contents
64	 *            contents of the source file
65	 * @throws IOException
66	 *             problems while reading the source file or writing the output
67	 */
68	public void render(final HTMLElement parent, final ISourceNode source,
69			final Reader contents) throws IOException {
70		final HTMLElement pre = parent.pre(Styles.SOURCE + " lang-" + lang
71				+ " linenums");
72		final BufferedReader lineBuffer = new BufferedReader(contents);
73		String line;
74		int nr = 0;
75		while ((line = lineBuffer.readLine()) != null) {
76			nr++;
77			renderCodeLine(pre, line, source.getLine(nr), nr);
78		}
79	}
80
81	private void renderCodeLine(final HTMLElement pre, final String linesrc,
82			final ILine line, final int lineNr) throws IOException {
83		highlight(pre, line, lineNr).text(linesrc);
84		pre.text("\n");
85	}
86
87	HTMLElement highlight(final HTMLElement pre, final ILine line,
88			final int lineNr) throws IOException {
89		final String style;
90		switch (line.getStatus()) {
91		case ICounter.NOT_COVERED:
92			style = Styles.NOT_COVERED;
93			break;
94		case ICounter.FULLY_COVERED:
95			style = Styles.FULLY_COVERED;
96			break;
97		case ICounter.PARTLY_COVERED:
98			style = Styles.PARTLY_COVERED;
99			break;
100		default:
101			return pre;
102		}
103
104		final String lineId = "L" + Integer.toString(lineNr);
105		final ICounter branches = line.getBranchCounter();
106		switch (branches.getStatus()) {
107		case ICounter.NOT_COVERED:
108			return span(pre, lineId, style, Styles.BRANCH_NOT_COVERED,
109					"All %2$d branches missed.", branches);
110		case ICounter.FULLY_COVERED:
111			return span(pre, lineId, style, Styles.BRANCH_FULLY_COVERED,
112					"All %2$d branches covered.", branches);
113		case ICounter.PARTLY_COVERED:
114			return span(pre, lineId, style, Styles.BRANCH_PARTLY_COVERED,
115					"%1$d of %2$d branches missed.", branches);
116		default:
117			return pre.span(style, lineId);
118		}
119	}
120
121	private HTMLElement span(final HTMLElement parent, final String id,
122			final String style1, final String style2, final String title,
123			final ICounter branches) throws IOException {
124		final HTMLElement span = parent.span(style1 + " " + style2, id);
125		final Integer missed = Integer.valueOf(branches.getMissedCount());
126		final Integer total = Integer.valueOf(branches.getTotalCount());
127		span.attr("title", String.format(locale, title, missed, total));
128		return span;
129	}
130
131}
132