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 */
016package org.kuali.common.util.maven;
017
018import java.io.File;
019import java.util.ArrayList;
020import java.util.List;
021
022import org.apache.commons.io.FileUtils;
023import org.apache.commons.lang3.StringUtils;
024import org.kuali.common.util.Assert;
025import org.kuali.common.util.LocationUtils;
026import org.kuali.common.util.Str;
027import org.kuali.common.util.maven.model.Artifact;
028import org.kuali.common.util.maven.model.Dependency;
029import org.kuali.common.util.nullify.NullUtils;
030
031public 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().get());
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        public static final boolean exists(File localRepositoryDir, Artifact artifact) {
280                File file = getFile(localRepositoryDir, artifact);
281                return LocationUtils.exists(file);
282        }
283
284}