001package org.kuali.common.util.metainf.service;
002
003import java.io.File;
004import java.io.IOException;
005import java.util.ArrayList;
006import java.util.Collections;
007import java.util.List;
008
009import org.apache.commons.io.FileUtils;
010import org.apache.commons.lang3.ArrayUtils;
011import org.apache.commons.lang3.StringUtils;
012import org.kuali.common.util.SimpleScanner;
013import org.kuali.common.util.Str;
014import org.kuali.common.util.metainf.model.PathComparator;
015import org.kuali.common.util.metainf.spring.MetaInfDataLocation;
016import org.kuali.common.util.metainf.spring.MetaInfDataType;
017import org.kuali.common.util.metainf.spring.MetaInfGroup;
018import org.kuali.common.util.project.KualiUtilProjectConstants;
019import org.kuali.common.util.project.ProjectUtils;
020import org.kuali.common.util.project.model.Build;
021import org.kuali.common.util.project.model.Project;
022import org.kuali.common.util.project.model.ProjectIdentifier;
023import org.springframework.core.io.ClassPathResource;
024import org.springframework.core.io.Resource;
025import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
026import org.springframework.util.ResourceUtils;
027
028import com.google.common.base.Optional;
029import com.google.common.collect.Lists;
030
031public class MetaInfUtils {
032
033        public static final String RESOURCES_FILENAME_EXTENSION = "resources";
034        public static final String METAINF_DIRECTORY_NAME = "META-INF";
035
036        /**
037         * @deprecated
038         */
039        @Deprecated
040        public static final org.kuali.common.util.project.model.FeatureIdentifier FEATURE_ID = new org.kuali.common.util.project.model.FeatureIdentifier(
041                        KualiUtilProjectConstants.PROJECT_ID, "metainf");
042
043        public static final String PROPERTY_PREFIX = "metainf";
044
045        /**
046         * <code>${project.build.outputDirectory}/META-INF/org/kuali/util/kuali-util/[group].resources</code>
047         */
048        public static File getOutputFile(Project project, Build build, MetaInfGroup group) {
049                return getOutputFile(project, build, group.name().toLowerCase());
050        }
051
052        /**
053         * <code>${project.build.outputDirectory}/META-INF/org/kuali/util/kuali-util/[filename].resources</code>
054         */
055        public static File getOutputFile(Project project, Build build, String filename) {
056                return getOutputFile(project, build, Optional.<String> absent(), filename);
057        }
058
059        /**
060         * <code>${project.build.outputDirectory}/META-INF/org/kuali/util/kuali-util/[qualifier]/[group].resources</code>
061         */
062        public static File getOutputFile(Project project, Build build, String qualifier, MetaInfGroup group) {
063                return getOutputFile(project, build, Optional.of(qualifier), group.name().toLowerCase());
064        }
065
066        /**
067         * <code>${project.build.outputDirectory}/META-INF/org/kuali/util/kuali-util/[qualifier]/[filename].resources</code>
068         */
069        public static File getOutputFile(Project project, Build build, String qualifier, String filename) {
070                return getOutputFile(project, build, Optional.of(qualifier), filename);
071        }
072
073        /**
074         * <code>${project.build.outputDirectory}/META-INF/org/kuali/util/kuali-util/[qualifier]/[filename].resources</code> where <code>[qualifier]</code> is optional
075         */
076        public static File getOutputFile(Project project, Build build, Optional<String> qualifier, String filename) {
077                return getOutputFile(project, build, qualifier, Optional.<MetaInfDataLocation> absent(), Optional.<MetaInfDataType> absent(), filename);
078        }
079
080        /**
081         * <code>${project.build.outputDirectory}/META-INF/org/kuali/util/kuali-util/[qualifier]/[location]/[type]/[filename].resources</code> where <code>[qualifier]</code>,
082         * <code>[location]</code>, and <type>[type]</type> are optional
083         */
084        public static File getOutputFile(Project project, Build build, Optional<String> qualifier, Optional<MetaInfDataLocation> location, Optional<MetaInfDataType> type,
085                        String filename) {
086                StringBuilder sb = new StringBuilder();
087                sb.append(getResourcePrefix(project));
088                if (qualifier.isPresent()) {
089                        sb.append("/");
090                        sb.append(qualifier.get());
091                }
092                if (location.isPresent()) {
093                        sb.append("/");
094                        sb.append(location.get().name().toLowerCase());
095                }
096                if (type.isPresent()) {
097                        sb.append("/");
098                        sb.append(type.get().name().toLowerCase());
099                }
100                sb.append("/");
101                sb.append(getFilename(filename));
102                return new File(build.getOutputDir(), sb.toString());
103        }
104
105        /**
106         * <code>classpath:META-INF/org/kuali/util/kuali-util/[group].resources</code>
107         */
108        public static String getClasspathResource(ProjectIdentifier project, MetaInfGroup group) {
109                return getClasspathResource(project.getGroupId(), project.getArtifactId(), group);
110        }
111
112        /**
113         * <code>classpath:META-INF/org/kuali/util/kuali-util/[filename].resources</code>
114         */
115        public static String getClasspathResource(ProjectIdentifier project, String filename) {
116                return getClasspathResource(project.getGroupId(), project.getArtifactId(), filename);
117        }
118
119        /**
120         * <code>classpath:META-INF/org/kuali/util/kuali-util/[qualifier]/[group].resources</code>
121         */
122        public static String getClasspathResource(ProjectIdentifier project, String qualifier, MetaInfGroup group) {
123                return getClasspathResource(project.getGroupId(), project.getArtifactId(), Optional.of(qualifier), group.name().toLowerCase());
124        }
125
126        /**
127         * <code>classpath:META-INF/org/kuali/util/kuali-util/[qualifier]/[filename].resources</code>
128         */
129        public static String getClasspathResource(ProjectIdentifier project, String qualifier, String filename) {
130                return getClasspathResource(project.getGroupId(), project.getArtifactId(), Optional.of(qualifier), filename);
131        }
132
133        /**
134         * <code>classpath:META-INF/org/kuali/util/kuali-util/[qualifier]/[filename].resources</code> where <code>[qualifier]</code>, <code>[location]</code>, and <type>[type]</type>
135         * are optional
136         */
137        public static String getClasspathResource(ProjectIdentifier project, Optional<String> qualifier, Optional<MetaInfDataLocation> location, Optional<MetaInfDataType> type,
138                        String filename) {
139                return getClasspathResource(project.getGroupId(), project.getArtifactId(), qualifier, location, type, filename);
140        }
141
142        /**
143         * <code>classpath:META-INF/org/kuali/util/kuali-util/[group].resources</code>
144         */
145        public static String getClasspathResource(Project project, MetaInfGroup group) {
146                return getClasspathResource(project.getGroupId(), project.getArtifactId(), group);
147        }
148
149        /**
150         * <code>classpath:META-INF/org/kuali/util/kuali-util/[filename].resources</code>
151         */
152        public static String getClasspathResource(Project project, String filename) {
153                return getClasspathResource(project.getGroupId(), project.getArtifactId(), filename);
154        }
155
156        /**
157         * <code>classpath:META-INF/org/kuali/util/kuali-util/[qualifier]/[group].resources</code>
158         */
159        public static String getClasspathResource(Project project, String qualifier, MetaInfGroup group) {
160                return getClasspathResource(project.getGroupId(), project.getArtifactId(), Optional.of(qualifier), group.name().toLowerCase());
161        }
162
163        /**
164         * <code>classpath:META-INF/org/kuali/util/kuali-util/[qualifier]/[filename].resources</code>
165         */
166        public static String getClasspathResource(Project project, String qualifier, String filename) {
167                return getClasspathResource(project.getGroupId(), project.getArtifactId(), Optional.of(qualifier), filename);
168        }
169
170        /**
171         * <code>classpath:META-INF/org/kuali/util/kuali-util/[qualifier]/[filename].resources</code> where <code>[qualifier]</code>, <code>[location]</code>, and <type>[type]</type>
172         * are optional
173         */
174        public static String getClasspathResource(Project project, Optional<String> qualifier, Optional<MetaInfDataLocation> location, Optional<MetaInfDataType> type, String filename) {
175                return getClasspathResource(project.getGroupId(), project.getArtifactId(), qualifier, location, type, filename);
176        }
177
178        /**
179         * <code>classpath:META-INF/org/kuali/util/kuali-util/[group].resources</code>
180         */
181        public static String getClasspathResource(String groupId, String artifactId, MetaInfGroup group) {
182                return getClasspathResource(groupId, artifactId, Optional.<String> absent(), group.name().toLowerCase());
183        }
184
185        /**
186         * <code>classpath:META-INF/org/kuali/util/kuali-util/[filename].resources</code>
187         */
188        public static String getClasspathResource(String groupId, String artifactId, String filename) {
189                return getClasspathResource(groupId, artifactId, Optional.<String> absent(), filename);
190        }
191
192        /**
193         * <code>classpath:META-INF/org/kuali/util/kuali-util/[qualifier]/[filename].resources</code> where <code>[qualifier]</code> is optional
194         */
195        public static String getClasspathResource(String groupId, String artifactId, Optional<String> qualifier, String filename) {
196                return getClasspathResource(groupId, artifactId, qualifier, Optional.<MetaInfDataLocation> absent(), Optional.<MetaInfDataType> absent(), filename);
197        }
198
199        /**
200         * <code>classpath:META-INF/org/kuali/util/kuali-util/[qualifier]/[filename].resources</code> where <code>[qualifier]</code>, <code>[location]</code>, and <type>[type]</type>
201         * are optional
202         */
203        public static String getClasspathResource(String groupId, String artifactId, Optional<String> qualifier, Optional<MetaInfDataLocation> location,
204                        Optional<MetaInfDataType> type, String filename) {
205                StringBuilder sb = new StringBuilder();
206                sb.append(ResourceUtils.CLASSPATH_URL_PREFIX);
207                sb.append(getResourcePrefix(groupId, artifactId));
208                if (qualifier.isPresent()) {
209                        sb.append("/");
210                        sb.append(qualifier.get());
211                }
212                if (location.isPresent()) {
213                        sb.append("/");
214                        sb.append(location.get().name().toLowerCase());
215                }
216                if (type.isPresent()) {
217                        sb.append("/");
218                        sb.append(type.get().name().toLowerCase());
219                }
220                sb.append("/");
221                sb.append(getFilename(filename));
222                return sb.toString();
223        }
224
225        /**
226         * List of <code>classpath:META-INF/org/kuali/util/kuali-util/[filename].resources</code> where <code>[filename]</code> can contain an Ant-style pattern
227         */
228        public static List<String> getPatternedClasspathResources(ProjectIdentifier project, String filename) {
229                return getPatternedClasspathResources(project.getGroupId(), project.getArtifactId(), filename);
230        }
231
232        /**
233         * List of <code>classpath:META-INF/org/kuali/util/kuali-util/[qualifier]/[group].resources</code> where <code>[qualifier]</code> can contain an Ant-style pattern
234         */
235        public static List<String> getPatternedClasspathResources(ProjectIdentifier project, String qualifier, MetaInfGroup group) {
236                return getPatternedClasspathResources(project.getGroupId(), project.getArtifactId(), Optional.of(qualifier), group.name().toLowerCase());
237        }
238
239        /**
240         * List of <code>classpath:META-INF/org/kuali/util/kuali-util/[qualifier]/[filename].resources</code> where <code>[qualifier]</code> and <code>[filename]</code> can contain an
241         * Ant-style pattern
242         */
243        public static List<String> getPatternedClasspathResources(ProjectIdentifier project, String qualifier, String filename) {
244                return getPatternedClasspathResources(project.getGroupId(), project.getArtifactId(), Optional.of(qualifier), filename);
245        }
246
247        /**
248         * List of <code>classpath:META-INF/org/kuali/util/kuali-util/[qualifier]/[location]/[type]/[filename].resources</code> where <code>[qualifier]</code>, <code>[location]</code>,
249         * and <type>[type]</type> are optional and <code>[qualifier]</code> and <code>[filename]</code> can contain an Ant-style pattern
250         */
251        public static List<String> getPatternedClasspathResources(ProjectIdentifier project, Optional<String> qualifier, Optional<MetaInfDataLocation> location,
252                        Optional<MetaInfDataType> type, String filename) {
253                return getPatternedClasspathResources(project.getGroupId(), project.getArtifactId(), qualifier, location, type, filename);
254        }
255
256        /**
257         * List of <code>classpath:META-INF/org/kuali/util/kuali-util/[filename].resources</code> where <code>[filename]</code> can contain an Ant-style pattern
258         */
259        public static List<String> getPatternedClasspathResources(Project project, String filename) {
260                return getPatternedClasspathResources(project.getGroupId(), project.getArtifactId(), filename);
261        }
262
263        /**
264         * List of <code>classpath:META-INF/org/kuali/util/kuali-util/[qualifier]/[group].resources</code> where <code>[qualifier]</code> can contain an Ant-style pattern
265         */
266        public static List<String> getPatternedClasspathResources(Project project, String qualifier, MetaInfGroup group) {
267                return getPatternedClasspathResources(project.getGroupId(), project.getArtifactId(), Optional.of(qualifier), group.name().toLowerCase());
268        }
269
270        /**
271         * List of <code>classpath:META-INF/org/kuali/util/kuali-util/[qualifier]/[filename].resources</code> where <code>[qualifier]</code> and <code>[filename]</code> can contain an
272         * Ant-style pattern
273         */
274        public static List<String> getPatternedClasspathResources(Project project, String qualifier, String filename) {
275                return getPatternedClasspathResources(project.getGroupId(), project.getArtifactId(), Optional.of(qualifier), filename);
276        }
277
278        /**
279         * List of <code>classpath:META-INF/org/kuali/util/kuali-util/[qualifier]/[location]/[type]/[filename].resources</code> where <code>[qualifier]</code>, <code>[location]</code>,
280         * and <type>[type]</type> are optional and <code>[qualifier]</code> and <code>[filename]</code> can contain an Ant-style pattern
281         */
282        public static List<String> getPatternedClasspathResources(Project project, Optional<String> qualifier, Optional<MetaInfDataLocation> location, Optional<MetaInfDataType> type,
283                        String filename) {
284                return getPatternedClasspathResources(project.getGroupId(), project.getArtifactId(), qualifier, location, type, filename);
285        }
286
287        /**
288         * List of <code>classpath:META-INF/org/kuali/util/kuali-util/[filename].resources</code> where <code>[filename]</code> can contain an Ant-style pattern
289         */
290        public static List<String> getPatternedClasspathResources(String groupId, String artifactId, String filename) {
291                return getPatternedClasspathResources(groupId, artifactId, Optional.<String> absent(), filename);
292        }
293
294        /**
295         * List of <code>classpath:META-INF/org/kuali/util/kuali-util/[qualifier]/[filename].resources</code> where <code>[qualifier]</code> is optional * and <code>[qualifier]</code>
296         * and <code>[filename]</code> can contain an Ant-style pattern
297         */
298        public static List<String> getPatternedClasspathResources(String groupId, String artifactId, Optional<String> qualifier, String filename) {
299                return getPatternedClasspathResources(groupId, artifactId, qualifier, Optional.<MetaInfDataLocation> absent(), Optional.<MetaInfDataType> absent(), filename);
300        }
301
302        /**
303         * List of <code>classpath:META-INF/org/kuali/util/kuali-util/[qualifier]/[filename].resources</code> where <code>[qualifier]</code>, <code>[location]</code>, and
304         * <type>[type]</type> are optional and <code>[qualifier]</code> and <code>[filename]</code> can contain an Ant-style pattern
305         */
306        public static List<String> getPatternedClasspathResources(String groupId, String artifactId, Optional<String> qualifier, Optional<MetaInfDataLocation> location,
307                        Optional<MetaInfDataType> type, String filename) {
308                List<String> patterenedClasspathResources = new ArrayList<String>();
309                String classpathResource = getClasspathResource(groupId, artifactId, qualifier, location, type, filename);
310                PathMatchingResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
311                try {
312                        Resource[] resources = resolver.getResources(classpathResource);
313                        if (ArrayUtils.isNotEmpty(resources)) {
314                                for (Resource resource : resources) {
315                                        if (resource instanceof ClassPathResource) {
316                                                ClassPathResource classPathResource = (ClassPathResource) resource;
317                                                patterenedClasspathResources.add(ResourceUtils.CLASSPATH_URL_PREFIX + classPathResource.getPath());
318                                        }
319                                }
320                        }
321                } catch (IOException ioe) {
322                        throw new IllegalStateException("Unexpected IO error", ioe);
323                }
324                return patterenedClasspathResources;
325        }
326
327        /**
328         * Returns <code>[group].resources</code> (always lowercase)
329         */
330        public static String getFilename(MetaInfGroup group) {
331                return getFilename(group.name().toLowerCase());
332        }
333
334        /**
335         * Returns <code>[filename].resources</code>
336         */
337        public static String getFilename(String filename) {
338                return filename + "." + RESOURCES_FILENAME_EXTENSION;
339        }
340
341        /**
342         * <code>META-INF/org/kuali/util</code>
343         */
344        public static String getGroupPrefix(Project project) {
345                return getGroupPrefix(project.getGroupId());
346        }
347
348        /**
349         * <code>META-INF/org/kuali/util</code>
350         */
351        public static String getGroupPrefix(String groupId) {
352                return METAINF_DIRECTORY_NAME + "/" + Str.getPath(groupId);
353        }
354
355        /**
356         * <code>classpath:META-INF</code>
357         */
358        public static String getClasspathPrefix() {
359                return ResourceUtils.CLASSPATH_URL_PREFIX + METAINF_DIRECTORY_NAME;
360        }
361
362        /**
363         * <code>META-INF/org/kuali/util/kuali-util</code>
364         */
365        public static String getResourcePrefix(Project project) {
366                return getResourcePrefix(project.getGroupId(), project.getArtifactId());
367        }
368
369        /**
370         * <code>META-INF/org/kuali/util/kuali-util</code>
371         */
372        public static String getResourcePrefix(String groupId, String artifactId) {
373                return getGroupPrefix(groupId) + "/" + artifactId;
374        }
375
376        /**
377         * <code>META-INF/org/kuali/util/kuali-util</code>
378         */
379        public static String getResourcePrefix(ProjectIdentifier project) {
380                return getResourcePrefix(project.getGroupId(), project.getArtifactId());
381        }
382
383        /**
384         * <code>classpath:META-INF/org/kuali/util/kuali-util</code>
385         */
386        public static String getClasspathResourcePrefix(ProjectIdentifier project) {
387                StringBuilder sb = new StringBuilder();
388                sb.append(ResourceUtils.CLASSPATH_URL_PREFIX);
389                sb.append(getResourcePrefix(project.getGroupId(), project.getArtifactId()));
390                return sb.toString();
391        }
392
393        /**
394         * <code>classpath:META-INF/org/kuali/util/kuali-util</code>
395         */
396        public static String getClasspathResourcePrefix(Project project) {
397                StringBuilder sb = new StringBuilder();
398                sb.append(ResourceUtils.CLASSPATH_URL_PREFIX);
399                sb.append(getResourcePrefix(project.getGroupId(), project.getArtifactId()));
400                return sb.toString();
401        }
402
403        public static List<String> getQualifiers(File baseDirectory, ProjectIdentifier project, List<String> includes, List<String> excludes) {
404                String resourcePath = ProjectUtils.getResourcePath(project.getGroupId(), project.getArtifactId());
405                File resourceDirectory = FileUtils.getFile(baseDirectory, resourcePath);
406                return getQualifiers(resourceDirectory, includes, excludes);
407        }
408
409        public static List<String> getQualifiers(File baseDirectory, Project project, List<String> includes, List<String> excludes) {
410                String resourcePath = ProjectUtils.getResourcePath(project.getGroupId(), project.getArtifactId());
411                File resourceDirectory = FileUtils.getFile(baseDirectory, resourcePath);
412                return getQualifiers(resourceDirectory, includes, excludes);
413        }
414
415        public static List<String> getQualifiers(File baseDirectory, List<String> includes, List<String> excludes) {
416                List<String> qualifiers = Lists.newArrayList();
417                SimpleScanner scanner = new SimpleScanner(baseDirectory, includes, excludes);
418                List<String> directories = scanner.getDirectories();
419                Collections.sort(directories, new PathComparator());
420                for (String directory : directories) {
421                        if (qualifiers.isEmpty()) {
422                                qualifiers.add(directory);
423                        } else {
424                                boolean matches = false;
425                                for (String qualifier : qualifiers) {
426                                        matches |= StringUtils.startsWith(directory, qualifier);
427                                }
428                                if (!matches) {
429                                        qualifiers.add(directory);
430                                }
431                        }
432                }
433                return qualifiers;
434        }
435
436}