org.gradle.internal.resource.ExternalResourceName Maven / Gradle / Ivy
/*
* Copyright 2014 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
*
* http://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 org.gradle.internal.resource;
import com.google.common.base.Objects;
import org.gradle.api.Describable;
import org.gradle.internal.UncheckedException;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.ArrayList;
import java.util.List;
/**
* An immutable resource name. Resources are arranged in a hierarchy. Names may be relative, or absolute with some opaque root resource.
*/
public class ExternalResourceName implements Describable {
private final String encodedRoot;
private final String path;
public ExternalResourceName(URI uri) {
if (uri.getPath() == null) {
throw new IllegalArgumentException(String.format("Cannot create resource name from non-hierarchical URI '%s'.", uri.toString()));
}
this.encodedRoot = encodeRoot(uri);
this.path = extractPath(uri);
}
public ExternalResourceName(String path) {
encodedRoot = null;
this.path = path;
}
private ExternalResourceName(String encodedRoot, String path) {
this.encodedRoot = encodedRoot;
this.path = path;
}
public ExternalResourceName(URI parent, String path) {
if (parent.getPath() == null) {
throw new IllegalArgumentException(String.format("Cannot create resource name from non-hierarchical URI '%s'.", parent.toString()));
}
String newPath;
String parentPath = extractPath(parent);
if (path.startsWith("/")) {
path = path.substring(1);
}
if (path.length() == 0) {
newPath = parentPath;
} else if (parentPath.endsWith("/")) {
newPath = parentPath + path;
} else {
newPath = parentPath + "/" + path;
}
this.encodedRoot = encodeRoot(parent);
this.path = newPath;
}
private boolean isFileOnHost(URI uri) {
return "file".equals(uri.getScheme()) && uri.getPath().startsWith("//");
}
private String extractPath(URI parent) {
if (isFileOnHost(parent)) {
return URI.create(parent.getPath()).getPath();
}
return parent.getPath();
}
private String encodeRoot(URI uri) {
StringBuilder builder = new StringBuilder();
if (uri.getScheme() != null) {
builder.append(uri.getScheme());
builder.append(":");
if (isFileOnHost(uri)) {
String hostName = URI.create(uri.getPath()).getHost();
builder.append("////");
builder.append(hostName);
}
}
if (uri.getHost() != null) {
builder.append("//");
builder.append(uri.getHost());
}
if (uri.getPort() > 0) {
builder.append(":");
builder.append(uri.getPort());
}
return builder.toString();
}
public String getDisplayName() {
return getDecoded();
}
public String getShortDisplayName() {
int lastSlash = path.lastIndexOf('/');
return lastSlash == -1 ? getDecoded() : path.substring(lastSlash + 1);
}
@Override
public String toString() {
return getDisplayName();
}
/**
* Returns a URI that represents this resource.
*/
public URI getUri() {
try {
if (encodedRoot == null) {
return new URI(encode(path, false));
}
return new URI(encodedRoot + encode(path, true));
} catch (URISyntaxException e) {
throw UncheckedException.throwAsUncheckedException(e);
}
}
private String encode(String path, boolean isPathSeg) {
StringBuilder builder = new StringBuilder();
for (int i = 0; i < path.length(); i++) {
char ch = path.charAt(i);
if (ch >= 'a' && ch <= 'z' || ch >= 'A' && ch <= 'Z' || ch >= '0' && ch <= '9') {
builder.append(ch);
} else if (ch == '/' || ch == '@' || isPathSeg && ch == ':' || ch == '.' || ch == '-' || ch == '_' || ch == '~'
|| ch == '!' || ch == '$' || ch == '&' || ch == '\'' || ch == '(' || ch == ')' || ch == '*' || ch == '+'
|| ch == ',' || ch == ';' || ch == '=') {
builder.append(ch);
} else {
if (ch <= 0x7F) {
escapeByte(ch, builder);
} else if (ch <= 0x7FF) {
escapeByte(0xC0 | (ch >> 6) & 0x1F, builder);
escapeByte(0x80 | ch & 0x3F, builder);
} else {
escapeByte(0xE0 | (ch >> 12) & 0x1F, builder);
escapeByte(0x80 | (ch >> 6) & 0x3F, builder);
escapeByte(0x80 | ch & 0x3F, builder);
}
}
}
return builder.toString();
}
private void escapeByte(int ch, StringBuilder builder) {
builder.append('%');
builder.append(Character.toUpperCase(Character.forDigit(ch >> 4 & 0xFF, 16)));
builder.append(Character.toUpperCase(Character.forDigit(ch & 0xF, 16)));
}
/**
* Returns the 'decoded' name, which is the opaque root + the path of the name.
*/
public String getDecoded() {
if (encodedRoot == null) {
return path;
}
return encodedRoot + path;
}
/**
* Returns the root name for this name.
*/
public ExternalResourceName getRoot() {
return new ExternalResourceName(encodedRoot, path.startsWith("/") ? "/" : "");
}
/**
* Returns the path for this resource. The '/' character is used to separate the elements of the path.
*/
public String getPath() {
return path;
}
/**
* Resolves the given path relative to this name. The path can be a relative path or an absolute path. The '/' character is used to separate the elements of the path.
*/
public ExternalResourceName resolve(String path) {
List parts = new ArrayList();
boolean leadingSlash;
boolean trailingSlash = path.endsWith("/");
if (path.startsWith("/")) {
leadingSlash = true;
append(path, parts);
} else {
leadingSlash = this.path.startsWith("/");
append(this.path, parts);
append(path, parts);
}
String newPath = join(leadingSlash, trailingSlash, parts);
return new ExternalResourceName(encodedRoot, newPath);
}
private String join(boolean leadingSlash, boolean trailingSlash, List parts) {
if (parts.isEmpty() && leadingSlash) {
return "/";
}
StringBuilder builder = new StringBuilder();
for (String part : parts) {
if (builder.length() > 0 || leadingSlash) {
builder.append("/");
}
builder.append(part);
}
if (trailingSlash) {
builder.append("/");
}
return builder.toString();
}
private void append(String path, List parts) {
for (int pos = 0; pos < path.length();) {
int end = path.indexOf('/', pos);
String part;
if (end < 0) {
part = path.substring(pos);
pos = path.length();
} else {
part = path.substring(pos, end);
pos = end + 1;
}
if (part.length() == 0 || part.equals(".")) {
continue;
}
if (part.equals("..")) {
parts.remove(parts.size() - 1);
continue;
}
parts.add(part);
}
}
/**
* Appends the given text to the end of this path.
*/
public ExternalResourceName append(String path) {
return new ExternalResourceName(encodedRoot, this.path + path);
}
@Override
public boolean equals(Object obj) {
if (obj == this) {
return true;
}
if (obj == null || !obj.getClass().equals(getClass())) {
return false;
}
ExternalResourceName other = (ExternalResourceName) obj;
return Objects.equal(encodedRoot, other.encodedRoot) && path.equals(other.path);
}
@Override
public int hashCode() {
return (encodedRoot == null ? 0 : encodedRoot.hashCode()) ^ path.hashCode();
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy