001/***************************************************************************** 002 * Copyright by The HDF Group. * 003 * Copyright by the Board of Trustees of the University of Illinois. * 004 * All rights reserved. * 005 * * 006 * This file is part of the HDF Java Products distribution. * 007 * The full copyright notice, including terms governing use, modification, * 008 * and redistribution, is contained in the files COPYING and Copyright.html. * 009 * COPYING can be found at the root of the source code distribution tree. * 010 * Or, see https://support.hdfgroup.org/products/licenses.html * 011 * If you do not have access to either file, you may request a copy from * 012 * help@hdfgroup.org. * 013 ****************************************************************************/ 014 015package hdf.view.TableView; 016 017import java.lang.reflect.Array; 018import java.util.ArrayList; 019import java.util.Arrays; 020import java.util.HashMap; 021import java.util.Iterator; 022import java.util.List; 023import java.util.ListIterator; 024import java.util.regex.Matcher; 025import java.util.regex.Pattern; 026 027import org.eclipse.nebula.widgets.nattable.NatTable; 028import org.eclipse.nebula.widgets.nattable.config.DefaultNatTableStyleConfiguration; 029import org.eclipse.nebula.widgets.nattable.config.EditableRule; 030import org.eclipse.nebula.widgets.nattable.config.IEditableRule; 031import org.eclipse.nebula.widgets.nattable.data.IDataProvider; 032import org.eclipse.nebula.widgets.nattable.grid.data.DefaultCornerDataProvider; 033import org.eclipse.nebula.widgets.nattable.grid.layer.ColumnHeaderLayer; 034import org.eclipse.nebula.widgets.nattable.grid.layer.CornerLayer; 035import org.eclipse.nebula.widgets.nattable.grid.layer.GridLayer; 036import org.eclipse.nebula.widgets.nattable.grid.layer.RowHeaderLayer; 037import org.eclipse.nebula.widgets.nattable.group.ColumnGroupExpandCollapseLayer; 038import org.eclipse.nebula.widgets.nattable.group.ColumnGroupGroupHeaderLayer; 039import org.eclipse.nebula.widgets.nattable.group.ColumnGroupHeaderLayer; 040import org.eclipse.nebula.widgets.nattable.group.ColumnGroupModel; 041import org.eclipse.nebula.widgets.nattable.group.ColumnGroupModel.ColumnGroup; 042import org.eclipse.nebula.widgets.nattable.layer.DataLayer; 043import org.eclipse.nebula.widgets.nattable.layer.ILayer; 044import org.eclipse.nebula.widgets.nattable.layer.ILayerListener; 045import org.eclipse.nebula.widgets.nattable.layer.cell.ILayerCell; 046import org.eclipse.nebula.widgets.nattable.layer.event.ILayerEvent; 047import org.eclipse.nebula.widgets.nattable.selection.SelectionLayer; 048import org.eclipse.nebula.widgets.nattable.selection.event.CellSelectionEvent; 049import org.eclipse.nebula.widgets.nattable.viewport.ViewportLayer; 050import org.eclipse.swt.SWT; 051import org.eclipse.swt.custom.ScrolledComposite; 052import org.eclipse.swt.widgets.Composite; 053 054import hdf.object.CompoundDS; 055import hdf.object.CompoundDataFormat; 056import hdf.object.DataFormat; 057import hdf.object.Datatype; 058import hdf.view.Tools; 059import hdf.view.ViewProperties; 060import hdf.view.DataView.DataViewManager; 061 062public class DefaultCompoundDSTableView extends DefaultBaseTableView implements TableView { 063 064 private static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(DefaultCompoundDSTableView.class); 065 066 /** 067 * Constructs a CompoundDS TableView with no additional data properties. 068 * 069 * @param theView 070 * the main HDFView. 071 */ 072 public DefaultCompoundDSTableView(DataViewManager theView) { 073 this(theView, null); 074 } 075 076 /** 077 * Constructs a CompoundDS TableView with the specified data properties. 078 * 079 * @param theView 080 * the main HDFView. 081 * 082 * @param dataPropertiesMap 083 * the properties on how to show the data. The map is used to allow 084 * applications to pass properties on how to display the data, such 085 * as: transposing data, showing data as characters, applying a 086 * bitmask, and etc. Predefined keys are listed at 087 * ViewProperties.DATA_VIEW_KEY. 088 */ 089 @SuppressWarnings("rawtypes") 090 public DefaultCompoundDSTableView(DataViewManager theView, HashMap dataPropertiesMap) { 091 super(theView, dataPropertiesMap); 092 093 isDataTransposed = false; // Disable transpose for compound datasets 094 095 if (!shell.isDisposed()) { 096 shell.setImage(ViewProperties.getTableIcon()); 097 098 viewer.addDataView(this); 099 100 shell.open(); 101 } 102 } 103 104 @Override 105 protected void loadData(DataFormat dataObject) throws Exception { 106 super.loadData(dataObject); 107 108 if (dataValue == null) { 109 log.debug("loadData(): data value is null"); 110 throw new RuntimeException("data value is null or not a list"); 111 } 112 } 113 114 /** 115 * Creates a NatTable for a Compound dataset 116 * 117 * @param parent 118 * The parent for the NatTable 119 * @param dataObject 120 * The Compound dataset for the NatTable to display 121 * 122 * @return The newly created NatTable 123 */ 124 @Override 125 protected NatTable createTable(Composite parent, DataFormat dataObject) { 126 // Create body layer 127 final ColumnGroupModel columnGroupModel = new ColumnGroupModel(); 128 final ColumnGroupModel secondLevelGroupModel = new ColumnGroupModel(); 129 130 try { 131 dataProvider = DataProviderFactory.getDataProvider(dataObject, dataValue, isDataTransposed); 132 133 log.trace("createTable(): rows={} : cols={}", dataProvider.getRowCount(), dataProvider.getColumnCount()); 134 135 dataLayer = new DataLayer(dataProvider); 136 } 137 catch (Exception ex) { 138 log.debug("createTable(): failed to retrieve DataProvider for table: ", ex); 139 return null; 140 } 141 142 final ColumnGroupExpandCollapseLayer expandCollapseLayer = new ColumnGroupExpandCollapseLayer(dataLayer, 143 secondLevelGroupModel, columnGroupModel); 144 selectionLayer = new SelectionLayer(expandCollapseLayer); 145 final ViewportLayer viewportLayer = new ViewportLayer(selectionLayer); 146 147 dataLayer.setDefaultColumnWidth(80); 148 149 // Create the Column Header layer 150 columnHeaderDataProvider = new CompoundDSColumnHeaderDataProvider(dataObject); 151 ColumnHeaderLayer columnHeaderLayer = new ColumnHeader(new DataLayer(columnHeaderDataProvider), viewportLayer, 152 selectionLayer); 153 154 // Set up column grouping 155 ColumnGroupHeaderLayer columnGroupHeaderLayer = new ColumnGroupHeaderLayer(columnHeaderLayer, selectionLayer, 156 columnGroupModel); 157 CompoundDSNestedColumnHeaderLayer nestedColumnGroupHeaderLayer = new CompoundDSNestedColumnHeaderLayer( 158 columnGroupHeaderLayer, selectionLayer, secondLevelGroupModel); 159 160 // Create the Row Header layer 161 rowHeaderDataProvider = new RowHeaderDataProvider(dataObject); 162 163 // Try to adapt row height to current font 164 int defaultRowHeight = curFont == null ? 20 : (2 * curFont.getFontData()[0].getHeight()); 165 166 DataLayer baseLayer = new DataLayer(rowHeaderDataProvider, 40, defaultRowHeight); 167 RowHeaderLayer rowHeaderLayer = new RowHeader(baseLayer, viewportLayer, selectionLayer); 168 169 // Create the Corner Layer 170 ILayer cornerLayer = new CornerLayer( 171 new DataLayer(new DefaultCornerDataProvider(columnHeaderDataProvider, rowHeaderDataProvider)), 172 rowHeaderLayer, nestedColumnGroupHeaderLayer); 173 174 // Create the Grid Layer 175 GridLayer gridLayer = new EditingGridLayer(viewportLayer, nestedColumnGroupHeaderLayer, rowHeaderLayer, 176 cornerLayer); 177 178 final NatTable natTable = new NatTable(parent, gridLayer, false); 179 natTable.addConfiguration(new DefaultNatTableStyleConfiguration()); 180 natTable.addLayerListener(new CompoundDSCellSelectionListener()); 181 182 // Create popup menu for region or object ref. 183 //if (isRegRef || isObjRef) { 184 // natTable.addConfiguration(new RefContextMenu(natTable)); 185 //} 186 187 natTable.configure(); 188 189 return natTable; 190 } 191 192 @Override 193 public Object getSelectedData() { 194 Object selectedData = null; 195 196 int cols = this.getSelectedColumnCount(); 197 int rows = this.getSelectedRowCount(); 198 199 if ((cols <= 0) || (rows <= 0)) { 200 shell.getDisplay().beep(); 201 Tools.showError(shell, "Select", "No data is selected."); 202 return null; 203 } 204 205 Object colData = null; 206 try { 207 colData = ((List<?>) dataObject.getData()).get(selectionLayer.getSelectedColumnPositions()[0]); 208 } 209 catch (Exception ex) { 210 log.debug("getSelectedData(): ", ex); 211 return null; 212 } 213 214 int size = Array.getLength(colData); 215 String cName = colData.getClass().getName(); 216 int cIndex = cName.lastIndexOf('['); 217 char nt = ' '; 218 if (cIndex >= 0) { 219 nt = cName.charAt(cIndex + 1); 220 } 221 log.trace("getSelectedData(): size={} cName={} nt={}", size, cName, nt); 222 223 if (isRegRef) { 224 // reg. ref data are stored in strings 225 selectedData = new String[size]; 226 } 227 else { 228 switch (nt) { 229 case 'B': 230 selectedData = new byte[size]; 231 break; 232 case 'S': 233 selectedData = new short[size]; 234 break; 235 case 'I': 236 selectedData = new int[size]; 237 break; 238 case 'J': 239 selectedData = new long[size]; 240 break; 241 case 'F': 242 selectedData = new float[size]; 243 break; 244 case 'D': 245 selectedData = new double[size]; 246 break; 247 default: 248 selectedData = null; 249 break; 250 } 251 } 252 253 if (selectedData == null) { 254 shell.getDisplay().beep(); 255 Tools.showError(shell, "Select", "Unsupported data type."); 256 return null; 257 } 258 259 log.trace("getSelectedData(): selectedData is type {}", nt); 260 261 System.arraycopy(colData, 0, selectedData, 0, size); 262 263 return selectedData; 264 } 265 266 267 @Override 268 protected void showObjRefData(long ref) { 269 // Currently no support for showing Obj. Ref. Data in Compound Datasets 270 } 271 272 @Override 273 protected void showRegRefData(String reg) { 274 // Currently no support for show Reg. Ref. Data in Compound Datasets 275 } 276 277 /** 278 * Returns an IEditableRule that determines whether cells can be edited. 279 * 280 * Cells can be edited as long as the dataset is not opened in read-only mode 281 * and the data is not currently displayed in hexadecimal, binary, or character 282 * mode. 283 * 284 * @param dataObject 285 * The dataset for editing 286 * 287 * @return a new IEditableRule for the dataset 288 */ 289 @Override 290 protected IEditableRule getDataEditingRule(DataFormat dataObject) { 291 if (dataObject == null) return null; 292 293 // Only Allow editing if not in read-only mode 294 return new EditableRule() { 295 @Override 296 public boolean isEditable(int columnIndex, int rowIndex) { 297 /* 298 * TODO: Should be able to edit character-displayed types and datasets when 299 * displayed as hex/binary. 300 */ 301 //return !(isReadOnly || isDisplayTypeChar || showAsBin || showAsHex); 302 return !isReadOnly; 303 } 304 }; 305 } 306 307 /** 308 * Update cell value label and cell value field when a cell is selected 309 */ 310 private class CompoundDSCellSelectionListener implements ILayerListener { 311 @Override 312 public void handleLayerEvent(ILayerEvent e) { 313 if (e instanceof CellSelectionEvent) { 314 log.trace("ScalarDSCellSelectionListener: CellSelected isRegRef={} isObjRef={}", isRegRef, isObjRef); 315 316 CellSelectionEvent event = (CellSelectionEvent) e; 317 Object val = dataTable.getDataValueByPosition(event.getColumnPosition(), event.getRowPosition()); 318 319 int rowStart = ((RowHeaderDataProvider) rowHeaderDataProvider).start; 320 int rowStride = ((RowHeaderDataProvider) rowHeaderDataProvider).stride; 321 322 int rowIndex = rowStart + indexBase + dataTable.getRowIndexByPosition(event.getRowPosition()) * rowStride; 323 Object fieldName = columnHeaderDataProvider.getDataValue(dataTable.getColumnIndexByPosition(event.getColumnPosition()), 0); 324 325 String colIndex = ""; 326 if (dataObject.getWidth() > 1) { 327 int groupSize = ((CompoundDataFormat) dataObject).getSelectedMemberCount(); 328 colIndex = "[" + String.valueOf((dataTable.getColumnIndexByPosition(event.getColumnPosition())) / groupSize) + "]"; 329 } 330 331 cellLabel.setText(String.valueOf(rowIndex) + ", " + fieldName + colIndex + " = "); 332 333 if (val == null) { 334 cellValueField.setText("Null"); 335 return; 336 } 337 338 ILayerCell cell = dataTable.getCellByPosition(((CellSelectionEvent) e).getColumnPosition(), ((CellSelectionEvent) e).getRowPosition()); 339 cellValueField.setText(dataDisplayConverter.canonicalToDisplayValue(cell, dataTable.getConfigRegistry(), val).toString()); 340 ((ScrolledComposite) cellValueField.getParent()).setMinSize(cellValueField.computeSize(SWT.DEFAULT, SWT.DEFAULT)); 341 } 342 } 343 } 344 345 /** 346 * Custom Column Header data provider to set column names based on selected 347 * members for Compound Datasets. 348 */ 349 private class CompoundDSColumnHeaderDataProvider implements IDataProvider { 350 // Column names with CompoundDS SEPARATOR character '->' left intact. 351 // Used in CompoundDSNestedColumnHeader to provide correct nesting structure. 352 private final String[] columnNamesFull; 353 354 // Simplified base column names without separator character. Used to 355 // actually label the columns. 356 private final ArrayList<String> columnNames; 357 358 private final int ncols; 359 private final int groupSize; 360 361 public CompoundDSColumnHeaderDataProvider(DataFormat dataObject) { 362 CompoundDataFormat dataFormat = (CompoundDataFormat) dataObject; 363 364 Datatype cmpdType = dataObject.getDatatype(); 365 List<Datatype> selectedTypes = DataFactoryUtils.filterNonSelectedMembers(dataFormat, cmpdType); 366 final List<String> datasetMemberNames = Arrays.asList(dataFormat.getSelectedMemberNames()); 367 368 columnNames = new ArrayList<>(dataFormat.getSelectedMemberCount()); 369 370 recursiveColumnHeaderSetup(columnNames, dataFormat, cmpdType, datasetMemberNames, selectedTypes); 371 372 // Make a copy of column names so changes to column names don't affect the full column names, 373 // which is used elsewhere. 374 columnNamesFull = Arrays.copyOf(columnNames.toArray(new String[0]), columnNames.size()); 375 376 // Simplify any nested field column names down to their base names. E.g., a 377 // nested field with the full name 'nested_name->a_name' has a simplified column 378 // name of 'a_name' 379 for (int j = 0; j < columnNames.size(); j++) { 380 String nestedName = columnNames.get(j); 381 int nestingPosition = nestedName.lastIndexOf("->"); 382 383 // If this is a nested field, this column's name is whatever follows the last 384 // nesting character '->' 385 if (nestingPosition >= 0) 386 columnNames.set(j, nestedName.substring(nestingPosition + 2)); 387 } 388 389 groupSize = columnNames.size(); 390 391 ncols = columnNames.size() * (int) dataFormat.getWidth(); 392 log.trace("CompoundDSColumnHeaderDataProvider: ncols={}", ncols); 393 } 394 395 private void recursiveColumnHeaderSetup(List<String> outColNames, CompoundDataFormat dataFormat, 396 Datatype curDtype, List<String> memberNames, List<Datatype> memberTypes) { 397 398 if (curDtype.isArray()) { 399 /* 400 * ARRAY of COMPOUND type 401 */ 402 int arrSize = 1; 403 Datatype nestedCompoundType = curDtype; 404 while (nestedCompoundType != null) { 405 if (nestedCompoundType.isCompound()) { 406 break; 407 } 408 else if (nestedCompoundType.isArray()) { 409 long[] arrayDims = nestedCompoundType.getArrayDims(); 410 for (int i = 0; i < arrayDims.length; i++) { 411 arrSize *= arrayDims[i]; 412 } 413 } 414 415 nestedCompoundType = nestedCompoundType.getDatatypeBase(); 416 } 417 418 log.trace("recursiveColumnHeaderSetup(): ARRAY size: {}", arrSize); 419 420 /* 421 * TODO: Temporary workaround for top-level array of compound types. 422 */ 423 if (memberTypes.isEmpty()) { 424 memberTypes = DataFactoryUtils.filterNonSelectedMembers(dataFormat, nestedCompoundType); 425 } 426 427 /* 428 * Duplicate member names by the size of the array. 429 * 430 * NOTE: this assumes that the member names of the ARRAY/VLEN of COMPOUND 431 * directly follow the name of the top-level member itself and will break if 432 * that assumption is not true. 433 */ 434 StringBuilder sBuilder = new StringBuilder(); 435 ArrayList<String> nestedMemberNames = new ArrayList<>(arrSize * memberNames.size()); 436 for (int i = 0; i < arrSize; i++) { 437 for (int j = 0; j < memberNames.size(); j++) { 438 sBuilder.setLength(0); 439 440 // Copy the dataset member name reference, so changes to the column name 441 // don't affect the dataset's internal member names. 442 sBuilder.append(memberNames.get(j).replaceAll(CompoundDS.SEPARATOR, "->")); 443 444 /* 445 * Add the index number to the member name so we can correctly setup nested 446 * column grouping. 447 */ 448 sBuilder.append("[" + i + "]"); 449 450 nestedMemberNames.add(sBuilder.toString()); 451 } 452 } 453 454 recursiveColumnHeaderSetup(outColNames, dataFormat, nestedCompoundType, nestedMemberNames, memberTypes); 455 } 456 else if (curDtype.isVLEN() && !curDtype.isVarStr()) { 457 /* 458 * TODO: empty until we have true variable-length support. 459 */ 460 } 461 else if (curDtype.isCompound()) { 462 ListIterator<String> localIt = memberNames.listIterator(); 463 while (localIt.hasNext()) { 464 int curIdx = localIt.nextIndex(); 465 String curName = localIt.next(); 466 Datatype curType = memberTypes.get(curIdx % memberTypes.size()); 467 Datatype nestedArrayOfCompoundType = null; 468 boolean nestedArrayOfCompound = false; 469 470 /* 471 * Recursively detect any nested array/vlen of compound types and deal with them 472 * by creating multiple copies of the member names. 473 */ 474 if (curType.isArray() /* || (curType.isVLEN() && !curType.isVarStr()) */ /* TODO: true variable-length support */) { 475 Datatype base = curType.getDatatypeBase(); 476 while (base != null) { 477 if (base.isCompound()) { 478 nestedArrayOfCompound = true; 479 nestedArrayOfCompoundType = base; 480 break; 481 } 482 483 base = base.getDatatypeBase(); 484 } 485 } 486 487 /* 488 * For ARRAY of COMPOUND and VLEN of COMPOUND types, we repeat the compound 489 * members n times, where n is the number of array or vlen elements. 490 */ 491 if (nestedArrayOfCompound) { 492 List<Datatype> selTypes = DataFactoryUtils.filterNonSelectedMembers(dataFormat, nestedArrayOfCompoundType); 493 List<String> selMemberNames = new ArrayList<>(selTypes.size()); 494 495 int arrCmpdLen = calcArrayOfCompoundLen(selTypes); 496 for (int i = 0; i < arrCmpdLen; i++) { 497 selMemberNames.add(localIt.next()); 498 } 499 500 recursiveColumnHeaderSetup(outColNames, dataFormat, curType, selMemberNames, selTypes); 501 } 502 else { 503 // Copy the dataset member name reference, so changes to the column name 504 // don't affect the dataset's internal member names. 505 curName = new String(curName.replaceAll(CompoundDS.SEPARATOR, "->")); 506 507 outColNames.add(curName); 508 } 509 } 510 } 511 } 512 513 private int calcArrayOfCompoundLen(List<Datatype> datatypes) { 514 int count = 0; 515 Iterator<Datatype> localIt = datatypes.iterator(); 516 while (localIt.hasNext()) { 517 Datatype curType = localIt.next(); 518 519 if (curType.isCompound()) { 520 count += calcArrayOfCompoundLen(curType.getCompoundMemberTypes()); 521 } 522 else if (curType.isArray()) { 523 /* 524 * TODO: nested array of compound length calculation 525 */ 526 } 527 else 528 count++; 529 } 530 531 return count; 532 } 533 534 @Override 535 public int getColumnCount() { 536 return ncols; 537 } 538 539 @Override 540 public int getRowCount() { 541 return 1; 542 } 543 544 @Override 545 public Object getDataValue(int columnIndex, int rowIndex) { 546 try { 547 return columnNames.get(columnIndex % groupSize); 548 } 549 catch (Exception ex) { 550 log.debug("getDataValue({}, {}): ", rowIndex, columnIndex, ex); 551 return "*ERROR*"; 552 } 553 } 554 555 @Override 556 public void setDataValue(int columnIndex, int rowIndex, Object newValue) { 557 // Disable column header editing 558 return; 559 } 560 } 561 562 /** 563 * Implementation of Column Grouping for Compound Datasets with nested members. 564 */ 565 private class CompoundDSNestedColumnHeaderLayer extends ColumnGroupGroupHeaderLayer { 566 public CompoundDSNestedColumnHeaderLayer(ColumnGroupHeaderLayer columnGroupHeaderLayer, 567 SelectionLayer selectionLayer, ColumnGroupModel columnGroupModel) { 568 super(columnGroupHeaderLayer, selectionLayer, columnGroupModel); 569 570 if (curFont != null) { 571 this.setRowHeight(2 * curFont.getFontData()[0].getHeight()); 572 columnGroupHeaderLayer.setRowHeight(2 * curFont.getFontData()[0].getHeight()); 573 } 574 575 final String[] allColumnNames = ((CompoundDSColumnHeaderDataProvider) columnHeaderDataProvider).columnNamesFull; 576 final int groupSize = ((CompoundDSColumnHeaderDataProvider) columnHeaderDataProvider).groupSize; 577 log.trace("CompoundDSNestedColumnHeaderLayer: groupSize={} -- allColumnNames={}", groupSize, allColumnNames); 578 579 // Set up first-level column grouping 580 int[] indices = new int[groupSize]; 581 for (int i = 0; i < dataObject.getWidth(); i++) { 582 for (int j = 0; j < groupSize; j++) { 583 indices[j] = (i * groupSize) + j; 584 } 585 586 this.addColumnsIndexesToGroup(String.valueOf(i), indices); 587 } 588 589 // Set up any further-nested column groups 590 StringBuilder columnHeaderBuilder = new StringBuilder(); 591 for (int k = 0; k < dataObject.getWidth(); k++) { 592 for (int i = 0; i < allColumnNames.length; i++) { 593 int colindex = i + k * allColumnNames.length; 594 int nestingPosition = allColumnNames[i].lastIndexOf("->"); 595 596 columnHeaderBuilder.setLength(0); 597 598 if (nestingPosition >= 0) { 599 ColumnGroup nestingGroup = columnGroupModel.getColumnGroupByIndex(colindex); 600 if (nestingGroup != null) { 601 String columnGroupName = nestingGroup.getName(); 602 int groupTitleStartPosition = allColumnNames[i].lastIndexOf("->", nestingPosition); 603 String nestingName = allColumnNames[i].substring(nestingPosition + 2); 604 String newGroupName; 605 606 if (groupTitleStartPosition == 0) { 607 /* Singly nested member */ 608 newGroupName = allColumnNames[i].substring(groupTitleStartPosition, nestingPosition); 609 } 610 else if (groupTitleStartPosition > 0) { 611 /* Member nested at second level or beyond, skip past leading '->' */ 612 newGroupName = allColumnNames[i].substring(0, groupTitleStartPosition); 613 } 614 else { 615 newGroupName = allColumnNames[i].substring(0, nestingPosition); 616 } 617 618 columnHeaderBuilder.append(newGroupName); 619 columnHeaderBuilder.append("{").append(columnGroupName).append("}"); 620 621 /* 622 * Special case for ARRAY of COMPOUND and VLEN of COMPOUND types. 623 * 624 * NOTE: This is a quick and dirty way of determining array/vlen of compound 625 * members. It will probably cause weird column grouping behavior if a user uses 626 * the "[number]" pattern in one of their member names, but for now we won't 627 * worry about it. 628 */ 629 if (nestingName.matches(".*\\[[0-9]*\\]")) { 630 processArrayOfCompound(columnHeaderBuilder, nestingName); 631 } 632 633 columnGroupHeaderLayer.addColumnsIndexesToGroup(columnHeaderBuilder.toString(), colindex); 634 } 635 else 636 log.debug("CompoundDSNestedColumnHeaderLayer: nesting group was null for index {}", colindex); 637 } 638 else if (allColumnNames[i].matches(".*\\[[0-9]*\\]")) { 639 /* 640 * Top-level ARRAY of COMPOUND types. 641 */ 642 columnHeaderBuilder.append("ARRAY"); 643 processArrayOfCompound(columnHeaderBuilder, allColumnNames[i]); 644 645 columnGroupHeaderLayer.addColumnsIndexesToGroup(columnHeaderBuilder.toString(), colindex); 646 } 647 } 648 } 649 } 650 651 private void processArrayOfCompound(StringBuilder curBuilder, String columnName) { 652 Pattern indexPattern = Pattern.compile(".*\\[([0-9]*)\\]"); 653 Matcher indexMatcher = indexPattern.matcher(columnName); 654 655 /* 656 * Group array/vlen of compounds members into array-indexed groups. 657 */ 658 if (indexMatcher.matches()) { 659 int containerIndex = 0; 660 661 try { 662 containerIndex = Integer.parseInt(indexMatcher.group(1)); 663 } 664 catch (Exception ex) { 665 log.debug("processArrayOfCompound(): error parsing array/vlen of compound index: ", ex); 666 return; 667 } 668 669 curBuilder.append("[").append(containerIndex).append("]"); 670 } 671 } 672 } 673}