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