Skip to content

Commit 832ef4e

Browse files
committed
change v3 attributes class to Attributes
1 parent dc35390 commit 832ef4e

File tree

11 files changed

+187
-162
lines changed

11 files changed

+187
-162
lines changed

src/main/java/dev/zarr/zarrjava/core/Attributes.java

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
package dev.zarr.zarrjava.core;
22

3+
import dev.zarr.zarrjava.ZarrException;
4+
35
import javax.annotation.Nonnull;
4-
import java.lang.reflect.InvocationTargetException;
56
import java.util.HashMap;
67
import java.util.List;
78
import java.util.Map;
@@ -77,19 +78,21 @@ public Attributes getAttributes(String key) {
7778
throw new IllegalArgumentException("Value for key " + key + " is not an Attributes object");
7879
}
7980

80-
public <T> T[] getArray(String key, Class<T> clazz) throws NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
81+
public <T> T[] getArray(String key, Class<T> clazz) throws ZarrException {
8182
Object value = this.get(key);
8283
if (value instanceof Object[] && ( ((Object[]) value).length == 0 || clazz.isInstance(((Object[]) value)[0]) )) {
8384
return (T[]) value;
8485
}
8586
if (value instanceof List) {
8687
List<?> list = (List<?>) value;
88+
@SuppressWarnings("unchecked")
8789
T[] array = (T[]) java.lang.reflect.Array.newInstance(clazz, list.size());
8890
for (int i = 0; i < list.size(); i++) {
8991
Object elem = list.get(i);
9092
if (clazz.isInstance(elem)) {
9193
array[i] = clazz.cast(elem);
9294
} else {
95+
// Try to find a constructor that takes the element's class
9396
java.lang.reflect.Constructor<?> matched = null;
9497
for (java.lang.reflect.Constructor<?> c : clazz.getConstructors()) {
9598
Class<?>[] params = c.getParameterTypes();
@@ -99,9 +102,13 @@ public <T> T[] getArray(String key, Class<T> clazz) throws NoSuchMethodException
99102
}
100103
}
101104
if (matched != null) {
102-
array[i] = (T) matched.newInstance(elem);
105+
try {
106+
array[i] = (T) matched.newInstance(elem);
107+
} catch (Exception e) {
108+
throw new ZarrException("Failed to convert element at index " + i + " to type " + clazz.getName(), e);
109+
}
103110
} else {
104-
throw new IllegalArgumentException("Element at index " + i + " is not of type " + clazz.getName());
111+
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());
105112
}
106113
}
107114
}

