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;
017    
018    import java.util.ArrayList;
019    import java.util.Arrays;
020    import java.util.HashMap;
021    import java.util.List;
022    import java.util.Map;
023    import java.util.Properties;
024    import java.util.Set;
025    
026    import org.apache.commons.lang3.StringUtils;
027    import org.kuali.common.util.property.Constants;
028    import org.kuali.common.util.property.ProjectProperties;
029    import org.kuali.common.util.property.PropertiesContext;
030    import org.slf4j.Logger;
031    import org.slf4j.LoggerFactory;
032    import org.springframework.util.PropertyPlaceholderHelper;
033    
034    /**
035     * 
036     */
037    public class ProjectUtils {
038    
039            private static final Logger logger = LoggerFactory.getLogger(ProjectUtils.class);
040            private static final PropertyPlaceholderHelper PPH = Constants.DEFAULT_PROPERTY_PLACEHOLDER_HELPER;
041    
042            public static final String KUALI_COMMON_GROUP_ID = "org.kuali.common";
043    
044            private static final Map<String, Properties> PROJECT_PROPERTIES_CACHE = new HashMap<String, Properties>();
045    
046            /**
047             * 
048             */
049            public static ProjectProperties loadProjectProperties(ProjectContext context) {
050    
051                    // Get a project object based on the context information
052                    Project project = loadProject(context);
053    
054                    // Create a properties context object from the project.properties file from META-INF
055                    PropertiesContext propertiesContext = new PropertiesContext(project.getProperties());
056                    propertiesContext.setEncoding(project.getEncoding());
057                    propertiesContext.setLocations(context.getPropertyLocations());
058    
059                    // Return a project properties object
060                    return new ProjectProperties(project, propertiesContext);
061            }
062    
063            /**
064             * Create a <code>Project</code> object from the <code>context</code>. This includes loading the corresponding <code>project.properties</code> file from disk.
065             */
066            public static Project loadProject(ProjectContext context) {
067                    return loadProject(getGav(context));
068            }
069    
070            public static String getGav(ProjectContext context) {
071                    return getGav(context.getGroupId(), context.getArtifactId());
072            }
073    
074            public static String getGav(Project project) {
075                    return getGav(project.getGroupId(), project.getArtifactId());
076            }
077    
078            public static String getGav(String groupId, String artifactId) {
079                    return groupId + ":" + artifactId;
080            }
081    
082            /**
083             * Create a <code>Project</code> object from the <code>gav</code>. This includes loading the corresponding <code>project.properties</code> file from disk.
084             */
085            public static Project loadProject(String gav) {
086                    // Convert the gav into a Project
087                    Project project = getProject(gav);
088    
089                    // Load properties from a .properties file for this project
090                    Properties properties = loadProperties(project);
091    
092                    // Return a fully configured project object based on the properties
093                    return getProject(properties);
094            }
095    
096            /**
097             * Provide a way to clear the cache
098             */
099            public synchronized static void clearProjectPropertiesCache() {
100                    PROJECT_PROPERTIES_CACHE.clear();
101            }
102    
103            /**
104             * Create a skeleton <code>Project</code> object from the <code>gav</code>. Nothing but the GAV info gets filled in. Does not read <code>project.properties</code> from disk.
105             */
106            public static Project getProject(String gav) {
107                    logger.debug("Processing [{}]", gav);
108                    String[] tokens = StringUtils.split(gav, ":");
109    
110                    Project project = new Project();
111                    if (tokens.length > 0) {
112                            project.setGroupId(RepositoryUtils.toNull(tokens[0]));
113                    }
114                    if (tokens.length > 1) {
115                            project.setArtifactId(RepositoryUtils.toNull(tokens[1]));
116                    }
117                    if (tokens.length > 2) {
118                            project.setPackaging(RepositoryUtils.toNull(tokens[2]));
119                    }
120                    if (tokens.length > 3) {
121                            project.setVersion(RepositoryUtils.toNull(tokens[3]));
122                    }
123                    if (tokens.length > 4) {
124                            project.setClassifier(RepositoryUtils.toNull(tokens[4]));
125                    }
126                    return project;
127            }
128    
129            public static List<Dependency> getDependencies(String csv) {
130                    List<String> tokens = CollectionUtils.getTrimmedListFromCSV(csv);
131                    List<Dependency> dependencies = new ArrayList<Dependency>();
132                    for (String token : tokens) {
133                            Dependency dependency = RepositoryUtils.parseDependency(token);
134                            dependencies.add(dependency);
135                    }
136                    return dependencies;
137            }
138    
139            /**
140             * Return a <code>Project</code> object by copying values from the <code>properties</code> object into a <code>Project</code> object.
141             */
142            public static Project getProject(Properties properties) {
143                    List<String> skipKeys = Arrays.asList("project.dependencies");
144                    String startsWith = "project.";
145                    List<String> keys = PropertyUtils.getStartsWithKeys(properties, startsWith);
146                    Project project = new Project();
147                    project.setProperties(properties);
148                    Map<String, Object> description = ReflectionUtils.describe(project);
149                    Set<String> beanProperties = description.keySet();
150                    for (String key : keys) {
151                            if (skipKeys.contains(key)) {
152                                    continue;
153                            }
154                            String value = properties.getProperty(key);
155                            String beanProperty = getBeanProperty(key, startsWith);
156                            if (beanProperties.contains(beanProperty)) {
157                                    ReflectionUtils.copyProperty(project, beanProperty, value);
158                            }
159                    }
160                    String csv = RepositoryUtils.toNull(properties.getProperty("project.dependencies"));
161                    List<Dependency> dependencies = getDependencies(csv);
162                    project.setDependencies(dependencies);
163                    return project;
164            }
165    
166            protected static String getBeanProperty(String key, String startsWith) {
167                    String s = StringUtils.substring(key, startsWith.length());
168                    String[] tokens = StringUtils.split(s, ".");
169                    StringBuilder sb = new StringBuilder();
170                    for (int i = 0; i < tokens.length; i++) {
171                            String token = tokens[i];
172                            if (i == 0) {
173                                    sb.append(token);
174                            } else {
175                                    sb.append(StringUtils.capitalize(token));
176                            }
177                    }
178                    return sb.toString();
179            }
180    
181            public static Properties loadProperties(String gav) {
182                    return loadProperties(getProject(gav));
183            }
184    
185            public static synchronized Properties loadProperties(Project project) {
186                    String gav = getGav(project.getGroupId(), project.getArtifactId());
187                    Properties properties = PROJECT_PROPERTIES_CACHE.get(gav);
188                    if (properties != null) {
189                            return properties;
190                    } else {
191                            return loadAndCache(project, gav);
192                    }
193            }
194    
195            protected static Properties loadAndCache(Project project, String gav) {
196                    String location = getPropertiesFileLocation(project);
197                    if (!LocationUtils.exists(location)) {
198                            throw new IllegalArgumentException("[" + location + "] does not exist");
199                    }
200                    Properties properties = PropertyUtils.load(location);
201                    PROJECT_PROPERTIES_CACHE.put(gav, properties);
202                    return properties;
203            }
204    
205            public static String getPropertiesFileLocation(Project project) {
206                    Assert.hasText(project.getGroupId(), "groupId has no text");
207                    Assert.hasText(project.getArtifactId(), "artifactId has no text");
208    
209                    Properties properties = new Properties();
210                    properties.setProperty("project.groupId.path", Str.getPath(project.getGroupId()));
211                    properties.setProperty("project.artifactId", project.getArtifactId());
212    
213                    return PPH.replacePlaceholders(Constants.PROJECT_PROPERTIES_LOCATION, properties);
214            }
215    
216    }