001/***************************************************************************** 002 * Copyright by The HDF Group. * 003 * All rights reserved. * 004 * * 005 * This file is part of the HDF Java Products distribution. * 006 * The full copyright notice, including terms governing use, modification, * 007 * and redistribution, is contained in the COPYING file, which can be found * 008 * at the root of the source code distribution tree, * 009 * or in https://www.hdfgroup.org/licenses. * 010 * If you do not have access to either file, you may request a copy from * 011 * help@hdfgroup.org. * 012 ****************************************************************************/ 013 014package hdf.view.TableView; 015 016import java.lang.reflect.Array; 017import java.math.BigInteger; 018import java.util.ArrayList; 019import java.util.Arrays; 020import java.util.Collection; 021import java.util.HashMap; 022import java.util.Iterator; 023import java.util.List; 024import java.util.Map; 025import java.util.StringTokenizer; 026import java.util.Vector; 027 028import hdf.object.CompoundDataFormat; 029import hdf.object.DataFormat; 030import hdf.object.Datatype; 031import hdf.object.FileFormat; 032import hdf.object.HObject; 033import hdf.object.Utils; 034import hdf.object.h5.H5Datatype; 035import hdf.object.h5.H5ReferenceType; 036import hdf.view.Tools; 037 038import hdf.hdf5lib.H5; 039import hdf.hdf5lib.HDF5Constants; 040 041import org.slf4j.Logger; 042import org.slf4j.LoggerFactory; 043 044import org.eclipse.nebula.widgets.nattable.data.IDataProvider; 045 046/** 047 * A Factory class to return a concrete class implementing the IDataProvider 048 * interface in order to provide data for a NatTable. 049 * 050 * @author Jordan T. Henderson 051 * @version 1.0 2/9/2019 052 */ 053public class DataProviderFactory { 054 private static final Logger log = LoggerFactory.getLogger(DataProviderFactory.class); 055 056 /** 057 * To keep things clean from an API perspective, keep a static reference to the last 058 * DataFormat that was passed in. This keeps us from needing to pass the DataFormat 059 * object as a parameter to every DataProvider class, since it's really only needed 060 * during the HDFDataProvider constructor. 061 */ 062 private static DataFormat dataFormatReference = null; 063 064 /** 065 * Get the Data Display Provider for the supplied data object 066 * 067 * @param dataObject 068 * the data object 069 * @param dataBuf 070 * the data buffer to use 071 * @param dataTransposed 072 * if the data should be transposed 073 * 074 * @return the provider instance 075 * 076 * @throws Exception if a failure occurred 077 */ 078 public static HDFDataProvider getDataProvider(final DataFormat dataObject, final Object dataBuf, 079 final boolean dataTransposed) throws Exception 080 { 081 if (dataObject == null) { 082 log.debug("getDataProvider(DataFormat): data object is null"); 083 return null; 084 } 085 086 dataFormatReference = dataObject; 087 088 HDFDataProvider dataProvider = getDataProvider(dataObject.getDatatype(), dataBuf, dataTransposed); 089 090 return dataProvider; 091 } 092 093 private static final HDFDataProvider getDataProvider(final Datatype dtype, final Object dataBuf, 094 final boolean dataTransposed) throws Exception 095 { 096 HDFDataProvider dataProvider = null; 097 log.debug("getDataProvider(): Datatype is {}", dtype.getDescription()); 098 099 try { 100 if (dtype.isCompound()) 101 dataProvider = new CompoundDataProvider(dtype, dataBuf, dataTransposed); 102 else if (dtype.isArray()) 103 dataProvider = new ArrayDataProvider(dtype, dataBuf, dataTransposed); 104 else if (dtype.isVLEN() && !dtype.isVarStr()) 105 dataProvider = new VlenDataProvider(dtype, dataBuf, dataTransposed); 106 else if (dtype.isString() || dtype.isVarStr()) 107 dataProvider = new StringDataProvider(dtype, dataBuf, dataTransposed); 108 else if (dtype.isChar()) 109 dataProvider = new CharDataProvider(dtype, dataBuf, dataTransposed); 110 else if (dtype.isInteger() || dtype.isFloat()) 111 dataProvider = new NumericalDataProvider(dtype, dataBuf, dataTransposed); 112 else if (dtype.isEnum()) 113 dataProvider = new EnumDataProvider(dtype, dataBuf, dataTransposed); 114 else if (dtype.isOpaque() || dtype.isBitField()) 115 dataProvider = new BitfieldDataProvider(dtype, dataBuf, dataTransposed); 116 else if (dtype.isRef()) 117 dataProvider = new RefDataProvider(dtype, dataBuf, dataTransposed); 118 } 119 catch (Exception ex) { 120 log.debug("getDataProvider(): error occurred in retrieving a DataProvider: ", ex); 121 dataProvider = null; 122 } 123 124 /* 125 * Try to use a default DataProvider. 126 */ 127 if (dataProvider == null) { 128 log.debug("getDataProvider(): using a default data provider"); 129 130 dataProvider = new HDFDataProvider(dtype, dataBuf, dataTransposed); 131 } 132 133 return dataProvider; 134 } 135 136 /** 137 * The base DataProvider which pulls data from a given Array object using direct 138 * indices. 139 */ 140 public static class HDFDataProvider implements IDataProvider { 141 private static final Logger log = LoggerFactory.getLogger(HDFDataProvider.class); 142 143 /** 144 * In order to support 3-dimensional datasets, which may need to update the data 145 * buffer object after flipping through a 'page', this field is not marked as 146 * final. However, it is important that subclasses DO NOT override this field. 147 */ 148 protected Object dataBuf; 149 150 /** the data value */ 151 protected Object theValue; 152 153 /** the data format class */ 154 protected final Class originalFormatClass; 155 156 /** if the data value has changed */ 157 protected boolean isValueChanged; 158 159 /** the type of the parent */ 160 protected final boolean isContainerType; 161 162 /** the rank */ 163 protected final int rank; 164 165 /** if the data is in original order */ 166 protected final boolean isNaturalOrder; 167 /** if the data is transposed */ 168 protected final boolean isDataTransposed; 169 170 /** the column */ 171 protected long colCount; 172 /** the row */ 173 protected long rowCount; 174 175 /** 176 * Create the HDF extended Data Display Provider for the supplied data object 177 * 178 * @param dtype 179 * the datatype object 180 * @param dataBuf 181 * the data buffer to use 182 * @param dataTransposed 183 * if the data should be transposed 184 * 185 * @throws Exception if a failure occurred 186 */ 187 HDFDataProvider(final Datatype dtype, final Object dataBuf, final boolean dataTransposed) 188 throws Exception 189 { 190 this.dataBuf = dataBuf; 191 192 this.originalFormatClass = dataFormatReference.getOriginalClass(); 193 194 char runtimeTypeClass = Utils.getJavaObjectRuntimeClass(dataBuf); 195 if (runtimeTypeClass == ' ') { 196 log.debug("invalid data value runtime type class: runtimeTypeClass={}", runtimeTypeClass); 197 throw new IllegalStateException("Invalid data value runtime type class: " + runtimeTypeClass); 198 } 199 200 rank = dataFormatReference.getRank(); 201 202 isNaturalOrder = ((rank == 1) || (dataFormatReference.getSelectedIndex()[0] < 203 dataFormatReference.getSelectedIndex()[1])); 204 isDataTransposed = dataTransposed; 205 206 if (rank > 1) { 207 rowCount = dataFormatReference.getHeight(); 208 colCount = dataFormatReference.getWidth(); 209 } 210 else { 211 rowCount = (int)dataFormatReference.getSelectedDims()[0]; 212 colCount = 1; 213 } 214 log.trace("constructor:class={} rowCount={} colCount={}", runtimeTypeClass, rowCount, colCount); 215 216 theValue = null; 217 isValueChanged = false; 218 219 isContainerType = (this instanceof CompoundDataProvider || this instanceof ArrayDataProvider || 220 this instanceof VlenDataProvider); 221 } 222 223 /** 224 * A utility method used to translate a set of physical table coordinates to an 225 * index into a data buffer. 226 * 227 * @param rowIndex 228 * the row 229 * @param columnIndex 230 * the column 231 * 232 * @return physical location in 1D notation 233 */ 234 public int physicalLocationToBufIndex(int rowIndex, int columnIndex) 235 { 236 long index = rowIndex * colCount + columnIndex; 237 238 if (rank > 1) { 239 log.trace( 240 "physicalLocationToBufIndex({}, {}): rank > 1; adjusting for multi-dimensional dataset", 241 rowIndex, columnIndex); 242 243 if (isDataTransposed && isNaturalOrder) 244 index = columnIndex * rowCount + rowIndex; 245 else if (!isDataTransposed && !isNaturalOrder) 246 // Reshape Data 247 index = rowIndex * colCount + columnIndex; 248 else if (isDataTransposed && !isNaturalOrder) 249 // Transpose Data 250 index = columnIndex * rowCount + rowIndex; 251 else 252 index = rowIndex * colCount + columnIndex; 253 } 254 255 log.trace("physicalLocationToBufIndex({}, {}, {}): finish", rowIndex, columnIndex, index); 256 257 return (int)index; 258 } 259 260 @Override 261 public Object getDataValue(int columnIndex, int rowIndex) 262 { 263 try { 264 int bufIndex = physicalLocationToBufIndex(rowIndex, columnIndex); 265 if (dataBuf instanceof ArrayList) 266 theValue = ((ArrayList)dataBuf).get(bufIndex); 267 else 268 theValue = Array.get(dataBuf, bufIndex); 269 } 270 catch (Exception ex) { 271 log.debug("getDataValue({}, {}): failure: ", rowIndex, columnIndex, ex); 272 theValue = DataFactoryUtils.errStr; 273 } 274 275 log.trace("getDataValue({}, {})=({}): finish", rowIndex, columnIndex, theValue); 276 277 return theValue; 278 } 279 280 /** 281 * When a CompoundDataProvider wants to pass a List of data down to a nested 282 * CompoundDataProvider, or when a top-level container DataProvider (such as an 283 * ArrayDataProvider) wants to hand data down to a base CompoundDataProvider, we 284 * need to pass down a List of data, plus a field and row index. This method is 285 * for facilitating this behavior. 286 * 287 * In general, all "container" DataProviders that have a "container" base 288 * DataProvider should call down into their base DataProvider(s) using this 289 * method, in order to ensure that buried CompoundDataProviders get handled 290 * correctly. When their base DataProvider is not a "container" type, the method 291 * getDataValue(Object, index) should be used instead. 292 * 293 * For atomic type DataProviders, we treat this method as directly calling into 294 * getDataValue(Object, index) using the passed rowIndex. However, this method 295 * should, in general, not be called by atomic type DataProviders. 296 * 297 * @param obj 298 * the data object 299 * @param rowIndex 300 * the row 301 * @param columnIndex 302 * the column 303 * 304 * @return value of the data 305 */ 306 public Object getDataValue(Object obj, int columnIndex, int rowIndex) 307 { 308 return getDataValue(obj, rowIndex); 309 } 310 311 /** 312 * When a parent HDFDataProvider (such as an ArrayDataProvider) wants to 313 * retrieve a data value by routing the operation through its base 314 * HDFDataProvider, the parent HDFDataProvider will generally know the direct 315 * index to have the base provider use. This method is to facilitate this kind 316 * of behavior. 317 * 318 * Note that this method takes an Object parameter, which is the object that the 319 * method should pull its data from. This is to be able to nicely support nested 320 * compound DataProviders. 321 * 322 * @param obj 323 * the data object 324 * @param index 325 * the index into the data array 326 * 327 * @return the data object 328 */ 329 public Object getDataValue(Object obj, int index) 330 { 331 try { 332 if (obj instanceof ArrayList) 333 theValue = ((ArrayList)obj).get(index); 334 else 335 theValue = Array.get(obj, index); 336 } 337 catch (Exception ex) { 338 log.debug("getDataValue({}): failure: ", index, ex); 339 theValue = DataFactoryUtils.errStr; 340 } 341 342 log.trace("getDataValue({})=({}): finish", index, theValue); 343 344 return theValue; 345 } 346 347 /** 348 * update the data value of a compound type. 349 * 350 * @param columnIndex 351 * the column 352 * @param rowIndex 353 * the row 354 * @param newValue 355 * the new data value object 356 */ 357 @Override 358 public void setDataValue(int columnIndex, int rowIndex, Object newValue) 359 { 360 try { 361 int bufIndex = physicalLocationToBufIndex(rowIndex, columnIndex); 362 363 updateAtomicValue(dataBuf, newValue, bufIndex); 364 } 365 catch (Exception ex) { 366 log.debug("setDataValue({}, {})=({}): cell value update failure: ", rowIndex, columnIndex, 367 newValue, ex); 368 } 369 log.trace("setDataValue({}, {})=({}): finish", rowIndex, columnIndex, newValue); 370 371 /* 372 * TODO: throwing error dialogs when something fails? 373 * 374 * Tools.showError(shell, "Select", "Unable to set new value:\n\n " + ex); 375 */ 376 } 377 378 /** 379 * When a CompoundDataProvider wants to pass a List of data down to a nested CompoundDataProvider, or 380 * when a top-level container DataProvider (such as an ArrayDataProvider) wants to hand data down to a 381 * base CompoundDataProvider, we need to pass down a List of data and the new value, plus a field and 382 * row index. This method is for facilitating this behavior. 383 * 384 * In general, all "container" DataProviders that have a "container" base DataProvider should call 385 * down into their base DataProvider(s) using this, method, in order to ensure that buried 386 * CompoundDataProviders get handled correctly. When their base DataProvider is not a "container" 387 * type, the method setDataValue(index, Object, Object) should be used instead. 388 * 389 * For atomic type DataProviders, we treat this method as directly calling into setDataValue(index, 390 * Object, Object) using the passed rowIndex. However, this method should, in general, not be called 391 * by atomic type DataProviders. 392 * 393 * @param columnIndex the column 394 * @param rowIndex the row 395 * @param bufObject the data object 396 * @param newValue the new data object 397 */ 398 public void setDataValue(int columnIndex, int rowIndex, Object bufObject, Object newValue) 399 { 400 setDataValue(rowIndex, bufObject, newValue); 401 } 402 403 /** 404 * When a parent HDFDataProvider (such as an ArrayDataProvider) wants to set a 405 * data value by routing the operation through its base HDFDataProvider, the 406 * parent HDFDataProvider will generally know the direct index to have the base 407 * provider use. This method is to facilitate this kind of behavior. 408 * 409 * Note that this method takes two Object parameters, one which is the object 410 * that the method should set its data inside of and one which is the new value 411 * to set. This is to be able to nicely support nested compound DataProviders. 412 * 413 * @param index 414 * the index into the data array 415 * @param bufObject 416 * the data object 417 * @param newValue 418 * the new data object 419 */ 420 public void setDataValue(int index, Object bufObject, Object newValue) 421 { 422 try { 423 updateAtomicValue(bufObject, newValue, index); 424 } 425 catch (Exception ex) { 426 log.debug("setDataValue({}, {})=({}): updateAtomicValue failure: ", index, bufObject, 427 newValue, ex); 428 } 429 log.trace("setDataValue({}, {})=({}): finish", index, bufObject, newValue); 430 } 431 432 private void updateAtomicValue(Object bufObject, Object newValue, int bufIndex) 433 { 434 if ((newValue == null) || ((newValue = ((String)newValue).trim()) == null)) { 435 log.debug("updateAtomicValue(): cell value not updated; new value is null"); 436 return; 437 } 438 439 // No need to update if values are the same 440 int buf_size = Array.getLength(bufObject); 441 log.trace("updateAtomicValue(): bufObject size is {}", buf_size); 442 if (buf_size > 0) { 443 Object oldVal = this.getDataValue(bufObject, bufIndex); 444 if ((oldVal != null) && newValue.equals(oldVal.toString())) { 445 log.debug("updateAtomicValue(): cell value not updated; new value same as old value"); 446 return; 447 } 448 } 449 450 String bname = bufObject.getClass().getName(); 451 String nname = newValue.getClass().getName(); 452 log.trace("updateArrayOfAtomicElements(): bufObject cname={} of data newValue={}", bname, nname); 453 char runtimeTypeClass = Utils.getJavaObjectRuntimeClass(bufObject); 454 log.trace("updateAtomicValue(): runtimeTypeClass={}", runtimeTypeClass); 455 456 switch (runtimeTypeClass) { 457 case 'B': 458 byte bvalue = 0; 459 bvalue = Byte.parseByte((String)newValue); 460 Array.setByte(bufObject, bufIndex, bvalue); 461 break; 462 case 'S': 463 short svalue = 0; 464 svalue = Short.parseShort((String)newValue); 465 Array.setShort(bufObject, bufIndex, svalue); 466 break; 467 case 'I': 468 int ivalue = 0; 469 ivalue = Integer.parseInt((String)newValue); 470 Array.setInt(bufObject, bufIndex, ivalue); 471 break; 472 case 'J': 473 long lvalue = 0; 474 String cname = this.originalFormatClass.getName(); 475 char dname = cname.charAt(cname.lastIndexOf('[') + 1); 476 if (dname == 'J') { 477 BigInteger big = new BigInteger((String)newValue); 478 lvalue = big.longValue(); 479 } 480 else 481 lvalue = Long.parseLong((String)newValue); 482 Array.setLong(bufObject, bufIndex, lvalue); 483 break; 484 case 'F': 485 float fvalue = 0; 486 fvalue = Float.parseFloat((String)newValue); 487 Array.setFloat(bufObject, bufIndex, fvalue); 488 break; 489 case 'D': 490 double dvalue = 0; 491 dvalue = Double.parseDouble((String)newValue); 492 Array.setDouble(bufObject, bufIndex, dvalue); 493 break; 494 default: 495 String rname = bufObject.getClass().getSimpleName(); 496 log.trace("updateAtomicValue(): getSimpleName={}", rname); 497 switch (rname.charAt(0)) { 498 case 'B': 499 Byte bValue = Byte.valueOf((String)newValue); 500 Array.set(bufObject, bufIndex, bValue); 501 break; 502 case 'S': 503 Short sValue = Short.valueOf((String)newValue); 504 Array.set(bufObject, bufIndex, sValue); 505 break; 506 case 'I': 507 Integer iValue = Integer.valueOf((String)newValue); 508 Array.set(bufObject, bufIndex, iValue); 509 break; 510 case 'J': 511 Long lValue = 0L; 512 String cAname = this.originalFormatClass.getName(); 513 char dAname = cAname.charAt(cAname.lastIndexOf('[') + 1); 514 if (dAname == 'J') { 515 BigInteger big = new BigInteger((String)newValue); 516 lValue = big.longValue(); 517 } 518 else 519 lValue = Long.valueOf((String)newValue); 520 Array.set(bufObject, bufIndex, lValue); 521 break; 522 case 'F': 523 Float fValue = Float.valueOf((String)newValue); 524 Array.set(bufObject, bufIndex, fValue); 525 break; 526 case 'D': 527 Double dValue = Double.valueOf((String)newValue); 528 Array.set(bufObject, bufIndex, dValue); 529 break; 530 default: 531 log.trace("updateAtomicValue(): bufObject={} bufIndex={} newValue={}", bufObject, 532 bufIndex, newValue); 533 Array.set(bufObject, bufIndex, newValue); 534 break; 535 } 536 break; 537 } 538 539 isValueChanged = true; 540 } 541 542 @Override 543 public int getColumnCount() 544 { 545 return (int)colCount; 546 } 547 548 @Override 549 public int getRowCount() 550 { 551 return (int)rowCount; 552 } 553 554 /** 555 * set if the data value has changed 556 * 557 * @param isChanged 558 * if the data value is changed 559 */ 560 public final void setIsValueChanged(boolean isChanged) { isValueChanged = isChanged; } 561 562 /** 563 * Check if the datavalue has changed 564 * 565 * @return if the datavalue has changed 566 */ 567 public final boolean getIsValueChanged() { return isValueChanged; } 568 569 /** 570 * Update the data buffer for this HDFDataProvider. This is necessary for when 571 * the data that has been read is invalidated, such as when flipping through 572 * 'pages' in a > 2-dimensional dataset. 573 * 574 * @param newBuf 575 * the new data buffer 576 */ 577 public final void updateDataBuffer(Object newBuf) 578 { 579 this.dataBuf = newBuf; 580 581 if (rank > 1) { 582 rowCount = dataFormatReference.getHeight(); 583 colCount = dataFormatReference.getWidth(); 584 } 585 else { 586 rowCount = (int)dataFormatReference.getSelectedDims()[0]; 587 colCount = 1; 588 } 589 log.trace("updateDataBuffer: rowCount={} colCount={}", rowCount, colCount); 590 } 591 } 592 593 /* 594 * A DataProvider for Compound datatype datasets which is a composite of 595 * DataProviders, one for each selected member of the Compound datatype. 596 */ 597 private static class CompoundDataProvider extends HDFDataProvider { 598 private static final Logger log = LoggerFactory.getLogger(CompoundDataProvider.class); 599 600 private final HashMap<Integer, Integer> baseProviderIndexMap; 601 private final HashMap<Integer, Integer> relCmpdStartIndexMap; 602 603 private final HDFDataProvider[] baseTypeProviders; 604 605 private final Datatype[] selectedMemberTypes; 606 607 private final int[] selectedMemberOrders; 608 609 private final int nSubColumns; 610 private final int nCols; 611 private final int nRows; 612 613 CompoundDataProvider(final Datatype dtype, final Object dataBuf, final boolean dataTransposed) 614 throws Exception 615 { 616 super(dtype, dataBuf, dataTransposed); 617 618 CompoundDataFormat compoundFormat = (CompoundDataFormat)dataFormatReference; 619 selectedMemberTypes = compoundFormat.getSelectedMemberTypes(); 620 selectedMemberOrders = compoundFormat.getSelectedMemberOrders(); 621 622 List<Datatype> localSelectedTypes = 623 DataFactoryUtils.filterNonSelectedMembers(compoundFormat, dtype); 624 625 log.trace("setting up {} base HDFDataProviders", localSelectedTypes.size()); 626 627 baseTypeProviders = new HDFDataProvider[localSelectedTypes.size()]; 628 for (int i = 0; i < baseTypeProviders.length; i++) { 629 log.trace("retrieving DataProvider for member {}", i); 630 631 try { 632 baseTypeProviders[i] = 633 getDataProvider(localSelectedTypes.get(i), dataBuf, dataTransposed); 634 } 635 catch (Exception ex) { 636 log.debug("failed to retrieve DataProvider for member {}: ", i, ex); 637 baseTypeProviders[i] = null; 638 } 639 } 640 641 /* 642 * Build necessary index maps. 643 */ 644 HashMap<Integer, Integer>[] maps = 645 DataFactoryUtils.buildIndexMaps(compoundFormat, localSelectedTypes); 646 baseProviderIndexMap = maps[DataFactoryUtils.COL_TO_BASE_CLASS_MAP_INDEX]; 647 relCmpdStartIndexMap = maps[DataFactoryUtils.CMPD_START_IDX_MAP_INDEX]; 648 649 log.trace("index maps built: baseProviderIndexMap = {}, relColIdxMap = {}", 650 baseProviderIndexMap.toString(), relCmpdStartIndexMap.toString()); 651 652 if (baseProviderIndexMap.size() == 0) { 653 log.debug("base DataProvider index mapping is invalid - size 0"); 654 throw new Exception("CompoundDataProvider: invalid DataProvider mapping of size 0 built"); 655 } 656 657 if (relCmpdStartIndexMap.size() == 0) { 658 log.debug("compound field start index mapping is invalid - size 0"); 659 throw new Exception( 660 "CompoundDataProvider: invalid compound field start index mapping of size 0 built"); 661 } 662 663 /* 664 * nCols should represent the number of columns covered by this CompoundDataProvider 665 * only. For top-level CompoundDataProviders, this should be the entire width of the 666 * dataset. For nested CompoundDataProviders, nCols will be a subset of these columns. 667 */ 668 nCols = (int)compoundFormat.getWidth() * baseProviderIndexMap.size(); 669 nRows = (int)compoundFormat.getHeight(); 670 671 nSubColumns = (int)compoundFormat.getWidth(); 672 } 673 674 @Override 675 public Object getDataValue(int columnIndex, int rowIndex) 676 { 677 try { 678 int fieldIdx = columnIndex; 679 int rowIdx = rowIndex; 680 681 if (nSubColumns > 1) { // multi-dimension compound dataset 682 /* 683 * Make sure fieldIdx is within a valid range, since even for multi-dimensional 684 * compound datasets there will only be as many lists of data as there are 685 * members in a single compound type. 686 */ 687 fieldIdx %= selectedMemberTypes.length; 688 689 int realColIdx = columnIndex / selectedMemberTypes.length; 690 rowIdx = rowIndex * nSubColumns + realColIdx; 691 } 692 693 int providerIndex = baseProviderIndexMap.get(fieldIdx); 694 Object colValue = ((List<?>)dataBuf).get(providerIndex); 695 if (colValue == null) 696 return DataFactoryUtils.nullStr; 697 698 /* 699 * Delegate data retrieval to one of the base DataProviders according to the 700 * index of the relevant compound field. 701 */ 702 HDFDataProvider base = baseTypeProviders[providerIndex]; 703 if (base instanceof CompoundDataProvider) 704 /* 705 * Adjust the compound field index by subtracting the starting index of the 706 * nested compound that we are delegating to. When the nested compound's index 707 * map is setup correctly, this adjusted index should map to the correct field 708 * among the nested compound's members. 709 */ 710 theValue = 711 base.getDataValue(colValue, fieldIdx - relCmpdStartIndexMap.get(fieldIdx), rowIdx); 712 else if (base instanceof ArrayDataProvider) { 713 /* 714 * TODO: quick temporary fix for specific compound of array of compound files. 715 * Transforms the given column index into a relative index from the starting 716 * index of the array of compound field. 717 */ 718 int arrCompoundStartIdx = columnIndex; 719 HDFDataProvider theProvider; 720 while (arrCompoundStartIdx >= 0) { 721 try { 722 theProvider = 723 baseTypeProviders[baseProviderIndexMap.get(arrCompoundStartIdx - 1)]; 724 if (theProvider != base) 725 break; 726 727 arrCompoundStartIdx--; 728 } 729 catch (Exception ex) { 730 break; 731 } 732 } 733 734 int adjustedColIndex = columnIndex - arrCompoundStartIdx; 735 736 theValue = base.getDataValue(colValue, adjustedColIndex, rowIdx); 737 } 738 else 739 theValue = base.getDataValue(colValue, rowIdx); 740 } 741 catch (Exception ex) { 742 log.debug("getDataValue({}, {}): failure: ", rowIndex, columnIndex, ex); 743 theValue = DataFactoryUtils.errStr; 744 } 745 746 log.trace("getDataValue({}, {}): finish", rowIndex, columnIndex); 747 748 return theValue; 749 } 750 751 @Override 752 public Object getDataValue(Object obj, int columnIndex, int rowIndex) 753 { 754 try { 755 int providerIndex = baseProviderIndexMap.get(columnIndex); 756 Object colValue = ((List<?>)obj).get(providerIndex); 757 if (colValue == null) 758 return DataFactoryUtils.nullStr; 759 760 /* 761 * Delegate data retrieval to one of the base DataProviders according to the 762 * index of the relevant compound field. 763 */ 764 HDFDataProvider base = baseTypeProviders[providerIndex]; 765 if (base instanceof CompoundDataProvider) 766 /* 767 * Adjust the compound field index by subtracting the starting index of the 768 * nested compound that we are delegating to. When the nested compound's index 769 * map is setup correctly, this adjusted index should map to the correct field 770 * among the nested compound's members. 771 */ 772 theValue = base.getDataValue( 773 colValue, columnIndex - relCmpdStartIndexMap.get(columnIndex), rowIndex); 774 else if (base instanceof ArrayDataProvider) 775 theValue = base.getDataValue(colValue, columnIndex, rowIndex); 776 else 777 theValue = base.getDataValue(colValue, rowIndex); 778 } 779 catch (Exception ex) { 780 log.debug("getDataValue({}, {}): failure: ", rowIndex, columnIndex, ex); 781 theValue = DataFactoryUtils.errStr; 782 } 783 log.trace("getDataValue({})=({}): finish", rowIndex, columnIndex); 784 785 return theValue; 786 } 787 788 @Override 789 public Object getDataValue(Object obj, int index) 790 { 791 throw new UnsupportedOperationException( 792 "getDataValue(Object, int) should not be called for CompoundDataProviders"); 793 } 794 795 @Override 796 public void setDataValue(int columnIndex, int rowIndex, Object newValue) 797 { 798 if ((newValue == null) || ((newValue = ((String)newValue).trim()) == null)) { 799 log.debug("setDataValue({}, {})=({}): cell value not updated; new value is null", rowIndex, 800 columnIndex, newValue); 801 return; 802 } 803 804 // No need to update if values are the same 805 Object oldVal = this.getDataValue(columnIndex, rowIndex); 806 if ((oldVal != null) && newValue.equals(oldVal.toString())) { 807 log.debug("setDataValue({}, {})=({}): cell value not updated; new value same as old value", 808 rowIndex, columnIndex, newValue); 809 return; 810 } 811 812 try { 813 int fieldIdx = columnIndex; 814 int rowIdx = rowIndex; 815 816 if (nSubColumns > 1) { // multi-dimension compound dataset 817 /* 818 * Make sure fieldIdx is within a valid range, since even for multi-dimensional 819 * compound datasets there will only be as many lists of data as there are 820 * members in a single compound type. 821 */ 822 fieldIdx %= selectedMemberTypes.length; 823 824 int realColIdx = columnIndex / selectedMemberTypes.length; 825 rowIdx = rowIndex * nSubColumns + realColIdx; 826 } 827 828 int providerIndex = baseProviderIndexMap.get(fieldIdx); 829 Object colValue = ((List<?>)dataBuf).get(providerIndex); 830 if (colValue == null) { 831 log.debug("setDataValue({}, {})=({}): colValue is null", rowIndex, columnIndex, newValue); 832 return; 833 } 834 835 /* 836 * Delegate data setting to one of the base DataProviders according to the index 837 * of the relevant compound field. 838 */ 839 HDFDataProvider base = baseTypeProviders[providerIndex]; 840 if (base.isContainerType) 841 /* 842 * Adjust the compound field index by subtracting the starting index of the 843 * nested compound that we are delegating to. When the nested compound's index 844 * map is setup correctly, this adjusted index should map to the correct field 845 * among the nested compound's members. 846 */ 847 base.setDataValue(fieldIdx - relCmpdStartIndexMap.get(fieldIdx), rowIdx, colValue, 848 newValue); 849 else 850 base.setDataValue(rowIdx, colValue, newValue); 851 852 isValueChanged = true; 853 } 854 catch (Exception ex) { 855 log.debug("setDataValue({}, {})=({}): cell value update failure: ", rowIndex, columnIndex, 856 newValue); 857 } 858 log.trace("setDataValue({}, {})=({}): finish", rowIndex, columnIndex, newValue); 859 860 /* 861 * TODO: throwing error dialogs when something fails? 862 * 863 * Tools.showError(shell, "Select", "Unable to set new value:\n\n " + ex); 864 */ 865 } 866 867 @Override 868 public void setDataValue(int columnIndex, int rowIndex, Object bufObject, Object newValue) 869 { 870 try { 871 int providerIndex = baseProviderIndexMap.get(columnIndex); 872 Object colValue = ((List<?>)bufObject).get(providerIndex); 873 if (colValue == null) { 874 log.debug("setDataValue({}, {}, {})=({}): colValue is null", rowIndex, columnIndex, 875 bufObject, newValue); 876 return; 877 } 878 879 /* 880 * Delegate data setting to one of the base DataProviders according to the index 881 * of the relevant compound field. 882 */ 883 HDFDataProvider base = baseTypeProviders[providerIndex]; 884 if (base.isContainerType) 885 /* 886 * Adjust the compound field index by subtracting the starting index of the 887 * nested compound that we are delegating to. When the nested compound's index 888 * map is setup correctly, this adjusted index should map to the correct field 889 * among the nested compound's members. 890 */ 891 base.setDataValue(columnIndex - relCmpdStartIndexMap.get(columnIndex), rowIndex, colValue, 892 newValue); 893 else 894 base.setDataValue(rowIndex, colValue, newValue); 895 896 isValueChanged = true; 897 } 898 catch (Exception ex) { 899 log.debug("setDataValue({}, {}, {})=({}): cell value update failure: ", rowIndex, columnIndex, 900 bufObject, newValue, ex); 901 } 902 log.trace("setDataValue({}, {}, {})=({}): finish", rowIndex, columnIndex, bufObject, newValue); 903 } 904 905 @Override 906 public void setDataValue(int index, Object bufObject, Object newValue) 907 { 908 throw new UnsupportedOperationException( 909 "setDataValue(int, Object, Object) should not be called for CompoundDataProviders"); 910 } 911 912 @Override 913 public int getColumnCount() 914 { 915 return nCols; 916 } 917 918 @Override 919 public int getRowCount() 920 { 921 return nRows; 922 } 923 } 924 925 private static class ArrayDataProvider extends HDFDataProvider { 926 private static final Logger log = LoggerFactory.getLogger(ArrayDataProvider.class); 927 928 private final HDFDataProvider baseTypeDataProvider; 929 930 private final Object[] arrayElements; 931 private final long arraySize; 932 933 private final int nCols; 934 935 ArrayDataProvider(final Datatype dtype, final Object dataBuf, final boolean dataTransposed) 936 throws Exception 937 { 938 super(dtype, dataBuf, dataTransposed); 939 940 Datatype baseType = dtype.getDatatypeBase(); 941 942 baseTypeDataProvider = getDataProvider(baseType, dataBuf, dataTransposed); 943 944 if (baseType.isVarStr()) 945 arraySize = dtype.getArrayDims()[0]; 946 else if (baseType.isBitField() || baseType.isOpaque()) 947 arraySize = dtype.getDatatypeSize(); 948 else 949 arraySize = dtype.getDatatypeSize() / baseType.getDatatypeSize(); 950 951 arrayElements = new Object[(int)arraySize]; 952 953 if (baseTypeDataProvider instanceof CompoundDataProvider) 954 nCols = (int)arraySize * ((CompoundDataProvider)baseTypeDataProvider).nCols; 955 else 956 nCols = super.getColumnCount(); 957 } 958 959 @Override 960 public Object getDataValue(int columnIndex, int rowIndex) 961 { 962 try { 963 int bufIndex = physicalLocationToBufIndex(rowIndex, columnIndex); 964 965 bufIndex *= arraySize; 966 967 if (baseTypeDataProvider instanceof CompoundDataProvider) { 968 /* 969 * Pass row and column indices down where they will be adjusted. 970 */ 971 theValue = retrieveArrayOfCompoundElements(dataBuf, columnIndex, rowIndex); 972 } 973 else if (baseTypeDataProvider instanceof ArrayDataProvider) { 974 /* 975 * TODO: assign to global arrayElements. 976 */ 977 theValue = retrieveArrayOfArrayElements(dataBuf, columnIndex, bufIndex); 978 } 979 else { 980 /* 981 * TODO: assign to global arrayElements. 982 */ 983 theValue = retrieveArrayOfAtomicElements(dataBuf, bufIndex); 984 } 985 } 986 catch (Exception ex) { 987 log.debug("getDataValue({}, {}): failure: ", rowIndex, columnIndex, ex); 988 theValue = DataFactoryUtils.errStr; 989 } 990 991 log.trace("getDataValue({}, {})({}): finish", rowIndex, columnIndex, theValue); 992 993 return theValue; 994 } 995 996 @Override 997 public Object getDataValue(Object obj, int columnIndex, int rowIndex) 998 { 999 try { 1000 long index = rowIndex * arraySize; 1001 1002 if (baseTypeDataProvider instanceof CompoundDataProvider) { 1003 /* 1004 * Pass row and column indices down where they will be adjusted. 1005 */ 1006 theValue = retrieveArrayOfCompoundElements(obj, columnIndex, rowIndex); 1007 } 1008 else if (baseTypeDataProvider instanceof ArrayDataProvider) { 1009 theValue = retrieveArrayOfArrayElements(obj, columnIndex, (int)index); 1010 } 1011 else { 1012 theValue = retrieveArrayOfAtomicElements(obj, (int)index); 1013 } 1014 } 1015 catch (Exception ex) { 1016 log.debug("getDataValue({}, {}): failure: ", rowIndex, columnIndex, ex); 1017 theValue = DataFactoryUtils.errStr; 1018 } 1019 1020 return theValue; 1021 } 1022 1023 private Object[] retrieveArrayOfCompoundElements(Object objBuf, int columnIndex, int rowIndex) 1024 { 1025 long adjustedRowIdx = 1026 (rowIndex * arraySize * colCount) + 1027 (columnIndex / ((CompoundDataProvider)baseTypeDataProvider).baseProviderIndexMap.size()); 1028 long adjustedColIdx = 1029 columnIndex % ((CompoundDataProvider)baseTypeDataProvider).baseProviderIndexMap.size(); 1030 1031 /* 1032 * Since we flatten array of compound types, we only need to return a single 1033 * value. 1034 */ 1035 return new Object[] { 1036 baseTypeDataProvider.getDataValue(objBuf, (int)adjustedColIdx, (int)adjustedRowIdx)}; 1037 } 1038 1039 private Object[] retrieveArrayOfArrayElements(Object objBuf, int columnIndex, int startRowIndex) 1040 { 1041 Object[] tempArray = new Object[(int)arraySize]; 1042 1043 for (int i = 0; i < arraySize; i++) 1044 tempArray[i] = baseTypeDataProvider.getDataValue(objBuf, columnIndex, startRowIndex + i); 1045 1046 return tempArray; 1047 } 1048 1049 private Object[] retrieveArrayOfAtomicElements(Object objBuf, int rowStartIdx) 1050 { 1051 Object[] tempArray = new Object[(int)arraySize]; 1052 1053 for (int i = 0; i < arraySize; i++) 1054 tempArray[i] = baseTypeDataProvider.getDataValue(objBuf, rowStartIdx + i); 1055 1056 return tempArray; 1057 } 1058 1059 @Override 1060 public Object getDataValue(Object obj, int index) 1061 { 1062 throw new UnsupportedOperationException( 1063 "getDataValue(Object, int) should not be called for ArrayDataProviders"); 1064 } 1065 1066 @Override 1067 public void setDataValue(int columnIndex, int rowIndex, Object newValue) 1068 { 1069 try { 1070 int bufIndex = physicalLocationToBufIndex(rowIndex, columnIndex); 1071 1072 bufIndex *= arraySize; 1073 1074 updateArrayElements(dataBuf, newValue, columnIndex, bufIndex); 1075 } 1076 catch (Exception ex) { 1077 log.debug("setDataValue({}, {}, {}): cell value update failure: ", rowIndex, columnIndex, 1078 newValue, ex); 1079 } 1080 log.trace("setDataValue({}, {})=({}): finish", rowIndex, columnIndex, newValue); 1081 } 1082 1083 @Override 1084 public void setDataValue(int columnIndex, int rowIndex, Object bufObject, Object newValue) 1085 { 1086 try { 1087 long bufIndex = rowIndex * arraySize; 1088 1089 updateArrayElements(bufObject, newValue, columnIndex, (int)bufIndex); 1090 } 1091 catch (Exception ex) { 1092 log.debug("setDataValue({}, {}, {}, {}): cell value update failure: ", rowIndex, columnIndex, 1093 bufObject, newValue, ex); 1094 } 1095 log.trace("setDataValue({}, {}, {})=({}): finish", rowIndex, columnIndex, bufObject, newValue); 1096 } 1097 1098 @Override 1099 public void setDataValue(int index, Object bufObject, Object newValue) 1100 { 1101 throw new UnsupportedOperationException( 1102 "setDataValue(int, Object, Object) should not be called for ArrayDataProviders"); 1103 } 1104 1105 private void updateArrayElements(Object curBuf, Object newValue, int columnIndex, int bufStartIndex) 1106 { 1107 StringTokenizer st = new StringTokenizer((String)newValue, ",[]"); 1108 if (st.countTokens() < arraySize) { 1109 /* 1110 * TODO: 1111 */ 1112 /* Tools.showError(shell, "Select", "Number of data points < " + morder + "."); */ 1113 log.debug("updateArrayElements(): number of data points ({}) < array size {}", 1114 st.countTokens(), arraySize); 1115 log.trace("updateArrayElements({}, {}, {}): finish", curBuf, newValue, bufStartIndex); 1116 return; 1117 } 1118 1119 if (baseTypeDataProvider instanceof CompoundDataProvider) 1120 updateArrayOfCompoundElements(st, curBuf, columnIndex, bufStartIndex); 1121 else if (baseTypeDataProvider instanceof ArrayDataProvider) 1122 updateArrayOfArrayElements(st, curBuf, columnIndex, bufStartIndex); 1123 else 1124 updateArrayOfAtomicElements(st, curBuf, bufStartIndex); 1125 } 1126 1127 private void updateArrayOfCompoundElements(StringTokenizer tokenizer, Object curBuf, int columnIndex, 1128 int bufStartIndex) 1129 { 1130 for (int i = 0; i < arraySize; i++) { 1131 List<?> cmpdDataList = (List<?>)((Object[])curBuf)[i]; 1132 baseTypeDataProvider.setDataValue(columnIndex, bufStartIndex + i, cmpdDataList, 1133 tokenizer.nextToken().trim()); 1134 isValueChanged = isValueChanged || baseTypeDataProvider.getIsValueChanged(); 1135 } 1136 } 1137 1138 private void updateArrayOfArrayElements(StringTokenizer tokenizer, Object curBuf, int columnIndex, 1139 int bufStartIndex) 1140 { 1141 for (int i = 0; i < arraySize; i++) { 1142 /* 1143 * TODO: not quite right. 1144 */ 1145 baseTypeDataProvider.setDataValue(columnIndex, bufStartIndex + i, curBuf, 1146 tokenizer.nextToken().trim()); 1147 isValueChanged = isValueChanged || baseTypeDataProvider.getIsValueChanged(); 1148 } 1149 } 1150 1151 private void updateArrayOfAtomicElements(StringTokenizer tokenizer, Object curBuf, int bufStartIndex) 1152 { 1153 for (int i = 0; i < arraySize; i++) { 1154 baseTypeDataProvider.setDataValue(bufStartIndex + i, curBuf, tokenizer.nextToken().trim()); 1155 isValueChanged = isValueChanged || baseTypeDataProvider.getIsValueChanged(); 1156 } 1157 } 1158 1159 @Override 1160 public int getColumnCount() 1161 { 1162 return nCols; 1163 } 1164 } 1165 1166 private static class VlenDataProvider extends HDFDataProvider { 1167 private static final Logger log = LoggerFactory.getLogger(VlenDataProvider.class); 1168 1169 private final HDFDataProvider baseTypeDataProvider; 1170 1171 private final StringBuilder buffer; 1172 1173 private final int baseTypeClass; 1174 1175 VlenDataProvider(final Datatype dtype, final Object dataBuf, final boolean dataTransposed) 1176 throws Exception 1177 { 1178 super(dtype, dataBuf, dataTransposed); 1179 1180 Datatype baseType = dtype.getDatatypeBase(); 1181 baseTypeClass = baseType.getDatatypeClass(); 1182 1183 baseTypeDataProvider = getDataProvider(baseType, dataBuf, dataTransposed); 1184 1185 buffer = new StringBuilder(); 1186 } 1187 1188 @Override 1189 public Object getDataValue(int columnIndex, int rowIndex) 1190 { 1191 buffer.setLength(0); 1192 1193 try { 1194 int bufIndex = physicalLocationToBufIndex(rowIndex, columnIndex); 1195 1196 if (baseTypeDataProvider instanceof CompoundDataProvider) { 1197 /* 1198 * Pass row and column indices down where they will be adjusted. 1199 */ 1200 theValue = retrieveArrayOfCompoundElements(dataBuf, columnIndex, rowIndex); 1201 } 1202 else if (baseTypeDataProvider instanceof ArrayDataProvider) { 1203 /* 1204 * TODO: assign to global arrayElements. 1205 */ 1206 theValue = retrieveArrayOfArrayElements(dataBuf, columnIndex, bufIndex); 1207 } 1208 else if (baseTypeDataProvider instanceof RefDataProvider) { 1209 /* 1210 * TODO: assign to global arrayElements. 1211 */ 1212 theValue = retrieveArrayOfArrayElements(dataBuf, columnIndex, bufIndex); 1213 } 1214 else { 1215 /* 1216 * TODO: assign to global arrayElements. 1217 */ 1218 theValue = retrieveArrayOfAtomicElements(dataBuf, bufIndex); 1219 } 1220 } 1221 catch (Exception ex) { 1222 log.debug("getDataValue(rowIndex={}, columnIndex={}): failure: ", rowIndex, columnIndex, ex); 1223 theValue = DataFactoryUtils.errStr; 1224 } 1225 1226 log.trace("getDataValue(rowIndex={}, columnIndex={})=({}): finish", rowIndex, columnIndex, 1227 theValue); 1228 1229 return theValue; 1230 } 1231 1232 @Override 1233 public Object getDataValue(Object obj, int columnIndex, int rowIndex) 1234 { 1235 buffer.setLength(0); 1236 1237 try { 1238 long vlSize = Array.getLength(obj); 1239 log.debug("getDataValue(): vlSize={} obj={}", vlSize, obj); 1240 1241 if (baseTypeDataProvider instanceof CompoundDataProvider) { 1242 /* 1243 * Pass row and column indices down where they will be adjusted. 1244 */ 1245 theValue = retrieveArrayOfCompoundElements(obj, columnIndex, rowIndex); 1246 } 1247 else if (baseTypeDataProvider instanceof ArrayDataProvider) { 1248 theValue = retrieveArrayOfArrayElements(obj, columnIndex, rowIndex); 1249 } 1250 else if (baseTypeDataProvider instanceof RefDataProvider) { 1251 theValue = retrieveArrayOfArrayElements(obj, columnIndex, rowIndex); 1252 } 1253 else { 1254 theValue = retrieveArrayOfAtomicElements(obj, rowIndex); 1255 } 1256 } 1257 catch (Exception ex) { 1258 log.debug("getDataValue(rowIndex={}, columnIndex={}): failure: ", rowIndex, columnIndex, ex); 1259 theValue = DataFactoryUtils.errStr; 1260 } 1261 1262 log.trace("getDataValue(obj={}, rowIndex={}, columnIndex={})=({}): finish", obj, rowIndex, 1263 columnIndex, theValue); 1264 1265 return theValue; 1266 } 1267 1268 private Object[] retrieveArrayOfCompoundElements(Object objBuf, int columnIndex, int rowIndex) 1269 { 1270 long vlSize = Array.getLength(objBuf); 1271 log.debug("retrieveArrayOfCompoundElements(): vlSize={}", vlSize); 1272 long adjustedRowIdx = 1273 (rowIndex * vlSize * colCount) + 1274 (columnIndex / ((CompoundDataProvider)baseTypeDataProvider).baseProviderIndexMap.size()); 1275 long adjustedColIdx = 1276 columnIndex % ((CompoundDataProvider)baseTypeDataProvider).baseProviderIndexMap.size(); 1277 1278 /* 1279 * Since we flatten array of compound types, we only need to return a single 1280 * value. 1281 */ 1282 return new Object[] { 1283 baseTypeDataProvider.getDataValue(objBuf, (int)adjustedColIdx, (int)adjustedRowIdx)}; 1284 } 1285 1286 private Object[] retrieveArrayOfArrayElements(Object objBuf, int columnIndex, int startRowIndex) 1287 { 1288 log.debug("retrieveArrayOfArrayElements(): objBuf={}", objBuf); 1289 ArrayList<byte[]> vlElements = ((ArrayList[])objBuf)[startRowIndex]; 1290 log.debug("retrieveArrayOfArrayElements(): vlElements={}", vlElements); 1291 long vlSize = vlElements.size(); 1292 log.debug("retrieveArrayOfArrayElements(): vlSize={} length={}", vlSize, vlElements.size()); 1293 Object[] tempArray = new Object[(int)vlSize]; 1294 1295 for (int i = 0; i < vlSize; i++) { 1296 ArrayList<byte[]> ref_value = vlElements; 1297 StringBuilder sb = new StringBuilder(); 1298 sb.append("{"); 1299 for (int m = 0; m < ref_value.size(); m++) { 1300 if (m > 0) 1301 sb.append(", "); 1302 byte[] byteElements = ref_value.get(m); 1303 log.trace("retrieveArrayOfArrayElements byteElements={}", byteElements); 1304 sb.append(baseTypeDataProvider.getDataValue(byteElements, columnIndex, i)); 1305 } 1306 sb.append("}"); 1307 tempArray[i] = sb.toString(); 1308 } 1309 1310 return tempArray; 1311 } 1312 1313 private Object[] retrieveArrayOfAtomicElements(Object objBuf, int rowStartIdx) 1314 { 1315 ArrayList vlElements = ((ArrayList[])objBuf)[rowStartIdx]; 1316 long vlSize = vlElements.size(); 1317 log.debug("retrieveArrayOfAtomicElements(): vlSize={}", vlSize); 1318 Object[] tempArray = new Object[(int)vlSize]; 1319 1320 for (int i = 0; i < vlSize; i++) 1321 tempArray[i] = baseTypeDataProvider.getDataValue(vlElements.toArray(), i); 1322 1323 return tempArray; 1324 } 1325 1326 @Override 1327 public Object getDataValue(Object obj, int index) 1328 { 1329 throw new UnsupportedOperationException( 1330 "getDataValue(Object, int) should not be called for VlenDataProviders"); 1331 } 1332 1333 @Override 1334 public void setDataValue(int columnIndex, int rowIndex, Object newValue) 1335 { 1336 try { 1337 int bufIndex = physicalLocationToBufIndex(rowIndex, columnIndex); 1338 1339 long vlSize = Array.getLength(dataBuf); 1340 log.debug("setDataValue(): vlSize={}", vlSize); 1341 1342 updateArrayElements(dataBuf, newValue, columnIndex, rowIndex); 1343 } 1344 catch (Exception ex) { 1345 log.debug("setDataValue(rowIndex={}, columnIndex={}, {}): cell value update failure: ", 1346 rowIndex, columnIndex, newValue, ex); 1347 } 1348 log.trace("setDataValue(rowIndex={}, columnIndex={})=({}): finish", rowIndex, columnIndex, 1349 newValue); 1350 } 1351 1352 @Override 1353 public void setDataValue(int columnIndex, int rowIndex, Object bufObject, Object newValue) 1354 { 1355 try { 1356 long vlSize = Array.getLength(bufObject); 1357 log.debug("setDataValue(): vlSize={} for [c{}, r{}]", vlSize, columnIndex, rowIndex); 1358 1359 updateArrayElements(bufObject, newValue, columnIndex, rowIndex); 1360 } 1361 catch (Exception ex) { 1362 log.debug( 1363 "setDataValue(rowIndex={}, columnIndex={}, bufObject={}, {}): cell value update failure: ", 1364 rowIndex, columnIndex, bufObject, newValue, ex); 1365 } 1366 log.trace("setDataValue(rowIndex={}, columnIndex={}, bufObject={})=({}): finish", rowIndex, 1367 columnIndex, bufObject, newValue); 1368 } 1369 1370 @Override 1371 public void setDataValue(int index, Object bufObject, Object newValue) 1372 { 1373 throw new UnsupportedOperationException( 1374 "setDataValue(int, Object, Object) should not be called for VlenDataProviders"); 1375 } 1376 1377 private void updateArrayElements(Object curBuf, Object newValue, int columnIndex, int rowStartIndex) 1378 { 1379 long vlSize = Array.getLength(curBuf); 1380 log.debug("updateArrayElements(): vlSize={}", vlSize); 1381 1382 if (baseTypeDataProvider instanceof CompoundDataProvider) 1383 updateArrayOfCompoundElements(newValue, curBuf, columnIndex, rowStartIndex); 1384 else if (baseTypeDataProvider instanceof ArrayDataProvider) 1385 updateArrayOfArrayElements(newValue, curBuf, columnIndex, rowStartIndex); 1386 else if (baseTypeDataProvider instanceof VlenDataProvider) 1387 updateArrayOfArrayElements(newValue, curBuf, columnIndex, rowStartIndex); 1388 else 1389 updateArrayOfAtomicElements(newValue, curBuf, rowStartIndex); 1390 } 1391 1392 private void updateArrayOfCompoundElements(Object newValue, Object curBuf, int columnIndex, 1393 int rowIndex) 1394 { 1395 long vlSize = Array.getLength(curBuf); 1396 log.debug("updateArrayOfCompoundElements(): vlSize={}", vlSize); 1397 long adjustedRowIdx = 1398 (rowIndex * vlSize * colCount) + 1399 (columnIndex / ((CompoundDataProvider)baseTypeDataProvider).baseProviderIndexMap.size()); 1400 long adjustedColIdx = 1401 columnIndex % ((CompoundDataProvider)baseTypeDataProvider).baseProviderIndexMap.size(); 1402 1403 /* 1404 * Since we flatten array of compound types, we only need to update a single value. 1405 */ 1406 baseTypeDataProvider.setDataValue((int)adjustedColIdx, (int)adjustedRowIdx, curBuf, newValue); 1407 isValueChanged = isValueChanged || baseTypeDataProvider.getIsValueChanged(); 1408 } 1409 1410 private void updateArrayOfArrayElements(Object newValue, Object curBuf, int columnIndex, int rowIndex) 1411 { 1412 ArrayList vlElements = ((ArrayList[])curBuf)[rowIndex]; 1413 log.debug("updateArrayOfArrayElements(): vlElements={}", vlElements); 1414 long vlSize = vlElements.size(); 1415 log.debug("updateArrayOfArrayElements(): vlSize={}", vlSize); 1416 1417 StringTokenizer st = new StringTokenizer((String)newValue, ",[]"); 1418 int newcnt = st.countTokens(); 1419 1420 Object[] buffer = null; 1421 switch (baseTypeClass) { 1422 case Datatype.CLASS_CHAR: 1423 buffer = new Byte[newcnt]; 1424 break; 1425 case Datatype.CLASS_INTEGER: 1426 buffer = new Integer[newcnt]; 1427 break; 1428 case Datatype.CLASS_FLOAT: 1429 buffer = new Double[newcnt]; 1430 break; 1431 case Datatype.CLASS_STRING: 1432 buffer = new String[newcnt]; 1433 break; 1434 case Datatype.CLASS_REFERENCE: 1435 case Datatype.CLASS_OPAQUE: 1436 case Datatype.CLASS_BITFIELD: 1437 case Datatype.CLASS_ENUM: 1438 case Datatype.CLASS_ARRAY: 1439 case Datatype.CLASS_COMPOUND: 1440 case Datatype.CLASS_VLEN: 1441 default: 1442 buffer = new Object[newcnt]; 1443 break; 1444 } 1445 for (int i = 0; i < newcnt; i++) { 1446 baseTypeDataProvider.setDataValue(columnIndex, i, buffer, st.nextToken().trim()); 1447 isValueChanged = isValueChanged || baseTypeDataProvider.getIsValueChanged(); 1448 } 1449 vlElements = new ArrayList<>(Arrays.asList(buffer)); 1450 ((ArrayList[])curBuf)[rowIndex] = vlElements; 1451 } 1452 1453 private void updateArrayOfAtomicElements(Object newValue, Object curBuf, int rowStartIdx) 1454 { 1455 ArrayList vlElements = ((ArrayList[])curBuf)[rowStartIdx]; 1456 long vlSize = vlElements.size(); 1457 log.debug("updateArrayOfAtomicElements(): vlSize={}", vlSize); 1458 1459 StringTokenizer st = new StringTokenizer((String)newValue, ",[]"); 1460 int newcnt = st.countTokens(); 1461 log.debug("updateArrayOfAtomicElements(): count={}", newcnt); 1462 Object[] buffer = null; 1463 switch (baseTypeClass) { 1464 case Datatype.CLASS_CHAR: 1465 buffer = new Byte[newcnt]; 1466 break; 1467 case Datatype.CLASS_INTEGER: 1468 buffer = new Integer[newcnt]; 1469 break; 1470 case Datatype.CLASS_FLOAT: 1471 buffer = new Double[newcnt]; 1472 break; 1473 case Datatype.CLASS_STRING: 1474 buffer = new String[newcnt]; 1475 break; 1476 case Datatype.CLASS_REFERENCE: 1477 case Datatype.CLASS_OPAQUE: 1478 case Datatype.CLASS_BITFIELD: 1479 case Datatype.CLASS_ENUM: 1480 case Datatype.CLASS_ARRAY: 1481 case Datatype.CLASS_COMPOUND: 1482 case Datatype.CLASS_VLEN: 1483 default: 1484 buffer = new Object[newcnt]; 1485 break; 1486 } 1487 for (int i = 0; i < newcnt; i++) { 1488 baseTypeDataProvider.setDataValue(i, buffer, st.nextToken().trim()); 1489 isValueChanged = isValueChanged || baseTypeDataProvider.getIsValueChanged(); 1490 } 1491 String bname = buffer.getClass().getName(); 1492 String cname = curBuf.getClass().getName(); 1493 log.trace("updateArrayOfAtomicElements(): buffer cname={} of data cname={}", bname, cname); 1494 vlElements = new ArrayList<>(Arrays.asList(buffer)); 1495 log.debug("updateArrayOfAtomicElements(): new vlSize={}", vlElements.size()); 1496 ((ArrayList[])curBuf)[rowStartIdx] = vlElements; 1497 } 1498 } 1499 1500 private static class StringDataProvider extends HDFDataProvider { 1501 private static final Logger log = LoggerFactory.getLogger(StringDataProvider.class); 1502 1503 private final long typeSize; 1504 1505 StringDataProvider(final Datatype dtype, final Object dataBuf, final boolean dataTransposed) 1506 throws Exception 1507 { 1508 super(dtype, dataBuf, dataTransposed); 1509 1510 typeSize = dtype.getDatatypeSize(); 1511 } 1512 1513 @Override 1514 public Object getDataValue(Object obj, int index) 1515 { 1516 if (obj instanceof byte[]) { 1517 int strlen = (int)typeSize; 1518 1519 log.trace("getDataValue({}, {}): converting byte[] to String", obj, index); 1520 1521 String str = new String((byte[])obj, index * strlen, strlen); 1522 int idx = str.indexOf('\0'); 1523 if (idx > 0) 1524 str = str.substring(0, idx); 1525 1526 theValue = str.trim(); 1527 } 1528 else 1529 super.getDataValue(obj, index); 1530 1531 log.trace("getDataValue({}, {})=({}): finish", obj, index, theValue); 1532 1533 return theValue; 1534 } 1535 1536 @Override 1537 public void setDataValue(int columnIndex, int rowIndex, Object newValue) 1538 { 1539 try { 1540 int bufIndex = physicalLocationToBufIndex(rowIndex, columnIndex); 1541 1542 updateStringBytes(dataBuf, newValue, bufIndex); 1543 } 1544 catch (Exception ex) { 1545 log.debug("setDataValue({}, {}, {}): cell value update failure: ", rowIndex, columnIndex, 1546 newValue, ex); 1547 } 1548 log.trace("setDataValue({}, {}, {}): finish", rowIndex, columnIndex, newValue); 1549 } 1550 1551 @Override 1552 public void setDataValue(int index, Object bufObject, Object newValue) 1553 { 1554 try { 1555 updateStringBytes(bufObject, newValue, index); 1556 } 1557 catch (Exception ex) { 1558 log.debug("setDataValue({}, {}, {}): cell value update failure: ", index, bufObject, newValue, 1559 ex); 1560 } 1561 log.trace("setDataValue({}, {}, {}): finish", index, bufObject, newValue); 1562 } 1563 1564 private void updateStringBytes(Object curBuf, Object newValue, int bufStartIndex) 1565 { 1566 if (curBuf instanceof String[]) { 1567 Array.set(curBuf, bufStartIndex, newValue); 1568 } 1569 else if (curBuf instanceof byte[]) { 1570 // Update String using data represented as a byte[] 1571 int strLen = (int)typeSize; 1572 byte[] newValueBytes = ((String)newValue).getBytes(); 1573 byte[] curBytes = (byte[])curBuf; 1574 int n = Math.min(strLen, newValueBytes.length); 1575 1576 bufStartIndex *= typeSize; 1577 1578 System.arraycopy(newValueBytes, 0, curBytes, bufStartIndex, n); 1579 1580 bufStartIndex += n; 1581 n = strLen - newValueBytes.length; 1582 1583 // space padding 1584 for (int i = 0; i < n; i++) 1585 curBytes[bufStartIndex + i] = ' '; 1586 } 1587 1588 isValueChanged = true; 1589 } 1590 } 1591 1592 private static class CharDataProvider extends HDFDataProvider { 1593 private static final Logger log = LoggerFactory.getLogger(CharDataProvider.class); 1594 1595 CharDataProvider(final Datatype dtype, final Object dataBuf, final boolean dataTransposed) 1596 throws Exception 1597 { 1598 super(dtype, dataBuf, dataTransposed); 1599 } 1600 1601 @Override 1602 public Object getDataValue(int columnIndex, int rowIndex) 1603 { 1604 /* 1605 * Compatibility with HDF4 8-bit character types that get converted to a String 1606 * ahead of time. 1607 */ 1608 if (dataBuf instanceof String) { 1609 log.trace("getDataValue({}, {})=({}): finish", rowIndex, columnIndex, dataBuf); 1610 return dataBuf; 1611 } 1612 1613 return super.getDataValue(columnIndex, rowIndex); 1614 } 1615 } 1616 1617 private static class NumericalDataProvider extends HDFDataProvider { 1618 private static final Logger log = LoggerFactory.getLogger(NumericalDataProvider.class); 1619 1620 private final boolean isUINT64; 1621 private final boolean isFLT16; 1622 private final long typeSize; 1623 1624 NumericalDataProvider(final Datatype dtype, final Object dataBuf, final boolean dataTransposed) 1625 throws Exception 1626 { 1627 super(dtype, dataBuf, dataTransposed); 1628 1629 typeSize = dtype.getDatatypeSize(); 1630 isUINT64 = dtype.isUnsigned() && (typeSize == 8); 1631 isFLT16 = dtype.isFloat() && (typeSize == 2); 1632 } 1633 1634 @Override 1635 public Object getDataValue(int columnIndex, int rowIndex) 1636 { 1637 super.getDataValue(columnIndex, rowIndex); 1638 1639 try { 1640 if (isFLT16) 1641 theValue = Float.float16ToFloat((short)theValue); 1642 else if (isUINT64) 1643 theValue = Tools.convertUINT64toBigInt(Long.valueOf((long)theValue)); 1644 } 1645 catch (Exception ex) { 1646 log.debug("getDataValue({}, {}): failure: ", rowIndex, columnIndex, ex); 1647 theValue = DataFactoryUtils.errStr; 1648 } 1649 1650 log.trace("getDataValue({}, {})=({}): finish", rowIndex, columnIndex, theValue); 1651 1652 return theValue; 1653 } 1654 1655 @Override 1656 public Object getDataValue(Object obj, int index) 1657 { 1658 super.getDataValue(obj, index); 1659 1660 try { 1661 if (isFLT16) 1662 theValue = Float.float16ToFloat((short)theValue); 1663 else if (isUINT64) 1664 theValue = Tools.convertUINT64toBigInt(Long.valueOf((long)theValue)); 1665 } 1666 catch (Exception ex) { 1667 log.debug("getDataValue({}): failure: ", index, ex); 1668 theValue = DataFactoryUtils.errStr; 1669 } 1670 1671 log.trace("getDataValue({})=({}): finish", index, theValue); 1672 1673 return theValue; 1674 } 1675 1676 /** 1677 * update the data value of a type. 1678 * 1679 * @param columnIndex the column 1680 * @param rowIndex the row 1681 * @param newValue the new data value object 1682 */ 1683 @Override 1684 public void setDataValue(int columnIndex, int rowIndex, Object newValue) 1685 { 1686 log.trace("setDataValue({}, {})=({}): start", rowIndex, columnIndex, newValue); 1687 try { 1688 if (isFLT16) { 1689 // must convert string from float to short first 1690 float fValue = Float.parseFloat((String)newValue); 1691 short sValue = Float.floatToFloat16(fValue); 1692 // setDataValue requires a String, so convert short to String 1693 String strValue = Short.toString(sValue); 1694 super.setDataValue(columnIndex, rowIndex, strValue); 1695 } 1696 else 1697 super.setDataValue(columnIndex, rowIndex, newValue); 1698 } 1699 catch (Exception ex) { 1700 log.debug("setDataValue({}, {})=({}): failure: ", rowIndex, columnIndex, newValue, ex); 1701 theValue = DataFactoryUtils.errStr; 1702 } 1703 } 1704 1705 /** 1706 * When a parent HDFDataProvider (such as an ArrayDataProvider) wants to set a data value by routing 1707 * the operation through its base HDFDataProvider, the parent HDFDataProvider will generally know the 1708 * direct index to have the base provider use. This method is to facilitate this kind of behavior. 1709 * 1710 * Note that this method takes two Object parameters, one which is the object that the method should 1711 * set its data inside of and one which is the new value to set. This is to be able to nicely support 1712 * nested compound DataProviders. 1713 * 1714 * @param index the index into the data array 1715 * @param bufObject the data object 1716 * @param newValue the new data object 1717 */ 1718 public void setDataValue(int index, Object bufObject, Object newValue) 1719 { 1720 log.trace("setDataValue({}: {})=({}): start", index, bufObject, newValue); 1721 try { 1722 if (isFLT16) { 1723 // must convert string from float to short first 1724 float fValue = Float.parseFloat((String)newValue); 1725 short sValue = Float.floatToFloat16(fValue); 1726 // setDataValue requires a String, so convert short to String 1727 String strValue = Short.toString(sValue); 1728 super.setDataValue(index, bufObject, strValue); 1729 } 1730 else 1731 super.setDataValue(index, bufObject, newValue); 1732 } 1733 catch (Exception ex) { 1734 log.debug("setDataValue({}, {})=({}): updateAtomicValue failure: ", index, bufObject, 1735 newValue, ex); 1736 } 1737 log.trace("setDataValue({}, {})=({}): finish", index, bufObject, newValue); 1738 } 1739 } 1740 1741 private static class EnumDataProvider extends HDFDataProvider { 1742 private static final Logger log = LoggerFactory.getLogger(EnumDataProvider.class); 1743 1744 EnumDataProvider(final Datatype dtype, final Object dataBuf, final boolean dataTransposed) 1745 throws Exception 1746 { 1747 super(dtype, dataBuf, dataTransposed); 1748 } 1749 } 1750 1751 private static class BitfieldDataProvider extends HDFDataProvider { 1752 private static final Logger log = LoggerFactory.getLogger(BitfieldDataProvider.class); 1753 1754 private final long typeSize; 1755 1756 BitfieldDataProvider(final Datatype dtype, final Object dataBuf, final boolean dataTransposed) 1757 throws Exception 1758 { 1759 super(dtype, dataBuf, dataTransposed); 1760 1761 typeSize = dtype.getDatatypeSize(); 1762 } 1763 1764 @Override 1765 public Object getDataValue(int columnIndex, int rowIndex) 1766 { 1767 try { 1768 int bufIndex = physicalLocationToBufIndex(rowIndex, columnIndex); 1769 1770 bufIndex *= typeSize; 1771 theValue = populateByteArray(dataBuf, bufIndex); 1772 } 1773 catch (Exception ex) { 1774 log.debug("getDataValue({}, {}): failure: ", rowIndex, columnIndex, ex); 1775 theValue = DataFactoryUtils.errStr; 1776 } 1777 1778 log.trace("getDataValue({}, {})=({}): finish", rowIndex, columnIndex, theValue); 1779 1780 return theValue; 1781 } 1782 1783 @Override 1784 public Object getDataValue(Object obj, int index) 1785 { 1786 try { 1787 index *= typeSize; 1788 theValue = populateByteArray(obj, index); 1789 } 1790 catch (Exception ex) { 1791 log.debug("getDataValue({}): ", index, ex); 1792 theValue = DataFactoryUtils.errStr; 1793 } 1794 1795 log.trace("getDataValue({})=({}): finish", index, theValue); 1796 1797 return theValue; 1798 } 1799 1800 private byte[] populateByteArray(Object byteBuf, int startIndex) 1801 { 1802 byte[] byteElements = new byte[(int)typeSize]; 1803 1804 for (int i = 0; i < typeSize; i++) 1805 byteElements[i] = Array.getByte(byteBuf, startIndex + i); 1806 1807 return byteElements; 1808 } 1809 } 1810 1811 private static class RefDataProvider extends HDFDataProvider { 1812 private static final Logger log = LoggerFactory.getLogger(RefDataProvider.class); 1813 1814 private final long typeSize; 1815 private final H5Datatype h5dtype; 1816 1817 RefDataProvider(final Datatype dtype, final Object dataBuf, final boolean dataTransposed) 1818 throws Exception 1819 { 1820 super(dtype, dataBuf, dataTransposed); 1821 1822 h5dtype = (H5Datatype)dtype; 1823 typeSize = h5dtype.getDatatypeSize(); 1824 log.trace("typeSize={}=", typeSize); 1825 } 1826 1827 @Override 1828 public Object getDataValue(int columnIndex, int rowIndex) 1829 { 1830 log.trace("getDataValue({}, {}): start", rowIndex, columnIndex); 1831 1832 try { 1833 int bufIndex = physicalLocationToBufIndex(rowIndex, columnIndex); 1834 byte[] rElements = null; 1835 1836 log.trace("getDataValue(dataBuf={}): start", dataBuf); 1837 if (dataBuf instanceof ArrayList) 1838 rElements = (byte[])((ArrayList)dataBuf).get(bufIndex); 1839 else 1840 rElements = (byte[])dataBuf; 1841 1842 if (h5dtype.isStdRef()) 1843 theValue = populateReference(rElements, 0); 1844 else if (h5dtype.isRegRef()) 1845 theValue = populateReferenceRegion(rElements, 0); 1846 else if (h5dtype.isRefObj()) 1847 theValue = populateReferenceObject(rElements, 0); 1848 else 1849 theValue = super.getDataValue(columnIndex, rowIndex); 1850 } 1851 catch (Exception ex) { 1852 log.debug("getDataValue({}, {}): failure: ", rowIndex, columnIndex, ex); 1853 theValue = DataFactoryUtils.errStr; 1854 } 1855 1856 log.trace("getDataValue({}, {})({}): finish", rowIndex, columnIndex, theValue); 1857 1858 return theValue; 1859 } 1860 1861 @Override 1862 public Object getDataValue(Object obj, int index) 1863 { 1864 log.trace("getDataValue(Object:{}, {}): start", obj, index); 1865 byte[] rElements = null; 1866 if (obj instanceof ArrayList) 1867 rElements = (byte[])((ArrayList)obj).get(index); 1868 else 1869 rElements = (byte[])obj; 1870 1871 log.trace("getDataValue(rElements:{})", rElements); 1872 try { 1873 if (h5dtype.isStdRef()) 1874 theValue = populateReference(rElements, 0); 1875 else if (h5dtype.isRegRef()) 1876 theValue = populateReferenceRegion(rElements, 0); 1877 else if (h5dtype.isRefObj()) 1878 theValue = populateReferenceObject(rElements, 0); 1879 else 1880 theValue = super.getDataValue(obj, index); 1881 } 1882 catch (Exception ex) { 1883 log.debug("getDataValueObject:({}, {}): ", obj, index, ex); 1884 theValue = DataFactoryUtils.errStr; 1885 } 1886 1887 log.trace("getDataValue(Object:{}, {})({}): finish", obj, index, theValue); 1888 1889 return theValue; 1890 } 1891 1892 private String populateReference(Object byteBuf, int startIndex) 1893 { 1894 byte[] rElements = new byte[(int)typeSize]; 1895 try { 1896 System.arraycopy(byteBuf, startIndex * (int)typeSize, rElements, 0, (int)typeSize); 1897 } 1898 catch (Exception err) { 1899 log.trace("populateReference(): arraycopy failure: ", err); 1900 } 1901 String regionStr = null; 1902 if (H5Datatype.zeroArrayCheck(rElements)) 1903 regionStr = "NULL"; 1904 else 1905 regionStr = ((H5ReferenceType)h5dtype).getReferenceRegion((byte[])byteBuf, false); 1906 log.trace("populateReference regionStr={}", regionStr); 1907 1908 return regionStr; 1909 } 1910 1911 private String populateReferenceRegion(Object byteBuf, int startIndex) 1912 { 1913 long fid = ((HObject)dataFormatReference).getFileFormat().getFID(); 1914 byte[] rElements = new byte[(int)typeSize]; 1915 try { 1916 System.arraycopy(byteBuf, startIndex * (int)typeSize, rElements, 0, (int)typeSize); 1917 } 1918 catch (Exception err) { 1919 log.trace("populateReferenceRegion(): arraycopy failure: ", err); 1920 } 1921 String regionStr = null; 1922 if (H5Datatype.zeroArrayCheck(rElements)) 1923 regionStr = "NULL"; 1924 else 1925 regionStr = H5Datatype.descRegionDataset(fid, rElements); 1926 log.trace("populateReferenceRegion regionStr={}", regionStr); 1927 1928 return regionStr; 1929 } 1930 1931 private String populateReferenceObject(Object byteBuf, int startIndex) 1932 { 1933 long fid = ((HObject)dataFormatReference).getFileFormat().getFID(); 1934 log.trace("populateReferenceObject byteBuf={}", byteBuf); 1935 byte[] rElements = new byte[(int)typeSize]; 1936 try { 1937 System.arraycopy(byteBuf, startIndex * (int)typeSize, rElements, 0, (int)typeSize); 1938 } 1939 catch (Exception err) { 1940 log.trace("populateReferenceRegion(): arraycopy failure: ", err); 1941 } 1942 String objectStr = null; 1943 if (H5Datatype.zeroArrayCheck(rElements)) 1944 objectStr = "NULL"; 1945 else 1946 objectStr = H5Datatype.descReferenceObject(fid, rElements); 1947 log.trace("populateReferenceObject objectStr={}", objectStr); 1948 1949 return objectStr; 1950 } 1951 } 1952}