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