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