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