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;
023import java.nio.file.FileSystemNotFoundException;
024import java.nio.file.Path;
025import java.nio.file.Paths;
026
027import org.springframework.core.io.Resource;
028import org.springframework.core.io.ResourceEditor;
029import org.springframework.core.io.ResourceLoader;
030import org.springframework.util.Assert;
031
032/**
033 * Editor for {@code java.nio.file.Path}, to directly populate a Path
034 * property instead of using a String property as bridge.
035 *
036 * <p>Based on {@link Paths#get(URI)}'s resolution algorithm, checking
037 * registered NIO file system providers, including the default file system
038 * for "file:..." paths. Also supports Spring-style URL notation: any fully
039 * qualified standard URL and Spring's special "classpath:" pseudo-URL, as
040 * well as Spring's context-specific relative file paths. As a fallback, a
041 * path will be resolved in the file system via {@code Paths#get(String)}
042 * if no existing context-relative resource could be found.
043 *
044 * @author Juergen Hoeller
045 * @since 4.3.2
046 * @see java.nio.file.Path
047 * @see Paths#get(URI)
048 * @see ResourceEditor
049 * @see org.springframework.core.io.ResourceLoader
050 * @see FileEditor
051 * @see URLEditor
052 */
053public class PathEditor extends PropertyEditorSupport {
054
055        private final ResourceEditor resourceEditor;
056
057
058        /**
059         * Create a new PathEditor, using the default ResourceEditor underneath.
060         */
061        public PathEditor() {
062                this.resourceEditor = new ResourceEditor();
063        }
064
065        /**
066         * Create a new PathEditor, using the given ResourceEditor underneath.
067         * @param resourceEditor the ResourceEditor to use
068         */
069        public PathEditor(ResourceEditor resourceEditor) {
070                Assert.notNull(resourceEditor, "ResourceEditor must not be null");
071                this.resourceEditor = resourceEditor;
072        }
073
074
075        @Override
076        public void setAsText(String text) throws IllegalArgumentException {
077                boolean nioPathCandidate = !text.startsWith(ResourceLoader.CLASSPATH_URL_PREFIX);
078                if (nioPathCandidate && !text.startsWith("/")) {
079                        try {
080                                URI uri = new URI(text);
081                                if (uri.getScheme() != null) {
082                                        nioPathCandidate = false;
083                                        // Let's try NIO file system providers via Paths.get(URI)
084                                        setValue(Paths.get(uri).normalize());
085                                        return;
086                                }
087                        }
088                        catch (URISyntaxException | FileSystemNotFoundException ex) {
089                                // Not a valid URI (let's try as Spring resource location),
090                                // or a URI scheme not registered for NIO (let's try URL
091                                // protocol handlers via Spring's resource mechanism).
092                        }
093                }
094
095                this.resourceEditor.setAsText(text);
096                Resource resource = (Resource) this.resourceEditor.getValue();
097                if (resource == null) {
098                        setValue(null);
099                }
100                else if (!resource.exists() && nioPathCandidate) {
101                        setValue(Paths.get(text).normalize());
102                }
103                else {
104                        try {
105                                setValue(resource.getFile().toPath());
106                        }
107                        catch (IOException ex) {
108                                throw new IllegalArgumentException("Failed to retrieve file for " + resource, ex);
109                        }
110                }
111        }
112
113        @Override
114        public String getAsText() {
115                Path value = (Path) getValue();
116                return (value != null ? value.toString() : "");
117        }
118
119}