1e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov/******************************************************************************* 2b9d1b54e300318b470d9fedccc69d75187016444Evgeny Mandrikov * Copyright (c) 2009, 2018 Mountainminds GmbH & Co. KG and Contributors 3e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov * All rights reserved. This program and the accompanying materials 4e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov * are made available under the terms of the Eclipse Public License v1.0 5e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov * which accompanies this distribution, and is available at 6e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov * http://www.eclipse.org/legal/epl-v10.html 7e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov * 8e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov * Contributors: 9e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov * Marc R. Hoffmann - initial API and implementation 10e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov * 11e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov *******************************************************************************/ 12e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikovpackage org.jacoco.report.internal; 13e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov 14e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikovimport java.io.IOException; 15e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikovimport java.io.OutputStream; 16e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikovimport java.util.HashMap; 17e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikovimport java.util.Map; 18e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov 19e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikovimport org.jacoco.report.IMultiReportOutput; 20e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov 21e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov/** 22e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov * Logical representation of a folder in the output structure. This utility 23e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov * ensures valid and unique file names and helps to create relative links. 24e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov */ 25e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikovpublic class ReportOutputFolder { 26e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov 27e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov private final IMultiReportOutput output; 28e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov 29e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov private final ReportOutputFolder parent; 30e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov 31e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov private final String path; 32e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov 33e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov /** Cached sub-folder instances to guarantee stable normalization */ 34e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov private final Map<String, ReportOutputFolder> subFolders = new HashMap<String, ReportOutputFolder>(); 35e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov 36e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov private final NormalizedFileNames fileNames; 37e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov 38e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov /** 39e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov * Creates a new root folder for the given output. 40e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov * 41e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov * @param output 42e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov * output for generated files 43e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov */ 44e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov public ReportOutputFolder(final IMultiReportOutput output) { 45e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov this(output, null, ""); 46e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov } 47e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov 48e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov /** 49e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov * Creates a new root folder for the given output. 50e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov * 51e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov * @param output 52e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov * output for generated files 53e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov */ 54e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov private ReportOutputFolder(final IMultiReportOutput output, 55e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov final ReportOutputFolder parent, final String path) { 56e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov this.output = output; 57e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov this.parent = parent; 58e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov this.path = path; 59e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov fileNames = new NormalizedFileNames(); 60e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov } 61e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov 62e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov /** 63e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov * Creates a sub-folder with the given name. 64e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov * 65e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov * @param name 66e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov * name of the sub-folder 67e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov * @return handle for output into the sub-folder 68e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov */ 69e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov public ReportOutputFolder subFolder(final String name) { 70e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov final String normalizedName = normalize(name); 71e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov ReportOutputFolder folder = subFolders.get(normalizedName); 72e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov if (folder != null) { 73e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov return folder; 74e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov } 75e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov folder = new ReportOutputFolder(output, this, path + normalizedName 76e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov + "/"); 77e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov subFolders.put(normalizedName, folder); 78e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov return folder; 79e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov } 80e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov 81e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov /** 82e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov * Creates a new file in this folder with the given local name. 83e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov * 84e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov * @param name 85e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov * name of the sub-folder 86e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov * @return handle for output into the sub-folder 87e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov * @throws IOException 88e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov * if the file creation fails 89e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov */ 90e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov public OutputStream createFile(final String name) throws IOException { 91e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov return output.createFile(path + normalize(name)); 92e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov } 93e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov 94e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov /** 95e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov * Returns a link relative to a given base to a resource within this folder. 96e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov * 97e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov * @param base 98e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov * base to create the relative link from 99e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov * @param name 100e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov * name of the file or folder in this folder 101e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov * @return relative link 102e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov * @throws IllegalArgumentException 103e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov * if this folder and the base do not have the same root 104e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov */ 105e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov public String getLink(final ReportOutputFolder base, final String name) { 106e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov if (base.isAncestorOf(this)) { 107e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov return this.path.substring(base.path.length()) + normalize(name); 108e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov } 109e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov if (base.parent == null) { 110e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov throw new IllegalArgumentException("Folders with different roots."); 111e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov } 112e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov return "../" + this.getLink(base.parent, name); 113e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov } 114e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov 115e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov private boolean isAncestorOf(final ReportOutputFolder folder) { 116e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov if (this == folder) { 117e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov return true; 118e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov } 119e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov return folder.parent == null ? false : isAncestorOf(folder.parent); 120e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov } 121e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov 122e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov private String normalize(final String name) { 123e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov return fileNames.getFileName(name); 124e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov } 125e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov 126e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov} 127