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