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