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 COPYING file, which can be found * 009 * at the root of the source code distribution tree, * 010 * or in https://www.hdfgroup.org/licenses. * 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.nc2; 016 017import java.lang.reflect.Array; 018import java.math.BigInteger; 019import java.util.Arrays; 020import java.util.Collection; 021import java.util.HashMap; 022import java.util.List; 023import java.util.Map; 024 025import org.slf4j.Logger; 026import org.slf4j.LoggerFactory; 027 028import hdf.object.Attribute; 029import hdf.object.DataFormat; 030import hdf.object.Dataset; 031import hdf.object.Datatype; 032import hdf.object.FileFormat; 033import hdf.object.Group; 034import hdf.object.HObject; 035import hdf.object.ScalarDS; 036 037/** 038 * An attribute is a (name, value) pair of metadata attached to a primary data object such as a dataset, group or named 039 * datatype. 040 * 041 * Like a dataset, an attribute has a name, datatype and dataspace. 042 * 043 * For more details on attributes, <a href="https://hdfgroup.github.io/hdf5/_h5_a__u_g.html#sec_attribute">HDF5 044 * Attributes in HDF5 User Guide</a> 045 * 046 * The following code is an example of an attribute with 1D integer array of two elements. 047 * 048 * <pre> 049 * // Example of creating a new attribute 050 * // The name of the new attribute 051 * String name = "Data range"; 052 * // Creating an unsigned 1-byte integer datatype 053 * Datatype type = new Datatype(Datatype.CLASS_INTEGER, // class 054 * 1, // size in bytes 055 * Datatype.ORDER_LE, // byte order 056 * Datatype.SIGN_NONE); // unsigned 057 * // 1-D array of size two 058 * long[] dims = {2}; 059 * // The value of the attribute 060 * int[] value = {0, 255}; 061 * // Create a new attribute 062 * Attribute dataRange = new Attribute(name, type, dims); 063 * // Set the attribute value 064 * dataRange.setValue(value); 065 * // See FileFormat.writeAttribute() for how to attach an attribute to an object, 066 * @see hdf.object.FileFormat#writeAttribute(HObject, Attribute, boolean) 067 * </pre> 068 * 069 * 070 * For an atomic datatype, the value of an Attribute will be a 1D array of integers, floats and strings. For a compound 071 * datatype, it will be a 1D array of strings with field members separated by a comma. For example, "{0, 10.5}, {255, 072 * 20.0}, {512, 30.0}" is a compound attribute of {int, float} of three data points. 073 * 074 * @see hdf.object.Datatype 075 * 076 * @version 2.0 4/2/2018 077 * @author Peter X. Cao, Jordan T. Henderson 078 */ 079public class NC2Attribute extends ScalarDS implements Attribute 080{ 081 private static final long serialVersionUID = 2072473407027648309L; 082 083 private static final Logger log = LoggerFactory.getLogger(NC2Attribute.class); 084 085 /** The HObject to which this NC2Attribute is attached, Attribute interface */ 086 protected HObject parentObject; 087 088 /** additional information and properties for the attribute, Attribute interface */ 089 private transient Map<String, Object> properties; 090 091 /** 092 * Create an attribute with specified name, data type and dimension sizes. 093 * 094 * For scalar attribute, the dimension size can be either an array of size one 095 * or null, and the rank can be either 1 or zero. Attribute is a general class 096 * and is independent of file format, e.g., the implementation of attribute 097 * applies to both HDF4 and HDF5. 098 * 099 * The following example creates a string attribute with the name "CLASS" and 100 * value "IMAGE". 101 * 102 * <pre> 103 * long[] attrDims = { 1 }; 104 * String attrName = "CLASS"; 105 * String[] classValue = { "IMAGE" }; 106 * Datatype attrType = null; 107 * try { 108 * attrType = new NC2Datatype(Datatype.CLASS_STRING, classValue[0].length() + 1, Datatype.NATIVE, Datatype.NATIVE); 109 * } 110 * catch (Exception ex) {} 111 * Attribute attr = new Attribute(attrName, attrType, attrDims); 112 * attr.setValue(classValue); 113 * </pre> 114 * 115 * @param parentObj 116 * the HObject to which this Attribute is attached. 117 * @param attrName 118 * the name of the attribute. 119 * @param attrType 120 * the datatype of the attribute. 121 * @param attrDims 122 * the dimension sizes of the attribute, null for scalar attribute 123 * 124 * @see hdf.object.Datatype 125 */ 126 public NC2Attribute(HObject parentObj, String attrName, Datatype attrType, long[] attrDims) { 127 this(parentObj, attrName, attrType, attrDims, null); 128 } 129 130 /** 131 * Create an attribute with specific name and value. 132 * 133 * For scalar attribute, the dimension size can be either an array of size one 134 * or null, and the rank can be either 1 or zero. Attribute is a general class 135 * and is independent of file format, e.g., the implementation of attribute 136 * applies to both HDF4 and HDF5. 137 * 138 * The following example creates a string attribute with the name "CLASS" and 139 * value "IMAGE". 140 * 141 * <pre> 142 * long[] attrDims = { 1 }; 143 * String attrName = "CLASS"; 144 * String[] classValue = { "IMAGE" }; 145 * Datatype attrType = null; 146 * try { 147 * attrType = new NC2Datatype(Datatype.CLASS_STRING, classValue[0].length() + 1, Datatype.NATIVE, Datatype.NATIVE); 148 * } 149 * catch (Exception ex) {} 150 * NC2Attribute attr = new NC2Attribute(attrName, attrType, attrDims, classValue); 151 * </pre> 152 * 153 * @param parentObj 154 * the HObject to which this Attribute is attached. 155 * @param attrName 156 * the name of the attribute. 157 * @param attrType 158 * the datatype of the attribute. 159 * @param attrDims 160 * the dimension sizes of the attribute, null for scalar attribute 161 * @param attrValue 162 * the value of the attribute, null if no value 163 * 164 * @see hdf.object.Datatype 165 */ 166 @SuppressWarnings({ "rawtypes", "unchecked", "deprecation" }) 167 public NC2Attribute(HObject parentObj, String attrName, Datatype attrType, long[] attrDims, Object attrValue) { 168 super((parentObj == null) ? null : parentObj.getFileFormat(), attrName, 169 (parentObj == null) ? null : parentObj.getFullName(), null); 170 171 log.trace("NC2Attribute: start {}", parentObj); 172 this.parentObject = parentObj; 173 174 unsignedConverted = false; 175 176 datatype = attrType; 177 178 if (attrValue != null) { 179 data = attrValue; 180 originalBuf = attrValue; 181 isDataLoaded = true; 182 } 183 properties = new HashMap(); 184 185 if (attrDims == null) { 186 rank = 1; 187 dims = new long[] { 1 }; 188 } 189 else { 190 dims = attrDims; 191 rank = dims.length; 192 } 193 194 selectedDims = new long[rank]; 195 startDims = new long[rank]; 196 selectedStride = new long[rank]; 197 198 log.trace("attrName={}, attrType={}, attrValue={}, rank={}, isUnsigned={}", 199 attrName, getDatatype().getDescription(), data, rank, getDatatype().isUnsigned()); 200 201 resetSelection(); 202 } 203 204 /* 205 * (non-Javadoc) 206 * 207 * @see hdf.object.HObject#open() 208 */ 209 @Override 210 public long open() { 211 long aid = -1; 212 long pObjID = -1; 213 214 if (parentObject == null) { 215 log.debug("open(): attribute's parent object is null"); 216 return -1; 217 } 218 219 try { 220 pObjID = parentObject.open(); 221 if (pObjID >= 0) { 222 if (this.getFileFormat().isThisType(FileFormat.getFileFormat(FileFormat.FILE_TYPE_NC3))) { 223 log.trace("open(): FILE_TYPE_NC3"); 224 /* 225 * TODO: Get type of netcdf3 object this is attached to and retrieve attribute info. 226 */ 227 } 228 } 229 230 log.trace("open(): aid={}", aid); 231 } 232 catch (Exception ex) { 233 log.debug("open(): Failed to open attribute {}: ", getName(), ex); 234 aid = -1; 235 } 236 finally { 237 parentObject.close(pObjID); 238 } 239 240 return aid; 241 } 242 243 /* 244 * (non-Javadoc) 245 * 246 * @see hdf.object.HObject#close(int) 247 */ 248 @Override 249 public void close(long aid) { 250 if (aid >= 0) { 251 if (this.getFileFormat().isThisType(FileFormat.getFileFormat(FileFormat.FILE_TYPE_NC3))) { 252 log.trace("close(): FILE_TYPE_NC3"); 253 /* 254 * TODO: Get type of netcdf3 object this is attached to and close attribute. 255 */ 256 } 257 } 258 } 259 260 @Override 261 public void init() { 262 if (inited) { 263 resetSelection(); 264 log.trace("init(): NC2Attribute already inited"); 265 return; 266 } 267 268 if (this.getFileFormat().isThisType(FileFormat.getFileFormat(FileFormat.FILE_TYPE_NC3))) { 269 log.trace("init(): FILE_TYPE_NC3"); 270 /* 271 * TODO: If netcdf3 attribute object needs to init dependent objects. 272 */ 273 inited = true; 274 } 275 276 resetSelection(); 277 } 278 279 /** 280 * Reads the data from file. 281 * 282 * read() reads the data from file to a memory buffer and returns the memory 283 * buffer. The dataset object does not hold the memory buffer. To store the 284 * memory buffer in the dataset object, one must call getData(). 285 * 286 * By default, the whole dataset is read into memory. Users can also select 287 * a subset to read. Subsetting is done in an implicit way. 288 * 289 * @return the data read from file. 290 * 291 * @see #getData() 292 * 293 * @throws Exception 294 * if object can not be read 295 * @throws OutOfMemoryError 296 * if memory is exhausted 297 */ 298 @Override 299 public Object read() throws Exception, OutOfMemoryError { 300 if (!inited) 301 init(); 302 303 return data; 304 } 305 306 /* Implement abstract Dataset */ 307 308 /** 309 * Writes a memory buffer to the object in the file. 310 * 311 * @param buf 312 * the data to write 313 * 314 * @throws Exception 315 * if data can not be written 316 */ 317 @Override 318 public void write(Object buf) throws Exception { 319 log.trace("function of dataset: write(Object) start"); 320 if (!buf.equals(data)) 321 setData(buf); 322 323 init(); 324 325 if (parentObject == null) { 326 log.debug("write(Object): parent object is null; nowhere to write attribute to"); 327 return; 328 } 329 } 330 331 /* 332 * (non-Javadoc) 333 * @see hdf.object.Dataset#copy(hdf.object.Group, java.lang.String, long[], java.lang.Object) 334 */ 335 @Override 336 public Dataset copy(Group pgroup, String dstName, long[] dims, Object buff) throws Exception { 337 // not supported 338 throw new UnsupportedOperationException("copy operation unsupported for NC2."); 339 } 340 341 /* 342 * (non-Javadoc) 343 * @see hdf.object.Dataset#readBytes() 344 */ 345 @Override 346 public byte[] readBytes() throws Exception { 347 // not supported 348 throw new UnsupportedOperationException("readBytes operation unsupported for NC2."); 349 } 350 351 /* Implement interface Attribute */ 352 353 /** 354 * Returns the HObject to which this Attribute is currently "attached". 355 * 356 * @return the HObject to which this Attribute is currently "attached". 357 */ 358 public HObject getParentObject() { 359 return parentObject; 360 } 361 362 /** 363 * Sets the HObject to which this Attribute is "attached". 364 * 365 * @param pObj 366 * the new HObject to which this Attribute is "attached". 367 */ 368 public void setParentObject(HObject pObj) { 369 parentObject = pObj; 370 } 371 372 /** 373 * set a property for the attribute. 374 * 375 * @param key the attribute Map key 376 * @param value the attribute Map value 377 */ 378 public void setProperty(String key, Object value) { 379 properties.put(key, value); 380 } 381 382 /** 383 * get a property for a given key. 384 * 385 * @param key the attribute Map key 386 * 387 * @return the property 388 */ 389 public Object getProperty(String key) { 390 return properties.get(key); 391 } 392 393 /** 394 * get all property keys. 395 * 396 * @return the Collection of property keys 397 */ 398 public Collection<String> getPropertyKeys() { 399 return properties.keySet(); 400 } 401 402 /** 403 * Returns the name of the object. For example, "Raster Image #2". 404 * 405 * @return The name of the object. 406 */ 407 public final String getAttributeName() { 408 return getName(); 409 } 410 411 /** 412 * Retrieves the attribute data from the file. 413 * 414 * @return the attribute data. 415 * 416 * @throws Exception 417 * if the data can not be retrieved 418 */ 419 public final Object getAttributeData() throws Exception, OutOfMemoryError { 420 return getData(); 421 } 422 423 /** 424 * Returns the datatype of the attribute. 425 * 426 * @return the datatype of the attribute. 427 */ 428 public final Datatype getAttributeDatatype() { 429 return getDatatype(); 430 } 431 432 /** 433 * Returns the space type for the attribute. It returns a 434 * negative number if it failed to retrieve the type information from 435 * the file. 436 * 437 * @return the space type for the attribute. 438 */ 439 public final int getAttributeSpaceType() { 440 return getSpaceType(); 441 } 442 443 /** 444 * Returns the rank (number of dimensions) of the attribute. It returns a 445 * negative number if it failed to retrieve the dimension information from 446 * the file. 447 * 448 * @return the number of dimensions of the attribute. 449 */ 450 public final int getAttributeRank() { 451 return getRank(); 452 } 453 454 /** 455 * Returns the selected size of the rows and columns of the attribute. It returns a 456 * negative number if it failed to retrieve the size information from 457 * the file. 458 * 459 * @return the selected size of the rows and colums of the attribute. 460 */ 461 public final int getAttributePlane() { 462 return (int)getWidth() * (int)getHeight(); 463 } 464 465 /** 466 * Returns the array that contains the dimension sizes of the data value of 467 * the attribute. It returns null if it failed to retrieve the dimension 468 * information from the file. 469 * 470 * @return the dimension sizes of the attribute. 471 */ 472 public final long[] getAttributeDims() { 473 return getDims(); 474 } 475 476 /** 477 * @return true if the dataspace is a NULL; otherwise, returns false. 478 */ 479 public boolean isAttributeNULL() { 480 return isNULL(); 481 } 482 483 /** 484 * @return true if the data is a single scalar point; otherwise, returns false. 485 */ 486 public boolean isAttributeScalar() { 487 return isScalar(); 488 } 489 490 /** 491 * Not for public use in the future. 492 * 493 * setData() is not safe to use because it changes memory buffer 494 * of the dataset object. Dataset operations such as write/read 495 * will fail if the buffer type or size is changed. 496 * 497 * @param d the object data -must be an array of Objects 498 */ 499 public void setAttributeData(Object d) { 500 setData(d); 501 } 502 503 /** 504 * Writes the memory buffer of this dataset to file. 505 * 506 * @throws Exception if buffer can not be written 507 */ 508 public void writeAttribute() throws Exception { 509 write(); 510 } 511 512 /** 513 * Writes the given data buffer into this attribute in a file. 514 * 515 * The data buffer is a vector that contains the data values of compound fields. The data is written 516 * into file as one data blob. 517 * 518 * @param buf 519 * The vector that contains the data values of compound fields. 520 * 521 * @throws Exception 522 * If there is an error at the library level. 523 */ 524 public void writeAttribute(Object buf) throws Exception { 525 write(buf); 526 } 527 528 /** 529 * Returns a string representation of the data value. For 530 * example, "0, 255". 531 * 532 * For a compound datatype, it will be a 1D array of strings with field 533 * members separated by the delimiter. For example, 534 * "{0, 10.5}, {255, 20.0}, {512, 30.0}" is a compound attribute of {int, 535 * float} of three data points. 536 * 537 * @param delimiter 538 * The delimiter used to separate individual data points. It 539 * can be a comma, semicolon, tab or space. For example, 540 * toString(",") will separate data by commas. 541 * 542 * @return the string representation of the data values. 543 */ 544 public String toAttributeString(String delimiter) { 545 return toString(delimiter, -1); 546 } 547 548 /** 549 * Returns a string representation of the data value. For 550 * example, "0, 255". 551 * 552 * For a compound datatype, it will be a 1D array of strings with field 553 * members separated by the delimiter. For example, 554 * "{0, 10.5}, {255, 20.0}, {512, 30.0}" is a compound attribute of {int, 555 * float} of three data points. 556 * 557 * @param delimiter 558 * The delimiter used to separate individual data points. It 559 * can be a comma, semicolon, tab or space. For example, 560 * toString(",") will separate data by commas. 561 * @param maxItems 562 * The maximum number of Array values to return 563 * 564 * @return the string representation of the data values. 565 */ 566 public String toAttributeString(String delimiter, int maxItems) { 567 return toString(delimiter, maxItems); 568 } 569}