1 /*
2 * Licensed to the Apache Software Foundation (ASF) under one
3 * or more contributor license agreements. See the NOTICE file
4 * distributed with this work for additional information
5 * regarding copyright ownership. The ASF licenses this file
6 * to you under the Apache License, Version 2.0 (the
7 * "License"); you may not use this file except in compliance
8 * with the License. You may obtain a copy of the License at
9 *
10 * http://www.apache.org/licenses/LICENSE-2.0
11 *
12 * Unless required by applicable law or agreed to in writing,
13 * software distributed under the License is distributed on an
14 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15 * KIND, either express or implied. See the License for the
16 * specific language governing permissions and limitations
17 * under the License.
18 */
19 package org.apache.maven.plugins.dependency.utils;
20
21 import java.io.BufferedReader;
22 import java.io.File;
23 import java.io.IOException;
24 import java.io.StringReader;
25 import java.io.Writer;
26 import java.nio.charset.Charset;
27 import java.nio.file.Files;
28 import java.nio.file.OpenOption;
29 import java.nio.file.StandardOpenOption;
30 import java.util.Objects;
31
32 import org.apache.maven.artifact.Artifact;
33 import org.apache.maven.artifact.ArtifactUtils;
34 import org.apache.maven.plugin.logging.Log;
35
36 /**
37 * Utility class with static helper methods.
38 *
39 * @author <a href="mailto:brianf@apache.org">Brian Fox</a>
40 */
41 public final class DependencyUtil {
42
43 /**
44 * Builds the file name. If removeVersion is set, then the file name must be reconstructed from the artifactId,
45 * Classifier (if used) and Type. Otherwise, this method returns the artifact file name.
46 *
47 * @param artifact file to be formatted
48 * @param removeVersion specifies if the version should be removed from the file name
49 * @return formatted file name in the format artifactId-[version]-[classifier].[type]
50 * @see #getFormattedFileName(Artifact, boolean, boolean)
51 */
52 public static String getFormattedFileName(Artifact artifact, boolean removeVersion) {
53 return getFormattedFileName(artifact, removeVersion, false);
54 }
55
56 /**
57 * Builds the file name. If removeVersion is set, then the file name must be reconstructed from the groupId (if
58 * <b>prependGroupId</b> is true) artifactId, Classifier (if used) and Type. Otherwise, this method returns the
59 * artifact file name.
60 *
61 * @param artifact file to be formatted
62 * @param removeVersion specifies if the version should be removed from the file name
63 * @param prependGroupId specifies if the groupId should be prepended to the file name
64 * @return formatted file name in the format [groupId].artifactId-[version]-[classifier].[type]
65 */
66 public static String getFormattedFileName(Artifact artifact, boolean removeVersion, boolean prependGroupId) {
67 return getFormattedFileName(artifact, removeVersion, prependGroupId, false);
68 }
69
70 /**
71 * Builds the file name. If removeVersion is set, then the file name must be reconstructed from the groupId (if
72 * <b>prependGroupId</b> is true) artifactId, Classifier (if used), and Type. Otherwise, this method returns the
73 * artifact file name.
74 *
75 * @param artifact file to be formatted
76 * @param removeVersion specifies if the version should be removed from the file name
77 * @param prependGroupId specifies if the groupId should be prepended to the file name
78 * @param useBaseVersion specifies if the baseVersion of the artifact should be used instead of the version
79 * @return formatted file name in the format [groupId].artifactId-[version]-[classifier].[type]
80 */
81 public static String getFormattedFileName(
82 Artifact artifact, boolean removeVersion, boolean prependGroupId, boolean useBaseVersion) {
83 return getFormattedFileName(artifact, removeVersion, prependGroupId, useBaseVersion, false);
84 }
85
86 /**
87 * Builds the file name. If removeVersion is set, then the file name must be reconstructed from the groupId (if
88 * <b>prependGroupId</b> is true) artifactId, Classifier (if used) and Type. Otherwise, this method returns the
89 * artifact file name.
90 *
91 * @param artifact file to be formatted
92 * @param removeVersion specifies if the version should be removed from the file name
93 * @param prependGroupId specifies if the groupId should be prepended to the file name
94 * @param useBaseVersion specifies if the baseVersion of the artifact should be used instead of the version
95 * @param removeClassifier specifies if the classifier of the artifact should be remved from the file name
96 * @return formatted file name in the format [groupId].artifactId-[version]-[classifier].[type]
97 */
98 public static String getFormattedFileName(
99 Artifact artifact,
100 boolean removeVersion,
101 boolean prependGroupId,
102 boolean useBaseVersion,
103 boolean removeClassifier) {
104 StringBuilder destFileName = new StringBuilder();
105
106 if (prependGroupId) {
107 destFileName.append(artifact.getGroupId()).append(".");
108 }
109
110 String versionString = "";
111 if (!removeVersion) {
112 if (useBaseVersion) {
113 versionString = "-" + ArtifactUtils.toSnapshotVersion(artifact.getVersion());
114 } else {
115 versionString = "-" + artifact.getVersion();
116 }
117 }
118
119 String classifierString = "";
120
121 if (!removeClassifier
122 && artifact.getClassifier() != null
123 && !artifact.getClassifier().isEmpty()) {
124 classifierString = "-" + artifact.getClassifier();
125 }
126 destFileName.append(artifact.getArtifactId()).append(versionString);
127 destFileName.append(classifierString).append(".");
128 destFileName.append(artifact.getArtifactHandler().getExtension());
129
130 return destFileName.toString();
131 }
132
133 /**
134 * Formats the outputDirectory based on type.
135 *
136 * @param useSubdirsPerScope if a new subdirectory should be used for each scope
137 * @param useSubdirsPerType if a new subdirectory should be used for each type
138 * @param useSubdirPerArtifact if a new subdirectory should be used for each artifact
139 * @param useRepositoryLayout if dependencies must be moved into a Maven repository layout.
140 * If set, other settings will be ignored.
141 * @param removeVersion if the version must not be mentioned in the filename
142 * @param removeType if the type must not be mentioned in the filename
143 * @param outputDirectory base outputDirectory
144 * @param artifact information about the artifact
145 * @return a formatted File object to use for output
146 */
147 public static File getFormattedOutputDirectory(
148 boolean useSubdirsPerScope,
149 boolean useSubdirsPerType,
150 boolean useSubdirPerArtifact,
151 boolean useRepositoryLayout,
152 boolean removeVersion,
153 boolean removeType,
154 File outputDirectory,
155 Artifact artifact) {
156 StringBuilder sb = new StringBuilder(128);
157 if (useRepositoryLayout) {
158 // group id
159 sb.append(artifact.getGroupId().replace('.', File.separatorChar)).append(File.separatorChar);
160 // artifact id
161 sb.append(artifact.getArtifactId()).append(File.separatorChar);
162 // version
163 sb.append(artifact.getBaseVersion()).append(File.separatorChar);
164 } else {
165 if (useSubdirsPerScope) {
166 sb.append(artifact.getScope()).append(File.separatorChar);
167 }
168 if (useSubdirsPerType) {
169 sb.append(artifact.getType()).append("s").append(File.separatorChar);
170 }
171 if (useSubdirPerArtifact) {
172 String artifactString = getDependencyId(artifact, removeVersion, removeType);
173 sb.append(artifactString).append(File.separatorChar);
174 }
175 }
176 return new File(outputDirectory, sb.toString());
177 }
178
179 private static String getDependencyId(Artifact artifact, boolean removeVersion, boolean removeType) {
180 StringBuilder sb = new StringBuilder();
181
182 sb.append(artifact.getArtifactId());
183
184 if (!removeVersion) {
185 sb.append("-");
186 sb.append(artifact.getVersion());
187 }
188
189 if (artifact.getClassifier() != null && !artifact.getClassifier().isEmpty()) {
190 sb.append("-");
191 sb.append(artifact.getClassifier());
192 }
193
194 // if the classifier and type are the same (sources), then don't
195 // repeat.
196 // avoids names like foo-sources-sources
197 if (!removeType && !Objects.equals(artifact.getClassifier(), artifact.getType())) {
198 sb.append("-");
199 sb.append(artifact.getType());
200 }
201
202 return sb.toString();
203 }
204
205 /**
206 * Writes the specified string to the specified file.
207 *
208 * @param string the string to write
209 * @param file the file to write to
210 * @param append append to existing file or not
211 * @param log ignored
212 * @throws IOException if an I/O error occurs
213 * @deprecated specify an encoding instead of a log
214 */
215 @Deprecated
216 public static synchronized void write(String string, File file, boolean append, Log log) throws IOException {
217 write(string, file, append, "UTF-8");
218 }
219
220 /**
221 * Writes the specified string to the specified file.
222 *
223 * @param string the string to write
224 * @param file the file to write to
225 * @param append append to existing file or not
226 * @param encoding character set name
227 * @throws IOException if an I/O error occurs
228 */
229 public static synchronized void write(String string, File file, boolean append, String encoding)
230 throws IOException {
231 Files.createDirectories(file.getParentFile().toPath());
232
233 OpenOption appendOption = append ? StandardOpenOption.APPEND : StandardOpenOption.TRUNCATE_EXISTING;
234
235 try (Writer writer = Files.newBufferedWriter(
236 file.toPath(),
237 Charset.forName(encoding),
238 appendOption,
239 StandardOpenOption.CREATE,
240 StandardOpenOption.WRITE)) {
241 writer.write(string);
242 }
243 }
244
245 /**
246 * Writes each line in the specified string to the log at info level.
247 * The difference between calling
248 * {@code DependencyUtil.log(s, log)} and {@code log.info(s)} is that the latter
249 * will put "[INFO]" in front of each line in the string whereas the former only
250 * outputs it once at the front of the string.
251 *
252 * @param string the string to write
253 * @param log where to log information
254 * @throws IOException if an I/O error occurs
255 */
256 public static synchronized void log(String string, Log log) throws IOException {
257 try (BufferedReader reader = new BufferedReader(new StringReader(string))) {
258 reader.lines().forEach(log::info);
259 }
260 }
261
262 /**
263 * Clean up configuration string before it can be tokenized.
264 *
265 * @param str the string which should be cleaned
266 * @return cleaned up string
267 */
268 public static String cleanToBeTokenizedString(String str) {
269 String ret = "";
270 if (!(str == null || str.isEmpty())) {
271 // remove initial and ending spaces, plus all spaces next to commas
272 ret = str.trim().replaceAll("\\s*,\\s*", ",");
273 }
274
275 return ret;
276 }
277 }