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 * @return if the datavalue has chaged 569 */ 570 public final boolean getIsValueChanged() { return isValueChanged; } 571 572 /** 573 * Update the data buffer for this HDFDataProvider. This is necessary for when 574 * the data that has been read is invalidated, such as when flipping through 575 * 'pages' in a > 2-dimensional dataset. 576 * 577 * @param newBuf 578 * the new data buffer 579 */ 580 public final void updateDataBuffer(Object newBuf) 581 { 582 this.dataBuf = newBuf; 583 584 if (rank > 1) { 585 rowCount = dataFormatReference.getHeight(); 586 colCount = dataFormatReference.getWidth(); 587 } 588 else { 589 rowCount = (int)dataFormatReference.getSelectedDims()[0]; 590 colCount = 1; 591 } 592 log.trace("updateDataBuffer: rowCount={} colCount={}", rowCount, colCount); 593 } 594 } 595 596 /* 597 * A DataProvider for Compound datatype datasets which is a composite of 598 * DataProviders, one for each selected member of the Compound datatype. 599 */ 600 private static class CompoundDataProvider extends HDFDataProvider { 601 private static final Logger log = LoggerFactory.getLogger(CompoundDataProvider.class); 602 603 private final HashMap<Integer, Integer> baseProviderIndexMap; 604 private final HashMap<Integer, Integer> relCmpdStartIndexMap; 605 606 private final HDFDataProvider[] baseTypeProviders; 607 608 private final Datatype[] selectedMemberTypes; 609 610 private final int[] selectedMemberOrders; 611 612 private final int nSubColumns; 613 private final int nCols; 614 private final int nRows; 615 616 CompoundDataProvider(final Datatype dtype, final Object dataBuf, final boolean dataTransposed) 617 throws Exception 618 { 619 super(dtype, dataBuf, dataTransposed); 620 621 CompoundDataFormat compoundFormat = (CompoundDataFormat)dataFormatReference; 622 selectedMemberTypes = compoundFormat.getSelectedMemberTypes(); 623 selectedMemberOrders = compoundFormat.getSelectedMemberOrders(); 624 625 List<Datatype> localSelectedTypes = 626 DataFactoryUtils.filterNonSelectedMembers(compoundFormat, dtype); 627 628 log.trace("setting up {} base HDFDataProviders", localSelectedTypes.size()); 629 630 baseTypeProviders = new HDFDataProvider[localSelectedTypes.size()]; 631 for (int i = 0; i < baseTypeProviders.length; i++) { 632 log.trace("retrieving DataProvider for member {}", i); 633 634 try { 635 baseTypeProviders[i] = 636 getDataProvider(localSelectedTypes.get(i), dataBuf, dataTransposed); 637 } 638 catch (Exception ex) { 639 log.debug("failed to retrieve DataProvider for member {}: ", i, ex); 640 baseTypeProviders[i] = null; 641 } 642 } 643 644 /* 645 * Build necessary index maps. 646 */ 647 HashMap<Integer, Integer>[] maps = 648 DataFactoryUtils.buildIndexMaps(compoundFormat, localSelectedTypes); 649 baseProviderIndexMap = maps[DataFactoryUtils.COL_TO_BASE_CLASS_MAP_INDEX]; 650 relCmpdStartIndexMap = maps[DataFactoryUtils.CMPD_START_IDX_MAP_INDEX]; 651 652 log.trace("index maps built: baseProviderIndexMap = {}, relColIdxMap = {}", 653 baseProviderIndexMap.toString(), relCmpdStartIndexMap.toString()); 654 655 if (baseProviderIndexMap.size() == 0) { 656 log.debug("base DataProvider index mapping is invalid - size 0"); 657 throw new Exception("CompoundDataProvider: invalid DataProvider mapping of size 0 built"); 658 } 659 660 if (relCmpdStartIndexMap.size() == 0) { 661 log.debug("compound field start index mapping is invalid - size 0"); 662 throw new Exception( 663 "CompoundDataProvider: invalid compound field start index mapping of size 0 built"); 664 } 665 666 /* 667 * nCols should represent the number of columns covered by this CompoundDataProvider 668 * only. For top-level CompoundDataProviders, this should be the entire width of the 669 * dataset. For nested CompoundDataProviders, nCols will be a subset of these columns. 670 */ 671 nCols = (int)compoundFormat.getWidth() * baseProviderIndexMap.size(); 672 nRows = (int)compoundFormat.getHeight(); 673 674 nSubColumns = (int)compoundFormat.getWidth(); 675 } 676 677 @Override 678 public Object getDataValue(int columnIndex, int rowIndex) 679 { 680 try { 681 int fieldIdx = columnIndex; 682 int rowIdx = rowIndex; 683 684 if (nSubColumns > 1) { // multi-dimension compound dataset 685 /* 686 * Make sure fieldIdx is within a valid range, since even for multi-dimensional 687 * compound datasets there will only be as many lists of data as there are 688 * members in a single compound type. 689 */ 690 fieldIdx %= selectedMemberTypes.length; 691 692 int realColIdx = columnIndex / selectedMemberTypes.length; 693 rowIdx = rowIndex * nSubColumns + realColIdx; 694 } 695 696 int providerIndex = baseProviderIndexMap.get(fieldIdx); 697 Object colValue = ((List<?>)dataBuf).get(providerIndex); 698 if (colValue == null) 699 return DataFactoryUtils.nullStr; 700 701 /* 702 * Delegate data retrieval to one of the base DataProviders according to the 703 * index of the relevant compound field. 704 */ 705 HDFDataProvider base = baseTypeProviders[providerIndex]; 706 if (base instanceof CompoundDataProvider) 707 /* 708 * Adjust the compound field index by subtracting the starting index of the 709 * nested compound that we are delegating to. When the nested compound's index 710 * map is setup correctly, this adjusted index should map to the correct field 711 * among the nested compound's members. 712 */ 713 theValue = 714 base.getDataValue(colValue, fieldIdx - relCmpdStartIndexMap.get(fieldIdx), rowIdx); 715 else if (base instanceof ArrayDataProvider) { 716 /* 717 * TODO: quick temporary fix for specific compound of array of compound files. 718 * Transforms the given column index into a relative index from the starting 719 * index of the array of compound field. 720 */ 721 int arrCompoundStartIdx = columnIndex; 722 HDFDataProvider theProvider; 723 while (arrCompoundStartIdx >= 0) { 724 try { 725 theProvider = 726 baseTypeProviders[baseProviderIndexMap.get(arrCompoundStartIdx - 1)]; 727 if (theProvider != base) 728 break; 729 730 arrCompoundStartIdx--; 731 } 732 catch (Exception ex) { 733 break; 734 } 735 } 736 737 int adjustedColIndex = columnIndex - arrCompoundStartIdx; 738 739 theValue = base.getDataValue(colValue, adjustedColIndex, rowIdx); 740 } 741 else 742 theValue = base.getDataValue(colValue, rowIdx); 743 } 744 catch (Exception ex) { 745 log.debug("getDataValue({}, {}): failure: ", rowIndex, columnIndex, ex); 746 theValue = DataFactoryUtils.errStr; 747 } 748 749 log.trace("getDataValue({}, {}): finish", rowIndex, columnIndex); 750 751 return theValue; 752 } 753 754 @Override 755 public Object getDataValue(Object obj, int columnIndex, int rowIndex) 756 { 757 try { 758 int providerIndex = baseProviderIndexMap.get(columnIndex); 759 Object colValue = ((List<?>)obj).get(providerIndex); 760 if (colValue == null) 761 return DataFactoryUtils.nullStr; 762 763 /* 764 * Delegate data retrieval to one of the base DataProviders according to the 765 * index of the relevant compound field. 766 */ 767 HDFDataProvider base = baseTypeProviders[providerIndex]; 768 if (base instanceof CompoundDataProvider) 769 /* 770 * Adjust the compound field index by subtracting the starting index of the 771 * nested compound that we are delegating to. When the nested compound's index 772 * map is setup correctly, this adjusted index should map to the correct field 773 * among the nested compound's members. 774 */ 775 theValue = base.getDataValue( 776 colValue, columnIndex - relCmpdStartIndexMap.get(columnIndex), rowIndex); 777 else if (base instanceof ArrayDataProvider) 778 theValue = base.getDataValue(colValue, columnIndex, rowIndex); 779 else 780 theValue = base.getDataValue(colValue, rowIndex); 781 } 782 catch (Exception ex) { 783 log.debug("getDataValue({}, {}): failure: ", rowIndex, columnIndex, ex); 784 theValue = DataFactoryUtils.errStr; 785 } 786 log.trace("getDataValue({})=({}): finish", rowIndex, columnIndex); 787 788 return theValue; 789 } 790 791 @Override 792 public Object getDataValue(Object obj, int index) 793 { 794 throw new UnsupportedOperationException( 795 "getDataValue(Object, int) should not be called for CompoundDataProviders"); 796 } 797 798 @Override 799 public void setDataValue(int columnIndex, int rowIndex, Object newValue) 800 { 801 if ((newValue == null) || ((newValue = ((String)newValue).trim()) == null)) { 802 log.debug("setDataValue({}, {})=({}): cell value not updated; new value is null", rowIndex, 803 columnIndex, newValue); 804 return; 805 } 806 807 // No need to update if values are the same 808 Object oldVal = this.getDataValue(columnIndex, rowIndex); 809 if ((oldVal != null) && newValue.equals(oldVal.toString())) { 810 log.debug("setDataValue({}, {})=({}): cell value not updated; new value same as old value", 811 rowIndex, columnIndex, newValue); 812 return; 813 } 814 815 try { 816 int fieldIdx = columnIndex; 817 int rowIdx = rowIndex; 818 819 if (nSubColumns > 1) { // multi-dimension compound dataset 820 /* 821 * Make sure fieldIdx is within a valid range, since even for multi-dimensional 822 * compound datasets there will only be as many lists of data as there are 823 * members in a single compound type. 824 */ 825 fieldIdx %= selectedMemberTypes.length; 826 827 int realColIdx = columnIndex / selectedMemberTypes.length; 828 rowIdx = rowIndex * nSubColumns + realColIdx; 829 } 830 831 int providerIndex = baseProviderIndexMap.get(fieldIdx); 832 Object colValue = ((List<?>)dataBuf).get(providerIndex); 833 if (colValue == null) { 834 log.debug("setDataValue({}, {})=({}): colValue is null", rowIndex, columnIndex, newValue); 835 return; 836 } 837 838 /* 839 * Delegate data setting to one of the base DataProviders according to the index 840 * of the relevant compound field. 841 */ 842 HDFDataProvider base = baseTypeProviders[providerIndex]; 843 if (base.isContainerType) 844 /* 845 * Adjust the compound field index by subtracting the starting index of the 846 * nested compound that we are delegating to. When the nested compound's index 847 * map is setup correctly, this adjusted index should map to the correct field 848 * among the nested compound's members. 849 */ 850 base.setDataValue(fieldIdx - relCmpdStartIndexMap.get(fieldIdx), rowIdx, colValue, 851 newValue); 852 else 853 base.setDataValue(rowIdx, colValue, newValue); 854 855 isValueChanged = true; 856 } 857 catch (Exception ex) { 858 log.debug("setDataValue({}, {})=({}): cell value update failure: ", rowIndex, columnIndex, 859 newValue); 860 } 861 log.trace("setDataValue({}, {})=({}): finish", rowIndex, columnIndex, newValue); 862 863 /* 864 * TODO: throwing error dialogs when something fails? 865 * 866 * Tools.showError(shell, "Select", "Unable to set new value:\n\n " + ex); 867 */ 868 } 869 870 @Override 871 public void setDataValue(int columnIndex, int rowIndex, Object bufObject, Object newValue) 872 { 873 try { 874 int providerIndex = baseProviderIndexMap.get(columnIndex); 875 Object colValue = ((List<?>)bufObject).get(providerIndex); 876 if (colValue == null) { 877 log.debug("setDataValue({}, {}, {})=({}): colValue is null", rowIndex, columnIndex, 878 bufObject, newValue); 879 return; 880 } 881 882 /* 883 * Delegate data setting to one of the base DataProviders according to the index 884 * of the relevant compound field. 885 */ 886 HDFDataProvider base = baseTypeProviders[providerIndex]; 887 if (base.isContainerType) 888 /* 889 * Adjust the compound field index by subtracting the starting index of the 890 * nested compound that we are delegating to. When the nested compound's index 891 * map is setup correctly, this adjusted index should map to the correct field 892 * among the nested compound's members. 893 */ 894 base.setDataValue(columnIndex - relCmpdStartIndexMap.get(columnIndex), rowIndex, colValue, 895 newValue); 896 else 897 base.setDataValue(rowIndex, colValue, newValue); 898 899 isValueChanged = true; 900 } 901 catch (Exception ex) { 902 log.debug("setDataValue({}, {}, {})=({}): cell value update failure: ", rowIndex, columnIndex, 903 bufObject, newValue, ex); 904 } 905 log.trace("setDataValue({}, {}, {})=({}): finish", rowIndex, columnIndex, bufObject, newValue); 906 } 907 908 @Override 909 public void setDataValue(int index, Object bufObject, Object newValue) 910 { 911 throw new UnsupportedOperationException( 912 "setDataValue(int, Object, Object) should not be called for CompoundDataProviders"); 913 } 914 915 @Override 916 public int getColumnCount() 917 { 918 return nCols; 919 } 920 921 @Override 922 public int getRowCount() 923 { 924 return nRows; 925 } 926 } 927 928 private static class ArrayDataProvider extends HDFDataProvider { 929 private static final Logger log = LoggerFactory.getLogger(ArrayDataProvider.class); 930 931 private final HDFDataProvider baseTypeDataProvider; 932 933 private final Object[] arrayElements; 934 private final long arraySize; 935 936 private final int nCols; 937 938 ArrayDataProvider(final Datatype dtype, final Object dataBuf, final boolean dataTransposed) 939 throws Exception 940 { 941 super(dtype, dataBuf, dataTransposed); 942 943 Datatype baseType = dtype.getDatatypeBase(); 944 945 baseTypeDataProvider = getDataProvider(baseType, dataBuf, dataTransposed); 946 947 if (baseType.isVarStr()) 948 arraySize = dtype.getArrayDims()[0]; 949 else if (baseType.isBitField() || baseType.isOpaque()) 950 arraySize = dtype.getDatatypeSize(); 951 else 952 arraySize = dtype.getDatatypeSize() / baseType.getDatatypeSize(); 953 954 arrayElements = new Object[(int)arraySize]; 955 956 if (baseTypeDataProvider instanceof CompoundDataProvider) 957 nCols = (int)arraySize * ((CompoundDataProvider)baseTypeDataProvider).nCols; 958 else 959 nCols = super.getColumnCount(); 960 } 961 962 @Override 963 public Object getDataValue(int columnIndex, int rowIndex) 964 { 965 try { 966 int bufIndex = physicalLocationToBufIndex(rowIndex, columnIndex); 967 968 bufIndex *= arraySize; 969 970 if (baseTypeDataProvider instanceof CompoundDataProvider) { 971 /* 972 * Pass row and column indices down where they will be adjusted. 973 */ 974 theValue = retrieveArrayOfCompoundElements(dataBuf, columnIndex, rowIndex); 975 } 976 else if (baseTypeDataProvider instanceof ArrayDataProvider) { 977 /* 978 * TODO: assign to global arrayElements. 979 */ 980 theValue = retrieveArrayOfArrayElements(dataBuf, columnIndex, bufIndex); 981 } 982 else { 983 /* 984 * TODO: assign to global arrayElements. 985 */ 986 theValue = retrieveArrayOfAtomicElements(dataBuf, bufIndex); 987 } 988 } 989 catch (Exception ex) { 990 log.debug("getDataValue({}, {}): failure: ", rowIndex, columnIndex, ex); 991 theValue = DataFactoryUtils.errStr; 992 } 993 994 log.trace("getDataValue({}, {})({}): finish", rowIndex, columnIndex, theValue); 995 996 return theValue; 997 } 998 999 @Override 1000 public Object getDataValue(Object obj, int columnIndex, int rowIndex) 1001 { 1002 try { 1003 long index = rowIndex * arraySize; 1004 1005 if (baseTypeDataProvider instanceof CompoundDataProvider) { 1006 /* 1007 * Pass row and column indices down where they will be adjusted. 1008 */ 1009 theValue = retrieveArrayOfCompoundElements(obj, columnIndex, rowIndex); 1010 } 1011 else if (baseTypeDataProvider instanceof ArrayDataProvider) { 1012 theValue = retrieveArrayOfArrayElements(obj, columnIndex, (int)index); 1013 } 1014 else { 1015 theValue = retrieveArrayOfAtomicElements(obj, (int)index); 1016 } 1017 } 1018 catch (Exception ex) { 1019 log.debug("getDataValue({}, {}): failure: ", rowIndex, columnIndex, ex); 1020 theValue = DataFactoryUtils.errStr; 1021 } 1022 1023 return theValue; 1024 } 1025 1026 private Object[] retrieveArrayOfCompoundElements(Object objBuf, int columnIndex, int rowIndex) 1027 { 1028 long adjustedRowIdx = 1029 (rowIndex * arraySize * colCount) + 1030 (columnIndex / ((CompoundDataProvider)baseTypeDataProvider).baseProviderIndexMap.size()); 1031 long adjustedColIdx = 1032 columnIndex % ((CompoundDataProvider)baseTypeDataProvider).baseProviderIndexMap.size(); 1033 1034 /* 1035 * Since we flatten array of compound types, we only need to return a single 1036 * value. 1037 */ 1038 return new Object[] { 1039 baseTypeDataProvider.getDataValue(objBuf, (int)adjustedColIdx, (int)adjustedRowIdx)}; 1040 } 1041 1042 private Object[] retrieveArrayOfArrayElements(Object objBuf, int columnIndex, int startRowIndex) 1043 { 1044 Object[] tempArray = new Object[(int)arraySize]; 1045 1046 for (int i = 0; i < arraySize; i++) 1047 tempArray[i] = baseTypeDataProvider.getDataValue(objBuf, columnIndex, startRowIndex + i); 1048 1049 return tempArray; 1050 } 1051 1052 private Object[] retrieveArrayOfAtomicElements(Object objBuf, int rowStartIdx) 1053 { 1054 Object[] tempArray = new Object[(int)arraySize]; 1055 1056 for (int i = 0; i < arraySize; i++) 1057 tempArray[i] = baseTypeDataProvider.getDataValue(objBuf, rowStartIdx + i); 1058 1059 return tempArray; 1060 } 1061 1062 @Override 1063 public Object getDataValue(Object obj, int index) 1064 { 1065 throw new UnsupportedOperationException( 1066 "getDataValue(Object, int) should not be called for ArrayDataProviders"); 1067 } 1068 1069 @Override 1070 public void setDataValue(int columnIndex, int rowIndex, Object newValue) 1071 { 1072 try { 1073 int bufIndex = physicalLocationToBufIndex(rowIndex, columnIndex); 1074 1075 bufIndex *= arraySize; 1076 1077 updateArrayElements(dataBuf, newValue, columnIndex, bufIndex); 1078 } 1079 catch (Exception ex) { 1080 log.debug("setDataValue({}, {}, {}): cell value update failure: ", rowIndex, columnIndex, 1081 newValue, ex); 1082 } 1083 log.trace("setDataValue({}, {})=({}): finish", rowIndex, columnIndex, newValue); 1084 } 1085 1086 @Override 1087 public void setDataValue(int columnIndex, int rowIndex, Object bufObject, Object newValue) 1088 { 1089 try { 1090 long bufIndex = rowIndex * arraySize; 1091 1092 updateArrayElements(bufObject, newValue, columnIndex, (int)bufIndex); 1093 } 1094 catch (Exception ex) { 1095 log.debug("setDataValue({}, {}, {}, {}): cell value update failure: ", rowIndex, columnIndex, 1096 bufObject, newValue, ex); 1097 } 1098 log.trace("setDataValue({}, {}, {})=({}): finish", rowIndex, columnIndex, bufObject, newValue); 1099 } 1100 1101 @Override 1102 public void setDataValue(int index, Object bufObject, Object newValue) 1103 { 1104 throw new UnsupportedOperationException( 1105 "setDataValue(int, Object, Object) should not be called for ArrayDataProviders"); 1106 } 1107 1108 private void updateArrayElements(Object curBuf, Object newValue, int columnIndex, int bufStartIndex) 1109 { 1110 StringTokenizer st = new StringTokenizer((String)newValue, ",[]"); 1111 if (st.countTokens() < arraySize) { 1112 /* 1113 * TODO: 1114 */ 1115 /* Tools.showError(shell, "Select", "Number of data points < " + morder + "."); */ 1116 log.debug("updateArrayElements(): number of data points ({}) < array size {}", 1117 st.countTokens(), arraySize); 1118 log.trace("updateArrayElements({}, {}, {}): finish", curBuf, newValue, bufStartIndex); 1119 return; 1120 } 1121 1122 if (baseTypeDataProvider instanceof CompoundDataProvider) 1123 updateArrayOfCompoundElements(st, curBuf, columnIndex, bufStartIndex); 1124 else if (baseTypeDataProvider instanceof ArrayDataProvider) 1125 updateArrayOfArrayElements(st, curBuf, columnIndex, bufStartIndex); 1126 else 1127 updateArrayOfAtomicElements(st, curBuf, bufStartIndex); 1128 } 1129 1130 private void updateArrayOfCompoundElements(StringTokenizer tokenizer, Object curBuf, int columnIndex, 1131 int bufStartIndex) 1132 { 1133 for (int i = 0; i < arraySize; i++) { 1134 List<?> cmpdDataList = (List<?>)((Object[])curBuf)[i]; 1135 baseTypeDataProvider.setDataValue(columnIndex, bufStartIndex + i, cmpdDataList, 1136 tokenizer.nextToken().trim()); 1137 isValueChanged = isValueChanged || baseTypeDataProvider.getIsValueChanged(); 1138 } 1139 } 1140 1141 private void updateArrayOfArrayElements(StringTokenizer tokenizer, Object curBuf, int columnIndex, 1142 int bufStartIndex) 1143 { 1144 for (int i = 0; i < arraySize; i++) { 1145 /* 1146 * TODO: not quite right. 1147 */ 1148 baseTypeDataProvider.setDataValue(columnIndex, bufStartIndex + i, curBuf, 1149 tokenizer.nextToken().trim()); 1150 isValueChanged = isValueChanged || baseTypeDataProvider.getIsValueChanged(); 1151 } 1152 } 1153 1154 private void updateArrayOfAtomicElements(StringTokenizer tokenizer, Object curBuf, int bufStartIndex) 1155 { 1156 for (int i = 0; i < arraySize; i++) { 1157 baseTypeDataProvider.setDataValue(bufStartIndex + i, curBuf, tokenizer.nextToken().trim()); 1158 isValueChanged = isValueChanged || baseTypeDataProvider.getIsValueChanged(); 1159 } 1160 } 1161 1162 @Override 1163 public int getColumnCount() 1164 { 1165 return nCols; 1166 } 1167 } 1168 1169 private static class VlenDataProvider extends HDFDataProvider { 1170 private static final Logger log = LoggerFactory.getLogger(VlenDataProvider.class); 1171 1172 private final HDFDataProvider baseTypeDataProvider; 1173 1174 private final StringBuilder buffer; 1175 1176 private final int baseTypeClass; 1177 1178 VlenDataProvider(final Datatype dtype, final Object dataBuf, final boolean dataTransposed) 1179 throws Exception 1180 { 1181 super(dtype, dataBuf, dataTransposed); 1182 1183 Datatype baseType = dtype.getDatatypeBase(); 1184 baseTypeClass = baseType.getDatatypeClass(); 1185 1186 baseTypeDataProvider = getDataProvider(baseType, dataBuf, dataTransposed); 1187 1188 buffer = new StringBuilder(); 1189 } 1190 1191 @Override 1192 public Object getDataValue(int columnIndex, int rowIndex) 1193 { 1194 buffer.setLength(0); 1195 1196 try { 1197 int bufIndex = physicalLocationToBufIndex(rowIndex, columnIndex); 1198 1199 if (baseTypeDataProvider instanceof CompoundDataProvider) { 1200 /* 1201 * Pass row and column indices down where they will be adjusted. 1202 */ 1203 theValue = retrieveArrayOfCompoundElements(dataBuf, columnIndex, rowIndex); 1204 } 1205 else if (baseTypeDataProvider instanceof ArrayDataProvider) { 1206 /* 1207 * TODO: assign to global arrayElements. 1208 */ 1209 theValue = retrieveArrayOfArrayElements(dataBuf, columnIndex, bufIndex); 1210 } 1211 else if (baseTypeDataProvider instanceof RefDataProvider) { 1212 /* 1213 * TODO: assign to global arrayElements. 1214 */ 1215 theValue = retrieveArrayOfArrayElements(dataBuf, columnIndex, bufIndex); 1216 } 1217 else { 1218 /* 1219 * TODO: assign to global arrayElements. 1220 */ 1221 theValue = retrieveArrayOfAtomicElements(dataBuf, bufIndex); 1222 } 1223 } 1224 catch (Exception ex) { 1225 log.debug("getDataValue(rowIndex={}, columnIndex={}): failure: ", rowIndex, columnIndex, ex); 1226 theValue = DataFactoryUtils.errStr; 1227 } 1228 1229 log.trace("getDataValue(rowIndex={}, columnIndex={})=({}): finish", rowIndex, columnIndex, 1230 theValue); 1231 1232 return theValue; 1233 } 1234 1235 @Override 1236 public Object getDataValue(Object obj, int columnIndex, int rowIndex) 1237 { 1238 buffer.setLength(0); 1239 1240 try { 1241 long vlSize = Array.getLength(obj); 1242 log.debug("getDataValue(): vlSize={} obj={}", vlSize, obj); 1243 1244 if (baseTypeDataProvider instanceof CompoundDataProvider) { 1245 /* 1246 * Pass row and column indices down where they will be adjusted. 1247 */ 1248 theValue = retrieveArrayOfCompoundElements(obj, columnIndex, rowIndex); 1249 } 1250 else if (baseTypeDataProvider instanceof ArrayDataProvider) { 1251 theValue = retrieveArrayOfArrayElements(obj, columnIndex, rowIndex); 1252 } 1253 else if (baseTypeDataProvider instanceof RefDataProvider) { 1254 theValue = retrieveArrayOfArrayElements(obj, columnIndex, rowIndex); 1255 } 1256 else { 1257 theValue = retrieveArrayOfAtomicElements(obj, rowIndex); 1258 } 1259 } 1260 catch (Exception ex) { 1261 log.debug("getDataValue(rowIndex={}, columnIndex={}): failure: ", rowIndex, columnIndex, ex); 1262 theValue = DataFactoryUtils.errStr; 1263 } 1264 1265 log.trace("getDataValue(obj={}, rowIndex={}, columnIndex={})=({}): finish", obj, rowIndex, 1266 columnIndex, theValue); 1267 1268 return theValue; 1269 } 1270 1271 private Object[] retrieveArrayOfCompoundElements(Object objBuf, int columnIndex, int rowIndex) 1272 { 1273 long vlSize = Array.getLength(objBuf); 1274 log.debug("retrieveArrayOfCompoundElements(): vlSize={}", vlSize); 1275 long adjustedRowIdx = 1276 (rowIndex * vlSize * colCount) + 1277 (columnIndex / ((CompoundDataProvider)baseTypeDataProvider).baseProviderIndexMap.size()); 1278 long adjustedColIdx = 1279 columnIndex % ((CompoundDataProvider)baseTypeDataProvider).baseProviderIndexMap.size(); 1280 1281 /* 1282 * Since we flatten array of compound types, we only need to return a single 1283 * value. 1284 */ 1285 return new Object[] { 1286 baseTypeDataProvider.getDataValue(objBuf, (int)adjustedColIdx, (int)adjustedRowIdx)}; 1287 } 1288 1289 private Object[] retrieveArrayOfArrayElements(Object objBuf, int columnIndex, int startRowIndex) 1290 { 1291 log.debug("retrieveArrayOfArrayElements(): objBuf={}", objBuf); 1292 ArrayList<byte[]> vlElements = ((ArrayList[])objBuf)[startRowIndex]; 1293 log.debug("retrieveArrayOfArrayElements(): vlElements={}", vlElements); 1294 long vlSize = vlElements.size(); 1295 log.debug("retrieveArrayOfArrayElements(): vlSize={} length={}", vlSize, vlElements.size()); 1296 Object[] tempArray = new Object[(int)vlSize]; 1297 1298 for (int i = 0; i < vlSize; i++) { 1299 ArrayList<byte[]> ref_value = vlElements; 1300 StringBuilder sb = new StringBuilder(); 1301 sb.append("{"); 1302 for (int m = 0; m < ref_value.size(); m++) { 1303 if (m > 0) 1304 sb.append(", "); 1305 byte[] byteElements = ref_value.get(m); 1306 log.trace("retrieveArrayOfArrayElements byteElements={}", byteElements); 1307 sb.append(baseTypeDataProvider.getDataValue(byteElements, columnIndex, i)); 1308 } 1309 sb.append("}"); 1310 tempArray[i] = sb.toString(); 1311 } 1312 1313 return tempArray; 1314 } 1315 1316 private Object[] retrieveArrayOfAtomicElements(Object objBuf, int rowStartIdx) 1317 { 1318 ArrayList vlElements = ((ArrayList[])objBuf)[rowStartIdx]; 1319 long vlSize = vlElements.size(); 1320 log.debug("retrieveArrayOfAtomicElements(): vlSize={}", vlSize); 1321 Object[] tempArray = new Object[(int)vlSize]; 1322 1323 for (int i = 0; i < vlSize; i++) 1324 tempArray[i] = baseTypeDataProvider.getDataValue(vlElements.toArray(), i); 1325 1326 return tempArray; 1327 } 1328 1329 @Override 1330 public Object getDataValue(Object obj, int index) 1331 { 1332 throw new UnsupportedOperationException( 1333 "getDataValue(Object, int) should not be called for VlenDataProviders"); 1334 } 1335 1336 @Override 1337 public void setDataValue(int columnIndex, int rowIndex, Object newValue) 1338 { 1339 try { 1340 int bufIndex = physicalLocationToBufIndex(rowIndex, columnIndex); 1341 1342 long vlSize = Array.getLength(dataBuf); 1343 log.debug("setDataValue(): vlSize={}", vlSize); 1344 1345 updateArrayElements(dataBuf, newValue, columnIndex, rowIndex); 1346 } 1347 catch (Exception ex) { 1348 log.debug("setDataValue(rowIndex={}, columnIndex={}, {}): cell value update failure: ", 1349 rowIndex, columnIndex, newValue, ex); 1350 } 1351 log.trace("setDataValue(rowIndex={}, columnIndex={})=({}): finish", rowIndex, columnIndex, 1352 newValue); 1353 } 1354 1355 @Override 1356 public void setDataValue(int columnIndex, int rowIndex, Object bufObject, Object newValue) 1357 { 1358 try { 1359 long vlSize = Array.getLength(bufObject); 1360 log.debug("setDataValue(): vlSize={} for [c{}, r{}]", vlSize, columnIndex, rowIndex); 1361 1362 updateArrayElements(bufObject, newValue, columnIndex, rowIndex); 1363 } 1364 catch (Exception ex) { 1365 log.debug( 1366 "setDataValue(rowIndex={}, columnIndex={}, bufObject={}, {}): cell value update failure: ", 1367 rowIndex, columnIndex, bufObject, newValue, ex); 1368 } 1369 log.trace("setDataValue(rowIndex={}, columnIndex={}, bufObject={})=({}): finish", rowIndex, 1370 columnIndex, bufObject, newValue); 1371 } 1372 1373 @Override 1374 public void setDataValue(int index, Object bufObject, Object newValue) 1375 { 1376 throw new UnsupportedOperationException( 1377 "setDataValue(int, Object, Object) should not be called for VlenDataProviders"); 1378 } 1379 1380 private void updateArrayElements(Object curBuf, Object newValue, int columnIndex, int rowStartIndex) 1381 { 1382 long vlSize = Array.getLength(curBuf); 1383 log.debug("updateArrayElements(): vlSize={}", vlSize); 1384 1385 if (baseTypeDataProvider instanceof CompoundDataProvider) 1386 updateArrayOfCompoundElements(newValue, curBuf, columnIndex, rowStartIndex); 1387 else if (baseTypeDataProvider instanceof ArrayDataProvider) 1388 updateArrayOfArrayElements(newValue, curBuf, columnIndex, rowStartIndex); 1389 else if (baseTypeDataProvider instanceof VlenDataProvider) 1390 updateArrayOfArrayElements(newValue, curBuf, columnIndex, rowStartIndex); 1391 else 1392 updateArrayOfAtomicElements(newValue, curBuf, rowStartIndex); 1393 } 1394 1395 private void updateArrayOfCompoundElements(Object newValue, Object curBuf, int columnIndex, 1396 int rowIndex) 1397 { 1398 long vlSize = Array.getLength(curBuf); 1399 log.debug("updateArrayOfCompoundElements(): vlSize={}", vlSize); 1400 long adjustedRowIdx = 1401 (rowIndex * vlSize * colCount) + 1402 (columnIndex / ((CompoundDataProvider)baseTypeDataProvider).baseProviderIndexMap.size()); 1403 long adjustedColIdx = 1404 columnIndex % ((CompoundDataProvider)baseTypeDataProvider).baseProviderIndexMap.size(); 1405 1406 /* 1407 * Since we flatten array of compound types, we only need to update a single value. 1408 */ 1409 baseTypeDataProvider.setDataValue((int)adjustedColIdx, (int)adjustedRowIdx, curBuf, newValue); 1410 isValueChanged = isValueChanged || baseTypeDataProvider.getIsValueChanged(); 1411 } 1412 1413 private void updateArrayOfArrayElements(Object newValue, Object curBuf, int columnIndex, int rowIndex) 1414 { 1415 ArrayList vlElements = ((ArrayList[])curBuf)[rowIndex]; 1416 log.debug("updateArrayOfArrayElements(): vlElements={}", vlElements); 1417 long vlSize = vlElements.size(); 1418 log.debug("updateArrayOfArrayElements(): vlSize={}", vlSize); 1419 1420 StringTokenizer st = new StringTokenizer((String)newValue, ",[]"); 1421 int newcnt = st.countTokens(); 1422 1423 Object[] buffer = null; 1424 switch (baseTypeClass) { 1425 case Datatype.CLASS_CHAR: 1426 buffer = new Byte[newcnt]; 1427 break; 1428 case Datatype.CLASS_INTEGER: 1429 buffer = new Integer[newcnt]; 1430 break; 1431 case Datatype.CLASS_FLOAT: 1432 buffer = new Double[newcnt]; 1433 break; 1434 case Datatype.CLASS_STRING: 1435 buffer = new String[newcnt]; 1436 break; 1437 case Datatype.CLASS_REFERENCE: 1438 case Datatype.CLASS_OPAQUE: 1439 case Datatype.CLASS_BITFIELD: 1440 case Datatype.CLASS_ENUM: 1441 case Datatype.CLASS_ARRAY: 1442 case Datatype.CLASS_COMPOUND: 1443 case Datatype.CLASS_VLEN: 1444 default: 1445 buffer = new Object[newcnt]; 1446 break; 1447 } 1448 for (int i = 0; i < newcnt; i++) { 1449 baseTypeDataProvider.setDataValue(columnIndex, i, buffer, st.nextToken().trim()); 1450 isValueChanged = isValueChanged || baseTypeDataProvider.getIsValueChanged(); 1451 } 1452 vlElements = new ArrayList<>(Arrays.asList(buffer)); 1453 ((ArrayList[])curBuf)[rowIndex] = vlElements; 1454 } 1455 1456 private void updateArrayOfAtomicElements(Object newValue, Object curBuf, int rowStartIdx) 1457 { 1458 ArrayList vlElements = ((ArrayList[])curBuf)[rowStartIdx]; 1459 long vlSize = vlElements.size(); 1460 log.debug("updateArrayOfAtomicElements(): vlSize={}", vlSize); 1461 1462 StringTokenizer st = new StringTokenizer((String)newValue, ",[]"); 1463 int newcnt = st.countTokens(); 1464 log.debug("updateArrayOfAtomicElements(): count={}", newcnt); 1465 Object[] buffer = null; 1466 switch (baseTypeClass) { 1467 case Datatype.CLASS_CHAR: 1468 buffer = new Byte[newcnt]; 1469 break; 1470 case Datatype.CLASS_INTEGER: 1471 buffer = new Integer[newcnt]; 1472 break; 1473 case Datatype.CLASS_FLOAT: 1474 buffer = new Double[newcnt]; 1475 break; 1476 case Datatype.CLASS_STRING: 1477 buffer = new String[newcnt]; 1478 break; 1479 case Datatype.CLASS_REFERENCE: 1480 case Datatype.CLASS_OPAQUE: 1481 case Datatype.CLASS_BITFIELD: 1482 case Datatype.CLASS_ENUM: 1483 case Datatype.CLASS_ARRAY: 1484 case Datatype.CLASS_COMPOUND: 1485 case Datatype.CLASS_VLEN: 1486 default: 1487 buffer = new Object[newcnt]; 1488 break; 1489 } 1490 for (int i = 0; i < newcnt; i++) { 1491 baseTypeDataProvider.setDataValue(i, buffer, st.nextToken().trim()); 1492 isValueChanged = isValueChanged || baseTypeDataProvider.getIsValueChanged(); 1493 } 1494 String bname = buffer.getClass().getName(); 1495 String cname = curBuf.getClass().getName(); 1496 log.trace("updateArrayOfAtomicElements(): buffer cname={} of data cname={}", bname, cname); 1497 vlElements = new ArrayList<>(Arrays.asList(buffer)); 1498 log.debug("updateArrayOfAtomicElements(): new vlSize={}", vlElements.size()); 1499 ((ArrayList[])curBuf)[rowStartIdx] = vlElements; 1500 } 1501 } 1502 1503 private static class StringDataProvider extends HDFDataProvider { 1504 private static final Logger log = LoggerFactory.getLogger(StringDataProvider.class); 1505 1506 private final long typeSize; 1507 1508 StringDataProvider(final Datatype dtype, final Object dataBuf, final boolean dataTransposed) 1509 throws Exception 1510 { 1511 super(dtype, dataBuf, dataTransposed); 1512 1513 typeSize = dtype.getDatatypeSize(); 1514 } 1515 1516 @Override 1517 public Object getDataValue(Object obj, int index) 1518 { 1519 if (obj instanceof byte[]) { 1520 int strlen = (int)typeSize; 1521 1522 log.trace("getDataValue({}, {}): converting byte[] to String", obj, index); 1523 1524 String str = new String((byte[])obj, index * strlen, strlen); 1525 int idx = str.indexOf('\0'); 1526 if (idx > 0) 1527 str = str.substring(0, idx); 1528 1529 theValue = str.trim(); 1530 } 1531 else 1532 super.getDataValue(obj, index); 1533 1534 log.trace("getDataValue({}, {})=({}): finish", obj, index, theValue); 1535 1536 return theValue; 1537 } 1538 1539 @Override 1540 public void setDataValue(int columnIndex, int rowIndex, Object newValue) 1541 { 1542 try { 1543 int bufIndex = physicalLocationToBufIndex(rowIndex, columnIndex); 1544 1545 updateStringBytes(dataBuf, newValue, bufIndex); 1546 } 1547 catch (Exception ex) { 1548 log.debug("setDataValue({}, {}, {}): cell value update failure: ", rowIndex, columnIndex, 1549 newValue, ex); 1550 } 1551 log.trace("setDataValue({}, {}, {}): finish", rowIndex, columnIndex, newValue); 1552 } 1553 1554 @Override 1555 public void setDataValue(int index, Object bufObject, Object newValue) 1556 { 1557 try { 1558 updateStringBytes(bufObject, newValue, index); 1559 } 1560 catch (Exception ex) { 1561 log.debug("setDataValue({}, {}, {}): cell value update failure: ", index, bufObject, newValue, 1562 ex); 1563 } 1564 log.trace("setDataValue({}, {}, {}): finish", index, bufObject, newValue); 1565 } 1566 1567 private void updateStringBytes(Object curBuf, Object newValue, int bufStartIndex) 1568 { 1569 if (curBuf instanceof String[]) { 1570 Array.set(curBuf, bufStartIndex, newValue); 1571 } 1572 else if (curBuf instanceof byte[]) { 1573 // Update String using data represented as a byte[] 1574 int strLen = (int)typeSize; 1575 byte[] newValueBytes = ((String)newValue).getBytes(); 1576 byte[] curBytes = (byte[])curBuf; 1577 int n = Math.min(strLen, newValueBytes.length); 1578 1579 bufStartIndex *= typeSize; 1580 1581 System.arraycopy(newValueBytes, 0, curBytes, bufStartIndex, n); 1582 1583 bufStartIndex += n; 1584 n = strLen - newValueBytes.length; 1585 1586 // space padding 1587 for (int i = 0; i < n; i++) 1588 curBytes[bufStartIndex + i] = ' '; 1589 } 1590 1591 isValueChanged = true; 1592 } 1593 } 1594 1595 private static class CharDataProvider extends HDFDataProvider { 1596 private static final Logger log = LoggerFactory.getLogger(CharDataProvider.class); 1597 1598 CharDataProvider(final Datatype dtype, final Object dataBuf, final boolean dataTransposed) 1599 throws Exception 1600 { 1601 super(dtype, dataBuf, dataTransposed); 1602 } 1603 1604 @Override 1605 public Object getDataValue(int columnIndex, int rowIndex) 1606 { 1607 /* 1608 * Compatibility with HDF4 8-bit character types that get converted to a String 1609 * ahead of time. 1610 */ 1611 if (dataBuf instanceof String) { 1612 log.trace("getDataValue({}, {})=({}): finish", rowIndex, columnIndex, dataBuf); 1613 return dataBuf; 1614 } 1615 1616 return super.getDataValue(columnIndex, rowIndex); 1617 } 1618 } 1619 1620 private static class NumericalDataProvider extends HDFDataProvider { 1621 private static final Logger log = LoggerFactory.getLogger(NumericalDataProvider.class); 1622 1623 private final boolean isUINT64; 1624 1625 private final long typeSize; 1626 1627 NumericalDataProvider(final Datatype dtype, final Object dataBuf, final boolean dataTransposed) 1628 throws Exception 1629 { 1630 super(dtype, dataBuf, dataTransposed); 1631 1632 typeSize = dtype.getDatatypeSize(); 1633 isUINT64 = dtype.isUnsigned() && (typeSize == 8); 1634 } 1635 1636 @Override 1637 public Object getDataValue(int columnIndex, int rowIndex) 1638 { 1639 super.getDataValue(columnIndex, rowIndex); 1640 1641 try { 1642 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 (isUINT64) 1662 theValue = Tools.convertUINT64toBigInt(Long.valueOf((long)theValue)); 1663 } 1664 catch (Exception ex) { 1665 log.debug("getDataValue({}): failure: ", index, ex); 1666 theValue = DataFactoryUtils.errStr; 1667 } 1668 1669 log.trace("getDataValue({})=({}): finish", index, theValue); 1670 1671 return theValue; 1672 } 1673 } 1674 1675 private static class EnumDataProvider extends HDFDataProvider { 1676 private static final Logger log = LoggerFactory.getLogger(EnumDataProvider.class); 1677 1678 EnumDataProvider(final Datatype dtype, final Object dataBuf, final boolean dataTransposed) 1679 throws Exception 1680 { 1681 super(dtype, dataBuf, dataTransposed); 1682 } 1683 } 1684 1685 private static class BitfieldDataProvider extends HDFDataProvider { 1686 private static final Logger log = LoggerFactory.getLogger(BitfieldDataProvider.class); 1687 1688 private final long typeSize; 1689 1690 BitfieldDataProvider(final Datatype dtype, final Object dataBuf, final boolean dataTransposed) 1691 throws Exception 1692 { 1693 super(dtype, dataBuf, dataTransposed); 1694 1695 typeSize = dtype.getDatatypeSize(); 1696 } 1697 1698 @Override 1699 public Object getDataValue(int columnIndex, int rowIndex) 1700 { 1701 try { 1702 int bufIndex = physicalLocationToBufIndex(rowIndex, columnIndex); 1703 1704 bufIndex *= typeSize; 1705 theValue = populateByteArray(dataBuf, bufIndex); 1706 } 1707 catch (Exception ex) { 1708 log.debug("getDataValue({}, {}): failure: ", rowIndex, columnIndex, ex); 1709 theValue = DataFactoryUtils.errStr; 1710 } 1711 1712 log.trace("getDataValue({}, {})=({}): finish", rowIndex, columnIndex, theValue); 1713 1714 return theValue; 1715 } 1716 1717 @Override 1718 public Object getDataValue(Object obj, int index) 1719 { 1720 try { 1721 index *= typeSize; 1722 theValue = populateByteArray(obj, index); 1723 } 1724 catch (Exception ex) { 1725 log.debug("getDataValue({}): ", index, ex); 1726 theValue = DataFactoryUtils.errStr; 1727 } 1728 1729 log.trace("getDataValue({})=({}): finish", index, theValue); 1730 1731 return theValue; 1732 } 1733 1734 private byte[] populateByteArray(Object byteBuf, int startIndex) 1735 { 1736 byte[] byteElements = new byte[(int)typeSize]; 1737 1738 for (int i = 0; i < typeSize; i++) 1739 byteElements[i] = Array.getByte(byteBuf, startIndex + i); 1740 1741 return byteElements; 1742 } 1743 } 1744 1745 private static class RefDataProvider extends HDFDataProvider { 1746 private static final Logger log = LoggerFactory.getLogger(RefDataProvider.class); 1747 1748 private final long typeSize; 1749 private final H5Datatype h5dtype; 1750 1751 RefDataProvider(final Datatype dtype, final Object dataBuf, final boolean dataTransposed) 1752 throws Exception 1753 { 1754 super(dtype, dataBuf, dataTransposed); 1755 1756 h5dtype = (H5Datatype)dtype; 1757 typeSize = h5dtype.getDatatypeSize(); 1758 log.trace("typeSize={}=", typeSize); 1759 } 1760 1761 @Override 1762 public Object getDataValue(int columnIndex, int rowIndex) 1763 { 1764 log.trace("getDataValue({}, {}): start", rowIndex, columnIndex); 1765 1766 try { 1767 int bufIndex = physicalLocationToBufIndex(rowIndex, columnIndex); 1768 byte[] rElements = null; 1769 1770 log.trace("getDataValue(dataBuf={}): start", dataBuf); 1771 if (dataBuf instanceof ArrayList) 1772 rElements = (byte[])((ArrayList)dataBuf).get(bufIndex); 1773 else 1774 rElements = (byte[])dataBuf; 1775 1776 if (h5dtype.isStdRef()) 1777 theValue = populateReference(rElements, 0); 1778 else if (h5dtype.isRegRef()) 1779 theValue = populateReferenceRegion(rElements, 0); 1780 else if (h5dtype.isRefObj()) 1781 theValue = populateReferenceObject(rElements, 0); 1782 else 1783 theValue = super.getDataValue(columnIndex, rowIndex); 1784 } 1785 catch (Exception ex) { 1786 log.debug("getDataValue({}, {}): failure: ", rowIndex, columnIndex, ex); 1787 theValue = DataFactoryUtils.errStr; 1788 } 1789 1790 log.trace("getDataValue({}, {})({}): finish", rowIndex, columnIndex, theValue); 1791 1792 return theValue; 1793 } 1794 1795 @Override 1796 public Object getDataValue(Object obj, int index) 1797 { 1798 log.trace("getDataValue(Object:{}, {}): start", obj, index); 1799 byte[] rElements = null; 1800 if (obj instanceof ArrayList) 1801 rElements = (byte[])((ArrayList)obj).get(index); 1802 else 1803 rElements = (byte[])obj; 1804 1805 log.trace("getDataValue(rElements:{})", rElements); 1806 try { 1807 if (h5dtype.isStdRef()) 1808 theValue = populateReference(rElements, 0); 1809 else if (h5dtype.isRegRef()) 1810 theValue = populateReferenceRegion(rElements, 0); 1811 else if (h5dtype.isRefObj()) 1812 theValue = populateReferenceObject(rElements, 0); 1813 else 1814 theValue = super.getDataValue(obj, index); 1815 } 1816 catch (Exception ex) { 1817 log.debug("getDataValueObject:({}, {}): ", obj, index, ex); 1818 theValue = DataFactoryUtils.errStr; 1819 } 1820 1821 log.trace("getDataValue(Object:{}, {})({}): finish", obj, index, theValue); 1822 1823 return theValue; 1824 } 1825 1826 private String populateReference(Object byteBuf, int startIndex) 1827 { 1828 byte[] rElements = new byte[(int)typeSize]; 1829 try { 1830 System.arraycopy(byteBuf, startIndex * (int)typeSize, rElements, 0, (int)typeSize); 1831 } 1832 catch (Exception err) { 1833 log.trace("populateReference(): arraycopy failure: ", err); 1834 } 1835 String regionStr = null; 1836 if (H5Datatype.zeroArrayCheck(rElements)) 1837 regionStr = "NULL"; 1838 else 1839 regionStr = ((H5ReferenceType)h5dtype).getReferenceRegion((byte[])byteBuf, false); 1840 log.trace("populateReference regionStr={}", regionStr); 1841 1842 return regionStr; 1843 } 1844 1845 private String populateReferenceRegion(Object byteBuf, int startIndex) 1846 { 1847 long fid = ((HObject)dataFormatReference).getFileFormat().getFID(); 1848 byte[] rElements = new byte[(int)typeSize]; 1849 try { 1850 System.arraycopy(byteBuf, startIndex * (int)typeSize, rElements, 0, (int)typeSize); 1851 } 1852 catch (Exception err) { 1853 log.trace("populateReferenceRegion(): arraycopy failure: ", err); 1854 } 1855 String regionStr = null; 1856 if (H5Datatype.zeroArrayCheck(rElements)) 1857 regionStr = "NULL"; 1858 else 1859 regionStr = H5Datatype.descRegionDataset(fid, rElements); 1860 log.trace("populateReferenceRegion regionStr={}", regionStr); 1861 1862 return regionStr; 1863 } 1864 1865 private String populateReferenceObject(Object byteBuf, int startIndex) 1866 { 1867 long fid = ((HObject)dataFormatReference).getFileFormat().getFID(); 1868 log.trace("populateReferenceObject byteBuf={}", byteBuf); 1869 byte[] rElements = new byte[(int)typeSize]; 1870 try { 1871 System.arraycopy(byteBuf, startIndex * (int)typeSize, rElements, 0, (int)typeSize); 1872 } 1873 catch (Exception err) { 1874 log.trace("populateReferenceRegion(): arraycopy failure: ", err); 1875 } 1876 String objectStr = null; 1877 if (H5Datatype.zeroArrayCheck(rElements)) 1878 objectStr = "NULL"; 1879 else 1880 objectStr = H5Datatype.descReferenceObject(fid, rElements); 1881 log.trace("populateReferenceObject objectStr={}", objectStr); 1882 1883 return objectStr; 1884 } 1885 } 1886}