View Javadoc
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 }