1 /******************************************************************************* 2 * Copyright (c) 2009, 2021 Mountainminds GmbH & Co. KG and Contributors 3 * This program and the accompanying materials are made available under 4 * the terms of the Eclipse Public License 2.0 which is available at 5 * http://www.eclipse.org/legal/epl-2.0 6 * 7 * SPDX-License-Identifier: EPL-2.0 8 * 9 * Contributors: 10 * Marc R. Hoffmann - initial API and implementation 11 * 12 *******************************************************************************/ 13 package org.jacoco.report.internal.html.page; 14 15 import java.io.IOException; 16 17 import org.jacoco.core.JaCoCo; 18 import org.jacoco.report.internal.ReportOutputFolder; 19 import org.jacoco.report.internal.html.HTMLElement; 20 import org.jacoco.report.internal.html.IHTMLReportContext; 21 import org.jacoco.report.internal.html.ILinkable; 22 import org.jacoco.report.internal.html.resources.Resources; 23 import 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 */ 30 public 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 */ ReportPage(final ReportPage parent, final ReportOutputFolder folder, final IHTMLReportContext context)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 */ isRootPage()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 */ render()73 public void render() throws IOException { 74 final HTMLElement html = new HTMLElement( 75 folder.createFile(getFileName()), context.getOutputEncoding()); 76 html.attr("lang", context.getLocale().getLanguage()); 77 head(html.head()); 78 body(html.body()); 79 html.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 */ head(final HTMLElement head)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 body(final HTMLElement body)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 */ getOnload()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 */ infoLinks(final HTMLElement span)129 protected void infoLinks(final HTMLElement span) throws IOException { 130 span.a(context.getSessionsPage(), folder); 131 } 132 breadcrumb(final HTMLElement div, final ReportOutputFolder base)133 private void breadcrumb(final HTMLElement div, 134 final ReportOutputFolder base) throws IOException { 135 breadcrumbParent(parent, div, base); 136 div.span(getLinkStyle()).text(getLinkLabel()); 137 } 138 breadcrumbParent(final ReportPage page, final HTMLElement div, final ReportOutputFolder base)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 footer(final HTMLElement body)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(" "); 155 versioninfo.text(JaCoCo.VERSION); 156 footer.text(context.getFooterText()); 157 } 158 159 /** 160 * Specifies the local file name of this page. 161 * 162 * @return local file name 163 */ getFileName()164 protected abstract String getFileName(); 165 166 /** 167 * Creates the actual content of the page. 168 * 169 * @param body 170 * body tag of the page 171 * @throws IOException 172 * in case of IO problems with the report writer 173 */ content(final HTMLElement body)174 protected abstract void content(final HTMLElement body) throws IOException; 175 176 // === ILinkable === 177 getLink(final ReportOutputFolder base)178 public final String getLink(final ReportOutputFolder base) { 179 return folder.getLink(base, getFileName()); 180 } 181 182 } 183