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