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