001/*
002 * Licensed to the Apache Software Foundation (ASF) under one
003 * or more contributor license agreements.  See the NOTICE file
004 * distributed with this work for additional information
005 * regarding copyright ownership.  The ASF licenses this file
006 * to you under the Apache License, Version 2.0 (the
007 * "License"); you may not use this file except in compliance
008 * with the License.  You may obtain a copy of the License at
009 *
010 *   http://www.apache.org/licenses/LICENSE-2.0
011 *
012 * Unless required by applicable law or agreed to in writing,
013 * software distributed under the License is distributed on an
014 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
015 * KIND, either express or implied.  See the License for the
016 * specific language governing permissions and limitations
017 * under the License.
018 */
019package org.eclipse.aether.internal.impl.synccontext.named;
020
021import java.util.Collection;
022import java.util.Comparator;
023import java.util.TreeSet;
024
025import org.eclipse.aether.RepositorySystemSession;
026import org.eclipse.aether.artifact.Artifact;
027import org.eclipse.aether.metadata.Metadata;
028import org.eclipse.aether.named.NamedLockKey;
029import org.eclipse.aether.util.PathUtils;
030import org.eclipse.aether.util.artifact.ArtifactIdUtils;
031
032import static java.util.Objects.requireNonNull;
033
034/**
035 * Artifact GAV {@link NameMapper}, uses artifact and metadata coordinates to name their corresponding locks. Is not
036 * considering local repository, only the artifact coordinates. May use custom prefixes and suffixes and separators,
037 * hence this instance may or may not be filesystem friendly (depends on strings used).
038 * <p>
039 * Note: in earlier Resolver 1.9.x versions this mapper was the default, but it changed to {@link GAECVNameMapper}
040 * in 1.9.25.
041 */
042public class GAVNameMapper implements NameMapper {
043    protected final boolean fileSystemFriendly;
044
045    protected final String artifactPrefix;
046
047    protected final String artifactSuffix;
048
049    protected final String metadataPrefix;
050
051    protected final String metadataSuffix;
052
053    protected final String fieldSeparator;
054
055    public GAVNameMapper(
056            boolean fileSystemFriendly,
057            String artifactPrefix,
058            String artifactSuffix,
059            String metadataPrefix,
060            String metadataSuffix,
061            String fieldSeparator) {
062        this.fileSystemFriendly = fileSystemFriendly;
063        this.artifactPrefix = requireNonNull(artifactPrefix);
064        this.artifactSuffix = requireNonNull(artifactSuffix);
065        this.metadataPrefix = requireNonNull(metadataPrefix);
066        this.metadataSuffix = requireNonNull(metadataSuffix);
067        this.fieldSeparator = requireNonNull(fieldSeparator);
068    }
069
070    @Override
071    public boolean isFileSystemFriendly() {
072        return fileSystemFriendly;
073    }
074
075    @Override
076    public Collection<NamedLockKey> nameLocks(
077            final RepositorySystemSession session,
078            final Collection<? extends Artifact> artifacts,
079            final Collection<? extends Metadata> metadatas) {
080        // Deadlock prevention: https://stackoverflow.com/a/16780988/696632
081        // We must acquire multiple locks always in the same order!
082        TreeSet<NamedLockKey> keys = new TreeSet<>(Comparator.comparing(NamedLockKey::name));
083        if (artifacts != null) {
084            for (Artifact artifact : artifacts) {
085                keys.add(NamedLockKey.of(getArtifactName(artifact), ArtifactIdUtils.toBaseId(artifact)));
086            }
087        }
088
089        if (metadatas != null) {
090            for (Metadata metadata : metadatas) {
091                keys.add(NamedLockKey.of(getMetadataName(metadata), toMetadataId(metadata)));
092            }
093        }
094        return keys;
095    }
096
097    protected String getArtifactName(Artifact artifact) {
098        return artifactPrefix
099                + artifact.getGroupId()
100                + fieldSeparator
101                + artifact.getArtifactId()
102                + fieldSeparator
103                + artifact.getBaseVersion()
104                + artifactSuffix;
105    }
106
107    protected static final String MAVEN_METADATA = "maven-metadata.xml";
108
109    protected String getMetadataName(Metadata metadata) {
110        String name = metadataPrefix;
111        if (!metadata.getGroupId().isEmpty()) {
112            name += metadata.getGroupId();
113            if (!metadata.getArtifactId().isEmpty()) {
114                name += fieldSeparator + metadata.getArtifactId();
115                if (!metadata.getVersion().isEmpty()) {
116                    name += fieldSeparator + metadata.getVersion();
117                }
118            }
119            if (!MAVEN_METADATA.equals(metadata.getType())) {
120                name += fieldSeparator
121                        + (fileSystemFriendly ? PathUtils.stringToPathSegment(metadata.getType()) : metadata.getType());
122            }
123        } else {
124            if (!MAVEN_METADATA.equals(metadata.getType())) {
125                name += (fileSystemFriendly ? PathUtils.stringToPathSegment(metadata.getType()) : metadata.getType());
126            }
127        }
128        return name + metadataSuffix;
129    }
130
131    protected String toMetadataId(Metadata metadata) {
132        String name = "";
133        if (!metadata.getGroupId().isEmpty()) {
134            name += metadata.getGroupId();
135            if (!metadata.getArtifactId().isEmpty()) {
136                name += ":" + metadata.getArtifactId();
137                if (!metadata.getVersion().isEmpty()) {
138                    name += ":" + metadata.getVersion();
139                }
140            }
141        }
142        if (!metadata.getType().isEmpty()) {
143            name += (name.isEmpty() ? "" : ":") + metadata.getType();
144        }
145        return name;
146    }
147
148    /**
149     * @deprecated Use {@link NameMappers} to create name mappers instead.
150     */
151    @Deprecated
152    public static NameMapper gav() {
153        return new GAVNameMapper(false, "artifact:", "", "metadata:", "", ":");
154    }
155
156    /**
157     * @deprecated Use {@link NameMappers} to create name mappers instead.
158     */
159    @Deprecated
160    public static NameMapper fileGav() {
161        return new GAVNameMapper(true, "artifact~", ".lock", "metadata~", ".lock", "~");
162    }
163}