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