001/*
002 * Copyright 2002-2019 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 *      https://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.test.context.util;
018
019import java.util.Arrays;
020import java.util.List;
021import java.util.regex.Pattern;
022import java.util.stream.Collectors;
023import java.util.stream.Stream;
024
025import org.springframework.core.io.Resource;
026import org.springframework.core.io.ResourceLoader;
027import org.springframework.core.io.support.ResourcePatternUtils;
028import org.springframework.util.ClassUtils;
029import org.springframework.util.ResourceUtils;
030import org.springframework.util.StringUtils;
031
032/**
033 * Utility methods for working with resources within the <em>Spring TestContext
034 * Framework</em>. Mainly for internal use within the framework.
035 *
036 * @author Sam Brannen
037 * @author Tadaya Tsuyukubo
038 * @since 4.1
039 * @see org.springframework.util.ResourceUtils
040 * @see org.springframework.core.io.Resource
041 * @see org.springframework.core.io.ClassPathResource
042 * @see org.springframework.core.io.FileSystemResource
043 * @see org.springframework.core.io.UrlResource
044 * @see org.springframework.core.io.ResourceLoader
045 */
046public abstract class TestContextResourceUtils {
047
048        private static final String SLASH = "/";
049
050        private static final Pattern PLACEHOLDER_PATTERN = Pattern.compile(".*\\$\\{[^}]+}.*");
051
052
053        /**
054         * Convert the supplied paths to classpath resource paths.
055         *
056         * <p>Delegates to {@link #convertToClasspathResourcePaths(Class, boolean, String...)}
057         * with {@code false} supplied for the {@code preservePlaceholders} flag.
058         * @param clazz the class with which the paths are associated
059         * @param paths the paths to be converted
060         * @return a new array of converted resource paths
061         * @see #convertToResources
062         */
063        public static String[] convertToClasspathResourcePaths(Class<?> clazz, String... paths) {
064                return convertToClasspathResourcePaths(clazz, false, paths);
065        }
066
067        /**
068         * Convert the supplied paths to classpath resource paths.
069         *
070         * <p>For each of the supplied paths:
071         * <ul>
072         * <li>A plain path &mdash; for example, {@code "context.xml"} &mdash; will
073         * be treated as a classpath resource that is relative to the package in
074         * which the specified class is defined. Such a path will be prepended with
075         * the {@code classpath:} prefix and the path to the package for the class.
076         * <li>A path starting with a slash will be treated as an absolute path
077         * within the classpath, for example: {@code "/org/example/schema.sql"}.
078         * Such a path will be prepended with the {@code classpath:} prefix.
079         * <li>A path which is already prefixed with a URL protocol (e.g.,
080         * {@code classpath:}, {@code file:}, {@code http:}, etc.) will not have its
081         * protocol modified.
082         * </ul>
083         * <p>Each path will then be {@linkplain StringUtils#cleanPath cleaned},
084         * unless the {@code preservePlaceholders} flag is {@code true} and the path
085         * contains one or more placeholders in the form <code>${placeholder.name}</code>.
086         * @param clazz the class with which the paths are associated
087         * @param preservePlaceholders {@code true} if placeholders should be preserved
088         * @param paths the paths to be converted
089         * @return a new array of converted resource paths
090         * @since 5.2
091         * @see #convertToResources
092         * @see ResourceUtils#CLASSPATH_URL_PREFIX
093         * @see ResourceUtils#FILE_URL_PREFIX
094         */
095        public static String[] convertToClasspathResourcePaths(Class<?> clazz, boolean preservePlaceholders, String... paths) {
096                String[] convertedPaths = new String[paths.length];
097                for (int i = 0; i < paths.length; i++) {
098                        String path = paths[i];
099
100                        // Absolute path
101                        if (path.startsWith(SLASH)) {
102                                convertedPaths[i] = ResourceUtils.CLASSPATH_URL_PREFIX + path;
103                        }
104                        // Relative path
105                        else if (!ResourcePatternUtils.isUrl(path)) {
106                                convertedPaths[i] = ResourceUtils.CLASSPATH_URL_PREFIX + SLASH +
107                                                ClassUtils.classPackageAsResourcePath(clazz) + SLASH + path;
108                        }
109                        // URL
110                        else {
111                                convertedPaths[i] = path;
112                        }
113
114                        if (!(preservePlaceholders && PLACEHOLDER_PATTERN.matcher(convertedPaths[i]).matches())) {
115                                convertedPaths[i] = StringUtils.cleanPath(convertedPaths[i]);
116                        }
117                }
118                return convertedPaths;
119        }
120
121        /**
122         * Convert the supplied paths to an array of {@link Resource} handles using
123         * the given {@link ResourceLoader}.
124         * @param resourceLoader the {@code ResourceLoader} to use to convert the paths
125         * @param paths the paths to be converted
126         * @return a new array of resources
127         * @see #convertToResourceList(ResourceLoader, String...)
128         * @see #convertToClasspathResourcePaths
129         */
130        public static Resource[] convertToResources(ResourceLoader resourceLoader, String... paths) {
131                return stream(resourceLoader, paths).toArray(Resource[]::new);
132        }
133
134        /**
135         * Convert the supplied paths to a list of {@link Resource} handles using
136         * the given {@link ResourceLoader}.
137         * @param resourceLoader the {@code ResourceLoader} to use to convert the paths
138         * @param paths the paths to be converted
139         * @return a new list of resources
140         * @since 4.2
141         * @see #convertToResources(ResourceLoader, String...)
142         * @see #convertToClasspathResourcePaths
143         */
144        public static List<Resource> convertToResourceList(ResourceLoader resourceLoader, String... paths) {
145                return stream(resourceLoader, paths).collect(Collectors.toList());
146        }
147
148        private static Stream<Resource> stream(ResourceLoader resourceLoader, String... paths) {
149                return Arrays.stream(paths).map(resourceLoader::getResource);
150        }
151
152}