Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions src/main/java/dev/zarr/zarrjava/core/Group.java
Original file line number Diff line number Diff line change
Expand Up @@ -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<Node> list() {
return storeHandle.list()
.map(key -> {
try {
return get(key);
} catch (ZarrException e) {
} catch (Exception e) {
throw new RuntimeException(e);
}
})
Expand Down
88 changes: 88 additions & 0 deletions src/main/java/dev/zarr/zarrjava/store/MemoryStore.java
Original file line number Diff line number Diff line change
@@ -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<List<String>, byte[]> map = new ConcurrentHashMap<>();

List<String> resolveKeys(String[] keys) {
ArrayList<String> resolvedKeys = new ArrayList<>();
for(String key:keys){
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we have a code formatter set up?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, the code is not uniformly formatted. Sometimes it's two spaces and sometimes it's tabs.

Copy link
Member Author

@brokkoli71 brokkoli71 Dec 8, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I could format the entire code in a separate PR, so that changes are easier to understand?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, please do.

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<String> list(String[] keys) {
List<String> prefix = resolveKeys(keys);
Set<String> allKeys = new HashSet<>();

for (List<String> 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<String> 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("<MemoryStore {%s}>", hashCode());
}
}

11 changes: 11 additions & 0 deletions src/main/java/dev/zarr/zarrjava/v2/Array.java
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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
Expand Down
180 changes: 166 additions & 14 deletions src/main/java/dev/zarr/zarrjava/v2/Group.java
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,15 @@
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 javax.annotation.Nonnull;
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;
Expand All @@ -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(
Expand All @@ -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<ArrayMetadataBuilder, ArrayMetadataBuilder> 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<Attributes, Attributes> attributeMapper)
throws ZarrException, IOException {
return setAttributes(attributeMapper.apply(metadata.attributes));
}


@Override
public String toString() {
return String.format("<v2.Group {%s}>", storeHandle);
Expand Down
Loading
Loading