001/* 002 * Copyright 2002-2014 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.core.io.support; 018 019import java.beans.PropertyEditorSupport; 020import java.io.IOException; 021import java.util.ArrayList; 022import java.util.Arrays; 023import java.util.Collection; 024import java.util.List; 025 026import org.apache.commons.logging.Log; 027import org.apache.commons.logging.LogFactory; 028 029import org.springframework.core.env.Environment; 030import org.springframework.core.env.PropertyResolver; 031import org.springframework.core.env.StandardEnvironment; 032import org.springframework.core.io.Resource; 033import org.springframework.util.Assert; 034 035/** 036 * Editor for {@link org.springframework.core.io.Resource} arrays, to 037 * automatically convert {@code String} location patterns 038 * (e.g. {@code "file:C:/my*.txt"} or {@code "classpath*:myfile.txt"}) 039 * to {@code Resource} array properties. Can also translate a collection 040 * or array of location patterns into a merged Resource array. 041 * 042 * <p>A path may contain {@code ${...}} placeholders, to be 043 * resolved as {@link org.springframework.core.env.Environment} properties: 044 * e.g. {@code ${user.dir}}. Unresolvable placeholders are ignored by default. 045 * 046 * <p>Delegates to a {@link ResourcePatternResolver}, 047 * by default using a {@link PathMatchingResourcePatternResolver}. 048 * 049 * @author Juergen Hoeller 050 * @author Chris Beams 051 * @since 1.1.2 052 * @see org.springframework.core.io.Resource 053 * @see ResourcePatternResolver 054 * @see PathMatchingResourcePatternResolver 055 */ 056public class ResourceArrayPropertyEditor extends PropertyEditorSupport { 057 058 private static final Log logger = LogFactory.getLog(ResourceArrayPropertyEditor.class); 059 060 private final ResourcePatternResolver resourcePatternResolver; 061 062 private PropertyResolver propertyResolver; 063 064 private final boolean ignoreUnresolvablePlaceholders; 065 066 067 /** 068 * Create a new ResourceArrayPropertyEditor with a default 069 * {@link PathMatchingResourcePatternResolver} and {@link StandardEnvironment}. 070 * @see PathMatchingResourcePatternResolver 071 * @see Environment 072 */ 073 public ResourceArrayPropertyEditor() { 074 this(new PathMatchingResourcePatternResolver(), null, true); 075 } 076 077 /** 078 * Create a new ResourceArrayPropertyEditor with the given {@link ResourcePatternResolver} 079 * and {@link PropertyResolver} (typically an {@link Environment}). 080 * @param resourcePatternResolver the ResourcePatternResolver to use 081 * @param propertyResolver the PropertyResolver to use 082 */ 083 public ResourceArrayPropertyEditor(ResourcePatternResolver resourcePatternResolver, PropertyResolver propertyResolver) { 084 this(resourcePatternResolver, propertyResolver, true); 085 } 086 087 /** 088 * Create a new ResourceArrayPropertyEditor with the given {@link ResourcePatternResolver} 089 * and {@link PropertyResolver} (typically an {@link Environment}). 090 * @param resourcePatternResolver the ResourcePatternResolver to use 091 * @param propertyResolver the PropertyResolver to use 092 * @param ignoreUnresolvablePlaceholders whether to ignore unresolvable placeholders 093 * if no corresponding system property could be found 094 */ 095 public ResourceArrayPropertyEditor(ResourcePatternResolver resourcePatternResolver, 096 PropertyResolver propertyResolver, boolean ignoreUnresolvablePlaceholders) { 097 098 Assert.notNull(resourcePatternResolver, "ResourcePatternResolver must not be null"); 099 this.resourcePatternResolver = resourcePatternResolver; 100 this.propertyResolver = propertyResolver; 101 this.ignoreUnresolvablePlaceholders = ignoreUnresolvablePlaceholders; 102 } 103 104 105 /** 106 * Treat the given text as a location pattern and convert it to a Resource array. 107 */ 108 @Override 109 public void setAsText(String text) { 110 String pattern = resolvePath(text).trim(); 111 try { 112 setValue(this.resourcePatternResolver.getResources(pattern)); 113 } 114 catch (IOException ex) { 115 throw new IllegalArgumentException( 116 "Could not resolve resource location pattern [" + pattern + "]: " + ex.getMessage()); 117 } 118 } 119 120 /** 121 * Treat the given value as a collection or array and convert it to a Resource array. 122 * Considers String elements as location patterns and takes Resource elements as-is. 123 */ 124 @Override 125 public void setValue(Object value) throws IllegalArgumentException { 126 if (value instanceof Collection || (value instanceof Object[] && !(value instanceof Resource[]))) { 127 Collection<?> input = (value instanceof Collection ? (Collection<?>) value : Arrays.asList((Object[]) value)); 128 List<Resource> merged = new ArrayList<Resource>(); 129 for (Object element : input) { 130 if (element instanceof String) { 131 // A location pattern: resolve it into a Resource array. 132 // Might point to a single resource or to multiple resources. 133 String pattern = resolvePath((String) element).trim(); 134 try { 135 Resource[] resources = this.resourcePatternResolver.getResources(pattern); 136 for (Resource resource : resources) { 137 if (!merged.contains(resource)) { 138 merged.add(resource); 139 } 140 } 141 } 142 catch (IOException ex) { 143 // ignore - might be an unresolved placeholder or non-existing base directory 144 if (logger.isDebugEnabled()) { 145 logger.debug("Could not retrieve resources for pattern '" + pattern + "'", ex); 146 } 147 } 148 } 149 else if (element instanceof Resource) { 150 // A Resource object: add it to the result. 151 Resource resource = (Resource) element; 152 if (!merged.contains(resource)) { 153 merged.add(resource); 154 } 155 } 156 else { 157 throw new IllegalArgumentException("Cannot convert element [" + element + "] to [" + 158 Resource.class.getName() + "]: only location String and Resource object supported"); 159 } 160 } 161 super.setValue(merged.toArray(new Resource[merged.size()])); 162 } 163 164 else { 165 // An arbitrary value: probably a String or a Resource array. 166 // setAsText will be called for a String; a Resource array will be used as-is. 167 super.setValue(value); 168 } 169 } 170 171 /** 172 * Resolve the given path, replacing placeholders with 173 * corresponding system property values if necessary. 174 * @param path the original file path 175 * @return the resolved file path 176 * @see PropertyResolver#resolvePlaceholders 177 * @see PropertyResolver#resolveRequiredPlaceholders(String) 178 */ 179 protected String resolvePath(String path) { 180 if (this.propertyResolver == null) { 181 this.propertyResolver = new StandardEnvironment(); 182 } 183 return (this.ignoreUnresolvablePlaceholders ? this.propertyResolver.resolvePlaceholders(path) : 184 this.propertyResolver.resolveRequiredPlaceholders(path)); 185 } 186 187}