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.h5;
016
017import java.lang.reflect.Array;
018
019import java.util.ArrayList;
020import java.util.Arrays;
021import java.util.Collection;
022import java.util.HashMap;
023import java.util.Iterator;
024import java.util.List;
025import java.util.Map;
026import java.util.Vector;
027import java.util.regex.Pattern;
028
029import hdf.hdf5lib.H5;
030import hdf.hdf5lib.HDF5Constants;
031
032import hdf.object.Datatype;
033import hdf.object.FileFormat;
034
035/**
036 * This class defines HDF5 reference characteristics and APIs for a data type of H5T_STD_REF.
037 *
038 * This class provides convenient functions to access H5T_STD_REF type information.
039 */
040public class H5ReferenceType extends H5Datatype
041{
042    private static final long serialVersionUID    = -3360885430038261178L;
043
044    private static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(H5ReferenceType.class);
045
046    /**
047     * The memory buffer that holds the raw data array of the reference.
048     */
049    protected transient ArrayList<H5ReferenceData> refdata;
050
051    /** Flag to indicate if data values are loaded into memory. */
052    protected boolean         isDataLoaded = false;
053
054    /** Flag to indicate if this dataset has been initialized */
055    protected boolean         inited = false;
056
057    /** The current array size of the reference. */
058    protected long            refsize;
059
060    /**
061     * The data buffer that contains the raw data directly reading from file
062     * (before any data conversion).
063     */
064    protected transient Object originalRefBuf = null;
065
066    /**
067     * Constructs an named HDF5 data type reference for a given file, dataset name and group path.
068     *
069     * The datatype object represents an existing named datatype in file. For example,
070     *
071     * <pre>
072     * new H5ReferenceType(file, "dtype1", "/g0")
073     * </pre>
074     *
075     * constructs a datatype object that corresponds to the dataset,"dset1", at group "/g0".
076     *
077     * @param theFile
078     *            the file that contains the datatype.
079     * @param theName
080     *            the name of the dataset such as "dset1".
081     * @param thePath
082     *            the group path to the dataset such as "/g0/".
083     */
084    public H5ReferenceType(FileFormat theFile, String theName, String thePath) {
085        this(theFile, theName, thePath, null);
086    }
087
088    /**
089     * @deprecated Not for public use in the future. <br>
090     *             Using {@link #H5ReferenceType(FileFormat, String, String)}
091     *
092     * @param theFile
093     *            the file that contains the datatype.
094     * @param theName
095     *            the name of the dataset such as "dset1".
096     * @param thePath
097     *            the group path to the dataset such as "/g0/".
098     * @param oid
099     *            the oid of the dataset.
100     */
101    @Deprecated
102    public H5ReferenceType(FileFormat theFile, String theName, String thePath, long[] oid) {
103        super(theFile, theName, thePath, oid);
104
105        log.trace("constructor theName {}", theName);
106        refdata = null;
107     }
108
109    /**
110     * Constructs a H5ReferenceType with specified class, size, byte order and sign.
111     *
112     * @param tclass
113     *            the class of the datatype, e.g. CLASS_INTEGER, CLASS_FLOAT and etc.
114     * @param tsize
115     *            the size of the datatype in bytes, e.g. for a 32-bit integer, the size is 4.
116     *            Valid values are NATIVE or a positive value. For string datatypes, -1 is also
117     *            a valid value (to create a variable-length string).
118     * @param torder
119     *            the byte order of the datatype. Valid values are ORDER_LE, ORDER_BE, ORDER_VAX,
120     *            ORDER_NONE and NATIVE.
121     * @param tsign
122     *            the sign of the datatype. Valid values are SIGN_NONE, SIGN_2 and NATIVE.
123     *
124     * @throws Exception
125     *            if there is an error
126     */
127    public H5ReferenceType(int tclass, int tsize, int torder, int tsign) throws Exception {
128        this(tclass, tsize, torder, tsign, null);
129    }
130
131    /**
132     * Constructs a H5ReferenceType with specified class, size, byte order and sign.
133     *
134     * @param tclass
135     *            the class of the datatype, e.g. CLASS_INTEGER, CLASS_FLOAT and etc.
136     * @param tsize
137     *            the size of the datatype in bytes, e.g. for a 32-bit integer, the size is 4.
138     *            Valid values are NATIVE or a positive value. For string datatypes, -1 is also
139     *            a valid value (to create a variable-length string).
140     * @param torder
141     *            the byte order of the datatype. Valid values are ORDER_LE, ORDER_BE, ORDER_VAX,
142     *            ORDER_NONE and NATIVE.
143     * @param tsign
144     *            the sign of the datatype. Valid values are SIGN_NONE, SIGN_2 and NATIVE.
145     * @param tbase
146     *            the base datatype of the new datatype
147     *
148     * @throws Exception
149     *            if there is an error
150     */
151    public H5ReferenceType(int tclass, int tsize, int torder, int tsign, Datatype tbase) throws Exception {
152        this(tclass, tsize, torder, tsign, tbase, null);
153    }
154
155    /**
156     * Constructs a H5ReferenceType with specified class, size, byte order and sign.
157     *
158     * @param tclass
159     *            the class of the datatype, e.g. CLASS_INTEGER, CLASS_FLOAT and etc.
160     * @param tsize
161     *            the size of the datatype in bytes, e.g. for a 32-bit integer, the
162     *            size is 4. Valid values are NATIVE or a positive value. For string
163     *            datatypes, -1 is also a valid value (to create a variable-length
164     *            string).
165     * @param torder
166     *            the byte order of the datatype. Valid values are ORDER_LE,
167     *            ORDER_BE, ORDER_VAX, ORDER_NONE and NATIVE.
168     * @param tsign
169     *            the sign of the datatype. Valid values are SIGN_NONE, SIGN_2 and
170     *            NATIVE.
171     * @param tbase
172     *            the base datatype of the new datatype
173     * @param pbase
174     *            the parent datatype of the new datatype
175     *
176     * @throws Exception
177     *            if there is an error
178     */
179    public H5ReferenceType(int tclass, int tsize, int torder, int tsign, Datatype tbase, Datatype pbase) throws Exception {
180        super(tclass, tsize, torder, tsign, tbase, pbase);
181
182        log.trace("constructor tsize {}", tsize);
183        refdata = null;
184    }
185
186    /**
187     * Constructs a H5ReferenceType with a given native datatype identifier.
188     *
189     * For example, if the datatype identifier is a 32-bit unsigned integer created from HDF5,
190     *
191     * <pre>
192     * int tid = H5.H5Tcopy(HDF5Constants.H5T_NATIVE_UNINT32);
193     * Datatype dtype = new Datatype(tid);
194     * </pre>
195     *
196     * will construct a datatype equivalent to new Datatype(Datatype.CLASS_INTEGER, 4, Datatype.NATIVE, Datatype.SIGN_NONE);
197     *
198     * @see #fromNative(long nativeID)
199     *
200     * @param theFile
201     *            the file that contains the datatype.
202     * @param theSize
203     *            the size of the datatype in bytes, e.g. for a 32-bit integer, the
204     *            size is 4. Valid values are NATIVE or a positive value. For string
205     *            datatypes, -1 is also a valid value (to create a variable-length
206     *            string).
207     * @param nativeID
208     *            the native datatype identifier.
209     *
210     * @throws Exception
211     *            if there is an error
212     */
213    public H5ReferenceType(FileFormat theFile, long theSize, long nativeID) throws Exception {
214        this(theFile, theSize, nativeID, null);
215    }
216
217    /**
218     * Constructs a H5ReferenceType with a given native datatype identifier.
219     *
220     * For example, if the datatype identifier is a 32-bit unsigned integer created from HDF5,
221     *
222     * <pre>
223     * int tid = H5.H5Tcopy(HDF5Constants.H5T_NATIVE_UNINT32);
224     * Datatype dtype = new Datatype(tid);
225     * </pre>
226     *
227     * will construct a datatype equivalent to new Datatype(Datatype.CLASS_INTEGER, 4, Datatype.NATIVE, Datatype.SIGN_NONE);
228     *
229     * @see #fromNative(long nativeID)
230     *
231     * @param theFile
232     *            the file that contains the datatype.
233     * @param theSize
234     *            the size of the datatype in bytes, e.g. for a 32-bit integer, the
235     *            size is 4. Valid values are NATIVE or a positive value. For string
236     *            datatypes, -1 is also a valid value (to create a variable-length
237     *            string).
238     * @param nativeID
239     *            the native datatype identifier.
240     * @param pbase
241     *            the parent datatype of the new datatype
242     *
243     * @throws Exception
244     *            if there is an error
245     */
246    public H5ReferenceType(FileFormat theFile, long theSize, long nativeID, Datatype pbase) throws Exception {
247        super(theFile, nativeID, pbase);
248
249        log.trace("constructor theSize {}", theSize);
250        refsize = theSize;
251        refdata = new ArrayList<H5ReferenceData>((int)theSize);
252    }
253
254    /**
255     * Clears memory held by the reference, such as the data buffer.
256     */
257    @SuppressWarnings("rawtypes")
258    public void clear() {
259        if (refdata != null) {
260            if (refdata instanceof List)
261                ((List) refdata).clear();
262            originalRefBuf = null;
263        }
264        isDataLoaded = false;
265    }
266
267    /**
268     * Writes the memory buffer of this reference to file.
269     *
270     * @throws Exception if buffer can not be written
271     */
272    public final void write() throws Exception {
273        log.trace("H5ReferenceType: write enter");
274        if (refdata != null) {
275            log.trace("H5ReferenceType: write data");
276            //write(refdata);
277        }
278    }
279
280    /**
281     * The status of initialization for this object
282     *
283     * @return true if the data has been initialized
284     */
285    public final boolean isInited() {
286        return inited;
287    }
288
289    /**
290     * setData() loads the reference raw data into the buffer. This
291     * buffer will be accessed to get the reference strings and data.
292     * Once the references are destroyed, the refdata can only be used
293     * to retrieve existing data.
294     *
295     * @param theData
296     *            the data to write.
297     */
298    public void setData(Object theData) {
299        log.trace("setData(): refsize={} theData={}", refsize, theData);
300        originalRefBuf = theData;
301        for (int i = 0; i < (int)refsize; i++) {
302            int refIndex = HDF5Constants.H5R_REF_BUF_SIZE * i;
303            byte[] refarr = new byte[(int) HDF5Constants.H5R_REF_BUF_SIZE];
304            System.arraycopy((byte[])theData, refIndex, refarr, 0, (int)HDF5Constants.H5R_REF_BUF_SIZE);
305            log.trace("setData(): refarr={}", refarr);
306            H5ReferenceData rf = new H5ReferenceData(refarr);
307            refdata.add(rf);
308        }
309        isDataLoaded = true;
310        init();
311    }
312
313    /**
314     * Returns the data buffer of the reference in memory.
315     *
316     * If data is already loaded into memory, returns the data; otherwise, calls
317     * read() to read data from file into a memory buffer and returns the memory
318     * buffer.
319     *
320     * By default, the whole reference is read into memory.
321     *
322     * @return the memory buffer of the reference.
323     *
324     * @throws Exception if object can not be read
325     * @throws OutOfMemoryError if memory is exhausted
326     */
327    public Object getData() throws Exception, OutOfMemoryError {
328        log.trace("getData(): isDataLoaded={}", isDataLoaded);
329        if (!isDataLoaded) {
330            //refdata = read(); // load the data
331            log.trace("getData(): size={} refdata={}", refdata.size(), refdata);
332            if (refdata != null) {
333                refsize = refdata.size();
334                originalRefBuf = refdata;
335                isDataLoaded = true;
336            }
337        }
338
339        return refdata;
340    }
341
342    /**
343     * Clears the current data buffer in memory and forces the next read() to load
344     * the data from file.
345     *
346     * The function read() loads data from file into memory only if the data is
347     * not read. If data is already in memory, read() just returns the memory
348     * buffer. Sometimes we want to force read() to re-read data from file. For
349     * example, when the selection is changed, we need to re-read the data.
350     *
351     * @see #getData()
352     */
353    public void clearData() {
354        isDataLoaded = false;
355    }
356
357    /**
358     * Returns the array size of the reference.
359     *
360     * @return the array size of the reference.
361     */
362    public final long getRefSize() {
363        if (!inited)
364            init();
365
366        return refsize;
367    }
368
369    /**
370     * Sets the array size of the reference.
371     *
372     * @param current_size
373     *        the array size of the current reference.
374     */
375    public final void setRefSize(long current_size) {
376        refsize = current_size;
377    }
378
379    /**
380     * Retrieves reference information from file into memory.
381     */
382    public void init() {
383        if (inited) {
384            log.trace("init(): H5ReferenceType already inited");
385            return;
386        }
387
388        log.trace("init(): refsize={}", refsize);
389        for (int i = 0; i < (int)refsize; i++) {
390            H5ReferenceData rf = refdata.get(i);
391            log.trace("init(): rf.ref_array={}", rf.ref_array);
392            byte[] refarr = new byte[(int) HDF5Constants.H5R_REF_BUF_SIZE];
393            System.arraycopy(rf.ref_array, 0, refarr, 0, (int)HDF5Constants.H5R_REF_BUF_SIZE);
394
395            if (zeroArrayCheck(refarr)) {
396                log.trace("init(): refarr is zero");
397                rf.file_fullpath = "NULL";
398                rf.file_name = "NULL";
399                rf.obj_name = "NULL";
400                rf.attr_name = "NULL";
401                rf.region_type = "NULL";
402                rf.region_desc = "NULL";
403            }
404            else {
405                log.trace("init(): refarr={}", refarr);
406                try {
407                    rf.file_fullpath = "NULL";
408                    rf.file_name = "NULL";
409                    rf.obj_name = "NULL";
410                    rf.attr_name = "NULL";
411                    try {
412                        rf.file_fullpath = H5.H5Rget_file_name(refarr);
413                        log.trace("Reference Full File Path {}", rf.file_fullpath);
414                        String[] split = rf.file_fullpath.split( Pattern.quote("/") );
415                        rf.file_name = split[split.length-1];
416                        log.trace("Reference File Name {}", rf.file_name);
417                        rf.obj_name = H5.H5Rget_obj_name(refarr, HDF5Constants.H5P_DEFAULT);
418                        log.trace("Reference Object Name {}", rf.obj_name);
419
420                        if (H5.H5Rget_type(refarr) == HDF5Constants.H5R_ATTR)
421                            rf.attr_name = H5.H5Rget_attr_name(refarr);
422                        else
423                            rf.attr_name = "NULL";
424                        log.trace("Reference Attribute Name {}", rf.attr_name);
425                    }
426                    catch (Exception ex) {
427                        log.debug("Reference H5Rget_*_name", ex);
428                    }
429                    initReferenceRegion(i, refarr, false);
430                }
431                catch (Exception ex) {
432                    log.debug("Reference Init", ex);
433                }
434                finally {
435                    H5.H5Rdestroy(refarr);
436                    log.trace("Reference H5Rdestroy");
437                }
438            }
439        }
440        log.trace("init(): finished");
441        inited = true;
442    }
443
444    private void initReferenceRegion(int refndx, byte[] refarr, boolean showData) {
445        H5ReferenceData rf = refdata.get(refndx);
446        rf.ref_type = HDF5Constants.H5R_BADTYPE;
447        rf.obj_type = HDF5Constants.H5O_TYPE_UNKNOWN;
448        rf.region_type = "NULL";
449        rf.region_desc = "NULL";
450        log.trace("initReferenceRegion start not null");
451        try {
452            rf.ref_type = (int)H5.H5Rget_type(refarr);
453            log.debug("initReferenceRegion ref_type={}", rf.ref_type);
454            try {
455                rf.obj_type = H5.H5Rget_obj_type3(refarr, HDF5Constants.H5P_DEFAULT);
456                log.debug("initReferenceRegion obj_type={}", rf.obj_type);
457            }
458            catch (Exception ex2) {
459                log.debug("initReferenceRegion H5Rget_obj_type3", ex2);
460            }
461        }
462        catch (Exception ex1) {
463            log.debug("initReferenceRegion H5Rget_type", ex1);
464        }
465
466        if (rf.ref_type > HDF5Constants.H5R_BADTYPE) {
467            if (rf.ref_type == HDF5Constants.H5R_OBJECT1) {
468                log.trace("initReferenceRegion H5R_OBJECT1");
469                if (rf.obj_type == HDF5Constants.H5O_TYPE_DATASET) {
470                    initRegionDataset(refndx, refarr);
471                } //obj_type == HDF5Constants.H5O_TYPE_DATASET
472                else {
473                    /* Object references -- show the type and OID of the referenced object. */
474                    rf.region_type = "H5O_TYPE_OBJ_REF";
475//                        H5O_info2_t oi;
476//                        char *      obj_tok_str = NULL;
477//                        H5Oget_info3(new_obj_id, &oi, H5O_INFO_BASIC);
478//                        HDsprintf(this_str, "%u-", (unsigned)oi.type);
479//                        H5Otoken_to_str(new_obj_id, &oi.token, &obj_tok_str);
480//                        /* Print OID */
481//                        {
482//                            char *token_str;
483//
484//                            H5Otoken_to_str(tid, &oi.token, &token_str);
485//
486//                            if (NULL == (this_str = (char *)HDmalloc(64 + strlen(token_str) + 1)))
487//                                H5_OUT_OF_MEMORY_ERROR(
488//                                    ENVONLY, "h5str_sprintf: failed to allocate string buffer");
489//                            if (HDsprintf(this_str, "%lu:%s", oi.fileno, token_str) < 0)
490//                                 H5_JNI_FATAL_ERROR(ENVONLY, "h5str_sprintf: HDsprintf failure");
491//                            H5free_memory(token_str);
492//                        }
493                }
494            }
495            else if (rf.ref_type == HDF5Constants.H5R_DATASET_REGION1) {
496                log.trace("initReferenceRegion H5R_DATASET_REGION1");
497                initRegionDataset(refndx, refarr);
498            }
499            else if (rf.ref_type == HDF5Constants.H5R_OBJECT2) {
500                log.trace("initReferenceRegion H5R_OBJECT2");
501                rf.region_type = "H5O_TYPE_OBJ_REF";
502            }
503            else if (rf.ref_type == HDF5Constants.H5R_DATASET_REGION2) {
504                log.trace("initReferenceRegion H5R_DATASET_REGION2");
505                initRegionDataset(refndx, refarr);
506            }
507            else if (rf.ref_type == HDF5Constants.H5R_ATTR) {
508                log.trace("initReferenceRegion H5R_ATTR");
509                rf.region_type = "H5R_ATTR";
510                initRegionAttribute(refndx, refarr);
511            }
512            else {
513                log.trace("initReferenceRegion OTHER");
514                rf.region_type = "UNKNOWN";
515            }
516        }
517        log.trace("initReferenceRegion finish");
518    }
519
520    private void initRegionAttribute(int refndx, byte[] refarr) {
521        H5ReferenceData rf = refdata.get(refndx);
522        long new_obj_id = HDF5Constants.H5I_INVALID_HID;
523        try {
524            log.trace("initRegionAttribute refarr2={}:", refarr);
525            new_obj_id = H5.H5Ropen_attr(refarr, HDF5Constants.H5P_DEFAULT, HDF5Constants.H5P_DEFAULT);
526            long new_obj_sid = HDF5Constants.H5I_INVALID_HID;
527            try {
528                new_obj_sid = H5.H5Aget_space(new_obj_id);
529                long reg_ndims = H5.H5Sget_simple_extent_ndims(new_obj_sid);
530                //rf.region_desc = dump_region_attrs(regStr, new_obj_id);
531            }
532            catch (Exception ex3) {
533                log.debug("initRegionAttribute Space Open", ex3);
534            }
535            finally {
536                H5.H5Sclose(new_obj_sid);
537            }
538            log.trace("initRegionAttribute finish");
539        }
540        catch (Exception ex2) {
541            log.debug("initRegionAttribute ", ex2);
542        }
543        finally {
544            H5.H5Aclose(new_obj_id);
545        }
546    }
547
548    private void initRegionDataset(int refndx, byte[] refarr) {
549        H5ReferenceData rf = refdata.get(refndx);
550        long new_obj_id = HDF5Constants.H5I_INVALID_HID;
551        try {
552            log.trace("initRegionDataset refarr2={}:", refarr);
553            new_obj_id = H5.H5Ropen_object(refarr, HDF5Constants.H5P_DEFAULT, HDF5Constants.H5P_DEFAULT);
554            long new_obj_sid = HDF5Constants.H5I_INVALID_HID;
555            try {
556                log.trace("initRegionDataset refarr3={}:", refarr);
557                new_obj_sid = H5.H5Ropen_region(refarr, HDF5Constants.H5P_DEFAULT, HDF5Constants.H5P_DEFAULT);
558                try {
559                    int region_type = H5.H5Sget_select_type(new_obj_sid);
560                    log.debug("Reference Region Type {}", region_type);
561                    long reg_ndims = H5.H5Sget_simple_extent_ndims(new_obj_sid);
562                    StringBuilder sb = new StringBuilder();
563                    if (HDF5Constants.H5S_SEL_POINTS == region_type) {
564                        rf.region_type = "REGION_TYPE POINT";
565                        long reg_npoints = H5.H5Sget_select_elem_npoints(new_obj_sid);
566                        long getcoord[] = new long[(int)(reg_ndims * reg_npoints)];
567                        try {
568                            H5.H5Sget_select_elem_pointlist(new_obj_sid, 0, reg_npoints, getcoord);
569                        }
570                        catch (Exception ex5) {
571                            log.debug("initRegionDataset H5.H5Sget_select_elem_pointlist: ", ex5);
572                        }
573                        sb.append("{ ");
574                        for (int i = 0; i < (int)reg_npoints; i++) {
575                            if (i > 0)
576                                sb.append(" ");
577                            sb.append("(");
578                            for (int j = 0; j < (int)reg_ndims; j++) {
579                                if (j > 0)
580                                    sb.append(",");
581                                sb.append(getcoord[i * (int)reg_ndims + j]);
582                            }
583                            sb.append(")");
584                        }
585                        sb.append(" }");
586                        rf.region_desc = sb.toString();
587                    }
588                    else if (HDF5Constants.H5S_SEL_HYPERSLABS == region_type) {
589                        rf.region_type = "REGION_TYPE BLOCK";
590                        long reg_nblocks = H5.H5Sget_select_hyper_nblocks(new_obj_sid);
591                        long getblocks[] = new long[(int)(reg_ndims * reg_nblocks) * 2];
592                        try {
593                            H5.H5Sget_select_hyper_blocklist(new_obj_sid, 0, reg_nblocks, getblocks);
594                        }
595                        catch (Exception ex5) {
596                            log.debug("initRegionDataset H5.H5Sget_select_hyper_blocklist: ", ex5);
597                        }
598                        sb.append("{ ");
599                        for (int i = 0; i < (int)reg_nblocks; i++) {
600                            if (i > 0)
601                                sb.append(" ");
602                            sb.append("(");
603                            for (int j = 0; j < (int)reg_ndims; j++) {
604                                if (j > 0)
605                                    sb.append(",");
606                                sb.append(getblocks[i * 2 * (int)reg_ndims + j]);
607                            }
608                            sb.append(")-(");
609                            for (int j = 0; j < (int)reg_ndims; j++) {
610                                if (j > 0)
611                                    sb.append(",");
612                                sb.append(getblocks[i * 2 * (int)reg_ndims + (int)reg_ndims + j]);
613                            }
614                            sb.append(")");
615                        }
616                        sb.append(" }");
617                        rf.region_desc = sb.toString();
618                    }
619                    else
620                        rf.region_type = "REGION_TYPE UNKNOWN";
621                }
622                catch (Exception ex4) {
623                    log.debug("initRegionDataset Region Type", ex4);
624                }
625            }
626            catch (Exception ex3) {
627                log.debug("initRegionDataset Space Open", ex3);
628            }
629            finally {
630                H5.H5Sclose(new_obj_sid);
631            }
632            log.trace("initRegionDataset finish");
633        }
634        catch (Exception ex2) {
635            log.debug("initRegionDataset ", ex2);
636        }
637        finally {
638            H5.H5Dclose(new_obj_id);
639        }
640    }
641
642    /**
643     * Checks if a reference datatype is all zero.
644     *
645     * @param refarr
646     *            the reference datatype data to be checked.
647     *
648     * @return true is the reference datatype data is all zero; otherwise returns false.
649     */
650    public static final boolean zeroArrayCheck(final byte[] refarr) {
651        for (byte b : refarr) {
652            if (b != 0)
653                return false;
654        }
655        return true;
656    }
657
658    /**
659     * Get the reference datatype reference name.
660     *
661     * @param refarr
662     *            the reference datatype data to be queried.
663     *
664     * @return the reference datatype name string, null otherwise.
665     */
666    public final String getObjectReferenceName(byte[] refarr) {
667        if (!inited)
668            init();
669
670        // find the index that matches refarr and ref_array
671        H5ReferenceData rf = null;
672        for (int i = 0; i < (int)refsize; i++) {
673            byte[] theref = refdata.get(i).ref_array;
674            if (Arrays.equals(theref, refarr)) {
675                rf = refdata.get(i);
676                break;
677            }
678        }
679        if (rf == null)
680            return null;
681
682        StringBuilder sb = new StringBuilder();
683        if (!rf.obj_name.equals("NULL")) {
684            sb.append(rf.obj_name);
685        }
686        if (!rf.attr_name.equals("NULL")) {
687            if (sb.length() > 0)
688                sb.append("/");
689            sb.append(rf.attr_name);
690        }
691        if (!rf.region_desc.equals("NULL")) {
692            if (sb.length() > 0)
693                sb.append(" ");
694            sb.append(rf.region_desc);
695        }
696        log.debug("Reference Object Name {}", sb);
697        return sb.toString();
698    }
699
700    /**
701     * Get the reference datatype reference name.
702     *
703     * @param refarr
704     *            the reference datatype data to be queried.
705     *
706     * @return the reference datatype name string, null otherwise.
707     */
708    public final String getFullReferenceName(byte[] refarr) {
709        if (!inited)
710            init();
711
712        // find the index that matches refarr and ref_array
713        H5ReferenceData rf = null;
714        for (int i = 0; i < (int)refsize; i++) {
715            byte[] theref = refdata.get(i).ref_array;
716            if (Arrays.equals(theref, refarr)) {
717                rf = refdata.get(i);
718                break;
719            }
720        }
721        if (rf == null)
722            return null;
723
724        StringBuilder sb = new StringBuilder();
725        if (!rf.file_name.equals("NULL"))
726            sb.append(rf.file_name);
727        if (!rf.obj_name.equals("NULL")) {
728            if (sb.length() > 0)
729                sb.append("/");
730            sb.append(rf.obj_name);
731        }
732        if (!rf.attr_name.equals("NULL")) {
733            if (sb.length() > 0)
734                sb.append("/");
735            sb.append(rf.attr_name);
736        }
737        if (!rf.region_desc.equals("NULL")) {
738            if (sb.length() > 0)
739                sb.append(" ");
740            sb.append(rf.region_desc);
741        }
742        log.debug("Full Reference Name {}", sb);
743        return sb.toString();
744    }
745
746
747    /**
748     * Get the reference datatype dataset region reference as string.
749     *
750     * @param refarr
751     *            the reference datatype data to be queried.
752     *
753     * @return the reference datatype name string, null otherwise.
754     */
755    public final String getRegionDataset(byte[] refarr) {
756        if (!inited)
757            init();
758
759        // find the index that matches refarr and ref_array
760        H5ReferenceData rf = null;
761        for (int i = 0; i < (int)refsize; i++) {
762            byte[] theref = refdata.get(i).ref_array;
763            if (Arrays.equals(theref, refarr)) {
764                rf = refdata.get(i);
765                break;
766            }
767        }
768        if (rf == null)
769            return null;
770
771        StringBuilder sb = new StringBuilder();
772        sb.append(rf.region_type);
773        if (!rf.region_desc.equals("NULL")) {
774            if (sb.length() > 0)
775                sb.append(" ");
776            sb.append(rf.region_desc);
777        }
778        log.debug("getRegionDataset Value {}", sb);
779        return sb.toString();
780    }
781
782    /**
783     * Get the reference datatype data.
784     *
785     * @param refarr
786     *            the reference datatype data to be queried.
787     *
788     * @return the reference datatype data.
789     */
790    public final H5ReferenceData getReferenceData(byte[] refarr) {
791        if (!inited)
792            init();
793
794        // find the index that matches refarr and ref_array
795        H5ReferenceData rf = null;
796        for (int i = 0; i < (int)refsize; i++) {
797            byte[] theref = refdata.get(i).ref_array;
798            if (Arrays.equals(theref, refarr)) {
799                rf = refdata.get(i);
800                break;
801            }
802        }
803        return rf;
804    }
805
806    /**
807     * Get the reference datatype region reference as string.
808     *
809     * @param refarr
810     *            the reference datatype data to be queried.
811     * @param showData
812     *            show the reference region dims
813     *
814     * @return the reference datatype name string, null otherwise.
815     */
816    public final String getReferenceRegion(byte[] refarr, boolean showData) {
817        if (!inited)
818            init();
819
820        // find the index that matches refarr and ref_array
821        H5ReferenceData rf = null;
822        for (int i = 0; i < (int)refsize; i++) {
823            byte[] theref = refdata.get(i).ref_array;
824            if (Arrays.equals(theref, refarr)) {
825                rf = refdata.get(i);
826                break;
827            }
828        }
829        if (rf == null)
830            return null;
831
832        StringBuilder objsb = new StringBuilder();
833        if (!rf.file_name.equals("NULL"))
834            objsb.append(rf.file_name);
835        if (!rf.obj_name.equals("NULL")) {
836            objsb.append(rf.obj_name);
837        }
838        if (!rf.attr_name.equals("NULL")) {
839            if (objsb.length() > 0)
840                objsb.append("/");
841            objsb.append(rf.attr_name);
842        }
843        log.debug("getReferenceRegion Region Name {}", objsb);
844
845        StringBuilder regsb = new StringBuilder();
846        if (!rf.region_type.equals("NULL"))
847            regsb.append(rf.region_type);
848        if (!rf.region_desc.equals("NULL")) {
849            if (regsb.length() > 0)
850                regsb.append(" ");
851            regsb.append(rf.region_desc);
852        }
853        log.debug("getReferenceRegion Region Type {}", regsb);
854        StringBuilder sb = new StringBuilder(objsb);
855        if (regsb.length() > 0) {
856            sb.append(" ");
857            sb.append(regsb);
858        }
859        if (sb.length() > 0)
860            return sb.toString();
861        else
862            return "NULL";
863    }
864
865    /**
866     * Returns a string representation of the data value. For
867     * example, "0, 255".
868     *
869     * For a compound datatype, it will be a 1D array of strings with field
870     * members separated by the delimiter. For example,
871     * "{0, 10.5}, {255, 20.0}, {512, 30.0}" is a compound attribute of {int,
872     * float} of three data points.
873     *
874     * @param delimiter
875     *            The delimiter used to separate individual data points. It
876     *            can be a comma, semicolon, tab or space. For example,
877     *            toString(",") will separate data by commas.
878     *
879     * @return the string representation of the data values.
880     */
881    public String toString(String delimiter) {
882        return toString(delimiter, -1);
883    }
884
885    /**
886     * Returns a string representation of the data value.
887     *
888     * @param delimiter
889     *            The delimiter used to separate individual data points. It
890     *            can be a comma, semicolon, tab or space. For example,
891     *            toString(",") will separate data by commas.
892     * @param maxItems
893     *            The maximum number of Array values to return
894     *
895     * @return the string representation of the data values.
896     */
897    public String toString(String delimiter, int maxItems) {
898        Object theData = originalRefBuf;
899        if (theData == null) {
900            log.debug("toString: value is null");
901            return null;
902        }
903
904        if (theData instanceof List<?>) {
905            log.trace("toString: value is list");
906            return null;
907        }
908
909        Class<? extends Object> valClass = theData.getClass();
910
911        if (!valClass.isArray()) {
912            log.trace("toString: finish - not array");
913            String strValue = theData.toString();
914            if (maxItems > 0 && strValue.length() > maxItems)
915                // truncate the extra characters
916                strValue = strValue.substring(0, maxItems);
917            return strValue;
918        }
919
920        // value is an array
921        StringBuilder sb = new StringBuilder();
922        log.trace("toString: refsize={} isStdRef={} Array.getLength={}", refsize, isStdRef(), Array.getLength(theData));
923        if (isStdRef()) {
924            String cname = valClass.getName();
925            char dname = cname.charAt(cname.lastIndexOf('[') + 1);
926            log.trace("toString: isStdRef with cname={} dname={}", cname, dname);
927            for (int i = 0; i < (int)refsize; i++) {
928                int refIndex = HDF5Constants.H5R_REF_BUF_SIZE * i;
929                byte[] refarr = new byte[(int) HDF5Constants.H5R_REF_BUF_SIZE];
930                System.arraycopy((byte[])theData, refIndex, refarr, 0, (int)HDF5Constants.H5R_REF_BUF_SIZE);
931                log.trace("toString: refarr[{}]={}", i, refarr);
932                StringBuilder ref_str = new StringBuilder(getReferenceRegion(refarr, false));
933                if ((maxItems > 0) && (ref_str.length() > maxItems)) {
934                    ref_str.setLength(maxItems);
935                }
936                log.trace("toString: ref_str[{}]={}", i, ref_str);
937                if (i > 0)
938                    sb.append(", ");
939                sb.append(ref_str);
940            }
941            return sb.toString();
942        }
943        return toString(delimiter, maxItems);
944    }
945
946    /**
947     * The individual reference data for a given object.
948     */
949    public static class H5ReferenceData
950    {
951        private static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(H5ReferenceData.class);
952
953        /** The reference array raw data */
954        public byte[] ref_array = new byte[(int) HDF5Constants.H5R_REF_BUF_SIZE];
955
956        /** The the full file path referenced */
957        public String file_fullpath;
958
959        /** The file name referenced */
960        public String file_name;
961
962        /** The object name referenced */
963        public String obj_name;
964
965        /** The attribute name referenced */
966        public String attr_name;
967
968        /** The type of region referenced */
969        public String region_type;
970
971        /** The point/block description of region referenced */
972        public String region_desc;
973
974        /** The default type of region referenced */
975        public int ref_type = HDF5Constants.H5R_BADTYPE;
976
977        /** The default type of object referenced */
978        public int obj_type = HDF5Constants.H5O_TYPE_UNKNOWN;
979
980        /**
981         *  Copy the individual reference array for further processing
982         *
983         * @param theArray
984         *            the reference datatype data to be copied.
985         */
986        H5ReferenceData(byte[] theArray)
987        {
988            System.arraycopy(theArray, 0, ref_array, 0, (int)HDF5Constants.H5R_REF_BUF_SIZE);
989        }
990    }
991}