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