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