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.io.File;
018import java.lang.reflect.Array;
019import java.nio.ByteBuffer;
020import java.util.ArrayList;
021import java.util.Hashtable;
022import java.util.Iterator;
023import java.util.LinkedList;
024import java.util.List;
025import java.util.Queue;
026import java.util.Vector;
027
028import hdf.object.Attribute;
029import hdf.object.Dataset;
030import hdf.object.Datatype;
031import hdf.object.FileFormat;
032import hdf.object.Group;
033import hdf.object.HObject;
034import hdf.object.ScalarDS;
035import hdf.object.h5.H5Attribute;
036import hdf.object.h5.H5CompoundAttr;
037import hdf.object.h5.H5Datatype;
038import hdf.object.h5.H5ReferenceType;
039import hdf.object.h5.H5ReferenceType.H5ReferenceData;
040import hdf.object.h5.H5ScalarAttr;
041
042import hdf.hdf5lib.H5;
043import hdf.hdf5lib.HDF5Constants;
044import hdf.hdf5lib.HDFNativeData;
045import hdf.hdf5lib.exceptions.HDF5Exception;
046import hdf.hdf5lib.structs.H5G_info_t;
047import hdf.hdf5lib.structs.H5L_info_t;
048import hdf.hdf5lib.structs.H5O_info_t;
049import hdf.hdf5lib.structs.H5O_token_t;
050
051import org.slf4j.Logger;
052import org.slf4j.LoggerFactory;
053
054/**
055 * H5File is an implementation of the FileFormat class for HDF5 files.
056 *
057 * The HDF5 file structure is made up of HObjects stored in a tree-like fashion. Each tree node represents an
058 * HDF5 object: a Group, Dataset, or Named Datatype. Starting from the root of the tree, <i>rootObject</i>,
059 * the tree can be traversed to find a specific object.
060 *
061 * The following example shows the implementation of finding an object for a given path in FileFormat. User
062 * applications can directly call the static method FileFormat.findObject(file, objPath) to get the object.
063 *
064 * <pre>
065 * HObject findObject(FileFormat file, String path) {
066 *     if (file == null || path == null)
067 *         return null;
068 *     if (!path.endsWith(&quot;/&quot;))
069 *         path = path + &quot;/&quot;;
070 *     HObject theRoot = file.getRootObject();
071 *     if (theRoot == null)
072 *         return null;
073 *     else if (path.equals(&quot;/&quot;))
074 *         return theRoot;
075 *
076 *     Iterator local_it = ((Group) theRoot)
077 *             .breadthFirstMemberList().iterator();
078 *     HObject theObj = null;
079 *     while (local_it.hasNext()) {
080 *         theObj = local_it.next();
081 *         String fullPath = theObj.getFullName() + &quot;/&quot;;
082 *         if (path.equals(fullPath) &amp;&amp;  theObj.getPath() != null ) {
083 *             break;
084 *     }
085 *     return theObj;
086 * }
087 * </pre>
088 *
089 * @author Peter X. Cao
090 * @version 2.4 9/4/2007
091 */
092public class H5File extends FileFormat {
093    private static final long serialVersionUID = 6247335559471526045L;
094
095    private static final Logger log = LoggerFactory.getLogger(H5File.class);
096
097    /**
098     * the file access flag. Valid values are
099     *   HDF5Constants.H5F_ACC_RDONLY,
100     *   HDF5Constants.H5F_ACC_SWMR_READ (with H5F_ACC_RDONLY)
101     *   HDF5Constants.H5F_ACC_RDWR
102     *   HDF5Constants.H5F_ACC_CREAT
103     */
104    private int flag;
105
106    /**
107     * The index type. Valid values are HDF5Constants.H5_INDEX_NAME, HDF5Constants.H5_INDEX_CRT_ORDER.
108     */
109    private int indexType = HDF5Constants.H5_INDEX_NAME;
110
111    /**
112     * The index order. Valid values are HDF5Constants.H5_ITER_INC, HDF5Constants.H5_ITER_DEC.
113     */
114    private int indexOrder = HDF5Constants.H5_ITER_INC;
115
116    /**
117     * The root object of the file hierarchy.
118     */
119    private HObject rootObject;
120
121    /**
122     * How many characters maximum in an attribute name?
123     */
124    private static final int attrNameLen = 256;
125
126    /**
127     * The library version bounds
128     */
129    private int[] libver;
130    /** The library latest version value */
131    public static final int LIBVER_LATEST = HDF5Constants.H5F_LIBVER_LATEST;
132    /** The library earliest version value */
133    public static final int LIBVER_EARLIEST = HDF5Constants.H5F_LIBVER_EARLIEST;
134    /** The library v1.8 version value */
135    public static final int LIBVER_V18 = HDF5Constants.H5F_LIBVER_V18;
136    /** The library v1.10 version value */
137    public static final int LIBVER_V110 = HDF5Constants.H5F_LIBVER_V110;
138    /** The library v1.12 version value */
139    public static final int LIBVER_V112 = HDF5Constants.H5F_LIBVER_V112;
140    /** The library v1.14 version value */
141    public static final int LIBVER_V114 = HDF5Constants.H5F_LIBVER_V114;
142
143    /**
144     * Indicate that this file is open for reading in a
145     * single-writer/multi-reader (SWMR) scenario. Note that
146     * the process(es) opening the file for SWMR reading must
147     * also open the file with the #H5F_ACC_RDONLY flag.
148     */
149    public static final int SWMR = MULTIREAD;
150
151    /**
152     * Enum to indicate the type of I/O to perform inside of the common I/O
153     * function.
154     */
155    public static enum IO_TYPE {
156        /** read IO type */
157        READ,
158        /** write IO type */
159        WRITE
160    }
161    ;
162
163    /***************************************************************************
164     * Constructor
165     **************************************************************************/
166    /**
167     * Constructs an H5File instance with an empty file name and read-only access.
168     */
169    public H5File() { this("", READ); }
170
171    /**
172     * Constructs an H5File instance with specified file name and read/write access.
173     *
174     * This constructor does not open the file for access, nor does it confirm that the file can be opened
175     * read/write.
176     *
177     * @param fileName
178     *            A valid file name, with a relative or absolute path.
179     *
180     * @throws NullPointerException
181     *             If the <code>fileName</code> argument is <code>null</code>.
182     */
183    public H5File(String fileName) { this(fileName, WRITE); }
184
185    /**
186     * Constructs an H5File instance with specified file name and access.
187     *
188     * The access parameter values and corresponding behaviors:
189     * <ul>
190     * <li>READ: Read-only access; open() will fail file doesn't exist.</li>
191     * <li>SWMR: Read-only access; open() will fail file doesn't exist.</li>
192     * <li>WRITE: Read/Write access; open() will fail if file doesn't exist or if file can't be opened with
193     * read/write access.</li> <li>CREATE: Read/Write access; create a new file or truncate an existing one;
194     * open() will fail if file can't be created or if file exists but can't be opened read/write.</li>
195     * </ul>
196     *
197     * This constructor does not open the file for access, nor does it confirm that the file can later be
198     * opened read/write or created.
199     *
200     * The flag returned by {@link #isReadOnly()} is set to true if the access parameter value is READ, even
201     * though the file isn't yet open.
202     *
203     * @param fileName
204     *            A valid file name, with a relative or absolute path.
205     * @param access
206     *            The file access flag, which determines behavior when file is opened. Acceptable values are
207     *            <code> READ, WRITE, </code> and <code>CREATE</code>.
208     *
209     * @throws NullPointerException
210     *             If the <code>fileName</code> argument is <code>null</code>.
211     */
212    public H5File(String fileName, int access)
213    {
214        // Call FileFormat ctor to set absolute path name
215        super(fileName);
216        libver    = new int[2];
217        libver[0] = HDF5Constants.H5F_LIBVER_EARLIEST;
218        libver[1] = HDF5Constants.H5F_LIBVER_LATEST;
219
220        if ((access & FILE_CREATE_OPEN) == FILE_CREATE_OPEN) {
221            File f = new File(fileName);
222            if (f.exists())
223                access = WRITE;
224            else
225                access = CREATE;
226        }
227
228        // set metadata for the instance
229        rootObject = null;
230        this.fid   = -1;
231        isReadOnly = (READ == (access & READ)) || (MULTIREAD == (access & MULTIREAD));
232
233        // At this point we just set up the flags for what happens later.
234        // We just pass unexpected access values on... subclasses may have
235        // their own values.
236        if (MULTIREAD == (access & MULTIREAD))
237            flag = HDF5Constants.H5F_ACC_RDONLY | HDF5Constants.H5F_ACC_SWMR_READ;
238        else if (READ == (access & READ))
239            flag = HDF5Constants.H5F_ACC_RDONLY;
240        else if (access == WRITE)
241            flag = HDF5Constants.H5F_ACC_RDWR;
242        else if (access == CREATE)
243            flag = HDF5Constants.H5F_ACC_CREAT;
244        else
245            flag = access;
246    }
247
248    /***************************************************************************
249     * Class methods
250     **************************************************************************/
251
252    /**
253     * Copies the attributes of one object to another object.
254     *
255     * This method copies all the attributes from one object (source object) to another (destination object).
256     * If an attribute already exists in the destination object, the attribute will not be copied. Attribute
257     * names exceeding 256 characters will be truncated in the destination object.
258     *
259     * The object can be an H5Group, an H5Dataset, or a named H5Datatype. This method is in the H5File class
260     * because there is no H5Object class and it is specific to HDF5 objects.
261     *
262     * The copy can fail for a number of reasons, including an invalid source or destination object, but no
263     * exceptions are thrown. The actual copy is carried out by the method: {@link #copyAttributes(long,
264     * long)}
265     *
266     * @param src
267     *            The source object.
268     * @param dst
269     *            The destination object.
270     *
271     * @see #copyAttributes(long, long)
272     */
273    public static final void copyAttributes(HObject src, HObject dst)
274    {
275        if ((src != null) && (dst != null)) {
276            long srcID = src.open();
277            long dstID = dst.open();
278
279            if ((srcID >= 0) && (dstID >= 0))
280                copyAttributes(srcID, dstID);
281
282            if (srcID >= 0)
283                src.close(srcID);
284
285            if (dstID >= 0)
286                dst.close(dstID);
287        }
288    }
289
290    /**
291     * Copies the attributes of one object to another object.
292     *
293     * This method copies all the attributes from one object (source object) to another (destination object).
294     * If an attribute already exists in the destination object, the attribute will not be copied. Attribute
295     * names exceeding 256 characters will be truncated in the destination object.
296     *
297     * The object can be an H5Group, an H5Dataset, or a named H5Datatype. This method is in the H5File class
298     * because there is no H5Object class and it is specific to HDF5 objects.
299     *
300     * The copy can fail for a number of reasons, including an invalid source or destination object
301     * identifier, but no exceptions are thrown.
302     *
303     * @param src_id
304     *            The identifier of the source object.
305     * @param dst_id
306     *            The identifier of the destination object.
307     */
308    public static final void copyAttributes(long src_id, long dst_id)
309    {
310        log.trace("copyAttributes(): start: src_id={} dst_id={}", src_id, dst_id);
311        long aid_src        = -1;
312        long aid_dst        = -1;
313        long asid           = -1;
314        long atid           = -1;
315        String aName        = null;
316        H5O_info_t obj_info = null;
317
318        try {
319            obj_info = H5.H5Oget_info(src_id);
320        }
321        catch (Exception ex) {
322            obj_info.num_attrs = -1;
323        }
324
325        if (obj_info.num_attrs < 0) {
326            log.debug("copyAttributes(): no attributes");
327            return;
328        }
329
330        for (int i = 0; i < obj_info.num_attrs; i++) {
331            try {
332                aid_src = H5.H5Aopen_by_idx(src_id, ".", HDF5Constants.H5_INDEX_CRT_ORDER,
333                                            HDF5Constants.H5_ITER_INC, i, HDF5Constants.H5P_DEFAULT,
334                                            HDF5Constants.H5P_DEFAULT);
335                aName   = H5.H5Aget_name(aid_src);
336                atid    = H5.H5Aget_type(aid_src);
337                asid    = H5.H5Aget_space(aid_src);
338
339                aid_dst = H5.H5Acreate(dst_id, aName, atid, asid, HDF5Constants.H5P_DEFAULT,
340                                       HDF5Constants.H5P_DEFAULT);
341
342                // use native data copy
343                H5.H5Acopy(aid_src, aid_dst);
344            }
345            catch (Exception ex) {
346                log.debug("copyAttributes(): Attribute[{}] failure: ", i, ex);
347            }
348
349            try {
350                H5.H5Sclose(asid);
351            }
352            catch (Exception ex) {
353                log.debug("copyAttributes(): Attribute[{}] H5Sclose(asid {}) failure: ", i, asid, ex);
354            }
355            try {
356                H5.H5Tclose(atid);
357            }
358            catch (Exception ex) {
359                log.debug("copyAttributes(): Attribute[{}] H5Tclose(atid {}) failure: ", i, atid, ex);
360            }
361            try {
362                H5.H5Aclose(aid_src);
363            }
364            catch (Exception ex) {
365                log.debug("copyAttributes(): Attribute[{}] H5Aclose(aid_src {}) failure: ", i, aid_src, ex);
366            }
367            try {
368                H5.H5Aclose(aid_dst);
369            }
370            catch (Exception ex) {
371                log.debug("copyAttributes(): Attribute[{}] H5Aclose(aid_dst {}) failure: ", i, aid_dst, ex);
372            }
373
374        } // (int i=0; i<num_attr; i++)
375    }
376
377    /**
378     * Returns a list of attributes for the specified object.
379     *
380     * This method returns a list containing the attributes associated with the
381     * identified object. If there are no associated attributes, an empty list will
382     * be returned.
383     *
384     * Attribute names exceeding 256 characters will be truncated in the returned
385     * list.
386     *
387     * @param obj
388     *            The HObject whose attributes are to be returned.
389     *
390     * @return The list of the object's attributes.
391     *
392     * @throws HDF5Exception
393     *             If an underlying HDF library routine is unable to perform a step
394     *             necessary to retrieve the attributes. A variety of failures throw
395     *             this exception.
396     *
397     * @see #getAttribute(HObject,int,int)
398     */
399    public static final List<Attribute> getAttribute(HObject obj) throws HDF5Exception
400    {
401        return H5File.getAttribute(obj, HDF5Constants.H5_INDEX_NAME, HDF5Constants.H5_ITER_INC);
402    }
403
404    /**
405     * Returns a list of attributes for the specified object, in creation or
406     * alphabetical order.
407     *
408     * This method returns a list containing the attributes associated with the
409     * identified object. If there are no associated attributes, an empty list will
410     * be returned. The list of attributes returned can be in increasing or
411     * decreasing, creation or alphabetical order.
412     *
413     * Attribute names exceeding 256 characters will be truncated in the returned
414     * list.
415     *
416     * @param obj
417     *            The HObject whose attributes are to be returned.
418     * @param idx_type
419     *            The type of index. Valid values are:
420     *            <ul>
421     *            <li>H5_INDEX_NAME: An alpha-numeric index by attribute name
422     *            <li>H5_INDEX_CRT_ORDER: An index by creation order
423     *            </ul>
424     * @param order
425     *            The index traversal order. Valid values are:
426     *            <ul>
427     *            <li>H5_ITER_INC: A top-down iteration incrementing the index
428     *            position at each step.
429     *            <li>H5_ITER_DEC: A bottom-up iteration decrementing the index
430     *            position at each step.
431     *            </ul>
432     *
433     * @return The list of the object's attributes.
434     *
435     * @throws HDF5Exception
436     *             If an underlying HDF library routine is unable to perform a step
437     *             necessary to retrieve the attributes. A variety of failures throw
438     *             this exception.
439     */
440
441    public static final List<Attribute> getAttribute(HObject obj, int idx_type, int order)
442        throws HDF5Exception
443    {
444        log.trace("getAttribute(): start: obj={} idx_type={} order={}", obj, idx_type, order);
445        List<Attribute> attributeList = null;
446        long objID                    = -1;
447        long aid                      = -1;
448        long sid                      = -1;
449        long tid                      = -1;
450        H5O_info_t obj_info           = null;
451
452        objID = obj.open();
453        if (objID >= 0) {
454            try {
455                try {
456                    log.trace("getAttribute(): get obj_info");
457                    obj_info = H5.H5Oget_info(objID);
458                }
459                catch (Exception ex) {
460                    log.debug("getAttribute(): H5Oget_info(objID {}) failure: ", objID, ex);
461                }
462                if (obj_info.num_attrs <= 0) {
463                    log.trace("getAttribute(): no attributes");
464                    return (attributeList = new Vector<>());
465                }
466
467                int n         = (int)obj_info.num_attrs;
468                attributeList = new Vector<>(n);
469                log.trace("getAttribute(): num_attrs={}", n);
470
471                for (int i = 0; i < n; i++) {
472                    long lsize = 1;
473                    log.trace("getAttribute(): attribute[{}]", i);
474
475                    try {
476                        aid = H5.H5Aopen_by_idx(objID, ".", idx_type, order, i, HDF5Constants.H5P_DEFAULT,
477                                                HDF5Constants.H5P_DEFAULT);
478                        sid = H5.H5Aget_space(aid);
479                        log.trace("getAttribute(): Attribute[{}] aid={} sid={}", i, aid, sid);
480
481                        long dims[] = null;
482                        int rank    = H5.H5Sget_simple_extent_ndims(sid);
483
484                        log.trace("getAttribute(): Attribute[{}] isScalar={}", i, (rank == 0));
485
486                        if (rank > 0) {
487                            dims = new long[rank];
488                            H5.H5Sget_simple_extent_dims(sid, dims, null);
489                            log.trace("getAttribute(): Attribute[{}] rank={}, dims={}", i, rank, dims);
490                            for (int j = 0; j < dims.length; j++) {
491                                lsize *= dims[j];
492                            }
493                        }
494
495                        String nameA = H5.H5Aget_name(aid);
496                        log.trace("getAttribute(): Attribute[{}] is {} with lsize={}", i, nameA, lsize);
497
498                        long tmptid = -1;
499                        try {
500                            tmptid = H5.H5Aget_type(aid);
501                            tid    = H5.H5Tget_native_type(tmptid);
502                            log.trace("getAttribute(): Attribute[{}] tid={} native tmptid={} from aid={}", i,
503                                      tid, tmptid, aid);
504                        }
505                        finally {
506                            try {
507                                H5.H5Tclose(tmptid);
508                            }
509                            catch (Exception ex) {
510                                log.debug("getAttribute(): Attribute[{}] H5Tclose(tmptid {}) failure: ", i,
511                                          tmptid, ex);
512                            }
513                        }
514
515                        H5Datatype attrType = null;
516                        try {
517                            int nativeClass = H5.H5Tget_class(tid);
518                            if (nativeClass == HDF5Constants.H5T_REFERENCE)
519                                attrType = new H5ReferenceType(obj.getFileFormat(), lsize, tid);
520                            else
521                                attrType = new H5Datatype(obj.getFileFormat(), tid);
522
523                            log.trace("getAttribute(): Attribute[{}] Datatype={}", i,
524                                      attrType.getDescription());
525                            log.trace(
526                                "getAttribute(): Attribute[{}] has size={} isCompound={} is_variable_str={} isVLEN={}",
527                                i, lsize, attrType.isCompound(), attrType.isVarStr(), attrType.isVLEN());
528                        }
529                        catch (Exception ex) {
530                            log.debug("getAttribute(): failed to create datatype for Attribute[{}]: ", i, ex);
531                            attrType = null;
532                        }
533
534                        Attribute attr = null;
535                        if (attrType.isCompound())
536                            attr = (Attribute) new H5CompoundAttr(obj, nameA, attrType, dims);
537                        else
538                            attr = (Attribute) new H5ScalarAttr(obj, nameA, attrType, dims);
539                        attributeList.add(attr);
540
541                        // retrieve the attribute value
542                        if (lsize <= 0) {
543                            log.debug("getAttribute(): Attribute[{}] lsize <= 0", i);
544                            continue;
545                        }
546
547                        if (lsize < Integer.MIN_VALUE || lsize > Integer.MAX_VALUE) {
548                            log.debug(
549                                "getAttribute(): Attribute[{}] lsize outside valid Java int range; unsafe cast",
550                                i);
551                            continue;
552                        }
553
554                        try {
555                            // attr.AttributeCommonIO(aid, H5File.IO_TYPE.READ, null);
556                            Object attrData = attr.getAttributeData();
557                            log.trace("getAttribute(): attrType.isRef()={}", attrType.isRef());
558                            if (attrType.isRef()) {
559                                if (attr.getAttributeRank() > 2)
560                                    ((H5ReferenceType)attrType).setRefSize(attr.getAttributePlane());
561                                ((H5ReferenceType)attrType).setData(attrData);
562                            }
563                        }
564                        catch (Exception ex) {
565                            log.debug("getAttribute(): failed to read attribute: ", ex);
566                        }
567                    }
568                    catch (HDF5Exception ex) {
569                        log.debug("getAttribute(): Attribute[{}] inspection failure: ", i, ex);
570                    }
571                    finally {
572                        try {
573                            H5.H5Tclose(tid);
574                        }
575                        catch (Exception ex) {
576                            log.debug("getAttribute(): Attribute[{}] H5Tclose(tid {}) failure: ", i, tid, ex);
577                        }
578                        try {
579                            H5.H5Sclose(sid);
580                        }
581                        catch (Exception ex) {
582                            log.debug("getAttribute(): Attribute[{}] H5Sclose(aid {}) failure: ", i, sid, ex);
583                        }
584                        try {
585                            H5.H5Aclose(aid);
586                        }
587                        catch (Exception ex) {
588                            log.debug("getAttribute(): Attribute[{}] H5Aclose(aid {}) failure: ", i, aid, ex);
589                        }
590                    }
591                } // (int i=0; i<obj_info.num_attrs; i++)
592                for (int i = 0; i < n; i++) {
593                    Attribute attr       = (Attribute)attributeList.get(i);
594                    H5Datatype atype     = (H5Datatype)attr.getAttributeDatatype();
595                    H5Datatype aBasetype = (H5Datatype)atype.getDatatypeBase();
596                    boolean BDTisRef     = false;
597                    if (aBasetype != null)
598                        BDTisRef = aBasetype.isRef();
599                    if (atype.isRef() || BDTisRef) {
600                        H5ReferenceType rtype = null;
601                        if (BDTisRef)
602                            rtype = (H5ReferenceType)aBasetype;
603                        else
604                            rtype = (H5ReferenceType)atype;
605                        try {
606                            List<H5ReferenceData> refdata = (List)rtype.getData();
607                            for (int r = 0; r < (int)rtype.getRefSize(); r++) {
608                                H5ReferenceData rf = refdata.get(r);
609                                log.trace("getAttribute(): refdata {}", rf.ref_array);
610                            }
611                        }
612                        catch (Exception ex) {
613                            log.trace("Error retrieving H5ReferenceData of object ", ex);
614                        }
615                    }
616                }
617            }
618            finally {
619                obj.close(objID);
620            }
621        }
622
623        return attributeList;
624    }
625
626    /**
627     * Creates attributes for an HDF5 image dataset.
628     *
629     * This method creates attributes for two common types of HDF5 images. It provides a way of adding
630     * multiple attributes to an HDF5 image dataset with a single call. The {@link #writeAttribute(HObject,
631     * Attribute, boolean)} method may be used to write image attributes that are not handled by this method.
632     *
633     * For more information about HDF5 image attributes, read <a
634     * href="https://hdfgroup.github.io/hdf5/_i_m_g.html">HDF5 Image and Palette Specification</a>
635     *
636     * This method can be called to create attributes for 24-bit true color and indexed images. The
637     * <code>selectionFlag</code> parameter controls whether this will be an indexed or true color image. If
638     * <code>selectionFlag</code> is <code>-1</code>, this will be an indexed image. If the value is
639     * <code>ScalarDS.INTERLACE_PIXEL</code> or <code>ScalarDS.INTERLACE_PLANE</code>, it will be a 24-bit
640     * true color image with the indicated interlace mode.
641     *
642     * <ul>
643     * The created attribute descriptions, names, and values are:
644     * <li>The image identifier: name="CLASS", value="IMAGE"
645     * <li>The version of image: name="IMAGE_VERSION", value="1.2"
646     * <li>The range of data values: name="IMAGE_MINMAXRANGE", value=[0, 255]
647     * <li>The type of the image: name="IMAGE_SUBCLASS", value="IMAGE_TRUECOLOR" or "IMAGE_INDEXED"
648     * <li>For IMAGE_TRUECOLOR, the interlace mode: name="INTERLACE_MODE", value="INTERLACE_PIXEL" or
649     * "INTERLACE_PLANE" <li>For IMAGE_INDEXED, the palettes to use in viewing the image: name="PALETTE",
650     * value= 1-d array of references to the palette datasets, with initial value of {-1}
651     * </ul>
652     *
653     * This method is in the H5File class rather than H5ScalarDS because images are typically thought of at
654     * the File Format implementation level.
655     *
656     * @param dataset       The image dataset the attributes are added to.
657     * @param selectionFlag Selects the image type and, for 24-bit true color images, the interlace mode.
658     *     Valid values
659     *                      are:
660     *                      <ul>
661     *                      <li>-1: Indexed Image.
662     *                      <li>ScalarDS.INTERLACE_PIXEL: True Color Image. The component values for a pixel
663     * are stored contiguously. <li>ScalarDS.INTERLACE_PLANE: True Color Image. Each component is stored in a
664     * separate plane.
665     *                      </ul>
666     *
667     * @throws Exception If there is a problem creating the attributes, or if the selectionFlag is invalid.
668     */
669    private static final void createImageAttributes(Dataset dataset, int selectionFlag) throws Exception
670    {
671        log.trace("createImageAttributes(): start: dataset={}", dataset.toString());
672        String subclass      = null;
673        String interlaceMode = null;
674
675        if (selectionFlag == ScalarDS.INTERLACE_PIXEL) {
676            log.trace("createImageAttributes(): subclass IMAGE_TRUECOLOR selectionFlag INTERLACE_PIXEL");
677            subclass      = "IMAGE_TRUECOLOR";
678            interlaceMode = "INTERLACE_PIXEL";
679        }
680        else if (selectionFlag == ScalarDS.INTERLACE_PLANE) {
681            log.trace("createImageAttributes(): subclass IMAGE_TRUECOLOR selectionFlag INTERLACE_PLANE");
682            subclass      = "IMAGE_TRUECOLOR";
683            interlaceMode = "INTERLACE_PLANE";
684        }
685        else if (selectionFlag == -1) {
686            log.trace("createImageAttributes(): subclass IMAGE_INDEXED");
687            subclass = "IMAGE_INDEXED";
688        }
689        else {
690            log.debug("createImageAttributes(): invalid selectionFlag");
691            throw new HDF5Exception("The selectionFlag is invalid.");
692        }
693
694        String attrName     = "CLASS";
695        String[] classValue = {"IMAGE"};
696        Datatype attrType = new H5Datatype(Datatype.CLASS_STRING, classValue[0].length() + 1, Datatype.NATIVE,
697                                           Datatype.NATIVE);
698        Attribute attr    = (Attribute) new H5ScalarAttr(dataset, attrName, attrType, null);
699        attr.writeAttribute(classValue);
700
701        attrName              = "IMAGE_VERSION";
702        String[] versionValue = {"1.2"};
703        attrType = new H5Datatype(Datatype.CLASS_STRING, versionValue[0].length() + 1, Datatype.NATIVE,
704                                  Datatype.NATIVE);
705        attr     = (Attribute) new H5ScalarAttr(dataset, attrName, attrType, null);
706        attr.writeAttribute(versionValue);
707
708        long[] attrDims     = {2};
709        attrName            = "IMAGE_MINMAXRANGE";
710        byte[] attrValueInt = {0, (byte)255};
711        attrType            = new H5Datatype(Datatype.CLASS_CHAR, 1, Datatype.NATIVE, Datatype.SIGN_NONE);
712        attr                = (Attribute) new H5ScalarAttr(dataset, attrName, attrType, attrDims);
713        attr.writeAttribute(attrValueInt);
714
715        attrName               = "IMAGE_SUBCLASS";
716        String[] subclassValue = {subclass};
717        attrType = new H5Datatype(Datatype.CLASS_STRING, subclassValue[0].length() + 1, Datatype.NATIVE,
718                                  Datatype.NATIVE);
719        attr     = (Attribute) new H5ScalarAttr(dataset, attrName, attrType, null);
720        attr.writeAttribute(subclassValue);
721
722        if ((selectionFlag == ScalarDS.INTERLACE_PIXEL) || (selectionFlag == ScalarDS.INTERLACE_PLANE)) {
723            attrName                = "INTERLACE_MODE";
724            String[] interlaceValue = {interlaceMode};
725            attrType = new H5Datatype(Datatype.CLASS_STRING, interlaceValue[0].length() + 1, Datatype.NATIVE,
726                                      Datatype.NATIVE);
727            attr     = (Attribute) new H5ScalarAttr(dataset, attrName, attrType, null);
728            attr.writeAttribute(interlaceValue);
729        }
730        else {
731            attrName      = "PALETTE";
732            String palRef = "."; // set ref to null
733            attrType      = new H5Datatype(Datatype.CLASS_REFERENCE, 1, Datatype.NATIVE, Datatype.SIGN_NONE);
734            attr          = (Attribute) new H5ScalarAttr(dataset, attrName, attrType, null);
735            attr.writeAttribute(palRef);
736        }
737    }
738
739    /**
740     * Updates values of scalar dataset object references in copied file.
741     *
742     * This method has very specific functionality as documented below, and the user is advised to pay close
743     * attention when dealing with files that contain references.
744     *
745     * When a copy is made from one HDF file to another, object references and dataset region references are
746     * copied, but the references in the destination file are not updated by the copy and are therefore
747     * invalid.
748     *
749     * When an entire file is copied, this method updates the values of the object references and dataset
750     * region references that are in scalar datasets in the destination file so that they point to the correct
751     * object(s) in the destination file. The method does not update references that occur in objects other
752     * than scalar datasets.
753     *
754     * In the current release, the updating of object references is not handled completely as it was not
755     * required by the projects that funded development. There is no support for updates when the copy does
756     * not include the entire file. Nor is there support for updating objects other than scalar datasets in
757     * full-file copies. This functionality will be extended as funding becomes available or, possibly, when
758     * the underlying HDF library supports the reference updates itself.
759     *
760     * @param srcFile
761     *            The file that was copied.
762     * @param dstFile
763     *            The destination file where the object references will be updated.
764     *
765     * @throws Exception
766     *             If there is a problem in the update process.
767     */
768    public static final void updateReferenceDataset(H5File srcFile, H5File dstFile) throws Exception
769    {
770        if ((srcFile == null) || (dstFile == null)) {
771            log.debug("updateReferenceDataset(): srcFile or dstFile is null");
772            return;
773        }
774
775        HObject srcRoot = srcFile.getRootObject();
776        HObject newRoot = dstFile.getRootObject();
777
778        Iterator<HObject> srcIt = getMembersBreadthFirst(srcRoot).iterator();
779        Iterator<HObject> newIt = getMembersBreadthFirst(newRoot).iterator();
780
781        long did = -1;
782        // build one-to-one table of between objects in
783        // the source file and new file
784        long tid = -1;
785        HObject srcObj, newObj;
786        Hashtable<String, long[]> oidMap = new Hashtable<>();
787        List<ScalarDS> refDatasets       = new Vector<>();
788        while (newIt.hasNext() && srcIt.hasNext()) {
789            srcObj = srcIt.next();
790            newObj = newIt.next();
791            oidMap.put(String.valueOf((srcObj.getOID())[0]), newObj.getOID());
792            did = -1;
793            tid = -1;
794
795            // for Scalar DataSets in destination, if there is an object
796            // reference in the dataset, add it to the refDatasets list for
797            // later updating.
798            if (newObj instanceof ScalarDS) {
799                ScalarDS sd = (ScalarDS)newObj;
800                did         = sd.open();
801                if (did >= 0) {
802                    try {
803                        tid = H5.H5Dget_type(did);
804                        if (H5.H5Tequal(tid, HDF5Constants.H5T_STD_REF)) {
805                            refDatasets.add(sd);
806                        }
807                    }
808                    catch (Exception ex) {
809                        log.debug("updateReferenceDataset(): ScalarDS reference failure: ", ex);
810                    }
811                    finally {
812                        try {
813                            H5.H5Tclose(tid);
814                        }
815                        catch (Exception ex) {
816                            log.debug(
817                                "updateReferenceDataset(): ScalarDS reference H5Tclose(tid {}) failure: ",
818                                tid, ex);
819                        }
820                    }
821                }
822                sd.close(did);
823            } // (newObj instanceof ScalarDS)
824        }
825
826        // Update the references in the scalar datasets in the dest file.
827        H5ScalarDS d   = null;
828        long sid       = -1;
829        int size       = 0;
830        int rank       = 0;
831        int space_type = -1;
832        int n          = refDatasets.size();
833        for (int i = 0; i < n; i++) {
834            log.trace(
835                "updateReferenceDataset(): Update the references in the scalar datasets in the dest file");
836            d           = (H5ScalarDS)refDatasets.get(i);
837            byte[] buf  = null;
838            long[] refs = null;
839
840            try {
841                did = d.open();
842                if (did >= 0) {
843                    tid        = H5.H5Dget_type(did);
844                    sid        = H5.H5Dget_space(did);
845                    rank       = H5.H5Sget_simple_extent_ndims(sid);
846                    space_type = H5.H5Sget_simple_extent_type(sid);
847                    size       = 1;
848                    if (rank > 0) {
849                        long[] dims = new long[rank];
850                        H5.H5Sget_simple_extent_dims(sid, dims, null);
851                        log.trace("updateReferenceDataset(): rank={}, dims={}, space_type={}", rank, dims,
852                                  space_type);
853                        for (int j = 0; j < rank; j++) {
854                            size *= (int)dims[j];
855                        }
856                        dims = null;
857                    }
858
859                    buf = new byte[size * 8];
860                    H5.H5Dread(did, tid, HDF5Constants.H5S_ALL, HDF5Constants.H5S_ALL,
861                               HDF5Constants.H5P_DEFAULT, buf);
862
863                    // update the ref values
864                    refs = HDFNativeData.byteToLong(buf);
865                    size = refs.length;
866                    for (int j = 0; j < size; j++) {
867                        long[] theOID = oidMap.get(String.valueOf(refs[j]));
868                        if (theOID != null) {
869                            refs[j] = theOID[0];
870                        }
871                    }
872
873                    // write back to file
874                    H5.H5Dwrite(did, tid, HDF5Constants.H5S_ALL, HDF5Constants.H5S_ALL,
875                                HDF5Constants.H5P_DEFAULT, refs);
876                }
877                else {
878                    log.debug("updateReferenceDataset(): dest file dataset failed to open");
879                }
880            }
881            catch (Exception ex) {
882                log.debug("updateReferenceDataset(): Reference[{}] failure: ", i, ex);
883                continue;
884            }
885            finally {
886                try {
887                    H5.H5Tclose(tid);
888                }
889                catch (Exception ex) {
890                    log.debug("updateReferenceDataset(): H5ScalarDS reference[{}] H5Tclose(tid {}) failure: ",
891                              i, tid, ex);
892                }
893                try {
894                    H5.H5Sclose(sid);
895                }
896                catch (Exception ex) {
897                    log.debug("updateReferenceDataset(): H5ScalarDS reference[{}] H5Sclose(sid {}) failure: ",
898                              i, sid, ex);
899                }
900                try {
901                    H5.H5Dclose(did);
902                }
903                catch (Exception ex) {
904                    log.debug("updateReferenceDataset(): H5ScalarDS reference[{}] H5Dclose(did {}) failure: ",
905                              i, did, ex);
906                }
907            }
908
909            refs = null;
910            buf  = null;
911        } // (int i=0; i<n; i++)
912    }
913
914    /***************************************************************************
915     * Implementation Class methods. These methods are related to the implementing H5File class, but not to a
916     *particular instance of the class. Since we can't override class methods (they can only be shadowed in
917     *Java), these are instance methods.
918     **************************************************************************/
919
920    /**
921     * Returns the version of the HDF5 library.
922     *
923     * @see hdf.object.FileFormat#getLibversion()
924     */
925    @Override
926    public String getLibversion()
927    {
928        int[] vers = new int[3];
929        String ver = "HDF5 ";
930
931        try {
932            H5.H5get_libversion(vers);
933        }
934        catch (Exception ex) {
935            ex.printStackTrace();
936        }
937
938        ver += vers[0] + "." + vers[1] + "." + vers[2];
939        log.debug("getLibversion(): libversion is {}", ver);
940
941        return ver;
942    }
943
944    /**
945     * Checks if the specified FileFormat instance has the HDF5 format.
946     *
947     * @see hdf.object.FileFormat#isThisType(hdf.object.FileFormat)
948     */
949    @Override
950    public boolean isThisType(FileFormat theFile)
951    {
952        return (theFile instanceof H5File);
953    }
954
955    /**
956     * Checks if the specified file has the HDF5 format.
957     *
958     * @see hdf.object.FileFormat#isThisType(java.lang.String)
959     */
960    @Override
961    public boolean isThisType(String filename)
962    {
963        boolean isH5 = false;
964
965        try {
966            isH5 = H5.H5Fis_hdf5(filename);
967        }
968        catch (HDF5Exception ex) {
969            isH5 = false;
970        }
971
972        return isH5;
973    }
974
975    /**
976     * Creates an HDF5 file with the specified name and returns a new H5File instance associated with the
977     * file.
978     *
979     * @throws Exception
980     *             If the file cannot be created or if createFlag has unexpected value.
981     *
982     * @see hdf.object.FileFormat#createFile(java.lang.String, int)
983     * @see #H5File(String, int)
984     */
985    @Override
986    public FileFormat createFile(String filename, int createFlag) throws Exception
987    {
988        log.trace("createFile(): start: filename={} createFlag={}", filename, createFlag);
989        // Flag if we need to create or truncate the file.
990        Boolean doCreateFile = true;
991
992        // Won't create or truncate if CREATE_OPEN specified and file exists
993        if ((createFlag & FILE_CREATE_OPEN) == FILE_CREATE_OPEN) {
994            File f = new File(filename);
995            if (f.exists()) {
996                doCreateFile = false;
997            }
998        }
999        log.trace("createFile(): doCreateFile={}", doCreateFile);
1000
1001        if (doCreateFile) {
1002            long fapl = H5.H5Pcreate(HDF5Constants.H5P_FILE_ACCESS);
1003
1004            if ((createFlag & FILE_CREATE_EARLY_LIB) == FILE_CREATE_EARLY_LIB) {
1005                int[] newlibver = getLibBounds();
1006                H5.H5Pset_libver_bounds(fapl, newlibver[0], newlibver[1]);
1007            }
1008
1009            long fileid =
1010                H5.H5Fcreate(filename, HDF5Constants.H5F_ACC_TRUNC, HDF5Constants.H5P_DEFAULT, fapl);
1011            try {
1012                H5.H5Pclose(fapl);
1013                H5.H5Fclose(fileid);
1014            }
1015            catch (HDF5Exception ex) {
1016                log.debug("H5 file, {} failure: ", filename, ex);
1017            }
1018        }
1019
1020        return new H5File(filename, WRITE);
1021    }
1022
1023    /**
1024     * Creates an H5File instance with specified file name and access.
1025     *
1026     * @see hdf.object.FileFormat#createInstance(java.lang.String, int)
1027     * @see #H5File(String, int)
1028     *
1029     * @throws Exception
1030     *            If there is a failure.
1031     */
1032    @Override
1033    public FileFormat createInstance(String filename, int access) throws Exception
1034    {
1035        log.trace("createInstance() for {} with {}", filename, access);
1036        return new H5File(filename, access);
1037    }
1038
1039    /***************************************************************************
1040     * Instance Methods
1041     *
1042     * These methods are related to the H5File class and to particular instances of objects with this class
1043     *type.
1044     **************************************************************************/
1045
1046    /**
1047     * Opens file and returns a file identifier.
1048     *
1049     * @see hdf.object.FileFormat#open()
1050     */
1051    @Override
1052    public long open() throws Exception
1053    {
1054        return open(true);
1055    }
1056
1057    /**
1058     * Opens file and returns a file identifier.
1059     *
1060     * @see hdf.object.FileFormat#open(int...)
1061     */
1062    @Override
1063    public long open(int... indexList) throws Exception
1064    {
1065        setIndexType(indexList[0]);
1066        setIndexOrder(indexList[1]);
1067        return open(true);
1068    }
1069
1070    /**
1071     * Sets the bounds of new library versions.
1072     *
1073     * @param lowStr
1074     *            The earliest version of the library.
1075     * @param highStr
1076     *            The latest version of the library.
1077     *
1078     * @throws Exception
1079     *             If there is an error at the HDF5 library level.
1080     */
1081    @Override
1082    public void setNewLibBounds(String lowStr, String highStr) throws Exception
1083    {
1084        int low  = -1;
1085        int high = -1;
1086
1087        if (lowStr == null)
1088            low = HDF5Constants.H5F_LIBVER_EARLIEST;
1089        else if (lowStr.equals("Earliest"))
1090            low = HDF5Constants.H5F_LIBVER_EARLIEST;
1091        else if (lowStr.equals("V18"))
1092            low = HDF5Constants.H5F_LIBVER_V18;
1093        else if (lowStr.equals("V110"))
1094            low = HDF5Constants.H5F_LIBVER_V110;
1095        else if (lowStr.equals("V112"))
1096            low = HDF5Constants.H5F_LIBVER_V112;
1097        else if (lowStr.equals("V114"))
1098            low = HDF5Constants.H5F_LIBVER_V114;
1099        else if (lowStr.equals("Latest"))
1100            low = HDF5Constants.H5F_LIBVER_LATEST;
1101        else
1102            low = HDF5Constants.H5F_LIBVER_EARLIEST;
1103
1104        if (highStr == null)
1105            high = HDF5Constants.H5F_LIBVER_LATEST;
1106        else if (highStr.equals("V18"))
1107            high = HDF5Constants.H5F_LIBVER_V18;
1108        else if (highStr.equals("V110"))
1109            high = HDF5Constants.H5F_LIBVER_V110;
1110        else if (highStr.equals("V112"))
1111            high = HDF5Constants.H5F_LIBVER_V112;
1112        else if (highStr.equals("V114"))
1113            high = HDF5Constants.H5F_LIBVER_V114;
1114        else if (highStr.equals("Latest"))
1115            high = HDF5Constants.H5F_LIBVER_LATEST;
1116        else
1117            high = HDF5Constants.H5F_LIBVER_LATEST;
1118        libver[0] = low;
1119        libver[1] = high;
1120    }
1121
1122    /**
1123     * Sets the bounds of library versions.
1124     *
1125     * @param lowStr
1126     *            The earliest version of the library.
1127     * @param highStr
1128     *            The latest version of the library.
1129     *
1130     * @throws Exception
1131     *             If there is an error at the HDF5 library level.
1132     */
1133    @Override
1134    public void setLibBounds(String lowStr, String highStr) throws Exception
1135    {
1136        long fapl = HDF5Constants.H5P_DEFAULT;
1137
1138        if (fid < 0)
1139            return;
1140
1141        fapl = H5.H5Fget_access_plist(fid);
1142
1143        try {
1144            int low  = -1;
1145            int high = -1;
1146
1147            if (lowStr == null)
1148                low = HDF5Constants.H5F_LIBVER_EARLIEST;
1149            else if (lowStr.equals("Earliest"))
1150                low = HDF5Constants.H5F_LIBVER_EARLIEST;
1151            else if (lowStr.equals("V18"))
1152                low = HDF5Constants.H5F_LIBVER_V18;
1153            else if (lowStr.equals("V110"))
1154                low = HDF5Constants.H5F_LIBVER_V110;
1155            else if (lowStr.equals("V112"))
1156                low = HDF5Constants.H5F_LIBVER_V112;
1157            else if (lowStr.equals("V114"))
1158                low = HDF5Constants.H5F_LIBVER_V114;
1159            else if (lowStr.equals("Latest"))
1160                low = HDF5Constants.H5F_LIBVER_LATEST;
1161            else
1162                low = HDF5Constants.H5F_LIBVER_EARLIEST;
1163
1164            if (highStr == null)
1165                high = HDF5Constants.H5F_LIBVER_LATEST;
1166            else if (highStr.equals("V18"))
1167                high = HDF5Constants.H5F_LIBVER_V18;
1168            else if (highStr.equals("V110"))
1169                high = HDF5Constants.H5F_LIBVER_V110;
1170            else if (highStr.equals("V112"))
1171                high = HDF5Constants.H5F_LIBVER_V112;
1172            else if (highStr.equals("V114"))
1173                high = HDF5Constants.H5F_LIBVER_V114;
1174            else if (highStr.equals("Latest"))
1175                high = HDF5Constants.H5F_LIBVER_LATEST;
1176            else
1177                high = HDF5Constants.H5F_LIBVER_LATEST;
1178
1179            H5.H5Pset_libver_bounds(fapl, low, high);
1180            H5.H5Pget_libver_bounds(fapl, libver);
1181        }
1182        finally {
1183            try {
1184                H5.H5Pclose(fapl);
1185            }
1186            catch (Exception e) {
1187                log.debug("setLibBounds(): libver bounds H5Pclose(fapl {}) failure: ", fapl, e);
1188            }
1189        }
1190    }
1191
1192    /**
1193     * Gets the bounds of library versions.
1194     *
1195     * @return libver The earliest and latest version of the library.
1196     *
1197     * @throws Exception
1198     *             If there is an error at the HDF5 library level.
1199     */
1200    @Override
1201    public int[] getLibBounds() throws Exception
1202    {
1203        if (libver.length == 0)
1204            initLibBounds();
1205        return libver;
1206    }
1207
1208    /**
1209     * Initialize the bounds of library versions
1210     *
1211     * @throws Exception
1212     *             The exceptions thrown vary depending on the implementing class.
1213     */
1214    @Override
1215    public void initLibBounds() throws Exception
1216    {
1217        if (fid >= 0) {
1218            /* Get the file's file access property list */
1219            long fapl = H5.H5Fget_access_plist(fid);
1220            /* Get library format */
1221            H5.H5Pget_libver_bounds(fapl, libver);
1222            /* Close FAPL */
1223            H5.H5Pclose(fapl);
1224        }
1225    }
1226
1227    /**
1228     * Gets the bounds of library versions as text.
1229     *
1230     * @return libversion The earliest and latest version of the library.
1231     */
1232    @Override
1233    public String getLibBoundsDescription()
1234    {
1235        String libversion = "";
1236
1237        if (libver[0] == HDF5Constants.H5F_LIBVER_EARLIEST)
1238            libversion = "Earliest and ";
1239        else if (libver[0] == HDF5Constants.H5F_LIBVER_V18)
1240            libversion = "V18 and ";
1241        else if (libver[0] == HDF5Constants.H5F_LIBVER_V110)
1242            libversion = "V110 and ";
1243        else if (libver[0] == HDF5Constants.H5F_LIBVER_V112)
1244            libversion = "V112 and ";
1245        else if (libver[0] == HDF5Constants.H5F_LIBVER_V114)
1246            libversion = "V114 and ";
1247        else if (libver[0] == HDF5Constants.H5F_LIBVER_LATEST)
1248            libversion = "Latest and ";
1249
1250        if (libver[1] == HDF5Constants.H5F_LIBVER_EARLIEST)
1251            libversion += "Earliest";
1252        else if (libver[1] == HDF5Constants.H5F_LIBVER_V18)
1253            libversion += "V18";
1254        else if (libver[1] == HDF5Constants.H5F_LIBVER_V110)
1255            libversion += "V110";
1256        else if (libver[1] == HDF5Constants.H5F_LIBVER_V112)
1257            libversion += "V112";
1258        else if (libver[1] == HDF5Constants.H5F_LIBVER_V114)
1259            libversion += "V114";
1260        else if (libver[1] == HDF5Constants.H5F_LIBVER_LATEST)
1261            libversion += "Latest";
1262        return libversion;
1263    }
1264
1265    /**
1266     * Closes file associated with this H5File instance.
1267     *
1268     * @see hdf.object.FileFormat#close()
1269     *
1270     * @throws HDF5Exception
1271     *             If there is an error at the HDF5 library level.
1272     */
1273    @Override
1274    public void close() throws HDF5Exception
1275    {
1276        if (fid < 0) {
1277            log.debug("close(): file {} is not open", fullFileName);
1278            return;
1279        }
1280        // The current working directory may be changed at Dataset.read()
1281        // by System.setProperty("user.dir", newdir) to make it work for external
1282        // datasets. We need to set it back to the original current working
1283        // directory (when hdf-java application started) before the file
1284        // is closed/opened. Otherwise, relative path, e.g. "./test.h5" may
1285        // not work
1286        String rootPath = System.getProperty("hdfview.workdir");
1287        if (rootPath == null) {
1288            rootPath = System.getProperty("user.dir");
1289        }
1290        System.setProperty("user.dir", rootPath); // H5.H5Dchdir_ext(rootPath);
1291
1292        // clean up unused objects
1293        if (rootObject != null) {
1294            HObject theObj       = null;
1295            Iterator<HObject> it = getMembersBreadthFirst(rootObject).iterator();
1296            while (it.hasNext()) {
1297                theObj = it.next();
1298
1299                if (theObj instanceof Dataset) {
1300                    log.trace("close(): clear Dataset {}", ((Dataset)theObj).toString());
1301                    ((Dataset)theObj).clear();
1302                }
1303                else if (theObj instanceof Group) {
1304                    log.trace("close(): clear Group {}", ((Group)theObj).toString());
1305                    ((Group)theObj).clear();
1306                }
1307            }
1308        }
1309
1310        // Close all open objects associated with this file.
1311        try {
1312            int type = -1;
1313            long[] objids;
1314            long n = H5.H5Fget_obj_count(fid, HDF5Constants.H5F_OBJ_ALL);
1315            log.trace("close(): open objects={}", n);
1316
1317            if (n > 0) {
1318                if (n < Integer.MIN_VALUE || n > Integer.MAX_VALUE)
1319                    throw new Exception("Invalid int size");
1320
1321                objids = new long[(int)n];
1322                H5.H5Fget_obj_ids(fid, HDF5Constants.H5F_OBJ_ALL, n, objids);
1323
1324                for (int i = 0; i < (int)n; i++) {
1325                    log.trace("close(): object[{}] id={}", i, objids[i]);
1326                    type = H5.H5Iget_type(objids[i]);
1327
1328                    if (HDF5Constants.H5I_DATASET == type) {
1329                        try {
1330                            H5.H5Dclose(objids[i]);
1331                        }
1332                        catch (Exception ex2) {
1333                            log.debug("close(): Object[{}] H5Dclose(objids[{}] {}) failure: ", i, i,
1334                                      objids[i], ex2);
1335                        }
1336                    }
1337                    else if (HDF5Constants.H5I_GROUP == type) {
1338                        try {
1339                            H5.H5Gclose(objids[i]);
1340                        }
1341                        catch (Exception ex2) {
1342                            log.debug("close(): Object[{}] H5Gclose(objids[{}] {}) failure: ", i, i,
1343                                      objids[i], ex2);
1344                        }
1345                    }
1346                    else if (HDF5Constants.H5I_DATATYPE == type) {
1347                        try {
1348                            H5.H5Tclose(objids[i]);
1349                        }
1350                        catch (Exception ex2) {
1351                            log.debug("close(): Object[{}] H5Tclose(objids[{}] {}) failure: ", i, i,
1352                                      objids[i], ex2);
1353                        }
1354                    }
1355                    else if (HDF5Constants.H5I_ATTR == type) {
1356                        try {
1357                            H5.H5Aclose(objids[i]);
1358                        }
1359                        catch (Exception ex2) {
1360                            log.debug("close(): Object[{}] H5Aclose(objids[{}] {}) failure: ", i, i,
1361                                      objids[i], ex2);
1362                        }
1363                    }
1364                    else if (HDF5Constants.H5I_FILE == type) {
1365                        int file_ref = H5.H5Iget_ref(objids[i]);
1366                        log.debug("close(): Object[{}] objids[{}] is type File with ref count of {}", i, i,
1367                                  file_ref);
1368                    }
1369                    else {
1370                        log.debug("close(): Object[{}] objids[{}] is type {}", i, i, type);
1371                    }
1372                } // (int i=0; i<n; i++)
1373            }     // ( n>0)
1374        }
1375        catch (Exception ex) {
1376            log.debug("close(): failure: ", ex);
1377        }
1378
1379        try {
1380            H5.H5Fflush(fid, HDF5Constants.H5F_SCOPE_GLOBAL);
1381        }
1382        catch (Exception ex) {
1383            log.debug("close(): H5Fflush(fid {}) failure: ", fid, ex);
1384        }
1385
1386        try {
1387            H5.H5Fclose(fid);
1388        }
1389        catch (Exception ex) {
1390            log.debug("close(): H5Fclose(fid {}) failure: ", fid, ex);
1391        }
1392
1393        // Set fid to -1 but don't reset rootObject
1394        fid = -1;
1395    }
1396
1397    /**
1398     * Returns the root object of the open HDF5 File.
1399     *
1400     * @see hdf.object.FileFormat#getRootObject()
1401     */
1402    @Override
1403    public HObject getRootObject()
1404    {
1405        return rootObject;
1406    }
1407
1408    /*
1409     * (non-Javadoc)
1410     *
1411     * @see hdf.object.FileFormat#get(java.lang.String)
1412     */
1413    @Override
1414    public HObject get(String path) throws Exception
1415    {
1416        log.trace("get({}): start", path);
1417        HObject obj = null;
1418
1419        if ((path == null) || (path.length() <= 0)) {
1420            log.debug("get(): path is null or invalid path length");
1421            System.err.println("(path == null) || (path.length() <= 0)");
1422            return null;
1423        }
1424
1425        // replace the wrong slash and get rid of "//"
1426        path = path.replace('\\', '/');
1427        path = "/" + path;
1428        path = path.replaceAll("//", "/");
1429
1430        // the whole file tree is loaded. find the object in the tree
1431        if (rootObject != null) {
1432            obj = findObject(this, path);
1433        }
1434
1435        // found object in memory
1436        if (obj != null) {
1437            log.trace("get(): Found object in memory");
1438            return obj;
1439        }
1440
1441        // open only the requested object
1442        String name  = null;
1443        String pPath = null;
1444        if (path.equals("/")) {
1445            name = "/"; // the root
1446        }
1447        else {
1448            // separate the parent path and the object name
1449            if (path.endsWith("/")) {
1450                path = path.substring(0, path.length() - 1);
1451            }
1452
1453            int idx = path.lastIndexOf('/');
1454            name    = path.substring(idx + 1);
1455            if (idx == 0) {
1456                pPath = "/";
1457            }
1458            else {
1459                pPath = path.substring(0, idx);
1460            }
1461        }
1462
1463        // do not open the full tree structure, only the file handler
1464        long fid_before_open = fid;
1465        fid                  = open(false);
1466        if (fid < 0) {
1467            log.debug("get(): Invalid FID");
1468            System.err.println("Could not open file handler");
1469            return null;
1470        }
1471
1472        try {
1473            H5O_info_t info;
1474            int objType;
1475            long objid = H5.H5Oopen(fid, path, HDF5Constants.H5P_DEFAULT);
1476
1477            if (objid >= 0) {
1478                info    = H5.H5Oget_info(objid);
1479                objType = info.type;
1480                if (objType == HDF5Constants.H5O_TYPE_DATASET) {
1481                    long did = -1;
1482                    try {
1483                        did = H5.H5Dopen(fid, path, HDF5Constants.H5P_DEFAULT);
1484                        obj = getDataset(did, name, pPath);
1485                    }
1486                    finally {
1487                        try {
1488                            H5.H5Dclose(did);
1489                        }
1490                        catch (Exception ex) {
1491                            log.debug("get(): {} H5Dclose(did {}) failure: ", path, did, ex);
1492                        }
1493                    }
1494                }
1495                else if (objType == HDF5Constants.H5O_TYPE_GROUP) {
1496                    long gid = -1;
1497                    try {
1498                        gid            = H5.H5Gopen(fid, path, HDF5Constants.H5P_DEFAULT);
1499                        H5Group pGroup = null;
1500                        if (pPath != null) {
1501                            pGroup = new H5Group(this, null, pPath, null);
1502                            obj    = getGroup(gid, name, pGroup);
1503                            pGroup.addToMemberList(obj);
1504                        }
1505                        else {
1506                            obj = getGroup(gid, name, pGroup);
1507                        }
1508                    }
1509                    finally {
1510                        try {
1511                            H5.H5Gclose(gid);
1512                        }
1513                        catch (Exception ex) {
1514                            log.debug("get(): {} H5Gclose(gid {}) failure: ", path, gid, ex);
1515                        }
1516                    }
1517                }
1518                else if (objType == HDF5Constants.H5O_TYPE_NAMED_DATATYPE) {
1519                    obj = new H5Datatype(this, name, pPath);
1520                }
1521            }
1522            try {
1523                H5.H5Oclose(objid);
1524            }
1525            catch (Exception ex) {
1526                log.debug("get(): H5Oclose(objid {}) failure: ", objid, ex);
1527                ex.printStackTrace();
1528            }
1529        }
1530        catch (Exception ex) {
1531            log.debug("get(): Exception finding obj {}", path, ex);
1532            obj = null;
1533        }
1534        finally {
1535            if ((fid_before_open <= 0) && (obj == null)) {
1536                // close the fid that is not attached to any object
1537                try {
1538                    H5.H5Fclose(fid);
1539                }
1540                catch (Exception ex) {
1541                    log.debug("get(): {} H5Fclose(fid {}) failure: ", path, fid, ex);
1542                }
1543                fid = fid_before_open;
1544            }
1545        }
1546
1547        return obj;
1548    }
1549
1550    /**
1551     * Creates a named datatype in a file.
1552     *
1553     * The following code creates a named datatype in a file.
1554     *
1555     * <pre>
1556     * H5File file = (H5File) h5file.createInstance(&quot;test_hdf5.h5&quot;, FileFormat.WRITE);
1557     * Datatype dtype = file.createDatatype(
1558     *                             Datatype.CLASS_INTEGER,
1559     *                             4,
1560     *                             Datatype.NATIVE,
1561     *                             Datatype.NATIVE,
1562     *                             basetype);
1563     * H5Datatype h5dtype = file.createNamedDatatype(
1564     *                             dtype,
1565     *                             null,
1566     *                             &quot;Native Integer&quot;);
1567     * </pre>
1568     *
1569     * @param tnative
1570     *            native datatype previously created
1571     * @param name
1572     *            name of the datatype to create, e.g. "Native Integer".
1573     * @return The new datatype if successful; otherwise returns null.
1574     * @throws Exception
1575     *             The exceptions thrown vary depending on the implementing class.
1576     */
1577    @Override
1578    public Datatype createNamedDatatype(Datatype tnative, String name) throws Exception
1579    {
1580        log.trace("createNamedDatatype(): start: name={}", name);
1581
1582        H5Datatype dtype = null;
1583
1584        if (name != null) {
1585            long tid = -1;
1586            log.trace("createNamedDatatype(): name={}", name);
1587            try {
1588                tnative.setFullname(name, null);
1589            }
1590            catch (Exception ex) {
1591                log.debug("createNamedDatatype():setName(): {} failure: {}", name, ex.getMessage());
1592            }
1593            try {
1594                if ((tid = tnative.createNative()) < 0) {
1595                    log.debug("createNamedDatatype(): createNative() failure");
1596                    throw new Exception("createNative() failed");
1597                }
1598                log.trace("createNamedDatatype(): createNative gets id={}", tid);
1599
1600                H5.H5Tcommit(fid, name, tid, HDF5Constants.H5P_DEFAULT, HDF5Constants.H5P_DEFAULT,
1601                             HDF5Constants.H5P_DEFAULT);
1602
1603                int nativeClass = H5.H5Tget_class(tid);
1604                if (nativeClass == HDF5Constants.H5T_REFERENCE)
1605                    dtype = new H5ReferenceType(this, name, null);
1606                else
1607                    dtype = new H5Datatype(this, name, null);
1608            }
1609            finally {
1610                H5.H5Tclose(tid);
1611            }
1612        }
1613        else {
1614            dtype = (H5Datatype)tnative;
1615        }
1616
1617        return dtype;
1618    }
1619
1620    /***************************************************************************
1621     * Methods related to Datatypes and HObjects in HDF5 Files. Strictly speaking, these methods aren't
1622     *related to H5File and the actions could be carried out through the H5Group, H5Datatype and H5*DS
1623     *classes. But, in some cases they allow a null input and expect the generated object to be of HDF5 type.
1624     *So, we put them in the H5File class so that we create the proper type of HObject... H5Group for example.
1625     *
1626     * Here again, if there could be Implementation Class methods we'd use those. But, since we can't override
1627     *class methods (they can only be shadowed in Java), these are instance methods.
1628     *
1629     **************************************************************************/
1630
1631    /*
1632     * (non-Javadoc)
1633     *
1634     * @see hdf.object.FileFormat#createDatatype(int, int, int, int)
1635     */
1636    @Override
1637    public Datatype createDatatype(int tclass, int tsize, int torder, int tsign) throws Exception
1638    {
1639        return new H5Datatype(tclass, tsize, torder, tsign);
1640    }
1641
1642    /*
1643     * (non-Javadoc)
1644     *
1645     * @see hdf.object.FileFormat#createDatatype(int, int, int, int, Datatype)
1646     */
1647    @Override
1648    public Datatype createDatatype(int tclass, int tsize, int torder, int tsign, Datatype tbase)
1649        throws Exception
1650    {
1651        return new H5Datatype(tclass, tsize, torder, tsign, tbase);
1652    }
1653
1654    /*
1655     * (non-Javadoc)
1656     *
1657     * @see hdf.object.FileFormat#createScalarDS(java.lang.String, hdf.object.Group, hdf.object.Datatype,
1658     * long[], long[], long[], int, java.lang.Object)
1659     */
1660    @Override
1661    public Dataset createScalarDS(String name, Group pgroup, Datatype type, long[] dims, long[] maxdims,
1662                                  long[] chunks, int gzip, Object fillValue, Object data) throws Exception
1663    {
1664        log.trace("createScalarDS(): name={}", name);
1665        // create new dataset at the root group by default
1666        if (pgroup == null)
1667            pgroup = (Group)get("/");
1668
1669        return H5ScalarDS.create(name, pgroup, type, dims, maxdims, chunks, gzip, fillValue, data);
1670    }
1671
1672    /*
1673     * (non-Javadoc)
1674     *
1675     * @see hdf.object.FileFormat#createCompoundDS(java.lang.String, hdf.object.Group, long[], long[], long[],
1676     * int, java.lang.String[], hdf.object.Datatype[], int[], java.lang.Object)
1677     */
1678    @Override
1679    public Dataset createCompoundDS(String name, Group pgroup, long[] dims, long[] maxdims, long[] chunks,
1680                                    int gzip, String[] memberNames, Datatype[] memberDatatypes,
1681                                    int[] memberSizes, Object data) throws Exception
1682    {
1683        log.trace("createCompoundDS(): start: name={}", name);
1684        int nMembers        = memberNames.length;
1685        int memberRanks[]   = new int[nMembers];
1686        long memberDims[][] = new long[nMembers][1];
1687        Dataset ds          = null;
1688
1689        for (int i = 0; i < nMembers; i++) {
1690            memberRanks[i] = 1;
1691            if (memberSizes == null)
1692                memberDims[i][0] = 1;
1693            else
1694                memberDims[i][0] = memberSizes[i];
1695        }
1696
1697        // create new dataset at the root group by default
1698        if (pgroup == null)
1699            pgroup = (Group)get("/");
1700        ds = H5CompoundDS.create(name, pgroup, dims, maxdims, chunks, gzip, memberNames, memberDatatypes,
1701                                 memberRanks, memberDims, data);
1702
1703        return ds;
1704    }
1705
1706    /*
1707     * (non-Javadoc)
1708     *
1709     * @see hdf.object.FileFormat#createImage(java.lang.String, hdf.object.Group, hdf.object.Datatype,
1710     * long[], long[], long[], int, int, int, java.lang.Object)
1711     */
1712    @Override
1713    public Dataset createImage(String name, Group pgroup, Datatype type, long[] dims, long[] maxdims,
1714                               long[] chunks, int gzip, int ncomp, int interlace, Object data)
1715        throws Exception
1716    {
1717        log.trace("createImage(): start: name={}", name);
1718        // create at the root group by default
1719        if (pgroup == null)
1720            pgroup = (Group)get("/");
1721
1722        H5ScalarDS dataset =
1723            (H5ScalarDS)H5ScalarDS.create(name, pgroup, type, dims, maxdims, chunks, gzip, data);
1724
1725        try {
1726            H5File.createImageAttributes(dataset, interlace);
1727            dataset.setIsImage(true);
1728        }
1729        catch (Exception ex) {
1730            log.debug("createImage(): {} createImageAttributtes failure: ", name, ex);
1731        }
1732
1733        return dataset;
1734    }
1735
1736    /***
1737     * Creates a new group with specified name in existing group.
1738     *
1739     * @see hdf.object.FileFormat#createGroup(java.lang.String, hdf.object.Group)
1740     */
1741    @Override
1742    public Group createGroup(String name, Group pgroup) throws Exception
1743    {
1744        return this.createGroup(name, pgroup, HDF5Constants.H5P_DEFAULT);
1745    }
1746
1747    /***
1748     * Creates a new group with specified name in existing group and with the group creation properties list,
1749     * gplist.
1750     *
1751     * @see hdf.object.h5.H5Group#create(java.lang.String, hdf.object.Group, long...)
1752     *
1753     */
1754    @Override
1755    public Group createGroup(String name, Group pgroup, long... gplist) throws Exception
1756    {
1757        // create new group at the root
1758        if (pgroup == null)
1759            pgroup = (Group)this.get("/");
1760
1761        return H5Group.create(name, pgroup, gplist);
1762    }
1763
1764    /***
1765     * Creates the group creation property list identifier, gcpl. This identifier is used when creating
1766     * Groups.
1767     *
1768     * @see hdf.object.FileFormat#createGcpl(int, int, int)
1769     *
1770     */
1771    @Override
1772    public long createGcpl(int creationorder, int maxcompact, int mindense) throws Exception
1773    {
1774        long gcpl = -1;
1775        try {
1776            gcpl = H5.H5Pcreate(HDF5Constants.H5P_GROUP_CREATE);
1777            if (gcpl >= 0) {
1778                // Set link creation order.
1779                if (creationorder == Group.CRT_ORDER_TRACKED) {
1780                    log.trace("createGcpl(): creation order ORDER_TRACKED");
1781                    H5.H5Pset_link_creation_order(gcpl, HDF5Constants.H5P_CRT_ORDER_TRACKED);
1782                }
1783                else if (creationorder == Group.CRT_ORDER_INDEXED) {
1784                    log.trace("createGcpl(): creation order ORDER_INDEXED");
1785                    H5.H5Pset_link_creation_order(gcpl, HDF5Constants.H5P_CRT_ORDER_TRACKED +
1786                                                            HDF5Constants.H5P_CRT_ORDER_INDEXED);
1787                }
1788                // Set link storage.
1789                H5.H5Pset_link_phase_change(gcpl, maxcompact, mindense);
1790            }
1791        }
1792        catch (Exception ex) {
1793            log.debug("createGcpl(): failure: ", ex);
1794            ex.printStackTrace();
1795        }
1796
1797        return gcpl;
1798    }
1799
1800    /*
1801     * (non-Javadoc)
1802     *
1803     * @see hdf.object.FileFormat#createLink(hdf.object.Group, java.lang.String, hdf.object.HObject)
1804     */
1805    @Override
1806    public HObject createLink(Group parentGroup, String name, Object currentObj) throws Exception
1807    {
1808        if (currentObj instanceof HObject)
1809            return this.createLink(parentGroup, name, (HObject)currentObj, Group.LINK_TYPE_HARD);
1810        else if (currentObj instanceof String)
1811            return this.createLink(parentGroup, name, (String)currentObj, Group.LINK_TYPE_HARD);
1812
1813        return null;
1814    }
1815
1816    /**
1817     * Creates a link to an object in the open file.
1818     *
1819     * If parentGroup is null, the new link is created in the root group.
1820     *
1821     * @param parentGroup
1822     *            The group where the link is created.
1823     * @param name
1824     *            The name of the link.
1825     * @param currentObj
1826     *            The existing object the new link will reference.
1827     * @param lType
1828     *            The type of link to be created. It can be a hard link, a soft link or an external link.
1829     *
1830     * @return The object pointed to by the new link if successful; otherwise returns null.
1831     *
1832     * @throws Exception
1833     *             The exceptions thrown vary depending on the implementing class.
1834     */
1835    @Override
1836    public HObject createLink(Group parentGroup, String name, HObject currentObj, int lType) throws Exception
1837    {
1838        log.trace("createLink(): start: name={}", name);
1839        HObject obj              = null;
1840        int type                 = 0;
1841        String current_full_name = null;
1842        String new_full_name     = null;
1843        String parent_path       = null;
1844
1845        if (currentObj == null) {
1846            log.debug("createLink(): Link target is null");
1847            throw new HDF5Exception("The object pointed to by the link cannot be null.");
1848        }
1849        if ((parentGroup == null) || parentGroup.isRoot())
1850            parent_path = HObject.SEPARATOR;
1851        else
1852            parent_path =
1853                parentGroup.getPath() + HObject.SEPARATOR + parentGroup.getName() + HObject.SEPARATOR;
1854
1855        new_full_name = parent_path + name;
1856
1857        if (lType == Group.LINK_TYPE_HARD) {
1858            type = HDF5Constants.H5L_TYPE_HARD;
1859            log.trace("createLink(): type H5L_TYPE_HARD");
1860        }
1861        else if (lType == Group.LINK_TYPE_SOFT) {
1862            type = HDF5Constants.H5L_TYPE_SOFT;
1863            log.trace("createLink(): type H5L_TYPE_SOFT");
1864        }
1865        else if (lType == Group.LINK_TYPE_EXTERNAL) {
1866            type = HDF5Constants.H5L_TYPE_EXTERNAL;
1867            log.trace("createLink(): type H5L_TYPE_EXTERNAL");
1868        }
1869
1870        if (H5.H5Lexists(fid, new_full_name, HDF5Constants.H5P_DEFAULT)) {
1871            H5.H5Ldelete(fid, new_full_name, HDF5Constants.H5P_DEFAULT);
1872        }
1873
1874        if (type == HDF5Constants.H5L_TYPE_HARD) {
1875            if ((currentObj instanceof Group) && ((Group)currentObj).isRoot()) {
1876                log.debug("createLink(): cannot create link to root group");
1877                throw new HDF5Exception("Cannot make a link to the root group.");
1878            }
1879            current_full_name = currentObj.getPath() + HObject.SEPARATOR + currentObj.getName();
1880
1881            H5.H5Lcreate_hard(fid, current_full_name, fid, new_full_name, HDF5Constants.H5P_DEFAULT,
1882                              HDF5Constants.H5P_DEFAULT);
1883        }
1884
1885        else if (type == HDF5Constants.H5L_TYPE_SOFT) {
1886            log.trace("createLink(): H5Lcreate_soft: {} in {} as {}", currentObj.getFullName(), fid,
1887                      new_full_name);
1888            H5.H5Lcreate_soft(currentObj.getFullName(), fid, new_full_name, HDF5Constants.H5P_DEFAULT,
1889                              HDF5Constants.H5P_DEFAULT);
1890        }
1891
1892        else if (type == HDF5Constants.H5L_TYPE_EXTERNAL) {
1893            log.trace("createLink(): H5Lcreate_external: File={} {} in {} as {}", currentObj.getFile(),
1894                      currentObj.getFullName(), fid, new_full_name);
1895            H5.H5Lcreate_external(currentObj.getFile(), currentObj.getFullName(), fid, new_full_name,
1896                                  HDF5Constants.H5P_DEFAULT, HDF5Constants.H5P_DEFAULT);
1897        }
1898
1899        if (currentObj instanceof Group) {
1900            log.trace("createLink(): Link target is type H5Group");
1901            obj = new H5Group(this, name, parent_path, parentGroup);
1902        }
1903        else if (currentObj instanceof H5ReferenceType) {
1904            log.trace("createLink(): Link target is type H5Datatype");
1905            obj = new H5ReferenceType(this, name, parent_path);
1906        }
1907        else if (currentObj instanceof H5Datatype) {
1908            log.trace("createLink(): Link target is type H5Datatype");
1909            obj = new H5Datatype(this, name, parent_path);
1910        }
1911        else if (currentObj instanceof H5CompoundDS) {
1912            log.trace("createLink(): Link target is type H5CompoundDS");
1913            obj = new H5CompoundDS(this, name, parent_path);
1914        }
1915        else if (currentObj instanceof H5ScalarDS) {
1916            log.trace("createLink(): Link target is type H5ScalarDS");
1917            obj = new H5ScalarDS(this, name, parent_path);
1918        }
1919        else
1920            log.trace("createLink(): Link target is type unknown");
1921
1922        return obj;
1923    }
1924
1925    /**
1926     * Creates a soft or external link to object in a file that does not exist at the time the link is
1927     * created.
1928     *
1929     * @param parentGroup
1930     *            The group where the link is created.
1931     * @param name
1932     *            The name of the link.
1933     * @param currentObj
1934     *            The name of the object the new link will reference. The object doesn't have to exist.
1935     * @param lType
1936     *            The type of link to be created.
1937     *
1938     * @return The H5Link object pointed to by the new link if successful; otherwise returns null.
1939     *
1940     * @throws Exception
1941     *             The exceptions thrown vary depending on the implementing class.
1942     */
1943    @Override
1944    public HObject createLink(Group parentGroup, String name, String currentObj, int lType) throws Exception
1945    {
1946        log.trace("createLink(): start: name={}", name);
1947        HObject obj          = null;
1948        int type             = 0;
1949        String new_full_name = null;
1950        String parent_path   = null;
1951
1952        if (currentObj == null) {
1953            log.debug("createLink(): Link target is null");
1954            throw new HDF5Exception("The object pointed to by the link cannot be null.");
1955        }
1956        if ((parentGroup == null) || parentGroup.isRoot())
1957            parent_path = HObject.SEPARATOR;
1958        else
1959            parent_path =
1960                parentGroup.getPath() + HObject.SEPARATOR + parentGroup.getName() + HObject.SEPARATOR;
1961
1962        new_full_name = parent_path + name;
1963
1964        if (lType == Group.LINK_TYPE_HARD) {
1965            type = HDF5Constants.H5L_TYPE_HARD;
1966            log.trace("createLink(): type H5L_TYPE_HARD");
1967        }
1968        else if (lType == Group.LINK_TYPE_SOFT) {
1969            type = HDF5Constants.H5L_TYPE_SOFT;
1970            log.trace("createLink(): type H5L_TYPE_SOFT");
1971        }
1972        else if (lType == Group.LINK_TYPE_EXTERNAL) {
1973            type = HDF5Constants.H5L_TYPE_EXTERNAL;
1974            log.trace("createLink(): type H5L_TYPE_EXTERNAL");
1975        }
1976
1977        if (H5.H5Lexists(fid, new_full_name, HDF5Constants.H5P_DEFAULT)) {
1978            H5.H5Ldelete(fid, new_full_name, HDF5Constants.H5P_DEFAULT);
1979        }
1980
1981        if (type == HDF5Constants.H5L_TYPE_SOFT) {
1982            H5.H5Lcreate_soft(currentObj, fid, new_full_name, HDF5Constants.H5P_DEFAULT,
1983                              HDF5Constants.H5P_DEFAULT);
1984        }
1985
1986        else if (type == HDF5Constants.H5L_TYPE_EXTERNAL) {
1987            String fileName   = null;
1988            String objectName = null;
1989
1990            // separate the object name and the file name
1991            fileName   = currentObj.substring(0, currentObj.lastIndexOf(FileFormat.FILE_OBJ_SEP));
1992            objectName = currentObj.substring(currentObj.indexOf(FileFormat.FILE_OBJ_SEP));
1993            objectName = objectName.substring(3);
1994
1995            H5.H5Lcreate_external(fileName, objectName, fid, new_full_name, HDF5Constants.H5P_DEFAULT,
1996                                  HDF5Constants.H5P_DEFAULT);
1997        }
1998
1999        if (name.startsWith(HObject.SEPARATOR)) {
2000            name = name.substring(1);
2001        }
2002        obj = new H5Link(this, name, parent_path);
2003
2004        return obj;
2005    }
2006
2007    /**
2008     * reload the sub-tree structure from file.
2009     *
2010     * reloadTree(Group g) is useful when the structure of the group in file is changed while the group
2011     * structure in memory is not changed.
2012     *
2013     * @param g
2014     *            the group where the structure is to be reloaded in memory
2015     */
2016    public void reloadTree(Group g)
2017    {
2018        if (fid < 0 || rootObject == null || g == null) {
2019            log.debug("reloadTree(): Invalid fid or null object");
2020            return;
2021        }
2022
2023        depth_first(g, Integer.MIN_VALUE);
2024    }
2025
2026    /*
2027     * (non-Javadoc) NOTE: Object references are copied but not updated by this method.
2028     *
2029     * @see hdf.object.FileFormat#copy(hdf.object.HObject, hdf.object.Group, java.lang.String)
2030     */
2031    @Override
2032    public HObject copy(HObject srcObj, Group dstGroup, String dstName) throws Exception
2033    {
2034        log.trace("copy(): start: srcObj={} dstGroup={} dstName={}", srcObj, dstGroup, dstName);
2035        if ((srcObj == null) || (dstGroup == null)) {
2036            log.debug("copy(): srcObj or dstGroup is null");
2037            return null;
2038        }
2039
2040        if (dstName == null)
2041            dstName = srcObj.getName();
2042
2043        List<HObject> members = dstGroup.getMemberList();
2044        int n                 = members.size();
2045        for (int i = 0; i < n; i++) {
2046            HObject obj = members.get(i);
2047            String name = obj.getName();
2048            while (name.equals(dstName))
2049                dstName += "~copy";
2050        }
2051
2052        HObject newObj = null;
2053        if (srcObj instanceof Dataset) {
2054            log.trace("copy(): srcObj instanceof Dataset");
2055            newObj = copyDataset((Dataset)srcObj, (H5Group)dstGroup, dstName);
2056        }
2057        else if (srcObj instanceof H5Group) {
2058            log.trace("copy(): srcObj instanceof H5Group");
2059            newObj = copyGroup((H5Group)srcObj, (H5Group)dstGroup, dstName);
2060        }
2061        else if (srcObj instanceof H5Datatype) {
2062            log.trace("copy(): srcObj instanceof H5Datatype");
2063            newObj = copyDatatype((H5Datatype)srcObj, (H5Group)dstGroup, dstName);
2064        }
2065
2066        return newObj;
2067    }
2068
2069    /*
2070     * (non-Javadoc)
2071     *
2072     * @see hdf.object.FileFormat#delete(hdf.object.HObject)
2073     */
2074    @Override
2075    public void delete(HObject obj)throws Exception
2076    {
2077        if ((obj == null) || (fid < 0)) {
2078            log.debug("delete(): Invalid FID or object is null");
2079            return;
2080        }
2081
2082        String name = obj.getPath() + obj.getName();
2083
2084        H5.H5Ldelete(fid, name, HDF5Constants.H5P_DEFAULT);
2085    }
2086
2087    /*
2088     * (non-Javadoc)
2089     *
2090     * @see hdf.object.FileFormat#writeAttribute(hdf.object.HObject, hdf.object.Attribute, boolean)
2091     */
2092    @Override
2093    public void writeAttribute(HObject obj, Attribute attr, boolean attrExisted) throws HDF5Exception
2094    {
2095        String obj_name = obj.getFullName();
2096        String name     = attr.getAttributeName();
2097        long tid        = -1;
2098        long sid        = -1;
2099        long aid        = -1;
2100        log.trace("writeAttribute(): name is {}", name);
2101
2102        long objID = obj.open();
2103        if (objID < 0) {
2104            log.debug("writeAttribute(): Invalid Object ID");
2105            return;
2106        }
2107
2108        if ((tid = attr.getAttributeDatatype().createNative()) >= 0) {
2109            log.trace("writeAttribute(): tid {} from toNative :{}", tid,
2110                      attr.getAttributeDatatype().getDescription());
2111            try {
2112                if (attr.isAttributeNULL())
2113                    sid = H5.H5Screate(HDF5Constants.H5S_NULL);
2114                else if (attr.isAttributeScalar())
2115                    sid = H5.H5Screate(HDF5Constants.H5S_SCALAR);
2116                else
2117                    sid = H5.H5Screate_simple(attr.getAttributeRank(), attr.getAttributeDims(), null);
2118
2119                if (attrExisted)
2120                    aid = H5.H5Aopen_by_name(objID, obj_name, name, HDF5Constants.H5P_DEFAULT,
2121                                             HDF5Constants.H5P_DEFAULT);
2122                else
2123                    aid = H5.H5Acreate(objID, name, tid, sid, HDF5Constants.H5P_DEFAULT,
2124                                       HDF5Constants.H5P_DEFAULT);
2125                log.trace("writeAttribute(): aid {} opened/created", aid);
2126
2127                if (!attr.isAttributeNULL()) {
2128                    // update value of the attribute
2129                    Object attrValue;
2130                    try {
2131                        attrValue = attr.getAttributeData();
2132                    }
2133                    catch (Exception ex) {
2134                        attrValue = null;
2135                        log.trace("writeAttribute(): getAttributeData() failure:", ex);
2136                    }
2137
2138                    // log.trace("writeAttribute(): attrValue={}", attrValue);
2139                    if (attrValue != null) {
2140                        try {
2141                            ((H5Attribute)attr).AttributeCommonIO(aid, H5File.IO_TYPE.WRITE, attrValue);
2142                        }
2143                        catch (Exception ex) {
2144                            log.debug("writeAttribute(): failed to write attribute: ", ex);
2145                        }
2146                    } // (attrValue != null)
2147                }
2148            }
2149            finally {
2150                try {
2151                    H5.H5Tclose(tid);
2152                }
2153                catch (Exception ex) {
2154                    log.debug("writeAttribute(): H5Tclose(tid {}) failure: ", tid, ex);
2155                }
2156                try {
2157                    H5.H5Sclose(sid);
2158                }
2159                catch (Exception ex) {
2160                    log.debug("writeAttribute(): H5Sclose(sid {}) failure: ", sid, ex);
2161                }
2162                try {
2163                    H5.H5Aclose(aid);
2164                }
2165                catch (Exception ex) {
2166                    log.debug("writeAttribute(): H5Aclose(aid {}) failure: ", aid, ex);
2167                }
2168            }
2169        }
2170        else {
2171            log.debug("writeAttribute(): toNative failure");
2172        }
2173
2174        obj.close(objID);
2175    }
2176
2177    /***************************************************************************
2178     * Implementations for methods specific to H5File
2179     **************************************************************************/
2180
2181    /**
2182     * Opens a file with specific file access property list.
2183     *
2184     * This function does the same as "long open()" except the you can also pass an HDF5 file access property
2185     * to file open. For example,
2186     *
2187     * <pre>
2188     * // All open objects remaining in the file are closed then file is closed
2189     * long plist = H5.H5Pcreate(HDF5Constants.H5P_FILE_ACCESS);
2190     * H5.H5Pset_fclose_degree(plist, HDF5Constants.H5F_CLOSE_STRONG);
2191     * long fid = open(plist);
2192     * </pre>
2193     *
2194     * @param plist
2195     *            a file access property list identifier.
2196     *
2197     * @return the file identifier if successful; otherwise returns negative value.
2198     *
2199     * @throws Exception
2200     *            If there is a failure.
2201     */
2202    public long open(long plist) throws Exception { return open(true, plist); }
2203
2204    /***************************************************************************
2205     * Private methods.
2206     **************************************************************************/
2207
2208    /**
2209     * Opens access to this file.
2210     *
2211     * @param loadFullHierarchy
2212     *            if true, load the full hierarchy into memory; otherwise just opens the file identifier.
2213     *
2214     * @return the file identifier if successful; otherwise returns negative value.
2215     *
2216     * @throws Exception
2217     *            If there is a failure.
2218     */
2219    private long open(boolean loadFullHierarchy) throws Exception
2220    {
2221        long the_fid = -1;
2222
2223        long plist = HDF5Constants.H5P_DEFAULT;
2224
2225        // BUG: HDF5Constants.H5F_CLOSE_STRONG does not flush cache
2226        /**
2227         * try { //All open objects remaining in the file are closed // then file is closed plist =
2228         * H5.H5Pcreate (HDF5Constants.H5P_FILE_ACCESS); H5.H5Pset_fclose_degree ( plist,
2229         * HDF5Constants.H5F_CLOSE_STRONG); } catch (Exception ex) {} the_fid = open(loadFullHierarchy,
2230         * plist); try { H5.H5Pclose(plist); } catch (Exception ex) {}
2231         */
2232
2233        log.trace("open(): loadFull={}", loadFullHierarchy);
2234        the_fid = open(loadFullHierarchy, plist);
2235
2236        return the_fid;
2237    }
2238
2239    /**
2240     * Opens access to this file.
2241     *
2242     * @param loadFullHierarchy
2243     *            if true, load the full hierarchy into memory; otherwise just opens the file identifier.
2244     *
2245     * @return the file identifier if successful; otherwise returns negative value.
2246     *
2247     * @throws Exception
2248     *            If there is a failure.
2249     */
2250    private long open(boolean loadFullHierarchy, long plist) throws Exception
2251    {
2252        log.trace("open(loadFullHierarchy = {}, plist = {}): start", loadFullHierarchy, plist);
2253        if (fid > 0) {
2254            log.trace("open(): FID already opened");
2255            return fid; // file is opened already
2256        }
2257
2258        // The cwd may be changed at Dataset.read() by System.setProperty("user.dir", newdir)
2259        // to make it work for external datasets. We need to set it back
2260        // before the file is closed/opened.
2261        String rootPath = System.getProperty("hdfview.workdir");
2262        if (rootPath == null) {
2263            rootPath = System.getProperty("user.dir");
2264        }
2265        System.setProperty("user.dir", rootPath);
2266
2267        log.trace("open(): flag={}", flag);
2268        // check for valid file access permission
2269        if (flag < 0) {
2270            log.debug("open(): Invalid access identifier -- " + flag);
2271            throw new HDF5Exception("Invalid access identifer -- " + flag);
2272        }
2273        else if (HDF5Constants.H5F_ACC_CREAT == flag) {
2274            // create a new file
2275            log.trace("open(): create file");
2276            fid = H5.H5Fcreate(fullFileName, HDF5Constants.H5F_ACC_TRUNC, HDF5Constants.H5P_DEFAULT,
2277                               HDF5Constants.H5P_DEFAULT);
2278            H5.H5Fflush(fid, HDF5Constants.H5F_SCOPE_LOCAL);
2279            H5.H5Fclose(fid);
2280            flag = HDF5Constants.H5F_ACC_RDWR;
2281        }
2282        else if (!exists()) {
2283            log.debug("open(): File {} does not exist", fullFileName);
2284            throw new HDF5Exception("File does not exist -- " + fullFileName);
2285        }
2286        else if (((flag == HDF5Constants.H5F_ACC_RDWR) || (flag == HDF5Constants.H5F_ACC_CREAT)) &&
2287                 !canWrite()) {
2288            log.debug("open(): Cannot write file {}", fullFileName);
2289            throw new HDF5Exception("Cannot write file, try opening as read-only -- " + fullFileName);
2290        }
2291        else if ((flag == HDF5Constants.H5F_ACC_RDONLY) && !canRead()) {
2292            log.debug("open(): Cannot read file {}", fullFileName);
2293            throw new HDF5Exception("Cannot read file -- " + fullFileName);
2294        }
2295
2296        try {
2297            fid = H5.H5Fopen(fullFileName, flag, plist);
2298        }
2299        catch (Exception ex) {
2300            try {
2301                log.debug("open(): open failed, attempting to open file read-only", ex);
2302                fid = H5.H5Fopen(fullFileName, HDF5Constants.H5F_ACC_RDONLY, HDF5Constants.H5P_DEFAULT);
2303                isReadOnly = true;
2304            }
2305            catch (Exception ex2) {
2306                // Attempt to open the file as a split file or family file
2307                try {
2308                    File tmpf      = new File(fullFileName);
2309                    String tmpname = tmpf.getName();
2310                    int idx        = tmpname.lastIndexOf('.');
2311
2312                    if (tmpname.contains("-m")) {
2313                        log.debug("open(): open read-only failed, attempting to open split file");
2314
2315                        while (idx > 0) {
2316                            char c = tmpname.charAt(idx - 1);
2317                            if (c != '-')
2318                                idx--;
2319                            else
2320                                break;
2321                        }
2322
2323                        if (idx > 0) {
2324                            tmpname = tmpname.substring(0, idx - 1);
2325                            log.trace("open(): attempting to open split file with name {}", tmpname);
2326                            long pid = H5.H5Pcreate(HDF5Constants.H5P_FILE_ACCESS);
2327                            H5.H5Pset_fapl_split(pid, "-m.h5", HDF5Constants.H5P_DEFAULT, "-r.h5",
2328                                                 HDF5Constants.H5P_DEFAULT);
2329                            fid = H5.H5Fopen(tmpf.getParent() + File.separator + tmpname, flag, pid);
2330                            H5.H5Pclose(pid);
2331                        }
2332                    }
2333                    else {
2334                        log.debug("open(): open read-only failed, checking for file family");
2335                        // try to see if it is a file family, always open a family file
2336                        // from the first one since other files will not be recognized
2337                        // as an HDF5 file
2338                        int cnt = idx;
2339                        while (idx > 0) {
2340                            char c = tmpname.charAt(idx - 1);
2341                            if (Character.isDigit(c))
2342                                idx--;
2343                            else
2344                                break;
2345                        }
2346
2347                        if (idx > 0) {
2348                            cnt -= idx;
2349                            tmpname = tmpname.substring(0, idx) + "%0" + cnt + "d" +
2350                                      tmpname.substring(tmpname.lastIndexOf('.'));
2351                            log.trace("open(): attempting to open file family with name {}", tmpname);
2352                            long pid = H5.H5Pcreate(HDF5Constants.H5P_FILE_ACCESS);
2353                            H5.H5Pset_fapl_family(pid, 0, HDF5Constants.H5P_DEFAULT);
2354                            fid = H5.H5Fopen(tmpf.getParent() + File.separator + tmpname, flag, pid);
2355                            H5.H5Pclose(pid);
2356                        }
2357                    }
2358                }
2359                catch (Exception ex3) {
2360                    log.debug("open(): open failed: ", ex3);
2361                }
2362            }
2363        }
2364
2365        initLibBounds();
2366
2367        if ((fid >= 0) && loadFullHierarchy) {
2368            long n = H5.H5Fget_obj_count(fid, HDF5Constants.H5F_OBJ_ALL);
2369            log.trace("open(): open objects={}", n);
2370            // load the hierarchy of the file
2371            loadIntoMemory();
2372        }
2373
2374        log.trace("open(loadFullHierarchy = {}, plist = {}): finish", loadFullHierarchy, plist);
2375        return fid;
2376    }
2377
2378    /**
2379     * Loads the file structure into memory.
2380     */
2381    private void loadIntoMemory()
2382    {
2383        if (fid < 0) {
2384            log.debug("loadIntoMemory(): Invalid FID");
2385            return;
2386        }
2387
2388        /*
2389         * TODO: Root group's name should be changed to 'this.getName()' and all
2390         * previous accesses of this field should now use getPath() instead of getName()
2391         * to get the root group. The root group actually does have a path of "/". The
2392         * depth_first method will have to be changed to setup other object paths
2393         * appropriately, as it currently assumes the root path to be null.
2394         */
2395        rootObject = new H5Group(this, "/", null, null);
2396        log.trace("loadIntoMemory(): depth_first on root");
2397        depth_first(rootObject, 0);
2398    }
2399
2400    /**
2401     * Retrieves the file structure by depth-first order, recursively. The current implementation retrieves
2402     * groups and datasets only. It does not include named datatypes and soft links.
2403     *
2404     * It also detects and stops loops. A loop is detected if there exists an object with the same object ID
2405     * by tracing a path back up to the root.
2406     *
2407     * @param parentObject
2408     *            the parent object.
2409     */
2410    @SuppressWarnings("deprecation")
2411    private int depth_first(HObject parentObject, int nTotal)
2412    {
2413        log.trace("depth_first({}): start", parentObject);
2414
2415        int nelems;
2416        String fullPath = null;
2417        String ppath    = null;
2418        long gid        = -1;
2419
2420        H5Group pgroup = (H5Group)parentObject;
2421        ppath          = pgroup.getPath();
2422
2423        if (ppath == null)
2424            fullPath = HObject.SEPARATOR;
2425        else
2426            fullPath = ppath + pgroup.getName() + HObject.SEPARATOR;
2427
2428        nelems = 0;
2429        try {
2430            gid             = pgroup.open();
2431            H5G_info_t info = H5.H5Gget_info(gid);
2432            nelems          = (int)info.nlinks;
2433        }
2434        catch (HDF5Exception ex) {
2435            nelems = -1;
2436            log.debug("depth_first({}): H5Gget_info(gid {}) failure: ", parentObject, gid, ex);
2437        }
2438
2439        if (nelems <= 0) {
2440            pgroup.close(gid);
2441            log.debug("depth_first({}): nelems <= 0", parentObject);
2442            return nTotal;
2443        }
2444
2445        // since each call of H5.H5Gget_objname_by_idx() takes about one second.
2446        // 1,000,000 calls take 12 days. Instead of calling it in a loop,
2447        // we use only one call to get all the information, which takes about
2448        // two seconds
2449        int[] objTypes                              = new int[nelems];
2450        long[] fNos                                 = new long[nelems];
2451        hdf.hdf5lib.structs.H5O_token_t[] objTokens = new hdf.hdf5lib.structs.H5O_token_t[nelems];
2452        String[] objNames                           = new String[nelems];
2453
2454        try {
2455            H5.H5Gget_obj_info_full(fid, fullPath, objNames, objTypes, null, fNos, objTokens, indexType,
2456                                    indexOrder);
2457        }
2458        catch (HDF5Exception ex) {
2459            log.debug("depth_first({}): failure: ", parentObject, ex);
2460            ex.printStackTrace();
2461            return nTotal;
2462        }
2463
2464        int nStart = getStartMembers();
2465        int nMax   = getMaxMembers();
2466
2467        String obj_name;
2468        int obj_type;
2469
2470        // Iterate through the file to see members of the group
2471        for (int i = 0; i < nelems; i++) {
2472            obj_name = objNames[i];
2473            obj_type = objTypes[i];
2474            log.trace("depth_first({}): obj_name={}, obj_type={}", parentObject, obj_name, obj_type);
2475            log.trace("depth_first({}): objTokens[{}]={}", parentObject, i, objTokens[i].data);
2476            long[] objtok = HDFNativeData.byteToLong(objTokens[i].data);
2477            log.trace("depth_first({}): objtok[0]={}, objtok[1]={}, fNos[{}]={}", parentObject, objtok[0],
2478                      objtok[1], i, fNos[i]);
2479
2480            if (obj_name == null) {
2481                log.trace("depth_first({}): continue after null obj_name", parentObject);
2482                continue;
2483            }
2484
2485            nTotal++;
2486
2487            if (nMax > 0) {
2488                if ((nTotal - nStart) >= nMax)
2489                    break; // loaded enough objects
2490            }
2491
2492            boolean skipLoad = false;
2493            if ((nTotal > 0) && (nTotal < nStart))
2494                skipLoad = true;
2495
2496            // create a new objects
2497            long[] oid = null;
2498            if (obj_type == HDF5Constants.H5O_TYPE_GROUP) {
2499                H5Group g = new H5Group(this, obj_name, fullPath, pgroup);
2500                oid       = g.getOID();
2501
2502                pgroup.addToMemberList(g);
2503
2504                // detect and stop loops
2505                // a loop is detected if there exists object with the same
2506                // object ID by tracing path back up to the root.
2507                boolean hasLoop = false;
2508                H5Group tmpObj  = (H5Group)parentObject;
2509
2510                while (tmpObj != null) {
2511                    if (tmpObj.equalsOID(oid) && (tmpObj.getPath() != null)) {
2512                        hasLoop = true;
2513                        break;
2514                    }
2515                    else {
2516                        tmpObj = (H5Group)tmpObj.getParent();
2517                    }
2518                }
2519
2520                // recursively go through the next group
2521                // stops if it has loop.
2522                if (!hasLoop) {
2523                    nTotal = depth_first(g, nTotal);
2524                }
2525            }
2526            else if (skipLoad) {
2527                continue;
2528            }
2529            else if (obj_type == HDF5Constants.H5O_TYPE_DATASET) {
2530                long did   = -1;
2531                long tid   = -1;
2532                int tclass = -1;
2533                try {
2534                    did = H5.H5Dopen(fid, fullPath + obj_name, HDF5Constants.H5P_DEFAULT);
2535                    if (did >= 0) {
2536                        tid = H5.H5Dget_type(did);
2537
2538                        tclass = H5.H5Tget_class(tid);
2539                        if ((tclass == HDF5Constants.H5T_ARRAY) || (tclass == HDF5Constants.H5T_VLEN)) {
2540                            // for ARRAY, the type is determined by the base type
2541                            long btid = H5.H5Tget_super(tid);
2542
2543                            tclass = H5.H5Tget_class(btid);
2544
2545                            try {
2546                                H5.H5Tclose(btid);
2547                            }
2548                            catch (Exception ex) {
2549                                log.debug("depth_first({})[{}] dataset {} H5Tclose(btid {}) failure: ",
2550                                          parentObject, i, obj_name, btid, ex);
2551                            }
2552                        }
2553                    }
2554                    else {
2555                        log.debug("depth_first({})[{}] {} dataset open failure", parentObject, i, obj_name);
2556                    }
2557                }
2558                catch (Exception ex) {
2559                    log.debug("depth_first({})[{}] {} dataset access failure: ", parentObject, i, obj_name,
2560                              ex);
2561                }
2562                finally {
2563                    try {
2564                        H5.H5Tclose(tid);
2565                    }
2566                    catch (Exception ex) {
2567                        log.debug("depth_first({})[{}] daatset {} H5Tclose(tid {}) failure: ", parentObject,
2568                                  i, obj_name, tid, ex);
2569                    }
2570                    try {
2571                        H5.H5Dclose(did);
2572                    }
2573                    catch (Exception ex) {
2574                        log.debug("depth_first({})[{}] dataset {} H5Dclose(did {}) failure: ", parentObject,
2575                                  i, obj_name, did, ex);
2576                    }
2577                }
2578                Dataset d = null;
2579                if (tclass == HDF5Constants.H5T_COMPOUND) {
2580                    // create a new compound dataset
2581                    d = new H5CompoundDS(this, obj_name, fullPath);
2582                }
2583                else {
2584                    // create a new scalar dataset
2585                    d = new H5ScalarDS(this, obj_name, fullPath);
2586                }
2587                oid = d.getOID();
2588
2589                pgroup.addToMemberList(d);
2590            }
2591            else if (obj_type == HDF5Constants.H5O_TYPE_NAMED_DATATYPE) {
2592                Datatype t = new H5Datatype(parentObject.getFileFormat(), obj_name, fullPath);
2593                log.trace("depth_first({}): H5O_TYPE_NAMED_DATATYPE name={}", parentObject, t.getFullName());
2594                oid = t.getOID();
2595
2596                pgroup.addToMemberList(t);
2597            }
2598            else if (obj_type == HDF5Constants.H5O_TYPE_UNKNOWN) {
2599                H5Link link = new H5Link(this, obj_name, fullPath);
2600                oid         = link.getOID();
2601
2602                pgroup.addToMemberList(link);
2603                continue; // do the next one, if the object is not identified.
2604            }
2605        } // ( i = 0; i < nelems; i++)
2606
2607        pgroup.close(gid);
2608
2609        log.debug("depth_first({}): nTotal={}", parentObject, nTotal);
2610        return nTotal;
2611    } // private depth_first()
2612
2613    /**
2614     * Returns a list of all the members of this H5File in a
2615     * breadth-first ordering that are rooted at the specified
2616     * object.
2617     */
2618    private static List<HObject> getMembersBreadthFirst(HObject obj)
2619    {
2620        List<HObject> allMembers = new Vector<>();
2621        Queue<HObject> queue     = new LinkedList<>();
2622        HObject currentObject    = obj;
2623
2624        queue.add(currentObject);
2625
2626        while (!queue.isEmpty()) {
2627            currentObject = queue.remove();
2628            allMembers.add(currentObject);
2629
2630            if (currentObject instanceof Group) {
2631                queue.addAll(((Group)currentObject).getMemberList());
2632            }
2633        }
2634
2635        return allMembers;
2636    }
2637
2638    private HObject copyDataset(Dataset srcDataset, H5Group pgroup, String dstName) throws Exception
2639    {
2640        Dataset dataset   = null;
2641        long srcdid       = -1;
2642        long dstdid       = -1;
2643        long ocp_plist_id = -1;
2644        String dname      = null;
2645        String path       = null;
2646
2647        if (pgroup.isRoot())
2648            path = HObject.SEPARATOR;
2649        else
2650            path = pgroup.getPath() + pgroup.getName() + HObject.SEPARATOR;
2651
2652        if ((dstName == null) || dstName.equals(HObject.SEPARATOR) || (dstName.length() < 1))
2653            dstName = srcDataset.getName();
2654        dname = path + dstName;
2655
2656        if (((H5Datatype)srcDataset.getDatatype()).isStdRef()) {
2657            log.debug("copyDataset(): isStdRef");
2658        }
2659        try {
2660            srcdid = srcDataset.open();
2661            dstdid = pgroup.open();
2662
2663            try {
2664                ocp_plist_id = H5.H5Pcreate(HDF5Constants.H5P_OBJECT_COPY);
2665                H5.H5Pset_copy_object(ocp_plist_id, HDF5Constants.H5O_COPY_EXPAND_REFERENCE_FLAG);
2666                H5.H5Ocopy(srcdid, ".", dstdid, dstName, ocp_plist_id, HDF5Constants.H5P_DEFAULT);
2667            }
2668            catch (Exception ex) {
2669                log.debug("copyDataset(): {} failure: ", dname, ex);
2670            }
2671            finally {
2672                try {
2673                    H5.H5Pclose(ocp_plist_id);
2674                }
2675                catch (Exception ex) {
2676                    log.debug("copyDataset(): {} H5Pclose(ocp_plist_id {}) failure: ", dname, ocp_plist_id,
2677                              ex);
2678                }
2679            }
2680
2681            if (srcDataset instanceof H5ScalarDS)
2682                dataset = new H5ScalarDS(pgroup.getFileFormat(), dstName, path);
2683            else
2684                dataset = new H5CompoundDS(pgroup.getFileFormat(), dstName, path);
2685
2686            pgroup.addToMemberList(dataset);
2687        }
2688        finally {
2689            try {
2690                srcDataset.close(srcdid);
2691            }
2692            catch (Exception ex) {
2693                log.debug("copyDataset(): {} srcDataset.close(srcdid {}) failure: ", dname, srcdid, ex);
2694            }
2695            try {
2696                pgroup.close(dstdid);
2697            }
2698            catch (Exception ex) {
2699                log.debug("copyDataset(): {} pgroup.close(dstdid {}) failure: ", dname, dstdid, ex);
2700            }
2701        }
2702
2703        return dataset;
2704    }
2705
2706    /**
2707     * Constructs a dataset for specified dataset identifier.
2708     *
2709     * @param did
2710     *            the dataset identifier
2711     * @param name
2712     *            the name of the dataset
2713     * @param path
2714     *            the path of the dataset
2715     *
2716     * @return the dataset if successful; otherwise return null.
2717     *
2718     * @throws HDF5Exception
2719     *             If there is an error at the HDF5 library level.
2720     */
2721    private Dataset getDataset(long did, String name, String path) throws HDF5Exception
2722    {
2723        Dataset dataset = null;
2724        if (did >= 0) {
2725            long tid   = -1;
2726            int tclass = -1;
2727            try {
2728                tid    = H5.H5Dget_type(did);
2729                tclass = H5.H5Tget_class(tid);
2730                if (tclass == HDF5Constants.H5T_ARRAY) {
2731                    // for ARRAY, the type is determined by the base type
2732                    long btid = H5.H5Tget_super(tid);
2733                    tclass    = H5.H5Tget_class(btid);
2734                    try {
2735                        H5.H5Tclose(btid);
2736                    }
2737                    catch (Exception ex) {
2738                        log.debug("getDataset(): {} H5Tclose(btid {}) failure: ", name, btid, ex);
2739                    }
2740                }
2741            }
2742            finally {
2743                try {
2744                    H5.H5Tclose(tid);
2745                }
2746                catch (Exception ex) {
2747                    log.debug("getDataset(): {} H5Tclose(tid {}) failure: ", name, tid, ex);
2748                }
2749            }
2750
2751            if (tclass == HDF5Constants.H5T_COMPOUND)
2752                dataset = new H5CompoundDS(this, name, path);
2753            else
2754                dataset = new H5ScalarDS(this, name, path);
2755        }
2756        else {
2757            log.debug("getDataset(): id failure");
2758        }
2759
2760        return dataset;
2761    }
2762
2763    /**
2764     * Copies a named datatype to another location.
2765     *
2766     * @param srcType
2767     *            the source datatype
2768     * @param pgroup
2769     *            the group which the new datatype is copied to
2770     * @param dstName
2771     *            the name of the new dataype
2772     *
2773     * @throws Exception
2774     *            If there is a failure.
2775     */
2776    private HObject copyDatatype(Datatype srcType, H5Group pgroup, String dstName) throws Exception
2777    {
2778        Datatype datatype = null;
2779        long tid_src      = -1;
2780        long gid_dst      = -1;
2781        String path       = null;
2782
2783        if (pgroup.isRoot())
2784            path = HObject.SEPARATOR;
2785        else
2786            path = pgroup.getPath() + pgroup.getName() + HObject.SEPARATOR;
2787
2788        if ((dstName == null) || dstName.equals(HObject.SEPARATOR) || (dstName.length() < 1))
2789            dstName = srcType.getName();
2790
2791        try {
2792            tid_src = srcType.open();
2793            gid_dst = pgroup.open();
2794
2795            try {
2796                H5.H5Ocopy(tid_src, ".", gid_dst, dstName, HDF5Constants.H5P_DEFAULT,
2797                           HDF5Constants.H5P_DEFAULT);
2798            }
2799            catch (Exception ex) {
2800                log.debug("copyDatatype(): {} H5Ocopy(tid_src {}) failure: ", dstName, tid_src, ex);
2801            }
2802            int nativeClass = H5.H5Tget_class(tid_src);
2803            if (nativeClass == HDF5Constants.H5T_REFERENCE)
2804                datatype = new H5ReferenceType(pgroup.getFileFormat(), dstName, path);
2805            else
2806                datatype = new H5Datatype(pgroup.getFileFormat(), dstName, path);
2807
2808            pgroup.addToMemberList(datatype);
2809        }
2810        finally {
2811            try {
2812                srcType.close(tid_src);
2813            }
2814            catch (Exception ex) {
2815                log.debug("copyDatatype(): {} srcType.close(tid_src {}) failure: ", dstName, tid_src, ex);
2816            }
2817            try {
2818                pgroup.close(gid_dst);
2819            }
2820            catch (Exception ex) {
2821                log.debug("copyDatatype(): {} pgroup.close(gid_dst {}) failure: ", dstName, gid_dst, ex);
2822            }
2823        }
2824
2825        return datatype;
2826    }
2827
2828    /**
2829     * Copies a group and its members to a new location.
2830     *
2831     * @param srcGroup
2832     *            the source group
2833     * @param dstGroup
2834     *            the location where the new group is located
2835     * @param dstName
2836     *            the name of the new group
2837     *
2838     * @throws Exception
2839     *            If there is a failure.
2840     */
2841    private HObject copyGroup(H5Group srcGroup, H5Group dstGroup, String dstName) throws Exception
2842    {
2843        H5Group group = null;
2844        long srcgid = -1, dstgid = -1;
2845        String path = null;
2846
2847        if (dstGroup.isRoot())
2848            path = HObject.SEPARATOR;
2849        else
2850            path = dstGroup.getPath() + dstGroup.getName() + HObject.SEPARATOR;
2851
2852        if ((dstName == null) || dstName.equals(HObject.SEPARATOR) || (dstName.length() < 1))
2853            dstName = srcGroup.getName();
2854
2855        try {
2856            srcgid = srcGroup.open();
2857            dstgid = dstGroup.open();
2858            try {
2859                H5.H5Ocopy(srcgid, ".", dstgid, dstName, HDF5Constants.H5P_DEFAULT,
2860                           HDF5Constants.H5P_DEFAULT);
2861            }
2862            catch (Exception ex) {
2863                log.debug("copyGroup(): {} H5Ocopy(srcgid {}) failure: ", dstName, srcgid, ex);
2864            }
2865
2866            group = new H5Group(dstGroup.getFileFormat(), dstName, path, dstGroup);
2867            depth_first(group, Integer.MIN_VALUE); // reload all
2868            dstGroup.addToMemberList(group);
2869        }
2870
2871        finally {
2872            try {
2873                srcGroup.close(srcgid);
2874            }
2875            catch (Exception ex) {
2876                log.debug("copyGroup(): {} srcGroup.close(srcgid {}) failure: ", dstName, srcgid, ex);
2877            }
2878            try {
2879                dstGroup.close(dstgid);
2880            }
2881            catch (Exception ex) {
2882                log.debug("copyGroup(): {} pgroup.close(dstgid {}) failure: ", dstName, dstgid, ex);
2883            }
2884        }
2885
2886        return group;
2887    }
2888
2889    /**
2890     * Constructs a group for specified group identifier and retrieves members.
2891     *
2892     * @param gid
2893     *            The group identifier.
2894     * @param name
2895     *            The group name.
2896     * @param pGroup
2897     *            The parent group, or null for the root group.
2898     *
2899     * @return The group if successful; otherwise returns false.
2900     *
2901     * @throws HDF5Exception
2902     *             If there is an error at the HDF5 library level.
2903     */
2904    private H5Group getGroup(long gid, String name, Group pGroup) throws HDF5Exception
2905    {
2906        String parentPath     = null;
2907        String thisFullName   = null;
2908        String memberFullName = null;
2909
2910        if (pGroup == null) {
2911            thisFullName = name = "/";
2912        }
2913        else {
2914            parentPath = pGroup.getFullName();
2915            if ((parentPath == null) || parentPath.equals("/"))
2916                thisFullName = "/" + name;
2917            else
2918                thisFullName = parentPath + "/" + name;
2919        }
2920
2921        // get rid of any extra "/"
2922        if (parentPath != null)
2923            parentPath = parentPath.replaceAll("//", "/");
2924        if (thisFullName != null)
2925            thisFullName = thisFullName.replaceAll("//", "/");
2926
2927        log.trace("getGroup(): fullName={}", thisFullName);
2928
2929        H5Group group = new H5Group(this, name, parentPath, pGroup);
2930
2931        H5G_info_t group_info = null;
2932        H5O_info_t obj_info   = null;
2933        long objid            = -1;
2934        String link_name      = null;
2935        try {
2936            group_info = H5.H5Gget_info(gid);
2937        }
2938        catch (Exception ex) {
2939            log.debug("getGroup(): {} H5Gget_info(gid {}) failure: ", name, gid, ex);
2940        }
2941        try {
2942            objid = H5.H5Oopen(gid, thisFullName, HDF5Constants.H5P_DEFAULT);
2943        }
2944        catch (Exception ex) {
2945            log.debug("getGroup(): {} H5Oopen(gid {}) failure: ", name, gid, ex);
2946        }
2947
2948        // retrieve only the immediate members of the group, do not follow
2949        // subgroups
2950        for (int i = 0; i < group_info.nlinks; i++) {
2951            try {
2952                link_name = H5.H5Lget_name_by_idx(gid, thisFullName, indexType, indexOrder, i,
2953                                                  HDF5Constants.H5P_DEFAULT);
2954                obj_info  = H5.H5Oget_info_by_idx(objid, thisFullName, indexType, indexOrder, i,
2955                                                  HDF5Constants.H5P_DEFAULT);
2956            }
2957            catch (HDF5Exception ex) {
2958                log.debug("getGroup()[{}]: {} name,info failure: ", i, name, ex);
2959                // do not stop if accessing one member fails
2960                continue;
2961            }
2962            // create a new group
2963            if (obj_info.type == HDF5Constants.H5O_TYPE_GROUP) {
2964                H5Group g = new H5Group(this, link_name, thisFullName, group);
2965                group.addToMemberList(g);
2966            }
2967            else if (obj_info.type == HDF5Constants.H5O_TYPE_DATASET) {
2968                long did  = -1;
2969                Dataset d = null;
2970
2971                if ((thisFullName == null) || thisFullName.equals("/"))
2972                    memberFullName = "/" + link_name;
2973                else
2974                    memberFullName = thisFullName + "/" + link_name;
2975
2976                try {
2977                    did = H5.H5Dopen(fid, memberFullName, HDF5Constants.H5P_DEFAULT);
2978                    d   = getDataset(did, link_name, thisFullName);
2979                }
2980                finally {
2981                    try {
2982                        H5.H5Dclose(did);
2983                    }
2984                    catch (Exception ex) {
2985                        log.debug("getGroup()[{}]: {} H5Dclose(did {}) failure: ", i, name, did, ex);
2986                    }
2987                }
2988                group.addToMemberList(d);
2989            }
2990            else if (obj_info.type == HDF5Constants.H5O_TYPE_NAMED_DATATYPE) {
2991                Datatype t = new H5Datatype(group.getFileFormat(), link_name, thisFullName);
2992                group.addToMemberList(t);
2993            }
2994        } // End of for loop.
2995        try {
2996            if (objid >= 0)
2997                H5.H5Oclose(objid);
2998        }
2999        catch (Exception ex) {
3000            log.debug("getGroup(): {} H5Oclose(oid {}) failure: ", name, objid, ex);
3001        }
3002
3003        return group;
3004    }
3005
3006    /**
3007     * Retrieves the name of the target object that is being linked to.
3008     *
3009     * @param obj
3010     *            The current link object.
3011     *
3012     * @return The name of the target object.
3013     *
3014     * @throws Exception
3015     *             If there is an error at the HDF5 library level.
3016     */
3017    public static String getLinkTargetName(HObject obj) throws Exception
3018    {
3019        String[] link_value  = {null, null};
3020        String targetObjName = null;
3021
3022        if (obj == null) {
3023            log.debug("getLinkTargetName(): object is null");
3024            return null;
3025        }
3026
3027        if (obj.getFullName().equals("/")) {
3028            log.debug("getLinkTargetName(): object is root group, links not allowed");
3029            return null;
3030        }
3031
3032        H5L_info_t link_info = null;
3033        if (obj.getFID() < 0)
3034            log.trace("getLinkTargetName(): file id for:{} is invalid", obj.getFullName());
3035        else {
3036            try {
3037                link_info = H5.H5Lget_info(obj.getFID(), obj.getFullName(), HDF5Constants.H5P_DEFAULT);
3038            }
3039            catch (Exception err) {
3040                log.debug("getLinkTargetName(): H5Lget_info {} failure: ", obj.getFullName(), err);
3041            }
3042        }
3043        if (link_info != null) {
3044            if ((link_info.type == HDF5Constants.H5L_TYPE_SOFT) ||
3045                (link_info.type == HDF5Constants.H5L_TYPE_EXTERNAL)) {
3046                try {
3047                    H5.H5Lget_value(obj.getFID(), obj.getFullName(), link_value, HDF5Constants.H5P_DEFAULT);
3048                }
3049                catch (Exception ex) {
3050                    log.debug("getLinkTargetName(): H5Lget_value {} failure: ", obj.getFullName(), ex);
3051                }
3052                if (link_info.type == HDF5Constants.H5L_TYPE_SOFT)
3053                    targetObjName = link_value[0];
3054                else if (link_info.type == HDF5Constants.H5L_TYPE_EXTERNAL)
3055                    targetObjName = link_value[1] + FileFormat.FILE_OBJ_SEP + link_value[0];
3056            }
3057        }
3058
3059        return targetObjName;
3060    }
3061
3062    /**
3063     * Export dataset.
3064     *
3065     * @param file_export_name
3066     *            The file name to export data into.
3067     * @param object
3068     *            The id of the HDF5 dataset.
3069     * @param binary_order
3070     *            The data byte order
3071     *
3072     * @throws Exception
3073     *            If there is a failure.
3074     */
3075    @Override
3076    public void exportDataset(String file_export_name, Dataset object, int binary_order) throws Exception
3077    {
3078        long did = object.open();
3079        H5.H5export_dataset(file_export_name, did, object.getFullName(), binary_order);
3080        object.close(did);
3081    }
3082
3083    /**
3084     * Renames an attribute.
3085     *
3086     * @param obj
3087     *            The object whose attribute is to be renamed.
3088     * @param oldAttrName
3089     *            The current name of the attribute.
3090     * @param newAttrName
3091     *            The new name of the attribute.
3092     *
3093     * @throws Exception
3094     *             If there is an error at the HDF5 library level.
3095     */
3096    @Override
3097    public void renameAttribute(HObject obj, String oldAttrName, String newAttrName) throws Exception
3098    {
3099        log.trace("renameAttribute(): rename {} to {}", oldAttrName, newAttrName);
3100        H5.H5Arename_by_name(obj.getFID(), obj.getFullName(), oldAttrName, newAttrName,
3101                             HDF5Constants.H5P_DEFAULT);
3102    }
3103
3104    /**
3105     * Rename the given object
3106     *
3107     * @param obj
3108     *            the object to be renamed.
3109     * @param newName
3110     *            the new name of the object.
3111     *
3112     * @throws Exception
3113     *            If there is a failure.
3114     */
3115    public static void renameObject(HObject obj, String newName) throws Exception
3116    {
3117        renameObject(obj, obj.getPath(), newName);
3118    }
3119
3120    /**
3121     * Rename the given object
3122     *
3123     * @param obj
3124     *            the object to be renamed.
3125     * @param newPath
3126     *            the new path of the object.
3127     * @param newName
3128     *            the new name of the object.
3129     *
3130     * @throws Exception
3131     *            If there is a failure.
3132     */
3133    public static void renameObject(HObject obj, String newPath, String newName) throws Exception
3134    {
3135        String currentFullPath = obj.getFullName();
3136        String newFullPath     = obj.createFullname(newPath, newName);
3137
3138        log.trace("renameObject(): currentFullPath={} newFullPath={}", currentFullPath, newFullPath);
3139        if ((currentFullPath != null) && (newFullPath != null)) {
3140            currentFullPath = currentFullPath.replaceAll("//", "/");
3141            newFullPath     = newFullPath.replaceAll("//", "/");
3142
3143            if (currentFullPath.equals("/") && obj instanceof Group)
3144                throw new HDF5Exception("Can't rename the root group.");
3145
3146            if (currentFullPath.equals(newFullPath))
3147                throw new HDF5Exception("The new name is the same as the current name.");
3148
3149            // Call the library to move things in the file if object exists
3150            if (obj.getName() != null)
3151                H5.H5Lmove(obj.getFID(), currentFullPath, obj.getFID(), newFullPath,
3152                           HDF5Constants.H5P_DEFAULT, HDF5Constants.H5P_DEFAULT);
3153        }
3154    }
3155
3156    /**
3157     * Get the value of the index type value.
3158     *
3159     * @return the int value of the index type value.
3160     *
3161     * @param strtype The name of the index type.
3162     */
3163    public static int getIndexTypeValue(String strtype)
3164    {
3165        if (strtype.compareTo("H5_INDEX_NAME") == 0)
3166            return HDF5Constants.H5_INDEX_NAME;
3167        if (strtype.compareTo("H5_INDEX_CRT_ORDER") == 0)
3168            return HDF5Constants.H5_INDEX_CRT_ORDER;
3169        if (strtype.compareTo("H5_INDEX_N") == 0)
3170            return HDF5Constants.H5_INDEX_N;
3171        return HDF5Constants.H5_INDEX_UNKNOWN;
3172    }
3173
3174    /**
3175     * Get the value of the index order.
3176     *
3177     * @return the int value of the index order.
3178     *
3179     * @param strorder The name of the index order.
3180     */
3181    public static int getIndexOrderValue(String strorder)
3182    {
3183        if (strorder.compareTo("H5_ITER_INC") == 0)
3184            return HDF5Constants.H5_ITER_INC;
3185        if (strorder.compareTo("H5_ITER_DEC") == 0)
3186            return HDF5Constants.H5_ITER_DEC;
3187        if (strorder.compareTo("H5_ITER_NATIVE") == 0)
3188            return HDF5Constants.H5_ITER_NATIVE;
3189        if (strorder.compareTo("H5_ITER_N") == 0)
3190            return HDF5Constants.H5_ITER_N;
3191        return HDF5Constants.H5_ITER_UNKNOWN;
3192    }
3193
3194    @Override
3195    /**
3196     * Get the value of the index type.
3197     *
3198     * @return the int value of the index type.
3199     *
3200     * @param strtype The name of the index type.
3201     */
3202    public int getIndexType(String strtype)
3203    {
3204        if (strtype != null) {
3205            if (strtype.compareTo("H5_INDEX_NAME") == 0)
3206                return HDF5Constants.H5_INDEX_NAME;
3207            if (strtype.compareTo("H5_INDEX_CRT_ORDER") == 0)
3208                return HDF5Constants.H5_INDEX_CRT_ORDER;
3209            return HDF5Constants.H5_INDEX_UNKNOWN;
3210        }
3211        return getIndexType();
3212    }
3213
3214    /**
3215     * Get the current value of the index type.
3216     *
3217     * @return the current value of the index type.
3218     */
3219    public int getIndexType() { return indexType; }
3220
3221    @Override
3222    /**
3223     * set the int value of the index type.
3224     *
3225     * @param indexType
3226     *            The value of the index type.
3227     */
3228    public void setIndexType(int indexType)
3229    {
3230        this.indexType = indexType;
3231    }
3232
3233    @Override
3234    /**
3235     * Get the value of the index order value.
3236     *
3237     * @return the int value of the index order value.
3238     *
3239     * @param strorder The name of the index order.
3240     */
3241    public int getIndexOrder(String strorder)
3242    {
3243        if (strorder != null) {
3244            if (strorder.compareTo("H5_ITER_INC") == 0)
3245                return HDF5Constants.H5_ITER_INC;
3246            if (strorder.compareTo("H5_ITER_DEC") == 0)
3247                return HDF5Constants.H5_ITER_DEC;
3248            if (strorder.compareTo("H5_ITER_NATIVE") == 0)
3249                return HDF5Constants.H5_ITER_NATIVE;
3250            if (strorder.compareTo("H5_ITER_N") == 0)
3251                return HDF5Constants.H5_ITER_N;
3252            return HDF5Constants.H5_ITER_UNKNOWN;
3253        }
3254        return getIndexOrder();
3255    }
3256
3257    /**
3258     * Get the current value of the index order.
3259     *
3260     * @return the current value of the index order.
3261     */
3262    public int getIndexOrder() { return indexOrder; }
3263
3264    @Override
3265    /**
3266     * set the current value of the index order.
3267     *
3268     * @param indexOrder
3269     *            The index order.
3270     */
3271    public void setIndexOrder(int indexOrder)
3272    {
3273        this.indexOrder = indexOrder;
3274    }
3275}