UriUtils.java
/*
* SPDX-FileCopyrightText: none
* SPDX-License-Identifier: CC0-1.0
*/
package gov.nist.secauto.metaschema.core.util;
import java.net.URI;
import java.net.URISyntaxException;
import java.nio.file.InvalidPathException;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Arrays;
import java.util.Objects;
import java.util.regex.Pattern;
import edu.umd.cs.findbugs.annotations.NonNull;
public final class UriUtils {
private static final Pattern URI_SEPERATOR_PATTERN = Pattern.compile("\\/");
private static final String URI_SEPERATOR = "/";
private UriUtils() {
// disable construction
}
/**
* Process a string to a local file path or remote location. If the location is
* convertible to a URI, return the {@link URI}. Normalize the resulting URI
* with the base URI, if provided.
*
* @param location
* a string defining a remote or local file-based location
* @param baseUri
* the base URI to use for URI normalization
* @return a new URI
* @throws URISyntaxException
* if the location string is not convertible to URI
*/
@SuppressWarnings("PMD.PreserveStackTrace")
@NonNull
public static URI toUri(@NonNull String location, @NonNull URI baseUri) throws URISyntaxException {
URI asUri;
try {
asUri = new URI(location);
} catch (URISyntaxException ex) {
// the location is not a valid URI
try {
// try to parse the location as a local file path
Path path = Paths.get(location);
asUri = path.toUri();
} catch (InvalidPathException ex2) {
// not a local file path, so rethrow the original URI expection
throw ex;
}
}
return ObjectUtils.notNull(baseUri.resolve(asUri.normalize()));
}
/**
* This function extends the functionality of {@link URI#relativize(URI)} by
* supporting relative reference pathing (e.g., ..), when the {@code prepend}
* parameter is set to {@code true}.
*
* @param base
* the URI to relativize against
* @param other
* the URI to make relative
* @param prepend
* if {@code true}, then prepend relative pathing
* @return a new relative URI
* @throws URISyntaxException
* if any of the URIs are malformed
*/
public static URI relativize(URI base, URI other, boolean prepend) throws URISyntaxException {
URI normBase = Objects.requireNonNull(base).normalize();
URI normOther = Objects.requireNonNull(other).normalize();
URI retval = normBase.relativize(normOther);
if (prepend && !normBase.isOpaque() && !retval.isOpaque() && hasSameSchemeAndAuthority(normBase, retval)) {
// the URIs are not opaque and they share the same scheme and authority
String basePath = normBase.getPath();
String targetPath = normOther.getPath();
String newPath = prependRelativePath(basePath, targetPath);
retval = new URI(null, null, newPath, normOther.getQuery(), normOther.getFragment());
}
return retval;
}
private static boolean hasSameSchemeAndAuthority(URI base, URI other) {
String baseScheme = base.getScheme();
boolean retval = baseScheme == null && other.getScheme() == null
|| baseScheme != null && baseScheme.equals(other.getScheme());
String baseAuthority = base.getAuthority();
return retval && (baseAuthority == null && other.getAuthority() == null
|| baseAuthority != null && baseAuthority.equals(other.getAuthority()));
}
/**
* Get the path of the provided target relative to the path of the provided
* base.
*
* @param base
* the base path to resolve against
* @param target
* the URI to relativize against the base
* @return the relativized URI
*/
@SuppressWarnings("PMD.CyclomaticComplexity")
public static String prependRelativePath(String base, String target) {
// based on code from
// http://stackoverflow.com/questions/10801283/get-relative-path-of-two-uris-in-java
// Split paths into segments
String[] baseSegments = URI_SEPERATOR_PATTERN.split(base);
String[] targetSegments = URI_SEPERATOR_PATTERN.split(target, -1);
// Discard trailing segment of base path, since this resource doesn't matter
if (baseSegments.length > 0 && !base.endsWith(URI_SEPERATOR)) {
baseSegments = Arrays.copyOf(baseSegments, baseSegments.length - 1);
}
// Remove common prefix segments
int segmentIndex = 0;
while (segmentIndex < baseSegments.length && segmentIndex < targetSegments.length
&& baseSegments[segmentIndex].equals(targetSegments[segmentIndex])) {
segmentIndex++;
}
// Construct the relative path
StringBuilder retval = new StringBuilder();
for (int j = 0; j < baseSegments.length - segmentIndex; j++) {
retval.append("..");
if (retval.length() != 0) {
retval.append(URI_SEPERATOR);
}
}
for (int j = segmentIndex; j < targetSegments.length; j++) {
retval.append(targetSegments[j]);
if (retval.length() != 0 && j < targetSegments.length - 1) {
retval.append(URI_SEPERATOR);
}
}
return retval.toString();
}
}