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