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; 14 15 import java.io.IOException; 16 import java.io.OutputStream; 17 import java.util.HashMap; 18 import java.util.Map; 19 20 import org.jacoco.report.IMultiReportOutput; 21 22 /** 23 * Logical representation of a folder in the output structure. This utility 24 * ensures valid and unique file names and helps to create relative links. 25 */ 26 public class ReportOutputFolder { 27 28 private final IMultiReportOutput output; 29 30 private final ReportOutputFolder parent; 31 32 private final String path; 33 34 /** Cached sub-folder instances to guarantee stable normalization */ 35 private final Map<String, ReportOutputFolder> subFolders = new HashMap<String, ReportOutputFolder>(); 36 37 private final NormalizedFileNames fileNames; 38 39 /** 40 * Creates a new root folder for the given output. 41 * 42 * @param output 43 * output for generated files 44 */ ReportOutputFolder(final IMultiReportOutput output)45 public ReportOutputFolder(final IMultiReportOutput output) { 46 this(output, null, ""); 47 } 48 49 /** 50 * Creates a new root folder for the given output. 51 * 52 * @param output 53 * output for generated files 54 */ ReportOutputFolder(final IMultiReportOutput output, final ReportOutputFolder parent, final String path)55 private ReportOutputFolder(final IMultiReportOutput output, 56 final ReportOutputFolder parent, final String path) { 57 this.output = output; 58 this.parent = parent; 59 this.path = path; 60 fileNames = new NormalizedFileNames(); 61 } 62 63 /** 64 * Creates a sub-folder with the given name. 65 * 66 * @param name 67 * name of the sub-folder 68 * @return handle for output into the sub-folder 69 */ subFolder(final String name)70 public ReportOutputFolder subFolder(final String name) { 71 final String normalizedName = normalize(name); 72 ReportOutputFolder folder = subFolders.get(normalizedName); 73 if (folder != null) { 74 return folder; 75 } 76 folder = new ReportOutputFolder(output, this, 77 path + normalizedName + "/"); 78 subFolders.put(normalizedName, folder); 79 return folder; 80 } 81 82 /** 83 * Creates a new file in this folder with the given local name. 84 * 85 * @param name 86 * name of the sub-folder 87 * @return handle for output into the sub-folder 88 * @throws IOException 89 * if the file creation fails 90 */ createFile(final String name)91 public OutputStream createFile(final String name) throws IOException { 92 return output.createFile(path + normalize(name)); 93 } 94 95 /** 96 * Returns a link relative to a given base to a resource within this folder. 97 * 98 * @param base 99 * base to create the relative link from 100 * @param name 101 * name of the file or folder in this folder 102 * @return relative link 103 * @throws IllegalArgumentException 104 * if this folder and the base do not have the same root 105 */ getLink(final ReportOutputFolder base, final String name)106 public String getLink(final ReportOutputFolder base, final String name) { 107 if (base.isAncestorOf(this)) { 108 return this.path.substring(base.path.length()) + normalize(name); 109 } 110 if (base.parent == null) { 111 throw new IllegalArgumentException("Folders with different roots."); 112 } 113 return "../" + this.getLink(base.parent, name); 114 } 115 isAncestorOf(final ReportOutputFolder folder)116 private boolean isAncestorOf(final ReportOutputFolder folder) { 117 if (this == folder) { 118 return true; 119 } 120 return folder.parent == null ? false : isAncestorOf(folder.parent); 121 } 122 normalize(final String name)123 private String normalize(final String name) { 124 return fileNames.getFileName(name); 125 } 126 127 } 128