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;
017
018import java.io.File;
019import java.util.ArrayList;
020import java.util.Arrays;
021import java.util.HashMap;
022import java.util.List;
023import java.util.Map;
024import java.util.Properties;
025import java.util.Set;
026
027import org.apache.commons.lang3.StringUtils;
028import org.kuali.common.util.property.Constants;
029import org.kuali.common.util.property.PropertiesContext;
030import org.slf4j.Logger;
031import org.slf4j.LoggerFactory;
032import org.springframework.util.PropertyPlaceholderHelper;
033
034/**
035 * 
036 */
037@Deprecated
038public class ProjectUtils {
039
040        private static final Logger logger = LoggerFactory.getLogger(ProjectUtils.class);
041        private static final PropertyPlaceholderHelper PPH = Constants.DEFAULT_PROPERTY_PLACEHOLDER_HELPER;
042        private static final String CLASSPATH = "classpath:";
043
044        @Deprecated
045        public static final String KUALI_COMMON_GROUP_ID = KualiProjectConstants.COMMON_GROUP_ID;
046
047        @Deprecated
048        public static final String KUALI_UTIL_ARTIFACT_ID = UtilProjectContext.ARTIFACT_ID;
049
050        private static final Map<String, Properties> PROJECT_PROPERTIES_CACHE = new HashMap<String, Properties>();
051
052        public static List<Project> loadProjects(List<String> projectIds) {
053                List<Project> projects = new ArrayList<Project>();
054                for (String projectId : projectIds) {
055                        Project project = ProjectUtils.loadProject(projectId);
056                        projects.add(project);
057                }
058                return projects;
059        }
060
061        /**
062         * <pre>
063         *   kuali-util = classpath:org/kuali/common/kuali-util
064         * </pre>
065         */
066        public static String getCommonClassPathPrefix(String artifactId) {
067                return getClassPathPrefix(KualiProjectConstants.COMMON_GROUP_ID, artifactId);
068        }
069
070        /**
071         * Given a groupId and artifactId, convert the groupId to groupId.base, then return the classpath prefix
072         * 
073         * <pre>
074         *   org.kuali.student.db:ks-impex-rice-db = classpath:org/kuali/student/ks-impex-rice-db
075         *   org.kuali.common:kuali-util           = classpath:org/kuali/common/kuali-util
076         * </pre>
077         */
078        public static String getClassPathPrefix(String groupId, String artifactId) {
079                Project project = loadProject(groupId, artifactId);
080                return CLASSPATH + getResourcePath(project);
081        }
082
083        /**
084         * Given groupId:artifactId, convert the groupId to groupId.base, then return the classpath prefix
085         * 
086         * <pre>
087         *   org.kuali.student.db:ks-impex-rice-db = classpath:org/kuali/student/ks-impex-rice-db
088         *   org.kuali.common:kuali-util           = classpath:org/kuali/common/kuali-util
089         * </pre>
090         * 
091         * Use getClassPathPrefixFromProjectId() instead
092         */
093        @Deprecated
094        public static String getClassPathPrefixFromGAV(String projectId) {
095                Project project = getProject(projectId);
096                return getClassPathPrefix(project);
097        }
098
099        /**
100         * Given groupId:artifactId, convert the groupId to groupId.base, then return the classpath prefix
101         * 
102         * <pre>
103         *   org.kuali.student.db:ks-impex-rice-db = classpath:org/kuali/student/ks-impex-rice-db
104         *   org.kuali.common:kuali-util           = classpath:org/kuali/common/kuali-util
105         * </pre>
106         */
107        public static String getClassPathPrefixFromProjectId(String projectId) {
108                Project project = getProject(projectId);
109                return getClassPathPrefix(project);
110        }
111
112        /**
113         * Given a project containing groupId + artifactId, convert the groupId to groupId.base, then return the classpath prefix
114         * 
115         * <pre>
116         *   org.kuali.student.db:ks-impex-rice-db = classpath:org/kuali/student/ks-impex-rice-db
117         *   org.kuali.common:kuali-util           = classpath:org/kuali/common/kuali-util
118         * </pre>
119         */
120        public static String getClassPathPrefix(Project project) {
121                return getClassPathPrefix(project.getGroupId(), project.getArtifactId());
122        }
123
124        /**
125         * Given a groupId and artifactId, convert the groupId to groupId.base, then return a resource path relative to directory
126         * 
127         * <pre>
128         *   org.kuali.student.db:ks-impex-rice-db    = org/kuali/student/ks-impex-rice-db
129         *   org.kuali.common:kuali-util              = org/kuali/common/kuali-util
130         *   
131         *   /tmp/x/y/z + org.kuali.common:kuali-util = /tmp/x/y/z/org/kuali/common/kuali-util
132         * </pre>
133         */
134        public static File getResourceDirectory(File directory, Project project) {
135                String resourcePath = getResourcePath(project);
136                File file = new File(directory, resourcePath);
137                return new File(LocationUtils.getCanonicalPath(file));
138        }
139
140        /**
141         * Given a groupId and artifactId, convert the groupId to groupId.base, then return a handle to a file relative to directory with the given filename
142         * 
143         * <pre>
144         *   org.kuali.student.db:ks-impex-rice-db              = org/kuali/student/ks-impex-rice-db
145         *   org.kuali.common:kuali-util                        = org/kuali/common/kuali-util
146         *   
147         *   /tmp/x/y/z + org.kuali.common:kuali-util + foo.txt = /tmp/x/y/z/org/kuali/common/kuali-util/foo.txt
148         * </pre>
149         */
150        public static File getResourceFile(File directory, Project project, String filename) {
151                File dir = getResourceDirectory(directory, project);
152                return new File(dir, filename);
153        }
154
155        /**
156         * Given groupId:artifactId, convert the groupId to groupId.base, then return a resource friendly prefix
157         * 
158         * <pre>
159         *   org.kuali.student.db:ks-impex-rice-db = org/kuali/student/ks-impex-rice-db
160         *   org.kuali.common:kuali-util           = org/kuali/common/kuali-util
161         * </pre>
162         */
163        public static String getResourcePath(Project project) {
164                Properties properties = project.getProperties();
165                String groupIdPath = properties.getProperty(Constants.GROUP_ID_PATH_KEY);
166                Assert.hasText(groupIdPath, "groupIdPath has no text");
167                String artifactId = project.getArtifactId();
168                return groupIdPath + "/" + artifactId;
169        }
170
171        /**
172         * 
173         */
174        @Deprecated
175        public static org.kuali.common.util.property.ProjectProperties getProjectProperties(ProjectContext context) {
176
177                // Get a project object based on the context information
178                Project project = loadProject(context);
179
180                // Create a properties context object from the project.properties file from META-INF
181                PropertiesContext propertiesContext = new PropertiesContext(project.getProperties());
182                propertiesContext.setEncoding(project.getEncoding());
183                propertiesContext.setLocations(context.getPropertyLocations());
184
185                // Return a project properties object
186                return new org.kuali.common.util.property.ProjectProperties(project, propertiesContext);
187        }
188
189        /**
190         * Create a <code>Project</code> object from the <code>context</code>. This includes loading the corresponding <code>project.properties</code> file from disk.
191         */
192        @Deprecated
193        public static Project loadProject(ProjectContext context) {
194                return loadProject(getGav(context));
195        }
196
197        @Deprecated
198        public static String getGav(ProjectContext context) {
199                return getGav(context.getGroupId(), context.getArtifactId());
200        }
201
202        @Deprecated
203        public static String getGav(Project project) {
204                return getGav(project.getGroupId(), project.getArtifactId());
205        }
206
207        @Deprecated
208        public static String getGav(String groupId, String artifactId) {
209                return groupId + ":" + artifactId;
210        }
211
212        public static String getProjectId(Project project) {
213                return getProjectId(project.getGroupId(), project.getArtifactId());
214        }
215
216        public static String getProjectId(String groupId, String artifactId) {
217                return groupId + ":" + artifactId;
218        }
219
220        /**
221         * Create a <code>Project</code> object from <code>groupId</code>, <code>artifactId</code> pair. This includes loading the corresponding <code>project.properties</code> file
222         * from disk.
223         */
224        public static Project loadProject(String groupId, String artifactId) {
225                String projectId = getProjectId(groupId, artifactId);
226                return loadProject(projectId);
227        }
228
229        /**
230         * Create a <code>Project</code> object from the <code>projectId</code>. This includes loading the corresponding <code>project.properties</code> file from disk.
231         */
232        public static Project loadProject(String projectId) {
233                // Convert the projectId into a Project
234                Project project = getProject(projectId);
235
236                // Load properties from a .properties file for this project
237                Properties properties = loadProperties(project);
238
239                // Return a fully configured project object based on the properties
240                Project loadedProject = getProject(properties);
241
242                // return the project we loaded
243                return loadedProject;
244        }
245
246        /**
247         * Provide a way to clear the cache
248         */
249        public synchronized static void clearCache() {
250                PROJECT_PROPERTIES_CACHE.clear();
251        }
252
253        /**
254         * Create a skeleton <code>Project</code> object from the <code>gav</code>. Nothing but the GAV info (groupId:artifactId:packaging:version:classifier) gets filled in. Does not
255         * read <code>project.properties</code> from disk.
256         */
257        public static Project getProject(String gav) {
258                logger.debug("Processing [{}]", gav);
259                String[] tokens = StringUtils.split(gav, ":");
260
261                Project project = new Project();
262                if (tokens.length > 0) {
263                        project.setGroupId(RepositoryUtils.toNull(tokens[0]));
264                }
265                if (tokens.length > 1) {
266                        project.setArtifactId(RepositoryUtils.toNull(tokens[1]));
267                }
268                if (tokens.length > 2) {
269                        project.setPackaging(RepositoryUtils.toNull(tokens[2]));
270                }
271                if (tokens.length > 3) {
272                        project.setVersion(RepositoryUtils.toNull(tokens[3]));
273                }
274                if (tokens.length > 4) {
275                        project.setClassifier(RepositoryUtils.toNull(tokens[4]));
276                }
277                return project;
278        }
279
280        public static List<Dependency> getDependencies(String csv) {
281                List<String> tokens = CollectionUtils.getTrimmedListFromCSV(csv);
282                List<Dependency> dependencies = new ArrayList<Dependency>();
283                for (String token : tokens) {
284                        Dependency dependency = RepositoryUtils.parseDependency(token);
285                        dependencies.add(dependency);
286                }
287                return dependencies;
288        }
289
290        /**
291         * Return a <code>Project</code> object by copying values from the <code>properties</code> object into a <code>Project</code> object.
292         */
293        public static Project getProject(Properties properties) {
294                List<String> skipKeys = Arrays.asList("project.dependencies");
295                String startsWith = "project.";
296                List<String> keys = PropertyUtils.getStartsWithKeys(properties, startsWith);
297                Project project = new Project();
298                project.setProperties(properties);
299                Map<String, Object> description = ReflectionUtils.describe(project);
300                Set<String> beanProperties = description.keySet();
301                for (String key : keys) {
302                        if (skipKeys.contains(key)) {
303                                continue;
304                        }
305                        String value = properties.getProperty(key);
306                        String beanProperty = getBeanProperty(key, startsWith);
307                        if (beanProperties.contains(beanProperty)) {
308                                ReflectionUtils.copyProperty(project, beanProperty, value);
309                        }
310                }
311                String csv = RepositoryUtils.toNull(properties.getProperty("project.dependencies"));
312                List<Dependency> dependencies = getDependencies(csv);
313                project.setDependencies(dependencies);
314                return project;
315        }
316
317        protected static String getBeanProperty(String key, String startsWith) {
318                String s = StringUtils.substring(key, startsWith.length());
319                String[] tokens = StringUtils.split(s, ".");
320                StringBuilder sb = new StringBuilder();
321                for (int i = 0; i < tokens.length; i++) {
322                        String token = tokens[i];
323                        if (i == 0) {
324                                sb.append(token);
325                        } else {
326                                sb.append(StringUtils.capitalize(token));
327                        }
328                }
329                return sb.toString();
330        }
331
332        public static Properties loadProperties(String gav) {
333                return loadProperties(getProject(gav));
334        }
335
336        /**
337         * Use the groupId and artifactId from this project to load the corresponding project.properties file and cache it in our internal Map
338         */
339        public static synchronized Properties loadProperties(Project project) {
340                String projectId = getProjectId(project.getGroupId(), project.getArtifactId());
341                Properties properties = PROJECT_PROPERTIES_CACHE.get(projectId);
342                if (properties == null) {
343                        properties = loadAndCache(project, projectId);
344                }
345                return properties;
346        }
347
348        protected static Properties loadAndCache(Project project, String projectId) {
349                String location = getPropertiesFileLocation(project);
350
351                // If it doesn't exist, we've got issues
352                Assert.exists(location);
353
354                Properties properties = PropertyUtils.load(location);
355                PROJECT_PROPERTIES_CACHE.put(projectId, properties);
356                return properties;
357        }
358
359        public static String getPropertiesFileLocation(Project project) {
360                Assert.hasText(project.getGroupId(), "groupId has no text");
361                Assert.hasText(project.getArtifactId(), "artifactId has no text");
362
363                Properties properties = new Properties();
364                properties.setProperty(Constants.GROUP_ID_PATH_KEY, Str.getPath(project.getGroupId()));
365                properties.setProperty(Constants.ARTIFACT_ID_KEY, project.getArtifactId());
366
367                return PPH.replacePlaceholders(Constants.PROJECT_PROPERTIES_LOCATION, properties);
368        }
369
370}