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