com.alibaba.nacos.common.packagescan.resource.PathResource Maven / Gradle / Ivy
/*
* Copyright 2002-2019 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.alibaba.nacos.common.packagescan.resource;
import com.alibaba.nacos.common.packagescan.util.AbstractAssert;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.URI;
import java.net.URL;
import java.nio.channels.ReadableByteChannel;
import java.nio.channels.WritableByteChannel;
import java.nio.file.Files;
import java.nio.file.LinkOption;
import java.nio.file.NoSuchFileException;
import java.nio.file.OpenOption;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardOpenOption;
/**
* Copy from https://github.com/spring-projects/spring-framework.git, with less modifications
* {@link Resource} implementation for {@link Path} handles,
* performing all operations and transformations via the {@code Path} API.
* Supports resolution as a {@link File} and also as a {@link URL}.
* Implements the extended {@link WritableResource} interface.
*
* Note: As of 5.1, {@link Path} support is also available
* in {@link FileSystemResource#FileSystemResource(Path) FileSystemResource},
* applying Spring's standard String-based path transformations but
* performing all operations via the {@link Files} API.
* This {@code PathResource} is effectively a pure {@code java.nio.path.Path}
* based alternative with different {@code createRelative} behavior.
*
* @author Philippe Marschall
* @author Juergen Hoeller
* @see Path
* @see Files
* @see FileSystemResource
* @since 4.0
*/
public class PathResource extends AbstractResource implements WritableResource {
private final Path path;
/**
* Create a new PathResource from a Path handle.
*
*
Note: Unlike {@link FileSystemResource}, when building relative resources
* via {@link #createRelative}, the relative path will be built underneath
* the given root: e.g. Paths.get("C:/dir1/"), relative path "dir2" → "C:/dir1/dir2"!
*
* @param path a Path handle
*/
public PathResource(Path path) {
AbstractAssert.notNull(path, "Path must not be null");
this.path = path.normalize();
}
/**
* Create a new PathResource from a Path handle.
*
*
Note: Unlike {@link FileSystemResource}, when building relative resources
* via {@link #createRelative}, the relative path will be built underneath
* the given root: e.g. Paths.get("C:/dir1/"), relative path "dir2" → "C:/dir1/dir2"!
*
* @param path a path
* @see Paths#get(String, String...)
*/
public PathResource(String path) {
AbstractAssert.notNull(path, "Path must not be null");
this.path = Paths.get(path).normalize();
}
/**
* Create a new PathResource from a Path handle.
*
*
Note: Unlike {@link FileSystemResource}, when building relative resources
* via {@link #createRelative}, the relative path will be built underneath
* the given root: e.g. Paths.get("C:/dir1/"), relative path "dir2" → "C:/dir1/dir2"!
*
* @param uri a path URI
* @see Paths#get(URI)
*/
public PathResource(URI uri) {
AbstractAssert.notNull(uri, "URI must not be null");
this.path = Paths.get(uri).normalize();
}
/**
* Return the file path for this resource.
*/
public final String getPath() {
return this.path.toString();
}
/**
* This implementation returns whether the underlying file exists.
*
* @see Files#exists(Path, LinkOption...)
*/
@Override
public boolean exists() {
return Files.exists(this.path);
}
/**
* This implementation checks whether the underlying file is marked as readable
* (and corresponds to an actual file with content, not to a directory).
*
* @see Files#isReadable(Path)
* @see Files#isDirectory(Path, LinkOption...)
*/
@Override
public boolean isReadable() {
return (Files.isReadable(this.path) && !Files.isDirectory(this.path));
}
/**
* This implementation opens a InputStream for the underlying file.
*
* @see java.nio.file.spi.FileSystemProvider#newInputStream(Path, OpenOption...)
*/
@Override
public InputStream getInputStream() throws IOException {
if (!exists()) {
throw new FileNotFoundException(getPath() + " (no such file or directory)");
}
if (Files.isDirectory(this.path)) {
throw new FileNotFoundException(getPath() + " (is a directory)");
}
return Files.newInputStream(this.path);
}
/**
* This implementation checks whether the underlying file is marked as writable
* (and corresponds to an actual file with content, not to a directory).
*
* @see Files#isWritable(Path)
* @see Files#isDirectory(Path, LinkOption...)
*/
@Override
public boolean isWritable() {
return (Files.isWritable(this.path) && !Files.isDirectory(this.path));
}
/**
* This implementation opens a OutputStream for the underlying file.
*
* @see java.nio.file.spi.FileSystemProvider#newOutputStream(Path, OpenOption...)
*/
@Override
public OutputStream getOutputStream() throws IOException {
if (Files.isDirectory(this.path)) {
throw new FileNotFoundException(getPath() + " (is a directory)");
}
return Files.newOutputStream(this.path);
}
/**
* This implementation returns a URL for the underlying file.
*
* @see Path#toUri()
* @see URI#toURL()
*/
@Override
public URL getUrl() throws IOException {
return this.path.toUri().toURL();
}
/**
* This implementation returns a URI for the underlying file.
*
* @see Path#toUri()
*/
@Override
public URI getUri() throws IOException {
return this.path.toUri();
}
/**
* This implementation always indicates a file.
*/
@Override
public boolean isFile() {
return true;
}
/**
* This implementation returns the underlying File reference.
*/
@Override
public File getFile() throws IOException {
try {
return this.path.toFile();
} catch (UnsupportedOperationException ex) {
// Only paths on the default file system can be converted to a File:
// Do exception translation for cases where conversion is not possible.
throw new FileNotFoundException(this.path + " cannot be resolved to absolute file path");
}
}
/**
* This implementation opens a Channel for the underlying file.
*
* @see Files#newByteChannel(Path, OpenOption...)
*/
@Override
public ReadableByteChannel readableChannel() throws IOException {
try {
return Files.newByteChannel(this.path, StandardOpenOption.READ);
} catch (NoSuchFileException ex) {
throw new FileNotFoundException(ex.getMessage());
}
}
/**
* This implementation opens a Channel for the underlying file.
*
* @see Files#newByteChannel(Path, OpenOption...)
*/
@Override
public WritableByteChannel writableChannel() throws IOException {
return Files.newByteChannel(this.path, StandardOpenOption.WRITE);
}
/**
* This implementation returns the underlying file's length.
*/
@Override
public long contentLength() throws IOException {
return Files.size(this.path);
}
/**
* This implementation returns the underlying File's timestamp.
*
* @see Files#getLastModifiedTime(Path, LinkOption...)
*/
@Override
public long lastModified() throws IOException {
// We can not use the superclass method since it uses conversion to a File and
// only a Path on the default file system can be converted to a File...
return Files.getLastModifiedTime(this.path).toMillis();
}
/**
* This implementation creates a PathResource, applying the given path
* relative to the path of the underlying file of this resource descriptor.
*
* @see Path#resolve(String)
*/
@Override
public Resource createRelative(String relativePath) {
return new PathResource(this.path.resolve(relativePath));
}
/**
* This implementation returns the name of the file.
*
* @see Path#getFileName()
*/
@Override
public String getFilename() {
return this.path.getFileName().toString();
}
@Override
public String getDescription() {
return "path [" + this.path.toAbsolutePath() + "]";
}
/**
* This implementation compares the underlying Path references.
*/
@Override
public boolean equals(Object other) {
return (this == other || (other instanceof PathResource
&& this.path.equals(((PathResource) other).path)));
}
/**
* This implementation returns the hash code of the underlying Path reference.
*/
@Override
public int hashCode() {
return this.path.hashCode();
}
}