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