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