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.h5; 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; 024import java.util.Vector; 025 026import hdf.hdf5lib.H5; 027import hdf.hdf5lib.HDF5Constants; 028import hdf.hdf5lib.HDFNativeData; 029import hdf.hdf5lib.exceptions.HDF5Exception; 030import hdf.object.Attribute; 031import hdf.object.DataFormat; 032import hdf.object.Dataset; 033import hdf.object.Datatype; 034import hdf.object.FileFormat; 035import hdf.object.Group; 036import hdf.object.HObject; 037import hdf.object.MetaDataContainer; 038 039/** 040 * An attribute is a (name, value) pair of metadata attached to a primary data object such as a 041 * dataset, group or named datatype. 042 * <p> 043 * Like a dataset, an attribute has a name, datatype and dataspace. 044 * 045 * <p> 046 * For more details on attributes, <a href= 047 * "https://support.hdfgroup.org/HDF5/doc/UG/HDF5_Users_Guide-Responsive%20HTML5/index.html">HDF5 048 * User's Guide</a> 049 * <p> 050 * 051 * The following code is an example of an attribute with 1D integer array of two elements. 052 * 053 * <pre> 054 * // Example of creating a new attribute 055 * // The name of the new attribute 056 * String name = "Data range"; 057 * // Creating an unsigned 1-byte integer datatype 058 * Datatype type = new Datatype(Datatype.CLASS_INTEGER, // class 059 * 1, // size in bytes 060 * Datatype.ORDER_LE, // byte order 061 * Datatype.SIGN_NONE); // unsigned 062 * // 1-D array of size two 063 * long[] dims = {2}; 064 * // The value of the attribute 065 * int[] value = {0, 255}; 066 * // Create a new attribute 067 * Attribute dataRange = new Attribute(name, type, dims); 068 * // Set the attribute value 069 * dataRange.setValue(value); 070 * // See FileFormat.writeAttribute() for how to attach an attribute to an object, 071 * @see hdf.object.FileFormat#writeAttribute(HObject, Attribute, boolean) 072 * </pre> 073 * 074 * 075 * For an atomic datatype, the value of an Attribute will be a 1D array of integers, floats and 076 * strings. For a compound datatype, it will be a 1D array of strings with field members separated 077 * by a comma. For example, "{0, 10.5}, {255, 20.0}, {512, 30.0}" is a compound attribute of {int, 078 * float} of three data points. 079 * 080 * @see hdf.object.Datatype 081 * 082 * @version 2.0 4/2/2018 083 * @author Peter X. Cao, Jordan T. Henderson 084 */ 085public class H5Attribute extends Attribute { 086 087 private static final long serialVersionUID = 2072473407027648309L; 088 089 private static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(H5Attribute.class); 090 091 092 /** 093 * Create an attribute with specified name, data type and dimension sizes. 094 * 095 * For scalar attribute, the dimension size can be either an array of size one 096 * or null, and the rank can be either 1 or zero. Attribute is a general class 097 * and is independent of file format, e.g., the implementation of attribute 098 * applies to both HDF4 and HDF5. 099 * <p> 100 * The following example creates a string attribute with the name "CLASS" and 101 * value "IMAGE". 102 * 103 * <pre> 104 * long[] attrDims = { 1 }; 105 * String attrName = "CLASS"; 106 * String[] classValue = { "IMAGE" }; 107 * Datatype attrType = null; 108 * try { 109 * attrType = new H5Datatype(Datatype.CLASS_STRING, classValue[0].length() + 1, Datatype.NATIVE, Datatype.NATIVE); 110 * } 111 * catch (Exception ex) {} 112 * Attribute attr = new Attribute(attrName, attrType, attrDims); 113 * attr.setValue(classValue); 114 * </pre> 115 * 116 * @param parentObj 117 * the HObject to which this Attribute is attached. 118 * @param attrName 119 * the name of the attribute. 120 * @param attrType 121 * the datatype of the attribute. 122 * @param attrDims 123 * the dimension sizes of the attribute, null for scalar attribute 124 * 125 * @see hdf.object.Datatype 126 */ 127 public H5Attribute(HObject parentObj, String attrName, Datatype attrType, long[] attrDims) { 128 this(parentObj, attrName, attrType, attrDims, null); 129 } 130 131 /** 132 * Create an attribute with specific name and value. 133 * 134 * For scalar attribute, the dimension size can be either an array of size one 135 * or null, and the rank can be either 1 or zero. Attribute is a general class 136 * and is independent of file format, e.g., the implementation of attribute 137 * applies to both HDF4 and HDF5. 138 * <p> 139 * The following example creates a string attribute with the name "CLASS" and 140 * value "IMAGE". 141 * 142 * <pre> 143 * long[] attrDims = { 1 }; 144 * String attrName = "CLASS"; 145 * String[] classValue = { "IMAGE" }; 146 * Datatype attrType = null; 147 * try { 148 * attrType = new H5Datatype(Datatype.CLASS_STRING, classValue[0].length() + 1, Datatype.NATIVE, Datatype.NATIVE); 149 * } 150 * catch (Exception ex) {} 151 * Attribute attr = new Attribute(attrName, attrType, attrDims, classValue); 152 * </pre> 153 * 154 * @param parentObj 155 * the HObject to which this Attribute is attached. 156 * @param attrName 157 * the name of the attribute. 158 * @param attrType 159 * the datatype of the attribute. 160 * @param attrDims 161 * the dimension sizes of the attribute, null for scalar attribute 162 * @param attrValue 163 * the value of the attribute, null if no value 164 * 165 * @see hdf.object.Datatype 166 */ 167 @SuppressWarnings({ "rawtypes", "unchecked", "deprecation" }) 168 public H5Attribute(HObject parentObj, String attrName, Datatype attrType, long[] attrDims, Object attrValue) { 169 super(parentObj, attrName, attrType, null); 170 } 171 172 /* 173 * (non-Javadoc) 174 * 175 * @see hdf.object.HObject#open() 176 */ 177 @Override 178 public long open() { 179 long aid = super.open(); 180 long pObjID = -1; 181 182 try { 183 pObjID = parentObject.open(); 184 if (pObjID >= 0) { 185 if (this.getFileFormat().isThisType(FileFormat.getFileFormat(FileFormat.FILE_TYPE_HDF5))) { 186 log.trace("open(): FILE_TYPE_HDF5"); 187 if (H5.H5Aexists(pObjID, getName())) 188 aid = H5.H5Aopen(pObjID, getName(), HDF5Constants.H5P_DEFAULT); 189 } 190 } 191 192 log.trace("open(): aid={}", aid); 193 } 194 catch (Exception ex) { 195 log.debug("open(): Failed to open attribute {}: ", getName(), ex); 196 aid = -1; 197 } 198 finally { 199 parentObject.close(pObjID); 200 } 201 202 return aid; 203 } 204 205 /* 206 * (non-Javadoc) 207 * 208 * @see hdf.object.HObject#close(int) 209 */ 210 @Override 211 public void close(long aid) { 212 if (aid >= 0) { 213 if (this.getFileFormat().isThisType(FileFormat.getFileFormat(FileFormat.FILE_TYPE_HDF5))) { 214 log.trace("close(): FILE_TYPE_HDF5"); 215 try { 216 H5.H5Aclose(aid); 217 } 218 catch (HDF5Exception ex) { 219 log.debug("close(): H5Aclose({}) failure: ", aid, ex); 220 } 221 } 222 } 223 } 224 225 @Override 226 public void init() { 227 super.init(); 228 if (inited) { 229 return; 230 } 231 232 if (this.getFileFormat().isThisType(FileFormat.getFileFormat(FileFormat.FILE_TYPE_HDF5))) { 233 long aid = -1; 234 long tid = -1; 235 int tclass = -1; 236 flatNameList = new Vector<>(); 237 flatTypeList = new Vector<>(); 238 long[] memberTIDs = null; 239 240 log.trace("init(): FILE_TYPE_HDF5"); 241 aid = open(); 242 if (aid >= 0) { 243 try { 244 tid = H5.H5Aget_type(aid); 245 tclass = H5.H5Tget_class(tid); 246 247 long tmptid = 0; 248 249 // Handle ARRAY and VLEN types by getting the base type 250 if (tclass == HDF5Constants.H5T_ARRAY || tclass == HDF5Constants.H5T_VLEN) { 251 try { 252 tmptid = tid; 253 tid = H5.H5Tget_super(tmptid); 254 log.trace("init(): H5T_ARRAY or H5T_VLEN class old={}, new={}", tmptid, tid); 255 } 256 catch (Exception ex) { 257 log.debug("init(): H5T_ARRAY or H5T_VLEN H5Tget_super({}) failure: ", tmptid, ex); 258 tid = -1; 259 } 260 finally { 261 try { 262 H5.H5Tclose(tmptid); 263 } 264 catch (HDF5Exception ex) { 265 log.debug("init(): H5Tclose({}) failure: ", tmptid, ex); 266 } 267 } 268 } 269 270 if (H5.H5Tget_class(tid) == HDF5Constants.H5T_COMPOUND) { 271 // initialize member information 272 H5Datatype.extractCompoundInfo((H5Datatype) getDatatype(), "", flatNameList, flatTypeList); 273 numberOfMembers = flatNameList.size(); 274 log.trace("init(): numberOfMembers={}", numberOfMembers); 275 276 memberNames = new String[numberOfMembers]; 277 memberTIDs = new long[numberOfMembers]; 278 memberTypes = new Datatype[numberOfMembers]; 279 memberOrders = new int[numberOfMembers]; 280 isMemberSelected = new boolean[numberOfMembers]; 281 memberDims = new Object[numberOfMembers]; 282 283 for (int i = 0; i < numberOfMembers; i++) { 284 isMemberSelected[i] = true; 285 memberTIDs[i] = flatTypeList.get(i).createNative(); 286 287 try { 288 memberTypes[i] = flatTypeList.get(i); 289 } 290 catch (Exception ex) { 291 log.debug("init(): failed to create datatype for member[{}]: ", i, ex); 292 memberTypes[i] = null; 293 } 294 295 memberNames[i] = flatNameList.get(i); 296 memberOrders[i] = 1; 297 memberDims[i] = null; 298 log.trace("init()[{}]: memberNames[{}]={}, memberTIDs[{}]={}, memberTypes[{}]={}", i, i, 299 memberNames[i], i, memberTIDs[i], i, memberTypes[i]); 300 301 try { 302 tclass = H5.H5Tget_class(memberTIDs[i]); 303 } 304 catch (HDF5Exception ex) { 305 log.debug("init(): H5Tget_class({}) failure: ", memberTIDs[i], ex); 306 } 307 308 if (tclass == HDF5Constants.H5T_ARRAY) { 309 int n = H5.H5Tget_array_ndims(memberTIDs[i]); 310 long mdim[] = new long[n]; 311 H5.H5Tget_array_dims(memberTIDs[i], mdim); 312 int idim[] = new int[n]; 313 for (int j = 0; j < n; j++) 314 idim[j] = (int) mdim[j]; 315 memberDims[i] = idim; 316 tmptid = H5.H5Tget_super(memberTIDs[i]); 317 memberOrders[i] = (int) (H5.H5Tget_size(memberTIDs[i]) / H5.H5Tget_size(tmptid)); 318 try { 319 H5.H5Tclose(tmptid); 320 } 321 catch (HDF5Exception ex) { 322 log.debug("init(): memberTIDs[{}] H5Tclose(tmptid {}) failure: ", i, tmptid, ex); 323 } 324 } 325 } // (int i=0; i<numberOfMembers; i++) 326 } 327 328 inited = true; 329 } 330 catch (HDF5Exception ex) { 331 numberOfMembers = 0; 332 memberNames = null; 333 memberTypes = null; 334 memberOrders = null; 335 log.debug("init(): ", ex); 336 } 337 finally { 338 try { 339 H5.H5Tclose(tid); 340 } 341 catch (HDF5Exception ex2) { 342 log.debug("init(): H5Tclose({}) failure: ", tid, ex2); 343 } 344 345 if (memberTIDs != null) { 346 for (int i = 0; i < memberTIDs.length; i++) { 347 try { 348 H5.H5Tclose(memberTIDs[i]); 349 } 350 catch (Exception ex) { 351 log.debug("init(): H5Tclose(memberTIDs[{}] {}) failure: ", i, memberTIDs[i], ex); 352 } 353 } 354 } 355 } 356 357 close(aid); 358 } 359 } 360 361 resetSelection(); 362 } 363 364 /** 365 * Given an array of bytes representing a compound Datatype and a start index 366 * and length, converts len number of bytes into the correct Object type and 367 * returns it. 368 * 369 * @param data 370 * The byte array representing the data of the compound Datatype 371 * @param data_type 372 * The type of data to convert the bytes to 373 * @param start 374 * The start index of the bytes to get 375 * @param len 376 * The number of bytes to convert 377 * @return The converted type of the bytes 378 */ 379 @Override 380 protected Object convertCompoundByteMember(byte[] data, long data_type, long start, long len) { 381 Object currentData = null; 382 383 try { 384 long typeClass = H5.H5Tget_class(data_type); 385 386 if (typeClass == HDF5Constants.H5T_INTEGER) { 387 long size = H5.H5Tget_size(data_type); 388 389 currentData = HDFNativeData.byteToInt((int) start, (int) (len / size), data); 390 } 391 else if (typeClass == HDF5Constants.H5T_FLOAT) { 392 currentData = HDFNativeData.byteToDouble((int) start, 1, data); 393 } 394 } 395 catch (Exception ex) { 396 log.debug("convertCompoundByteMember(): conversion failure: ", ex); 397 } 398 399 return currentData; 400 } 401}