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