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