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.IOException;
15
16import org.jacoco.core.JaCoCo;
17import org.jacoco.report.internal.ReportOutputFolder;
18import org.jacoco.report.internal.html.HTMLDocument;
19import org.jacoco.report.internal.html.HTMLElement;
20import org.jacoco.report.internal.html.IHTMLReportContext;
21import org.jacoco.report.internal.html.ILinkable;
22import org.jacoco.report.internal.html.resources.Resources;
23import org.jacoco.report.internal.html.resources.Styles;
24
25/**
26 * Base class for HTML page generators. It renders the page skeleton with the
27 * breadcrumb, the title and the footer. Every report page is part of a
28 * hierarchy and has a parent page (except the root page).
29 */
30public abstract class ReportPage implements ILinkable {
31
32	private final ReportPage parent;
33
34	/** output folder for this node */
35	protected final ReportOutputFolder folder;
36
37	/** context for this report */
38	protected final IHTMLReportContext context;
39
40	/**
41	 * Creates a new report page.
42	 *
43	 * @param parent
44	 *            optional hierarchical parent
45	 * @param folder
46	 *            base folder to create this report in
47	 * @param context
48	 *            settings context
49	 */
50	protected ReportPage(final ReportPage parent,
51			final ReportOutputFolder folder, final IHTMLReportContext context) {
52		this.parent = parent;
53		this.context = context;
54		this.folder = folder;
55	}
56
57	/**
58	 * Checks whether this is the root page of the report.
59	 *
60	 * @return <code>true</code> if this is the root page
61	 */
62	protected final boolean isRootPage() {
63		return parent == null;
64	}
65
66	/**
67	 * Renders this page's content and optionally additional pages. This method
68	 * must be called at most once.
69	 *
70	 * @throws IOException
71	 *             if the page can't be written
72	 */
73	public void render() throws IOException {
74		final HTMLDocument doc = new HTMLDocument(
75				folder.createFile(getFileName()), context.getOutputEncoding());
76		doc.attr("lang", context.getLocale().getLanguage());
77		head(doc.head());
78		body(doc.body());
79		doc.close();
80	}
81
82	/**
83	 * Creates the elements within the head element.
84	 *
85	 * @param head
86	 *            head tag of the page
87	 * @throws IOException
88	 *             in case of IO problems with the report writer
89	 */
90	protected void head(final HTMLElement head) throws IOException {
91		head.meta("Content-Type", "text/html;charset=UTF-8");
92		head.link("stylesheet",
93				context.getResources().getLink(folder, Resources.STYLESHEET),
94				"text/css");
95		head.link("shortcut icon",
96				context.getResources().getLink(folder, "report.gif"),
97				"image/gif");
98		head.title().text(getLinkLabel());
99	}
100
101	private void body(final HTMLElement body) throws IOException {
102		body.attr("onload", getOnload());
103		final HTMLElement navigation = body.div(Styles.BREADCRUMB);
104		navigation.attr("id", "breadcrumb");
105		infoLinks(navigation.span(Styles.INFO));
106		breadcrumb(navigation, folder);
107		body.h1().text(getLinkLabel());
108		content(body);
109		footer(body);
110	}
111
112	/**
113	 * Returns the onload handler for this page.
114	 *
115	 * @return handler or <code>null</code>
116	 */
117	protected String getOnload() {
118		return null;
119	}
120
121	/**
122	 * Inserts additional links on the top right corner.
123	 *
124	 * @param span
125	 *            parent element
126	 * @throws IOException
127	 *             in case of IO problems with the report writer
128	 */
129	protected void infoLinks(final HTMLElement span) throws IOException {
130		span.a(context.getSessionsPage(), folder);
131	}
132
133	private void breadcrumb(final HTMLElement div, final ReportOutputFolder base)
134			throws IOException {
135		breadcrumbParent(parent, div, base);
136		div.span(getLinkStyle()).text(getLinkLabel());
137	}
138
139	private static void breadcrumbParent(final ReportPage page,
140			final HTMLElement div, final ReportOutputFolder base)
141			throws IOException {
142		if (page != null) {
143			breadcrumbParent(page.parent, div, base);
144			div.a(page, base);
145			div.text(" > ");
146		}
147	}
148
149	private void footer(final HTMLElement body) throws IOException {
150		final HTMLElement footer = body.div(Styles.FOOTER);
151		final HTMLElement versioninfo = footer.span(Styles.RIGHT);
152		versioninfo.text("Created with ");
153		versioninfo.a(JaCoCo.HOMEURL).text("JaCoCo");
154		versioninfo.text(" ").text(JaCoCo.VERSION);
155		footer.text(context.getFooterText());
156	}
157
158	/**
159	 * Specifies the local file name of this page.
160	 *
161	 * @return local file name
162	 */
163	protected abstract String getFileName();
164
165	/**
166	 * Creates the actual content of the page.
167	 *
168	 * @param body
169	 *            body tag of the page
170	 * @throws IOException
171	 *             in case of IO problems with the report writer
172	 */
173	protected abstract void content(final HTMLElement body) throws IOException;
174
175	// === ILinkable ===
176
177	public final String getLink(final ReportOutputFolder base) {
178		return folder.getLink(base, getFileName());
179	}
180
181}
182