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