org.dataconservancy.packaging.impl.UriUtility Maven / Gradle / Ivy
/*
* Copyright 2016 Johns Hopkins University
*
* 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.dataconservancy.packaging.impl;
import java.io.File;
import java.net.MalformedURLException;
import java.net.URI;
import java.net.URISyntaxException;
import java.nio.file.Path;
import java.nio.file.Paths;
import org.apache.commons.io.FilenameUtils;
/**
* A utility class for doing URI-related work for verification and uniform File handling
*
* @author [email protected]
*/
public abstract class UriUtility {
private static final String ERR_RESOLVE_BAGURI = "Unable to resolve bag uri %s against base directory %s: ";
static String BAG_URI_SCHEME = "bag";
static String FILE_URI_SCHEME = "file";
/**
* Determine if a URL is a URL using HTTP or HTTPS protocols
*
* @param toCheck The URI to check
* @return true if the URI is a URL with a non-empty host and uses either the http or https protocol
*/
public static boolean isHttpUrl(final URI toCheck) {
return toCheck.getHost() != null &&
(toCheck.getScheme().equals("http") ||
toCheck.getScheme().equals("https"));
}
/**
* Determine if a string is a URL with HTTP or HTTPS protocols
*
* @param toCheck the string to check
* @return true if the string is a URL with http or https protocols
*/
public static boolean isHttpUrl(final String toCheck) {
try {
return isHttpUrl(new URI(toCheck));
} catch (final URISyntaxException e) {
return false;
}
}
/**
* Determine if a URI is resolvable. Currently this means the URI is a valid URL
*
* @param toCheck the URI to check
* @return true if resolvable, false if not
*/
public static boolean isResolvable(final URI toCheck) {
try {
toCheck.toURL();
} catch (final MalformedURLException e) {
return false;
}
return true;
}
/**
* Create a URI string for a file, ensuring that it has 3 slashes to meet File URL specifications
*
* @param file The file to check. This doesn't have to be an actual existing file
* @param basedir The directory to make the file URI relative to. Can be null. If not null, the basedir must be in
* the path of the file parameter, or an exception will be thrown
* @return A string representing the URI to the file on the local disk.
* @throws URISyntaxException if there is an error in the URI syntax
*/
public static URI makeFileUriString(final File file, final File basedir) throws URISyntaxException {
final File dir = basedir == null ? new File(".") : basedir;
Path relativePath = file.toPath();
if (relativePath.startsWith(dir.toPath())) {
relativePath = dir.toPath().relativize(file.toPath());
}
String path = FilenameUtils.separatorsToUnix(relativePath.toString());
// Remove leading slashes from the path
path = path.replaceFirst("^\\/*", "");
return new URI("file", null, "///" + path, null);
}
/**
* Create a URI string for a file in a BagIt bag,
*
* @param file The file to check. This doesn't have to be an actual existing file
* @param basedir The directory to make the file URI relative to. Can be null. If not null, the basedir must be in
* the path of the file parameter, or an exception will be thrown
* @return A string representing the URI to the file on the local disk.
* @throws URISyntaxException if there is an error in the URI syntax
*/
public static URI makeBagUriString(final File file, final File basedir) throws URISyntaxException {
final File dir = basedir == null ? new File(".") : basedir;
Path relativePath = file.toPath();
if (relativePath.startsWith(dir.toPath())) {
relativePath = dir.toPath().relativize(file.toPath());
}
String path = FilenameUtils.separatorsToUnix(relativePath.toString());
if (relativePath.getNameCount() > 1) {
final Path uriAuthority = relativePath.getName(0);
final Path uriPath = relativePath.subpath(1, relativePath.getNameCount());
path = FilenameUtils.separatorsToUnix(uriPath.toString());
if (!uriPath.isAbsolute()) {
path = "/" + path;
}
return new URI(BAG_URI_SCHEME, uriAuthority.toString(), path, null, null);
}
return new URI(BAG_URI_SCHEME, path, null, null, null);
}
/**
* Resolves the supplied {@code bag://} URI against a platform-specific base directory. This method is used to
* resolve resources in a bag to a platform-specific {@code Path} used by the caller to access the content of the
* resource.
*
* Example usage: Given a bag that contains a resource identified by the URI {@code bag://my-bag/data/bar}, and
* the bag has been exploded into the directory {@code /tmp/foo/my-bag} (where the bag payload directory is
* located at {@code /tmp/foo/my-bag/data}) then the base directory of the bag is {@code /tmp/foo}. If the caller
* wishes to resolve the URI {@code bag://my-bag/data/bar}, they would invoke this method:
*
*
* Path result = UriUtility.resolveBagUri(Paths.get("/tmp/foo"), new URI("bag://my-bag/data/bar"));
* assert Paths.get("/tmp/foo/my-bag/data/bar").equals(result);
*
*
* The base directory does not need to exist. This implementation will {@link Path#normalize() normalize} the
* supplied directory.
*
*
* The {@code bag://} URI is converted to a path by concatenating the authority portion of the URI with the path
* portion.
*
*
* If the supplied {@code bagUri} is not a URI with the {@code bag} scheme, an
* {@code IllegalArgumentException} is thrown.
*
*
* @param baseDir the base directory that contains the bag
* @param bagUri a URI identifying a resource in a bag
* @return a platform-specific {@code Path}, used to access the contents of the resource identified by
* {@code bagUri}
* @throws IllegalArgumentException if the supplied bagUri is null or empty, if {@code baseDir} is null, if
* {@code bagUri} does not have scheme {@code bag}
* @throws RuntimeException if the supplied base directory cannot be normalized
*/
public static Path resolveBagUri(final Path baseDir, final URI bagUri) {
if (bagUri == null) {
throw new IllegalArgumentException(
String.format(ERR_RESOLVE_BAGURI + "bag uri was null.", "null", baseDir));
}
if (!bagUri.getScheme().equals(BAG_URI_SCHEME)) {
throw new IllegalArgumentException(
String.format(ERR_RESOLVE_BAGURI + "bag uri had incorrect scheme.", bagUri, baseDir));
}
if (baseDir == null) {
throw new IllegalArgumentException(
String.format(ERR_RESOLVE_BAGURI + "base directory was null", bagUri, "null"));
}
// normalize the base directory path
final Path originalDir = baseDir;
final Path normalizedDir = baseDir.normalize();
if (normalizedDir == null) {
throw new RuntimeException(String.format(ERR_RESOLVE_BAGURI +
"failed to normalize the base directory.", bagUri, originalDir));
}
final Path bagPath = Paths.get(bagUri.getAuthority(), bagUri.getPath());
return normalizedDir.resolve(bagPath);
}
/**
* Returns true if the scheme for {@code uri} is equal to {@code bag}, otherwise it returns false.
*
* @param uri the uri
* @return true if the scheme of the URI equals {@code bag}
*/
public static boolean isBagUri(final URI uri) {
if (uri == null) {
return false;
}
return BAG_URI_SCHEME.equals(uri.getScheme());
}
}