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