diff --git a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/synccontext/named/GAVNameMapper.java b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/synccontext/named/GAVNameMapper.java index 556a9f686..b08f7867c 100644 --- a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/synccontext/named/GAVNameMapper.java +++ b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/synccontext/named/GAVNameMapper.java @@ -24,6 +24,7 @@ import org.eclipse.aether.RepositorySystemSession; import org.eclipse.aether.artifact.Artifact; import org.eclipse.aether.metadata.Metadata; +import org.eclipse.aether.util.PathUtils; import static java.util.Objects.requireNonNull; @@ -97,6 +98,8 @@ private String getArtifactName(Artifact artifact) { + artifactSuffix; } + private static final String MAVEN_METADATA = "maven-metadata.xml"; + private String getMetadataName(Metadata metadata) { String name = metadataPrefix; if (!metadata.getGroupId().isEmpty()) { @@ -107,6 +110,14 @@ private String getMetadataName(Metadata metadata) { name += fieldSeparator + metadata.getVersion(); } } + if (!MAVEN_METADATA.equals(metadata.getType())) { + name += fieldSeparator + + (fileSystemFriendly ? PathUtils.stringToPathSegment(metadata.getType()) : metadata.getType()); + } + } else { + if (!MAVEN_METADATA.equals(metadata.getType())) { + name += (fileSystemFriendly ? PathUtils.stringToPathSegment(metadata.getType()) : metadata.getType()); + } } return name + metadataSuffix; } diff --git a/maven-resolver-util/src/main/java/org/eclipse/aether/util/PathUtils.java b/maven-resolver-util/src/main/java/org/eclipse/aether/util/PathUtils.java new file mode 100644 index 000000000..e4948264a --- /dev/null +++ b/maven-resolver-util/src/main/java/org/eclipse/aether/util/PathUtils.java @@ -0,0 +1,75 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.eclipse.aether.util; + +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; + +import static java.util.Objects.requireNonNull; + +/** + * A reusable utility class for file paths. + * + * @since 1.9.25 (backported from 2.0.13) + */ +public final class PathUtils { + private PathUtils() { + // hide constructor + } + + private static final Map ILLEGAL_PATH_SEGMENT_REPLACEMENTS; + + static { + HashMap illegalPathSegmentReplacements = new HashMap<>(); + illegalPathSegmentReplacements.put("\\", "-BACKSLASH-"); + illegalPathSegmentReplacements.put("/", "-SLASH-"); + illegalPathSegmentReplacements.put(":", "-COLON-"); + illegalPathSegmentReplacements.put("\"", "-QUOTE-"); + illegalPathSegmentReplacements.put("<", "-LT-"); + illegalPathSegmentReplacements.put(">", "-GT-"); + illegalPathSegmentReplacements.put("|", "-PIPE-"); + illegalPathSegmentReplacements.put("?", "-QMARK-"); + illegalPathSegmentReplacements.put("*", "-ASTERISK-"); + ILLEGAL_PATH_SEGMENT_REPLACEMENTS = Collections.unmodifiableMap(illegalPathSegmentReplacements); + } + + /** + * Method that makes sure that passed in string is valid "path segment" string. It achieves it by potentially + * changing it, replacing illegal characters in it with legal ones. + *

+ * Note: this method considers empty string as "valid path segment", it is caller duty to ensure empty string + * is not used as path segment alone. + *

+ * This method is simplistic on purpose, and if frequently used, best if results are cached (per session) + */ + public static String stringToPathSegment(String string) { + requireNonNull(string); + StringBuilder result = new StringBuilder(string); + for (Map.Entry entry : ILLEGAL_PATH_SEGMENT_REPLACEMENTS.entrySet()) { + String illegal = entry.getKey(); + int pos = result.indexOf(illegal); + while (pos >= 0) { + result.replace(pos, pos + illegal.length(), entry.getValue()); + pos = result.indexOf(illegal); + } + } + return result.toString(); + } +}