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.artifact; 020 021import java.io.File; 022import java.nio.file.Path; 023import java.util.Collections; 024import java.util.HashMap; 025import java.util.Map; 026import java.util.Objects; 027import java.util.regex.Matcher; 028import java.util.regex.Pattern; 029 030/** 031 * A skeleton class for artifacts. 032 */ 033public abstract class AbstractArtifact implements Artifact { 034 035 private static final String SNAPSHOT = "SNAPSHOT"; 036 037 private static final Pattern SNAPSHOT_TIMESTAMP = Pattern.compile("^(.*-)?([0-9]{8}\\.[0-9]{6}-[0-9]+)$"); 038 039 /** 040 * Constructor for subclasses. 041 */ 042 public AbstractArtifact() {} 043 044 @Override 045 public boolean isSnapshot() { 046 return isSnapshot(getVersion()); 047 } 048 049 private static boolean isSnapshot(String version) { 050 return version.endsWith(SNAPSHOT) || SNAPSHOT_TIMESTAMP.matcher(version).matches(); 051 } 052 053 @Override 054 public String getBaseVersion() { 055 return toBaseVersion(getVersion()); 056 } 057 058 private static String toBaseVersion(String version) { 059 String baseVersion; 060 061 if (version == null) { 062 baseVersion = null; 063 } else if (version.startsWith("[") || version.startsWith("(")) { 064 baseVersion = version; 065 } else { 066 Matcher m = SNAPSHOT_TIMESTAMP.matcher(version); 067 if (m.matches()) { 068 if (m.group(1) != null) { 069 baseVersion = m.group(1) + SNAPSHOT; 070 } else { 071 baseVersion = SNAPSHOT; 072 } 073 } else { 074 baseVersion = version; 075 } 076 } 077 078 return baseVersion; 079 } 080 081 /** 082 * Creates a new artifact with the specified coordinates, properties and file. 083 * 084 * @param version The version of the artifact, may be {@code null}. 085 * @param properties The properties of the artifact, may be {@code null} if none. The method may assume immutability 086 * of the supplied map, i.e. need not copy it. 087 * @param path The resolved file of the artifact, may be {@code null}. 088 * @return The new artifact instance, never {@code null}. 089 */ 090 private Artifact newInstance(String version, Map<String, String> properties, Path path) { 091 return new DefaultArtifact( 092 getGroupId(), getArtifactId(), getClassifier(), getExtension(), version, path, properties); 093 } 094 095 @Override 096 public Artifact setVersion(String version) { 097 String current = getVersion(); 098 if (current.equals(version) || (version == null && current.isEmpty())) { 099 return this; 100 } 101 return newInstance(version, getProperties(), getPath()); 102 } 103 104 /** 105 * This method should (and in Resolver is) overridden, but is kept just to preserve backward compatibility if 106 * this class is extended somewhere. 107 */ 108 public Path getPath() { 109 File file = getFile(); 110 return file != null ? file.toPath() : null; 111 } 112 113 @Deprecated 114 @Override 115 public Artifact setFile(File file) { 116 return setPath(file != null ? file.toPath() : null); 117 } 118 119 @Override 120 public Artifact setPath(Path path) { 121 Path current = getPath(); 122 if (Objects.equals(current, path)) { 123 return this; 124 } 125 return newInstance(getVersion(), getProperties(), path); 126 } 127 128 public Artifact setProperties(Map<String, String> properties) { 129 Map<String, String> current = getProperties(); 130 if (current.equals(properties) || (properties == null && current.isEmpty())) { 131 return this; 132 } 133 return newInstance(getVersion(), copyProperties(properties), getPath()); 134 } 135 136 public String getProperty(String key, String defaultValue) { 137 String value = getProperties().get(key); 138 return (value != null) ? value : defaultValue; 139 } 140 141 /** 142 * Copies the specified artifact properties. This utility method should be used when creating new artifact instances 143 * with caller-supplied properties. 144 * 145 * @param properties The properties to copy, may be {@code null}. 146 * @return The copied and read-only properties, never {@code null}. 147 */ 148 protected static Map<String, String> copyProperties(Map<String, String> properties) { 149 if (properties != null && !properties.isEmpty()) { 150 return Collections.unmodifiableMap(new HashMap<>(properties)); 151 } else { 152 return Collections.emptyMap(); 153 } 154 } 155 156 @Override 157 public String toString() { 158 StringBuilder buffer = new StringBuilder(128); 159 buffer.append(getGroupId()); 160 buffer.append(':').append(getArtifactId()); 161 buffer.append(':').append(getExtension()); 162 if (!getClassifier().isEmpty()) { 163 buffer.append(':').append(getClassifier()); 164 } 165 buffer.append(':').append(getVersion()); 166 return buffer.toString(); 167 } 168 169 /** 170 * Compares this artifact with the specified object. 171 * 172 * @param obj The object to compare this artifact against, may be {@code null}. 173 * @return {@code true} if and only if the specified object is another {@link Artifact} with equal coordinates, 174 * properties and file, {@code false} otherwise. 175 */ 176 @Override 177 public boolean equals(Object obj) { 178 if (obj == this) { 179 return true; 180 } else if (!(obj instanceof Artifact)) { 181 return false; 182 } 183 184 Artifact that = (Artifact) obj; 185 186 return Objects.equals(getArtifactId(), that.getArtifactId()) 187 && Objects.equals(getGroupId(), that.getGroupId()) 188 && Objects.equals(getVersion(), that.getVersion()) 189 && Objects.equals(getExtension(), that.getExtension()) 190 && Objects.equals(getClassifier(), that.getClassifier()) 191 && Objects.equals(getPath(), that.getPath()) 192 && Objects.equals(getProperties(), that.getProperties()); 193 } 194 195 /** 196 * Returns a hash code for this artifact. 197 * 198 * @return A hash code for the artifact. 199 */ 200 @Override 201 public int hashCode() { 202 int hash = 17; 203 hash = hash * 31 + getGroupId().hashCode(); 204 hash = hash * 31 + getArtifactId().hashCode(); 205 hash = hash * 31 + getExtension().hashCode(); 206 hash = hash * 31 + getClassifier().hashCode(); 207 hash = hash * 31 + getVersion().hashCode(); 208 hash = hash * 31 + hash(getPath()); 209 return hash; 210 } 211 212 private static int hash(Object obj) { 213 return (obj != null) ? obj.hashCode() : 0; 214 } 215}