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