001    /**
002     * Copyright 2010-2013 The Kuali Foundation
003     *
004     * Licensed under the Educational Community License, Version 2.0 (the "License");
005     * you may not use this file except in compliance with the License.
006     * You may obtain a copy of the License at
007     *
008     * http://www.opensource.org/licenses/ecl2.php
009     *
010     * Unless required by applicable law or agreed to in writing, software
011     * distributed under the License is distributed on an "AS IS" BASIS,
012     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013     * See the License for the specific language governing permissions and
014     * limitations under the License.
015     */
016    package org.kuali.common.util.maven;
017    
018    import java.io.File;
019    import java.util.ArrayList;
020    import java.util.List;
021    
022    import org.apache.commons.io.FileUtils;
023    import org.apache.commons.lang3.StringUtils;
024    import org.kuali.common.util.Assert;
025    import org.kuali.common.util.LocationUtils;
026    import org.kuali.common.util.Str;
027    import org.kuali.common.util.maven.model.Artifact;
028    import org.kuali.common.util.maven.model.Dependency;
029    import org.kuali.common.util.nullify.NullUtils;
030    
031    public class RepositoryUtils {
032    
033            private static final String FS = File.separator;
034            private static final String GAV_DELIMITER = ":";
035            private static final String DEFAULT_MAVEN_REPO_PATH = ".m2" + FS + "repository";
036    
037            public static File getDefaultLocalRepository() {
038                    return new File(FileUtils.getUserDirectoryPath() + FS + DEFAULT_MAVEN_REPO_PATH);
039            }
040    
041            /**
042             * Copy an artifact from <code>repository</code> into a local repository.
043             */
044            public static final void copyArtifactToDirectory(String repository, Artifact artifact, File localRepository) {
045                    String filename = getFilename(artifact);
046                    File file = new File(localRepository, filename);
047                    copyArtifactToFile(repository, artifact, file);
048            }
049    
050            /**
051             * Copy an artifact from <code>repository</code> to a specific file on the local file system.
052             */
053            public static final void copyArtifactToFile(String repository, Artifact artifact, File file) {
054                    String location = repository + getRepositoryPath(artifact);
055                    LocationUtils.copyLocationToFile(location, file);
056            }
057    
058            /**
059             * <p>
060             * Order is <code>groupId:artifactId:version:classifier:type</code>. The ordering here matches the order Maven uses to create actual files. Which is different from what the
061             * toString() method on Maven's Artifact object produces.
062             * </p>
063             * 
064             * <p>
065             * Trailing <code>:</code>'s are omitted.
066             * </p>
067             * 
068             * <p>
069             * If every field is left blank, <code>::::</code> is returned.
070             * </p>
071             * 
072             * <pre>
073             *   org.kuali.common:kuali-jdbc:1.0.0:webapp:jar  - groupId + artifactId + version + classifier + type
074             *   org.kuali.common:kuali-jdbc:1.0.0::jar        - no classifier
075             *   ::::                                          - Every field is blank
076             *   org.kuali.common                              - groupId only
077             *   ::::jar                                       - type only
078             *   :kuali-jdbc:::jar                             - no groupId, version, classifier, or type 
079             *   org.kuali.common:kuali-jdbc                   - groupId + artifactId
080             *   org.kuali.common:kuali-jdbc:1.0.0             - groupId + artifactId + version 
081             *   org.kuali.common:kuali-jdbc:1.0.0:webapp      - no type
082             *   org.kuali.common:kuali-jdbc:1.0.0             - no classifier or type
083             *   org.kuali.common:kuali-jdbc::webapp:jar       - no version
084             * </pre>
085             */
086            public static final String toString(Artifact artifact) {
087                    List<String> tokens = new ArrayList<String>();
088                    tokens.add(toEmpty(artifact.getGroupId()));
089                    tokens.add(toEmpty(artifact.getArtifactId()));
090                    tokens.add(toEmpty(artifact.getVersion()));
091                    tokens.add(toEmpty(artifact.getClassifier().orNull()));
092                    tokens.add(toEmpty(artifact.getType()));
093                    int delimiterCount = getDelimiterCount(tokens);
094                    return getDelimitedString(tokens, delimiterCount, GAV_DELIMITER);
095            }
096    
097            /**
098             * <p>
099             * Order is <code>groupId:artifactId:version:classifier:type:scope</code>. The ordering here matches the order Maven uses to create actual files. As opposed to what the
100             * toString() method on Maven's Dependency object produces.
101             * </p>
102             * 
103             * <p>
104             * Trailing <code>:</code>'s are omitted.
105             * </p>
106             * 
107             * <p>
108             * If every field is left blank, <code>:::::</code> is returned.
109             * </p>
110             * 
111             * <pre>
112             *   org.kuali.common:kuali-jdbc:1.0.0:webapp:jar:compile - groupId + artifactId + version + classifier + type + scope
113             *   org.kuali.common:kuali-jdbc:1.0.0::jar:compile       - no classifier
114             *   org.kuali.common:kuali-jdbc:1.0.0:webapp:jar:        - no scope
115             *   :::::                                                - Every field is blank
116             *   org.kuali.common                                     - groupId only
117             *   :::::compile                                         - scope only
118             *   :kuali-jdbc:::jar                                    - artifactId + type 
119             *   org.kuali.common:kuali-jdbc                          - groupId + artifactId
120             *   org.kuali.common:kuali-jdbc:1.0.0                    - groupId + artifactId + version 
121             *   org.kuali.common:kuali-jdbc:1.0.0:webapp             - groupId + artifactId + version + classifier
122             *   org.kuali.common:kuali-jdbc:1.0.0:::compile          - no classifier or type
123             *   org.kuali.common:kuali-jdbc::webapp:jar:compile      - no version
124             * </pre>
125             */
126            public static final String toString(Dependency dependency) {
127                    List<String> tokens = new ArrayList<String>();
128                    tokens.add(toEmpty(dependency.getGroupId()));
129                    tokens.add(toEmpty(dependency.getArtifactId()));
130                    tokens.add(toEmpty(dependency.getVersion()));
131                    tokens.add(toEmpty(dependency.getClassifier().orNull()));
132                    tokens.add(toEmpty(dependency.getType()));
133                    tokens.add(toEmpty(dependency.getScope()));
134                    int delimiterCount = getDelimiterCount(tokens);
135                    return getDelimitedString(tokens, delimiterCount, GAV_DELIMITER);
136            }
137    
138            /**
139             * <p>
140             * Order is <code>groupId:artifactId:version:classifier:type:scope</code>.
141             * </p>
142             */
143            public static final Artifact parseArtifact(String gav) {
144                    Assert.noBlanks(gav);
145    
146                    String[] tokens = StringUtils.splitPreserveAllTokens(gav, GAV_DELIMITER);
147                    int len = tokens.length;
148                    Assert.isTrue(len >= 2, "groupId, artifactId, and version are required");
149                    for (int i = 0; i < len; i++) {
150                            tokens[i] = NullUtils.trimToNull(tokens[i]);
151                    }
152    
153                    String groupId = tokens[0];
154                    String artifactId = tokens[1];
155                    String version = tokens[2];
156                    String classifier = (len > 3) ? tokens[3] : NullUtils.NONE;
157                    String type = (len > 4) ? tokens[4] : Artifact.Builder.DEFAULT_TYPE;
158    
159                    return new Artifact.Builder(groupId, artifactId, version).classifier(classifier).type(type).build();
160            }
161    
162            /**
163             * <p>
164             * Order is <code>groupId:artifactId:version:classifier:type:scope</code>.
165             * </p>
166             */
167            public static final Dependency parseDependency(String gav) {
168                    Assert.noBlanks(gav);
169    
170                    String[] tokens = StringUtils.splitPreserveAllTokens(gav, GAV_DELIMITER);
171                    int len = tokens.length;
172                    Assert.isTrue(len >= 2, "groupId, artifactId, and version are required");
173                    for (int i = 0; i < len; i++) {
174                            tokens[i] = NullUtils.trimToNull(tokens[i]);
175                    }
176    
177                    String groupId = tokens[0];
178                    String artifactId = tokens[1];
179                    String version = tokens[2];
180                    String classifier = (len > 3) ? tokens[3] : NullUtils.NONE;
181                    String type = (len > 4) ? tokens[4] : Dependency.Builder.DEFAULT_TYPE;
182                    String scope = (len > 5) ? tokens[5] : Dependency.Builder.DEFAULT_SCOPE;
183    
184                    return new Dependency.Builder(groupId, artifactId, version).classifier(classifier).type(type).scope(scope).build();
185            }
186    
187            protected static final String getDelimitedString(List<String> tokens, int delimiterCount, String delimiter) {
188                    StringBuilder sb = new StringBuilder();
189                    for (int i = 0; i < tokens.size(); i++) {
190                            if (i != 0 && i < delimiterCount) {
191                                    sb.append(delimiter);
192                            }
193                            sb.append(tokens.get(i));
194                    }
195                    return sb.toString();
196            }
197    
198            protected static final int getDelimiterCount(List<String> tokens) {
199                    int count = 0;
200                    for (int i = 0; i < tokens.size(); i++) {
201                            String token = toEmpty(tokens.get(i));
202                            if (!StringUtils.isEmpty(token)) {
203                                    count = i + 1;
204                            }
205                    }
206                    return count == 0 ? tokens.size() : count;
207            }
208    
209            /**
210             * Return null if token is blank, "NULL", or "NONE"
211             * 
212             * @deprecated Use NullUtils.isNullOrNone() instead
213             */
214            @Deprecated
215            public static String toNull(String token) {
216                    if (StringUtils.isBlank(token)) {
217                            return null;
218                    }
219                    if (NullUtils.isNullOrNone(token)) {
220                            return null;
221                    }
222                    return token;
223            }
224    
225            /**
226             * Return the empty string if token is blank, "NULL", or "NONE"
227             */
228            public static String toEmpty(String token) {
229                    if (StringUtils.isBlank(token)) {
230                            return "";
231                    }
232                    if (NullUtils.isNullOrNone(token)) {
233                            return "";
234                    }
235                    return token;
236            }
237    
238            /**
239             * <pre>
240             *  org.kuali.common:kuali-util:2.0.1 -> org/kuali/common/kuali-util/2.0.1
241             * </pre>
242             */
243            public static final String getRepositoryPath(Artifact artifact) {
244                    StringBuilder sb = new StringBuilder();
245                    sb.append(Str.getPath(artifact.getGroupId()));
246                    sb.append(FS);
247                    sb.append(artifact.getArtifactId());
248                    sb.append(FS);
249                    sb.append(artifact.getVersion());
250                    return sb.toString();
251            }
252    
253            /**
254             * <pre>
255             *  org.kuali.common:kuali-util:2.0.1::jar    -> kuali-util-2.0.1.jar
256             *  org.kuali.common:kuali-util:2.0.1:sql:jar -> kuali-util-2.0.1-sql.jar
257             * </pre>
258             */
259            public static final String getFilename(Artifact artifact) {
260                    StringBuilder sb = new StringBuilder();
261                    sb.append(artifact.getArtifactId());
262                    sb.append("-");
263                    sb.append(artifact.getVersion());
264                    if (artifact.getClassifier().isPresent()) {
265                            sb.append("-");
266                            sb.append(artifact.getClassifier());
267                    }
268                    sb.append(".");
269                    sb.append(artifact.getType());
270                    return sb.toString();
271            }
272    
273            public static final File getFile(File localRepositoryDir, Artifact artifact) {
274                    String path = getRepositoryPath(artifact);
275                    String filename = getFilename(artifact);
276                    return new File(localRepositoryDir.getAbsolutePath() + FS + path, filename);
277            }
278    
279    }