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