Index: test/java/org/apache/ivy/util/IvyPatternHelperTest.java =================================================================== --- test/java/org/apache/ivy/util/IvyPatternHelperTest.java (revision 806404) +++ test/java/org/apache/ivy/util/IvyPatternHelperTest.java (working copy) @@ -17,6 +17,7 @@ */ package org.apache.ivy.util; +import java.lang.reflect.Field; import java.util.HashMap; import java.util.Map; @@ -69,4 +70,21 @@ pattern, "apache", "Test", "1.0", "test", "jar", "jar")); } + public void testWindowsCompatibleFilenames() throws Exception { + String pattern = "[organization]/[module]/special:chars/[type]/[artifact].[version].[ext]"; + String org = "org.example?", module = "test|", version = "\"8.3\"", artifact = "ba*se", type = "", ext = "wh/y"; + setWindowsPlatform(true); + assertEquals("org.example_/test_/special_chars/_jar_/ba_se._8.3_.wh_y", + IvyPatternHelper.substitute(pattern, org, module, version, artifact, type, ext)); + + setWindowsPlatform(false); + assertEquals("org.example?/test|/special:chars//ba*se.\"8.3\".wh/y", + IvyPatternHelper.substitute(pattern, org, module, version, artifact, type, ext)); + } + + private void setWindowsPlatform(boolean b) throws Exception { + Field f = IvyPatternHelper.class.getDeclaredField("windowsPlatform"); + f.setAccessible(true); + f.setBoolean(null, b); + } } Index: src/java/org/apache/ivy/core/IvyPatternHelper.java =================================================================== --- src/java/org/apache/ivy/core/IvyPatternHelper.java (revision 806404) +++ src/java/org/apache/ivy/core/IvyPatternHelper.java (working copy) @@ -34,6 +34,7 @@ import org.apache.ivy.core.settings.IvyVariableContainer; import org.apache.ivy.core.settings.IvyVariableContainerImpl; import org.apache.ivy.util.Message; +import org.apache.ivy.util.StringUtils; /** */ @@ -67,6 +68,21 @@ private static final Pattern VAR_PATTERN = Pattern.compile("\\$\\{(.*?)\\}"); + private static final char[] WINDOWS_INVALID_FILENAME_CHARS = {'[', ']', '<', '>', ':', '|', '\"'}; + + private static final char[] SLASHES = {'\\', '/'}; + + + private static boolean windowsPlatform; + static { + try { + windowsPlatform = System.getProperty("os.name").indexOf("windows") > -1; + } catch (SecurityException ignored) { + //don't fail when running under a restrictive security manager + windowsPlatform = false; + } + } + public static String substitute(String pattern, ModuleRevisionId moduleRevision) { return substitute(pattern, moduleRevision.getOrganisation(), moduleRevision.getName(), moduleRevision.getBranch(), moduleRevision.getRevision(), "ivy", "ivy", "xml", null, @@ -268,8 +284,7 @@ } String token = tokenBuffer.toString(); - Object tokenValue = tokensCopy.get(token); - String value = (tokenValue == null) ? null : tokenValue.toString(); + String value = scrub(tokensCopy.get(token), EXT_KEY.equals(token)); if (insideOptionalPart) { tokenHadValue = (value != null) && (value.length() > 0); @@ -494,4 +509,19 @@ } return pattern.substring(startIndex + 1, endIndex); } + + private static String scrub(Object value, boolean removeSlashes) { + if (value == null) { + return null; + } + if (!windowsPlatform) { + return value.toString(); + } + final char substitute = '_'; + String s = StringUtils.replaceChars(value.toString(), WINDOWS_INVALID_FILENAME_CHARS, substitute); + if (removeSlashes) { + s = StringUtils.replaceChars(s, SLASHES, substitute); + } + return s; + } } Index: src/java/org/apache/ivy/util/StringUtils.java =================================================================== --- src/java/org/apache/ivy/util/StringUtils.java (revision 806404) +++ src/java/org/apache/ivy/util/StringUtils.java (working copy) @@ -189,4 +189,18 @@ return sb.toString(); } + public static String replaceChars(String s, char[] removeChars, char replacement) { + if (s == null || removeChars == null) { + return s; + } + char[] cs = s.toCharArray(); + for (int i=0; i < removeChars.length; i++) { + for (int j=0; j < cs.length; j++) { + if (cs[j] == removeChars[i]) { + cs[j] = replacement; + } + } + } + return new String(cs); + } }