| /******************************************************************************* |
| * Copyright (c) 2009, 2017 Mountainminds GmbH & Co. KG and Contributors |
| * All rights reserved. This program and the accompanying materials |
| * are made available under the terms of the Eclipse Public License v1.0 |
| * which accompanies this distribution, and is available at |
| * http://www.eclipse.org/legal/epl-v10.html |
| * |
| * Contributors: |
| * Marc R. Hoffmann - initial API and implementation |
| * |
| *******************************************************************************/ |
| package org.jacoco.report.internal; |
| |
| import java.util.BitSet; |
| import java.util.HashMap; |
| import java.util.HashSet; |
| import java.util.Locale; |
| import java.util.Map; |
| import java.util.Set; |
| |
| /** |
| * Internal utility to create normalized file names from string ids. The file |
| * names generated by an instance of this class have the following properties: |
| * |
| * <ul> |
| * <li>The same input id is mapped to the same file name.</li> |
| * <li>Different ids are mapped to different file names.</li> |
| * <li>For safe characters the file name corresponds to the input id, other |
| * characters are replaced by <code>_</code> (underscore).</li> |
| * <li>File names are case aware, i.e. the same file name but with different |
| * upper/lower case characters is not possible.</li> |
| * <li>If unique filenames can't directly created from the ids, additional |
| * suffixes are appended.</li> |
| * </ul> |
| */ |
| class NormalizedFileNames { |
| |
| private static final BitSet LEGAL_CHARS = new BitSet(); |
| |
| static { |
| final String legal = "abcdefghijklmnopqrstuvwxyz" |
| + "ABCDEFGHIJKLMNOPQRSTUVWYXZ0123456789$-._"; |
| for (final char c : legal.toCharArray()) { |
| LEGAL_CHARS.set(c); |
| } |
| } |
| |
| private final Map<String, String> mapping = new HashMap<String, String>(); |
| |
| private final Set<String> usedNames = new HashSet<String>(); |
| |
| public String getFileName(final String id) { |
| String name = mapping.get(id); |
| if (name != null) { |
| return name; |
| } |
| name = replaceIllegalChars(id); |
| name = ensureUniqueness(name); |
| mapping.put(id, name); |
| return name; |
| } |
| |
| private String replaceIllegalChars(final String s) { |
| final StringBuilder sb = new StringBuilder(s.length()); |
| boolean modified = false; |
| for (int i = 0; i < s.length(); i++) { |
| final char c = s.charAt(i); |
| if (LEGAL_CHARS.get(c)) { |
| sb.append(c); |
| } else { |
| sb.append('_'); |
| modified = true; |
| } |
| } |
| return modified ? sb.toString() : s; |
| } |
| |
| private String ensureUniqueness(final String s) { |
| String unique = s; |
| String lower = unique.toLowerCase(Locale.ENGLISH); |
| int idx = 1; |
| while (usedNames.contains(lower)) { |
| unique = s + '~' + idx++; |
| lower = unique.toLowerCase(Locale.ENGLISH); |
| } |
| usedNames.add(lower); |
| return unique; |
| } |
| |
| } |