001/* 002 * Copyright 2002-2018 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.beans.propertyeditors; 018 019import java.beans.PropertyEditorSupport; 020import java.io.IOException; 021import java.net.URI; 022import java.net.URISyntaxException; 023 024import org.springframework.core.io.ClassPathResource; 025import org.springframework.util.ClassUtils; 026import org.springframework.util.ResourceUtils; 027import org.springframework.util.StringUtils; 028 029/** 030 * Editor for {@code java.net.URI}, to directly populate a URI property 031 * instead of using a String property as bridge. 032 * 033 * <p>Supports Spring-style URI notation: any fully qualified standard URI 034 * ("file:", "http:", etc) and Spring's special "classpath:" pseudo-URL, 035 * which will be resolved to a corresponding URI. 036 * 037 * <p>By default, this editor will encode Strings into URIs. For instance, 038 * a space will be encoded into {@code %20}. This behavior can be changed 039 * by calling the {@link #URIEditor(boolean)} constructor. 040 * 041 * <p>Note: A URI is more relaxed than a URL in that it does not require 042 * a valid protocol to be specified. Any scheme within a valid URI syntax 043 * is allowed, even without a matching protocol handler being registered. 044 * 045 * @author Juergen Hoeller 046 * @since 2.0.2 047 * @see java.net.URI 048 * @see URLEditor 049 */ 050public class URIEditor extends PropertyEditorSupport { 051 052 private final ClassLoader classLoader; 053 054 private final boolean encode; 055 056 057 058 /** 059 * Create a new, encoding URIEditor, converting "classpath:" locations into 060 * standard URIs (not trying to resolve them into physical resources). 061 */ 062 public URIEditor() { 063 this(true); 064 } 065 066 /** 067 * Create a new URIEditor, converting "classpath:" locations into 068 * standard URIs (not trying to resolve them into physical resources). 069 * @param encode indicates whether Strings will be encoded or not 070 * @since 3.0 071 */ 072 public URIEditor(boolean encode) { 073 this.classLoader = null; 074 this.encode = encode; 075 } 076 077 /** 078 * Create a new URIEditor, using the given ClassLoader to resolve 079 * "classpath:" locations into physical resource URLs. 080 * @param classLoader the ClassLoader to use for resolving "classpath:" locations 081 * (may be {@code null} to indicate the default ClassLoader) 082 */ 083 public URIEditor(ClassLoader classLoader) { 084 this(classLoader, true); 085 } 086 087 /** 088 * Create a new URIEditor, using the given ClassLoader to resolve 089 * "classpath:" locations into physical resource URLs. 090 * @param classLoader the ClassLoader to use for resolving "classpath:" locations 091 * (may be {@code null} to indicate the default ClassLoader) 092 * @param encode indicates whether Strings will be encoded or not 093 * @since 3.0 094 */ 095 public URIEditor(ClassLoader classLoader, boolean encode) { 096 this.classLoader = (classLoader != null ? classLoader : ClassUtils.getDefaultClassLoader()); 097 this.encode = encode; 098 } 099 100 101 @Override 102 public void setAsText(String text) throws IllegalArgumentException { 103 if (StringUtils.hasText(text)) { 104 String uri = text.trim(); 105 if (this.classLoader != null && uri.startsWith(ResourceUtils.CLASSPATH_URL_PREFIX)) { 106 ClassPathResource resource = new ClassPathResource( 107 uri.substring(ResourceUtils.CLASSPATH_URL_PREFIX.length()), this.classLoader); 108 try { 109 setValue(resource.getURI()); 110 } 111 catch (IOException ex) { 112 throw new IllegalArgumentException("Could not retrieve URI for " + resource + ": " + ex.getMessage()); 113 } 114 } 115 else { 116 try { 117 setValue(createURI(uri)); 118 } 119 catch (URISyntaxException ex) { 120 throw new IllegalArgumentException("Invalid URI syntax: " + ex); 121 } 122 } 123 } 124 else { 125 setValue(null); 126 } 127 } 128 129 /** 130 * Create a URI instance for the given user-specified String value. 131 * <p>The default implementation encodes the value into a RFC-2396 compliant URI. 132 * @param value the value to convert into a URI instance 133 * @return the URI instance 134 * @throws java.net.URISyntaxException if URI conversion failed 135 */ 136 protected URI createURI(String value) throws URISyntaxException { 137 int colonIndex = value.indexOf(':'); 138 if (this.encode && colonIndex != -1) { 139 int fragmentIndex = value.indexOf('#', colonIndex + 1); 140 String scheme = value.substring(0, colonIndex); 141 String ssp = value.substring(colonIndex + 1, (fragmentIndex > 0 ? fragmentIndex : value.length())); 142 String fragment = (fragmentIndex > 0 ? value.substring(fragmentIndex + 1) : null); 143 return new URI(scheme, ssp, fragment); 144 } 145 else { 146 // not encoding or the value contains no scheme - fallback to default 147 return new URI(value); 148 } 149 } 150 151 152 @Override 153 public String getAsText() { 154 URI value = (URI) getValue(); 155 return (value != null ? value.toString() : ""); 156 } 157 158}