Skip to content

Package: EditableImage$DataType

EditableImage$DataType

nameinstructionbranchcomplexitylinemethod
EditableImage.DataType(String, int, int)
M: 0 C: 8
100%
M: 0 C: 0
100%
M: 0 C: 1
100%
M: 0 C: 1
100%
M: 0 C: 1
100%
getSize()
M: 0 C: 4
100%
M: 0 C: 0
100%
M: 0 C: 1
100%
M: 0 C: 1
100%
M: 0 C: 1
100%
getValue()
M: 3 C: 0
0%
M: 0 C: 0
100%
M: 1 C: 0
0%
M: 1 C: 0
0%
M: 1 C: 0
0%
static {...}
M: 0 C: 81
100%
M: 0 C: 0
100%
M: 0 C: 1
100%
M: 0 C: 8
100%
M: 0 C: 1
100%
valueOf(int)
M: 2 C: 22
92%
M: 1 C: 3
75%
M: 1 C: 2
67%
M: 1 C: 3
75%
M: 0 C: 1
100%

Coverage

1: /*
2: * *********************************************************************************************************************
3: *
4: * Mistral: open source imaging engine
5: * http://tidalwave.it/projects/mistral
6: *
7: * Copyright (C) 2003 - 2023 by Tidalwave s.a.s. (http://tidalwave.it)
8: *
9: * *********************************************************************************************************************
10: *
11: * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
12: * the License. You may obtain a copy of the License at
13: *
14: * http://www.apache.org/licenses/LICENSE-2.0
15: *
16: * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
17: * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
18: * specific language governing permissions and limitations under the License.
19: *
20: * *********************************************************************************************************************
21: *
22: * git clone https://bitbucket.org/tidalwave/mistral-src
23: * git clone https://github.com/tidalwave-it/mistral-src
24: *
25: * *********************************************************************************************************************
26: */
27: package it.tidalwave.image;
28:
29: import javax.annotation.Nonnegative;
30: import javax.annotation.Nonnull;
31: import java.time.Duration;
32: import java.time.Instant;
33: import java.util.Arrays;
34: import java.util.Collection;
35: import java.util.HashMap;
36: import java.util.List;
37: import java.util.Map;
38: import java.util.Optional;
39: import java.util.Set;
40: import java.util.TreeSet;
41: import java.io.IOException;
42: import java.io.Serializable;
43: import javax.imageio.ImageIO;
44: import javax.imageio.metadata.IIOMetadata;
45: import java.awt.color.ICC_ColorSpace;
46: import java.awt.color.ICC_Profile;
47: import java.awt.image.ColorModel;
48: import java.awt.image.DataBuffer;
49: import it.tidalwave.util.Key;
50: import it.tidalwave.util.TypeSafeMap;
51: import it.tidalwave.image.metadata.Directory;
52: import it.tidalwave.image.metadata.EXIF;
53: import it.tidalwave.image.metadata.IPTC;
54: import it.tidalwave.image.metadata.MakerNote;
55: import it.tidalwave.image.metadata.TIFF;
56: import it.tidalwave.image.metadata.XMP;
57: import it.tidalwave.image.op.AbstractCreateOp;
58: import it.tidalwave.image.op.AccessorOp;
59: import it.tidalwave.image.op.ImplementationFactoryRegistry;
60: import it.tidalwave.image.op.Operation;
61: import it.tidalwave.image.op.OperationImplementation;
62: import it.tidalwave.image.op.ReadOp;
63: import it.tidalwave.image.op.ScaleOp;
64: import lombok.Getter;
65: import lombok.RequiredArgsConstructor;
66: import lombok.ToString;
67: import lombok.extern.slf4j.Slf4j;
68:
69: /***********************************************************************************************************************
70: *
71: * An opaque class which encapsulates all the image manipulation logics, and allows the implementation of these logics
72: * to be transparently changed (e.g. by using or not JAI, etc...)
73: *
74: * @author Fabrizio Giudici
75: *
76: **********************************************************************************************************************/
77: @ToString(of = {"imageModelHolder", "attributeMapByName"}) @Slf4j
78: public final class EditableImage implements Cloneable, Serializable // Externalizable
79: {
80: /*******************************************************************************************************************
81: *
82: *
83: ******************************************************************************************************************/
84: @RequiredArgsConstructor
85: public static enum DataType
86: {
87: BYTE(DataBuffer.TYPE_BYTE),
88: UNSIGNED_SHORT(DataBuffer.TYPE_USHORT),
89: SHORT(DataBuffer.TYPE_SHORT),
90: INT(DataBuffer.TYPE_INT),
91: FLOAT(DataBuffer.TYPE_FLOAT),
92: DOUBLE(DataBuffer.TYPE_DOUBLE),
93: UNDEFINED(DataBuffer.TYPE_UNDEFINED);
94:
95: @Getter
96: private final int value;
97:
98: /***************************************************************************************************************
99: *
100: * Returns the size in bits of this data type.
101: *
102: **************************************************************************************************************/
103: @Nonnegative
104: public int getSize()
105: {
106: return DataBuffer.getDataTypeSize(value);
107: }
108:
109: /***************************************************************************************************************
110: *
111: *
112: **************************************************************************************************************/
113: @Nonnull
114: public static DataType valueOf (final int value)
115: {
116:• for (final var dataType : DataType.values())
117: {
118:• if (dataType.value == value)
119: {
120: return dataType;
121: }
122: }
123:
124: return EditableImage.DataType.UNDEFINED;
125: }
126: }
127:
128: @RequiredArgsConstructor
129: public static class Accessor // FIXME: protected
130: {
131: @Nonnull
132: private final EditableImage image;
133:
134: public void setIIOMetadata (@Nonnull final IIOMetadata iioMetadata)
135: {
136: image.iioMetadata = iioMetadata;
137: }
138:
139: @Nonnull
140: public Map<Class<? extends Directory>, List<Directory>> getMetadataMapByClass ()
141: {
142: return image.metadataMapByClass;
143: }
144:
145: public void setLatestOperationDuration (@Nonnull final Duration latestOperationDuration)
146: {
147: image.latestOperationDuration = latestOperationDuration;
148: }
149: }
150:
151: private final AccessorOp accessor = new AccessorOp(this);
152:
153: private static final long serialVersionUID = -4524534539832240717L;
154: private static final String CLASS = EditableImage.class.getName();
155:
156: public static final Key<String> PROP_FORMAT = Key.of(CLASS + ".format", String.class);
157:
158: public static final Key<String> PROP_MIME_TYPE = Key.of(CLASS + ".mimeType", String.class);
159:
160: /** The current image model. */
161: private ImageModelHolder imageModelHolder;
162:
163: /** The metadata as it comes from Image I/O. */
164: private transient IIOMetadata iioMetadata; // TODO make it serializable
165:
166: private final Map<Class<? extends Directory>, List<Directory>> metadataMapByClass = new HashMap<>();
167:
168: /** The attributes. */
169: @Nonnull
170: private TypeSafeMap attributeMapByName = TypeSafeMap.newInstance();
171:
172: private Duration latestOperationDuration;
173:
174: @Nonnegative
175: private int latestSerializationSize;
176:
177: /*******************************************************************************************************************
178: *
179: * For serialization only. Do not use.
180: *
181: ******************************************************************************************************************/
182: public EditableImage()
183: {
184: // By default put empty objects for which isAvailable() returns false
185: metadataMapByClass.put(TIFF.class, List.of(new TIFF()));
186: metadataMapByClass.put(EXIF.class, List.of(new EXIF()));
187: metadataMapByClass.put(IPTC.class, List.of(new IPTC()));
188: metadataMapByClass.put(XMP.class, List.of(new XMP()));
189: metadataMapByClass.put(MakerNote.class, List.of(new MakerNote()));
190: }
191:
192: /*******************************************************************************************************************
193: *
194: * For inner implementation only. Do not use.
195: *
196: ******************************************************************************************************************/
197: public EditableImage (final ImageModel imageModel) // FIXME: try to make it protected
198: {
199: // null imageModel is accepted for instances carrying only metadata
200: imageModelHolder = ImageModelHolder.wrap(imageModel);
201: }
202:
203: /*******************************************************************************************************************
204: *
205: *
206: ******************************************************************************************************************/
207: public void setNickName (final @Nonnull String nickName)
208: {
209: if (imageModelHolder != null)
210: {
211: imageModelHolder.setNickName(nickName);
212: }
213: }
214:
215: /*******************************************************************************************************************
216: *
217: *
218: ******************************************************************************************************************/
219: @Nonnull
220: public Optional<String> getNickName()
221: {
222: return (imageModelHolder != null) ? Optional.ofNullable(imageModelHolder.getNickName()) : Optional.empty();
223: }
224:
225: /*******************************************************************************************************************
226: *
227: * Creates a new EditableImage as specified by the parameter
228: *
229: * @param createOp the way the image should be created
230: * @return the image
231: *
232: ******************************************************************************************************************/
233: @Nonnull
234: public static EditableImage create (@Nonnull final AbstractCreateOp createOp)
235: {
236: final var editableImage = new EditableImage(null);
237: final var image = editableImage.internalExecute(createOp);
238: final var imageModel = ImplementationFactoryRegistry.getDefault().createImageModel(image);
239: editableImage.imageModelHolder = ImageModelHolder.wrap(imageModel);
240:
241: return editableImage;
242: }
243:
244: /*******************************************************************************************************************
245: *
246: * Reads a new EditableImage as specified by the parameter
247: *
248: * @param readOp the way the image should be read
249: * @return the image
250: *
251: ******************************************************************************************************************/
252: // FIXME: merge with create(AbstractCreateOp), introduce ReadJ2DOp
253: @Nonnull
254: public static EditableImage create (@Nonnull final ReadOp readOp)
255: throws IOException
256: {
257: return readOp.execute();
258: }
259:
260: /*******************************************************************************************************************
261: *
262: * Returns true if the image has a raster (EditableImages can be loaded with
263: * metadata only).
264: *
265: * @return true if the image has a raster
266: *
267: ******************************************************************************************************************/
268: public boolean hasRaster()
269: {
270: return imageModelHolder.get() != null;
271: }
272:
273: /*******************************************************************************************************************
274: *
275: * DO NOT USE THIS. This method is only used by the module implementation.
276: *
277: ******************************************************************************************************************/
278: public ImageModel getImageModel()
279: {
280: return imageModelHolder.get();
281: }
282:
283: private static boolean availableExtensionsLogged;
284:
285: /*******************************************************************************************************************
286: *
287: * Returns all the file extensions of file formats that can be read into an
288: * EditableImage. The <code>ImageIO</code> registry is called to retrieve the
289: * requested information.
290: *
291: * @return an array of all file extensions
292: *
293: ******************************************************************************************************************/
294: @Nonnull
295: public static Collection<String> getAvailableExtensions()
296: {
297: final boolean logExtensions;
298:
299: synchronized (EditableImage.class)
300: {
301: logExtensions = !availableExtensionsLogged;
302: availableExtensionsLogged = true;
303: }
304:
305: if (logExtensions)
306: {
307: log.info("getAvailableExtensions()");
308: }
309:
310: final Set<String> suffixList = new TreeSet<>();
311:
312: for (final var formatName : ImageIO.getReaderFormatNames())
313: {
314: for (final var i = ImageIO.getImageReadersByFormatName(formatName); i.hasNext(); )
315: {
316: final var imageReader = i.next();
317: final var originatingProvider = imageReader.getOriginatingProvider();
318: final var suffixes = originatingProvider.getFileSuffixes();
319: final var suffixesAsList = Arrays.asList(suffixes);
320: suffixList.addAll(suffixesAsList);
321:
322: if (logExtensions)
323: {
324: log.info(">>>> reader - format name: {} provider: {} supports {}",
325: formatName, originatingProvider.getPluginClassName(), suffixesAsList);
326: }
327: }
328: }
329:
330: if (logExtensions)
331: {
332: log.info(">>>> returning {}", suffixList);
333: }
334:
335: return suffixList;
336: }
337:
338: /*******************************************************************************************************************
339: *
340: *
341: ******************************************************************************************************************/
342: public <T extends Directory> Optional<T> getMetadata (final @Nonnull Class<T> metadataClass)
343: {
344: return getMetadata(metadataClass, 0);
345: }
346:
347: /*******************************************************************************************************************
348: *
349: * Retrieve a metadata directory.
350: *
351: * @param metadataClass the type of the directory
352: * @param index the index (in case of multiple items)
353: * @return the metadata directory
354: *
355: ******************************************************************************************************************/
356: public <T extends Directory> Optional<T> getMetadata (final @Nonnull Class<T> metadataClass, final @Nonnegative int index)
357: {
358: final var list = (List<T>)metadataMapByClass.get(metadataClass);
359: return Optional.ofNullable(list).flatMap(l -> l.isEmpty() ? Optional.empty() : Optional.of(l.get(index)));
360: }
361:
362: /*******************************************************************************************************************
363: *
364: *
365: ******************************************************************************************************************/
366: @Nonnegative
367: public int getMetadataCount (final @Nonnull Class<?> metadataClass)
368: {
369: return Optional.ofNullable(metadataMapByClass.get(metadataClass)).map(List::size).orElse(0);
370: }
371:
372: /*******************************************************************************************************************
373: *
374: * Returns the width of this image.
375: *
376: * @return the width
377: *
378: ******************************************************************************************************************/
379: @Nonnegative
380: public int getWidth()
381: {
382: return imageModelHolder.get().getWidth();
383: }
384:
385: /*******************************************************************************************************************
386: *
387: * Returns the height of this image.
388: *
389: * @return the height
390: *
391: ******************************************************************************************************************/
392: @Nonnegative
393: public int getHeight()
394: {
395: return imageModelHolder.get().getHeight();
396: }
397:
398: /*******************************************************************************************************************
399: *
400: * Returns the dataType used by this image.
401: *
402: * @return the data type
403: *
404: ******************************************************************************************************************/
405: @Nonnull
406: public DataType getDataType()
407: {
408: return imageModelHolder.get().getDataType();
409: }
410:
411: /*******************************************************************************************************************
412: *
413: * Returns the number of bands this EditableImage is composed of.
414: *
415: * @return the band count
416: *
417: ******************************************************************************************************************/
418: @Nonnegative
419: public int getBandCount()
420: {
421: return imageModelHolder.get().getBandCount();
422: }
423:
424: /*******************************************************************************************************************
425: *
426: * Returns the number of sample bits for each band this EditableImage is
427: * composed of.
428: *
429: * @return the number of bits
430: *
431: ******************************************************************************************************************/
432: @Nonnegative
433: public int getBitsPerBand()
434: {
435: return getDataType().getSize();
436: }
437:
438: /*******************************************************************************************************************
439: *
440: * Returns the number of sample bits for each pixel this EditableImage is
441: * composed of.
442: *
443: * @return the number of bits
444: *
445: ******************************************************************************************************************/
446: @Nonnegative
447: public int getBitsPerPixel()
448: {
449: return getBandCount() * getBitsPerBand();
450: }
451:
452: /*******************************************************************************************************************
453: *
454: * Executes an operation. The original image is lost and replaced by results.
455: *
456: * @param operation the operation to perform
457: * @return the operation (as a convenience in case it carries results)
458: *
459: ******************************************************************************************************************/
460: @Nonnull
461: public <T extends Operation> T executeInPlace (@Nonnull final T operation)
462: {
463: final var time = Instant.now();
464: final var image = internalExecute(operation);
465: imageModelHolder.get().setImage(image);
466: latestOperationDuration = Duration.between(time, Instant.now());
467:
468: return operation;
469: }
470:
471: /*******************************************************************************************************************
472: *
473: * Executes an operation. The original image is untouched as the results are placed in a brand-new instance of
474: * EditableImage.
475: *
476: * @param operation the operation to perform
477: * @return the result
478: *
479: ******************************************************************************************************************/
480: @Nonnull
481: public EditableImage execute (@Nonnull final Operation operation)
482: {
483: try
484: {
485: final var time = Instant.now();
486: final var image = internalExecute(operation);
487: final var modelClass = imageModelHolder.get().getClass();
488: final var constructor = modelClass.getConstructor(Object.class);
489: final var newModel = constructor.newInstance(image);
490: final var result = new EditableImage(newModel);
491: result.attributeMapByName = attributeMapByName; // immutable
492: result.latestOperationDuration = Duration.between(time, Instant.now());;
493:
494: return result;
495: }
496: catch (Exception e)
497: {
498: throw new RuntimeException(e);
499: }
500: }
501:
502: /*******************************************************************************************************************
503: *
504: * Returns the elapsed time of the latest operation performed. Note that for
505: * execute2() this value is available on the result. When an image is
506: * deserialized, this method returns the serialization time (this relies upon
507: * the fact that the clocks on all network nodes are synchronized).
508: *
509: * @return the latest operation elapsed time
510: *
511: ******************************************************************************************************************/
512: @Nonnull
513: public Duration getLatestOperationDuration()
514: {
515: return latestOperationDuration;
516: }
517:
518: /*******************************************************************************************************************
519: *
520: * Creates a similar image, that is a blank image with the same characteristics
521: * of this image (width, height, data type, color model).
522: * @deprecated will be merged with create(AbstractCreateOp)
523: *
524: * @return a new, similar image
525: *
526: ******************************************************************************************************************/
527: @Nonnull
528: public EditableImage createSimilarImage()
529: {
530: final var imageCopy = imageModelHolder.get().createCopy(false);
531: imageCopy.attributeMapByName = attributeMapByName; // immutable
532: return imageCopy;
533: }
534:
535: /*******************************************************************************************************************
536: *
537: * Clones this image.
538: *
539: ******************************************************************************************************************/
540: @Nonnull
541: public EditableImage cloneImage()
542: {
543: final var imageCopy = imageModelHolder.get().createCopy(true);
544: imageCopy.attributeMapByName = attributeMapByName; // immutable
545: return imageCopy;
546: }
547:
548: /*******************************************************************************************************************
549: *
550: * Creates a resized image. - FIXME should be removed
551: * @deprecated
552: *
553: ******************************************************************************************************************/
554: @Nonnull
555: public EditableImage createResizedImage (final @Nonnegative int width, final @Nonnegative int height)
556: {
557: return createResizedImage(width, height, Quality.FASTEST);
558: }
559:
560: /*******************************************************************************************************************
561: *
562: * Creates a resized image. - FIXME move to a factory method for ScaleOp.
563: * @deprecated
564: *
565: ******************************************************************************************************************/
566: @Nonnull
567: public EditableImage createResizedImage (final @Nonnegative int width,
568: final @Nonnegative int height,
569: final @Nonnull Quality quality)
570: {
571: final var hScale = (double)width / (double)getWidth();
572: final var vScale = (double)height / (double)getHeight();
573: final var scaleOp = new ScaleOp(hScale, vScale, quality);
574: executeInPlace(scaleOp);
575:
576: return this;
577: }
578:
579: /*******************************************************************************************************************
580: *
581: * Sets an attribute of this image. Attributes are user-specific name-value pairs.
582: *
583: * @param key the attribute name
584: * @param value the attribute value
585: *
586: ******************************************************************************************************************/
587: public <T> void setAttribute (final @Nonnull Key<T> key, final @Nonnull T value)
588: {
589: attributeMapByName = attributeMapByName.with(key, value);
590: }
591:
592: /*******************************************************************************************************************
593: *
594: * Returns an attribute of this image.
595: *
596: * @param key the attribute name
597: * @return the attribute value
598: *
599: ******************************************************************************************************************/
600: @Nonnull
601: public <T> Optional<T> getAttribute (final @Nonnull Key<T> key)
602: {
603: return attributeMapByName.getOptional(key);
604: }
605:
606: /*******************************************************************************************************************
607: *
608: *
609: ******************************************************************************************************************/
610: public void setAttributes (final @Nonnull Map<Key<?>, Object> attributes)
611: {
612: attributeMapByName = TypeSafeMap.ofCloned(attributes);
613: }
614:
615: /*******************************************************************************************************************
616: *
617: *
618: ******************************************************************************************************************/
619: @Nonnull
620: public TypeSafeMap getAttributes()
621: {
622: return attributeMapByName; // immutable
623: }
624:
625: /*******************************************************************************************************************
626: *
627: * Removes an attribute from this image.
628: *
629: * @param name the attribute name
630: * @return the attribute value
631: *
632: ******************************************************************************************************************/
633: @Nonnull
634: public <T> T removeAttribute (final @Nonnull String name)
635: {
636: throw new UnsupportedOperationException(); // FIXME: need to add TypeSafeMap.without()
637: }
638:
639: /*******************************************************************************************************************
640: *
641: * Removes all the resources bound to this image.
642: *
643: ******************************************************************************************************************/
644: public void dispose()
645: {
646: imageModelHolder.get().dispose();
647: imageModelHolder = null;
648: attributeMapByName = TypeSafeMap.newInstance();
649: }
650:
651: /*******************************************************************************************************************
652: *
653: * Returns an estimate of the memory allocated by this image.
654: *
655: * @return the memory allocated for this image
656: *
657: ******************************************************************************************************************/
658: @Nonnegative
659: public long getMemorySize()
660: {
661: final var imageModel = imageModelHolder.get();
662: return (imageModel != null) ? imageModel.getMemorySize() : 0;
663: }
664:
665: /*******************************************************************************************************************
666: *
667: * Returns the ColorModel of this image.
668: *
669: * @return the color model
670: *
671: ******************************************************************************************************************/
672: @Nonnegative
673: public ColorModel getColorModel() // FIXME: to be removed
674: {
675: return imageModelHolder.get().getColorModel();
676: }
677:
678: /*******************************************************************************************************************
679: *
680: * Returns the ICC_Profile of this image (null will be returned if the ColorModel is not ICC-based). <i>Note that
681: * this is the profile of the image as it is optimized for the display, which is almost surely sRGB; and <b>it's
682: * probably different than the original image profile</b></i>.
683: *
684: * @return the color profile
685: *
686: ******************************************************************************************************************/
687: @Nonnegative
688: public ICC_Profile getICCProfile() // FIXME: to be removed
689: {
690: final var colorModel = getColorModel();
691:
692: if (colorModel != null)
693: {
694: final var colorSpace = colorModel.getColorSpace();
695:
696: if (colorSpace instanceof ICC_ColorSpace)
697: {
698: final var iccColorSpace = (ICC_ColorSpace)colorSpace;
699: return iccColorSpace.getProfile();
700: }
701: }
702:
703: return null;
704: }
705:
706: /*******************************************************************************************************************
707: *
708: *
709: ******************************************************************************************************************/
710:
711: /*
712: private final static int COMPRESSED = 1;
713:
714: public void writeExternal (ObjectOutput out)
715: throws IOException
716: {
717: ByteArrayOutputStream baos = new ByteArrayOutputStream();
718: // GZIPOutputStream gos = new GZIPOutputStream(baos);
719: DataOutputStream dos = new DataOutputStream(baos);
720: dos.writeUTF(imageModel.getClass().getName());
721: imageModel.writeExternal(dos);
722: dos.flush();
723: dos.close();
724: byte[] buffer = baos.toByteArray();
725:
726: out.writeLong(System.currentTimeMillis());
727: out.writeInt(0);
728: out.writeInt(buffer.length);
729: out.write(buffer);
730: out.flush();
731: }*/
732:
733: /*******************************************************************************************************************
734: *
735: *
736: ******************************************************************************************************************/
737:
738: /*
739: public void readExternal (ObjectInput in)
740: throws IOException, ClassNotFoundException
741: {
742: long serializationTimeStamp = in.readLong();
743: latestOperationTime = System.currentTimeMillis() - serializationTimeStamp;
744: int mode = in.readInt();
745: int bufferSize = in.readInt();
746: byte[] buffer = new byte[bufferSize];
747: in.readFully(buffer);
748: InputStream is = new ByteArrayInputStream(buffer);
749:
750: if (mode == COMPRESSED)
751: {
752: is = new GZIPInputStream(is);
753: }
754:
755: DataInputStream dis = new DataInputStream(is);
756: String className = dis.readUTF();
757: Class clazz = Class.forName(className);
758: try
759: {
760: imageModel = (ImageModel)clazz.newInstance();
761: }
762: catch (InstantiationException e)
763: {
764: throw new RuntimeException(e);
765: }
766: catch (IllegalAccessException e)
767: {
768: throw new RuntimeException(e);
769: }
770:
771: imageModel.readExternal(dis);
772: dis.close();
773: latestSerializationSize = bufferSize;
774: }*/
775:
776: /*******************************************************************************************************************
777: *
778: *
779: ******************************************************************************************************************/
780: @Nonnull
781: public long getLatestSerializationSize()
782: {
783: return latestSerializationSize;
784: }
785:
786: /*******************************************************************************************************************
787: *
788: * DO NOT USE. This is only for implementation and testing purposes.
789: *
790: ******************************************************************************************************************/
791: @Nonnull
792: public <T> T getInnerProperty (final @Nonnull Class<T> propertyClass)
793: {
794: if (AccessorOp.class.equals(propertyClass))
795: {
796: return propertyClass.cast(accessor);
797: }
798:
799: if (IIOMetadata.class.equals(propertyClass))
800: {
801: return propertyClass.cast(iioMetadata);
802: }
803:
804: return imageModelHolder.get().getInnerProperty(propertyClass);
805: }
806:
807: /*******************************************************************************************************************
808: *
809: * Executes an operation and return the raw result (the object to be wrapped by the ImageModel).
810: *
811: * @param operation the operation to perform
812: * @return the result (the object wrapped by the ImageModel)
813: *
814: ******************************************************************************************************************/
815: @Nonnull
816: private Object internalExecute (final @Nonnull Operation operation)
817: throws UnsupportedOperationException
818: {
819: final var implementationFactoryRegistry = ImplementationFactoryRegistry.getDefault();
820: final var imageModel = imageModelHolder.get();
821: var image = (imageModel != null) ? imageModel.getImage() : null;
822: OperationImplementation implementation = null;
823:
824: if ((image == null) && !(operation instanceof AbstractCreateOp))
825: {
826: throw new RuntimeException("null image with an Op different that AbstractCreateOp");
827: }
828:
829: try
830: {
831: implementation = implementationFactoryRegistry.findImplementation(operation, imageModelHolder.get(), false);
832: }
833: catch (UnsupportedOperationException e)
834: {
835: log.warn("No default implementation of {} for model: {}", operation, image);
836: implementation = implementationFactoryRegistry.findImplementation(operation, imageModelHolder.get(), true);
837: log.info("Found alternate implementation: {}", implementation);
838:
839: if (!(operation instanceof AbstractCreateOp))
840: {
841: if (implementation.getFactory().canConvertFrom(image.getClass()))
842: {
843: log.info(">>>> CONVERT FROM using {}", implementation.getFactory());
844: imageModelHolder = ImageModelHolder.wrap(implementation.getFactory().convertFrom(image));
845: image = imageModelHolder.get().getImage();
846: }
847:
848: else if (imageModelHolder.get().getFactory().canConvertTo(implementation.getFactory().getModelClass()))
849: {
850: log.info(">>>> CONVERT TO {}", implementation.getFactory().getModelClass());
851: image = imageModelHolder.get().getFactory().convertTo(implementation.getFactory().getModelClass());
852: }
853:
854: else
855: {
856: throw new RuntimeException("Shouldn't get here");
857: }
858:
859: log.info(">>>> NEW IMAGE {} NEW IMAGE MODEL {}", image, imageModelHolder);
860: }
861: }
862:
863: try
864: {
865: return implementation.execute(this, image);
866: }
867: catch (RuntimeException e)
868: {
869: log.error("Operation failed, offending image: {}", image);
870: throw e;
871: }
872: }
873: }