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