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
2 changes: 2 additions & 0 deletions src/main/java/dev/zarr/zarrjava/core/ArrayMetadata.java
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,8 @@ public int ndim() {

public abstract Object parsedFillValue();

public @Nonnull abstract Attributes attributes() throws ZarrException;

public static Object parseFillValue(Object fillValue, @Nonnull DataType dataType)
throws ZarrException {
if (fillValue == null) {
Expand Down
241 changes: 241 additions & 0 deletions src/main/java/dev/zarr/zarrjava/core/Attributes.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,241 @@
package dev.zarr.zarrjava.core;

import dev.zarr.zarrjava.ZarrException;

import javax.annotation.Nonnull;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.function.Function;

public class Attributes extends HashMap<String, Object> {

public Attributes() {
super();
}

public Attributes (Function<Attributes, Attributes> attributeMapper) {
super();
attributeMapper.apply(this);
}


public Attributes(Map<String, Object> attributes) {
super(attributes);
}

public Attributes set(String s, Object o){
this.put(s, o);
return this;
}

public Attributes delete(String s){
this.remove(s);
return this;
}

public boolean getBoolean(String key) {
Object value = this.get(key);
if (value instanceof Boolean) {
return (Boolean) value;
}
throw new IllegalArgumentException("Value for key " + key + " is not a Boolean");
}

public int getInt(String key) {
Object value = this.get(key);
if (value instanceof Number) {
return ((Number) value).intValue();
}
throw new IllegalArgumentException("Value for key " + key + " is not an Integer");
}

public double getDouble(String key) {
Object value = this.get(key);
if (value instanceof Number) {
return ((Number) value).doubleValue();
}
throw new IllegalArgumentException("Value for key " + key + " is not a Double");
}

public float getFloat(String key) {
Object value = this.get(key);
if (value instanceof Number) {
return ((Number) value).floatValue();
}
throw new IllegalArgumentException("Value for key " + key + " is not a Float");
}

@Nonnull
public String getString(String key) {
Object value = this.get(key);
if (value instanceof String) {
return (String) value;
}
throw new IllegalArgumentException("Value for key " + key + " is not a String");
}

@Nonnull
public List<Object> getList(String key) {
Object value = this.get(key);
if (value instanceof List) {
return (List<Object>) value;
}
throw new IllegalArgumentException("Value for key " + key + " is not a List");
}

public Attributes getAttributes(String key) {
Object value = this.get(key);
if (value instanceof Attributes) {
return (Attributes) value;
}
if (value instanceof Map) {
return new Attributes((Map<String, Object>) value);
}
throw new IllegalArgumentException("Value for key " + key + " is not an Attributes object");
}

public <T> T[] getArray(String key, Class<T> clazz) throws ZarrException {
Object value = this.get(key);
if (value instanceof Object[] && (((Object[]) value).length == 0 || clazz.isInstance(((Object[]) value)[0]) )) {
return (T[]) value;
}
if (value instanceof List) {
List<?> list = (List<?>) value;
@SuppressWarnings("unchecked")
T[] array = (T[]) java.lang.reflect.Array.newInstance(clazz, list.size());
for (int i = 0; i < list.size(); i++) {
Object elem = list.get(i);
if (clazz.isInstance(elem)) {
array[i] = clazz.cast(elem);
} else {
// Try to find a constructor that takes the element's class
java.lang.reflect.Constructor<?> matched = null;
for (java.lang.reflect.Constructor<?> c : clazz.getConstructors()) {
Class<?>[] params = c.getParameterTypes();
if (params.length == 1 && params[0].isAssignableFrom(elem.getClass())) {
matched = c;
break;
}
}
if (matched != null) {
try {
array[i] = (T) matched.newInstance(elem);
} catch (Exception e) {
throw new ZarrException("Failed to convert element at index " + i + " to type " + clazz.getName(), e);
}
} else {
throw new IllegalArgumentException("Element at index " + i + " is not of type " + clazz.getName() + " and no suitable constructor found for conversion of type " + elem.getClass().getName());
}
}
}
return array;
}
throw new IllegalArgumentException("Value for key " + key + " is not a List or array of type " + clazz.getName());
}

public int[] getIntArray(String key) {
Object value = this.get(key);
if (value instanceof int[]) {
return (int[]) value;
}
if (value instanceof List) {
List<?> list = (List<?>) value;
int[] array = new int[list.size()];
for (int i = 0; i < list.size(); i++) {
Object elem = list.get(i);
if (elem instanceof Number) {
array[i] = ((Number) elem).intValue();
} else {
throw new IllegalArgumentException("Element at index " + i + " is not a Number");
}
}
return array;
}
throw new IllegalArgumentException("Value for key " + key + " is not an int array or List");
}

public long[] getLongArray(String key) {
Object value = this.get(key);
if (value instanceof long[]) {
return (long[]) value;
}
if (value instanceof List) {
List<?> list = (List<?>) value;
long[] array = new long[list.size()];
for (int i = 0; i < list.size(); i++) {
Object elem = list.get(i);
if (elem instanceof Number) {
array[i] = ((Number) elem).longValue();
} else {
throw new IllegalArgumentException("Element at index " + i + " is not a Number");
}
}
return array;
}
throw new IllegalArgumentException("Value for key " + key + " is not a long array or List");
}

public double[] getDoubleArray(String key) {
Object value = this.get(key);
if (value instanceof double[]) {
return (double[]) value;
}
if (value instanceof List) {
List<?> list = (List<?>) value;
double[] array = new double[list.size()];
for (int i = 0; i < list.size(); i++) {
Object elem = list.get(i);
if (elem instanceof Number) {
array[i] = ((Number) elem).doubleValue();
} else {
throw new IllegalArgumentException("Element at index " + i + " is not a Number");
}
}
return array;
}
throw new IllegalArgumentException("Value for key " + key + " is not a double array or List");
}

public float[] getFloatArray(String key) {
Object value = this.get(key);
if (value instanceof float[]) {
return (float[]) value;
}
if (value instanceof List) {
List<?> list = (List<?>) value;
float[] array = new float[list.size()];
for (int i = 0; i < list.size(); i++) {
Object elem = list.get(i);
if (elem instanceof Number) {
array[i] = ((Number) elem).floatValue();
} else {
throw new IllegalArgumentException("Element at index " + i + " is not a Number");
}
}
return array;
}
throw new IllegalArgumentException("Value for key " + key + " is not a float array or List");
}

public boolean[] getBooleanArray(String key) {
Object value = this.get(key);
if (value instanceof boolean[]) {
return (boolean[]) value;
}
if (value instanceof List) {
List<?> list = (List<?>) value;
boolean[] array = new boolean[list.size()];
for (int i = 0; i < list.size(); i++) {
Object elem = list.get(i);
if (elem instanceof Boolean) {
array[i] = (Boolean) elem;
} else {
throw new IllegalArgumentException("Element at index " + i + " is not a Boolean");
}
}
return array;
}
throw new IllegalArgumentException("Value for key " + key + " is not a boolean array or List");
}
}
2 changes: 2 additions & 0 deletions src/main/java/dev/zarr/zarrjava/core/Group.java
Original file line number Diff line number Diff line change
Expand Up @@ -83,4 +83,6 @@ public Node[] listAsArray() {
return nodeStream.toArray(Node[]::new);
}
}

public abstract GroupMetadata metadata();
}
10 changes: 9 additions & 1 deletion src/main/java/dev/zarr/zarrjava/core/GroupMetadata.java
Original file line number Diff line number Diff line change
@@ -1,3 +1,11 @@
package dev.zarr.zarrjava.core;

public abstract class GroupMetadata {}
import javax.annotation.Nonnull;

import dev.zarr.zarrjava.ZarrException;

public abstract class GroupMetadata {

public @Nonnull abstract Attributes attributes() throws ZarrException;

}
1 change: 1 addition & 0 deletions src/main/java/dev/zarr/zarrjava/core/Node.java
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ public interface Node {

String ZARR_JSON = "zarr.json";
String ZARRAY = ".zarray";
String ZATTRS = ".zattrs";
String ZGROUP = ".zgroup";

/**
Expand Down
79 changes: 74 additions & 5 deletions src/main/java/dev/zarr/zarrjava/v2/Array.java
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
package dev.zarr.zarrjava.v2;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.ObjectWriter;
import dev.zarr.zarrjava.ZarrException;
import dev.zarr.zarrjava.core.Attributes;
import dev.zarr.zarrjava.store.FilesystemStore;
import dev.zarr.zarrjava.store.StoreHandle;
import dev.zarr.zarrjava.utils.Utils;
Expand Down Expand Up @@ -47,13 +49,19 @@ protected Array(StoreHandle storeHandle, ArrayMetadata arrayMetadata) throws IOE
* @throws ZarrException throws ZarrException if the Zarr array cannot be opened
*/
public static Array open(StoreHandle storeHandle) throws IOException, ZarrException {
ObjectMapper mapper = makeObjectMapper();
ArrayMetadata metadata = mapper.readValue(
Utils.toArray(storeHandle.resolve(ZARRAY).readNonNull()),
ArrayMetadata.class
);
if (storeHandle.resolve(ZATTRS).exists())
metadata.attributes = mapper.readValue(
Utils.toArray(storeHandle.resolve(ZATTRS).readNonNull()),
Attributes.class
);
return new Array(
storeHandle,
makeObjectMapper()
.readValue(
Utils.toArray(storeHandle.resolve(ZARRAY).readNonNull()),
ArrayMetadata.class
)
metadata
);
}

Expand Down Expand Up @@ -144,6 +152,12 @@ public static Array create(StoreHandle storeHandle, ArrayMetadata arrayMetadata,
}
ObjectWriter objectWriter = makeObjectWriter();
ByteBuffer metadataBytes = ByteBuffer.wrap(objectWriter.writeValueAsBytes(arrayMetadata));
if (arrayMetadata.attributes != null) {
StoreHandle attrsHandle = storeHandle.resolve(ZATTRS);
ByteBuffer attrsBytes = ByteBuffer.wrap(
objectWriter.writeValueAsBytes(arrayMetadata.attributes));
attrsHandle.set(attrsBytes);
}
metadataHandle.set(metadataBytes);
return new Array(storeHandle, arrayMetadata);
}
Expand All @@ -165,6 +179,61 @@ public static ArrayMetadataBuilder metadataBuilder(ArrayMetadata existingMetadat
return ArrayMetadataBuilder.fromArrayMetadata(existingMetadata);
}

private Array writeMetadata(ArrayMetadata newArrayMetadata) throws ZarrException, IOException {
return Array.create(storeHandle, newArrayMetadata, true);
}

/**
* Sets a new shape for the Zarr array. It only changes the metadata, no array data is modified or
* deleted. This method returns a new instance of the Zarr array class and the old instance
* becomes invalid.
*
* @param newShape the new shape of the Zarr array
* @throws ZarrException if the new metadata is invalid
* @throws IOException throws IOException if the new metadata cannot be serialized
*/
public Array resize(long[] newShape) throws ZarrException, IOException {
if (newShape.length != metadata.ndim()) {
throw new IllegalArgumentException(
"'newShape' needs to have rank '" + metadata.ndim() + "'.");
}

ArrayMetadata newArrayMetadata = ArrayMetadataBuilder.fromArrayMetadata(metadata)
.withShape(newShape)
.build();
return writeMetadata(newArrayMetadata);
}

/**
* Sets the attributes of the Zarr array. It overwrites and removes any existing attributes. This
* method returns a new instance of the Zarr array class and the old instance becomes invalid.
*
* @param newAttributes the new attributes of the Zarr array
* @throws ZarrException throws ZarrException if the new metadata is invalid
* @throws IOException throws IOException if the new metadata cannot be serialized
*/
public Array setAttributes(Attributes newAttributes) throws ZarrException, IOException {
ArrayMetadata newArrayMetadata =
ArrayMetadataBuilder.fromArrayMetadata(metadata, false)
.withAttributes(newAttributes)
.build();
return writeMetadata(newArrayMetadata);
}

/**
* Updates the attributes of the Zarr array. It provides a callback that gets the current
* attributes as input and needs to return the new set of attributes. The attributes in the
* callback may be mutated. This method overwrites and removes any existing attributes. This
* method returns a new instance of the Zarr array class and the old instance becomes invalid.
*
* @param attributeMapper the callback that is used to construct the new attributes
* @throws ZarrException throws ZarrException if the new metadata is invalid
* @throws IOException throws IOException if the new metadata cannot be serialized
*/
public Array updateAttributes(Function<Attributes, Attributes> attributeMapper) throws ZarrException, IOException {
return setAttributes(attributeMapper.apply(metadata.attributes));
}

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