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; 016 017import java.lang.reflect.Array; 018import java.math.BigDecimal; 019import java.math.BigInteger; 020import java.text.DecimalFormat; 021import java.util.ArrayList; 022import java.util.Arrays; 023import java.util.Collection; 024import java.util.HashMap; 025import java.util.Iterator; 026import java.util.List; 027import java.util.Map; 028import java.util.Vector; 029 030import org.slf4j.Logger; 031import org.slf4j.LoggerFactory; 032 033/** 034 * The abstract class provides general APIs to create and manipulate dataset/attribute objects, and retrieve 035 * dataset/attribute properties, datatype and dimension sizes. 036 * 037 * This class provides two convenient functions, read()/write(), to read/write data values. Reading/writing 038 * data may take many library calls if we use the library APIs directly. The read() and write functions hide 039 * all the details of these calls from users. 040 * 041 * For more details on dataset and attributes, See 042 * <a href="https://hdfgroup.github.io/hdf5/_h5_d__u_g.html#sec_dataset">HDF5 Datasets in HDF5 User Guide</a> 043 * <a href="https://hdfgroup.github.io/hdf5/_h5_a__u_g.html#sec_attribute">HDF5 Attributes in HDF5 User 044 * Guide</a> 045 * 046 * @see hdf.object.ScalarDS 047 * @see hdf.object.CompoundDS 048 * 049 * @version 1.1 9/4/2007 050 * @author Peter X. Cao 051 */ 052public abstract class Dataset extends HObject implements DataFormat { 053 private static final long serialVersionUID = -3360885430038261178L; 054 055 private static final Logger log = LoggerFactory.getLogger(Dataset.class); 056 057 /** 058 * The memory buffer that holds the raw data array of the dataset. 059 */ 060 protected transient Object data; 061 062 /** 063 * The type of space for the dataset. 064 */ 065 protected int space_type; 066 067 /** 068 * The number of dimensions of the dataset. 069 */ 070 protected int rank; 071 072 /** 073 * The current dimension sizes of the dataset 074 */ 075 protected long[] dims; 076 077 /** 078 * The max dimension sizes of the dataset 079 */ 080 protected long[] maxDims; 081 082 /** 083 * Array that contains the number of data points selected (for read/write) 084 * in each dimension. 085 * 086 * The selected size must be less than or equal to the current dimension size. 087 * A subset of a rectangle selection is defined by the starting position and 088 * selected sizes. 089 * 090 * For example, if a 4 X 5 dataset is as follows: 091 * 092 * <pre> 093 * 0, 1, 2, 3, 4 094 * 10, 11, 12, 13, 14 095 * 20, 21, 22, 23, 24 096 * 30, 31, 32, 33, 34 097 * long[] dims = {4, 5}; 098 * long[] startDims = {1, 2}; 099 * long[] selectedDims = {3, 3}; 100 * then the following subset is selected by the startDims and selectedDims above: 101 * 12, 13, 14 102 * 22, 23, 24 103 * 32, 33, 34 104 * </pre> 105 */ 106 protected long[] selectedDims; 107 108 /** 109 * The starting position of each dimension of a selected subset. With both 110 * the starting position and selected sizes, the subset of a rectangle 111 * selection is fully defined. 112 */ 113 protected long[] startDims; 114 115 /** 116 * Array that contains the indices of the dimensions selected for display. 117 * 118 * <B>selectedIndex[] is provided for two purposes:</B> 119 * <OL> 120 * <LI> 121 * selectedIndex[] is used to indicate the order of dimensions for display, 122 * i.e. selectedIndex[0] = row, selectedIndex[1] = column and 123 * selectedIndex[2] = depth. For example, for a four dimension dataset, if 124 * selectedIndex[] is {1, 2, 3}, then dim[1] is selected as row index, 125 * dim[2] is selected as column index and dim[3] is selected as depth index. 126 * <LI> 127 * selectedIndex[] is also used to select dimensions for display for 128 * datasets with three or more dimensions. We assume that applications such 129 * as HDFView can only display data up to three dimensions (a 2D 130 * spreadsheet/image with a third dimension that the 2D spreadsheet/image is 131 * cut from). For datasets with more than three dimensions, we need 132 * selectedIndex[] to store which three dimensions are chosen for display. 133 * For example, for a four dimension dataset, if selectedIndex[] = {1, 2, 3}, 134 * then dim[1] is selected as row index, dim[2] is selected as column index 135 * and dim[3] is selected as depth index. dim[0] is not selected. Its 136 * location is fixed at 0 by default. 137 * </OL> 138 */ 139 protected final int[] selectedIndex; 140 141 /** 142 * The number of elements to move from the start location in each dimension. 143 * For example, if selectedStride[0] = 2, every other data point is selected 144 * along dim[0]. 145 */ 146 protected long[] selectedStride; 147 148 /** 149 * The array of dimension sizes for a chunk. 150 */ 151 protected long[] chunkSize; 152 153 /** The compression information. */ 154 protected StringBuilder compression; 155 /** The compression information default prefix. */ 156 public static final String COMPRESSION_GZIP_TXT = "GZIP: level = "; 157 158 /** The filters information. */ 159 protected StringBuilder filters; 160 161 /** The storage layout information. */ 162 protected StringBuilder storageLayout; 163 164 /** The storage information. */ 165 protected StringBuilder storage; 166 167 /** The datatype object of the dataset. */ 168 protected Datatype datatype; 169 170 /** 171 * Array of strings that represent the dimension names. It is null if dimension names do not exist. 172 */ 173 protected String[] dimNames; 174 175 /** Flag to indicate if the byte[] array is converted to strings */ 176 protected boolean convertByteToString = true; 177 178 /** Flag to indicate if data values are loaded into memory. */ 179 protected boolean isDataLoaded = false; 180 181 /** Flag to indicate if this dataset has been initialized */ 182 protected boolean inited = false; 183 184 /** The number of data points in the memory buffer. */ 185 protected long nPoints = 1; 186 187 /** Flag to indicate if the dataspace is NULL */ 188 protected boolean isNULL = false; 189 190 /** Flag to indicate if the data is a single scalar point */ 191 protected boolean isScalar = false; 192 193 /** True if this dataset is an image. */ 194 protected boolean isImage = false; 195 196 /** True if this dataset is ASCII text. */ 197 protected boolean isText = false; 198 199 /** 200 * The data buffer that contains the raw data directly reading from file 201 * (before any data conversion). 202 */ 203 protected transient Object originalBuf = null; 204 205 /** 206 * The array that holds the converted data of unsigned C-type integers. 207 * 208 * For example, Suppose that the original data is an array of unsigned 209 * 16-bit short integers. Since Java does not support unsigned integer, the 210 * data is converted to an array of 32-bit singed integer. In that case, the 211 * converted buffer is the array of 32-bit singed integer. 212 */ 213 protected transient Object convertedBuf = null; 214 215 /** 216 * Constructs a Dataset object with a given file, name and path. 217 * 218 * @param theFile 219 * the file that contains the dataset. 220 * @param dsName 221 * the name of the Dataset, e.g. "dset1". 222 * @param dsPath 223 * the full group path of this Dataset, e.g. "/arrays/". 224 */ 225 public Dataset(FileFormat theFile, String dsName, String dsPath) { this(theFile, dsName, dsPath, null); } 226 227 /** 228 * @deprecated Not for public use in the future. <br> 229 * Using {@link #Dataset(FileFormat, String, String)} 230 * 231 * @param theFile 232 * the file that contains the dataset. 233 * @param dsName 234 * the name of the Dataset, e.g. "dset1". 235 * @param dsPath 236 * the full group path of this Dataset, e.g. "/arrays/". 237 * @param oid 238 * the oid of this Dataset. 239 */ 240 @Deprecated 241 public Dataset(FileFormat theFile, String dsName, String dsPath, long[] oid) 242 { 243 super(theFile, dsName, dsPath, oid); 244 log.trace("Dataset: start {}", dsName); 245 246 datatype = null; 247 rank = -1; 248 space_type = -1; 249 data = null; 250 dims = null; 251 maxDims = null; 252 selectedDims = null; 253 startDims = null; 254 selectedStride = null; 255 chunkSize = null; 256 compression = new StringBuilder("NONE"); 257 filters = new StringBuilder("NONE"); 258 storageLayout = new StringBuilder("NONE"); 259 storage = new StringBuilder("NONE"); 260 dimNames = null; 261 262 selectedIndex = new int[3]; 263 selectedIndex[0] = 0; 264 selectedIndex[1] = 1; 265 selectedIndex[2] = 2; 266 } 267 268 /** 269 * Clears memory held by the dataset, such as the data buffer. 270 */ 271 @SuppressWarnings("rawtypes") 272 public void clear() 273 { 274 if (data != null) { 275 if (data instanceof List) 276 ((List)data).clear(); 277 data = null; 278 originalBuf = null; 279 convertedBuf = null; 280 } 281 isDataLoaded = false; 282 } 283 284 /** 285 * Returns the type of space for the dataset. 286 * 287 * @return the type of space for the dataset. 288 */ 289 @Override 290 public final int getSpaceType() 291 { 292 return space_type; 293 } 294 295 /** 296 * Returns the rank (number of dimensions) of the dataset. 297 * 298 * @return the number of dimensions of the dataset. 299 */ 300 @Override 301 public final int getRank() 302 { 303 return rank; 304 } 305 306 /** 307 * Returns the array that contains the dimension sizes of the dataset. 308 * 309 * @return the dimension sizes of the dataset. 310 */ 311 @Override 312 public final long[] getDims() 313 { 314 return dims; 315 } 316 317 /** 318 * Returns the array that contains the max dimension sizes of the dataset. 319 * 320 * @return the max dimension sizes of the dataset. 321 */ 322 public final long[] getMaxDims() 323 { 324 if (maxDims == null) 325 return dims; 326 327 return maxDims; 328 } 329 330 /** 331 * Returns the dimension sizes of the selected subset. 332 * 333 * The SelectedDims is the number of data points of the selected subset. 334 * Applications can use this array to change the size of selected subset. 335 * 336 * The selected size must be less than or equal to the current dimension size. 337 * Combined with the starting position, selected sizes and stride, the 338 * subset of a rectangle selection is fully defined. 339 * 340 * For example, if a 4 X 5 dataset is as follows: 341 * 342 * <pre> 343 * 0, 1, 2, 3, 4 344 * 10, 11, 12, 13, 14 345 * 20, 21, 22, 23, 24 346 * 30, 31, 32, 33, 34 347 * long[] dims = {4, 5}; 348 * long[] startDims = {1, 2}; 349 * long[] selectedDims = {3, 3}; 350 * long[] selectedStride = {1, 1}; 351 * then the following subset is selected by the startDims and selectedDims 352 * 12, 13, 14 353 * 22, 23, 24 354 * 32, 33, 34 355 * </pre> 356 * 357 * @return the dimension sizes of the selected subset. 358 */ 359 @Override 360 public final long[] getSelectedDims() 361 { 362 return selectedDims; 363 } 364 365 /** 366 * Returns the starting position of a selected subset. 367 * 368 * Applications can use this array to change the starting position of a 369 * selection. Combined with the selected dimensions, selected sizes and 370 * stride, the subset of a rectangle selection is fully defined. 371 * 372 * For example, if a 4 X 5 dataset is as follows: 373 * 374 * <pre> 375 * 0, 1, 2, 3, 4 376 * 10, 11, 12, 13, 14 377 * 20, 21, 22, 23, 24 378 * 30, 31, 32, 33, 34 379 * long[] dims = {4, 5}; 380 * long[] startDims = {1, 2}; 381 * long[] selectedDims = {3, 3}; 382 * long[] selectedStride = {1, 1}; 383 * then the following subset is selected by the startDims and selectedDims 384 * 12, 13, 14 385 * 22, 23, 24 386 * 32, 33, 34 387 * </pre> 388 * 389 * @return the starting position of a selected subset. 390 */ 391 @Override 392 public final long[] getStartDims() 393 { 394 return startDims; 395 } 396 397 /** 398 * Returns the selectedStride of the selected dataset. 399 * 400 * Applications can use this array to change how many elements to move in 401 * each dimension. 402 * 403 * Combined with the starting position and selected sizes, the subset of a 404 * rectangle selection is defined. 405 * 406 * For example, if a 4 X 5 dataset is as follows: 407 * 408 * <pre> 409 * 0, 1, 2, 3, 4 410 * 10, 11, 12, 13, 14 411 * 20, 21, 22, 23, 24 412 * 30, 31, 32, 33, 34 413 * long[] dims = {4, 5}; 414 * long[] startDims = {0, 0}; 415 * long[] selectedDims = {2, 2}; 416 * long[] selectedStride = {2, 3}; 417 * then the following subset is selected by the startDims and selectedDims 418 * 0, 3 419 * 20, 23 420 * </pre> 421 * 422 * @return the selectedStride of the selected dataset. 423 */ 424 @Override 425 public final long[] getStride() 426 { 427 if (rank <= 0) 428 return null; 429 430 if (selectedStride == null) { 431 selectedStride = new long[rank]; 432 for (int i = 0; i < rank; i++) 433 selectedStride[i] = 1; 434 } 435 436 return selectedStride; 437 } 438 439 /** 440 * Sets the flag that indicates if a byte array is converted to a string 441 * array. 442 * 443 * In a string dataset, the raw data from file is stored in a byte array. By 444 * default, this byte array is converted to an array of strings. For a large 445 * dataset (e.g. more than one million strings), the conversion takes a long 446 * time and requires a lot of memory space to store the strings. In some 447 * applications, such a conversion can be delayed. For example, A GUI 448 * application may convert only the part of the strings that is visible to the 449 * users, not the entire data array. 450 * 451 * setConvertByteToString(boolean b) allows users to set the flag so that 452 * applications can choose to perform the byte-to-string conversion or not. 453 * If the flag is set to false, the getData() returns an array of byte 454 * instead of an array of strings. 455 * 456 * @param b 457 * convert bytes to strings if b is true; otherwise, if false, do 458 * not convert bytes to strings. 459 */ 460 public final void setConvertByteToString(boolean b) { convertByteToString = b; } 461 462 /** 463 * Returns the flag that indicates if a byte array is converted to a string 464 * array. 465 * 466 * @return true if byte array is converted to string; otherwise, returns 467 * false if there is no conversion. 468 */ 469 public final boolean getConvertByteToString() { return convertByteToString; } 470 471 /** 472 * Reads the raw data of the dataset from file to a byte array. 473 * 474 * readBytes() reads raw data to an array of bytes instead of array of its 475 * datatype. For example, for a one-dimension 32-bit integer dataset of 476 * size 5, readBytes() returns a byte array of size 20 instead of an 477 * int array of 5. 478 * 479 * readBytes() can be used to copy data from one dataset to another 480 * efficiently because the raw data is not converted to its native type, it 481 * saves memory space and CPU time. 482 * 483 * @return the byte array of the raw data. 484 * 485 * @throws Exception if data can not be read 486 */ 487 public abstract byte[] readBytes() throws Exception; 488 489 /** 490 * Writes the memory buffer of this dataset to file. 491 * 492 * @throws Exception if buffer can not be written 493 */ 494 @Override 495 public final void write() throws Exception 496 { 497 log.trace("Dataset: write enter"); 498 if (data != null) { 499 log.trace("Dataset: write data"); 500 write(data); 501 } 502 } 503 504 /** 505 * Creates a new dataset and writes the data buffer to the new dataset. 506 * 507 * This function allows applications to create a new dataset for a given 508 * data buffer. For example, users can select a specific interesting part 509 * from a large image and create a new image with the selection. 510 * 511 * The new dataset retains the datatype and dataset creation properties of 512 * this dataset. 513 * 514 * @param pgroup 515 * the group which the dataset is copied to. 516 * @param name 517 * the name of the new dataset. 518 * @param dims 519 * the dimension sizes of the the new dataset. 520 * @param data 521 * the data values of the subset to be copied. 522 * 523 * @return the new dataset. 524 * 525 * @throws Exception if dataset can not be copied 526 */ 527 public abstract Dataset copy(Group pgroup, String name, long[] dims, Object data) throws Exception; 528 529 /** 530 * The status of initialization for this object 531 * 532 * @return true if the data has been initialized 533 */ 534 @Override 535 public final boolean isInited() 536 { 537 return inited; 538 } 539 540 /** 541 * Resets selection of dataspace 542 */ 543 protected void resetSelection() 544 { 545 for (int i = 0; i < rank; i++) { 546 startDims[i] = 0; 547 selectedDims[i] = 1; 548 if (selectedStride != null) 549 selectedStride[i] = 1; 550 } 551 552 if (rank == 1) { 553 selectedIndex[0] = 0; 554 selectedDims[0] = dims[0]; 555 } 556 else if (rank == 2) { 557 selectedIndex[0] = 0; 558 selectedIndex[1] = 1; 559 selectedDims[0] = dims[0]; 560 selectedDims[1] = dims[1]; 561 } 562 else if (rank > 2) { 563 if (isImage) { 564 // 3D dataset is arranged in the order of [frame][height][width] 565 selectedIndex[1] = rank - 1; // width, the fastest dimension 566 selectedIndex[0] = rank - 2; // height 567 selectedIndex[2] = rank - 3; // frames 568 } 569 else { 570 selectedIndex[0] = 0; // width, the fastest dimension 571 selectedIndex[1] = 1; // height 572 selectedIndex[2] = 2; // frames 573 } 574 575 selectedDims[selectedIndex[0]] = dims[selectedIndex[0]]; 576 selectedDims[selectedIndex[1]] = dims[selectedIndex[1]]; 577 selectedDims[selectedIndex[2]] = dims[selectedIndex[2]]; 578 } 579 580 isDataLoaded = false; 581 } 582 583 /** 584 * Returns the data buffer of the dataset in memory. 585 * 586 * If data is already loaded into memory, returns the data; otherwise, calls 587 * read() to read data from file into a memory buffer and returns the memory 588 * buffer. 589 * 590 * By default, the whole dataset is read into memory. Users can also select 591 * a subset to read. Subsetting is done in an implicit way. 592 * 593 * <b>How to Select a Subset</b> 594 * 595 * A selection is specified by three arrays: start, stride and count. 596 * <ol> 597 * <li>start: offset of a selection 598 * <li>stride: determines how many elements to move in each dimension 599 * <li>count: number of elements to select in each dimension 600 * </ol> 601 * getStartDims(), getStride() and getSelectedDims() returns the start, 602 * stride and count arrays respectively. Applications can make a selection 603 * by changing the values of the arrays. 604 * 605 * The following example shows how to make a subset. In the example, the 606 * dataset is a 4-dimensional array of [200][100][50][10], i.e. dims[0]=200; 607 * dims[1]=100; dims[2]=50; dims[3]=10; <br> 608 * We want to select every other data point in dims[1] and dims[2] 609 * 610 * <pre> 611 * int rank = dataset.getRank(); // number of dimensions of the dataset 612 * long[] dims = dataset.getDims(); // the dimension sizes of the dataset 613 * long[] selected = dataset.getSelectedDims(); // the selected size of the dataet 614 * long[] start = dataset.getStartDims(); // the offset of the selection 615 * long[] stride = dataset.getStride(); // the stride of the dataset 616 * int[] selectedIndex = dataset.getSelectedIndex(); // the selected dimensions for display 617 * 618 * // select dim1 and dim2 as 2D data for display,and slice through dim0 619 * selectedIndex[0] = 1; 620 * selectedIndex[1] = 2; 621 * selectedIndex[1] = 0; 622 * 623 * // reset the selection arrays 624 * for (int i = 0; i < rank; i++) { 625 * start[i] = 0; 626 * selected[i] = 1; 627 * stride[i] = 1; 628 * } 629 * 630 * // set stride to 2 on dim1 and dim2 so that every other data point is 631 * // selected. 632 * stride[1] = 2; 633 * stride[2] = 2; 634 * 635 * // set the selection size of dim1 and dim2 636 * selected[1] = dims[1] / stride[1]; 637 * selected[2] = dims[1] / stride[2]; 638 * 639 * // when dataset.getData() is called, the selection above will be used since 640 * // the dimension arrays are passed by reference. Changes of these arrays 641 * // outside the dataset object directly change the values of these array 642 * // in the dataset object. 643 * </pre> 644 * 645 * For ScalarDS, the memory data buffer is a one-dimensional array of byte, 646 * short, int, float, double or String type based on the datatype of the 647 * dataset. 648 * 649 * For CompoundDS, the memory data object is an java.util.List object. Each 650 * element of the list is a data array that corresponds to a compound field. 651 * 652 * For example, if compound dataset "comp" has the following nested 653 * structure, and member datatypes 654 * 655 * <pre> 656 * comp --> m01 (int) 657 * comp --> m02 (float) 658 * comp --> nest1 --> m11 (char) 659 * comp --> nest1 --> m12 (String) 660 * comp --> nest1 --> nest2 --> m21 (long) 661 * comp --> nest1 --> nest2 --> m22 (double) 662 * </pre> 663 * 664 * getData() returns a list of six arrays: {int[], float[], char[], 665 * String[], long[] and double[]}. 666 * 667 * @return the memory buffer of the dataset. 668 * 669 * @throws Exception if object can not be read 670 * @throws OutOfMemoryError if memory is exhausted 671 */ 672 @Override 673 public Object getData() throws Exception, OutOfMemoryError 674 { 675 log.trace("getData(): isDataLoaded={}", isDataLoaded); 676 if (!isDataLoaded) { 677 data = read(); // load the data 678 if (data != null) { 679 originalBuf = data; 680 isDataLoaded = true; 681 nPoints = 1; 682 log.trace("getData(): selectedDims length={}", selectedDims.length); 683 for (int j = 0; j < selectedDims.length; j++) 684 nPoints *= selectedDims[j]; 685 } 686 log.trace("getData(): read {}", nPoints); 687 } 688 689 return data; 690 } 691 692 /** 693 * Not for public use in the future. 694 * 695 * setData() is not safe to use because it changes memory buffer 696 * of the dataset object. Dataset operations such as write/read 697 * will fail if the buffer type or size is changed. 698 * 699 * @param d the object data -must be an array of Objects 700 */ 701 @Override 702 public final void setData(Object d) 703 { 704 if (!(this instanceof Attribute)) 705 throw new UnsupportedOperationException("setData: unsupported for non-Attribute objects"); 706 707 log.trace("setData(): isDataLoaded={}", isDataLoaded); 708 data = d; 709 originalBuf = data; 710 isDataLoaded = true; 711 } 712 713 /** 714 * Clears the current data buffer in memory and forces the next read() to load 715 * the data from file. 716 * 717 * The function read() loads data from file into memory only if the data is 718 * not read. If data is already in memory, read() just returns the memory 719 * buffer. Sometimes we want to force read() to re-read data from file. For 720 * example, when the selection is changed, we need to re-read the data. 721 * 722 * @see #getData() 723 * @see #read() 724 */ 725 @Override 726 public void clearData() 727 { 728 isDataLoaded = false; 729 } 730 731 /** 732 * Refreshes the current object in the file. 733 * 734 * The function read() loads data from file into memory only if the data is not 735 * read. If data is already in memory, read() just returns the memory buffer. 736 * Sometimes we want to force a clear and read to re-read the object from the file. 737 * For example, when the selection is changed, we need to re-read the data. 738 * 739 * @see #getData() 740 * @see #read() 741 */ 742 @Override 743 public Object refreshData() 744 { 745 Object dataValue = null; 746 747 clearData(); 748 try { 749 dataValue = getData(); 750 751 /* 752 * TODO: Converting data from unsigned C integers to Java integers 753 * is currently unsupported for Compound Datasets. 754 */ 755 if (!(this instanceof CompoundDS)) 756 convertFromUnsignedC(); 757 758 dataValue = getData(); 759 log.trace("refresh data"); 760 } 761 catch (Exception ex) { 762 log.trace("refresh data failure: ", ex); 763 } 764 return dataValue; 765 } 766 767 /** 768 * Returns the dimension size of the vertical axis. 769 * 770 * This function is used by GUI applications such as HDFView. GUI 771 * applications display a dataset in a 2D table or 2D image. The display 772 * order is specified by the index array of selectedIndex as follow: 773 * <dl> 774 * <dt>selectedIndex[0] -- height</dt> 775 * <dd>The vertical axis</dd> 776 * <dt>selectedIndex[1] -- width</dt> 777 * <dd>The horizontal axis</dd> 778 * <dt>selectedIndex[2] -- depth</dt> 779 * <dd>The depth axis is used for 3 or more dimensional datasets.</dd> 780 * </dl> 781 * Applications can use getSelectedIndex() to access and change the display 782 * order. For example, in a 2D dataset of 200x50 (dim0=200, dim1=50), the 783 * following code will set the height=200 and width=50. 784 * 785 * <pre> 786 * int[] selectedIndex = dataset.getSelectedIndex(); 787 * selectedIndex[0] = 0; 788 * selectedIndex[1] = 1; 789 * </pre> 790 * 791 * @see #getSelectedIndex() 792 * @see #getWidth() 793 * 794 * @return the size of dimension of the vertical axis. 795 */ 796 @Override 797 public final long getHeight() 798 { 799 if ((selectedDims == null) || (selectedIndex == null)) 800 return 0; 801 802 if ((selectedDims.length < 1) || (selectedIndex.length < 1)) 803 return 0; 804 805 log.trace("getHeight {}", selectedDims[selectedIndex[0]]); 806 return selectedDims[selectedIndex[0]]; 807 } 808 809 /** 810 * Returns the dimension size of the horizontal axis. 811 * 812 * This function is used by GUI applications such as HDFView. GUI 813 * applications display a dataset in 2D Table or 2D Image. The display order is 814 * specified by the index array of selectedIndex as follow: 815 * <dl> 816 * <dt>selectedIndex[0] -- height</dt> 817 * <dd>The vertical axis</dd> 818 * <dt>selectedIndex[1] -- width</dt> 819 * <dd>The horizontal axis</dd> 820 * <dt>selectedIndex[2] -- depth</dt> 821 * <dd>The depth axis, which is used for 3 or more dimension datasets.</dd> 822 * </dl> 823 * Applications can use getSelectedIndex() to access and change the display 824 * order. For example, in a 2D dataset of 200x50 (dim0=200, dim1=50), the 825 * following code will set the height=200 and width=100. 826 * 827 * <pre> 828 * int[] selectedIndex = dataset.getSelectedIndex(); 829 * selectedIndex[0] = 0; 830 * selectedIndex[1] = 1; 831 * </pre> 832 * 833 * @see #getSelectedIndex() 834 * @see #getHeight() 835 * 836 * @return the size of dimension of the horizontal axis. 837 */ 838 @Override 839 public final long getWidth() 840 { 841 if ((selectedDims == null) || (selectedIndex == null)) 842 return 0; 843 844 if ((selectedDims.length < 2) || (selectedIndex.length < 2)) 845 return 1; 846 847 log.trace("getWidth {}", selectedDims[selectedIndex[1]]); 848 return selectedDims[selectedIndex[1]]; 849 } 850 851 /** 852 * Returns the dimension size of the frame axis. 853 * 854 * This function is used by GUI applications such as HDFView. GUI 855 * applications display a dataset in 2D Table or 2D Image. The display order is 856 * specified by the index array of selectedIndex as follow: 857 * <dl> 858 * <dt>selectedIndex[0] -- height</dt> 859 * <dd>The vertical axis</dd> 860 * <dt>selectedIndex[1] -- width</dt> 861 * <dd>The horizontal axis</dd> 862 * <dt>selectedIndex[2] -- depth</dt> 863 * <dd>The depth axis, which is used for 3 or more dimension datasets.</dd> 864 * </dl> 865 * Applications can use getSelectedIndex() to access and change the display 866 * order. For example, in a 2D dataset of 200x50 (dim0=200, dim1=50), the 867 * following code will set the height=200 and width=100. 868 * 869 * <pre> 870 * int[] selectedIndex = dataset.getSelectedIndex(); 871 * selectedIndex[0] = 0; 872 * selectedIndex[1] = 1; 873 * </pre> 874 * 875 * @see #getSelectedIndex() 876 * @see #getHeight() 877 * 878 * @return the size of dimension of the frame axis. 879 */ 880 @Override 881 public final long getDepth() 882 { 883 if ((selectedDims == null) || (selectedIndex == null)) 884 return 0; 885 886 if ((selectedDims.length < 2) || (selectedIndex.length < 2)) 887 return 1; 888 889 log.trace("getDepth {}", selectedDims[selectedIndex[2]]); 890 return selectedDims[selectedIndex[2]]; 891 } 892 893 /** 894 * Returns the indices of display order. 895 * 896 * selectedIndex[] is provided for two purposes: 897 * <OL> 898 * <LI> 899 * selectedIndex[] is used to indicate the order of dimensions for display. 900 * selectedIndex[0] is for the row, selectedIndex[1] is for the column and 901 * selectedIndex[2] for the depth. 902 * 903 * For example, for a four dimension dataset, if selectedIndex[] = {1, 2, 3}, 904 * then dim[1] is selected as row index, dim[2] is selected as column index 905 * and dim[3] is selected as depth index. 906 * <LI> 907 * selectedIndex[] is also used to select dimensions for display for 908 * datasets with three or more dimensions. We assume that applications such 909 * as HDFView can only display data values up to three dimensions (2D 910 * spreadsheet/image with a third dimension which the 2D spreadsheet/image 911 * is selected from). For datasets with more than three dimensions, we need 912 * selectedIndex[] to tell applications which three dimensions are chosen 913 * for display. <br> 914 * For example, for a four dimension dataset, if selectedIndex[] = {1, 2, 3}, 915 * then dim[1] is selected as row index, dim[2] is selected as column index 916 * and dim[3] is selected as depth index. dim[0] is not selected. Its 917 * location is fixed at 0 by default. 918 * </OL> 919 * 920 * @return the array of the indices of display order. 921 */ 922 @Override 923 public final int[] getSelectedIndex() 924 { 925 return selectedIndex; 926 } 927 928 /** 929 * Returns the string representation of compression information. 930 * 931 * For example, 932 * "SZIP: Pixels per block = 8: H5Z_FILTER_CONFIG_DECODE_ENABLED". 933 * 934 * @return the string representation of compression information. 935 */ 936 @Override 937 public final String getCompression() 938 { 939 return compression.toString(); 940 } 941 942 /** 943 * Returns the string representation of filter information. 944 * 945 * @return the string representation of filter information. 946 */ 947 public final String getFilters() { return filters.toString(); } 948 949 /** 950 * Returns the string representation of storage layout information. 951 * 952 * @return the string representation of storage layout information. 953 */ 954 public final String getStorageLayout() { return storageLayout.toString(); } 955 956 /** 957 * Returns the string representation of storage information. 958 * 959 * @return the string representation of storage information. 960 */ 961 public final String getStorage() { return storage.toString(); } 962 963 /** 964 * Returns the array that contains the dimension sizes of the chunk of the 965 * dataset. Returns null if the dataset is not chunked. 966 * 967 * @return the array of chunk sizes or returns null if the dataset is not 968 * chunked. 969 */ 970 public final long[] getChunkSize() { return chunkSize; } 971 972 /** 973 * Returns the datatype of the data object. 974 * 975 * @return the datatype of the data object. 976 */ 977 @Override 978 public Datatype getDatatype() 979 { 980 return datatype; 981 } 982 983 /** 984 * @deprecated Not for public use in the future. <br> 985 * Using {@link #convertFromUnsignedC(Object, Object)} 986 * 987 * @param dataIN the object data 988 * 989 * @return the converted object 990 */ 991 @Deprecated 992 public static Object convertFromUnsignedC(Object dataIN) 993 { 994 return Dataset.convertFromUnsignedC(dataIN, null); 995 } 996 997 /** 998 * Converts one-dimension array of unsigned C-type integers to a new array 999 * of appropriate Java integer in memory. 1000 * 1001 * Since Java does not support unsigned integer, values of unsigned C-type 1002 * integers must be converted into its appropriate Java integer. Otherwise, 1003 * the data value will not displayed correctly. For example, if an unsigned 1004 * C byte, x = 200, is stored into an Java byte y, y will be -56 instead of 1005 * the correct value of 200. 1006 * 1007 * Unsigned C integers are upgrade to Java integers according to the 1008 * following table: 1009 * <table border=1> 1010 * <caption><b>Mapping Unsigned C Integers to Java Integers</b></caption> 1011 * <TR> 1012 * <TD><B>Unsigned C Integer</B></TD> 1013 * <TD><B>JAVA Intege</B>r</TD> 1014 * </TR> 1015 * <TR> 1016 * <TD>unsigned byte</TD> 1017 * <TD>signed short</TD> 1018 * </TR> 1019 * <TR> 1020 * <TD>unsigned short</TD> 1021 * <TD>signed int</TD> 1022 * </TR> 1023 * <TR> 1024 * <TD>unsigned int</TD> 1025 * <TD>signed long</TD> 1026 * </TR> 1027 * <TR> 1028 * <TD>unsigned long</TD> 1029 * <TD>signed long</TD> 1030 * </TR> 1031 * </TABLE> 1032 * <strong>NOTE: this conversion cannot deal with unsigned 64-bit integers. 1033 * Therefore, the values of unsigned 64-bit datasets may be wrong in Java 1034 * applications</strong>. 1035 * 1036 * If memory data of unsigned integers is converted by 1037 * convertFromUnsignedC(), convertToUnsignedC() must be called to convert 1038 * the data back to unsigned C before data is written into file. 1039 * 1040 * @see #convertToUnsignedC(Object, Object) 1041 * 1042 * @param dataIN 1043 * the input 1D array of the unsigned C-type integers. 1044 * @param dataOUT 1045 * the output converted (or upgraded) 1D array of Java integers. 1046 * 1047 * @return the upgraded 1D array of Java integers. 1048 */ 1049 @SuppressWarnings("rawtypes") 1050 public static Object convertFromUnsignedC(Object dataIN, Object dataOUT) 1051 { 1052 if (dataIN == null) { 1053 log.debug("convertFromUnsignedC(): data_in is null"); 1054 return null; 1055 } 1056 1057 Class dataClass = dataIN.getClass(); 1058 if (!dataClass.isArray()) { 1059 log.debug("convertFromUnsignedC(): data_in not an array"); 1060 return null; 1061 } 1062 1063 if (dataOUT != null) { 1064 Class dataClassOut = dataOUT.getClass(); 1065 if (!dataClassOut.isArray() || (Array.getLength(dataIN) != Array.getLength(dataOUT))) { 1066 log.debug("convertFromUnsignedC(): data_out not an array or does not match data_in size"); 1067 dataOUT = null; 1068 } 1069 } 1070 1071 String cname = dataClass.getName(); 1072 char dname = cname.charAt(cname.lastIndexOf('[') + 1); 1073 int size = Array.getLength(dataIN); 1074 log.trace("convertFromUnsignedC(): cname={} dname={} size={}", cname, dname, size); 1075 1076 if (dname == 'B') { 1077 log.trace("convertFromUnsignedC(): Java convert byte to short"); 1078 short[] sdata = null; 1079 if (dataOUT == null) 1080 sdata = new short[size]; 1081 else 1082 sdata = (short[])dataOUT; 1083 1084 byte[] bdata = (byte[])dataIN; 1085 for (int i = 0; i < size; i++) 1086 sdata[i] = (short)((bdata[i] + 256) & 0xFF); 1087 1088 dataOUT = sdata; 1089 } 1090 else if (dname == 'S') { 1091 log.trace("convertFromUnsignedC(): Java convert short to int"); 1092 int[] idata = null; 1093 if (dataOUT == null) 1094 idata = new int[size]; 1095 else 1096 idata = (int[])dataOUT; 1097 1098 short[] sdata = (short[])dataIN; 1099 for (int i = 0; i < size; i++) 1100 idata[i] = (sdata[i] + 65536) & 0xFFFF; 1101 1102 dataOUT = idata; 1103 } 1104 else if (dname == 'I') { 1105 log.trace("convertFromUnsignedC(): Java convert int to long"); 1106 long[] ldata = null; 1107 if (dataOUT == null) 1108 ldata = new long[size]; 1109 else 1110 ldata = (long[])dataOUT; 1111 1112 int[] idata = (int[])dataIN; 1113 for (int i = 0; i < size; i++) 1114 ldata[i] = (idata[i] + 4294967296L) & 0xFFFFFFFFL; 1115 1116 dataOUT = ldata; 1117 } 1118 else { 1119 dataOUT = dataIN; 1120 log.debug("convertFromUnsignedC(): Java does not support unsigned long"); 1121 } 1122 1123 return dataOUT; 1124 } 1125 1126 /** 1127 * @deprecated Not for public use in the future. <br> 1128 * Using {@link #convertToUnsignedC(Object, Object)} 1129 * 1130 * @param dataIN 1131 * the input 1D array of the unsigned C-type integers. 1132 * 1133 * @return the upgraded 1D array of Java integers. 1134 */ 1135 @Deprecated 1136 public static Object convertToUnsignedC(Object dataIN) 1137 { 1138 return Dataset.convertToUnsignedC(dataIN, null); 1139 } 1140 1141 /** 1142 * Converts the array of converted unsigned integers back to unsigned C-type 1143 * integer data in memory. 1144 * 1145 * If memory data of unsigned integers is converted by 1146 * convertFromUnsignedC(), convertToUnsignedC() must be called to convert 1147 * the data back to unsigned C before data is written into file. 1148 * 1149 * @see #convertFromUnsignedC(Object, Object) 1150 * 1151 * @param dataIN 1152 * the input array of the Java integer. 1153 * @param dataOUT 1154 * the output array of the unsigned C-type integer. 1155 * 1156 * @return the converted data of unsigned C-type integer array. 1157 */ 1158 @SuppressWarnings("rawtypes") 1159 public static Object convertToUnsignedC(Object dataIN, Object dataOUT) 1160 { 1161 if (dataIN == null) { 1162 log.debug("convertToUnsignedC(): data_in is null"); 1163 return null; 1164 } 1165 1166 Class dataClass = dataIN.getClass(); 1167 if (!dataClass.isArray()) { 1168 log.debug("convertToUnsignedC(): data_in not an array"); 1169 return null; 1170 } 1171 1172 if (dataOUT != null) { 1173 Class dataClassOut = dataOUT.getClass(); 1174 if (!dataClassOut.isArray() || (Array.getLength(dataIN) != Array.getLength(dataOUT))) { 1175 log.debug("convertToUnsignedC(): data_out not an array or does not match data_in size"); 1176 dataOUT = null; 1177 } 1178 } 1179 1180 String cname = dataClass.getName(); 1181 char dname = cname.charAt(cname.lastIndexOf('[') + 1); 1182 int size = Array.getLength(dataIN); 1183 log.trace("convertToUnsignedC(): cname={} dname={} size={}", cname, dname, size); 1184 1185 if (dname == 'S') { 1186 log.trace("convertToUnsignedC(): Java convert short to byte"); 1187 byte[] bdata = null; 1188 if (dataOUT == null) 1189 bdata = new byte[size]; 1190 else 1191 bdata = (byte[])dataOUT; 1192 short[] sdata = (short[])dataIN; 1193 for (int i = 0; i < size; i++) 1194 bdata[i] = (byte)sdata[i]; 1195 dataOUT = bdata; 1196 } 1197 else if (dname == 'I') { 1198 log.trace("convertToUnsignedC(): Java convert int to short"); 1199 short[] sdata = null; 1200 if (dataOUT == null) 1201 sdata = new short[size]; 1202 else 1203 sdata = (short[])dataOUT; 1204 int[] idata = (int[])dataIN; 1205 for (int i = 0; i < size; i++) 1206 sdata[i] = (short)idata[i]; 1207 dataOUT = sdata; 1208 } 1209 else if (dname == 'J') { 1210 log.trace("convertToUnsignedC(): Java convert long to int"); 1211 int[] idata = null; 1212 if (dataOUT == null) 1213 idata = new int[size]; 1214 else 1215 idata = (int[])dataOUT; 1216 long[] ldata = (long[])dataIN; 1217 for (int i = 0; i < size; i++) 1218 idata[i] = (int)ldata[i]; 1219 dataOUT = idata; 1220 } 1221 else { 1222 dataOUT = dataIN; 1223 log.debug("convertToUnsignedC(): Java does not support unsigned long"); 1224 } 1225 1226 return dataOUT; 1227 } 1228 1229 /** 1230 * Converts an array of bytes into an array of Strings for a fixed string 1231 * dataset. 1232 * 1233 * A C-string is an array of chars while an Java String is an object. When a 1234 * string dataset is read into a Java application, the data is stored in an 1235 * array of Java bytes. byteToString() is used to convert the array of bytes 1236 * into an array of Java strings so that applications can display and modify 1237 * the data content. 1238 * 1239 * For example, the content of a two element C string dataset is {"ABC", 1240 * "abc"}. Java applications will read the data into a byte array of {65, 1241 * 66, 67, 97, 98, 99). byteToString(bytes, 3) returns an array of Java 1242 * String of strs[0]="ABC", and strs[1]="abc". 1243 * 1244 * If memory data of strings is converted to Java Strings, stringToByte() 1245 * must be called to convert the memory data back to byte array before data 1246 * is written to file. 1247 * 1248 * @see #stringToByte(String[], int) 1249 * 1250 * @param bytes 1251 * the array of bytes to convert. 1252 * @param length 1253 * the length of string. 1254 * 1255 * @return the array of Java String. 1256 */ 1257 public static final String[] byteToString(byte[] bytes, int length) 1258 { 1259 if (bytes == null) { 1260 log.debug("byteToString(): input is null"); 1261 return null; 1262 } 1263 1264 int n = bytes.length / length; 1265 log.trace("byteToString(): n={} from length of {}", n, length); 1266 String[] strArray = new String[n]; 1267 String str = null; 1268 int idx = 0; 1269 for (int i = 0; i < n; i++) { 1270 str = new String(bytes, i * length, length); 1271 idx = str.indexOf('\0'); 1272 if (idx >= 0) 1273 str = str.substring(0, idx); 1274 1275 // trim only the end 1276 int end = str.length(); 1277 while (end > 0 && str.charAt(end - 1) <= '\u0020') 1278 end--; 1279 1280 strArray[i] = (end <= 0) ? "" : str.substring(0, end); 1281 } 1282 1283 return strArray; 1284 } 1285 1286 /** 1287 * Converts a string array into an array of bytes for a fixed string 1288 * dataset. 1289 * 1290 * If memory data of strings is converted to Java Strings, stringToByte() 1291 * must be called to convert the memory data back to byte array before data 1292 * is written to file. 1293 * 1294 * @see #byteToString(byte[] bytes, int length) 1295 * 1296 * @param strings 1297 * the array of string. 1298 * @param length 1299 * the length of string. 1300 * 1301 * @return the array of bytes. 1302 */ 1303 public static final byte[] stringToByte(String[] strings, int length) 1304 { 1305 if (strings == null) { 1306 log.debug("stringToByte(): input is null"); 1307 return null; 1308 } 1309 1310 int size = strings.length; 1311 byte[] bytes = new byte[size * length]; 1312 log.trace("stringToByte(): size={} length={}", size, length); 1313 StringBuilder strBuff = new StringBuilder(length); 1314 for (int i = 0; i < size; i++) { 1315 // initialize the string with spaces 1316 strBuff.replace(0, length, " "); 1317 1318 if (strings[i] != null) { 1319 if (strings[i].length() > length) 1320 strings[i] = strings[i].substring(0, length); 1321 strBuff.replace(0, length, strings[i]); 1322 } 1323 1324 strBuff.setLength(length); 1325 System.arraycopy(strBuff.toString().getBytes(), 0, bytes, length * i, length); 1326 } 1327 1328 return bytes; 1329 } 1330 1331 /** 1332 * Returns the array of strings that represent the dimension names. Returns 1333 * null if there is no dimension name. 1334 * 1335 * Some datasets have pre-defined names for each dimension such as 1336 * "Latitude" and "Longitude". getDimNames() returns these pre-defined 1337 * names. 1338 * 1339 * @return the names of dimensions, or null if there is no dimension name. 1340 */ 1341 public final String[] getDimNames() { return dimNames; } 1342 1343 /** 1344 * Checks if a given datatype is a string. Sub-classes must replace this 1345 * default implementation. 1346 * 1347 * @param tid 1348 * The data type identifier. 1349 * 1350 * @return true if the datatype is a string; otherwise returns false. 1351 */ 1352 public boolean isString(long tid) { return false; } 1353 1354 /** 1355 * Returns the size in bytes of a given datatype. Sub-classes must replace 1356 * this default implementation. 1357 * 1358 * @param tid 1359 * The data type identifier. 1360 * 1361 * @return The size of the datatype 1362 */ 1363 public long getSize(long tid) { return -1; } 1364 1365 /** 1366 * Get Class of the original data buffer if converted. 1367 * 1368 * @return the Class of originalBuf 1369 */ 1370 @Override 1371 @SuppressWarnings("rawtypes") 1372 public final Class getOriginalClass() 1373 { 1374 return originalBuf.getClass(); 1375 } 1376 1377 /** 1378 * Check if dataset's dataspace is a NULL 1379 * 1380 * @return true if the dataspace is a NULL; otherwise, returns false. 1381 */ 1382 public boolean isNULL() { return isNULL; } 1383 1384 /** 1385 * Check if dataset is a single scalar point 1386 * 1387 * @return true if the data is a single scalar point; otherwise, returns false. 1388 */ 1389 public boolean isScalar() { return isScalar; } 1390 1391 /** 1392 * Checks if dataset is virtual. Sub-classes must replace 1393 * this default implementation. 1394 * 1395 * @return true if the dataset is virtual; otherwise returns false. 1396 */ 1397 public boolean isVirtual() { return false; } 1398 1399 /** 1400 * Gets the source file name at index if dataset is virtual. Sub-classes must replace 1401 * this default implementation. 1402 * 1403 * @param index 1404 * index of the source file name if dataset is virtual. 1405 * 1406 * @return filename if the dataset is virtual; otherwise returns null. 1407 */ 1408 public String getVirtualFilename(int index) { return null; } 1409 1410 /** 1411 * Gets the number of source files if dataset is virtual. Sub-classes must replace 1412 * this default implementation. 1413 * 1414 * @return the list size if the dataset is virtual; otherwise returns negative. 1415 */ 1416 public int getVirtualMaps() { return -1; } 1417 1418 /** 1419 * Returns a string representation of the data value. For 1420 * example, "0, 255". 1421 * 1422 * For a compound datatype, it will be a 1D array of strings with field 1423 * members separated by the delimiter. For example, 1424 * "{0, 10.5}, {255, 20.0}, {512, 30.0}" is a compound attribute of {int, 1425 * float} of three data points. 1426 * 1427 * @param delimiter 1428 * The delimiter used to separate individual data points. It 1429 * can be a comma, semicolon, tab or space. For example, 1430 * toString(",") will separate data by commas. 1431 * 1432 * @return the string representation of the data values. 1433 */ 1434 public String toString(String delimiter) { return toString(delimiter, -1); } 1435 1436 /** 1437 * Returns a string representation of the data value. For 1438 * example, "0, 255". 1439 * 1440 * For a compound datatype, it will be a 1D array of strings with field 1441 * members separated by the delimiter. For example, 1442 * "{0, 10.5}, {255, 20.0}, {512, 30.0}" is a compound attribute of {int, 1443 * float} of three data points. 1444 * 1445 * @param delimiter 1446 * The delimiter used to separate individual data points. It 1447 * can be a comma, semicolon, tab or space. For example, 1448 * toString(",") will separate data by commas. 1449 * @param maxItems 1450 * The maximum number of Array values to return 1451 * 1452 * @return the string representation of the data values. 1453 */ 1454 public String toString(String delimiter, int maxItems) 1455 { 1456 Object theData = originalBuf; 1457 if (theData == null) { 1458 log.debug("toString: value is null"); 1459 return null; 1460 } 1461 1462 if (theData instanceof List<?>) { 1463 log.trace("toString: value is list"); 1464 return null; 1465 } 1466 1467 Class<? extends Object> valClass = theData.getClass(); 1468 1469 if (!valClass.isArray()) { 1470 log.trace("toString: finish - not array"); 1471 String strValue = theData.toString(); 1472 if (maxItems > 0 && strValue.length() > maxItems) 1473 // truncate the extra characters 1474 strValue = strValue.substring(0, maxItems); 1475 return strValue; 1476 } 1477 1478 // value is an array 1479 int n = Array.getLength(theData); 1480 if ((maxItems > 0) && (n > maxItems)) 1481 n = maxItems; 1482 1483 return toString(theData, getDatatype(), delimiter, n); 1484 } 1485 1486 /** 1487 * Returns a string representation of the dataset object. 1488 * 1489 * @param theData The Object data 1490 * @param theType The type of the data in the Object 1491 * @param delimiter The delimiter used to separate individual data points. It can be a comma, semicolon, 1492 * tab or 1493 * space. For example, toString(",") will separate data by commas. 1494 * @param count The maximum number of Array values to return 1495 * 1496 * @return the string representation of the dataset object. 1497 */ 1498 protected String toString(Object theData, Datatype theType, String delimiter, int count) 1499 { 1500 log.trace("toString: is_enum={} is_unsigned={} Array.getLength={}", theType.isEnum(), 1501 theType.isUnsigned(), count); 1502 StringBuilder sb = new StringBuilder(); 1503 Class<? extends Object> valClass = theData.getClass(); 1504 1505 if (theType.isEnum()) { 1506 String cname = valClass.getName(); 1507 char dname = cname.charAt(cname.lastIndexOf('[') + 1); 1508 log.trace("toString: is_enum with cname={} dname={}", cname, dname); 1509 1510 Map<String, String> map = theType.getEnumMembers(); 1511 String theValue = null; 1512 switch (dname) { 1513 case 'B': 1514 byte[] barray = (byte[])theData; 1515 short sValue = barray[0]; 1516 theValue = String.valueOf(sValue); 1517 if (map.containsKey(theValue)) 1518 sb.append(map.get(theValue)); 1519 else 1520 sb.append(sValue); 1521 for (int i = 1; i < count; i++) { 1522 sb.append(delimiter); 1523 sValue = barray[i]; 1524 theValue = String.valueOf(sValue); 1525 if (map.containsKey(theValue)) 1526 sb.append(map.get(theValue)); 1527 else 1528 sb.append(sValue); 1529 } 1530 break; 1531 case 'S': 1532 short[] sarray = (short[])theData; 1533 int iValue = sarray[0]; 1534 theValue = String.valueOf(iValue); 1535 if (map.containsKey(theValue)) 1536 sb.append(map.get(theValue)); 1537 else 1538 sb.append(iValue); 1539 for (int i = 1; i < count; i++) { 1540 sb.append(delimiter); 1541 iValue = sarray[i]; 1542 theValue = String.valueOf(iValue); 1543 if (map.containsKey(theValue)) 1544 sb.append(map.get(theValue)); 1545 else 1546 sb.append(iValue); 1547 } 1548 break; 1549 case 'I': 1550 int[] iarray = (int[])theData; 1551 long lValue = iarray[0]; 1552 theValue = String.valueOf(lValue); 1553 if (map.containsKey(theValue)) 1554 sb.append(map.get(theValue)); 1555 else 1556 sb.append(lValue); 1557 for (int i = 1; i < count; i++) { 1558 sb.append(delimiter); 1559 lValue = iarray[i]; 1560 theValue = String.valueOf(lValue); 1561 if (map.containsKey(theValue)) 1562 sb.append(map.get(theValue)); 1563 else 1564 sb.append(lValue); 1565 } 1566 break; 1567 case 'J': 1568 long[] larray = (long[])theData; 1569 Long l = larray[0]; 1570 theValue = Long.toString(l); 1571 if (map.containsKey(theValue)) 1572 sb.append(map.get(theValue)); 1573 else 1574 sb.append(theValue); 1575 for (int i = 1; i < count; i++) { 1576 sb.append(delimiter); 1577 l = larray[i]; 1578 theValue = Long.toString(l); 1579 if (map.containsKey(theValue)) 1580 sb.append(map.get(theValue)); 1581 else 1582 sb.append(theValue); 1583 } 1584 break; 1585 default: 1586 sb.append(Array.get(theData, 0)); 1587 for (int i = 1; i < count; i++) { 1588 sb.append(delimiter); 1589 sb.append(Array.get(theData, i)); 1590 } 1591 break; 1592 } 1593 } 1594 else if (theType.isFloat() && theType.getDatatypeSize() == 2) { 1595 Object value = Array.get(theData, 0); 1596 String strValue; 1597 1598 if (value == null) 1599 strValue = "null"; 1600 else 1601 strValue = Float.toString(Float.float16ToFloat((short)value)); 1602 1603 // if (count > 0 && strValue.length() > count) 1604 // truncate the extra characters 1605 // strValue = strValue.substring(0, count); 1606 sb.append(strValue); 1607 1608 for (int i = 1; i < count; i++) { 1609 sb.append(delimiter); 1610 value = Array.get(theData, i); 1611 1612 if (value == null) 1613 strValue = "null"; 1614 else 1615 strValue = Float.toString(Float.float16ToFloat((short)value)); 1616 1617 if (count > 0 && strValue.length() > count) 1618 // truncate the extra characters 1619 strValue = strValue.substring(0, count); 1620 sb.append(strValue); 1621 } 1622 } 1623 else if (theType.isUnsigned()) { 1624 String cname = valClass.getName(); 1625 char dname = cname.charAt(cname.lastIndexOf('[') + 1); 1626 log.trace("toString: is_unsigned with cname={} dname={}", cname, dname); 1627 1628 switch (dname) { 1629 case 'B': 1630 byte[] barray = (byte[])theData; 1631 short sValue = barray[0]; 1632 if (sValue < 0) 1633 sValue += 256; 1634 sb.append(sValue); 1635 for (int i = 1; i < count; i++) { 1636 sb.append(delimiter); 1637 sValue = barray[i]; 1638 if (sValue < 0) 1639 sValue += 256; 1640 sb.append(sValue); 1641 } 1642 break; 1643 case 'S': 1644 short[] sarray = (short[])theData; 1645 int iValue = sarray[0]; 1646 if (iValue < 0) 1647 iValue += 65536; 1648 sb.append(iValue); 1649 for (int i = 1; i < count; i++) { 1650 sb.append(delimiter); 1651 iValue = sarray[i]; 1652 if (iValue < 0) 1653 iValue += 65536; 1654 sb.append(iValue); 1655 } 1656 break; 1657 case 'I': 1658 int[] iarray = (int[])theData; 1659 long lValue = iarray[0]; 1660 if (lValue < 0) 1661 lValue += 4294967296L; 1662 sb.append(lValue); 1663 for (int i = 1; i < count; i++) { 1664 sb.append(delimiter); 1665 lValue = iarray[i]; 1666 if (lValue < 0) 1667 lValue += 4294967296L; 1668 sb.append(lValue); 1669 } 1670 break; 1671 case 'J': 1672 long[] larray = (long[])theData; 1673 Long l = larray[0]; 1674 String theValue = Long.toString(l); 1675 if (l < 0) { 1676 l = (l << 1) >>> 1; 1677 BigInteger big1 = new BigInteger("9223372036854775808"); // 2^65 1678 BigInteger big2 = new BigInteger(l.toString()); 1679 BigInteger big = big1.add(big2); 1680 theValue = big.toString(); 1681 } 1682 sb.append(theValue); 1683 for (int i = 1; i < count; i++) { 1684 sb.append(delimiter); 1685 l = larray[i]; 1686 theValue = Long.toString(l); 1687 if (l < 0) { 1688 l = (l << 1) >>> 1; 1689 BigInteger big1 = new BigInteger("9223372036854775808"); // 2^65 1690 BigInteger big2 = new BigInteger(l.toString()); 1691 BigInteger big = big1.add(big2); 1692 theValue = big.toString(); 1693 } 1694 sb.append(theValue); 1695 } 1696 break; 1697 default: 1698 String strValue = Array.get(theData, 0).toString(); 1699 if (count > 0 && strValue.length() > count) 1700 // truncate the extra characters 1701 strValue = strValue.substring(0, count); 1702 sb.append(strValue); 1703 for (int i = 1; i < count; i++) { 1704 sb.append(delimiter); 1705 strValue = Array.get(theData, i).toString(); 1706 if (count > 0 && strValue.length() > count) 1707 // truncate the extra characters 1708 strValue = strValue.substring(0, count); 1709 sb.append(strValue); 1710 } 1711 break; 1712 } 1713 } 1714 else if (theType.isVLEN() && !theType.isVarStr()) { 1715 log.trace("toString: vlen"); 1716 String strValue; 1717 1718 Object value = Array.get(theData, 0); 1719 if (value == null) 1720 strValue = "null"; 1721 else { 1722 if (theType.getDatatypeBase().isRef()) { 1723 if (theType.getDatatypeBase().getDatatypeSize() > 8) 1724 strValue = "Region Reference"; 1725 else 1726 strValue = "Object Reference"; 1727 } 1728 else 1729 strValue = value.toString(); 1730 } 1731 sb.append(strValue); 1732 } 1733 else { 1734 log.trace("toString: not enum or unsigned"); 1735 Object value = Array.get(theData, 0); 1736 String strValue; 1737 1738 if (value == null) 1739 strValue = "null"; 1740 else 1741 strValue = value.toString(); 1742 1743 // if (count > 0 && strValue.length() > count) 1744 // truncate the extra characters 1745 // strValue = strValue.substring(0, count); 1746 sb.append(strValue); 1747 1748 for (int i = 1; i < count; i++) { 1749 sb.append(delimiter); 1750 value = Array.get(theData, i); 1751 1752 if (value == null) 1753 strValue = "null"; 1754 else 1755 strValue = value.toString(); 1756 1757 if (count > 0 && strValue.length() > count) 1758 // truncate the extra characters 1759 strValue = strValue.substring(0, count); 1760 sb.append(strValue); 1761 } 1762 } 1763 1764 return sb.toString(); 1765 } 1766}