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