001/*****************************************************************************
002 * Copyright by The HDF Group.                                               *
003 * Copyright by the Board of Trustees of the University of Illinois.         *
004 * All rights reserved.                                                      *
005 *                                                                           *
006 * This file is part of the HDF Java Products distribution.                  *
007 * The full copyright notice, including terms governing use, modification,   *
008 * and redistribution, is contained in the files COPYING and Copyright.html. *
009 * COPYING can be found at the root of the source code distribution tree.    *
010 * Or, see https://support.hdfgroup.org/products/licenses.html               *
011 * If you do not have access to either file, you may request a copy from     *
012 * help@hdfgroup.org.                                                        *
013 ****************************************************************************/
014
015package hdf.object;
016
017import java.util.Iterator;
018import java.util.List;
019import java.util.Vector;
020
021/**
022 * A scalar dataset is a multiple dimension array of scalar points. The Datatype of a scalar dataset must be an atomic
023 * datatype. Common datatypes of scalar datasets include char, byte, short, int, long, float, double and string.
024 * <p>
025 * A ScalarDS can be an image or spreadsheet data. ScalarDS defines methods to deal with both images and
026 * spreadsheets.
027 * <p>
028 * ScalarDS is an abstract class. Current implementing classes are the H4SDS, H5GRImage and H5ScalarDS.
029 *
030 * @version 1.1 9/4/2007
031 * @author Peter X. Cao
032 */
033public abstract class ScalarDS extends Dataset {
034    private static final long serialVersionUID = 8925371455928203981L;
035
036    private static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(ScalarDS.class);
037
038    /************************************************************
039     * The following constant strings are copied from           *
040     * https://support.hdfgroup.org/HDF5/doc/ADGuide/ImageSpec.html *
041     * to make the definition consistent with the image specs.  *
042     ************************************************************/
043
044    /**
045     * Indicates that the pixel RGB values are contiguous.
046     */
047    public static final int INTERLACE_PIXEL = 0;
048
049    /** Indicates that each pixel component of RGB is stored as a scan line. */
050    public static final int INTERLACE_LINE = 1;
051
052    /** Indicates that each pixel component of RGB is stored as a plane. */
053    public static final int INTERLACE_PLANE = 2;
054
055    /**
056     * The interlace mode of the stored raster image data. Valid values are INTERLACE_PIXEL, INTERLACE_LINE and
057     * INTERLACE_PLANE.
058     */
059    protected int interlace;
060
061    /**
062     * The min-max range of image data values. For example, [0, 255] indicates the min is 0, and the max is 255.
063     */
064    protected double[] imageDataRange;
065
066    /**
067     * The indexed RGB color model with 256 colors.
068     * <p>
069     * The palette values are stored in a two-dimensional byte array and arrange by color components of red, green and
070     * blue. palette[][] = byte[3][256], where, palette[0][], palette[1][] and palette[2][] are the red, green and blue
071     * components respectively.
072     */
073    protected byte[][] palette;
074
075    /**
076     * True if this dataset is an image.
077     */
078    protected boolean isImage;
079
080    /**
081     * True if this dataset is a true color image.
082     */
083    protected boolean isTrueColor;
084
085    /**
086     * True if this dataset is ASCII text.
087     */
088    protected boolean isText;
089
090    /**
091     * Flag to indicate is the original unsigned C data is converted.
092     */
093    protected boolean unsignedConverted;
094
095    /** The fill value of the dataset. */
096    protected Object fillValue = null;
097
098    private List<Number> filteredImageValues;
099
100    /** Flag to indicate if the dataset is displayed as an image. */
101    protected boolean isImageDisplay;
102
103    /**
104     * Flag to indicate if the dataset is displayed as an image with default order of dimensions.
105     */
106    protected boolean isDefaultImageOrder;
107
108    /**
109     * Flag to indicate if the FillValue is converted from unsigned C.
110     */
111    public boolean isFillValueConverted;
112
113    /**
114     * Constructs an instance of a ScalarDS with specific name and path. An HDF data object must have a name. The path
115     * is the group path starting from the root.
116     * <p>
117     * For example, in H5ScalarDS(h5file, "dset", "/arrays/"), "dset" is the name of the dataset, "/arrays" is the group
118     * path of the dataset.
119     *
120     * @param theFile
121     *            the file that contains the data object.
122     * @param theName
123     *            the name of the data object, e.g. "dset".
124     * @param thePath
125     *            the full path of the data object, e.g. "/arrays/".
126     */
127    public ScalarDS(FileFormat theFile, String theName, String thePath) {
128        this(theFile, theName, thePath, null);
129    }
130
131    /**
132     * @deprecated Not for public use in the future.<br>
133     *             Using {@link #ScalarDS(FileFormat, String, String)}
134     *
135     * @param theFile
136     *            the file that contains the data object.
137     * @param theName
138     *            the name of the data object, e.g. "dset".
139     * @param thePath
140     *            the full path of the data object, e.g. "/arrays/".
141     * @param oid
142     *            the v of the data object.
143     */
144    @Deprecated
145    public ScalarDS(FileFormat theFile, String theName, String thePath, long[] oid) {
146        super(theFile, theName, thePath, oid);
147
148        palette = null;
149        isImage = false;
150        isTrueColor = false;
151        isText = false;
152        interlace = -1;
153        imageDataRange = null;
154        isImageDisplay = false;
155        isDefaultImageOrder = true;
156        isFillValueConverted = false;
157        filteredImageValues = new Vector<>();
158    }
159
160    /*
161     * (non-Javadoc)
162     *
163     * @see hdf.object.Dataset#clearData()
164     */
165    @Override
166    public void clearData() {
167        super.clearData();
168        unsignedConverted = false;
169    }
170
171    /**
172     * Converts the data values of this dataset to appropriate Java integer if they are unsigned integers.
173     *
174     * @see hdf.object.Dataset#convertToUnsignedC(Object)
175     * @see hdf.object.Dataset#convertFromUnsignedC(Object, Object)
176     *
177     * @return the converted data buffer.
178     */
179    @Override
180    public Object convertFromUnsignedC() {
181        // keep a copy of original buffer and the converted buffer
182        // so that they can be reused later to save memory
183        log.trace("convertFromUnsignedC(): unsigned={}", getDatatype().isUnsigned());
184        if ((data != null) && getDatatype().isUnsigned() && !unsignedConverted) {
185            log.trace("convertFromUnsignedC(): convert");
186            originalBuf = data;
187            convertedBuf = convertFromUnsignedC(originalBuf, convertedBuf);
188            data = convertedBuf;
189            unsignedConverted = true;
190
191            if (fillValue != null) {
192                if (!isFillValueConverted) {
193                    fillValue = convertFromUnsignedC(fillValue, null);
194                    isFillValueConverted = true;
195                }
196            }
197        }
198
199        return data;
200    }
201
202    /**
203     * Converts Java integer data of this dataset back to unsigned C-type integer data if they are unsigned integers.
204     *
205     * @see hdf.object.Dataset#convertToUnsignedC(Object)
206     * @see hdf.object.Dataset#convertToUnsignedC(Object, Object)
207     * @see #convertFromUnsignedC(Object data_in)
208     *
209     * @return the converted data buffer.
210     */
211    @Override
212    public Object convertToUnsignedC() {
213        // keep a copy of original buffer and the converted buffer
214        // so that they can be reused later to save memory
215        log.trace("convertToUnsignedC(): unsigned={}", getDatatype().isUnsigned());
216        if ((data != null) && getDatatype().isUnsigned()) {
217            log.trace("convertToUnsignedC(): convert");
218            convertedBuf = data;
219            originalBuf = convertToUnsignedC(convertedBuf, originalBuf);
220            data = originalBuf;
221        }
222
223        return data;
224    }
225
226    /**
227     * Returns the palette of this scalar dataset or null if palette does not exist.
228     * <p>
229     * A Scalar dataset can be displayed as spreadsheet data or an image. When a scalar dataset is displayed as an
230     * image, the palette or color table may be needed to translate a pixel value to color components (for example, red,
231     * green, and blue). Some scalar datasets have no palette and some datasets have one or more than one palettes. If
232     * an associated palette exists but is not loaded, this interface retrieves the palette from the file and returns the
233     * palette. If the palette is loaded, it returns the palette. It returns null if there is no palette associated with
234     * the dataset.
235     * <p>
236     * Current implementation only supports palette model of indexed RGB with 256 colors. Other models such as
237     * YUV", "CMY", "CMYK", "YCbCr", "HSV will be supported in the future.
238     * <p>
239     * The palette values are stored in a two-dimensional byte array and are arranges by color components of red, green and
240     * blue. palette[][] = byte[3][256], where, palette[0][], palette[1][] and palette[2][] are the red, green and blue
241     * components respectively.
242     * <p>
243     * Sub-classes have to implement this interface. HDF4 and HDF5 images use different libraries to retrieve the
244     * associated palette.
245     *
246     * @return the 2D palette byte array.
247     */
248    public abstract byte[][] getPalette();
249
250    /**
251     * Sets the palette for this dataset.
252     *
253     * @param pal
254     *            the 2D palette byte array.
255     */
256    public final void setPalette(byte[][] pal) {
257        palette = pal;
258    }
259
260    /**
261     * Reads a specific image palette from file.
262     * <p>
263     * A scalar dataset may have multiple palettes attached to it. readPalette(int idx) returns a specific palette
264     * identified by its index.
265     *
266     * @param idx
267     *            the index of the palette to read.
268     *
269     * @return the image palette
270     */
271    public abstract byte[][] readPalette(int idx);
272
273    /**
274     * Get the name of a specific image palette from file.
275     * <p>
276     * A scalar dataset may have multiple palettes attached to it. getPaletteName(int idx) returns the name of a
277     * specific palette identified by its index.
278     *
279     * @param idx
280     *            the index of the palette to retrieve the name.
281     *
282     * @return The name of the palette
283     */
284    public String getPaletteName(int idx) {
285        String paletteName = "Default ";
286        if (idx != 0)
287            paletteName = "Default " + idx;
288        return paletteName;
289    }
290
291    /**
292     * Returns the byte array of palette refs.
293     * <p>
294     * A palette reference is an object reference that points to the palette dataset.
295     * <p>
296     * For example, Dataset "Iceberg" has an attribute of object reference "Palette". The arrtibute "Palette" has value
297     * "2538" that is the object reference of the palette data set "Iceberg Palette".
298     *
299     * @return null if there is no palette attribute attached to this dataset.
300     */
301    public abstract byte[] getPaletteRefs();
302
303    /**
304     * Returns true if this dataset is an image.
305     * <p>
306     * For all Images, they must have an attribute called "CLASS". The value of this attribute is "IMAGE". For more
307     * details, read <a href="https://support.hdfgroup.org/HDF5/doc/ADGuide/ImageSpec.html"> HDF5 Image and Palette Specification</a>
308     *
309     * @return true if the dataset is an image; otherwise, returns false.
310     */
311    public final boolean isImage() {
312        return isImage;
313    }
314
315    /**
316     * Returns true if this dataset is displayed as an image.
317     * <p>
318     * A ScalarDS can be displayed as an image or a spreadsheet in a table.
319     *
320     * @return true if this dataset is displayed as an image; otherwise, returns false.
321     */
322    public final boolean isImageDisplay() {
323
324        return isImageDisplay;
325    }
326
327    /**
328     * Returns true if this dataset is displayed as an image with default image order.
329     * <p>
330     * A ScalarDS can be displayed as an image with different orders of dimensions.
331     *
332     * @return true if this dataset is displayed as an image with default image order; otherwise, returns false.
333     */
334    public final boolean isDefaultImageOrder() {
335        return isDefaultImageOrder;
336    }
337
338    /**
339     * Sets the flag to display the dataset as an image.
340     *
341     * @param b
342     *            if b is true, display the dataset as an image
343     */
344    public final void setIsImageDisplay(boolean b) {
345        isImageDisplay = b;
346    }
347
348    /**
349     * Sets the flag to indicate this dataset is an image.
350     *
351     * @param b
352     *            if b is true, the dataset is an image.
353     */
354    public final void setIsImage(boolean b) {
355        isImage = b;
356    }
357
358    /**
359     * Sets data range for an image.
360     *
361     * @param min
362     *            the data range start.
363     * @param max
364     *            the data range end.
365     */
366    public final void setImageDataRange(double min, double max) {
367        if (max <= min)
368            return;
369
370        if (imageDataRange == null)
371            imageDataRange = new double[2];
372
373        imageDataRange[0] = min;
374        imageDataRange[1] = max;
375    }
376
377    /**
378     * Add a value that will be filtered out in an image.
379     *
380     * @param x
381     *            value to be filtered
382     */
383    public void addFilteredImageValue(Number x) {
384        Iterator<Number> it = filteredImageValues.iterator();
385        while (it.hasNext()) {
386            if (it.next().toString().equals(x.toString()))
387                return;
388        }
389
390        filteredImageValues.add(x);
391    }
392
393    /**
394     * Get a list of values that will be filtered out in an image.
395     *
396     * @return the list of Image values
397     */
398    public List<Number> getFilteredImageValues() {
399        return filteredImageValues;
400    }
401
402    /**
403     * @return true if this dataset is a true color image.
404     *
405     */
406
407    public final boolean isTrueColor() {
408        return isTrueColor;
409    }
410
411    /**
412     * Returns the interlace mode of a true color image (RGB).
413     *
414     * Valid values:
415     *
416     * <pre>
417     *     INTERLACE_PIXEL -- RGB components are contiguous, i.e. rgb, rgb, rgb, ...
418     *     INTERLACE_LINE -- each RGB component is stored as a scan line
419     *     INTERLACE_PLANE -- each RGB component is stored as a plane
420     * </pre>
421     *
422     * @return the interlace mode of a true color image (RGB).
423     */
424    public final int getInterlace() {
425        return interlace;
426    }
427
428    /**
429     * Returns the (min, max) pair of image data range.
430     *
431     * @return the (min, max) pair of image data range.
432     */
433    public double[] getImageDataRange() {
434        return imageDataRange;
435    }
436
437    /**
438     * Returns the fill values for the dataset.
439     *
440     * @return the fill values for the dataset.
441     */
442    @Override
443    public final Object getFillValue() {
444        return fillValue;
445    }
446}