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