001/* 002 * Licensed under the Apache License, Version 2.0 (the "License"); 003 * you may not use this file except in compliance with the License. 004 * You may obtain a copy of the License at 005 * 006 * http://www.apache.org/licenses/LICENSE-2.0 007 * 008 * Unless required by applicable law or agreed to in writing, software 009 * distributed under the License is distributed on an "AS IS" BASIS, 010 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 011 * See the License for the specific language governing permissions and 012 * limitations under the License. 013 */ 014 015package org.basepom.mojo.dvc; 016 017import static com.google.common.base.Preconditions.checkNotNull; 018import static com.google.common.base.Preconditions.checkState; 019 020import java.util.Objects; 021import java.util.Optional; 022 023import com.google.common.annotations.VisibleForTesting; 024import com.google.common.base.Joiner; 025import com.google.common.base.MoreObjects; 026import com.google.common.collect.ImmutableList; 027import org.apache.maven.RepositoryUtils; 028import org.apache.maven.artifact.Artifact; 029import org.apache.maven.project.MavenProject; 030import org.eclipse.aether.graph.Dependency; 031import org.eclipse.aether.graph.DependencyNode; 032 033/** 034 * A qualified name for a dependency or artifact. This is everything but a version. 035 */ 036public final class QualifiedName 037 implements Comparable<QualifiedName> { 038 039 private final String groupId; 040 private final String artifactId; 041 private final String type; 042 private final String classifier; 043 044 public static QualifiedName fromDependencyNode(final DependencyNode dependencyNode) { 045 checkNotNull(dependencyNode, "dependency is null"); 046 return fromArtifact(RepositoryUtils.toArtifact(dependencyNode.getArtifact())); 047 } 048 049 public static QualifiedName fromDependency(final Dependency dependency) { 050 checkNotNull(dependency, "dependency is null"); 051 return fromArtifact(RepositoryUtils.toArtifact(dependency.getArtifact())); 052 } 053 054 public static QualifiedName fromArtifact(final Artifact artifact) { 055 checkNotNull(artifact, "artifact is null"); 056 057 return new QualifiedName(artifact.getGroupId(), 058 artifact.getArtifactId(), 059 artifact.getType(), 060 artifact.getClassifier()); 061 } 062 063 public static QualifiedName fromProject(final MavenProject project) { 064 checkNotNull(project, "project is null"); 065 066 return new QualifiedName(project.getGroupId(), 067 project.getArtifactId(), 068 null, 069 null); 070 } 071 072 @VisibleForTesting 073 QualifiedName(String groupId, String artifactId, String type, String classifier) { 074 this.groupId = checkNotNull(groupId, "groupId is null"); 075 this.artifactId = checkNotNull(artifactId, "artifactId is null"); 076 this.type = type; 077 this.classifier = classifier; 078 079 checkState(classifier == null || type != null, "Classifier must be null if type is null"); 080 } 081 082 public String getGroupId() { 083 return groupId; 084 } 085 086 public String getArtifactId() { 087 return artifactId; 088 } 089 090 public Optional<String> getType() { 091 return Optional.ofNullable(type); 092 } 093 094 public Optional<String> getClassifier() { 095 return Optional.ofNullable(classifier); 096 } 097 098 /** 099 * @return True if this qualified name refers to a test artifact. 100 */ 101 public boolean hasTests() { 102 return getType().map(t -> t.equals("test-jar")).orElse(false) 103 || (getClassifier().map(c -> c.equals("tests")).orElse(false) && getType().map(t -> t.equals("jar")).orElse(false)); 104 } 105 106 /** 107 * @return The full name (group, artifact, type, classifier). Normalizes any test jar to be group:artifact:jar:tests. 108 */ 109 public String getFullName() { 110 ImmutableList.Builder<String> builder = ImmutableList.builder(); 111 builder.add(getGroupId()).add(getArtifactId()); 112 113 getType().ifPresent(builder::add); 114 getClassifier().ifPresent(builder::add); 115 return Joiner.on(':').join(builder.build()); 116 } 117 118 /** 119 * @return The short name (group, artifact, optional classifier). Skips absent classifiers. Normalizes test jars to `tests` classifier. 120 */ 121 public String getShortName() { 122 String result = Joiner.on(':').skipNulls() 123 .join(getGroupId(), 124 getArtifactId()); 125 126 String classifier = hasTests() ? "tests" : getClassifier().orElse(""); 127 128 if (!classifier.isEmpty()) { 129 result = result + " (" + classifier + ")"; 130 } 131 132 return result; 133 } 134 135 public int length() { 136 return getShortName().length(); 137 } 138 139 public String getMinimalName() { 140 return Joiner.on(':') 141 .join(getGroupId(), getArtifactId()); 142 } 143 144 @Override 145 public boolean equals(final Object o) { 146 if (this == o) { 147 return true; 148 } 149 if (o == null || getClass() != o.getClass()) { 150 return false; 151 } 152 QualifiedName that = (QualifiedName) o; 153 if (Objects.equals(groupId, that.groupId) 154 && Objects.equals(artifactId, that.artifactId)) { 155 156 // Two test artifacts test equal 157 if (hasTests() && ((QualifiedName) o).hasTests()) { 158 return true; 159 } 160 161 return Objects.equals(getType().orElse("jar"), that.getType().orElse("jar")) 162 && Objects.equals(getClassifier().orElse(""), that.getClassifier().orElse("")); 163 } 164 165 return false; 166 } 167 168 @Override 169 public int hashCode() { 170 if (hasTests()) { 171 return Objects.hash(groupId, artifactId, "test-jar", "tests"); 172 } else { 173 return Objects.hash(groupId, artifactId, getType().orElse("jar"), getClassifier().orElse("")); 174 } 175 } 176 177 @Override 178 public String toString() { 179 return MoreObjects.toStringHelper(this) 180 .add("groupId", groupId) 181 .add("artifactId", artifactId) 182 .add("type", type) 183 .add("classifier", classifier) 184 .toString(); 185 } 186 187 @Override 188 public int compareTo(final QualifiedName other) { 189 if (other == null) { 190 return 1; 191 } else if (other == this || equals(other)) { 192 return 0; 193 } else { 194 return getMinimalName().compareTo(other.getMinimalName()); 195 } 196 } 197}