diff --git a/cdm/core/src/main/java/thredds/filesystem/MFileOS.java b/cdm/core/src/main/java/thredds/filesystem/MFileOS.java index 76ca060529..25e395ec5f 100644 --- a/cdm/core/src/main/java/thredds/filesystem/MFileOS.java +++ b/cdm/core/src/main/java/thredds/filesystem/MFileOS.java @@ -139,4 +139,9 @@ public void writeToStream(OutputStream outputStream, long offset, long maxBytes) public File getFile() { return file; } + + @Override + public MFileOS resolve(String newFilename) { + return new MFileOS(new File(file, newFilename)); + } } diff --git a/cdm/core/src/main/java/thredds/filesystem/MFileOS7.java b/cdm/core/src/main/java/thredds/filesystem/MFileOS7.java index e47e980341..2c8fdff625 100644 --- a/cdm/core/src/main/java/thredds/filesystem/MFileOS7.java +++ b/cdm/core/src/main/java/thredds/filesystem/MFileOS7.java @@ -141,6 +141,11 @@ public void writeToStream(OutputStream outputStream, long offset, long maxBytes) } } + @Override + public MFileOS7 resolve(String newFilename) { + throw new UnsupportedOperationException("MFileOS7::resolve not implemented. Filename: " + getName()); + } + public Path getNioPath() { return path; } diff --git a/cdm/core/src/main/java/thredds/inventory/CollectionManagerCatalog.java b/cdm/core/src/main/java/thredds/inventory/CollectionManagerCatalog.java index 9844c002df..d017f88bd9 100644 --- a/cdm/core/src/main/java/thredds/inventory/CollectionManagerCatalog.java +++ b/cdm/core/src/main/java/thredds/inventory/CollectionManagerCatalog.java @@ -168,6 +168,11 @@ public void writeToStream(OutputStream outputStream) { public void writeToStream(OutputStream outputStream, long offset, long maxBytes) { throw new UnsupportedOperationException("Writing MFileRemote not implemented. Filename: " + getName()); } + + @Override + public MFileRemote resolve(String newFilename) { + throw new UnsupportedOperationException("MFileRemote::resolve 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 e0d3fd7604..9bd5e6ccbe 100644 --- a/cdm/core/src/main/java/thredds/inventory/MFile.java +++ b/cdm/core/src/main/java/thredds/inventory/MFile.java @@ -9,6 +9,7 @@ import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; +import javax.annotation.Nullable; /** * An abstraction for java.io.File / java.nio.file.Path @@ -95,4 +96,13 @@ default boolean isReadable() { * @param maxBytes the maximum number of bytes to copy */ void writeToStream(OutputStream outputStream, long offset, long maxBytes) throws IOException; + + /** + * Resolve a new MFile in the same location as this MFile + * + * @param newFileName the relative file path of the new MFile + * @return the new MFile or null if the file can't be resolved + */ + @Nullable + MFile resolve(String newFileName); } diff --git a/cdm/core/src/test/java/thredds/filesystem/TestMFileOS.java b/cdm/core/src/test/java/thredds/filesystem/TestMFileOS.java index 28aa08106c..e7b7560940 100644 --- a/cdm/core/src/test/java/thredds/filesystem/TestMFileOS.java +++ b/cdm/core/src/test/java/thredds/filesystem/TestMFileOS.java @@ -90,6 +90,14 @@ public void shouldGetInputStream() throws IOException { assertThat(inputStream.read()).isNotEqualTo(-1); } } + + @Test + public void shouldResolveNewMFile() { + final MFileOS mFile = new MFileOS(tempFolder.getRoot() + "/testFile"); + final MFileOS newMFile = mFile.resolve("newFile"); + assertThat(newMFile.getName()).isEqualTo("newFile"); + assertThat(newMFile.getParent().getPath()).isEqualTo(mFile.getParent().getPath()); + } } private static File createTemporaryFile(int size) throws IOException { 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 df9730c88b..8de1fc9804 100644 --- a/cdm/s3/src/main/java/thredds/inventory/s3/MFileS3.java +++ b/cdm/s3/src/main/java/thredds/inventory/s3/MFileS3.java @@ -8,6 +8,7 @@ import java.io.OutputStream; import java.net.URISyntaxException; import java.util.Objects; +import java.util.Optional; import java.util.function.Supplier; import javax.annotation.Nullable; import org.slf4j.Logger; @@ -367,6 +368,18 @@ public void writeToStream(OutputStream outputStream, long offset, long maxBytes) } } + @Nullable + @Override + public MFileS3 resolve(String newFilename) { + final String newKey = cdmS3Uri.getKey().map(k -> k + newFilename).orElse(newFilename); + + try { + return new MFileS3(cdmS3Uri.resolveNewKey(newKey)); + } catch (URISyntaxException e) { + return null; + } + } + public static class Provider implements MFileProvider { private static String protocol = CdmS3Uri.SCHEME_CDM_S3; diff --git a/cdm/s3/src/test/java/thredds/inventory/s3/TestMFileS3.java b/cdm/s3/src/test/java/thredds/inventory/s3/TestMFileS3.java index 9d533c9242..a02567a56e 100644 --- a/cdm/s3/src/test/java/thredds/inventory/s3/TestMFileS3.java +++ b/cdm/s3/src/test/java/thredds/inventory/s3/TestMFileS3.java @@ -301,6 +301,42 @@ public void shouldReturnFalseForNonExistentBucket() throws IOException { assertThat(bucketWithoutDelimiter.exists()).isEqualTo(false); } + @Test + public void shouldResolveNewMFileFromBucket() throws IOException { + final MFileS3 withDelimiter = new MFileS3("cdms3:bucket" + DELIMITER_FRAGMENT); + final MFileS3 newMFileWithDelimiter = withDelimiter.resolve("newKey"); + assertThat(newMFileWithDelimiter).isNotNull(); + assertThat(newMFileWithDelimiter.getPath()).isEqualTo("cdms3:bucket?newKey" + DELIMITER_FRAGMENT); + + final MFileS3 withoutDelimiter = new MFileS3("cdms3:bucket"); + final MFileS3 newMFileWithoutDelimiter = withoutDelimiter.resolve("newKey"); + assertThat(newMFileWithoutDelimiter).isNotNull(); + assertThat(newMFileWithoutDelimiter.getPath()).isEqualTo("cdms3:bucket?newKey"); + } + + @Test + public void shouldResolveNewMFileFromBucketAndKey() throws IOException { + final MFileS3 withDelimiterWithSlash = new MFileS3("cdms3:bucket?key/" + DELIMITER_FRAGMENT); + final MFileS3 newMFileWithDelimiterWithSlash = withDelimiterWithSlash.resolve("newKey"); + assertThat(newMFileWithDelimiterWithSlash).isNotNull(); + assertThat(newMFileWithDelimiterWithSlash.getPath()).isEqualTo("cdms3:bucket?key/newKey" + DELIMITER_FRAGMENT); + + final MFileS3 withoutDelimiterWithSlash = new MFileS3("cdms3:bucket?key/"); + final MFileS3 newMFileWithoutDelimiterWithSlash = withoutDelimiterWithSlash.resolve("newKey"); + assertThat(newMFileWithoutDelimiterWithSlash).isNotNull(); + assertThat(newMFileWithoutDelimiterWithSlash.getPath()).isEqualTo("cdms3:bucket?key/newKey"); + + final MFileS3 withDelimiterWithoutSlash = new MFileS3("cdms3:bucket?key" + DELIMITER_FRAGMENT); + final MFileS3 newMFileWithDelimiterWithoutSlash = withDelimiterWithoutSlash.resolve("newKey"); + assertThat(newMFileWithDelimiterWithoutSlash).isNotNull(); + assertThat(newMFileWithDelimiterWithoutSlash.getPath()).isEqualTo("cdms3:bucket?keynewKey" + DELIMITER_FRAGMENT); + + final MFileS3 withoutDelimiterWithoutSlash = new MFileS3("cdms3:bucket?key"); + final MFileS3 newMFileWithoutDelimiterWithoutSlash = withoutDelimiterWithoutSlash.resolve("newKey"); + assertThat(newMFileWithoutDelimiterWithoutSlash).isNotNull(); + assertThat(newMFileWithoutDelimiterWithoutSlash.getPath()).isEqualTo("cdms3:bucket?keynewKey"); + } + @Test public void shouldGetInputStream() throws IOException { final MFile mFile = new MFileS3(AWS_G16_S3_OBJECT_1); 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 7a5835d41e..fa0df924db 100644 --- a/cdm/zarr/src/main/java/thredds/inventory/zarr/MFileZip.java +++ b/cdm/zarr/src/main/java/thredds/inventory/zarr/MFileZip.java @@ -174,6 +174,11 @@ public void writeToStream(OutputStream outputStream, long offset, long maxBytes) "Writing MFileZip with a byte range to stream not implemented. Filename: " + getName()); } + @Override + public MFileZip resolve(String newFilename) { + throw new UnsupportedOperationException("MFileZip::resolve 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 2c00962f54..235d81421d 100644 --- a/grib/src/main/java/ucar/nc2/grib/collection/GcMFile.java +++ b/grib/src/main/java/ucar/nc2/grib/collection/GcMFile.java @@ -136,4 +136,9 @@ public void writeToStream(OutputStream outputStream, long offset, long maxBytes) IO.copyRafB(randomAccessFile, offset, maxBytes, outputStream); } } + + @Override + public GcMFile resolve(String newFilename) { + throw new UnsupportedOperationException("GcMFile::resolve not implemented. Filename: " + getName()); + } }