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