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