001/*
002 * Copyright 2012-2016 the original author or authors.
003 *
004 * Licensed under the Apache 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.apache.org/licenses/LICENSE-2.0
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
017package org.springframework.boot.cli.util;
018
019import java.io.IOException;
020import java.net.MalformedURLException;
021import java.net.URL;
022import java.util.ArrayList;
023import java.util.LinkedHashSet;
024import java.util.List;
025import java.util.Set;
026
027import org.springframework.core.io.ClassPathResource;
028import org.springframework.core.io.DefaultResourceLoader;
029import org.springframework.core.io.FileSystemResourceLoader;
030import org.springframework.core.io.Resource;
031import org.springframework.core.io.UrlResource;
032import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
033import org.springframework.util.Assert;
034import org.springframework.util.ClassUtils;
035import org.springframework.util.StringUtils;
036
037/**
038 * Utilities for manipulating resource paths and URLs.
039 *
040 * @author Dave Syer
041 * @author Phillip Webb
042 */
043public abstract class ResourceUtils {
044
045        /**
046         * Pseudo URL prefix for loading from the class path: "classpath:".
047         */
048        public static final String CLASSPATH_URL_PREFIX = "classpath:";
049
050        /**
051         * Pseudo URL prefix for loading all resources from the class path: "classpath*:".
052         */
053        public static final String ALL_CLASSPATH_URL_PREFIX = "classpath*:";
054
055        /**
056         * URL prefix for loading from the file system: "file:".
057         */
058        public static final String FILE_URL_PREFIX = "file:";
059
060        /**
061         * Return URLs from a given source path. Source paths can be simple file locations
062         * (/some/file.java) or wildcard patterns (/some/**). Additionally the prefixes
063         * "file:", "classpath:" and "classpath*:" can be used for specific path types.
064         * @param path the source path
065         * @param classLoader the class loader or {@code null} to use the default
066         * @return a list of URLs
067         */
068        public static List<String> getUrls(String path, ClassLoader classLoader) {
069                if (classLoader == null) {
070                        classLoader = ClassUtils.getDefaultClassLoader();
071                }
072                path = StringUtils.cleanPath(path);
073                try {
074                        return getUrlsFromWildcardPath(path, classLoader);
075                }
076                catch (Exception ex) {
077                        throw new IllegalArgumentException(
078                                        "Cannot create URL from path [" + path + "]", ex);
079                }
080        }
081
082        private static List<String> getUrlsFromWildcardPath(String path,
083                        ClassLoader classLoader) throws IOException {
084                if (path.contains(":")) {
085                        return getUrlsFromPrefixedWildcardPath(path, classLoader);
086                }
087                Set<String> result = new LinkedHashSet<String>();
088                try {
089                        result.addAll(getUrls(FILE_URL_PREFIX + path, classLoader));
090                }
091                catch (IllegalArgumentException ex) {
092                        // ignore
093                }
094                path = stripLeadingSlashes(path);
095                result.addAll(getUrls(ALL_CLASSPATH_URL_PREFIX + path, classLoader));
096                return new ArrayList<String>(result);
097        }
098
099        private static List<String> getUrlsFromPrefixedWildcardPath(String path,
100                        ClassLoader classLoader) throws IOException {
101                Resource[] resources = new PathMatchingResourcePatternResolver(
102                                new FileSearchResourceLoader(classLoader)).getResources(path);
103                List<String> result = new ArrayList<String>();
104                for (Resource resource : resources) {
105                        if (resource.exists()) {
106                                if (resource.getURI().getScheme().equals("file")) {
107                                        if (resource.getFile().isDirectory()) {
108                                                result.addAll(getChildFiles(resource));
109                                                continue;
110                                        }
111                                }
112                                result.add(absolutePath(resource));
113                        }
114                }
115                return result;
116        }
117
118        private static List<String> getChildFiles(Resource resource) throws IOException {
119                Resource[] children = new PathMatchingResourcePatternResolver()
120                                .getResources(resource.getURL() + "/**");
121                List<String> childFiles = new ArrayList<String>();
122                for (Resource child : children) {
123                        if (!child.getFile().isDirectory()) {
124                                childFiles.add(absolutePath(child));
125                        }
126                }
127                return childFiles;
128        }
129
130        private static String absolutePath(Resource resource) throws IOException {
131                if (!resource.getURI().getScheme().equals("file")) {
132                        return resource.getURL().toExternalForm();
133                }
134                return resource.getFile().getAbsoluteFile().toURI().toString();
135        }
136
137        private static String stripLeadingSlashes(String path) {
138                while (path.startsWith("/")) {
139                        path = path.substring(1);
140                }
141                return path;
142        }
143
144        private static class FileSearchResourceLoader extends DefaultResourceLoader {
145
146                private final FileSystemResourceLoader files;
147
148                FileSearchResourceLoader(ClassLoader classLoader) {
149                        super(classLoader);
150                        this.files = new FileSystemResourceLoader();
151                }
152
153                @Override
154                public Resource getResource(String location) {
155                        Assert.notNull(location, "Location must not be null");
156                        if (location.startsWith(CLASSPATH_URL_PREFIX)) {
157                                return new ClassPathResource(
158                                                location.substring(CLASSPATH_URL_PREFIX.length()),
159                                                getClassLoader());
160                        }
161                        else {
162                                if (location.startsWith(FILE_URL_PREFIX)) {
163                                        Resource resource = this.files.getResource(location);
164                                        return resource;
165                                }
166                                try {
167                                        // Try to parse the location as a URL...
168                                        URL url = new URL(location);
169                                        return new UrlResource(url);
170                                }
171                                catch (MalformedURLException ex) {
172                                        // No URL -> resolve as resource path.
173                                        return getResourceByPath(location);
174                                }
175                        }
176                }
177
178        }
179
180}