NormalizedFileNames.java revision 0e69e1fa656262cd8d14cac05569190d813c8ea0
1/*******************************************************************************
2 * Copyright (c) 2009, 2012 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;
13
14import java.util.BitSet;
15import java.util.HashMap;
16import java.util.HashSet;
17import java.util.Locale;
18import java.util.Map;
19import java.util.Set;
20
21/**
22 * Internal utility to create normalized file names from string ids. The file
23 * names generated by an instance of this class have the following properties:
24 *
25 * <ul>
26 * <li>The same input id is mapped to the same file name.</li>
27 * <li>Different ids are mapped to different file names.</li>
28 * <li>For safe characters the file name corresponds to the input id, other
29 * characters are replaced by <code>_</code> (underscore).</li>
30 * <li>File names are case aware, i.e. the same file name but with different
31 * upper/lower case characters is not possible.</li>
32 * <li>If unique filenames can't directly created from the ids, additional
33 * suffixes are appended.</li>
34 * </ul>
35 */
36class NormalizedFileNames {
37
38	private static final BitSet LEGAL_CHARS = new BitSet();
39
40	static {
41		final String legal = "abcdefghijklmnopqrstuvwxyz"
42				+ "ABCDEFGHIJKLMNOPQRSTUVWYXZ0123456789$-._";
43		for (final char c : legal.toCharArray()) {
44			LEGAL_CHARS.set(c);
45		}
46	}
47
48	private final Map<String, String> mapping = new HashMap<String, String>();
49
50	private final Set<String> usedNames = new HashSet<String>();
51
52	public String getFileName(final String id) {
53		String name = mapping.get(id);
54		if (name != null) {
55			return name;
56		}
57		name = replaceIllegalChars(id);
58		name = ensureUniqueness(name);
59		mapping.put(id, name);
60		return name;
61	}
62
63	private String replaceIllegalChars(final String s) {
64		final StringBuilder sb = new StringBuilder(s.length());
65		boolean modified = false;
66		for (int i = 0; i < s.length(); i++) {
67			final char c = s.charAt(i);
68			if (LEGAL_CHARS.get(c)) {
69				sb.append(c);
70			} else {
71				sb.append('_');
72				modified = true;
73			}
74		}
75		return modified ? sb.toString() : s;
76	}
77
78	private String ensureUniqueness(final String s) {
79		String unique = s;
80		String lower = unique.toLowerCase(Locale.ENGLISH);
81		int idx = 1;
82		while (usedNames.contains(lower)) {
83			unique = s + '~' + idx++;
84			lower = unique.toLowerCase(Locale.ENGLISH);
85		}
86		usedNames.add(lower);
87		return unique;
88	}
89
90}
91