Skip to content

Commit c1eb287

Browse files
committed
reduce code duplication and better unified interface between versions (via MultiscalesEntry)
1 parent 215de0f commit c1eb287

19 files changed

Lines changed: 227 additions & 299 deletions

src/main/java/dev/zarr/zarrjava/ome/MultiscaleImage.java

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

33
import dev.zarr.zarrjava.ZarrException;
44
import dev.zarr.zarrjava.core.Node;
5+
import dev.zarr.zarrjava.ome.metadata.MultiscalesEntry;
56
import dev.zarr.zarrjava.store.StoreHandle;
67
import dev.zarr.zarrjava.utils.Utils;
78

89
import java.io.IOException;
9-
import java.nio.ByteBuffer;
1010
import java.util.ArrayList;
1111
import java.util.Collections;
1212
import java.util.List;
@@ -22,9 +22,10 @@ public interface MultiscaleImage {
2222
StoreHandle getStoreHandle();
2323

2424
/**
25-
* Returns the multiscale node descriptor at index {@code i}.
25+
* Returns a {@link MultiscalesEntry} view of multiscale {@code i}, normalized to the shared
26+
* metadata type. All axis and dataset information is accessible from the returned entry.
2627
*/
27-
UnifiedMultiscaleNode getMultiscaleNode(int i) throws ZarrException;
28+
MultiscalesEntry getMultiscaleNode(int i) throws ZarrException;
2829

2930
/**
3031
* Opens the scale level array at index {@code i} within the first multiscale entry.
@@ -40,9 +41,9 @@ public interface MultiscaleImage {
4041
* Returns the axis names of the first multiscale entry.
4142
*/
4243
default List<String> getAxisNames() throws ZarrException {
43-
UnifiedMultiscaleNode node = getMultiscaleNode(0);
44+
MultiscalesEntry entry = getMultiscaleNode(0);
4445
List<String> names = new ArrayList<>();
45-
for (dev.zarr.zarrjava.ome.metadata.Axis axis : node.axes) {
46+
for (dev.zarr.zarrjava.ome.metadata.Axis axis : entry.axes) {
4647
names.add(axis.name);
4748
}
4849
return names;
@@ -103,7 +104,7 @@ default MultiscaleImage openLabel(String name) throws IOException, ZarrException
103104
* <p>Tries v0.5 (zarr.json with "ome" key) first, then v0.4 (.zattrs with "multiscales" key).
104105
*/
105106
static MultiscaleImage open(StoreHandle storeHandle) throws IOException, ZarrException {
106-
// Try v0.5: zarr.json with "ome" key
107+
// Try version>= 0.5: zarr.json with "ome" key
107108
StoreHandle zarrJson = storeHandle.resolve(Node.ZARR_JSON);
108109
if (zarrJson.exists()) {
109110
com.fasterxml.jackson.databind.ObjectMapper mapper = dev.zarr.zarrjava.v3.Node.makeObjectMapper();

src/main/java/dev/zarr/zarrjava/ome/MultiscalesMetadataImage.java

Lines changed: 11 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@
55
import dev.zarr.zarrjava.ome.metadata.MultiscalesEntry;
66

77
import java.io.IOException;
8-
import java.util.ArrayList;
98
import java.util.List;
109

1110
/**
@@ -17,33 +16,33 @@
1716
public interface MultiscalesMetadataImage<M> extends MultiscaleImage {
1817

1918
/**
20-
* Returns the raw multiscales entry at index {@code i}.
19+
* Returns the raw multiscales entry at index {@code i} — the version-specific type.
2120
*/
2221
M getMultiscalesEntry(int i) throws ZarrException;
2322

2423
/**
25-
* Creates a new scale level array at {@code path} with the given metadata and coordinate transformations,
26-
* then registers it in the multiscales metadata.
24+
* Creates a new scale level array at {@code path} with the given metadata and coordinate
25+
* transformations, then registers it in the multiscales metadata.
2726
*/
2827
void createScaleLevel(
2928
String path,
3029
dev.zarr.zarrjava.core.ArrayMetadata arrayMetadata,
3130
List<CoordinateTransformation> coordinateTransformations
3231
) throws IOException, ZarrException;
3332

33+
/**
34+
* Default implementation: casts the version-specific entry to the shared {@link MultiscalesEntry}.
35+
* Versions whose entry type does not extend {@link MultiscalesEntry} (e.g., v0.6, v1.0) must
36+
* override {@link #getMultiscaleNode(int)} directly.
37+
*/
3438
@Override
35-
default UnifiedMultiscaleNode getMultiscaleNode(int i) throws ZarrException {
39+
default MultiscalesEntry getMultiscaleNode(int i) throws ZarrException {
3640
Object entry = getMultiscalesEntry(i);
3741
if (!(entry instanceof MultiscalesEntry)) {
3842
throw new ZarrException(
3943
"getMultiscaleNode() not supported for entry type " + entry.getClass().getName()
40-
+ "; override getMultiscaleNode() in your MultiscalesMetadataImage implementation.");
41-
}
42-
MultiscalesEntry mse = (MultiscalesEntry) entry;
43-
List<UnifiedSinglescaleNode> nodes = new ArrayList<>();
44-
for (dev.zarr.zarrjava.ome.metadata.Dataset dataset : mse.datasets) {
45-
nodes.add(new UnifiedSinglescaleNode(dataset.path, dataset.coordinateTransformations));
44+
+ "; override getMultiscaleNode() in your implementation.");
4645
}
47-
return new UnifiedMultiscaleNode(mse.name, mse.axes, nodes);
46+
return (MultiscalesEntry) entry;
4847
}
4948
}
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
package dev.zarr.zarrjava.ome;
2+
3+
import com.fasterxml.jackson.core.type.TypeReference;
4+
import dev.zarr.zarrjava.ZarrException;
5+
import dev.zarr.zarrjava.core.Attributes;
6+
import dev.zarr.zarrjava.store.StoreHandle;
7+
import dev.zarr.zarrjava.v2.Group;
8+
import dev.zarr.zarrjava.v2.GroupMetadata;
9+
10+
import javax.annotation.Nonnull;
11+
12+
/**
13+
* Base class for all OME-Zarr nodes backed by a Zarr v2 group.
14+
*
15+
* <p>Provides {@code protected static} helpers for reading attributes and building
16+
* {@link Attributes} for writing. The actual byte serialization is performed by
17+
* {@link dev.zarr.zarrjava.v2.Node#makeObjectWriter()} inside {@code Group.create()} and
18+
* {@code Group.setAttributes()}.
19+
*/
20+
public abstract class OmeV2Group extends Group {
21+
22+
protected OmeV2Group(@Nonnull StoreHandle storeHandle, @Nonnull GroupMetadata groupMetadata) {
23+
super(storeHandle, groupMetadata);
24+
}
25+
26+
/** Reads and converts a named attribute value from the given v2 group's attributes. */
27+
protected static <T> T readAttribute(
28+
Attributes attributes, StoreHandle storeHandle, String key, Class<T> cls)
29+
throws ZarrException {
30+
if (attributes == null || !attributes.containsKey(key)) {
31+
throw new ZarrException("No '" + key + "' key found in attributes at " + storeHandle);
32+
}
33+
return dev.zarr.zarrjava.v2.Node.makeObjectMapper().convertValue(attributes.get(key), cls);
34+
}
35+
36+
/** Reads and converts a named attribute using a {@link TypeReference} (e.g. for {@code List<T>}). */
37+
protected static <T> T readTypedAttribute(
38+
Attributes attributes, StoreHandle storeHandle, String key, TypeReference<T> typeRef)
39+
throws ZarrException {
40+
if (attributes == null || !attributes.containsKey(key)) {
41+
throw new ZarrException("No '" + key + "' key found in attributes at " + storeHandle);
42+
}
43+
return dev.zarr.zarrjava.v2.Node.makeObjectMapper().convertValue(attributes.get(key), typeRef);
44+
}
45+
46+
/**
47+
* Builds {@link Attributes} containing {@code {key: <serialized value>}}, ready to
48+
* pass to {@code Group.create()} or {@code Group.setAttributes()}.
49+
*/
50+
protected static Attributes buildAttributes(String key, Object value) {
51+
Object serialized = dev.zarr.zarrjava.v2.Node.makeObjectMapper()
52+
.convertValue(value, Object.class);
53+
Attributes attrs = new Attributes();
54+
attrs.put(key, serialized);
55+
return attrs;
56+
}
57+
58+
/** Serializes {@code value} via the v2 mapper to a plain Java object (Map/List/primitive). */
59+
protected static Object serialize(Object value) {
60+
return dev.zarr.zarrjava.v2.Node.makeObjectMapper().convertValue(value, Object.class);
61+
}
62+
}
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
package dev.zarr.zarrjava.ome;
2+
3+
import com.fasterxml.jackson.core.type.TypeReference;
4+
import dev.zarr.zarrjava.ZarrException;
5+
import dev.zarr.zarrjava.core.Attributes;
6+
import dev.zarr.zarrjava.store.StoreHandle;
7+
import dev.zarr.zarrjava.v3.Group;
8+
import dev.zarr.zarrjava.v3.GroupMetadata;
9+
10+
import javax.annotation.Nonnull;
11+
import java.io.IOException;
12+
13+
/**
14+
* Base class for all OME-Zarr nodes backed by a Zarr v3 group.
15+
*
16+
* <p>Provides {@code protected static} helpers for reading OME attributes and building
17+
* {@link Attributes} for writing. The actual byte serialization is performed by
18+
* {@link dev.zarr.zarrjava.v3.Node#makeObjectWriter()} inside {@code Group.create()} and
19+
* {@code Group.setAttributes()}.
20+
*/
21+
public abstract class OmeV3Group extends Group {
22+
23+
protected OmeV3Group(@Nonnull StoreHandle storeHandle, @Nonnull GroupMetadata groupMetadata)
24+
throws IOException {
25+
super(storeHandle, groupMetadata);
26+
}
27+
28+
/** Reads and converts the {@code "ome"} attribute value from the given group's attributes. */
29+
protected static <T> T readOmeAttribute(
30+
Attributes attributes, StoreHandle storeHandle, Class<T> cls) throws ZarrException {
31+
if (attributes == null || !attributes.containsKey("ome")) {
32+
throw new ZarrException("No 'ome' key found in attributes at " + storeHandle);
33+
}
34+
return dev.zarr.zarrjava.v3.Node.makeObjectMapper().convertValue(attributes.get("ome"), cls);
35+
}
36+
37+
/**
38+
* Builds {@link Attributes} containing {@code {"ome": <serialized omeMetadata>}}, ready to
39+
* pass to {@code Group.create()} or {@code Group.setAttributes()}.
40+
*/
41+
protected static Attributes omeAttributes(Object omeMetadata) {
42+
Object serialized = dev.zarr.zarrjava.v3.Node.makeObjectMapper()
43+
.convertValue(omeMetadata, Object.class);
44+
Attributes attrs = new Attributes();
45+
attrs.put("ome", serialized);
46+
return attrs;
47+
}
48+
}

src/main/java/dev/zarr/zarrjava/ome/UnifiedMultiscaleNode.java

Lines changed: 0 additions & 21 deletions
This file was deleted.

src/main/java/dev/zarr/zarrjava/ome/UnifiedSinglescaleNode.java

Lines changed: 0 additions & 17 deletions
This file was deleted.

src/main/java/dev/zarr/zarrjava/ome/v0_4/MultiscaleImage.java

Lines changed: 10 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
package dev.zarr.zarrjava.ome.v0_4;
22

33
import com.fasterxml.jackson.core.type.TypeReference;
4-
import com.fasterxml.jackson.databind.ObjectMapper;
54
import dev.zarr.zarrjava.ZarrException;
65
import dev.zarr.zarrjava.core.Attributes;
6+
import dev.zarr.zarrjava.ome.OmeV2Group;
77
import dev.zarr.zarrjava.ome.MultiscalesMetadataImage;
88
import dev.zarr.zarrjava.ome.metadata.CoordinateTransformation;
99
import dev.zarr.zarrjava.ome.metadata.Dataset;
@@ -20,14 +20,11 @@
2020
import java.util.ArrayList;
2121
import java.util.Collections;
2222
import java.util.List;
23-
import java.util.Map;
24-
25-
import static dev.zarr.zarrjava.v2.Node.makeObjectMapper;
2623

2724
/**
2825
* OME-Zarr v0.4 multiscale image backed by a Zarr v2 group.
2926
*/
30-
public final class MultiscaleImage extends Group implements MultiscalesMetadataImage<MultiscalesEntry> {
27+
public final class MultiscaleImage extends OmeV2Group implements MultiscalesMetadataImage<MultiscalesEntry> {
3128

3229
private List<MultiscalesEntry> multiscales;
3330
@Nullable
@@ -53,19 +50,12 @@ private MultiscaleImage(
5350
*/
5451
public static MultiscaleImage openMultiscaleImage(@Nonnull StoreHandle storeHandle) throws IOException, ZarrException {
5552
Group group = Group.open(storeHandle);
56-
ObjectMapper mapper = makeObjectMapper();
5753
Attributes attributes = group.metadata.attributes;
58-
if (attributes == null || !attributes.containsKey("multiscales")) {
59-
throw new ZarrException("No 'multiscales' key found in attributes at " + storeHandle);
60-
}
61-
List<MultiscalesEntry> multiscales = mapper.convertValue(
62-
attributes.get("multiscales"),
63-
new TypeReference<List<MultiscalesEntry>>() {}
64-
);
65-
OmeroMetadata omeroMetadata = null;
66-
if (attributes.containsKey("omero")) {
67-
omeroMetadata = mapper.convertValue(attributes.get("omero"), OmeroMetadata.class);
68-
}
54+
List<MultiscalesEntry> multiscales = readTypedAttribute(
55+
attributes, storeHandle, "multiscales", new TypeReference<List<MultiscalesEntry>>() {});
56+
OmeroMetadata omeroMetadata = attributes.containsKey("omero")
57+
? readAttribute(attributes, storeHandle, "omero", OmeroMetadata.class)
58+
: null;
6959
Integer bioformats2rawLayout = null;
7060
if (attributes.containsKey("bioformats2raw.layout")) {
7161
Object raw = attributes.get("bioformats2raw.layout");
@@ -83,13 +73,8 @@ public static MultiscaleImage create(
8373
@Nonnull StoreHandle storeHandle,
8474
@Nonnull MultiscalesEntry multiscalesEntry
8575
) throws IOException, ZarrException {
86-
ObjectMapper mapper = makeObjectMapper();
8776
List<MultiscalesEntry> multiscales = Collections.singletonList(multiscalesEntry);
88-
@SuppressWarnings("unchecked")
89-
List<Object> multiscalesList = mapper.convertValue(multiscales, List.class);
90-
Attributes attributes = new Attributes();
91-
attributes.put("multiscales", multiscalesList);
92-
Group group = Group.create(storeHandle, attributes);
77+
Group group = Group.create(storeHandle, buildAttributes("multiscales", multiscales));
9378
return new MultiscaleImage(storeHandle, group.metadata, multiscales, null, null);
9479
}
9580

@@ -150,15 +135,9 @@ public void createScaleLevel(
150135
}
151136

152137
private void persistAttributes() throws IOException, ZarrException {
153-
ObjectMapper mapper = makeObjectMapper();
154-
@SuppressWarnings("unchecked")
155-
List<Object> multiscalesList = mapper.convertValue(multiscales, List.class);
156-
Attributes newAttributes = new Attributes();
157-
newAttributes.put("multiscales", multiscalesList);
138+
Attributes newAttributes = buildAttributes("multiscales", multiscales);
158139
if (omeroMetadata != null) {
159-
@SuppressWarnings("unchecked")
160-
Map<String, Object> omeroMap = mapper.convertValue(omeroMetadata, Map.class);
161-
newAttributes.put("omero", omeroMap);
140+
newAttributes.put("omero", serialize(omeroMetadata));
162141
}
163142
if (bioformats2rawLayout != null) {
164143
newAttributes.put("bioformats2raw.layout", bioformats2rawLayout);

src/main/java/dev/zarr/zarrjava/ome/v0_4/Plate.java

Lines changed: 5 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,19 @@
11
package dev.zarr.zarrjava.ome.v0_4;
22

3-
import com.fasterxml.jackson.databind.ObjectMapper;
43
import dev.zarr.zarrjava.ZarrException;
5-
import dev.zarr.zarrjava.core.Attributes;
4+
import dev.zarr.zarrjava.ome.OmeV2Group;
65
import dev.zarr.zarrjava.ome.metadata.PlateMetadata;
76
import dev.zarr.zarrjava.store.StoreHandle;
87
import dev.zarr.zarrjava.v2.Group;
98
import dev.zarr.zarrjava.v2.GroupMetadata;
109

1110
import javax.annotation.Nonnull;
1211
import java.io.IOException;
13-
import java.util.Map;
14-
15-
import static dev.zarr.zarrjava.v2.Node.makeObjectMapper;
1612

1713
/**
1814
* OME-Zarr v0.4 HCS plate backed by a Zarr v2 group.
1915
*/
20-
public final class Plate extends Group implements dev.zarr.zarrjava.ome.Plate {
16+
public final class Plate extends OmeV2Group implements dev.zarr.zarrjava.ome.Plate {
2117

2218
private PlateMetadata plateMetadata;
2319

@@ -35,12 +31,8 @@ private Plate(
3531
*/
3632
public static Plate openPlate(@Nonnull StoreHandle storeHandle) throws IOException, ZarrException {
3733
Group group = Group.open(storeHandle);
38-
ObjectMapper mapper = makeObjectMapper();
39-
Attributes attributes = group.metadata.attributes;
40-
if (attributes == null || !attributes.containsKey("plate")) {
41-
throw new ZarrException("No 'plate' key found in attributes at " + storeHandle);
42-
}
43-
PlateMetadata plateMetadata = mapper.convertValue(attributes.get("plate"), PlateMetadata.class);
34+
PlateMetadata plateMetadata = readAttribute(
35+
group.metadata.attributes, storeHandle, "plate", PlateMetadata.class);
4436
return new Plate(storeHandle, group.metadata, plateMetadata);
4537
}
4638

@@ -51,12 +43,7 @@ public static Plate createPlate(
5143
@Nonnull StoreHandle storeHandle,
5244
@Nonnull PlateMetadata plateMetadata
5345
) throws IOException, ZarrException {
54-
ObjectMapper mapper = makeObjectMapper();
55-
@SuppressWarnings("unchecked")
56-
Map<String, Object> plateMap = mapper.convertValue(plateMetadata, Map.class);
57-
Attributes attributes = new Attributes();
58-
attributes.put("plate", plateMap);
59-
Group group = Group.create(storeHandle, attributes);
46+
Group group = Group.create(storeHandle, buildAttributes("plate", plateMetadata));
6047
return new Plate(storeHandle, group.metadata, plateMetadata);
6148
}
6249

0 commit comments

Comments
 (0)