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