001/***************************************************************************** 002 * Copyright by The HDF Group. * 003 * Copyright by the Board of Trustees of the University of Illinois. * 004 * All rights reserved. * 005 * * 006 * This file is part of the HDF Java Products distribution. * 007 * The full copyright notice, including terms governing use, modification, * 008 * and redistribution, is contained in the files COPYING and Copyright.html. * 009 * COPYING can be found at the root of the source code distribution tree. * 010 * Or, see https://support.hdfgroup.org/products/licenses.html * 011 * If you do not have access to either file, you may request a copy from * 012 * help@hdfgroup.org. * 013 ****************************************************************************/ 014 015package hdf.object; 016 017import java.io.Serializable; 018 019/** 020 * The HObject class is the root class of all the HDF data objects. Every data 021 * class has HObject as a superclass. All objects implement the methods of this 022 * class. The following is the inherited structure of HDF Objects. 023 * 024 * <pre> 025 * HObject 026 * __________________________|________________________________ 027 * | | | 028 * Group Dataset Datatype 029 * | _________|___________ | 030 * | | | | 031 * | ScalarDS CompoundDS | 032 * | | | | 033 * ---------------------Implementing classes such as------------------------- 034 * ____|____ _____|______ _____|_____ _____|_____ 035 * | | | | | | | | 036 * H5Group H4Group H5ScalarDS H4ScalarDS H5CompDS H4CompDS H5Datatype H4Datatype 037 * 038 * </pre> 039 * 040 * All HDF4 and HDF5 data objects are inherited from HObject. At the top level 041 * of the hierarchy, both HDF4 and HDF5 have the same super-classes, such as 042 * Group and Dataset. At the bottom level of the hierarchy, HDF4 and HDF5 043 * objects have their own implementation, such as H5Group, H5ScalarDS, 044 * H5CompoundDS, and H5Datatype. 045 * 046 * <b>Warning: HDF4 and HDF5 may have multiple links to the same object. Data 047 * objects in this model do not deal with multiple links. Users may create 048 * duplicate copies of the same data object with different paths. Applications 049 * should check the OID of the data object to avoid duplicate copies of the same 050 * object.</b> 051 * 052 * HDF4 objects are uniquely identified by the OID (tag_id, ref_id) pair. The 053 * ref_id is the object reference count. The tag_id is a pre-defined number to 054 * identify the type of object. For example, DFTAG_RI is for raster image, 055 * DFTAG_SD is for scientific dataset, and DFTAG_VG is for Vgroup. 056 * 057 * HDF5 objects are uniquely identified by the OID containing just the object 058 * reference. The OID is usually obtained by H5Rcreate_object(). The following example 059 * shows how to retrieve an object ID from a file: 060 * 061 * <pre> 062 * // retrieve the object ID 063 * byte[] refBuf = null; 064 * try { 065 * refBuf = H5.H5Rcreate_object(theFile.getFID(), this.getFullName(), HDF5Constants.H5P_DEFAULT); 066 * this.oid = HDFNativeData.byteToLong(refBuf); 067 * log.trace("constructor REF {} to OID {}", refBuf, this.oid); 068 * } 069 * catch (Exception ex) { 070 * log.debug("constructor ID {} for {} failed H5Rcreate_object", theFile.getFID(), this.getFullName()); 071 * } 072 * finally { 073 * if (refBuf) 074 * H5.H5Rdestroy(refBuf); 075 * } 076 * </pre> 077 * 078 * @version 2.0 4/2/2018 079 * @author Peter X. Cao, Jordan T. Henderson 080 * @see <a href="DataFormat.html">hdf.object.DataFormat</a> 081 */ 082public abstract class HObject implements Serializable 083{ 084 /** 085 * The serialVersionUID is a universal version identifier for a Serializable 086 * class. Deserialization uses this number to ensure that a loaded class 087 * corresponds exactly to a serialized object. For details, see 088 * http://java.sun.com/j2se/1.5.0/docs/api/java/io/Serializable.html 089 */ 090 private static final long serialVersionUID = -1723666708199882519L; 091 092 private static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(HObject.class); 093 094 /** 095 * The separator of object path, i.e. "/". 096 */ 097 public static final String SEPARATOR = "/"; 098 099 /** 100 * The full path of the file that contains the object. 101 */ 102 private String filename; 103 104 /** 105 * The file which contains the object 106 */ 107 protected final FileFormat fileFormat; 108 109 /** 110 * The name of the data object. The root group has its default name, a 111 * slash. The name can be changed except the root group. 112 */ 113 private String name; 114 115 /** 116 * The full path of the data object. The full path always starts with the 117 * root, a slash. The path cannot be changed. Also, a path must be ended with a 118 * slash. For example, /arrays/ints/ 119 */ 120 private String path; 121 122 /** The full name of the data object, i.e. "path + name" */ 123 private String fullName; 124 125 /** 126 * Array of long integer storing unique identifier for the object. 127 * 128 * HDF4 objects are uniquely identified by a (tag_id, ref_id) pair. i.e. 129 * oid[0] = tag, oid[1] = ref_id.<br> 130 * HDF5 objects are uniquely identified by a reference. i.e. 131 * oid[0...7] = H5R_ref_t. 132 */ 133 protected long[] oid; 134 135 /** 136 * The name of the Target Object that is being linked to. 137 */ 138 protected String linkTargetObjName; 139 140 /** 141 * Number of attributes attached to the object. 142 */ 143 // protected int nAttributes = -1; 144 145 /** 146 * Constructs an instance of a data object without name and path. 147 */ 148 public HObject() { 149 this(null, null, null, null); 150 } 151 152 /** 153 * Constructs an instance of a data object with specific name and path. 154 * 155 * For example, in H5ScalarDS(h5file, "dset", "/arrays"), "dset" is the name 156 * of the dataset, "/arrays" is the group path of the dataset. 157 * 158 * @param theFile 159 * the file that contains the data object. 160 * @param theName 161 * the name of the data object, e.g. "dset". 162 * @param thePath 163 * the group path of the data object, e.g. "/arrays". 164 */ 165 public HObject(FileFormat theFile, String theName, String thePath) { 166 this(theFile, theName, thePath, null); 167 } 168 169 /** 170 * Constructs an instance of a data object with specific name and path. 171 * 172 * For example, in H5ScalarDS(h5file, "dset", "/arrays"), "dset" is the name 173 * of the dataset, "/arrays" is the group path of the dataset. 174 * 175 * @param theFile 176 * the file that contains the data object. 177 * @param theName 178 * the name of the data object, e.g. "dset". 179 * @param thePath 180 * the group path of the data object, e.g. "/arrays". 181 * @param oid 182 * the ids of the data object. 183 */ 184 @Deprecated 185 public HObject(FileFormat theFile, String theName, String thePath, long[] oid) { 186 this.fileFormat = theFile; 187 this.oid = oid; 188 189 if (fileFormat != null) 190 this.filename = fileFormat.getFilePath(); 191 else 192 this.filename = null; 193 194 try { 195 setFullname(thePath, theName); 196 } 197 catch (Exception e) { 198 log.debug("setFullname failed", e.getMessage()); 199 } 200 log.trace("Fullname={} oid={}", this.fullName, this.oid); 201 } 202 203 /** 204 * Print out debug information 205 * 206 * @param msg 207 * the debug message to print 208 */ 209 protected final void debug(Object msg) { 210 System.out.println("*** " + this.getClass().getName() + ": " + msg); 211 } 212 213 /** 214 * Returns the name of the file that contains this data object. 215 * 216 * The file name is necessary because the file of this data object is 217 * uniquely identified when multiple files are opened by an application at 218 * the same time. 219 * 220 * @return The full path (path + name) of the file. 221 */ 222 public final String getFile() { 223 return filename; 224 } 225 226 /** 227 * Returns the name of the object. For example, "Raster Image #2". 228 * 229 * @return The name of the object. 230 */ 231 public final String getName() { 232 return name; 233 } 234 235 /** 236 * Returns the name of the target object that is linked to. 237 * 238 * @return The name of the object that is linked to. 239 */ 240 public final String getLinkTargetObjName() { 241 return linkTargetObjName; 242 } 243 244 /** 245 * Sets the name of the target object that is linked to. 246 * 247 * @param targetObjName 248 * The new name of the object. 249 */ 250 public final void setLinkTargetObjName(String targetObjName) { 251 linkTargetObjName = targetObjName; 252 } 253 254 /** 255 * Returns the full name (group path + object name) of the object. For 256 * example, "/Images/Raster Image #2" 257 * 258 * @return The full name (group path + object name) of the object. 259 */ 260 public final String getFullName() { 261 return fullName; 262 } 263 264 /** 265 * Returns the group path of the object. For example, "/Images". 266 * 267 * @return The group path of the object. 268 */ 269 public final String getPath() { 270 return path; 271 } 272 273 /** 274 * Sets the name of the object. 275 * 276 * setName (String newName) changes the name of the object in the file. 277 * 278 * @param newName 279 * The new name of the object. 280 * 281 * @throws Exception if name is root or contains separator 282 */ 283 public void setName(String newName) throws Exception { 284 if (newName == null) 285 throw new IllegalArgumentException("The new name is NULL"); 286 287 if (newName.equals(HObject.SEPARATOR)) 288 throw new IllegalArgumentException("The new name cannot be the root"); 289 290 if (newName.startsWith(HObject.SEPARATOR)) 291 newName = newName.substring(1); 292 293 if (newName.endsWith(HObject.SEPARATOR)) 294 newName = newName.substring(0, newName.length() - 2); 295 296 if (newName.contains(HObject.SEPARATOR)) 297 throw new IllegalArgumentException("The new name contains the SEPARATOR character: " + HObject.SEPARATOR); 298 299 name = newName; 300 } 301 302 /** 303 * Sets the path of the object. 304 * 305 * setPath() is needed to change the path for an object when the name of a 306 * group containing the object is changed by setName(). The path of the 307 * object in memory under this group should be updated to the new path to 308 * the group. Unlike setName(), setPath() does not change anything in file. 309 * 310 * @param newPath 311 * The new path of the object. 312 * 313 * @throws Exception if a failure occurred 314 */ 315 public void setPath(String newPath) throws Exception { 316 if (newPath == null) 317 newPath = "/"; 318 319 path = newPath; 320 } 321 322 /** 323 * Sets the full name of the object. 324 * 325 * @param thePath 326 * The path of the object. 327 * @param theName 328 * The name of the object. 329 * 330 * @throws Exception if a failure occurred 331 */ 332 public void setFullname(String thePath, String theName) throws Exception { 333 // file name is packed in the full path 334 if ((theName == null) && (thePath != null)) { 335 if (thePath.equals(SEPARATOR)) { 336 theName = SEPARATOR; 337 thePath = null; 338 } 339 else { 340 // the path must starts with "/" 341 if (!thePath.startsWith(HObject.SEPARATOR)) 342 thePath = HObject.SEPARATOR + thePath; 343 344 // get rid of the last "/" 345 if (thePath.endsWith(HObject.SEPARATOR)) 346 thePath = thePath.substring(0, thePath.length() - 1); 347 348 // separate the name and the path 349 theName = thePath.substring(thePath.lastIndexOf(SEPARATOR) + 1); 350 thePath = thePath.substring(0, thePath.lastIndexOf(SEPARATOR)); 351 } 352 } 353 else if ((theName != null) && (thePath == null) && (theName.indexOf(SEPARATOR) >= 0)) { 354 if (theName.equals(SEPARATOR)) { 355 theName = SEPARATOR; 356 thePath = null; 357 } 358 else { 359 // the full name must starts with "/" 360 if (!theName.startsWith(SEPARATOR)) 361 theName = SEPARATOR + theName; 362 363 // the fullname must not end with "/" 364 int n = theName.length(); 365 if (theName.endsWith(SEPARATOR)) 366 theName = theName.substring(0, n - 1); 367 368 int idx = theName.lastIndexOf(SEPARATOR); 369 if (idx < 0) 370 thePath = SEPARATOR; 371 else { 372 thePath = theName.substring(0, idx); 373 theName = theName.substring(idx + 1); 374 } 375 } 376 } 377 378 // the path must start and end with "/" 379 if (thePath != null) { 380 thePath = thePath.replaceAll("//", "/"); 381 if (!thePath.endsWith(SEPARATOR)) 382 thePath += SEPARATOR; 383 } 384 385 this.name = theName; 386 this.path = thePath; 387 388 this.fullName = createFullname(thePath, theName); 389 } 390 391 /** 392 * Creates the full name of the object. 393 * 394 * @param thePath 395 * The path of the object. 396 * @param theName 397 * The name of the object. 398 * 399 * @return the full name of the object. 400 */ 401 public String createFullname(String thePath, String theName) { 402 String theFullName; 403 404 if (thePath != null) { 405 theFullName = thePath + theName; 406 } 407 else { 408 if (theName == null) 409 theFullName = "/"; 410 else if (theName.startsWith("/")) 411 theFullName = theName; 412 else { 413 if (this instanceof Attribute) 414 theFullName = theName; 415 else 416 theFullName = "/" + theName; 417 } 418 } 419 420 return theFullName; 421 } 422 423 /** 424 * Opens an existing object such as a dataset or group for access. 425 * 426 * The return value is an object identifier obtained by implementing classes 427 * such as H5.H5Dopen(). This function is needed to allow other objects to 428 * be able to access the object. For instance, H5File class uses the open() 429 * function to obtain object identifier for copyAttributes(long src_id, long 430 * dst_id) and other purposes. The open() function should be used in pair 431 * with close(long) function. 432 * 433 * @see hdf.object.HObject#close(long) 434 * 435 * @return the object identifier if successful; otherwise returns a negative 436 * value. 437 */ 438 public abstract long open(); 439 440 /** 441 * Closes access to the object. 442 * 443 * Sub-classes must implement this interface because different data objects 444 * have their own ways of how the data resources are closed. 445 * 446 * For example, H5Group.close() calls the hdf.hdf5lib.H5.H5Gclose() 447 * method and closes the group resource specified by the group id. 448 * 449 * @param id 450 * The object identifier. 451 */ 452 public abstract void close(long id); 453 454 /** 455 * Returns the file identifier of of the file containing the object. 456 * 457 * @return the file identifier of of the file containing the object. 458 */ 459 public final long getFID() { 460 if (fileFormat != null) 461 return fileFormat.getFID(); 462 else 463 return -1; 464 } 465 466 /** 467 * Returns the file that contains the object. 468 * 469 * @return The file that contains the object. 470 */ 471 public final FileFormat getFileFormat() { 472 return fileFormat; 473 } 474 475 /** 476 * Returns a cloned copy of the object identifier. 477 * 478 * The object OID cannot be modified once it is created. getOID() clones the object OID to ensure 479 * the object OID cannot be modified outside of this class. 480 * 481 * @return the cloned copy of the object OID. 482 */ 483 public final long[] getOID() { 484 if (oid == null) { 485 return null; 486 } 487 488 return oid.clone(); 489 } 490 491 /** 492 * Checks if the OID of the object is the same as the given object identifier within the same file. 493 * 494 * HDF4 and HDF5 data objects are identified by their unique OIDs. A data object in a file may have 495 * multiple logical names , which are represented in a graph structure as separate objects. 496 * 497 * The HObject.equalsOID(long[] theID) can be used to check if two data objects with different names 498 * are pointed to the same object within the same file. 499 * 500 * @param theID 501 * The list object identifiers. 502 * 503 * @return true if the ID of the object equals the given OID; otherwise, returns false. 504 */ 505 public final boolean equalsOID(long[] theID) { 506 if ((theID == null) || (oid == null)) 507 return false; 508 509 int n1 = theID.length; 510 int n2 = oid.length; 511 512 if (n1 == 0 || n2 == 0) 513 return false; 514 515 int n = Math.min(n1, n2); 516 boolean isMatched = (theID[0] == oid[0]); 517 518 for (int i = 1; isMatched && (i < n); i++) 519 isMatched = (theID[i] == oid[i]); 520 521 return isMatched; 522 } 523 524 /** 525 * Returns the name of the object. 526 * 527 * This method overwrites the toString() method in the Java Object class 528 * (the root class of all Java objects) so that it returns the name of the 529 * HObject instead of the name of the class. 530 * 531 * For example, toString() returns "Raster Image #2" instead of 532 * "hdf.object.h4.H4SDS". 533 * 534 * @return The name of the object. 535 */ 536 @Override 537 public String toString() { 538 if (this instanceof Group) 539 if (((Group) this).isRoot() && this.getFileFormat() != null) return this.getFileFormat().getName(); 540 541 if (name != null) return name; 542 543 return super.toString(); 544 } 545 546 /** 547 * Returns whether this HObject is equal to the specified HObject by comparing their OIDs. 548 * 549 * @param obj 550 * The object 551 * 552 * @return true if the object is equal by OID 553 */ 554 public boolean equals(HObject obj) { 555 // Cast down to Object to avoid infinite recursion 556 if (this.equals((Object) obj)) 557 return true; 558 559 // comparing the state of OID with 560 // the state of 'this' OID. 561 return this.equalsOID(obj.getOID()); 562 } 563 564 @Override 565 public boolean equals(Object obj) { 566 if (obj == null) 567 return false; 568 569 // checking if both the object references are 570 // referring to the same object. 571 if (this == obj) 572 return true; 573 574 return false; 575 } 576 577 @Override 578 public int hashCode() { 579 // We are returning the OID as a hashcode value. 580 return (int) oid[0]; 581 } 582}