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