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