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.util.List; 018import java.util.Vector; 019 020import org.slf4j.Logger; 021import org.slf4j.LoggerFactory; 022 023import hdf.hdf5lib.H5; 024import hdf.hdf5lib.HDF5Constants; 025import hdf.hdf5lib.HDFArray; 026import hdf.hdf5lib.HDFNativeData; 027import hdf.hdf5lib.exceptions.HDF5Exception; 028import hdf.hdf5lib.structs.H5G_info_t; 029import hdf.hdf5lib.structs.H5O_info_t; 030import hdf.hdf5lib.structs.H5O_token_t; 031 032import hdf.object.Attribute; 033import hdf.object.FileFormat; 034import hdf.object.Group; 035import hdf.object.HObject; 036 037import hdf.object.h5.H5MetaDataContainer; 038 039/** 040 * An H5Group object represents an existing HDF5 group in file. 041 * 042 * In HDF5, every object has at least one name. An HDF5 group is used to store a set of the names together in one place, 043 * i.e. a group. The general structure of a group is similar to that of the UNIX file system in that the group may 044 * contain references to other groups or data objects just as the UNIX directory may contain sub-directories or files. 045 * 046 * For more information on HDF5 Groups, 047 * 048 * <a href="https://hdfgroup.github.io/hdf5/_h5_g__u_g.html#sec_group">HDF5 Groups in HDF5 User Guide</a> 049 * 050 * @version 1.1 9/4/2007 051 * @author Peter X. Cao 052 */ 053public class H5Group extends Group 054{ 055 private static final long serialVersionUID = -951164512330444150L; 056 057 private static final Logger log = LoggerFactory.getLogger(H5Group.class); 058 059 /** 060 * The metadata object for this data object. Members of the metadata are instances of Attribute. 061 */ 062 private H5MetaDataContainer objMetadata; 063 064 /** the object properties */ 065 private H5O_info_t objInfo; 066 067 /** 068 * Constructs an HDF5 group with specific name, path, and parent. 069 * 070 * @param theFile 071 * the file that contains the group. 072 * @param theName 073 * the name of this group, e.g. "grp01". 074 * @param thePath 075 * the full path of this group, e.g. "/groups/". 076 * @param theParent 077 * the parent of this group. 078 */ 079 public H5Group(FileFormat theFile, String theName, String thePath, Group theParent) { 080 this(theFile, theName, thePath, theParent, null); 081 } 082 083 /** 084 * @deprecated Not for public use in the future.<br> 085 * Using {@link #H5Group(FileFormat, String, String, Group)} 086 * 087 * @param theFile 088 * the file that contains the group. 089 * @param theName 090 * the name of this group, e.g. "grp01". 091 * @param thePath 092 * the full path of this group, e.g. "/groups/". 093 * @param theParent 094 * the parent of this group. 095 * @param oid 096 * the oid of this group. 097 */ 098 @Deprecated 099 public H5Group(FileFormat theFile, String theName, String thePath, Group theParent, long[] oid) { 100 super(theFile, theName, thePath, theParent, oid); 101 nMembersInFile = -1; 102 objMetadata = new H5MetaDataContainer(theFile, theName, thePath, this); 103 104 if (theFile != null) { 105 if (oid == null) { 106 // retrieve the object ID 107 byte[] refBuf = null; 108 try { 109 refBuf = H5.H5Rcreate_object(theFile.getFID(), this.getFullName(), HDF5Constants.H5P_DEFAULT); 110 this.oid = HDFNativeData.byteToLong(refBuf); 111 log.trace("constructor REF {} to OID {}", refBuf, this.oid); 112 } 113 catch (Exception ex) { 114 log.debug("constructor ID {} for {} failed H5Rcreate_object", theFile.getFID(), this.getFullName()); 115 } 116 finally { 117 if (refBuf != null) 118 H5.H5Rdestroy(refBuf); 119 } 120 } 121 log.trace("constructor OID {}", this.oid); 122 try { 123 objInfo = H5.H5Oget_info_by_name(theFile.getFID(), this.getFullName(), HDF5Constants.H5O_INFO_BASIC, HDF5Constants.H5P_DEFAULT); 124 } 125 catch (Exception ex) { 126 objInfo = new H5O_info_t(-1L, null, 0, 0, 0L, 0L, 0L, 0L, 0L); 127 } 128 } 129 else { 130 this.oid = null; 131 objInfo = new H5O_info_t(-1L, null, 0, 0, 0L, 0L, 0L, 0L, 0L); 132 } 133 } 134 135 /* 136 * (non-Javadoc) 137 * 138 * @see hdf.object.HObject#open() 139 */ 140 @Override 141 public long open() { 142 long gid = HDF5Constants.H5I_INVALID_HID; 143 144 try { 145 if (isRoot()) { 146 gid = H5.H5Gopen(getFID(), SEPARATOR, HDF5Constants.H5P_DEFAULT); 147 } 148 else { 149 gid = H5.H5Gopen(getFID(), getPath() + getName(), HDF5Constants.H5P_DEFAULT); 150 } 151 log.trace("open(): gid={}", gid); 152 } 153 catch (HDF5Exception ex) { 154 log.debug("open(): Failed to open group {}", getPath() + getName(), ex); 155 gid = HDF5Constants.H5I_INVALID_HID; 156 } 157 158 return gid; 159 } 160 161 /* 162 * (non-Javadoc) 163 * 164 * @see hdf.object.HObject#close(int) 165 */ 166 @Override 167 public void close(long gid) { 168 if (gid >= 0) { 169 try { 170 H5.H5Gclose(gid); 171 } 172 catch (HDF5Exception ex) { 173 log.debug("close(): H5Gclose(gid {}): ", gid, ex); 174 } 175 } 176 } 177 178 /** 179 * Get the token for this object. 180 * 181 * @return true if it has any attributes, false otherwise. 182 */ 183 public long[] getToken() { 184 H5O_token_t token = objInfo.token; 185 return HDFNativeData.byteToLong(token.data); 186 } 187 188 /** 189 * Check if the object has any attributes attached. 190 * 191 * @return true if it has any attributes, false otherwise. 192 */ 193 @Override 194 public boolean hasAttribute() { 195 objInfo.num_attrs = objMetadata.getObjectAttributeSize(); 196 197 if (objInfo.num_attrs < 0) { 198 long gid = open(); 199 if (gid > 0) { 200 try { 201 objInfo = H5.H5Oget_info(gid); 202 } 203 catch (Exception ex) { 204 objInfo.num_attrs = 0; 205 log.debug("hasAttribute(): get object info failure: ", ex); 206 } 207 finally { 208 close(gid); 209 } 210 objMetadata.setObjectAttributeSize((int) objInfo.num_attrs); 211 } 212 else { 213 log.debug("hasAttribute(): could not open group"); 214 } 215 } 216 217 log.trace("hasAttribute(): nAttributes={}", objInfo.num_attrs); 218 return (objInfo.num_attrs > 0); 219 } 220 221 /* 222 * (non-Javadoc) 223 * 224 * @see hdf.object.Group#getNumberOfMembersInFile() 225 */ 226 @Override 227 public int getNumberOfMembersInFile() { 228 if (nMembersInFile < 0) { 229 long gid = open(); 230 if (gid > 0) { 231 try { 232 H5G_info_t group_info = null; 233 group_info = H5.H5Gget_info(gid); 234 nMembersInFile = (int) group_info.nlinks; 235 } 236 catch (Exception ex) { 237 nMembersInFile = 0; 238 } 239 close(gid); 240 } 241 } 242 return nMembersInFile; 243 } 244 245 /** 246 * Removes all of the elements from metadata list. 247 * The list should be empty after this call returns. 248 */ 249 @Override 250 public void clear() { 251 super.clear(); 252 objMetadata.clear(); 253 } 254 255 /** 256 * Retrieves the object's metadata, such as attributes, from the file. 257 * 258 * Metadata, such as attributes, is stored in a List. 259 * 260 * @return the list of metadata objects. 261 * 262 * @throws HDF5Exception 263 * if the metadata can not be retrieved 264 */ 265 @Override 266 public List<Attribute> getMetadata() throws HDF5Exception { 267 int gmIndexType = 0; 268 int gmIndexOrder = 0; 269 270 try { 271 gmIndexType = fileFormat.getIndexType(null); 272 } 273 catch (Exception ex) { 274 log.debug("getMetadata(): getIndexType failed: ", ex); 275 } 276 try { 277 gmIndexOrder = fileFormat.getIndexOrder(null); 278 } 279 catch (Exception ex) { 280 log.debug("getMetadata(): getIndexOrder failed: ", ex); 281 } 282 return this.getMetadata(gmIndexType, gmIndexOrder); 283 } 284 285 /** 286 * Retrieves the object's metadata, such as attributes, from the file. 287 * 288 * Metadata, such as attributes, is stored in a List. 289 * 290 * @param attrPropList 291 * the list of properties to get 292 * 293 * @return the list of metadata objects. 294 * 295 * @throws HDF5Exception 296 * if the metadata can not be retrieved 297 */ 298 public List<Attribute> getMetadata(int... attrPropList) throws HDF5Exception { 299 try { 300 this.linkTargetObjName = H5File.getLinkTargetName(this); 301 } 302 catch (Exception ex) { 303 log.debug("getMetadata(): getLinkTargetName failed: ", ex); 304 } 305 306 List<Attribute> attrlist = null; 307 try { 308 attrlist = objMetadata.getMetadata(attrPropList); 309 } 310 catch (Exception ex) { 311 log.debug("getMetadata(): getMetadata failed: ", ex); 312 } 313 return attrlist; 314 } 315 316 /** 317 * Writes a specific piece of metadata (such as an attribute) into the file. 318 * 319 * If an HDF(4&5) attribute exists in the file, this method updates its 320 * value. If the attribute does not exist in the file, it creates the 321 * attribute in the file and attaches it to the object. It will fail to 322 * write a new attribute to the object where an attribute with the same name 323 * already exists. To update the value of an existing attribute in the file, 324 * one needs to get the instance of the attribute by getMetadata(), change 325 * its values, then use writeMetadata() to write the value. 326 * 327 * @param info 328 * the metadata to write. 329 * 330 * @throws Exception 331 * if the metadata can not be written 332 */ 333 @Override 334 public void writeMetadata(Object info) throws Exception { 335 try { 336 objMetadata.writeMetadata(info); 337 } 338 catch (Exception ex) { 339 log.debug("writeMetadata(): Object not an Attribute"); 340 } 341 } 342 343 /** 344 * Deletes an existing piece of metadata from this object. 345 * 346 * @param info 347 * the metadata to delete. 348 * 349 * @throws HDF5Exception 350 * if the metadata can not be removed 351 */ 352 @Override 353 public void removeMetadata(Object info) throws HDF5Exception { 354 try { 355 objMetadata.removeMetadata(info); 356 } 357 catch (Exception ex) { 358 log.debug("removeMetadata(): Object not an Attribute"); 359 return; 360 } 361 362 Attribute attr = (Attribute) info; 363 log.trace("removeMetadata(): {}", attr.getAttributeName()); 364 long gid = open(); 365 if (gid >= 0) { 366 try { 367 H5.H5Adelete(gid, attr.getAttributeName()); 368 } 369 catch (Exception ex) { 370 log.debug("removeMetadata(): ", ex); 371 } 372 finally { 373 close(gid); 374 } 375 } 376 else { 377 log.debug("removeMetadata(): failed to open group"); 378 } 379 } 380 381 /** 382 * Updates an existing piece of metadata attached to this object. 383 * 384 * @param info 385 * the metadata to update. 386 * 387 * @throws HDF5Exception 388 * if the metadata can not be updated 389 */ 390 @Override 391 public void updateMetadata(Object info) throws HDF5Exception { 392 try { 393 objMetadata.updateMetadata(info); 394 } 395 catch (Exception ex) { 396 log.debug("updateMetadata(): Object not an Attribute"); 397 return; 398 } 399 } 400 401 /* 402 * (non-Javadoc) 403 * 404 * @see hdf.object.HObject#setName(java.lang.String) 405 */ 406 @Override 407 public void setName(String newName) throws Exception { 408 if (newName == null) 409 throw new IllegalArgumentException("The new name is NULL"); 410 411 H5File.renameObject(this, newName); 412 super.setName(newName); 413 } 414 415 /* 416 * (non-Javadoc) 417 * 418 * @see hdf.object.HObject#setPath(java.lang.String) 419 */ 420 @SuppressWarnings("rawtypes") 421 @Override 422 public void setPath(String newPath) throws Exception { 423 super.setPath(newPath); 424 425 List members = this.getMemberList(); 426 if (members == null) 427 return; 428 429 int n = members.size(); 430 HObject obj = null; 431 for (int i = 0; i < n; i++) { 432 obj = (HObject) members.get(i); 433 obj.setPath(getPath() + getName() + HObject.SEPARATOR); 434 } 435 } 436 437 /** 438 * Creates a new group with a name in a group and with the group creation 439 * properties specified in gplist. 440 * 441 * The gplist contains a sequence of group creation property list 442 * identifiers, lcpl, gcpl, gapl. It allows the user to create a group with 443 * group creation properties. It will close the group creation properties 444 * specified in gplist. 445 * 446 * @see hdf.hdf5lib.H5#H5Gcreate(long, String, long, long, long) for the 447 * order of property list identifiers. 448 * 449 * @param name 450 * The name of a new group. 451 * @param pgroup 452 * The parent group object. 453 * @param gplist 454 * The group creation properties, in which the order of the 455 * properties conforms the HDF5 library API, H5Gcreate(), i.e. 456 * lcpl, gcpl and gapl, where 457 * <ul> 458 * <li>lcpl : Property list for link creation <li>gcpl : Property 459 * list for group creation <li>gapl : Property list for group 460 * access 461 * </ul> 462 * 463 * @return The new group if successful; otherwise returns null. 464 * 465 * @throws Exception if there is a failure. 466 */ 467 public static H5Group create(String name, Group pgroup, long... gplist) throws Exception { 468 H5Group group = null; 469 String fullPath = null; 470 long lcpl = HDF5Constants.H5P_DEFAULT; 471 long gcpl = HDF5Constants.H5P_DEFAULT; 472 long gapl = HDF5Constants.H5P_DEFAULT; 473 474 if (gplist.length > 0) { 475 lcpl = gplist[0]; 476 if (gplist.length > 1) { 477 gcpl = gplist[1]; 478 if (gplist.length > 2) gapl = gplist[2]; 479 } 480 } 481 482 if ((name == null) || (pgroup == null)) { 483 log.debug("create(): one or more parameters are null"); 484 return null; 485 } 486 487 H5File file = (H5File) pgroup.getFileFormat(); 488 if (file == null) { 489 log.debug("create(): parent group FileFormat is null"); 490 return null; 491 } 492 493 String path = HObject.SEPARATOR; 494 if (!pgroup.isRoot()) { 495 path = pgroup.getPath() + pgroup.getName() + HObject.SEPARATOR; 496 if (name.endsWith("/")) 497 name = name.substring(0, name.length() - 1); 498 int idx = name.lastIndexOf('/'); 499 if (idx >= 0) 500 name = name.substring(idx + 1); 501 } 502 503 fullPath = path + name; 504 505 // create a new group and add it to the parent node 506 long gid = H5.H5Gcreate(file.open(), fullPath, lcpl, gcpl, gapl); 507 try { 508 H5.H5Gclose(gid); 509 } 510 catch (Exception ex) { 511 log.debug("create(): H5Gcreate {} H5Gclose(gid {}) failure: ", fullPath, gid, ex); 512 } 513 514 group = new H5Group(file, name, path, pgroup); 515 516 if (group != null) 517 pgroup.addToMemberList(group); 518 519 if (gcpl > 0) { 520 try { 521 H5.H5Pclose(gcpl); 522 } 523 catch (final Exception ex) { 524 log.debug("create(): create prop H5Pclose(gcpl {}) failure: ", gcpl, ex); 525 } 526 } 527 528 return group; 529 } 530 531}