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