diff --git a/cdm/core/src/main/java/thredds/filesystem/MFileOS.java b/cdm/core/src/main/java/thredds/filesystem/MFileOS.java index 094a02f6ec..5525e1b025 100644 --- a/cdm/core/src/main/java/thredds/filesystem/MFileOS.java +++ b/cdm/core/src/main/java/thredds/filesystem/MFileOS.java @@ -146,4 +146,12 @@ public File getFile() { public MFileOS getChild(String newFilename) { return new MFileOS(new File(file, newFilename)); } + + @Override + public String relativize(MFile other) { + if (other instanceof MFileOS) { + return file.toPath().relativize(((MFileOS) other).getFile().toPath()).toString(); + } + throw new IllegalArgumentException("Cannot relativize " + other + " against " + this); + } } diff --git a/cdm/core/src/main/java/thredds/filesystem/MFileOS7.java b/cdm/core/src/main/java/thredds/filesystem/MFileOS7.java index 5a04a5ca85..4bacc297b9 100644 --- a/cdm/core/src/main/java/thredds/filesystem/MFileOS7.java +++ b/cdm/core/src/main/java/thredds/filesystem/MFileOS7.java @@ -149,4 +149,12 @@ public MFileOS7 getChild(String newFilename) { public Path getNioPath() { return path; } + + @Override + public String relativize(MFile other) { + if (other instanceof MFileOS7) { + return path.relativize(((MFileOS7) other).path).toString(); + } + throw new IllegalArgumentException("Cannot relativize " + other + " against " + this); + } } diff --git a/cdm/core/src/main/java/thredds/inventory/CollectionManagerCatalog.java b/cdm/core/src/main/java/thredds/inventory/CollectionManagerCatalog.java index 70c408db6e..6e157f9d19 100644 --- a/cdm/core/src/main/java/thredds/inventory/CollectionManagerCatalog.java +++ b/cdm/core/src/main/java/thredds/inventory/CollectionManagerCatalog.java @@ -173,6 +173,11 @@ public void writeToStream(OutputStream outputStream, long offset, long maxBytes) public MFileRemote getChild(String newFilename) { throw new UnsupportedOperationException("MFileRemote::getChild not implemented. Filename: " + getName()); } + + @Override + public String relativize(MFile other) { + throw new UnsupportedOperationException("MFileRemote::relativize not implemented. Filename: " + getName()); + } } /////////////////////////////// diff --git a/cdm/core/src/main/java/thredds/inventory/MFile.java b/cdm/core/src/main/java/thredds/inventory/MFile.java index 38b73bc6cd..b84767ab3c 100644 --- a/cdm/core/src/main/java/thredds/inventory/MFile.java +++ b/cdm/core/src/main/java/thredds/inventory/MFile.java @@ -114,4 +114,12 @@ default boolean isReadable() { */ @Nullable MFile getChild(String newFileName); + + /** + * Construct a relative path between this MFile and a given MFile. + * + * @param other the MFile to relativize against this MFile's path + * @return the resulting relative path as a String, or an empty path if both paths are equal + */ + String relativize(MFile other); } diff --git a/cdm/s3/src/main/java/thredds/inventory/s3/MFileS3.java b/cdm/s3/src/main/java/thredds/inventory/s3/MFileS3.java index 48a26cab1e..d6f5e6ba07 100644 --- a/cdm/s3/src/main/java/thredds/inventory/s3/MFileS3.java +++ b/cdm/s3/src/main/java/thredds/inventory/s3/MFileS3.java @@ -7,6 +7,7 @@ import java.io.IOException; import java.io.OutputStream; import java.net.URISyntaxException; +import java.nio.file.Paths; import java.util.Objects; import java.util.function.Supplier; import javax.annotation.Nonnull; @@ -382,6 +383,31 @@ public MFileS3 getChild(String newFilename) { } } + /** + * Construct a relative key path (as if it were a file path) between this MFile and a given MFile. + * They must have same delimiter and bucket, otherwise the path returned is empty. + * + * @param other the MFile to relativize against this MFile's path + * @return the resulting relative path as a String, or an empty path if both paths are equal + */ + @Override + public String relativize(MFile other) { + if (!(other instanceof MFileS3)) { + throw new IllegalArgumentException("Cannot relativize " + other + " against " + this); + } + final MFileS3 otherS3 = (MFileS3) other; + + if (getDelimiter() != null && getDelimiter().equals("/") && getDelimiter().equals(otherS3.getDelimiter()) + && cdmS3Uri.getBucket().equals(otherS3.cdmS3Uri.getBucket())) { + final String key = getKey(); + final String otherKey = otherS3.getKey(); + return key == null || otherKey == null ? "" + : Paths.get("/" + key).relativize(Paths.get("/" + otherKey)).toString(); + } + + return ""; + } + public static class Provider implements MFileProvider { private static String protocol = CdmS3Uri.SCHEME_CDM_S3; diff --git a/cdm/zarr/src/main/java/thredds/inventory/zarr/MFileZip.java b/cdm/zarr/src/main/java/thredds/inventory/zarr/MFileZip.java index 061c10dad4..2b21ba7f25 100644 --- a/cdm/zarr/src/main/java/thredds/inventory/zarr/MFileZip.java +++ b/cdm/zarr/src/main/java/thredds/inventory/zarr/MFileZip.java @@ -184,6 +184,11 @@ public MFileZip getChild(String newFilename) { throw new UnsupportedOperationException("MFileZip::getChild not implemented. Filename: " + getName()); } + @Override + public String relativize(MFile other) { + throw new UnsupportedOperationException("MFileZip::relativize not implemented. Filename: " + getName()); + } + public Path getRootPath() { return rootPath; } diff --git a/grib/src/main/java/ucar/nc2/grib/collection/GcMFile.java b/grib/src/main/java/ucar/nc2/grib/collection/GcMFile.java index 0f7ffee20a..1976210268 100644 --- a/grib/src/main/java/ucar/nc2/grib/collection/GcMFile.java +++ b/grib/src/main/java/ucar/nc2/grib/collection/GcMFile.java @@ -141,4 +141,9 @@ public void writeToStream(OutputStream outputStream, long offset, long maxBytes) public GcMFile getChild(String newFilename) { throw new UnsupportedOperationException("GcMFile::getChild not implemented. Filename: " + getName()); } + + @Override + public String relativize(MFile other) { + throw new UnsupportedOperationException("GcMFile::relativize not implemented. Filename: " + getName()); + } }