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