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 H4SclarDS 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 pathes. 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 (ref_id, tag_id) pair. 054 * The ref_id is the object reference count. tag_id is a pre-defined number to 055 * identify the type of object. For example, DFTAG_RI is for raster image, 056 * DFTAG_SD is for scientific dataset, and DFTAG_VG is for Vgroup. 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 (ref_id, tag_id) pair. i.e. 121 * oid[0]=tag, oid[1]=ref.<br> 122 * HDF5 objects are uniquely identified by an object reference. 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 * @deprecated Not for public use in the future.<br> 162 * Using {@link #HObject(FileFormat, String, String)} 163 */ 164 @Deprecated 165 public HObject(FileFormat theFile, String theName, String thePath, long[] oid) { 166 this.fileFormat = theFile; 167 this.oid = oid; 168 169 if (fileFormat != null) { 170 this.filename = fileFormat.getFilePath(); 171 } 172 else { 173 this.filename = null; 174 } 175 176 // file name is packed in the full path 177 if ((theName == null) && (thePath != null)) { 178 if (thePath.equals(separator)) { 179 theName = separator; 180 thePath = null; 181 } 182 else { 183 // the path must starts with "/" 184 if (!thePath.startsWith(HObject.separator)) { 185 thePath = HObject.separator + thePath; 186 } 187 188 // get rid of the last "/" 189 if (thePath.endsWith(HObject.separator)) { 190 thePath = thePath.substring(0, thePath.length() - 1); 191 } 192 193 // seperate the name and the path 194 theName = thePath.substring(thePath.lastIndexOf(separator) + 1); 195 thePath = thePath.substring(0, thePath.lastIndexOf(separator)); 196 } 197 } 198 else if ((theName != null) && (thePath == null) && (theName.indexOf(separator) >= 0)) { 199 if (theName.equals(separator)) { 200 theName = separator; 201 thePath = null; 202 } 203 else { 204 // the full name must starts with "/" 205 if (!theName.startsWith(separator)) { 206 theName = separator + theName; 207 } 208 209 // the fullname must not end with "/" 210 int n = theName.length(); 211 if (theName.endsWith(separator)) { 212 theName = theName.substring(0, n - 1); 213 } 214 215 int idx = theName.lastIndexOf(separator); 216 if (idx < 0) { 217 thePath = separator; 218 } 219 else { 220 thePath = theName.substring(0, idx); 221 theName = theName.substring(idx + 1); 222 } 223 } 224 } 225 226 // the path must start and end with "/" 227 if (thePath != null) { 228 thePath = thePath.replaceAll("//", "/"); 229 if (!thePath.endsWith(separator)) { 230 thePath += separator; 231 } 232 } 233 234 this.name = theName; 235 this.path = thePath; 236 237 if (thePath != null) { 238 this.fullName = thePath + theName; 239 } 240 else { 241 if (theName == null) { 242 this.fullName = "/"; 243 } 244 else if (theName.startsWith("/")) { 245 this.fullName = theName; 246 } 247 else { 248 this.fullName = "/" + theName; 249 } 250 } 251 } 252 253 /** 254 * Print out debug information 255 * <p> 256 * 257 * @param msg 258 * the debug message to print 259 */ 260 protected final void debug(Object msg) { 261 System.out.println("*** " + this.getClass().getName() + ": " + msg); 262 } 263 264 /** 265 * Returns the name of the file that contains this data object. 266 * <p> 267 * The file name is necessary because the file of this data object is 268 * uniquely identified when multiple files are opened by an application at 269 * the same time. 270 * 271 * @return The full path (path + name) of the file. 272 */ 273 public final String getFile() { 274 return filename; 275 } 276 277 /** 278 * Returns the name of the object. For example, "Raster Image #2". 279 * 280 * @return The name of the object. 281 */ 282 public final String getName() { 283 return name; 284 } 285 286 /** 287 * Returns the name of the target object that is linked to. 288 * 289 * @return The name of the object that is linked to. 290 */ 291 public final String getLinkTargetObjName() { 292 return linkTargetObjName; 293 } 294 295 /** 296 * Sets the name of the target object that is linked to. 297 */ 298 public final void setLinkTargetObjName(String targetObjName) { 299 linkTargetObjName = targetObjName; 300 } 301 302 /** 303 * Returns the full name (group path + object name) of the object. For 304 * example, "/Images/Raster Image #2" 305 * 306 * @return The full name (group path + object name) of the object. 307 */ 308 public final String getFullName() { 309 return fullName; 310 } 311 312 /** 313 * Returns the group path of the object. For example, "/Images". 314 * 315 * @return The group path of the object. 316 */ 317 public final String getPath() { 318 return path; 319 } 320 321 /** 322 * Sets the name of the object. 323 * <p> 324 * setName (String newName) changes the name of the object in the file. 325 * 326 * @param newName 327 * The new name of the object. 328 */ 329 public void setName(String newName) throws Exception { 330 if (newName != null) { 331 if (newName.equals(HObject.separator)) { 332 throw new IllegalArgumentException("The new name cannot be the root"); 333 } 334 335 if (newName.startsWith(HObject.separator)) { 336 newName = newName.substring(1); 337 } 338 339 if (newName.endsWith(HObject.separator)) { 340 newName = newName.substring(0, newName.length() - 2); 341 } 342 343 if (newName.contains(HObject.separator)) { 344 throw new IllegalArgumentException("The new name contains the separator character: " 345 + HObject.separator); 346 } 347 } 348 349 name = newName; 350 } 351 352 /** 353 * Sets the path of the object. 354 * <p> 355 * setPath() is needed to change the path for an object when the name of a 356 * group conatining the object is changed by setName(). The path of the 357 * object in memory under this group should be updated to the new path to 358 * the group. Unlike setName(), setPath() does not change anything in file. 359 * 360 * @param newPath 361 * The new path of the object. 362 */ 363 public void setPath(String newPath) throws Exception { 364 if (newPath == null) { 365 newPath = "/"; 366 } 367 368 path = newPath; 369 } 370 371 /** 372 * Opens an existing object such as dataset or group for access. 373 * 374 * The return value is an object identifier obtained by implementing classes 375 * such as H5.H5Dopen(). This function is needed to allow other objects to 376 * be able to access the object. For instance, H5File class uses the open() 377 * function to obtain object identifier for copyAttributes(int src_id, int 378 * dst_id) and other purposes. The open() function should be used in pair 379 * with close(int) function. 380 * 381 * @see hdf.object.HObject#close(int) 382 * 383 * @return the object identifier if successful; otherwise returns a negative 384 * value. 385 */ 386 public abstract int open(); 387 388 /** 389 * Closes access to the object. 390 * <p> 391 * Sub-classes must implement this interface because different data objects 392 * have their own ways of how the data resources are closed. 393 * <p> 394 * For example, H5Group.close() calls the hdf.hdf5lib.H5.H5Gclose() 395 * method and closes the group resource specified by the group id. 396 * 397 * @param id 398 * The object identifier. 399 */ 400 public abstract void close(int id); 401 402 /** 403 * Returns the file identifier of of the file containing the object. 404 * 405 * @return the file identifier of of the file containing the object. 406 */ 407 public final int getFID() { 408 if (fileFormat != null) { 409 return fileFormat.getFID(); 410 } 411 else { 412 return -1; 413 } 414 } 415 416 /** 417 * Checks if the OID of the object is the same as the given object 418 * identifier within the same file. 419 * <p> 420 * HDF4 and HDF5 data objects are identified by their unique OIDs. A data 421 * object in a file may have multiple logical names , which are represented 422 * in a graph structure as separate objects. 423 * <p> 424 * The HObject.equalsOID(long[] theID) can be used to check if two data 425 * objects with different names are pointed to the same object within the 426 * same file. 427 * 428 * @return true if the ID of the object equals the given OID; otherwise, 429 * returns false. 430 */ 431 public final boolean equalsOID(long[] theID) { 432 if ((theID == null) || (oid == null)) { 433 return false; 434 } 435 436 int n1 = theID.length; 437 int n2 = oid.length; 438 439 if (n1 == 0 || n2 == 0) { 440 return false; 441 } 442 443 int n = Math.min(n1, n2); 444 boolean isMatched = (theID[0] == oid[0]); 445 446 for (int i = 1; isMatched && (i < n); i++) { 447 isMatched = (theID[i] == oid[i]); 448 } 449 450 return isMatched; 451 } 452 453 /** 454 * Returns the file that contains the object. 455 * 456 * @return The file that contains the object. 457 */ 458 public final FileFormat getFileFormat() { 459 return fileFormat; 460 } 461 462 /** 463 * Returns a cloned copy of the object identifier. 464 * <p> 465 * The object OID cannot be modified once it is created. getIOD() clones the 466 * object OID to ensure the object OID cannot be modified outside of this 467 * class. 468 * 469 * @return the cloned copy of the object OID. 470 */ 471 public final long[] getOID() { 472 if (oid == null) { 473 return null; 474 } 475 476 return oid.clone(); 477 } 478 479 /** 480 * Returns the name of the object. 481 * <p> 482 * This method overwrites the toString() method in the Java Object class 483 * (the root class of all Java objects) so that it returns the name of the 484 * HObject instead of the name of the class. 485 * <p> 486 * For example, toString() returns "Raster Image #2" instead of 487 * "hdf.object.h4.H4SDS". 488 * 489 * @return The name of the object. 490 */ 491 @Override 492 public String toString() { 493 if (this instanceof Group) { 494 if (((Group) this).isRoot() && this.getFileFormat() != null) return this.getFileFormat().getName(); 495 } 496 497 if (name != null) return name; 498 499 return super.toString(); 500 } 501 502}