diff --git a/cdm/core/src/main/java/thredds/filesystem/MFileOS.java b/cdm/core/src/main/java/thredds/filesystem/MFileOS.java index 76ca060529..a5739c28b1 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 getChild(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..5a04a5ca85 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 getChild(String newFilename) { + throw new UnsupportedOperationException("MFileOS7::getChild 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..70c408db6e 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 getChild(String newFilename) { + throw new UnsupportedOperationException("MFileRemote::getChild 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..d144f8cbf6 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; + + /** + * Get child MFile of 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 getChild(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..51524048fc 100644 --- a/cdm/core/src/test/java/thredds/filesystem/TestMFileOS.java +++ b/cdm/core/src/test/java/thredds/filesystem/TestMFileOS.java @@ -90,6 +90,15 @@ public void shouldGetInputStream() throws IOException { assertThat(inputStream.read()).isNotEqualTo(-1); } } + + @Test + public void shouldGetChildMFile() { + final MFileOS mFile = new MFileOS(tempFolder.getRoot() + "/testFile"); + final MFileOS newMFile = mFile.getChild("newFile"); + assertThat(newMFile.getName()).isEqualTo("newFile"); + assertThat(newMFile.getParent().getPath()).isEqualTo(mFile.getPath()); + assertThat(newMFile.getPath()).isEqualTo(tempFolder.getRoot() + "/testFile/newFile"); + } } 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 9f8cd0e843..df651bdb5c 100644 --- a/cdm/s3/src/main/java/thredds/inventory/s3/MFileS3.java +++ b/cdm/s3/src/main/java/thredds/inventory/s3/MFileS3.java @@ -368,6 +368,20 @@ public void writeToStream(OutputStream outputStream, long offset, long maxBytes) } } + @Nullable + @Override + public MFileS3 getChild(String newFilename) { + final String existingKey = cdmS3Uri.getKey().orElse(""); + final boolean addDelimiter = delimiter != null && !existingKey.endsWith(delimiter) && !existingKey.isEmpty(); + final String newKey = addDelimiter ? existingKey + delimiter + newFilename : existingKey + 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..cac3356b37 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 shouldGetChildMFileFromBucket() throws IOException { + final MFileS3 withDelimiter = new MFileS3("cdms3:bucket" + DELIMITER_FRAGMENT); + final MFileS3 newMFileWithDelimiter = withDelimiter.getChild("newKey"); + assertThat(newMFileWithDelimiter).isNotNull(); + assertThat(newMFileWithDelimiter.getPath()).isEqualTo("cdms3:bucket?newKey" + DELIMITER_FRAGMENT); + + final MFileS3 withoutDelimiter = new MFileS3("cdms3:bucket"); + final MFileS3 newMFileWithoutDelimiter = withoutDelimiter.getChild("newKey"); + assertThat(newMFileWithoutDelimiter).isNotNull(); + assertThat(newMFileWithoutDelimiter.getPath()).isEqualTo("cdms3:bucket?newKey"); + } + + @Test + public void shouldGetChildMFileFromBucketAndKey() throws IOException { + final MFileS3 withDelimiterWithSlash = new MFileS3("cdms3:bucket?key/" + DELIMITER_FRAGMENT); + final MFileS3 newMFileWithDelimiterWithSlash = withDelimiterWithSlash.getChild("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.getChild("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.getChild("newKey"); + assertThat(newMFileWithDelimiterWithoutSlash).isNotNull(); + assertThat(newMFileWithDelimiterWithoutSlash.getPath()).isEqualTo("cdms3:bucket?key/newKey" + DELIMITER_FRAGMENT); + + final MFileS3 withoutDelimiterWithoutSlash = new MFileS3("cdms3:bucket?key"); + final MFileS3 newMFileWithoutDelimiterWithoutSlash = withoutDelimiterWithoutSlash.getChild("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 8e12939507..5fee4a8d7e 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 getChild(String newFilename) { + throw new UnsupportedOperationException("MFileZip::getChild 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..0f7ffee20a 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 getChild(String newFilename) { + throw new UnsupportedOperationException("GcMFile::getChild not implemented. Filename: " + getName()); + } }