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