src/main/java/dev/zarr/zarrjava/v2/ArrayMetadataBuilder.java

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ public class ArrayMetadataBuilder {
1717
Object fillValue = null;
1818
Codec[] filters = null;
1919
Codec compressor = null;
20-
Attributes attributes = null;
20+
Attributes attributes = new Attributes();
2121

2222

2323
protected ArrayMetadataBuilder() {
@@ -121,6 +121,11 @@ public ArrayMetadataBuilder withZlibCompressor() {
121121
return withZlibCompressor(5);
122122
}
123123

124+
public ArrayMetadataBuilder putAttribute(String key, Object value) {
125+
this.attributes.put(key, value);
126+
return this;
127+
}
128+
124129
public ArrayMetadataBuilder withAttributes(Attributes attributes) {
125130
if (this.attributes == null) {
126131
this.attributes = attributes;

src/main/java/dev/zarr/zarrjava/v3/Array.java

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
import com.fasterxml.jackson.databind.ObjectMapper;
44
import dev.zarr.zarrjava.ZarrException;
5+
import dev.zarr.zarrjava.core.Attributes;
56
import dev.zarr.zarrjava.store.FilesystemStore;
67
import dev.zarr.zarrjava.store.StoreHandle;
78
import dev.zarr.zarrjava.utils.Utils;
@@ -218,7 +219,7 @@ public Array resize(long[] newShape) throws ZarrException, IOException {
218219
* @throws ZarrException throws ZarrException if the new metadata is invalid
219220
* @throws IOException throws IOException if the new metadata cannot be serialized
220221
*/
221-
public Array setAttributes(Map<String, Object> newAttributes) throws ZarrException, IOException {
222+
public Array setAttributes(Attributes newAttributes) throws ZarrException, IOException {
222223
ArrayMetadata newArrayMetadata =
223224
ArrayMetadataBuilder.fromArrayMetadata(metadata)
224225
.withAttributes(newAttributes)
@@ -234,11 +235,10 @@ public Array setAttributes(Map<String, Object> newAttributes) throws ZarrExcepti
234235
*
235236
* @param attributeMapper the callback that is used to construct the new attributes
236237
* @throws ZarrException throws ZarrException if the new metadata is invalid
237-
* @throws IOException throws IOException if the new metadata cannot be serialized
238+
* @throws IOException throws IOException if the new metadata cannot be serialized
238239
*/
239-
public Array updateAttributes(Function<Map<String, Object>, Map<String, Object>> attributeMapper)
240-
throws ZarrException, IOException {
241-
return setAttributes(attributeMapper.apply(new HashMap<String, Object>(metadata.attributes) {
240+
public Array updateAttributes(Function<Attributes, Attributes> attributeMapper) throws ZarrException, IOException {
241+
return setAttributes(attributeMapper.apply(new Attributes(metadata.attributes) {
242242
}));
243243
}
244244

src/main/java/dev/zarr/zarrjava/v3/ArrayMetadata.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ public final class ArrayMetadata extends dev.zarr.zarrjava.core.ArrayMetadata {
4040
public final Codec[] codecs;
4141
@Nullable
4242
@JsonProperty("attributes")
43-
public final Map<String, Object> attributes;
43+
public final Attributes attributes;
4444
@Nullable
4545
@JsonProperty("dimension_names")
4646
public final String[] dimensionNames;
@@ -56,7 +56,7 @@ public ArrayMetadata(
5656
Object fillValue,
5757
@Nonnull Codec[] codecs,
5858
@Nullable String[] dimensionNames,
59-
@Nullable Map<String, Object> attributes,
59+
@Nullable Attributes attributes,
6060
@Nullable Map<String, Object>[] storageTransformers
6161
) throws ZarrException {
6262
this(ZARR_FORMAT, NODE_TYPE, shape, dataType, chunkGrid, chunkKeyEncoding, fillValue, codecs,
@@ -76,7 +76,7 @@ public ArrayMetadata(
7676
@JsonProperty(value = "fill_value", required = true) Object fillValue,
7777
@Nonnull @JsonProperty(value = "codecs") Codec[] codecs,
7878
@Nullable @JsonProperty(value = "dimension_names") String[] dimensionNames,
79-
@Nullable @JsonProperty(value = "attributes") Map<String, Object> attributes,
79+
@Nullable @JsonProperty(value = "attributes") Attributes attributes,
8080
@Nullable @JsonProperty(value = "storage_transformers") Map<String, Object>[] storageTransformers
8181
) throws ZarrException {
8282
super(shape, fillValue, dataType);

src/main/java/dev/zarr/zarrjava/v3/ArrayMetadataBuilder.java

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package dev.zarr.zarrjava.v3;
22

33
import dev.zarr.zarrjava.ZarrException;
4+
import dev.zarr.zarrjava.core.Attributes;
45
import dev.zarr.zarrjava.v3.chunkgrid.ChunkGrid;
56
import dev.zarr.zarrjava.v3.chunkgrid.RegularChunkGrid;
67
import dev.zarr.zarrjava.v3.chunkkeyencoding.ChunkKeyEncoding;
@@ -26,7 +27,7 @@ public class ArrayMetadataBuilder {
2627

2728
Object fillValue = 0;
2829
Codec[] codecs = new Codec[]{new BytesCodec(Endian.LITTLE)};
29-
Map<String, Object> attributes = new HashMap<>();
30+
Attributes attributes = new Attributes();
3031
Map<String, Object>[] storageTransformers = new HashMap[]{};
3132
String[] dimensionNames = null;
3233

@@ -121,8 +122,12 @@ public ArrayMetadataBuilder putAttribute(String key, Object value) {
121122
return this;
122123
}
123124

124-
public ArrayMetadataBuilder withAttributes(Map<String, Object> attributes) {
125-
this.attributes = attributes;
125+
public ArrayMetadataBuilder withAttributes(Attributes attributes) {
126+
if (this.attributes == null) {
127+
this.attributes = attributes;
128+
} else {
129+
this.attributes.putAll(attributes);
130+
}
126131
return this;
127132
}
128133

src/main/java/dev/zarr/zarrjava/v3/Group.java

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
import com.fasterxml.jackson.databind.ObjectMapper;
44
import dev.zarr.zarrjava.ZarrException;
5+
import dev.zarr.zarrjava.core.Attributes;
56
import dev.zarr.zarrjava.store.FilesystemStore;
67
import dev.zarr.zarrjava.store.StoreHandle;
78
import dev.zarr.zarrjava.utils.Utils;
@@ -53,7 +54,7 @@ public static Group create(
5354

5455
public static Group create(
5556
@Nonnull StoreHandle storeHandle,
56-
@Nonnull Map<String, Object> attributes
57+
@Nonnull Attributes attributes
5758
) throws IOException, ZarrException {
5859
return new Group(storeHandle, new GroupMetadata(attributes));
5960
}
@@ -93,7 +94,7 @@ public Group createGroup(String key, GroupMetadata groupMetadata)
9394
return Group.create(storeHandle.resolve(key), groupMetadata);
9495
}
9596

96-
public Group createGroup(String key, Map<String, Object> attributes)
97+
public Group createGroup(String key, Attributes attributes)
9798
throws IOException, ZarrException {
9899
return Group.create(storeHandle.resolve(key), new GroupMetadata(attributes));
99100
}
@@ -121,12 +122,12 @@ private Group writeMetadata(GroupMetadata newGroupMetadata) throws IOException {
121122
return new Group(storeHandle, newGroupMetadata);
122123
}
123124

124-
public Group setAttributes(Map<String, Object> newAttributes) throws ZarrException, IOException {
125+
public Group setAttributes(Attributes newAttributes) throws ZarrException, IOException {
125126
GroupMetadata newGroupMetadata = new GroupMetadata(newAttributes);
126127
return writeMetadata(newGroupMetadata);
127128
}
128129

129-
public Group updateAttributes(Function<Map<String, Object>, Map<String, Object>> attributeMapper)
130+
public Group updateAttributes(Function<Attributes, Attributes> attributeMapper)
130131
throws ZarrException, IOException {
131132
return setAttributes(attributeMapper.apply(metadata.attributes));
132133
}

src/main/java/dev/zarr/zarrjava/v3/GroupMetadata.java

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,8 @@
33
import com.fasterxml.jackson.annotation.JsonCreator;
44
import com.fasterxml.jackson.annotation.JsonProperty;
55
import dev.zarr.zarrjava.ZarrException;
6-
import java.util.HashMap;
7-
import java.util.Map;
6+
import dev.zarr.zarrjava.core.Attributes;
7+
88
import javax.annotation.Nullable;
99

1010
public final class GroupMetadata extends dev.zarr.zarrjava.core.GroupMetadata {
@@ -17,17 +17,17 @@ public final class GroupMetadata extends dev.zarr.zarrjava.core.GroupMetadata {
1717
public final String nodeType = "group";
1818

1919
@Nullable
20-
public final Map<String, Object> attributes;
20+
public final Attributes attributes;
2121

22-
public GroupMetadata(@Nullable Map<String, Object> attributes) throws ZarrException {
22+
public GroupMetadata(@Nullable Attributes attributes) throws ZarrException {
2323
this(ZARR_FORMAT, NODE_TYPE, attributes);
2424
}
2525

2626
@JsonCreator(mode = JsonCreator.Mode.PROPERTIES)
2727
public GroupMetadata(
2828
@JsonProperty(value = "zarr_format", required = true) int zarrFormat,
2929
@JsonProperty(value = "node_type", required = true) String nodeType,
30-
@Nullable @JsonProperty(value = "attributes") Map<String, Object> attributes
30+
@Nullable @JsonProperty(value = "attributes") Attributes attributes
3131
) throws ZarrException {
3232
if (zarrFormat != this.zarrFormat) {
3333
throw new ZarrException(
@@ -41,6 +41,6 @@ public GroupMetadata(
4141
}
4242

4343
public static GroupMetadata defaultValue() throws ZarrException {
44-
return new GroupMetadata(ZARR_FORMAT, NODE_TYPE, new HashMap<>());
44+
return new GroupMetadata(ZARR_FORMAT, NODE_TYPE, new Attributes());
4545
}
4646
}
Lines changed: 139 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,139 @@
1+
package dev.zarr.zarrjava;
2+
3+
import dev.zarr.zarrjava.core.Attributes;
4+
import dev.zarr.zarrjava.store.FilesystemStore;
5+
import dev.zarr.zarrjava.store.StoreHandle;
6+
import dev.zarr.zarrjava.v2.Array;
7+
import dev.zarr.zarrjava.v2.ArrayMetadata;
8+
import dev.zarr.zarrjava.v2.DataType;
9+
import org.junit.jupiter.api.Assertions;
10+
import org.junit.jupiter.api.Test;
11+
12+
import java.io.IOException;
13+
import java.util.ArrayList;
14+
import java.util.List;
15+
16+
public class ZarrAttributesTest extends ZarrTest {
17+
18+
ArrayList<Object> listAttribute = new ArrayList<Object>() {
19+
{
20+
add(1);
21+
add(2.0d);
22+
add("string");
23+
}
24+
};
25+
int[] intArrayAttribute = new int[]{1, 2, 3};
26+
long[] longArrayAttribute = new long[]{1, 2, 3};
27+
double[] doubleArrayAttribute = new double[]{1.0, 2.0, 3.0};
28+
float[] floatArrayAttribute = new float[]{1.0f, 2.0f, 3.0f};
29+
Attributes testAttributes = new Attributes() {{
30+
put("string", "stringvalue");
31+
put("int", 42);
32+
put("float", 0.5f);
33+
put("double", 3.14);
34+
put("boolean", true);
35+
put("list", listAttribute);
36+
put("int_array", intArrayAttribute);
37+
put("long_array", longArrayAttribute);
38+
put("double_array", doubleArrayAttribute);
39+
put("float_array", floatArrayAttribute);
40+
put("nested", new Attributes() {{
41+
put("element", "value");
42+
}});
43+
put("array_of_attributes", new Attributes[]{
44+
new Attributes() {{
45+
put("a", 1);
46+
}},
47+
new Attributes() {{
48+
put("b", 2);
49+
}}
50+
});
51+
}};
52+
53+
static void assertListEquals(List<Object> a, List<Object> b) {
54+
Assertions.assertEquals(a.size(), b.size());
55+
for (int i = 0; i < a.size(); i++) {
56+
Object aval = a.get(i);
57+
Object bval = b.get(i);
58+
if (aval instanceof List && bval instanceof List) {
59+
assertListEquals((List<Object>) aval, (List<Object>) bval);
60+
} else {
61+
Assertions.assertEquals(aval, bval);
62+
}
63+
}
64+
}
65+
66+
void assertContainsTestAttributes(Attributes attributes) throws ZarrException {
67+
Assertions.assertEquals("stringvalue", attributes.getString("string"));
68+
Assertions.assertEquals(42, attributes.getInt("int"));
69+
Assertions.assertEquals(0.5, attributes.getFloat("float"));
70+
Assertions.assertEquals(3.14, attributes.getDouble("double"));
71+
Assertions.assertTrue(attributes.getBoolean("boolean"));
72+
assertListEquals(listAttribute, attributes.getList("list"));
73+
Assertions.assertArrayEquals(intArrayAttribute, attributes.getIntArray("int_array"));
74+
Assertions.assertArrayEquals(longArrayAttribute, attributes.getLongArray("long_array"));
75+
Assertions.assertArrayEquals(doubleArrayAttribute, attributes.getDoubleArray("double_array"));
76+
Assertions.assertArrayEquals(floatArrayAttribute, attributes.getFloatArray("float_array"));
77+
Assertions.assertEquals("value", attributes.getAttributes("nested").getString("element"));
78+
Assertions.assertArrayEquals(
79+
new Attributes[]{
80+
new Attributes() {{ put("a", 1); }},
81+
new Attributes() {{ put("b", 2); }}
82+
},
83+
attributes.getArray("array_of_attributes", Attributes.class)
84+
);
85+
}
86+
87+
@Test
88+
public void testAttributesV2() throws IOException, ZarrException {
89+
StoreHandle storeHandle = new FilesystemStore(TESTOUTPUT).resolve("testAttributesV2");
90+
91+
ArrayMetadata arrayMetadata = dev.zarr.zarrjava.v2.Array.metadataBuilder()
92+
.withShape(10, 10)
93+
.withDataType(DataType.UINT8)
94+
.withChunks(5, 5)
95+
.putAttribute("specific", "attribute")
96+
.withAttributes(testAttributes)
97+
.withAttributes(new Attributes() {{
98+
put("another", "attribute");
99+
}})
100+
.build();
101+
102+
dev.zarr.zarrjava.v2.Array array = dev.zarr.zarrjava.v2.Array.create(storeHandle, arrayMetadata);
103+
assertContainsTestAttributes(array.metadata().attributes());
104+
Assertions.assertEquals("attribute", array.metadata().attributes().getString("specific"));
105+
Assertions.assertEquals("attribute", array.metadata().attributes().getString("another"));
106+
107+
dev.zarr.zarrjava.v2.Array arrayOpened = Array.open(storeHandle);
108+
assertContainsTestAttributes(array.metadata().attributes());
109+
Assertions.assertEquals("attribute", arrayOpened.metadata().attributes().getString("another"));
110+
Assertions.assertEquals("attribute", arrayOpened.metadata().attributes().getString("specific"));
111+
}
112+
113+
@Test
114+
public void testAttributesV3() throws IOException, ZarrException {
115+
StoreHandle storeHandle = new FilesystemStore(TESTOUTPUT).resolve("testAttributesV3");
116+
117+
dev.zarr.zarrjava.v3.ArrayMetadata arrayMetadata = dev.zarr.zarrjava.v3.Array.metadataBuilder()
118+
.withShape(10, 10)
119+
.withDataType(dev.zarr.zarrjava.v3.DataType.UINT8)
120+
.withChunkShape(5, 5)
121+
.putAttribute("specific", "attribute")
122+
.withAttributes(testAttributes)
123+
.withAttributes(new Attributes() {{
124+
put("another", "attribute");
125+
}})
126+
127+
.build();
128+
129+
dev.zarr.zarrjava.v3.Array array = dev.zarr.zarrjava.v3.Array.create(storeHandle, arrayMetadata);
130+
assertContainsTestAttributes(array.metadata().attributes());
131+
Assertions.assertEquals("attribute", array.metadata().attributes().getString("specific"));
132+
Assertions.assertEquals("attribute", array.metadata().attributes().getString("another"));
133+
134+
dev.zarr.zarrjava.v3.Array arrayOpened = dev.zarr.zarrjava.v3.Array.open(storeHandle);
135+
assertContainsTestAttributes(arrayOpened.metadata().attributes());
136+
Assertions.assertEquals("attribute", arrayOpened.metadata().attributes().getString("specific"));
137+
Assertions.assertEquals("attribute", arrayOpened.metadata().attributes().getString("another"));
138+
}
139+
}

src/test/java/dev/zarr/zarrjava/ZarrPythonTests.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -210,7 +210,7 @@ public void testReadV3(String codec, String codecParam, DataType dataType) throw
210210
@ParameterizedTest
211211
@MethodSource("compressorAndDataTypeProviderV3")
212212
public void testWriteV3(String codec, String codecParam, DataType dataType) throws Exception {
213-
Map<String, Object> attributes = new HashMap<>();
213+
Attributes attributes = new Attributes();
214214
attributes.put("test_key", "test_value");
215215
StoreHandle storeHandle = new FilesystemStore(TESTOUTPUT).resolve("testWriteV3", codec, codecParam, dataType.name());
216216

0 commit comments

Comments
 (0)