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.h4; 016 017import java.util.List; 018import java.util.Vector; 019 020import hdf.hdflib.HDFConstants; 021import hdf.hdflib.HDFException; 022import hdf.hdflib.HDFLibrary; 023import hdf.object.Attribute; 024import hdf.object.CompoundDS; 025import hdf.object.Dataset; 026import hdf.object.Datatype; 027import hdf.object.FileFormat; 028import hdf.object.Group; 029 030/** 031 * H4Vdata describes a multi-dimension array of HDF4 vdata, inheriting CompoundDS. 032 * <p> 033 * A vdata is like a table that consists of a collection of records whose values 034 * are stored in fixed-length fields. All records have the same structure and 035 * all values in each field have the same data type. Vdatas are uniquely 036 * identified by a name, a class, and a series of individual field names. 037 * <p> 038 * <b>How to Select a Subset</b> 039 * <p> 040 * Dataset defines APIs for reading, writing and subsetting a dataset. No function is 041 * defined to select a subset of a data array. The selection is done in an implicit way. 042 * Function calls to dimension information such as getSelectedDims() return an array 043 * of dimension values, which is a reference to the array in the dataset object. 044 * Changes of the array outside the dataset object directly change the values of 045 * the array in the dataset object. It is like pointers in C. 046 * <p> 047 * 048 * The following is an example of how to make a subset. In the example, the dataset 049 * is a 4-dimension with size of [200][100][50][10], i.e. 050 * dims[0]=200; dims[1]=100; dims[2]=50; dims[3]=10; <br> 051 * We want to select every other data point in dims[1] and dims[2] 052 * <pre> 053 int rank = dataset.getRank(); // number of dimensions of the dataset 054 long[] dims = dataset.getDims(); // the dimension sizes of the dataset 055 long[] selected = dataset.getSelectedDims(); // the selected size of the dataet 056 long[] start = dataset.getStartDims(); // the offset of the selection 057 long[] stride = dataset.getStride(); // the stride of the dataset 058 int[] selectedIndex = dataset.getSelectedIndex(); // the selected dimensions for display 059 060 // select dim1 and dim2 as 2D data for display, and slice through dim0 061 selectedIndex[0] = 1; 062 selectedIndex[1] = 2; 063 selectedIndex[1] = 0; 064 065 // reset the selection arrays 066 for (int i=0; i<rank; i++) { 067 start[i] = 0; 068 selected[i] = 1; 069 stride[i] = 1; 070 } 071 072 // set stride to 2 on dim1 and dim2 so that every other data point is selected. 073 stride[1] = 2; 074 stride[2] = 2; 075 076 // set the selection size of dim1 and dim2 077 selected[1] = dims[1]/stride[1]; 078 selected[2] = dims[1]/stride[2]; 079 080 // when dataset.read() is called, the selection above will be used since 081 // the dimension arrays is passed by reference. Changes of these arrays 082 // outside the dataset object directly change the values of these array 083 // in the dataset object. 084 085 * </pre> 086 * 087 * @version 1.1 9/4/2007 088 * @author Peter X. Cao 089 */ 090public class H4Vdata extends CompoundDS 091{ 092 private static final long serialVersionUID = -5978700886955419959L; 093 094 private final static org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(H4Vdata.class); 095 096 /** 097 * The list of attributes of this data object. Members of the list are 098 * instance of Attribute. 099 */ 100 @SuppressWarnings("rawtypes") 101 private List attributeList; 102 103 /** 104 * Number of records of this Vdata table. 105 */ 106 private int numberOfRecords; 107 108 /** 109 * The data types of the members of the compound dataset. 110 */ 111 private long[] memberTIDs; 112 113 private int nAttributes = -1; 114 115 116 public H4Vdata(FileFormat theFile, String name, String path) 117 { 118 this(theFile, name, path, null); 119 } 120 121 /** 122 * Creates an H4Vdata object with specific name and path. 123 * 124 * @param theFile the HDF file. 125 * @param name the name of this H4Vdata. 126 * @param path the full path of this H4Vdata. 127 * @param oid the unique identifier of this data object. 128 */ 129 @SuppressWarnings("deprecation") 130 public H4Vdata( 131 FileFormat theFile, 132 String name, 133 String path, 134 long[] oid) 135 { 136 super (theFile, name, path, oid); 137 numberOfRecords = 0; 138 numberOfMembers = 0; 139 memberOrders = null; 140 } 141 142 /* 143 * (non-Javadoc) 144 * @see hdf.object.DataFormat#hasAttribute() 145 */ 146 @Override 147 public boolean hasAttribute () 148 { 149 if (nAttributes < 0) { 150 long id = open(); 151 152 if (id >= 0) { 153 try { 154 nAttributes = HDFLibrary.VSnattrs(id); 155 } 156 catch (Exception ex) { 157 log.debug("hasAttribute() failure: ", ex); 158 nAttributes = 0; 159 } 160 161 log.trace("hasAttribute(): nAttributes={}", nAttributes); 162 163 close(id); 164 } 165 } 166 167 return (nAttributes>0); 168 } 169 170 // implementing Dataset 171 @Override 172 public Datatype getDatatype() 173 { 174 if (datatype == null) { 175 datatype = new H4Datatype(-1); 176 } 177 178 return datatype; 179 } 180 181 @Override 182 public Object getFillValue() { 183 return null; 184 } 185 186 // Implementing Dataset 187 @Override 188 public byte[] readBytes() throws HDFException 189 { 190 log.trace("readBytes(): start"); 191 192 byte[] theData = null; 193 194 if (!isInited()) 195 init(); 196 197 if (numberOfMembers <= 0) { 198 log.debug("readBytes(): VData contains no members"); 199 log.trace("readBytes(): finish"); 200 return null; // this Vdata does not have any filed 201 } 202 203 long id = open(); 204 if (id < 0) { 205 log.debug("readBytes(): Invalid VData ID"); 206 log.trace("readBytes(): finish"); 207 return null; 208 } 209 210 String allNames = memberNames[0]; 211 for (int i=0; i<numberOfMembers; i++) { 212 allNames += ","+memberNames[i]; 213 } 214 215 try { 216 // moves the access pointer to the start position 217 HDFLibrary.VSseek(id, (int)startDims[0]); 218 // Specify the fields to be accessed 219 HDFLibrary.VSsetfields(id, allNames); 220 int[] recordSize = {0}; 221 HDFLibrary.VSQueryvsize(id, recordSize); 222 int size =recordSize[0] * (int)selectedDims[0]; 223 theData = new byte[size]; 224 HDFLibrary.VSread( 225 id, 226 theData, 227 (int)selectedDims[0], 228 HDFConstants.FULL_INTERLACE); 229 } 230 catch (Exception ex) { 231 log.debug("readBytes(): failure: ", ex); 232 } 233 finally { 234 close(id); 235 } 236 237 log.trace("readBytes(): finish"); 238 return theData; 239 } 240 241 // Implementing DataFormat 242 @SuppressWarnings({ "rawtypes", "unchecked", "deprecation" }) 243 @Override 244 public Object read() throws HDFException 245 { 246 log.trace("read(): start"); 247 248 List list = null; 249 250 if (!isInited()) 251 init(); 252 253 if (numberOfMembers <= 0) { 254 log.debug("read(): VData contains no members"); 255 log.trace("read(): finish"); 256 return null; // this Vdata does not have any filed 257 } 258 259 long id = open(); 260 if (id < 0) { 261 log.debug("read(): Invalid VData ID"); 262 log.trace("read(): finish"); 263 return null; 264 } 265 266 list = new Vector(); 267 268 // assume external data files are located in the same directory as the main file. 269 HDFLibrary.HXsetdir(getFileFormat().getParent()); 270 271 Object member_data = null; 272 for (int i=0; i<numberOfMembers; i++) { 273 if (!isMemberSelected[i]) { 274 continue; 275 } 276 277 try { 278 // moves the access pointer to the start position 279 HDFLibrary.VSseek(id, (int)startDims[0]); 280 // Specify the fields to be accessed 281 HDFLibrary.VSsetfields(id, memberNames[i]); 282 } 283 catch (HDFException ex) { 284 log.debug("read(): failure: ", ex); 285 isMemberSelected[i] = false; 286 continue; 287 } 288 289 int n = memberOrders[i]*(int)selectedDims[0]; 290 291 member_data = H4Datatype.allocateArray(memberTIDs[i], n); 292 293 log.trace("read(): index={} isMemberSelected[i]={} memberOrders[i]={} array size={}", i, isMemberSelected[i], memberOrders[i], n); 294 if (member_data == null) { 295 String[] nullValues = new String[n]; 296 for (int j=0; j<n; j++) { 297 nullValues[j] = "*error*"; 298 } 299 list.add(nullValues); 300 continue; 301 } 302 303 try { 304 HDFLibrary.VSread( 305 id, 306 member_data, 307 (int)selectedDims[0], 308 HDFConstants.FULL_INTERLACE); 309 if ((memberTIDs[i] == HDFConstants.DFNT_CHAR) || 310 (memberTIDs[i] == HDFConstants.DFNT_UCHAR8)) { 311 // convert characters to string 312 log.trace("read(): convert characters to string"); 313 member_data = Dataset.byteToString((byte[])member_data, memberOrders[i]); 314 memberTypes[i] = new H4Datatype(Datatype.CLASS_STRING, memberOrders[i], -1, -1); 315 memberOrders[i] = 1; //one String 316 } 317 else if (H4Datatype.isUnsigned(memberTIDs[i])) { 318 // convert unsigned integer to appropriate Java integer 319 log.trace("read(): convert unsigned integer to appropriate Java integer"); 320 member_data = Dataset.convertFromUnsignedC(member_data); 321 } 322 } 323 catch (HDFException ex) { 324 String[] nullValues = new String[n]; 325 for (int j=0; j<n; j++) { 326 nullValues[j] = "*error*"; 327 } 328 list.add(nullValues); 329 continue; 330 } 331 332 list.add(member_data); 333 } // for (int i=0; i<numberOfMembers; i++) 334 335 close(id); 336 337 log.trace("read(): finish"); 338 return list; 339 } 340 341 // Implementing DataFormat 342 @Override 343 public void write(Object buf) throws HDFException 344 { 345 //For writing to a vdata, VSsetfields can only be called once, to set 346 //up the fields in a vdata. Once the vdata fields are set, they may 347 //not be changed. Thus, to update some fields of a record after the 348 //first write, the user must read all the fields to a buffer, update 349 //the buffer, then write the entire record back to the vdata. 350 log.trace("write(): disabled"); 351 /* 352 if (buf == null || numberOfMembers <= 0 || !(buf instanceof List)) 353 return; // no data to write 354 355 List list = (List)buf; 356 Object member_data = null; 357 String member_name = null; 358 359 int vid = open(); 360 if (vid < 0) return; 361 362 int idx = 0; 363 for (int i=0; i<numberOfMembers; i++) { 364 if (!isMemberSelected[i]) 365 continue; 366 367 HDFLibrary.VSsetfields(vid, memberNames[i]); 368 369 try { 370 // Specify the fields to be accessed 371 372 // moves the access pointer to the start position 373 HDFLibrary.VSseek(vid, (int)startDims[0]); 374 } 375 catch (HDFException ex) { 376 continue; 377 } 378 379 member_data = list.get(idx++); 380 if (member_data == null) 381 continue; 382 383 if (memberTIDs[i] == HDFConstants.DFNT_CHAR || 384 memberTIDs[i] == HDFConstants.DFNT_UCHAR8) { 385 member_data = Dataset.stringToByte((String[])member_data, memberOrders[i]); 386 } 387 else if (H4Datatype.isUnsigned(memberTIDs[i])) { 388 // convert unsigned integer to appropriate Java integer 389 member_data = Dataset.convertToUnsignedC(member_data); 390 } 391 392 393 int interlace = HDFConstants.NO_INTERLACE; 394 try { 395 int write_num = HDFLibrary.VSwrite( 396 vid, member_data, (int)selectedDims[0], interlace); 397 } 398 catch (HDFException ex) { 399 log.debug("write():", ex); 400 } 401 } // for (int i=0; i<numberOfMembers; i++) 402 403 close(vid); 404 */ 405 } 406 407 @Override 408 public Object convertFromUnsignedC() { 409 throw new UnsupportedOperationException("H4Vdata:convertFromUnsignedC Unsupported operation."); 410 } 411 412 @Override 413 public Object convertToUnsignedC() { 414 throw new UnsupportedOperationException("H4Vdata:convertToUnsignedC Unsupported operation."); 415 } 416 417 // Implementing DataFormat 418 @Override 419 @SuppressWarnings({"rawtypes", "unchecked"}) 420 public List getMetadata() throws HDFException 421 { 422 log.trace("getMetadata(): start"); 423 424 if (attributeList != null) { 425 log.trace("getMetdata(): attributeList != null"); 426 log.trace("getMetadata(): finish"); 427 return attributeList; 428 } 429 430 long id = open(); 431 432 if (id < 0) { 433 log.debug("getMetadata(): Invalid VData ID"); 434 log.trace("getMetadata(): finish"); 435 return attributeList; 436 } 437 438 int n=0; 439 try { 440 n = HDFLibrary.VSnattrs(id); 441 442 if (n <= 0) { 443 log.debug("getMetadata(): VData number of attributes <= 0"); 444 log.trace("getMetadata(): finish"); 445 return attributeList; 446 } 447 448 attributeList = new Vector(n, 5); 449 boolean b = false; 450 String[] attrName = new String[1]; 451 int[] attrInfo = new int[5]; 452 453 // _HDF_VDATA (or -1) to specify the vdata attribute 454 int nleft = n; 455 for (int j=-1; j<numberOfMembers; j++) { 456 for (int i=0; i<nleft; i++) { 457 attrName[0] = ""; 458 459 try { 460 b = HDFLibrary.VSattrinfo(id, j, i, attrName, attrInfo); 461 // mask off the litend bit 462 attrInfo[0] = attrInfo[0] & (~HDFConstants.DFNT_LITEND); 463 } 464 catch (HDFException ex) { 465 log.debug("getMetadata(): attribute[{}] VSattrinfo failure: ", i, ex); 466 b = false; 467 ex.printStackTrace(); 468 } 469 470 if (!b || attrName[0].length()<=0) { 471 continue; 472 } 473 474 long[] attrDims = {attrInfo[1]}; 475 Attribute attr = new Attribute(this, attrName[0], new H4Datatype(attrInfo[0]), attrDims); 476 if (j>=0) 477 attr.setProperty("field", memberNames[j]); 478 attributeList.add(attr); 479 480 Object buf = null; 481 try { 482 buf = H4Datatype.allocateArray(attrInfo[0], attrInfo[1]); 483 } 484 catch (OutOfMemoryError e) { 485 log.debug("getMetadata(): out of memory: ", e); 486 buf = null; 487 } 488 489 try { 490 HDFLibrary.VSgetattr(id, j, i, buf); 491 } 492 catch (HDFException ex) { 493 log.debug("getMetadata(): attribute[{}] VSgetattr failure: ", i, ex); 494 buf = null; 495 } 496 497 if (buf != null) { 498 if ((attrInfo[0] == HDFConstants.DFNT_CHAR) || 499 (attrInfo[0] == HDFConstants.DFNT_UCHAR8)) { 500 buf = Dataset.byteToString((byte[])buf, attrInfo[1]); 501 } 502 503 attr.setData(buf); 504 nleft--; 505 } 506 } // for (int i=0; i<n; i++) 507 } // for (int j=-1; j<numberOfMembers; j++) 508 } 509 catch (Exception ex) { 510 log.debug("getMetadata(): failure: ", ex); 511 } 512 finally { 513 close(id); 514 } 515 516 // todo: We shall also load attributes of fields 517 518 log.trace("getMetadata(): finish"); 519 return attributeList; 520 } 521 522 // To do: Implementing DataFormat 523 @Override 524 @SuppressWarnings({"rawtypes", "unchecked"}) 525 public void writeMetadata(Object info) throws Exception 526 { 527 log.trace("writeMetadata(): start"); 528 529 // only attribute metadata is supported. 530 if (!(info instanceof Attribute)) { 531 log.debug("writeMetadata(): Object not an Attribute"); 532 log.trace("writeMetadata(): finish"); 533 return; 534 } 535 536 try { 537 getFileFormat().writeAttribute(this, (Attribute)info, true); 538 539 if (attributeList == null) { 540 attributeList = new Vector(); 541 } 542 543 attributeList.add(info); 544 nAttributes = attributeList.size(); 545 } 546 catch (Exception ex) { 547 log.trace("writeMetadata(): failure: ", ex); 548 } 549 550 log.trace("writeMetadata(): finish"); 551 } 552 553 // To do: Implementing DataFormat 554 @Override 555 public void removeMetadata(Object info) throws HDFException 556 { 557 log.trace("removeMetadata(): disabled"); 558 } 559 560 // implementing DataFormat 561 @Override 562 public void updateMetadata(Object info) throws Exception { 563 log.trace("updateMetadata(): disabled"); 564 } 565 566 // Implementing DataFormat 567 @Override 568 public long open() 569 { 570 log.trace("open(): start"); 571 572 // try to open with write permission 573 long vsid = -1; 574 try { 575 vsid = HDFLibrary.VSattach(getFID(), (int)oid[1], "w"); 576 } 577 catch (HDFException ex) { 578 log.debug("open(): VSattach failure: ", ex); 579 vsid = -1; 580 } 581 582 // try to open with read-only permission 583 if (vsid < 0) { 584 try { 585 vsid = HDFLibrary.VSattach(getFID(), (int)oid[1], "r"); 586 } 587 catch (HDFException ex) { 588 log.debug("open(): VSattach failure: ", ex); 589 vsid = -1; 590 } 591 } 592 593 log.trace("open(): finish"); 594 return vsid; 595 } 596 597 // Implementing DataFormat 598 @Override 599 public void close(long vsid) 600 { 601 try { 602 HDFLibrary.VSdetach(vsid); 603 } 604 catch (Exception ex) { 605 log.debug("close(): VSdetach failure: ", ex); 606 } 607 } 608 609 /** 610 * Initializes the H4Vdata such as dimension sizes of this dataset. 611 */ 612 @Override 613 public void init() 614 { 615 log.trace("init(): start"); 616 617 if (inited) { 618 log.trace("init(): Already initialized"); 619 log.trace("init(): finish"); 620 return; // already called. Initialize only once 621 } 622 623 long id = open(); 624 if (id < 0) { 625 log.debug("init(): Invalid VData ID"); 626 log.trace("init(): finish"); 627 return; 628 } 629 630 try { 631 numberOfMembers = HDFLibrary.VFnfields(id); 632 numberOfRecords = HDFLibrary.VSelts(id); 633 } 634 catch (HDFException ex) { 635 numberOfMembers = 0; 636 numberOfRecords = 0; 637 } 638 639 // Still need to get information if there is no record, see bug 1738 640 // if ((numberOfMembers <=0) || (numberOfRecords <= 0)) { 641 // // no table field is defined or no records 642 // close(id); 643 // return; 644 // } 645 646 // a Vdata table is an one dimension array of records. 647 // each record has the same fields 648 rank = 1; 649 dims = new long[1]; 650 dims[0] = numberOfRecords; 651 selectedDims = new long[1]; 652 selectedDims[0] = numberOfRecords; 653 selectedIndex[0] = 0; 654 startDims = new long[1]; 655 startDims[0] = 0; 656 657 memberNames = new String[numberOfMembers]; 658 memberTIDs = new long[numberOfMembers]; 659 memberTypes = new Datatype[numberOfMembers]; 660 memberOrders = new int[numberOfMembers]; 661 isMemberSelected = new boolean[numberOfMembers]; 662 663 for (int i=0; i<numberOfMembers; i++) { 664 isMemberSelected[i] = true; 665 try { 666 memberNames[i] = HDFLibrary.VFfieldname(id, i); 667 memberTIDs[i] = HDFLibrary.VFfieldtype(id, i); 668 memberTypes[i] = new H4Datatype(memberTIDs[i]); 669 // mask off the litend bit 670 memberTIDs[i] = memberTIDs[i] & (~HDFConstants.DFNT_LITEND); 671 memberOrders[i] = HDFLibrary.VFfieldorder(id, i); 672 log.trace("init():{}> isMemberSelected[i]={} memberNames[i]={} memberTIDs[i]={} memberOrders[i]={}", i, isMemberSelected[i], memberNames[i], memberTIDs[i], memberOrders[i]); 673 } 674 catch (HDFException ex) { 675 log.debug("init(): member[{}]: ", i, ex); 676 log.trace("init(): continue"); 677 continue; 678 } 679 } // for (int i=0; i<numberOfMembers; i++) 680 681 inited = true; 682 683 close(id); 684 log.trace("init(): finish"); 685 } 686 687 /** 688 * Returns the number of records. 689 * 690 * @return the number of records 691 */ 692 public int getRecordCount() 693 { 694 return numberOfRecords; 695 } 696 697 /** 698 * Returns the number of fields. 699 * 700 * @return the number of fields 701 */ 702 public int getFieldCount() 703 { 704 return numberOfMembers; 705 } 706 707 /** 708 * Returns the orders of fields 709 * 710 * @return the orders of fields 711 */ 712 public int[] getFieldOrders() 713 { 714 return memberOrders; 715 } 716 717 //Implementing DataFormat 718 @SuppressWarnings("rawtypes") 719 public List getMetadata(int... attrPropList) throws Exception { 720 throw new UnsupportedOperationException("getMetadata(int... attrPropList) is not supported"); 721 } 722 723 @Override 724 public Dataset copy(Group pgroup, String name, long[] dims, Object data) 725 throws Exception { 726 throw new UnsupportedOperationException( 727 "Writing a vdata to a new dataset is not implemented."); 728 } 729}