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(). 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 * 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 * 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 * 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 else 185 this.filename = null; 186 187 try { 188 setFullname(thePath, theName); 189 } 190 catch (Exception e) { 191 log.debug("setFullname failed", e.getMessage()); 192 } 193 } 194 195 /** 196 * Print out debug information 197 * 198 * @param msg 199 * the debug message to print 200 */ 201 protected final void debug(Object msg) { 202 System.out.println("*** " + this.getClass().getName() + ": " + msg); 203 } 204 205 /** 206 * Returns the name of the file that contains this data object. 207 * 208 * The file name is necessary because the file of this data object is 209 * uniquely identified when multiple files are opened by an application at 210 * the same time. 211 * 212 * @return The full path (path + name) of the file. 213 */ 214 public final String getFile() { 215 return filename; 216 } 217 218 /** 219 * Returns the name of the object. For example, "Raster Image #2". 220 * 221 * @return The name of the object. 222 */ 223 public final String getName() { 224 return name; 225 } 226 227 /** 228 * Returns the name of the target object that is linked to. 229 * 230 * @return The name of the object that is linked to. 231 */ 232 public final String getLinkTargetObjName() { 233 return linkTargetObjName; 234 } 235 236 /** 237 * Sets the name of the target object that is linked to. 238 * 239 * @param targetObjName 240 * The new name of the object. 241 */ 242 public final void setLinkTargetObjName(String targetObjName) { 243 linkTargetObjName = targetObjName; 244 } 245 246 /** 247 * Returns the full name (group path + object name) of the object. For 248 * example, "/Images/Raster Image #2" 249 * 250 * @return The full name (group path + object name) of the object. 251 */ 252 public final String getFullName() { 253 return fullName; 254 } 255 256 /** 257 * Returns the group path of the object. For example, "/Images". 258 * 259 * @return The group path of the object. 260 */ 261 public final String getPath() { 262 return path; 263 } 264 265 /** 266 * Sets the name of the object. 267 * 268 * setName (String newName) changes the name of the object in the file. 269 * 270 * @param newName 271 * The new name of the object. 272 * 273 * @throws Exception if name is root or contains separator 274 */ 275 public void setName(String newName) throws Exception { 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: " + HObject.SEPARATOR); 290 291 name = newName; 292 } 293 294 /** 295 * Sets the path of the object. 296 * 297 * setPath() is needed to change the path for an object when the name of a 298 * group containing the object is changed by setName(). The path of the 299 * object in memory under this group should be updated to the new path to 300 * the group. Unlike setName(), setPath() does not change anything in file. 301 * 302 * @param newPath 303 * The new path of the object. 304 * 305 * @throws Exception if a failure occurred 306 */ 307 public void setPath(String newPath) throws Exception { 308 if (newPath == null) 309 newPath = "/"; 310 311 path = newPath; 312 } 313 314 /** 315 * Sets the full name of the object. 316 * 317 * @param thePath 318 * The path of the object. 319 * @param theName 320 * The name of the object. 321 * 322 * @throws Exception if a failure occurred 323 */ 324 public void setFullname(String thePath, String theName) throws Exception { 325 // file name is packed in the full path 326 if ((theName == null) && (thePath != null)) { 327 if (thePath.equals(SEPARATOR)) { 328 theName = SEPARATOR; 329 thePath = null; 330 } 331 else { 332 // the path must starts with "/" 333 if (!thePath.startsWith(HObject.SEPARATOR)) 334 thePath = HObject.SEPARATOR + thePath; 335 336 // get rid of the last "/" 337 if (thePath.endsWith(HObject.SEPARATOR)) 338 thePath = thePath.substring(0, thePath.length() - 1); 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 // the fullname must not end with "/" 356 int n = theName.length(); 357 if (theName.endsWith(SEPARATOR)) 358 theName = theName.substring(0, n - 1); 359 360 int idx = theName.lastIndexOf(SEPARATOR); 361 if (idx < 0) 362 thePath = SEPARATOR; 363 else { 364 thePath = theName.substring(0, idx); 365 theName = theName.substring(idx + 1); 366 } 367 } 368 } 369 370 // the path must start and end with "/" 371 if (thePath != null) { 372 thePath = thePath.replaceAll("//", "/"); 373 if (!thePath.endsWith(SEPARATOR)) 374 thePath += SEPARATOR; 375 } 376 377 this.name = theName; 378 this.path = thePath; 379 380 this.fullName = createFullname(thePath, theName); 381 } 382 383 /** 384 * Creates the full name of the object. 385 * 386 * @param thePath 387 * The path of the object. 388 * @param theName 389 * The name of the object. 390 * 391 * @return the full name of the object. 392 */ 393 public String createFullname(String thePath, String theName) { 394 String theFullName; 395 396 if (thePath != null) { 397 theFullName = thePath + theName; 398 } 399 else { 400 if (theName == null) 401 theFullName = "/"; 402 else if (theName.startsWith("/")) 403 theFullName = theName; 404 else { 405 if (this instanceof Attribute) 406 theFullName = theName; 407 else 408 theFullName = "/" + theName; 409 } 410 } 411 412 return theFullName; 413 } 414 415 /** 416 * Opens an existing object such as a dataset or group for access. 417 * 418 * The return value is an object identifier obtained by implementing classes 419 * such as H5.H5Dopen(). This function is needed to allow other objects to 420 * be able to access the object. For instance, H5File class uses the open() 421 * function to obtain object identifier for copyAttributes(long src_id, long 422 * dst_id) and other purposes. The open() function should be used in pair 423 * with close(long) function. 424 * 425 * @see hdf.object.HObject#close(long) 426 * 427 * @return the object identifier if successful; otherwise returns a negative 428 * value. 429 */ 430 public abstract long open(); 431 432 /** 433 * Closes access to the object. 434 * 435 * Sub-classes must implement this interface because different data objects 436 * have their own ways of how the data resources are closed. 437 * 438 * For example, H5Group.close() calls the hdf.hdf5lib.H5.H5Gclose() 439 * method and closes the group resource specified by the group id. 440 * 441 * @param id 442 * The object identifier. 443 */ 444 public abstract void close(long id); 445 446 /** 447 * Returns the file identifier of of the file containing the object. 448 * 449 * @return the file identifier of of the file containing the object. 450 */ 451 public final long getFID() { 452 if (fileFormat != null) 453 return fileFormat.getFID(); 454 else 455 return -1; 456 } 457 458 /** 459 * Returns the file that contains the object. 460 * 461 * @return The file that contains the object. 462 */ 463 public final FileFormat getFileFormat() { 464 return fileFormat; 465 } 466 467 /** 468 * Returns a cloned copy of the object identifier. 469 * 470 * The object OID cannot be modified once it is created. getOID() clones the object OID to ensure 471 * the object OID cannot be modified outside of this class. 472 * 473 * @return the cloned copy of the object OID. 474 */ 475 public final long[] getOID() { 476 if (oid == null) { 477 return null; 478 } 479 480 return oid.clone(); 481 } 482 483 /** 484 * Checks if the OID of the object is the same as the given object identifier within the same file. 485 * 486 * HDF4 and HDF5 data objects are identified by their unique OIDs. A data object in a file may have 487 * multiple logical names , which are represented in a graph structure as separate objects. 488 * 489 * The HObject.equalsOID(long[] theID) can be used to check if two data objects with different names 490 * are pointed to the same object within the same file. 491 * 492 * @param theID 493 * The list object identifiers. 494 * 495 * @return true if the ID of the object equals the given OID; otherwise, returns false. 496 */ 497 public final boolean equalsOID(long[] theID) { 498 if ((theID == null) || (oid == null)) 499 return false; 500 501 int n1 = theID.length; 502 int n2 = oid.length; 503 504 if (n1 == 0 || n2 == 0) 505 return false; 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 return isMatched; 514 } 515 516 /** 517 * Returns the name of the object. 518 * 519 * This method overwrites the toString() method in the Java Object class 520 * (the root class of all Java objects) so that it returns the name of the 521 * HObject instead of the name of the class. 522 * 523 * For example, toString() returns "Raster Image #2" instead of 524 * "hdf.object.h4.H4SDS". 525 * 526 * @return The name of the object. 527 */ 528 @Override 529 public String toString() { 530 if (this instanceof Group) 531 if (((Group) this).isRoot() && this.getFileFormat() != null) return this.getFileFormat().getName(); 532 533 if (name != null) return name; 534 535 return super.toString(); 536 } 537 538 /** 539 * Returns whether this HObject is equal to the specified HObject by comparing their OIDs. 540 * 541 * @param obj 542 * The object 543 * 544 * @return true if the object is equal by OID 545 */ 546 public boolean equals(HObject obj) { 547 // Cast down to Object to avoid infinite recursion 548 if (this.equals((Object) obj)) 549 return true; 550 551 // comparing the state of OID with 552 // the state of 'this' OID. 553 return this.equalsOID(obj.getOID()); 554 } 555 556 @Override 557 public boolean equals(Object obj) { 558 if (obj == null) 559 return false; 560 561 // checking if both the object references are 562 // referring to the same object. 563 if (this == obj) 564 return true; 565 566 return false; 567 } 568 569 @Override 570 public int hashCode() { 571 // We are returning the OID as a hashcode value. 572 return (int) oid[0]; 573 } 574}