diff --git a/src/main/java/dev/zarr/zarrjava/core/Group.java b/src/main/java/dev/zarr/zarrjava/core/Group.java index b7cc83d..d8b9a6b 100644 --- a/src/main/java/dev/zarr/zarrjava/core/Group.java +++ b/src/main/java/dev/zarr/zarrjava/core/Group.java @@ -64,14 +64,14 @@ public static Group open(String path) throws IOException, ZarrException { } @Nullable - public abstract Node get(String key) throws ZarrException; + public abstract Node get(String key) throws ZarrException, IOException; public Stream list() { return storeHandle.list() .map(key -> { try { return get(key); - } catch (ZarrException e) { + } catch (Exception e) { throw new RuntimeException(e); } }) diff --git a/src/main/java/dev/zarr/zarrjava/store/MemoryStore.java b/src/main/java/dev/zarr/zarrjava/store/MemoryStore.java new file mode 100644 index 0000000..c1bbb9d --- /dev/null +++ b/src/main/java/dev/zarr/zarrjava/store/MemoryStore.java @@ -0,0 +1,88 @@ +package dev.zarr.zarrjava.store; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; +import java.nio.ByteBuffer; +import java.util.*; +import java.util.concurrent.ConcurrentHashMap; +import java.util.stream.Stream; + +public class MemoryStore implements Store, Store.ListableStore { + private final Map, byte[]> map = new ConcurrentHashMap<>(); + + List resolveKeys(String[] keys) { + ArrayList resolvedKeys = new ArrayList<>(); + for(String key:keys){ + if(key.startsWith("/")){ + key = key.substring(1); + } + resolvedKeys.addAll(Arrays.asList(key.split("/"))); + } + return resolvedKeys; + } + + @Override + public boolean exists(String[] keys) { + return map.containsKey(resolveKeys(keys)); + } + + @Nullable + @Override + public ByteBuffer get(String[] keys) { + return get(keys, 0); + } + + @Nullable + @Override + public ByteBuffer get(String[] keys, long start) { + return get(keys, start, -1); + } + + @Nullable + @Override + public ByteBuffer get(String[] keys, long start, long end) { + byte[] bytes = map.get(resolveKeys(keys)); + if (bytes == null) return null; + if (end < 0) end = bytes.length; + if (end > Integer.MAX_VALUE) throw new IllegalArgumentException("End index too large"); + return ByteBuffer.wrap(bytes, (int) start, (int) end); + } + + + @Override + public void set(String[] keys, ByteBuffer bytes) { + map.put(resolveKeys(keys), bytes.array()); + } + + @Override + public void delete(String[] keys) { + map.remove(resolveKeys(keys)); + } + + public Stream list(String[] keys) { + List prefix = resolveKeys(keys); + Set allKeys = new HashSet<>(); + + for (List k : map.keySet()) { + if (k.size() <= prefix.size() || ! k.subList(0, prefix.size()).equals(prefix)) + continue; + for (int i = 0; i < k.size(); i++) { + List subKey = k.subList(0, i+1); + allKeys.add(String.join("/", subKey)); + } + } + return allKeys.stream(); + } + + @Nonnull + @Override + public StoreHandle resolve(String... keys) { + return new StoreHandle(this, keys); + } + + @Override + public String toString() { + return String.format("", hashCode()); + } +} + diff --git a/src/main/java/dev/zarr/zarrjava/v2/Array.java b/src/main/java/dev/zarr/zarrjava/v2/Array.java index e66c20e..421dbc6 100644 --- a/src/main/java/dev/zarr/zarrjava/v2/Array.java +++ b/src/main/java/dev/zarr/zarrjava/v2/Array.java @@ -5,6 +5,7 @@ import dev.zarr.zarrjava.ZarrException; import dev.zarr.zarrjava.core.Attributes; import dev.zarr.zarrjava.store.FilesystemStore; +import dev.zarr.zarrjava.store.MemoryStore; import dev.zarr.zarrjava.store.StoreHandle; import dev.zarr.zarrjava.utils.Utils; import dev.zarr.zarrjava.core.codec.CodecPipeline; @@ -87,6 +88,16 @@ public static Array open(String path) throws IOException, ZarrException { return open(Paths.get(path)); } + /** + * Creates a new Zarr array with the provided metadata in an in-memory store + * + * @param arrayMetadata the metadata of the Zarr array + * @throws IOException if the metadata cannot be serialized + * @throws ZarrException if the Zarr array cannot be created + */ + public static Array create(ArrayMetadata arrayMetadata) throws ZarrException, IOException { + return create(new StoreHandle(new MemoryStore()), arrayMetadata); + } /** * Creates a new Zarr array with the provided metadata at a specified storage location. This * method will raise an exception if a Zarr array already exists at the specified storage diff --git a/src/main/java/dev/zarr/zarrjava/v2/Group.java b/src/main/java/dev/zarr/zarrjava/v2/Group.java index 701e787..c568294 100644 --- a/src/main/java/dev/zarr/zarrjava/v2/Group.java +++ b/src/main/java/dev/zarr/zarrjava/v2/Group.java @@ -5,6 +5,7 @@ import dev.zarr.zarrjava.ZarrException; import dev.zarr.zarrjava.core.Attributes; import dev.zarr.zarrjava.store.FilesystemStore; +import dev.zarr.zarrjava.store.MemoryStore; import dev.zarr.zarrjava.store.StoreHandle; import dev.zarr.zarrjava.utils.Utils; @@ -12,6 +13,7 @@ import javax.annotation.Nullable; import java.io.IOException; import java.nio.ByteBuffer; +import java.nio.file.NoSuchFileException; import java.nio.file.Path; import java.nio.file.Paths; import java.util.function.Function; @@ -26,6 +28,12 @@ protected Group(@Nonnull StoreHandle storeHandle, @Nonnull GroupMetadata groupMe this.metadata = groupMetadata; } + /** + * Opens an existing Zarr group at a specified storage location. + * + * @param storeHandle the storage location of the Zarr group + * @throws IOException if the metadata cannot be read + */ public static Group open(@Nonnull StoreHandle storeHandle) throws IOException { ObjectMapper mapper = makeObjectMapper(); GroupMetadata metadata = mapper.readValue( @@ -40,77 +48,221 @@ public static Group open(@Nonnull StoreHandle storeHandle) throws IOException { return new Group(storeHandle, metadata); } + /** + * Opens an existing Zarr group at a specified storage location. + * + * @param path the storage location of the Zarr group + * @throws IOException if the metadata cannot be read + */ public static Group open(Path path) throws IOException { return open(new StoreHandle(new FilesystemStore(path))); } - public static Group open(String path) throws IOException { + /** + * Opens an existing Zarr group at a specified storage location. + * + * @param path the storage location of the Zarr group + * @throws IOException if the metadata cannot be read + */ + public static Group open(String path) throws IOException { return open(Paths.get(path)); } + /** + * Creates a new Zarr group with the provided metadata in an in-memory store. + * + * @param groupMetadata the metadata of the Zarr group + * @throws IOException if the metadata cannot be serialized + */ + public static Group create(@Nonnull GroupMetadata groupMetadata) throws IOException { + return new Group(new MemoryStore().resolve(), groupMetadata).writeMetadata(); + } + + /** + * Creates a new Zarr group with the provided metadata at a specified storage location. + * + * @param storeHandle the storage location of the Zarr group + * @param groupMetadata the metadata of the Zarr group + * @throws IOException if the metadata cannot be serialized + */ public static Group create( @Nonnull StoreHandle storeHandle, @Nonnull GroupMetadata groupMetadata ) throws IOException { - ObjectWriter objectWriter = makeObjectWriter(); - ByteBuffer metadataBytes = ByteBuffer.wrap(objectWriter.writeValueAsBytes(groupMetadata)); - storeHandle.resolve(ZGROUP).set(metadataBytes); - if (groupMetadata.attributes != null) { - StoreHandle attrsHandle = storeHandle.resolve(ZATTRS); - ByteBuffer attrsBytes = ByteBuffer.wrap( - objectWriter.writeValueAsBytes(groupMetadata.attributes)); - attrsHandle.set(attrsBytes); - } - return new Group(storeHandle, groupMetadata); + return new Group(storeHandle, groupMetadata).writeMetadata(); } + /** + * Creates a new Zarr group with default metadata at a specified storage location. + * + * @param storeHandle the storage location of the Zarr group + * @throws IOException if the metadata cannot be serialized + * @throws ZarrException if the metadata is invalid + */ public static Group create(@Nonnull StoreHandle storeHandle) throws IOException, ZarrException { return create(storeHandle, new GroupMetadata()); } + /** + * Creates a new Zarr group with the provided attributes at a specified storage location. + * + * @param storeHandle the storage location of the Zarr group + * @param attributes the attributes of the Zarr group + * @throws IOException if the metadata cannot be serialized + * @throws ZarrException if the attributes are invalid + */ public static Group create(@Nonnull StoreHandle storeHandle, Attributes attributes) throws IOException, ZarrException { return create(storeHandle, new GroupMetadata(attributes)); } + /** + * Creates a new Zarr group with default metadata at a specified storage location. + * + * @param path the storage location of the Zarr group + * @throws IOException if the metadata cannot be serialized + * @throws ZarrException if the metadata is invalid + */ public static Group create(Path path) throws IOException, ZarrException { return create(new StoreHandle(new FilesystemStore(path))); } + /** + * Creates a new Zarr group with the provided attributes at a specified storage location. + * + * @param path the storage location of the Zarr group + * @param attributes the attributes of the Zarr group + * @throws IOException if the metadata cannot be serialized + * @throws ZarrException if the attributes are invalid + */ public static Group create(Path path, Attributes attributes) throws IOException, ZarrException { return create(new StoreHandle(new FilesystemStore(path)), attributes); } + /** + * Creates a new Zarr group with default metadata at a specified storage location. + * + * @param path the storage location of the Zarr group + * @throws IOException if the metadata cannot be serialized + * @throws ZarrException if the metadata is invalid + */ public static Group create(String path) throws IOException, ZarrException { return create(Paths.get(path)); } + /** + * Creates a new Zarr group with the provided attributes at a specified storage location. + * + * @param path the storage location of the Zarr group + * @param attributes the attributes of the Zarr group + * @throws IOException if the metadata cannot be serialized + * @throws ZarrException if the attributes are invalid + */ public static Group create(String path, Attributes attributes) throws IOException, ZarrException { return create(Paths.get(path), attributes); } + /** + * Retrieves a node (group or array) at the specified key within the current group. + * + * @param key the key of the node to retrieve + * @return the node at the specified key, or null if it does not exist + * @throws ZarrException if the node cannot be opened + * @throws IOException if there is an error accessing the storage + */ @Nullable - public Node get(String key) throws ZarrException { + public Node get(String key) throws ZarrException, IOException { StoreHandle keyHandle = storeHandle.resolve(key); try { return Node.open(keyHandle); - } catch (IOException e) { + } catch (NoSuchFileException e) { return null; } } + /** + * Creates a new subgroup with default metadata at the specified key. + * + * @param key the key of the new Zarr group within the current group + * @return the created subgroup + * @throws IOException if the metadata cannot be serialized + * @throws ZarrException if the group cannot be created + */ public Group createGroup(String key) throws IOException, ZarrException { return Group.create(storeHandle.resolve(key)); } + /** + * Creates a new array with the provided metadata at the specified key. + * + * @param key the key of the new Zarr array within the current group + * @param arrayMetadata the metadata of the Zarr array + * @return the created array + * @throws IOException if the metadata cannot be serialized + * @throws ZarrException if the array cannot be created + */ public Array createArray(String key, ArrayMetadata arrayMetadata) throws IOException, ZarrException { return Array.create(storeHandle.resolve(key), arrayMetadata); } + /** + * Creates a new array with the provided metadata at the specified key. + * + * @param key the key of the new Zarr array within the current group + * @param arrayMetadataBuilderMapper a function that modifies the array metadata + * @return the created array + * @throws IOException if the metadata cannot be serialized + * @throws ZarrException if the array cannot be created + */ public Array createArray(String key, Function arrayMetadataBuilderMapper) - throws IOException, ZarrException { + throws IOException, ZarrException { return Array.create(storeHandle.resolve(key), arrayMetadataBuilderMapper, false); } + private Group writeMetadata() throws IOException { + return writeMetadata(this.metadata); + } + + private Group writeMetadata(GroupMetadata newGroupMetadata) throws IOException { + ObjectWriter objectWriter = makeObjectWriter(); + ByteBuffer metadataBytes = ByteBuffer.wrap(objectWriter.writeValueAsBytes(newGroupMetadata)); + storeHandle.resolve(ZGROUP).set(metadataBytes); + if (newGroupMetadata.attributes != null) { + StoreHandle attrsHandle = storeHandle.resolve(ZATTRS); + ByteBuffer attrsBytes = ByteBuffer.wrap( + objectWriter.writeValueAsBytes(newGroupMetadata.attributes)); + attrsHandle.set(attrsBytes); + } + this.metadata = newGroupMetadata; + return this; + } + + /** + * Sets new attributes for the group, replacing any existing attributes. + * + * @param newAttributes the new attributes to set + * @return the updated group + * @throws ZarrException if the new attributes are invalid + * @throws IOException if the metadata cannot be serialized + */ + public Group setAttributes(Attributes newAttributes) throws ZarrException, IOException { + GroupMetadata newGroupMetadata = new GroupMetadata(newAttributes); + return writeMetadata(newGroupMetadata); + } + + /** + * Updates the attributes of the group using a mapper function. + * + * @param attributeMapper a function that takes the current attributes and returns the updated attributes + * @return the updated group + * @throws ZarrException if the new attributes are invalid + * @throws IOException if the metadata cannot be serialized + */ + public Group updateAttributes(Function attributeMapper) + throws ZarrException, IOException { + return setAttributes(attributeMapper.apply(metadata.attributes)); + } + + @Override public String toString() { return String.format("", storeHandle); diff --git a/src/main/java/dev/zarr/zarrjava/v3/Array.java b/src/main/java/dev/zarr/zarrjava/v3/Array.java index 1536893..8e263a1 100644 --- a/src/main/java/dev/zarr/zarrjava/v3/Array.java +++ b/src/main/java/dev/zarr/zarrjava/v3/Array.java @@ -4,6 +4,7 @@ import dev.zarr.zarrjava.ZarrException; import dev.zarr.zarrjava.core.Attributes; import dev.zarr.zarrjava.store.FilesystemStore; +import dev.zarr.zarrjava.store.MemoryStore; import dev.zarr.zarrjava.store.StoreHandle; import dev.zarr.zarrjava.utils.Utils; import dev.zarr.zarrjava.core.codec.CodecPipeline; @@ -72,6 +73,18 @@ public static Array open(String path) throws IOException, ZarrException { return open(Paths.get(path)); } + /** + * Creates a new Zarr array with the provided metadata in an in-memory store + * + * @param arrayMetadata the metadata of the Zarr array + * @throws IOException if the metadata cannot be serialized + * @throws ZarrException if the Zarr array cannot be created + */ + public static Array create(ArrayMetadata arrayMetadata) + throws IOException, ZarrException { + return Array.create(new MemoryStore().resolve(), arrayMetadata); + } + /** * Creates a new Zarr array with the provided metadata at a specified storage location. This * method will raise an exception if a Zarr array already exists at the specified storage diff --git a/src/main/java/dev/zarr/zarrjava/v3/Group.java b/src/main/java/dev/zarr/zarrjava/v3/Group.java index 62a36c8..d17eb77 100644 --- a/src/main/java/dev/zarr/zarrjava/v3/Group.java +++ b/src/main/java/dev/zarr/zarrjava/v3/Group.java @@ -4,10 +4,12 @@ import dev.zarr.zarrjava.ZarrException; import dev.zarr.zarrjava.core.Attributes; import dev.zarr.zarrjava.store.FilesystemStore; +import dev.zarr.zarrjava.store.MemoryStore; import dev.zarr.zarrjava.store.StoreHandle; import dev.zarr.zarrjava.utils.Utils; import java.io.IOException; import java.nio.ByteBuffer; +import java.nio.file.NoSuchFileException; import java.nio.file.Path; import java.nio.file.Paths; import java.util.function.Function; @@ -26,32 +28,88 @@ protected Group(@Nonnull StoreHandle storeHandle, @Nonnull GroupMetadata groupMe this.metadata = groupMetadata; } + /** + * Opens an existing Zarr group at a specified storage location. + * + * @param storeHandle the storage location of the Zarr group + * @throws IOException if the metadata cannot be read + */ public static Group open(@Nonnull StoreHandle storeHandle) throws IOException { StoreHandle metadataHandle = storeHandle.resolve(ZARR_JSON); ByteBuffer metadataBytes = metadataHandle.readNonNull(); - return new Group(storeHandle, makeObjectMapper() - .readValue(Utils.toArray(metadataBytes), GroupMetadata.class)); + return new Group(storeHandle, makeObjectMapper().readValue(Utils.toArray(metadataBytes), GroupMetadata.class)); } - + + /** + * Opens an existing Zarr group at a specified storage location. + * + * @param path the storage location of the Zarr group + * @throws IOException if the metadata cannot be read + */ public static Group open(Path path) throws IOException { - return open(new StoreHandle(new FilesystemStore(path))); - } + return open(new StoreHandle(new FilesystemStore(path))); + } - public static Group open(String path) throws IOException { - return open(Paths.get(path)); - } + /** + * Opens an existing Zarr group at a specified storage location. + * + * @param path the storage location of the Zarr group + * @throws IOException if the metadata cannot be read + */ + public static Group open(String path) throws IOException { + return open(Paths.get(path)); + } - public static Group create( - @Nonnull StoreHandle storeHandle, @Nonnull GroupMetadata groupMetadata - ) throws IOException { - ObjectWriter objectWriter = makeObjectWriter(); - ByteBuffer metadataBytes = ByteBuffer.wrap(objectWriter.writeValueAsBytes(groupMetadata)); - storeHandle.resolve(ZARR_JSON) - .set(metadataBytes); - return new Group(storeHandle, groupMetadata); + /** + * Creates a new Zarr group with default metadata in an in-memory store. + * + * @throws IOException if the metadata cannot be serialized + */ + public static Group create() throws IOException { + return new Group(new MemoryStore().resolve(), GroupMetadata.defaultValue()).writeMetadata(); + } + + /** + * Creates a new Zarr group with the provided attributes in an in-memory store. + * + * @param attributes the attributes of the Zarr group + * @throws IOException if the metadata cannot be serialized + * @throws ZarrException if the attributes are invalid + */ + public static Group create(@Nonnull Attributes attributes) throws IOException, ZarrException { + return new Group(new MemoryStore().resolve(), new GroupMetadata(attributes)).writeMetadata(); + } + + /** + * Creates a new Zarr group with the provided metadata in an in-memory store. + * + * @param groupMetadata the metadata of the Zarr group + * @throws IOException if the metadata cannot be serialized + */ + public static Group create(@Nonnull GroupMetadata groupMetadata) throws IOException { + return new Group(new MemoryStore().resolve(), groupMetadata).writeMetadata(); + } + + /** + * Creates a new Zarr group with the provided metadata at a specified storage location. + * + * @param storeHandle the storage location of the Zarr group + * @param groupMetadata the metadata of the Zarr group + * @throws IOException if the metadata cannot be serialized + */ + public static Group create(@Nonnull StoreHandle storeHandle, @Nonnull GroupMetadata groupMetadata) throws IOException { + return new Group(storeHandle, groupMetadata).writeMetadata(); } + /** + * Creates a new Zarr group with the provided attributes at a specified storage location. + * + * @param storeHandle the storage location of the Zarr group + * @param attributes the attributes of the Zarr group + * @throws IOException if the metadata cannot be serialized + * @throws ZarrException if the attributes are invalid + */ public static Group create( @Nonnull StoreHandle storeHandle, @Nonnull Attributes attributes @@ -59,79 +117,181 @@ public static Group create( return create(storeHandle, new GroupMetadata(attributes)); } - public static Group create(@Nonnull StoreHandle storeHandle) throws IOException, ZarrException { + /** + * Creates a new Zarr group with default metadata at a specified storage location. + * + * @param storeHandle the storage location of the Zarr group + * @throws IOException if the metadata cannot be serialized + */ + public static Group create(@Nonnull StoreHandle storeHandle) throws IOException { return create(storeHandle, GroupMetadata.defaultValue()); } + /** + * Creates a new Zarr group with the provided metadata at a specified storage location. + * + * @param path the storage location of the Zarr group + * @param groupMetadata the metadata of the Zarr group + * @throws IOException if the metadata cannot be serialized + * @throws ZarrException if the metadata is invalid + */ public static Group create(Path path, GroupMetadata groupMetadata) throws IOException, ZarrException { return create(new FilesystemStore(path).resolve(), groupMetadata); } + /** + * Creates a new Zarr group with the provided metadata at a specified storage location. + * + * @param path the storage location of the Zarr group + * @param groupMetadata the metadata of the Zarr group + * @throws IOException if the metadata cannot be serialized + * @throws ZarrException if the metadata is invalid + */ public static Group create(String path, GroupMetadata groupMetadata) throws IOException, ZarrException { return create(Paths.get(path), groupMetadata); } + /** + * Creates a new Zarr group with default metadata at a specified storage location. + * + * @param path the storage location of the Zarr group + * @throws IOException if the metadata cannot be serialized + * @throws ZarrException if the metadata is invalid + */ public static Group create(Path path) throws IOException, ZarrException { return create(new FilesystemStore(path).resolve()); } + /** + * Creates a new Zarr group with default metadata at a specified storage location. + * + * @param path the storage location of the Zarr group + * @throws IOException if the metadata cannot be serialized + * @throws ZarrException if the metadata is invalid + */ public static Group create(String path) throws IOException, ZarrException { return create(Paths.get(path)); } + /** + * Retrieves a node (group or array) at the specified key within the current group. + * + * @param key the key of the node to retrieve + * @return the node at the specified key, or null if it does not exist + * @throws ZarrException if the node cannot be opened + * @throws IOException if there is an error accessing the storage + */ @Nullable - public Node get(String key) throws ZarrException { + public Node get(String key) throws ZarrException, IOException{ StoreHandle keyHandle = storeHandle.resolve(key); try { return Node.open(keyHandle); - } catch (IOException e) { + } catch (NoSuchFileException e) { return null; } } + /** + * Creates a new subgroup with the provided metadata at the specified key. + * + * @param key the key of the new Zarr group within the current group + * @param groupMetadata the metadata of the Zarr group + * @throws IOException if the metadata cannot be serialized + */ public Group createGroup(String key, GroupMetadata groupMetadata) - throws IOException, ZarrException { + throws IOException, ZarrException { return Group.create(storeHandle.resolve(key), groupMetadata); } + /** + * Creates a new subgroup with the provided attributes at the specified key. + * + * @param key the key of the new Zarr group within the current group + * @param attributes attributes of the Zarr group + * @throws IOException if the metadata cannot be serialized + */ public Group createGroup(String key, Attributes attributes) - throws IOException, ZarrException { + throws IOException, ZarrException { return Group.create(storeHandle.resolve(key), new GroupMetadata(attributes)); } - public Group createGroup(String key) throws IOException, ZarrException { + /** + * Creates a new subgroup with default metadata at the specified key. + * + * @param key the key of the new Zarr group within the current group + * @return the created subgroup + * @throws IOException if the metadata cannot be serialized + */ + public Group createGroup(String key) throws IOException{ return Group.create(storeHandle.resolve(key), GroupMetadata.defaultValue()); } + /** + * Creates a new array with the provided metadata at the specified key. + * + * @param key the key of the new Zarr array within the current group + * @param arrayMetadata the metadata of the Zarr array + * @return the created array + * @throws IOException if the metadata cannot be serialized + * @throws ZarrException if the array cannot be created + */ public Array createArray(String key, ArrayMetadata arrayMetadata) - throws IOException, ZarrException { + throws IOException, ZarrException { return Array.create(storeHandle.resolve(key), arrayMetadata); } + /** + * Creates a new array with the provided metadata builder mapper at the specified key. + * + * @param key the key of the new Zarr array within the current group + * @param arrayMetadataBuilderMapper a function building the metadata of the Zarr array + * @throws IOException if the metadata cannot be serialized + */ public Array createArray(String key, Function arrayMetadataBuilderMapper) throws IOException, ZarrException { return Array.create(storeHandle.resolve(key), arrayMetadataBuilderMapper, false); } + private Group writeMetadata() throws IOException { + return writeMetadata(this.metadata); + } + private Group writeMetadata(GroupMetadata newGroupMetadata) throws IOException { ObjectWriter objectWriter = makeObjectWriter(); ByteBuffer metadataBytes = ByteBuffer.wrap(objectWriter.writeValueAsBytes(newGroupMetadata)); storeHandle.resolve(ZARR_JSON) .set(metadataBytes); - return new Group(storeHandle, newGroupMetadata); - } - - public Group setAttributes(Attributes newAttributes) throws ZarrException, IOException { - GroupMetadata newGroupMetadata = new GroupMetadata(newAttributes); - return writeMetadata(newGroupMetadata); + this.metadata = newGroupMetadata; + return this; } + /** + * Updates the attributes of the group using a mapper function. + * + * @param attributeMapper a function that takes the current attributes and returns the updated attributes + * @return the updated group + * @throws ZarrException if the new attributes are invalid + * @throws IOException if the metadata cannot be serialized + */ public Group updateAttributes(Function attributeMapper) throws ZarrException, IOException { return setAttributes(attributeMapper.apply(metadata.attributes)); } + /** + * Sets new attributes for the group, replacing any existing attributes. + * + * @param newAttributes the new attributes to set + * @return the updated group + * @throws ZarrException if the new attributes are invalid + * @throws IOException if the metadata cannot be serialized + */ + public Group setAttributes(Attributes newAttributes) throws ZarrException, IOException { + GroupMetadata newGroupMetadata = new GroupMetadata(newAttributes); + return writeMetadata(newGroupMetadata); + } + @Override public String toString() { return String.format("", storeHandle); diff --git a/src/main/java/dev/zarr/zarrjava/v3/GroupMetadata.java b/src/main/java/dev/zarr/zarrjava/v3/GroupMetadata.java index e761873..e8acd93 100644 --- a/src/main/java/dev/zarr/zarrjava/v3/GroupMetadata.java +++ b/src/main/java/dev/zarr/zarrjava/v3/GroupMetadata.java @@ -44,8 +44,13 @@ public GroupMetadata( this.attributes = attributes; } - public static GroupMetadata defaultValue() throws ZarrException { - return new GroupMetadata(ZARR_FORMAT, NODE_TYPE, new Attributes()); + public static GroupMetadata defaultValue() { + try { + return new GroupMetadata(ZARR_FORMAT, NODE_TYPE, new Attributes()); + } catch (ZarrException e) { + // This should never happen + throw new RuntimeException(e); + } } @Override diff --git a/src/main/java/dev/zarr/zarrjava/v3/Node.java b/src/main/java/dev/zarr/zarrjava/v3/Node.java index 00e1744..4c125fe 100644 --- a/src/main/java/dev/zarr/zarrjava/v3/Node.java +++ b/src/main/java/dev/zarr/zarrjava/v3/Node.java @@ -12,6 +12,7 @@ import java.io.IOException; import java.nio.ByteBuffer; +import java.nio.file.NoSuchFileException; import java.nio.file.Path; import java.nio.file.Paths; diff --git a/src/test/java/dev/zarr/zarrjava/ZarrStoreTest.java b/src/test/java/dev/zarr/zarrjava/ZarrStoreTest.java index f788d06..4a369c9 100644 --- a/src/test/java/dev/zarr/zarrjava/ZarrStoreTest.java +++ b/src/test/java/dev/zarr/zarrjava/ZarrStoreTest.java @@ -1,23 +1,26 @@ package dev.zarr.zarrjava; import com.fasterxml.jackson.databind.ObjectMapper; -import dev.zarr.zarrjava.store.FilesystemStore; -import dev.zarr.zarrjava.store.HttpStore; -import dev.zarr.zarrjava.store.S3Store; +import dev.zarr.zarrjava.core.Attributes; +import dev.zarr.zarrjava.store.*; import dev.zarr.zarrjava.v3.*; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.CsvSource; import software.amazon.awssdk.auth.credentials.AnonymousCredentialsProvider; import software.amazon.awssdk.regions.Region; import software.amazon.awssdk.services.s3.S3Client; import java.io.IOException; import java.nio.file.Files; +import java.util.Arrays; +import java.util.stream.Stream; import static dev.zarr.zarrjava.v3.Node.makeObjectMapper; public class ZarrStoreTest extends ZarrTest { - @Test + @Test public void testFileSystemStores() throws IOException, ZarrException { FilesystemStore fsStore = new FilesystemStore(TESTDATA); ObjectMapper objectMapper = makeObjectMapper(); @@ -78,4 +81,55 @@ public void testHttpStore() throws IOException, ZarrException { Assertions.assertArrayEquals(new long[]{1, 4096, 4096, 2048}, array.metadata().shape); } + + @ParameterizedTest + @CsvSource({"false", "true",}) + public void testMemoryStoreV3(boolean useParallel) throws ZarrException, IOException { + int[] testData = new int[1024 * 1024]; + Arrays.setAll(testData, p -> p); + + Group group = Group.create(new MemoryStore().resolve()); + Array array = group.createArray("array", b -> b + .withShape(1024, 1024) + .withDataType(DataType.UINT32) + .withChunkShape(5, 5) + ); + array.write(ucar.ma2.Array.factory(ucar.ma2.DataType.UINT, new int[]{1024, 1024}, testData), useParallel); + group.createGroup("subgroup"); + group.setAttributes(new Attributes(b -> b.set("some", "value"))); + Stream nodes = group.list(); + Assertions.assertEquals(2, nodes.count()); + + ucar.ma2.Array result = array.read(useParallel); + Assertions.assertArrayEquals(testData, (int[]) result.get1DJavaArray(ucar.ma2.DataType.UINT)); + Attributes attrs = group.metadata().attributes; + Assertions.assertNotNull(attrs); + Assertions.assertEquals("value", attrs.getString("some")); + } + + @ParameterizedTest + @CsvSource({"false", "true",}) + public void testMemoryStoreV2(boolean useParallel) throws ZarrException, IOException { + int[] testData = new int[1024 * 1024]; + Arrays.setAll(testData, p -> p); + + dev.zarr.zarrjava.v2.Group group = dev.zarr.zarrjava.v2.Group.create(new MemoryStore().resolve()); + dev.zarr.zarrjava.v2.Array array = group.createArray("array", b -> b + .withShape(1024, 1024) + .withDataType(dev.zarr.zarrjava.v2.DataType.UINT32) + .withChunks(5, 5) + ); + array.write(ucar.ma2.Array.factory(ucar.ma2.DataType.UINT, new int[]{1024, 1024}, testData), useParallel); + group.createGroup("subgroup"); + Stream nodes = group.list(); + group.setAttributes(new Attributes().set("description", "test group")); + Assertions.assertEquals(2, nodes.count()); + + ucar.ma2.Array result = array.read(useParallel); + Assertions.assertArrayEquals(testData, (int[]) result.get1DJavaArray(ucar.ma2.DataType.UINT)); + Attributes attrs = group.metadata().attributes; + Assertions.assertNotNull(attrs); + Assertions.assertEquals("test group", attrs.getString("description")); + + } } diff --git a/src/test/java/dev/zarr/zarrjava/ZarrV2Test.java b/src/test/java/dev/zarr/zarrjava/ZarrV2Test.java index f783646..346fd2a 100644 --- a/src/test/java/dev/zarr/zarrjava/ZarrV2Test.java +++ b/src/test/java/dev/zarr/zarrjava/ZarrV2Test.java @@ -4,6 +4,7 @@ import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import dev.zarr.zarrjava.store.FilesystemStore; +import dev.zarr.zarrjava.store.MemoryStore; import dev.zarr.zarrjava.store.StoreHandle; import dev.zarr.zarrjava.v2.*; import org.junit.jupiter.api.Assertions; @@ -393,4 +394,20 @@ public void testZarrJsonFormat(Function b + .withShape(10, 10) + .withDataType(DataType.UINT8) + .withChunks(5, 5) + ); + group.createGroup("subgroup"); + Assertions.assertEquals(2, group.list().count()); + for(String s: storeHandle.list().toArray(String[]::new)) + System.out.println(s); + } + } \ No newline at end of file diff --git a/src/test/java/dev/zarr/zarrjava/ZarrV3Test.java b/src/test/java/dev/zarr/zarrjava/ZarrV3Test.java index 846f770..54fded2 100644 --- a/src/test/java/dev/zarr/zarrjava/ZarrV3Test.java +++ b/src/test/java/dev/zarr/zarrjava/ZarrV3Test.java @@ -388,7 +388,7 @@ public void testParallel(boolean useParallel) throws IOException, ZarrException int[] testData = new int[512 * 512 * 512]; Arrays.setAll(testData, p -> p); - StoreHandle storeHandle = new FilesystemStore(TESTOUTPUT).resolve("testParallelRead"); + StoreHandle storeHandle = new FilesystemStore(TESTOUTPUT).resolve("testParallelRead", useParallel ? "parallel" : "serial"); ArrayMetadata metadata = Array.metadataBuilder() .withShape(512, 512, 512) .withDataType(DataType.UINT32) @@ -402,7 +402,6 @@ public void testParallel(boolean useParallel) throws IOException, ZarrException ucar.ma2.Array result = readArray.read(useParallel); Assertions.assertArrayEquals(testData, (int[]) result.get1DJavaArray(ucar.ma2.DataType.UINT)); - clearTestoutputFolder(); } @Test