diff --git a/src/main/java/fr/igred/omero/AnnotatableWrapper.java b/src/main/java/fr/igred/omero/AnnotatableWrapper.java index d5a0a239..d7a81f27 100644 --- a/src/main/java/fr/igred/omero/AnnotatableWrapper.java +++ b/src/main/java/fr/igred/omero/AnnotatableWrapper.java @@ -19,6 +19,7 @@ import fr.igred.omero.annotations.Annotation; +import fr.igred.omero.annotations.AnnotationsWrapper; import fr.igred.omero.annotations.FileAnnotation; import fr.igred.omero.annotations.FileAnnotationWrapper; import fr.igred.omero.annotations.MapAnnotation; @@ -552,7 +553,7 @@ protected void removeLink(C client, @Override public List getAnnotations(BasicBrowser browser) throws AccessException, ServiceException, ExecutionException { - return wrap(getAnnotationData(browser), Wrapper::wrap); + return wrap(getAnnotationData(browser), AnnotationsWrapper::wrap); } } diff --git a/src/main/java/fr/igred/omero/Wrapper.java b/src/main/java/fr/igred/omero/Wrapper.java index 55fad07c..b77f8634 100644 --- a/src/main/java/fr/igred/omero/Wrapper.java +++ b/src/main/java/fr/igred/omero/Wrapper.java @@ -18,12 +18,7 @@ package fr.igred.omero; -import fr.igred.omero.annotations.Annotation; -import fr.igred.omero.annotations.FileAnnotationWrapper; -import fr.igred.omero.annotations.MapAnnotationWrapper; -import fr.igred.omero.annotations.RatingAnnotationWrapper; -import fr.igred.omero.annotations.TagAnnotationWrapper; -import fr.igred.omero.annotations.TextualAnnotationWrapper; +import fr.igred.omero.annotations.AnnotationsWrapper; import fr.igred.omero.containers.DatasetWrapper; import fr.igred.omero.containers.FolderWrapper; import fr.igred.omero.containers.ProjectWrapper; @@ -33,22 +28,31 @@ import fr.igred.omero.core.PlaneInfoWrapper; import fr.igred.omero.meta.ExperimenterWrapper; import fr.igred.omero.meta.GroupWrapper; -import fr.igred.omero.roi.EllipseWrapper; -import fr.igred.omero.roi.LineWrapper; -import fr.igred.omero.roi.MaskWrapper; -import fr.igred.omero.roi.PointWrapper; -import fr.igred.omero.roi.PolygonWrapper; -import fr.igred.omero.roi.PolylineWrapper; import fr.igred.omero.roi.ROIWrapper; -import fr.igred.omero.roi.RectangleWrapper; -import fr.igred.omero.roi.Shape; -import fr.igred.omero.roi.TextWrapper; +import fr.igred.omero.roi.ShapesWrapper; import fr.igred.omero.screen.PlateAcquisitionWrapper; import fr.igred.omero.screen.PlateWrapper; import fr.igred.omero.screen.ScreenWrapper; import fr.igred.omero.screen.WellSampleWrapper; import fr.igred.omero.screen.WellWrapper; -import omero.gateway.model.*; +import omero.gateway.model.AnnotationData; +import omero.gateway.model.ChannelData; +import omero.gateway.model.DataObject; +import omero.gateway.model.DatasetData; +import omero.gateway.model.ExperimenterData; +import omero.gateway.model.FolderData; +import omero.gateway.model.GroupData; +import omero.gateway.model.ImageData; +import omero.gateway.model.PixelsData; +import omero.gateway.model.PlaneInfoData; +import omero.gateway.model.PlateAcquisitionData; +import omero.gateway.model.PlateData; +import omero.gateway.model.ProjectData; +import omero.gateway.model.ROIData; +import omero.gateway.model.ScreenData; +import omero.gateway.model.ShapeData; +import omero.gateway.model.WellData; +import omero.gateway.model.WellSampleData; import java.util.function.Function; @@ -59,73 +63,85 @@ * Utility class to convert DataObjects dynamically. */ public enum Wrapper { - /** Containers */ + /** + * Shapes + */ + SHAPE(ShapeData.class, ShapesWrapper::wrap), + /** + * Annotations + */ + ANNOTATION(AnnotationData.class, AnnotationsWrapper::wrap), + /** + * Containers + */ FOLDER(FolderData.class, FolderWrapper::new), PROJECT(ProjectData.class, ProjectWrapper::new), DATASET(DatasetData.class, DatasetWrapper::new), - /** Screen */ + /** + * Screen + */ SCREEN(ScreenData.class, ScreenWrapper::new), PLATE(PlateData.class, PlateWrapper::new), PLATEACQUISITION(PlateAcquisitionData.class, PlateAcquisitionWrapper::new), WELLSAMPLE(WellSampleData.class, WellSampleWrapper::new), WELL(WellData.class, WellWrapper::new), - /** Core */ + /** + * Core + */ IMAGE(ImageData.class, ImageWrapper::new), PIXELS(PixelsData.class, PixelsWrapper::new), PLANEINFO(PlaneInfoData.class, PlaneInfoWrapper::new), CHANNEL(ChannelData.class, ChannelWrapper::new), - /** ROI */ + /** + * ROI + */ ROI(ROIData.class, ROIWrapper::new), - /** Shapes */ - RECTANGLE(RectangleData.class, RectangleWrapper::new), - POLYGON(PolygonData.class, PolygonWrapper::new), - POLYLINE(PolylineData.class, PolylineWrapper::new), - ELLIPSE(EllipseData.class, EllipseWrapper::new), - POINT(PointData.class, PointWrapper::new), - LINE(LineData.class, LineWrapper::new), - TEXT(TextData.class, TextWrapper::new), - MASK(MaskData.class, MaskWrapper::new), - /** Annotations */ - FILEANNOTATION(FileAnnotationData.class, FileAnnotationWrapper::new), - MAPANNOTATION(MapAnnotationData.class, MapAnnotationWrapper::new), - TAGANNOTATION(TagAnnotationData.class, TagAnnotationWrapper::new), - RATINGANNOTATION(RatingAnnotationData.class, RatingAnnotationWrapper::new), - TEXTUALANNOTATION(TextualAnnotationData.class, TextualAnnotationWrapper::new), - /** Meta */ + /** + * Meta + */ EXPERIMENTER(ExperimenterData.class, ExperimenterWrapper::new), GROUP(GroupData.class, GroupWrapper::new); - /** Error message for unknown type. */ + /** + * Error message for unknown type. + */ private static final String UNKNOWN_TYPE = "Unknown type: %s"; - /** The converter to convert the DataObject to a RemoteObject. */ + /** + * The converter to convert the DataObject to a RemoteObject. + */ private final Converter converter; - /** Constructor of the Wrappers enum. */ - Wrapper(Class klazz, Function mapper) { + /** + * Constructor of the Wrappers enum. + */ + > + Wrapper(Class klazz, Function mapper) { converter = new Converter<>(klazz, mapper); } - /** * Returns the Wrapper enum from a given class. * - * @param klazz The class to get the Wrapper enum from. + * @param object The object to convert. * * @return See above. */ - private static Wrapper fromClass(Class klazz) { + private static Wrapper fromObject(DataObject object) { for (Wrapper c : values()) { - if (c.converter.getKlazz().equals(klazz)) { + if (c.isWrapperFor(object)) { return c; } } - String msg = format(UNKNOWN_TYPE, klazz.getName()); - throw new IllegalArgumentException(msg); + if (object == null) { + throw new IllegalArgumentException("Object is null"); + } else { + String msg = format(UNKNOWN_TYPE, object.getClass().getName()); + throw new IllegalArgumentException(msg); + } } - /** * Returns the converter to convert the DataObject to a RemoteObject. * @@ -135,64 +151,43 @@ private static Wrapper fromClass(Class klazz) { * * @return See above. */ - private static + private static > Converter getConverter(T object) { - Wrapper c = fromClass(object.getClass()); + Wrapper c = fromObject(object); //noinspection unchecked return (Converter) c.converter; } - /** - * Converts (wraps) a ShapeData object to a Shape object. + * Converts (wraps) a DataObject to a ObjectWrapper. * * @param object The object to convert. - * @param The ShapeData type. - * @param The Shape type. + * @param The DataObject type. + * @param The RemoteObject type. * * @return See above. */ - public static U wrap(T object) { + public static > U wrap(T object) { Converter converter = getConverter(object); return converter.convert(object); } - /** - * Converts (wraps) an AnnotationData object to an Annotation object. + * Returns whether the provided object can be wrapped by the current wrapper. * - * @param object The object to convert. - * @param The AnnotationData type. + * @param target The object to compare. * * @return See above. */ - public static Annotation wrap(T object) { - Converter converter = getConverter(object); - return converter.convert(object); + boolean isWrapperFor(DataObject target) { + return converter.canConvert(target); } - - /** - * Converts (wraps) a DataObject to a ObjectWrapper. - * - * @param object The object to convert. - * @param The DataObject type. - * @param The RemoteObject type. - * - * @return See above. - */ - public static U wrap(T object) { - Converter converter = getConverter(object); - return converter.convert(object); - } - - @Override public String toString() { return format("Wrapper{%s}", name()); } - /** * Converter class to convert DataObjects to RemoteObjects. * @@ -201,13 +196,16 @@ public String toString() { */ private static class Converter { - /** The class of the DataObject. */ + /** + * The class of the DataObject. + */ private final Class klazz; - /** The function to convert the DataObject to a RemoteObject. */ + /** + * The function to convert the DataObject to a RemoteObject. + */ private final Function mapper; - /** * Constructor of the Converter class. * @@ -219,7 +217,6 @@ private static class Converter { this.klazz = klazz; } - /** * Converts the DataObject to a RemoteObject. * @@ -231,14 +228,15 @@ U convert(T object) { return mapper.apply(object); } - /** - * Returns the class of the DataObject. + * Returns whether the provided object can be converted. + * + * @param target The object to compare. * * @return See above. */ - Class getKlazz() { - return klazz; + boolean canConvert(DataObject target) { + return this.klazz.isInstance(target); } } diff --git a/src/main/java/fr/igred/omero/annotations/AnnotationsWrapper.java b/src/main/java/fr/igred/omero/annotations/AnnotationsWrapper.java new file mode 100644 index 00000000..1d066879 --- /dev/null +++ b/src/main/java/fr/igred/omero/annotations/AnnotationsWrapper.java @@ -0,0 +1,163 @@ +/* + * Copyright (C) 2020-2024 GReD + * + * This program is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free Software + * Foundation; either version 2 of the License, or (at your option) any later + * version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., 51 Franklin + * Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +package fr.igred.omero.annotations; + + +import omero.gateway.model.AnnotationData; +import omero.gateway.model.FileAnnotationData; +import omero.gateway.model.MapAnnotationData; +import omero.gateway.model.RatingAnnotationData; +import omero.gateway.model.TagAnnotationData; +import omero.gateway.model.TextualAnnotationData; + +import java.util.function.Function; + +import static java.lang.String.format; + + +/** + * Utility class to convert DataObjects dynamically. + */ +public enum AnnotationsWrapper { + /** Annotations */ + FILEANNOTATION(FileAnnotationData.class, FileAnnotationWrapper::new), + MAPANNOTATION(MapAnnotationData.class, MapAnnotationWrapper::new), + TAGANNOTATION(TagAnnotationData.class, TagAnnotationWrapper::new), + RATINGANNOTATION(RatingAnnotationData.class, RatingAnnotationWrapper::new), + TEXTUALANNOTATION(TextualAnnotationData.class, TextualAnnotationWrapper::new); + + /** Error message for unknown type. */ + private static final String UNKNOWN_TYPE = "Unknown type: %s"; + + /** The converter to convert the DataObject to a RemoteObject. */ + private final AnnotationConverter> converter; + + + /** Constructor of the Wrappers enum. */ + > + AnnotationsWrapper(Class klazz, Function mapper) { + converter = new AnnotationConverter<>(klazz, mapper); + } + + + /** + * Returns the Wrapper enum from a given class. + * + * @param klazz The class to get the Wrapper enum from. + * + * @return See above. + */ + private static AnnotationsWrapper fromClass(Class klazz) { + for (AnnotationsWrapper c : values()) { + if (c.converter.getKlazz().equals(klazz)) { + return c; + } + } + String msg = format(UNKNOWN_TYPE, klazz.getName()); + throw new IllegalArgumentException(msg); + } + + + /** + * Returns the converter to convert the DataObject to a RemoteObject. + * + * @param object The DataObject to convert. + * @param The DataObject type. + * @param The RemoteObject type. + * + * @return See above. + */ + private static > + AnnotationConverter getConverter(T object) { + AnnotationsWrapper c = fromClass(object.getClass()); + //noinspection unchecked + return (AnnotationConverter) c.converter; + } + + + /** + * Converts (wraps) a ShapeData object to a Shape object. + * + * @param object The object to convert. + * @param The ShapeData type. + * @param The Shape type. + * + * @return See above. + */ + public static > U wrap(T object) { + AnnotationConverter converter = getConverter(object); + return converter.convert(object); + } + + + @Override + public String toString() { + return format("AnnotationsWrapper{%s}", name()); + } + + + /** + * AnnotationConverter class to convert ShapeData to Shapes. + * + * @param The DataObject type. + * @param The RemoteObject type. + */ + private static class AnnotationConverter> { + + /** The class of the DataObject. */ + private final Class klazz; + + /** The function to convert the DataObject to a RemoteObject. */ + private final Function mapper; + + + /** + * Constructor of the AnnotationConverter class. + * + * @param klazz The class of the DataObject. + * @param mapper The function to convert the DataObject to a RemoteObject. + */ + AnnotationConverter(Class klazz, Function mapper) { + this.mapper = mapper; + this.klazz = klazz; + } + + + /** + * Converts the DataObject to a RemoteObject. + * + * @param object The DataObject to convert. + * + * @return See above. + */ + U convert(T object) { + return mapper.apply(object); + } + + + /** + * Returns the class of the DataObject. + * + * @return See above. + */ + Class getKlazz() { + return klazz; + } + + } +} diff --git a/src/main/java/fr/igred/omero/roi/ROIWrapper.java b/src/main/java/fr/igred/omero/roi/ROIWrapper.java index 85b69558..2e200ef3 100644 --- a/src/main/java/fr/igred/omero/roi/ROIWrapper.java +++ b/src/main/java/fr/igred/omero/roi/ROIWrapper.java @@ -23,7 +23,6 @@ import fr.igred.omero.core.Image; import fr.igred.omero.exception.AccessException; import fr.igred.omero.exception.ServiceException; -import fr.igred.omero.Wrapper; import ij.gui.PointRoi; import ij.gui.ShapeRoi; import omero.RString; @@ -223,7 +222,7 @@ public void addShape(Shape shape) { */ @Override public List getShapes() { - return wrap(data.getShapes(), Wrapper::wrap); + return wrap(data.getShapes(), ShapesWrapper::wrap); } diff --git a/src/main/java/fr/igred/omero/roi/ShapesWrapper.java b/src/main/java/fr/igred/omero/roi/ShapesWrapper.java new file mode 100644 index 00000000..82b8ec99 --- /dev/null +++ b/src/main/java/fr/igred/omero/roi/ShapesWrapper.java @@ -0,0 +1,170 @@ +/* + * Copyright (C) 2020-2024 GReD + * + * This program is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free Software + * Foundation; either version 2 of the License, or (at your option) any later + * version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., 51 Franklin + * Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +package fr.igred.omero.roi; + + +import omero.gateway.model.DataObject; +import omero.gateway.model.EllipseData; +import omero.gateway.model.LineData; +import omero.gateway.model.MaskData; +import omero.gateway.model.PointData; +import omero.gateway.model.PolygonData; +import omero.gateway.model.PolylineData; +import omero.gateway.model.RectangleData; +import omero.gateway.model.ShapeData; +import omero.gateway.model.TextData; + +import java.util.function.Function; + +import static java.lang.String.format; + + +/** + * Utility class to convert DataObjects dynamically. + */ +public enum ShapesWrapper { + /** Shapes */ + MASK(MaskData.class, MaskWrapper::new), + TEXT(TextData.class, TextWrapper::new), + POLYGON(PolygonData.class, PolygonWrapper::new), + POLYLINE(PolylineData.class, PolylineWrapper::new), + ELLIPSE(EllipseData.class, EllipseWrapper::new), + LINE(LineData.class, LineWrapper::new), + RECTANGLE(RectangleData.class, RectangleWrapper::new), + POINT(PointData.class, PointWrapper::new); + + /** Error message for unknown type. */ + private static final String UNKNOWN_TYPE = "Unknown type: %s"; + + /** The converter to convert the DataObject to a RemoteObject. */ + private final ShapeConverter> converter; + + + /** Constructor of the Wrappers enum. */ + > + ShapesWrapper(Class klazz, Function mapper) { + converter = new ShapeConverter<>(klazz, mapper); + } + + + /** + * Returns the Wrapper enum from a given class. + * + * @param klazz The class to get the Wrapper enum from. + * + * @return See above. + */ + private static ShapesWrapper fromClass(Class klazz) { + for (ShapesWrapper c : values()) { + if (c.converter.getKlazz().equals(klazz)) { + return c; + } + } + String msg = format(UNKNOWN_TYPE, klazz.getName()); + throw new IllegalArgumentException(msg); + } + + + /** + * Returns the converter to convert the DataObject to a RemoteObject. + * + * @param object The DataObject to convert. + * @param The DataObject type. + * @param The RemoteObject type. + * + * @return See above. + */ + private static > + ShapeConverter getConverter(T object) { + ShapesWrapper c = fromClass(object.getClass()); + //noinspection unchecked + return (ShapeConverter) c.converter; + } + + + /** + * Converts (wraps) a ShapeData object to a Shape object. + * + * @param object The object to convert. + * @param The ShapeData type. + * @param The Shape type. + * + * @return See above. + */ + public static > U wrap(T object) { + ShapeConverter converter = getConverter(object); + return converter.convert(object); + } + + + @Override + public String toString() { + return format("ShapesWrapper{%s}", name()); + } + + + /** + * ShapeConverter class to convert ShapeData to Shapes. + * + * @param The DataObject type. + * @param The RemoteObject type. + */ + private static class ShapeConverter> { + + /** The class of the DataObject. */ + private final Class klazz; + + /** The function to convert the DataObject to a RemoteObject. */ + private final Function mapper; + + + /** + * Constructor of the ShapeConverter class. + * + * @param klazz The class of the DataObject. + * @param mapper The function to convert the DataObject to a RemoteObject. + */ + ShapeConverter(Class klazz, Function mapper) { + this.mapper = mapper; + this.klazz = klazz; + } + + + /** + * Converts the DataObject to a RemoteObject. + * + * @param object The DataObject to convert. + * + * @return See above. + */ + U convert(T object) { + return mapper.apply(object); + } + + + /** + * Returns the class of the DataObject. + * + * @return See above. + */ + Class getKlazz() { + return klazz; + } + + } +} diff --git a/src/test/java/fr/igred/omero/WrapperTest.java b/src/test/java/fr/igred/omero/WrapperTest.java index c51cc644..e92399cf 100644 --- a/src/test/java/fr/igred/omero/WrapperTest.java +++ b/src/test/java/fr/igred/omero/WrapperTest.java @@ -18,6 +18,7 @@ package fr.igred.omero; +import fr.igred.omero.annotations.AnnotationsWrapper; import fr.igred.omero.annotations.FileAnnotationWrapper; import fr.igred.omero.annotations.MapAnnotationWrapper; import fr.igred.omero.annotations.RatingAnnotationWrapper; @@ -40,6 +41,7 @@ import fr.igred.omero.roi.PolylineWrapper; import fr.igred.omero.roi.ROIWrapper; import fr.igred.omero.roi.RectangleWrapper; +import fr.igred.omero.roi.ShapesWrapper; import fr.igred.omero.roi.TextWrapper; import fr.igred.omero.screen.PlateAcquisitionWrapper; import fr.igred.omero.screen.PlateWrapper; @@ -106,14 +108,17 @@ private static Stream classes() { //arguments(named("FileAnnotationData", FileAnnotationData.class, FileAnnotationWrapper.class), //arguments(named("TagAnnotationData", TagAnnotationData.class, TagAnnotationWrapper.class), //arguments(named("TextualAnnotationData", TextualAnnotationData.class), TextualAnnotationWrapper.class), - arguments(named("RatingAnnotationData", RatingAnnotationData.class), RatingAnnotationWrapper.class), - arguments(named("MapAnnotationData", MapAnnotationData.class), MapAnnotationWrapper.class), + arguments(named("RatingAnnotationData", RatingAnnotationData.class), + RatingAnnotationWrapper.class), + arguments(named("MapAnnotationData", MapAnnotationData.class), + MapAnnotationWrapper.class), arguments(named("ProjectData", ProjectData.class), ProjectWrapper.class), arguments(named("DatasetData", DatasetData.class), DatasetWrapper.class), arguments(named("ImageData", ImageData.class), ImageWrapper.class), arguments(named("ScreenData", ScreenData.class), ScreenWrapper.class), arguments(named("PlateData", PlateData.class), PlateWrapper.class), - arguments(named("PlateAcquisitionData", PlateAcquisitionData.class), PlateAcquisitionWrapper.class), + arguments(named("PlateAcquisitionData", PlateAcquisitionData.class), + PlateAcquisitionWrapper.class), arguments(named("WellData", WellData.class), WellWrapper.class), arguments(named("FolderData", FolderData.class), FolderWrapper.class), arguments(named("RectangleData", RectangleData.class), RectangleWrapper.class), @@ -128,7 +133,8 @@ private static Stream classes() { arguments(named("ROIData", ROIData.class), ROIWrapper.class), arguments(named("PlaneInfoData", PlaneInfoData.class), PlaneInfoWrapper.class), arguments(named("WellSampleData", WellSampleData.class), WellSampleWrapper.class), - arguments(named("ExperimenterData", ExperimenterData.class), ExperimenterWrapper.class), + arguments(named("ExperimenterData", ExperimenterData.class), + ExperimenterWrapper.class), arguments(named("GroupData", GroupData.class), GroupWrapper.class) ); } @@ -136,7 +142,8 @@ private static Stream classes() { @ParameterizedTest(name = "{0}") @MethodSource("classes") - > void testWrap(Class input, Class output) + > void testWrap(Class input, + Class output) throws Exception { T object = input.getConstructor().newInstance(); U result = wrap(object); @@ -174,6 +181,12 @@ void testWrapTextualAnnotationData() { } + @Test + void testWrapNull() { + assertThrows(IllegalArgumentException.class, () -> wrap(null)); + } + + @Test void testWrapWrongDataObject() { DataObject object = new WrongDataObject(); @@ -196,11 +209,24 @@ void testWrapWrongAnnotationData() { @Test - void testToString() { + void testToString1() { assertEquals("Wrapper{IMAGE}", Wrapper.IMAGE.toString()); } + @Test + void testToString2() { + assertEquals("ShapesWrapper{RECTANGLE}", ShapesWrapper.RECTANGLE.toString()); + } + + + @Test + void testToString3() { + assertEquals("AnnotationsWrapper{TAGANNOTATION}", + AnnotationsWrapper.TAGANNOTATION.toString()); + } + + private static class WrongDataObject extends DataObject { WrongDataObject() {