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 COPYING file, which can be found * 009 * at the root of the source code distribution tree, * 010 * or in https://www.hdfgroup.org/licenses. * 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.TreeView; 016 017import java.io.BufferedInputStream; 018import java.io.BufferedOutputStream; 019import java.io.File; 020import java.io.FileInputStream; 021import java.io.FileNotFoundException; 022import java.io.FileOutputStream; 023import java.io.Serializable; 024import java.lang.reflect.Method; 025import java.util.ArrayList; 026import java.util.Arrays; 027import java.util.BitSet; 028import java.util.Enumeration; 029import java.util.HashMap; 030import java.util.Iterator; 031import java.util.LinkedList; 032import java.util.List; 033import java.util.Queue; 034 035import org.slf4j.Logger; 036import org.slf4j.LoggerFactory; 037 038import org.eclipse.swt.SWT; 039import org.eclipse.swt.events.DisposeEvent; 040import org.eclipse.swt.events.DisposeListener; 041import org.eclipse.swt.events.KeyAdapter; 042import org.eclipse.swt.events.KeyEvent; 043import org.eclipse.swt.events.MenuAdapter; 044import org.eclipse.swt.events.MenuDetectEvent; 045import org.eclipse.swt.events.MenuDetectListener; 046import org.eclipse.swt.events.MenuEvent; 047import org.eclipse.swt.events.MouseAdapter; 048import org.eclipse.swt.events.MouseEvent; 049import org.eclipse.swt.events.SelectionAdapter; 050import org.eclipse.swt.events.SelectionEvent; 051import org.eclipse.swt.graphics.Font; 052import org.eclipse.swt.graphics.Image; 053import org.eclipse.swt.graphics.Point; 054import org.eclipse.swt.graphics.Rectangle; 055import org.eclipse.swt.layout.GridData; 056import org.eclipse.swt.layout.GridLayout; 057import org.eclipse.swt.widgets.Button; 058import org.eclipse.swt.widgets.Combo; 059import org.eclipse.swt.widgets.Composite; 060import org.eclipse.swt.widgets.Dialog; 061import org.eclipse.swt.widgets.Display; 062import org.eclipse.swt.widgets.Event; 063import org.eclipse.swt.widgets.FileDialog; 064import org.eclipse.swt.widgets.Label; 065import org.eclipse.swt.widgets.Listener; 066import org.eclipse.swt.widgets.Menu; 067import org.eclipse.swt.widgets.MenuItem; 068import org.eclipse.swt.widgets.Shell; 069import org.eclipse.swt.widgets.Tree; 070import org.eclipse.swt.widgets.TreeItem; 071 072import hdf.hdf5lib.HDF5Constants; 073 074import hdf.object.CompoundDS; 075import hdf.object.DataFormat; 076import hdf.object.Dataset; 077import hdf.object.Datatype; 078import hdf.object.FileFormat; 079import hdf.object.Group; 080import hdf.object.HObject; 081import hdf.object.MetaDataContainer; 082import hdf.object.ScalarDS; 083 084import hdf.view.DefaultFileFilter; 085import hdf.view.HDFView; 086import hdf.view.Tools; 087import hdf.view.ViewProperties; 088import hdf.view.ViewProperties.DATA_VIEW_KEY; 089import hdf.view.ViewProperties.DataViewType; 090import hdf.view.DataView.DataView; 091import hdf.view.DataView.DataViewFactory; 092import hdf.view.DataView.DataViewFactoryProducer; 093import hdf.view.DataView.DataViewManager; 094import hdf.view.MetaDataView.MetaDataView; 095import hdf.view.dialog.DataOptionDialog; 096import hdf.view.dialog.InputDialog; 097import hdf.view.dialog.NewCompoundDatasetDialog; 098import hdf.view.dialog.NewDatasetDialog; 099import hdf.view.dialog.NewDatatypeDialog; 100import hdf.view.dialog.NewGroupDialog; 101import hdf.view.dialog.NewImageDialog; 102import hdf.view.dialog.NewLinkDialog; 103 104/** 105 * TreeView defines APIs for opening files and displaying the file structure in 106 * a tree structure. 107 * 108 * TreeView uses folders and leaf items to represent groups and data objects in 109 * the file. You can expand or collapse folders to navigate data objects in the 110 * file. 111 * 112 * From the TreeView, you can open data content or metadata of the selected object. 113 * You can select object(s) to delete or add new objects to the file. 114 * 115 * @author Jordan T. Henderson 116 * @version 2.4 12//2015 117 */ 118public class DefaultTreeView implements TreeView { 119 120 private static final Logger log = LoggerFactory.getLogger(DefaultTreeView.class); 121 122 private Shell shell; 123 124 private Font curFont; 125 126 /** The owner of this TreeView */ 127 private DataViewManager viewer; 128 129 /** Thread to load TableView Data in the background */ 130 private LoadDataThread loadDataThread; 131 132 /** 133 * The tree which holds file structures. 134 */ 135 private final Tree tree; 136 137 /** The currently selected tree item */ 138 private TreeItem selectedItem = null; 139 140 /** The list of current selected objects for copying */ 141 private TreeItem[] objectsToCopy = null; 142 143 private TreeItem[] currentSelectionsForMove = null; 144 145 /** The currently selected object */ 146 private HObject selectedObject; 147 148 /** The currently selected file */ 149 private FileFormat selectedFile; 150 151 /** Maintains a list of TreeItems in the tree in breadth-first order 152 * to prevent many calls of getAllItemsBreadthFirst. 153 */ 154 //private ArrayList<TreeItem> breadthFirstItems = null; 155 156 /** A list of currently open files */ 157 private final List<FileFormat> fileList = new ArrayList<>(); 158 159 /** A list of editing GUI components */ 160 private List<MenuItem> editGUIs = new ArrayList<>(); 161 162 /** 163 * The popup menu used to display user choice of actions on data object. 164 */ 165 private final Menu popupMenu; 166 167 private Menu newObjectMenu; 168 private Menu exportDatasetMenu; 169 170 private MenuItem openVirtualFilesMenuItem; 171 private MenuItem addDatasetMenuItem; 172 private MenuItem exportDatasetMenuItem; 173 private MenuItem addTableMenuItem; 174 private MenuItem addDatatypeMenuItem; 175 private MenuItem addLinkMenuItem; 176 private MenuItem setLibVerBoundsItem; 177 private MenuItem changeIndexItem; 178 179 /** Keep Image instances to prevent many calls to ViewProperties.getTypeIcon() */ 180 private Image h4Icon = ViewProperties.getH4Icon(); 181 private Image h4IconR = ViewProperties.getH4IconR(); 182 private Image h5Icon = ViewProperties.getH5Icon(); 183 private Image h5IconR = ViewProperties.getH5IconR(); 184 private Image nc3Icon = ViewProperties.getNC3Icon(); 185 private Image nc3IconR = ViewProperties.getNC3IconR(); 186 private Image imageIcon = ViewProperties.getImageIcon(); 187 private Image imageIconA = ViewProperties.getImageIconA(); 188 private Image textIcon = ViewProperties.getTextIcon(); 189 private Image textIconA = ViewProperties.getTextIconA(); 190 private Image datasetIcon = ViewProperties.getDatasetIcon(); 191 private Image datasetIconA = ViewProperties.getDatasetIconA(); 192 private Image tableIcon = ViewProperties.getTableIcon(); 193 private Image tableIconA = ViewProperties.getTableIconA(); 194 private Image datatypeIcon = ViewProperties.getDatatypeIcon(); 195 private Image datatypeIconA = ViewProperties.getDatatypeIconA(); 196 private Image folderCloseIcon = ViewProperties.getFoldercloseIcon(); 197 private Image folderCloseIconA = ViewProperties.getFoldercloseIconA(); 198 private Image folderOpenIcon = ViewProperties.getFolderopenIcon(); 199 private Image folderOpenIconA = ViewProperties.getFolderopenIconA(); 200 private Image questionIcon = ViewProperties.getQuestionIcon(); 201 202 /** Flag to indicate if the dataset is displayed as default */ 203 private boolean isDefaultDisplay = true; 204 205 /** Flag to indicate if TreeItems are being moved */ 206 private boolean moveFlag = false; 207 208 private boolean isApplyBitmaskOnly = false; 209 210 private int binaryOrder; 211 212 private String currentSearchPhrase = null; 213 214 /** Used to open a File using a temporary indexing type and order */ 215 private int tempIdxType = -1; 216 private int tempIdxOrder = -1; 217 218 private enum OBJECT_TYPE {GROUP, DATASET, IMAGE, TABLE, DATATYPE, LINK}; 219 220 /** 221 * Create a visual component for opening files and displaying the file 222 * structure in a tree structure. 223 * 224 * @param parent 225 * the parent component 226 * @param theView 227 * the associated data view manager 228 */ 229 public DefaultTreeView(Composite parent, DataViewManager theView) { 230 viewer = theView; 231 shell = parent.getShell(); 232 233 try { 234 curFont = new Font( 235 Display.getCurrent(), 236 ViewProperties.getFontType(), 237 ViewProperties.getFontSize(), 238 SWT.NORMAL); 239 } 240 catch (Exception ex) { 241 curFont = null; 242 } 243 244 // Initialize the Tree 245 tree = new Tree(parent, SWT.MULTI | SWT.VIRTUAL); 246 tree.setSize(tree.computeSize(SWT.DEFAULT, SWT.DEFAULT)); 247 tree.setFont(curFont); 248 249 // Create the context menu for the Tree 250 popupMenu = createPopupMenu(); 251 tree.setMenu(popupMenu); 252 253 // Handle tree key events 254 tree.addKeyListener(new KeyAdapter() { 255 @Override 256 public void keyReleased(KeyEvent e) { 257 int key = e.keyCode; 258 259 if (key == SWT.ARROW_DOWN || key == SWT.ARROW_UP || key == SWT.KEYPAD_2 || key == SWT.KEYPAD_8) { 260 TreeItem[] selectedItems = tree.getSelection(); 261 TreeItem theItem = selectedItems[0]; 262 263 if(theItem.equals(selectedItem)) return; 264 265 selectedItem = theItem; 266 selectedObject = ((HObject) (selectedItem.getData())); 267 FileFormat theFile = selectedObject.getFileFormat(); 268 if ((theFile != null) && !theFile.equals(selectedFile)) { 269 // A different file is selected, handle only one file at a time 270 selectedFile = theFile; 271 tree.deselectAll(); 272 } 273 274 ((HDFView) viewer).showMetaData(selectedObject); 275 } 276 else if (key == SWT.ARROW_LEFT || key == SWT.KEYPAD_4) { 277 if(selectedObject instanceof Group) { 278 selectedItem.setExpanded(false); 279 280 Event collapse = new Event(); 281 collapse.item = selectedItem; 282 283 tree.notifyListeners(SWT.Collapse, collapse); 284 } 285 } 286 else if (key == SWT.ARROW_RIGHT || key == SWT.KEYPAD_6) { 287 if(selectedObject instanceof Group) { 288 selectedItem.setExpanded(true); 289 290 Event expand = new Event(); 291 expand.item = selectedItem; 292 293 tree.notifyListeners(SWT.Expand, expand); 294 } 295 } 296 } 297 }); 298 299 /** 300 * If user presses Enter on a TreeItem, expand/collapse the item if it 301 * is a group, or try to show the data content if it is a data object. 302 */ 303 tree.addListener(SWT.Traverse, new Listener() { 304 @Override 305 public void handleEvent(Event event) { 306 if (event.detail != SWT.TRAVERSE_RETURN) 307 return; 308 309 TreeItem item = selectedItem; 310 if(item == null) 311 return; 312 313 final HObject obj = (HObject) item.getData(); 314 if(obj == null) 315 return; 316 317 if(obj instanceof Group) { 318 boolean isExpanded = item.getExpanded(); 319 320 item.setExpanded(!isExpanded); 321 322 Event expand = new Event(); 323 expand.item = item; 324 325 if(isExpanded) 326 tree.notifyListeners(SWT.Collapse, expand); 327 else 328 tree.notifyListeners(SWT.Expand, expand); 329 } 330 else { 331 if ((selectedObject instanceof Dataset) && !((Dataset) selectedObject).isNULL()) { 332 try { 333 loadDataThread = new LoadDataThread(); 334 loadDataThread.start(); 335 } 336 catch (Exception err) { 337 shell.getDisplay().beep(); 338 Tools.showError(shell, "Select", err.getMessage()); 339 } 340 } 341 else { 342 Tools.showInformation(shell, "Open", "No data to display in an object with a NULL dataspace."); 343 } 344 } 345 } 346 }); 347 348 /** 349 * Handle mouse clicks on data objects in the tree view. A right mouse-click 350 * to show the popup menu for user choice. A double left-mouse-click to 351 * display the data content. A single left-mouse-click to select the current 352 * data object. 353 */ 354 tree.addMouseListener(new MouseAdapter() { 355 // Double click opens data content of selected data object 356 @Override 357 public void mouseDoubleClick(MouseEvent e) { 358 isDefaultDisplay = true; 359 360 try { 361 if(!(selectedObject instanceof Group)) { 362 if ((selectedObject instanceof Dataset) && !((Dataset) selectedObject).isNULL()) { 363 loadDataThread = new LoadDataThread(); 364 loadDataThread.start(); 365 } 366 else { 367 Tools.showInformation(shell, "Open", 368 "No data to display in an object with a NULL dataspace."); 369 } 370 } 371 else { 372 boolean isExpanded = selectedItem.getExpanded(); 373 374 selectedItem.setExpanded(!isExpanded); 375 376 Event expand = new Event(); 377 expand.item = selectedItem; 378 379 if(isExpanded) 380 tree.notifyListeners(SWT.Collapse, expand); 381 else 382 tree.notifyListeners(SWT.Expand, expand); 383 } 384 } 385 catch (Exception ex) { 386 log.trace("defaultDisplay showDataContent failed: {}", ex.getMessage()); 387 ex.printStackTrace(); 388 } 389 } 390 391 // When a mouse release is detected, attempt to set the selected item 392 // and object to the TreeItem under the pointer 393 @Override 394 public void mouseUp(MouseEvent e) { 395 // Make sure user clicked on a TreeItem 396 TreeItem theItem = tree.getItem(new Point(e.x, e.y)); 397 398 if (theItem == null) { 399 tree.deselectAll(); 400 selectedItem = null; 401 selectedObject = null; 402 selectedFile = null; 403 404 // Clear any information shown in the object info panel 405 ((HDFView) viewer).showMetaData(null); 406 407 return; 408 } 409 410 if (theItem.equals(selectedItem)) 411 return; 412 413 FileFormat theFile = null; 414 415 selectedItem = theItem; 416 417 try { 418 selectedObject = (HObject) selectedItem.getData(); 419 } 420 catch(NullPointerException ex) { 421 viewer.showError("Object " + selectedItem.getText() + " had no associated data."); 422 return; 423 } 424 425 try { 426 theFile = selectedObject.getFileFormat(); 427 } 428 catch(NullPointerException ex) { 429 viewer.showError("Error retrieving FileFormat of HObject " + selectedObject.getName() + "."); 430 return; 431 } 432 433 if ((theFile != null) && !theFile.equals(selectedFile)) { 434 // A different file is selected, handle only one file at a time 435 selectedFile = theFile; 436 } 437 438 // Set this file to the most recently selected file in the recent files bar 439 Combo recentFilesCombo = ((HDFView) viewer).getUrlBar(); 440 String filename = selectedFile.getAbsolutePath(); 441 442 try { 443 recentFilesCombo.remove(filename); 444 } 445 catch (Exception ex) {} 446 447 // first entry is always the workdir 448 recentFilesCombo.add(filename, 1); 449 recentFilesCombo.select(1); 450 451 ((HDFView) viewer).showMetaData(selectedObject); 452 } 453 }); 454 455 // Show context menu only if user has selected a data object 456 tree.addMenuDetectListener(new MenuDetectListener() { 457 @Override 458 public void menuDetected(MenuDetectEvent e) { 459 Display display = Display.getDefault(); 460 461 Point pt = display.map(null, tree, new Point(e.x, e.y)); 462 TreeItem item = tree.getItem(pt); 463 if(item == null) { e.doit = false; return; } 464 465 FileFormat theFile = null; 466 467 selectedItem = item; 468 469 log.trace("tree.addMenuDetectListener(): selectedItem={}", selectedItem.getText()); 470 try { 471 selectedObject = (HObject) selectedItem.getData(); 472 } 473 catch(NullPointerException ex) { 474 viewer.showError("Object " + selectedItem.getText() + " had no associated data."); 475 return; 476 } 477 478 try { 479 theFile = selectedObject.getFileFormat(); 480 } 481 catch(NullPointerException ex) { 482 viewer.showError("Error retrieving FileFormat of HObject " + selectedObject.getName() + "."); 483 return; 484 } 485 486 if ((theFile != null) && !theFile.equals(selectedFile)) { 487 // A different file is selected, handle only one file at a time 488 selectedFile = theFile; 489 //tree.deselectAll(); 490 //tree.setSelection(selPath); 491 log.trace("tree.addMenuDetectListener(): selectedFile={}", selectedFile.getAbsolutePath()); 492 } 493 494 ((HDFView) viewer).showMetaData(selectedObject); 495 496 popupMenu.setLocation(display.map(tree, null, pt)); 497 popupMenu.setVisible(true); 498 } 499 }); 500 501 tree.addListener(SWT.Expand, new Listener() { 502 @Override 503 public void handleEvent(Event event) { 504 TreeItem item = (TreeItem) event.item; 505 Object obj = item.getData(); 506 507 if (!(obj instanceof Group)) 508 return; 509 510 Group theGroup = (Group) item.getData(); 511 512 if(theGroup.isRoot()) 513 return; 514 515 // Prevent graphical issues from happening by stopping 516 // tree from redrawing until all the items are created 517 tree.setRedraw(false); 518 519 if(item.getItemCount() > 0) 520 item.setImage(theGroup.hasAttribute() ? folderOpenIconA : folderOpenIcon); 521 522 // Process any remaining SetData events and then allow 523 // the tree to redraw once all are finished 524 // while(tree.getDisplay().readAndDispatch()); 525 526 tree.setRedraw(true); 527 } 528 }); 529 530 tree.addListener(SWT.Collapse, new Listener() { 531 @Override 532 public void handleEvent(Event event) { 533 TreeItem item = (TreeItem) event.item; 534 Object obj = item.getData(); 535 536 if (!(obj instanceof Group)) 537 return; 538 539 Group theGroup = (Group) item.getData(); 540 541 if(theGroup.isRoot()) 542 return; 543 544 item.setImage(theGroup.hasAttribute() ? folderCloseIconA : folderCloseIcon); 545 } 546 }); 547 548 // When groups are expanded, populate TreeItems corresponding to file objects 549 // on demand. 550 tree.addListener(SWT.SetData, new Listener() { 551 @Override 552 public void handleEvent(Event event) { 553 TreeItem item = (TreeItem) event.item; 554 TreeItem parentItem = item.getParentItem(); 555 556 int position = parentItem.indexOf(item); 557 HObject obj = ((Group) parentItem.getData()).getMember(position); 558 559 item.setData(obj); 560 item.setFont(curFont); 561 item.setText(obj.getName()); 562 item.setImage(getObjectTypeImage(obj)); 563 564 if(obj instanceof Group) 565 item.setItemCount(((Group) obj).getMemberList().size()); 566 } 567 }); 568 569 tree.addDisposeListener(new DisposeListener() { 570 @Override 571 public void widgetDisposed(DisposeEvent e) { 572 if (curFont != null) 573 curFont.dispose(); 574 } 575 }); 576 } 577 578 /** Creates a popup menu for a right mouse click on a data object */ 579 private Menu createPopupMenu() { 580 Menu menu = new Menu(tree); 581 MenuItem item; 582 583 item = new MenuItem(menu, SWT.PUSH); 584 item.setText("&Open"); 585 item.addSelectionListener(new SelectionAdapter() { 586 @Override 587 public void widgetSelected(SelectionEvent e) { 588 isDefaultDisplay = true; 589 590 try { 591 if ((selectedObject instanceof Dataset) && !((Dataset) selectedObject).isNULL()) { 592 loadDataThread = new LoadDataThread(); 593 loadDataThread.start(); 594 } 595 else { 596 Tools.showInformation(shell, "Open", "No data to display in an object with a NULL dataspace."); 597 } 598 } 599 catch (Exception err) { 600 shell.getDisplay().beep(); 601 Tools.showError(shell, "Open", err.getMessage()); 602 } 603 } 604 }); 605 606 item = new MenuItem(menu, SWT.PUSH); 607 item.setText("Open &As"); 608 item.addSelectionListener(new SelectionAdapter() { 609 @Override 610 public void widgetSelected(SelectionEvent e) { 611 isDefaultDisplay = false; 612 613 try { 614 if ((selectedObject instanceof Dataset) && !((Dataset) selectedObject).isNULL()) { 615 loadDataThread = new LoadDataThread(); 616 loadDataThread.start(); 617 } 618 else { 619 Tools.showInformation(shell, "Open", "No data to display in an object with a NULL dataspace."); 620 } 621 } 622 catch (Exception err) { 623 shell.getDisplay().beep(); 624 err.printStackTrace(); 625 Tools.showError(shell, "Open", err.getMessage()); 626 } 627 } 628 }); 629 630 openVirtualFilesMenuItem = new MenuItem(menu, SWT.PUSH); 631 openVirtualFilesMenuItem.setText("Open Source Fi&les"); 632 openVirtualFilesMenuItem.addSelectionListener(new SelectionAdapter() { 633 @Override 634 public void widgetSelected(SelectionEvent e) { 635 isDefaultDisplay = false; 636 637 log.trace("createPopupMenu(): selectedObject={}", selectedObject); 638 // If dataset is virtual - open source files. Only for HDF5 639 if (selectedObject != null) { 640 log.trace("createPopupMenu(): selectedObject={} is dataset instance-{} of type H5 {}", selectedObject, selectedObject instanceof Dataset, selectedObject.getFileFormat().isThisType(FileFormat.getFileFormat(FileFormat.FILE_TYPE_HDF5))); 641 if (selectedObject.getFileFormat().isThisType(FileFormat.getFileFormat(FileFormat.FILE_TYPE_HDF5)) && 642 (selectedObject instanceof Dataset)) { 643 Dataset dataset = (Dataset) selectedObject; 644 boolean isVirtual = dataset.isVirtual(); 645 log.trace("createPopupMenu(): isVirtual={}", isVirtual); 646 if(isVirtual) { 647 for(int ndx=0; ndx<dataset.getVirtualMaps(); ndx++) { 648 try { 649 String theFile = selectedFile.getParentFile().getAbsolutePath() + File.separator + dataset.getVirtualFilename(ndx); 650 openFile(theFile, FileFormat.WRITE); 651 } 652 catch (Exception ex) { 653 shell.getDisplay().beep(); 654 ex.printStackTrace(); 655 Tools.showError(shell, "Open", ex.getMessage() + "\n" + dataset.getVirtualFilename(ndx)); 656 } 657 log.trace("createPopupMenu(): virtualNameList[{}]={}", ndx, dataset.getVirtualFilename(ndx)); 658 } 659 } 660 } 661 } 662 } 663 }); 664 665 new MenuItem(menu, SWT.SEPARATOR); 666 667 MenuItem newObjectMenuItem = new MenuItem(menu, SWT.CASCADE); 668 newObjectMenuItem.setText("New"); 669 editGUIs.add(newObjectMenuItem); 670 671 new MenuItem(menu, SWT.SEPARATOR); 672 673 item = new MenuItem(menu, SWT.PUSH); 674 item.setText("Cu&t"); 675 item.addSelectionListener(new SelectionAdapter() { 676 @Override 677 public void widgetSelected(SelectionEvent e) { 678 moveObject(); 679 } 680 }); 681 editGUIs.add(item); 682 683 item = new MenuItem(menu, SWT.PUSH); 684 item.setText("&Copy"); 685 item.addSelectionListener(new SelectionAdapter() { 686 @Override 687 public void widgetSelected(SelectionEvent e) { 688 copyObject(); 689 } 690 }); 691 692 item = new MenuItem(menu, SWT.PUSH); 693 item.setText("&Paste"); 694 item.addSelectionListener(new SelectionAdapter() { 695 @Override 696 public void widgetSelected(SelectionEvent e) { 697 pasteObject(); 698 } 699 }); 700 editGUIs.add(item); 701 702 item = new MenuItem(menu, SWT.PUSH); 703 item.setText("&Delete"); 704 item.addSelectionListener(new SelectionAdapter() { 705 @Override 706 public void widgetSelected(SelectionEvent e) { 707 cutObject(); 708 } 709 }); 710 editGUIs.add(item); 711 712 exportDatasetMenuItem = new MenuItem(menu, SWT.CASCADE); 713 exportDatasetMenuItem.setText("Export Dataset"); 714 715 new MenuItem(menu, SWT.SEPARATOR); 716 717 item = new MenuItem(menu, SWT.PUSH); 718 item.setText("&Save to"); 719 item.addSelectionListener(new SelectionAdapter() { 720 @Override 721 public void widgetSelected(SelectionEvent e) { 722 TreeItem[] selectedItems = tree.getSelection(); 723 724 if (selectedItems.length <= 0) return; 725 726 for (int i = 0; i < selectedItems.length; i++) { 727 if (((HObject) selectedItems[i].getData() instanceof Group) 728 && ((Group) selectedItems[i].getData()).isRoot()) { 729 shell.getDisplay().beep(); 730 Tools.showError(shell, "Save", "Cannot save the root group.\nUse \"Save As\" from file menu to save the whole file"); 731 return; 732 } 733 } 734 735 String filetype = FileFormat.FILE_TYPE_HDF4; 736 if (selectedObject.getFileFormat().isThisType(FileFormat.getFileFormat(FileFormat.FILE_TYPE_HDF5))) 737 filetype = FileFormat.FILE_TYPE_HDF5; 738 else if (selectedObject.getFileFormat().isThisType(FileFormat.getFileFormat(FileFormat.FILE_TYPE_NC3))) 739 filetype = FileFormat.FILE_TYPE_NC3; 740 741 String currentDir = selectedObject.getFileFormat().getParent(); 742 743 if (currentDir != null) 744 currentDir += File.separator; 745 else 746 currentDir = ""; 747 748 FileDialog fChooser = new FileDialog(shell, SWT.SAVE); 749 750 DefaultFileFilter filter = null; 751 752 if (filetype.equals(FileFormat.FILE_TYPE_HDF4)) { 753 fChooser.setFileName(Tools.checkNewFile(currentDir, ".hdf").getName()); 754 filter = DefaultFileFilter.getFileFilterHDF4(); 755 } 756 else if (filetype.equals(FileFormat.FILE_TYPE_NC3)) { 757 fChooser.setFileName(Tools.checkNewFile(currentDir, ".nc").getName()); 758 filter = DefaultFileFilter.getFileFilterNetCDF3(); 759 } 760 else { 761 fChooser.setFileName(Tools.checkNewFile(currentDir, ".h5").getName()); 762 filter = DefaultFileFilter.getFileFilterHDF5(); 763 } 764 765 fChooser.setFilterExtensions(new String[] {filter.getExtensions()}); 766 fChooser.setFilterNames(new String[] {filter.getDescription()}); 767 fChooser.setFilterIndex(0); 768 769 String filename = fChooser.open(); 770 771 if(filename == null) 772 return; 773 774 try { 775 Tools.createNewFile(filename, currentDir, filetype, fileList); 776 } 777 catch (Exception ex) { 778 Tools.showError(shell, "Save", ex.getMessage()); 779 } 780 781 FileFormat dstFile = null; 782 783 try { 784 dstFile = openFile(filename, FileFormat.WRITE); 785 } 786 catch (Exception ex) { 787 shell.getDisplay().beep(); 788 Tools.showError(shell, "Save", ex.getMessage() + "\n" + filename); 789 } 790 if (dstFile != null) 791 pasteObject(selectedItems, findTreeItem(dstFile.getRootObject()), dstFile); 792 } 793 }); 794 795 item = new MenuItem(menu, SWT.PUSH); 796 item.setText("&Rename"); 797 item.addSelectionListener(new SelectionAdapter() { 798 @Override 799 public void widgetSelected(SelectionEvent e) { 800 renameObject(); 801 } 802 }); 803 editGUIs.add(item); 804 805 new MenuItem(menu, SWT.SEPARATOR); 806 807 changeIndexItem = new MenuItem(menu, SWT.PUSH); 808 changeIndexItem.setText("Change file indexing"); 809 changeIndexItem.addSelectionListener(new SelectionAdapter() { 810 @Override 811 public void widgetSelected(SelectionEvent e) { 812 ChangeIndexingDialog dialog = new ChangeIndexingDialog(shell, SWT.NONE, selectedFile); 813 dialog.open(); 814 if (dialog.isReloadFile()) { 815 try { 816 selectedFile.setIndexType(dialog.getIndexType()); 817 } 818 catch (Exception ex) { 819 log.debug("ChangeIndexingDialog(): setIndexType failed: ", ex); 820 } 821 try { 822 selectedFile.setIndexOrder(dialog.getIndexOrder()); 823 } 824 catch (Exception ex) { 825 log.debug("ChangeIndexingDialog(): setIndexOrder failed: ", ex); 826 } 827 828 try { 829 reopenFile(selectedFile, -1); 830 } 831 catch (Exception ex) { 832 log.debug("reload file {} failure after indexing change: ", selectedFile.getAbsolutePath(), ex); 833 Tools.showError(shell, "File reload error", "Error reloading file " + selectedFile.getAbsolutePath() + " after changing indexing: " + ex.getMessage()); 834 } 835 } 836 } 837 }); 838 839 new MenuItem(menu, SWT.SEPARATOR); 840 841 item = new MenuItem(menu, SWT.PUSH); 842 item.setText("&Find"); 843 item.addSelectionListener(new SelectionAdapter() { 844 @Override 845 public void widgetSelected(SelectionEvent e) { 846 String findStr = currentSearchPhrase; 847 if (findStr == null) 848 findStr = ""; 849 850 findStr = (new InputDialog(shell, "Find Object by Name", 851 "Find (e.g. O3Quality, O3*, or *Quality):", findStr)).open(); 852 853 if (findStr != null && findStr.length() > 0) 854 currentSearchPhrase = findStr; 855 856 find(currentSearchPhrase, selectedItem); 857 } 858 }); 859 860 new MenuItem(menu, SWT.SEPARATOR); 861 862 item = new MenuItem(menu, SWT.PUSH); 863 item.setText("Expand All"); 864 item.addSelectionListener(new SelectionAdapter() { 865 @Override 866 public void widgetSelected(SelectionEvent e) { 867 if(selectedItem != null) 868 recursiveExpand(selectedItem, true); 869 } 870 }); 871 872 item = new MenuItem(menu, SWT.PUSH); 873 item.setText("Collapse All"); 874 item.addSelectionListener(new SelectionAdapter() { 875 @Override 876 public void widgetSelected(SelectionEvent e) { 877 if(selectedItem != null) 878 recursiveExpand(selectedItem, false); 879 } 880 }); 881 882 new MenuItem(menu, SWT.SEPARATOR); 883 884 item = new MenuItem(menu, SWT.PUSH); 885 item.setText("Close Fil&e"); 886 item.addSelectionListener(new SelectionAdapter() { 887 @Override 888 public void widgetSelected(SelectionEvent e) { 889 try { 890 ((HDFView) viewer).closeFile(selectedFile); 891 } 892 catch (Exception ex) { 893 Tools.showError(shell, "Close", ex.getMessage()); 894 } 895 } 896 }); 897 898 item = new MenuItem(menu, SWT.PUSH); 899 item.setText("&Reload File"); 900 item.addSelectionListener(new SelectionAdapter() { 901 @Override 902 public void widgetSelected(SelectionEvent e) { 903 try { 904 reopenFile(selectedFile, -1); 905 } 906 catch (Exception ex) { 907 log.debug("reload file {} failure: ", selectedFile.getAbsolutePath(), ex); 908 Tools.showError(shell, "File reload error", "Error reloading file " + selectedFile.getAbsolutePath() + ": " + ex.getMessage()); 909 } 910 } 911 }); 912 913 item = new MenuItem(menu, SWT.CASCADE); 914 item.setText("Reload File As"); 915 916 Menu reloadFileMenu = new Menu(item); 917 item.setMenu(reloadFileMenu); 918 919 item = new MenuItem(reloadFileMenu, SWT.PUSH); 920 item.setText("Read-Only"); 921 item.addSelectionListener(new SelectionAdapter() { 922 @Override 923 public void widgetSelected(SelectionEvent e) { 924 try { 925 reopenFile(selectedFile, FileFormat.READ); 926 } 927 catch (Exception ex) { 928 log.debug("reload file {} as read-only failure: ", selectedFile.getAbsolutePath(), ex); 929 Tools.showError(shell, "File reload error", "Error reloading file " + selectedFile.getAbsolutePath() + " read-only: " + ex.getMessage()); 930 } 931 } 932 }); 933 934 item = new MenuItem(reloadFileMenu, SWT.PUSH); 935 item.setText("SWMR Read-Only"); 936 item.addSelectionListener(new SelectionAdapter() { 937 @Override 938 public void widgetSelected(SelectionEvent e) { 939 try { 940 reopenFile(selectedFile, FileFormat.READ | FileFormat.MULTIREAD); 941 } 942 catch (Exception ex) { 943 log.debug("reload file {} as SWMR read-only failure: ", selectedFile.getAbsolutePath(), ex); 944 Tools.showError(shell, "File reload error", "Error reloading file " + selectedFile.getAbsolutePath() + " SWMR read-only: " + ex.getMessage()); 945 } 946 } 947 }); 948 949 item = new MenuItem(reloadFileMenu, SWT.PUSH); 950 item.setText("Read/Write"); 951 item.addSelectionListener(new SelectionAdapter() { 952 @Override 953 public void widgetSelected(SelectionEvent e) { 954 try { 955 reopenFile(selectedFile, FileFormat.WRITE); 956 } 957 catch (Exception ex) { 958 log.debug("reload file {} as read/write failure: ", selectedFile.getAbsolutePath(), ex); 959 Tools.showError(shell, "File reload error", "Error reloading file " + selectedFile.getAbsolutePath() + " read/write: " + ex.getMessage()); 960 } 961 } 962 }); 963 964 new MenuItem(menu, SWT.SEPARATOR); 965 966 setLibVerBoundsItem = new MenuItem(menu, SWT.NONE); 967 setLibVerBoundsItem.setText("Set Lib version bounds"); 968 setLibVerBoundsItem.addSelectionListener(new SelectionAdapter() { 969 @Override 970 public void widgetSelected(SelectionEvent e) { 971 new ChangeLibVersionDialog(shell, SWT.NONE).open(); 972 } 973 }); 974 975 976 // Add new object menu 977 newObjectMenu = new Menu(menu); 978 newObjectMenuItem.setMenu(newObjectMenu); 979 980 item = new MenuItem(newObjectMenu, SWT.PUSH); 981 item.setText("Group"); 982 item.setImage(ViewProperties.getFoldercloseIcon()); 983 item.addSelectionListener(new SelectionAdapter() { 984 @Override 985 public void widgetSelected(SelectionEvent e) { 986 addNewObject(OBJECT_TYPE.GROUP); 987 } 988 }); 989 editGUIs.add(item); 990 991 addDatasetMenuItem = new MenuItem(newObjectMenu, SWT.PUSH); 992 addDatasetMenuItem.setText("Dataset"); 993 addDatasetMenuItem.setImage(ViewProperties.getDatasetIcon()); 994 addDatasetMenuItem.addSelectionListener(new SelectionAdapter() { 995 @Override 996 public void widgetSelected(SelectionEvent e) { 997 addNewObject(OBJECT_TYPE.DATASET); 998 } 999 }); 1000 editGUIs.add(addDatasetMenuItem); 1001 1002 item = new MenuItem(newObjectMenu, SWT.PUSH); 1003 item.setText("Image"); 1004 item.setImage(ViewProperties.getImageIcon()); 1005 item.addSelectionListener(new SelectionAdapter() { 1006 @Override 1007 public void widgetSelected(SelectionEvent e) { 1008 addNewObject(OBJECT_TYPE.IMAGE); 1009 } 1010 }); 1011 editGUIs.add(item); 1012 1013 addTableMenuItem = new MenuItem(newObjectMenu, SWT.PUSH); 1014 addTableMenuItem.setText("Compound DS"); 1015 addTableMenuItem.setImage(ViewProperties.getTableIcon()); 1016 addTableMenuItem.addSelectionListener(new SelectionAdapter() { 1017 @Override 1018 public void widgetSelected(SelectionEvent e) { 1019 addNewObject(OBJECT_TYPE.TABLE); 1020 } 1021 }); 1022 editGUIs.add(addTableMenuItem); 1023 1024 addDatatypeMenuItem = new MenuItem(newObjectMenu, SWT.PUSH); 1025 addDatatypeMenuItem.setText("Datatype"); 1026 addDatatypeMenuItem.setImage(ViewProperties.getDatatypeIcon()); 1027 addDatatypeMenuItem.addSelectionListener(new SelectionAdapter() { 1028 @Override 1029 public void widgetSelected(SelectionEvent e) { 1030 addNewObject(OBJECT_TYPE.DATATYPE); 1031 } 1032 }); 1033 editGUIs.add(addDatatypeMenuItem); 1034 1035 addLinkMenuItem = new MenuItem(newObjectMenu, SWT.PUSH); 1036 addLinkMenuItem.setText("Link"); 1037 addLinkMenuItem.setImage(ViewProperties.getLinkIcon()); 1038 addLinkMenuItem.addSelectionListener(new SelectionAdapter() { 1039 @Override 1040 public void widgetSelected(SelectionEvent e) { 1041 addNewObject(OBJECT_TYPE.LINK); 1042 } 1043 }); 1044 editGUIs.add(addLinkMenuItem); 1045 1046 1047 // Add export dataset menu 1048 exportDatasetMenu = new Menu(menu); 1049 exportDatasetMenuItem.setMenu(exportDatasetMenu); 1050 1051 item = new MenuItem(exportDatasetMenu, SWT.PUSH); 1052 item.setText("Export Data to Text File"); 1053 item.addSelectionListener(new SelectionAdapter() { 1054 @Override 1055 public void widgetSelected(SelectionEvent e) { 1056 binaryOrder = 99; 1057 1058 try { 1059 saveDataAsFile(); 1060 } 1061 catch (Exception ex) { 1062 shell.getDisplay().beep(); 1063 Tools.showError(shell, "Export Dataset", ex.getMessage()); 1064 } 1065 } 1066 }); 1067 1068 item = new MenuItem(exportDatasetMenu, SWT.PUSH); 1069 item.setText("Export Data as Native Order"); 1070 item.addSelectionListener(new SelectionAdapter() { 1071 @Override 1072 public void widgetSelected(SelectionEvent e) { 1073 binaryOrder = 1; 1074 1075 try { 1076 saveDataAsFile(); 1077 } 1078 catch (Exception ex) { 1079 shell.getDisplay().beep(); 1080 Tools.showError(shell, "Export Dataset", ex.getMessage()); 1081 } 1082 } 1083 }); 1084 1085 item = new MenuItem(exportDatasetMenu, SWT.PUSH); 1086 item.setText("Export Data as Little Endian"); 1087 item.addSelectionListener(new SelectionAdapter() { 1088 @Override 1089 public void widgetSelected(SelectionEvent e) { 1090 binaryOrder = 2; 1091 1092 try { 1093 saveDataAsFile(); 1094 } 1095 catch (Exception ex) { 1096 shell.getDisplay().beep(); 1097 Tools.showError(shell, "Export Dataset", ex.getMessage()); 1098 } 1099 } 1100 }); 1101 1102 item = new MenuItem(exportDatasetMenu, SWT.PUSH); 1103 item.setText("Export Data as Big Endian"); 1104 item.addSelectionListener(new SelectionAdapter() { 1105 @Override 1106 public void widgetSelected(SelectionEvent e) { 1107 binaryOrder = 3; 1108 1109 try { 1110 saveDataAsFile(); 1111 } 1112 catch (Exception ex) { 1113 shell.getDisplay().beep(); 1114 Tools.showError(shell, "Export Dataset", ex.getMessage()); 1115 } 1116 } 1117 }); 1118 1119 // Add listener to dynamically enable/disable menu items based 1120 // on selection in tree 1121 menu.addMenuListener(new MenuAdapter() { 1122 @Override 1123 public void menuShown(MenuEvent e) { 1124 if (selectedItem == null || selectedObject == null || selectedFile == null) return; 1125 1126 boolean isReadOnly = selectedObject.getFileFormat().isReadOnly(); 1127 boolean isWritable = !isReadOnly; 1128 1129 setEnabled(editGUIs, isWritable); 1130 1131 if (selectedObject instanceof Group) { 1132 boolean state = !(((Group) selectedObject).isRoot()); 1133 1134 popupMenu.getItem(0).setEnabled(false); // "Open" menuitem 1135 popupMenu.getItem(1).setEnabled(false); // "Open as" menuitem 1136 popupMenu.getItem(2).setEnabled(false); // "Open Source Files" menuitem 1137 popupMenu.getItem(6).setEnabled( 1138 (selectedObject.getFileFormat().isThisType(FileFormat.getFileFormat(FileFormat.FILE_TYPE_HDF5))) 1139 && state && isWritable); // "Cut" menuitem 1140 popupMenu.getItem(7).setEnabled(state); // "Copy" menuitem 1141 popupMenu.getItem(9).setEnabled(state && isWritable); // "Delete" menuitem 1142 popupMenu.getItem(10).setEnabled(false); // "Export Dataset" menuitem 1143 popupMenu.getItem(12).setEnabled(state && isWritable); // "Save to" menuitem 1144 popupMenu.getItem(13).setEnabled(state && isWritable); // "Rename" menuitem 1145 } 1146 else { 1147 popupMenu.getItem(0).setEnabled(true); // "Open" menuitem 1148 popupMenu.getItem(1).setEnabled(true); // "Open as" menuitem 1149 popupMenu.getItem(2).setEnabled( 1150 (selectedObject.getFileFormat().isThisType(FileFormat.getFileFormat(FileFormat.FILE_TYPE_HDF5)))); // "Open Source Files" menuitem 1151 popupMenu.getItem(6).setEnabled( 1152 (selectedObject.getFileFormat().isThisType(FileFormat.getFileFormat(FileFormat.FILE_TYPE_HDF5))) 1153 && isWritable); // "Cut" menuitem 1154 popupMenu.getItem(7).setEnabled(true); // "Copy" menuitem 1155 popupMenu.getItem(9).setEnabled(isWritable); // "Delete" menuitem 1156 popupMenu.getItem(10).setEnabled(true); // "Export Dataset" menuitem 1157 popupMenu.getItem(12).setEnabled(true); // "Save to" menuitem 1158 popupMenu.getItem(13).setEnabled(isWritable); // "Rename" menuitem 1159 } 1160 1161 // Adding table is only supported by HDF5 1162 if ((selectedFile != null) && selectedFile.isThisType(FileFormat.getFileFormat(FileFormat.FILE_TYPE_HDF5))) { 1163 //openVirtualFilesMenuItem.setEnabled(true); // Should be moved to createPopupMenu() since swt doesn't support MenuItem.setVisible 1164 1165 boolean state = false; 1166 if ((selectedObject instanceof Group)) { 1167 state = (((Group) selectedObject).isRoot()); 1168 setLibVerBoundsItem.setEnabled(isWritable && state); 1169 } 1170 else { 1171 setLibVerBoundsItem.setEnabled(false); 1172 } 1173 1174 changeIndexItem.setEnabled(state); 1175 } 1176 else { 1177 addTableMenuItem.setEnabled(false); 1178 addDatatypeMenuItem.setEnabled(false); 1179 addLinkMenuItem.setEnabled(false); 1180 //openVirtualFilesMenuItem.setEnabled(false); 1181 setLibVerBoundsItem.setEnabled(false); 1182 changeIndexItem.setEnabled(false); 1183 } 1184 1185 // Export table is only supported by HDF5 1186 if ((selectedObject != null) && selectedObject.getFileFormat().isThisType(FileFormat.getFileFormat(FileFormat.FILE_TYPE_HDF5))) { 1187 if ((selectedObject instanceof Dataset)) { 1188 Dataset dataset = (Dataset) selectedObject; 1189 if ((dataset instanceof ScalarDS)) 1190 exportDatasetMenuItem.setEnabled(true); 1191 openVirtualFilesMenuItem.setEnabled(true); 1192 } 1193 else { 1194 exportDatasetMenuItem.setEnabled(false); 1195 openVirtualFilesMenuItem.setEnabled(false); 1196 } 1197 } 1198 else { 1199 exportDatasetMenuItem.setEnabled(false); 1200 openVirtualFilesMenuItem.setEnabled(false); 1201 } 1202 } 1203 }); 1204 1205 return menu; 1206 } 1207 1208 /** 1209 * Creates a dialog for the user to select a type of new 1210 * object to be added to the TreeView, then passes the 1211 * result of the dialog on to addObject(HObject newObject, Group parentGroup) 1212 * 1213 * @param type 1214 * The type (GROUP, DATASET, IMAGE, TABLE, DATATYPE, LINK) of object to add. 1215 */ 1216 private void addNewObject(OBJECT_TYPE type) { 1217 if ((selectedObject == null) || (selectedItem == null)) return; 1218 1219 TreeItem parentItem = null; 1220 if(selectedObject instanceof Group) 1221 parentItem = selectedItem; 1222 else 1223 parentItem = selectedItem.getParentItem(); 1224 1225 // Find the root item of the selected file 1226 TreeItem rootItem = selectedItem; 1227 while(rootItem.getParentItem() != null) 1228 rootItem = rootItem.getParentItem(); 1229 1230 HObject obj = null; 1231 1232 switch(type) { 1233 case GROUP: 1234 NewGroupDialog groupDialog = new NewGroupDialog(shell, (Group) parentItem.getData(), 1235 breadthFirstUserObjects(rootItem)); 1236 groupDialog.open(); 1237 obj = groupDialog.getObject(); 1238 parentItem = findTreeItem(groupDialog.getParentGroup()); 1239 break; 1240 case DATASET: 1241 NewDatasetDialog datasetDialog = new NewDatasetDialog(shell, (Group) parentItem.getData(), breadthFirstUserObjects(rootItem)); 1242 datasetDialog.open(); 1243 obj = datasetDialog.getObject(); 1244 parentItem = findTreeItem(datasetDialog.getParentGroup()); 1245 break; 1246 case IMAGE: 1247 NewImageDialog imageDialog = new NewImageDialog(shell, (Group) parentItem.getData(), breadthFirstUserObjects(rootItem)); 1248 imageDialog.open(); 1249 obj = imageDialog.getObject(); 1250 parentItem = findTreeItem(imageDialog.getParentGroup()); 1251 break; 1252 case TABLE: 1253 NewCompoundDatasetDialog tableDialog = new NewCompoundDatasetDialog(shell, (Group) parentItem.getData(), breadthFirstUserObjects(rootItem)); 1254 tableDialog.open(); 1255 obj = tableDialog.getObject(); 1256 parentItem = findTreeItem(tableDialog.getParentGroup()); 1257 break; 1258 case DATATYPE: 1259 NewDatatypeDialog datatypeDialog = new NewDatatypeDialog(shell, (Group) parentItem.getData(), breadthFirstUserObjects(rootItem)); 1260 datatypeDialog.open(); 1261 obj = datatypeDialog.getObject(); 1262 parentItem = findTreeItem(datatypeDialog.getParentGroup()); 1263 break; 1264 case LINK: 1265 NewLinkDialog linkDialog = new NewLinkDialog(shell, (Group) parentItem.getData(), breadthFirstUserObjects(rootItem), getCurrentFiles()); 1266 linkDialog.open(); 1267 obj = linkDialog.getObject(); 1268 parentItem = findTreeItem(linkDialog.getParentGroup()); 1269 break; 1270 } 1271 1272 if (obj == null) 1273 return; 1274 1275 try { 1276 this.insertObject(obj, parentItem); 1277 } 1278 catch (Exception ex) { 1279 shell.getDisplay().beep(); 1280 Tools.showError(shell, "Create", ex.getMessage()); 1281 } 1282 } 1283 1284 /** 1285 * Adds an already created HObject to the tree under the 1286 * TreeItem containing the specified parent group. 1287 * 1288 * @param obj 1289 * the object to add. 1290 * @param parentGroup 1291 * the parent group to add the object to. 1292 */ 1293 @Override 1294 public TreeItem addObject(HObject obj, Group parentGroup) { 1295 if ((obj == null) || (parentGroup == null)) 1296 return null; 1297 1298 return insertObject(obj, findTreeItem(parentGroup)); 1299 } 1300 1301 /** 1302 * Insert an object into the tree as the last object 1303 * under parent item pobj. 1304 * 1305 * @param obj 1306 * the object to insert. 1307 * @param pobj 1308 * the parent TreeItem to insert the new object under. 1309 * If null, inserts the object at the end of the Tree. 1310 * 1311 * @return the newly created TreeItem 1312 */ 1313 private TreeItem insertObject(HObject obj, TreeItem pobj) { 1314 if ((obj == null)) 1315 return null; 1316 1317 TreeItem item; 1318 1319 if(pobj != null) { 1320 item = new TreeItem(pobj, SWT.NONE, pobj.getItemCount()); 1321 item.setFont(curFont); 1322 item.setText(obj.getName()); 1323 } 1324 else { 1325 // Parent object was null, insert at end of tree as root object 1326 item = new TreeItem(tree, SWT.NONE, tree.getItemCount()); 1327 item.setFont(curFont); 1328 item.setText(obj.getFileFormat().getName()); 1329 } 1330 1331 item.setData(obj); 1332 item.setImage(getObjectTypeImage(obj)); 1333 1334 return item; 1335 } 1336 1337 /** Move selected objects */ 1338 private void moveObject() { 1339 objectsToCopy = tree.getSelection(); 1340 moveFlag = true; 1341 currentSelectionsForMove = tree.getSelection(); 1342 } 1343 1344 /** Copy selected objects */ 1345 private void copyObject() { 1346 if (moveFlag) 1347 if(!Tools.showConfirm(shell, "Copy object", "Do you want to copy all the selected object(s) instead of move?")) 1348 return; 1349 moveFlag = false; 1350 currentSelectionsForMove = null; 1351 objectsToCopy = tree.getSelection(); 1352 } 1353 1354 /** Delete selected objects */ 1355 private void cutObject() { 1356 if (moveFlag) 1357 if(!Tools.showConfirm(shell, "Delete object", "Do you want to delete all the selected object(s) instead of move?")) 1358 return; 1359 moveFlag = false; 1360 currentSelectionsForMove = null; 1361 objectsToCopy = tree.getSelection(); 1362 removeSelectedObjects(); 1363 } 1364 1365 /** Paste selected objects */ 1366 private void pasteObject() { 1367 if (moveFlag) { 1368 HObject theObj = null; 1369 for (int i = 0; i < currentSelectionsForMove.length; i++) { 1370 TreeItem currentItem = currentSelectionsForMove[i]; 1371 theObj = (HObject) currentItem.getData(); 1372 1373 if (isObjectOpen(theObj)) { 1374 shell.getDisplay().beep(); 1375 Tools.showError(shell, "Move Objects", "Cannot move the selected object: " + theObj 1376 + "\nThe dataset or dataset in the group is in use." 1377 + "\n\nPlease close the dataset(s) and try again.\n"); 1378 1379 moveFlag = false; 1380 currentSelectionsForMove = null; 1381 objectsToCopy = null; 1382 return; 1383 } 1384 } 1385 } 1386 1387 TreeItem pitem = selectedItem; 1388 1389 if ((objectsToCopy == null) || (objectsToCopy.length <= 0) || (pitem == null)) 1390 return; 1391 1392 FileFormat srcFile = ((HObject) objectsToCopy[0].getData()).getFileFormat(); 1393 FileFormat dstFile = getSelectedFile(); 1394 FileFormat h5file = FileFormat.getFileFormat(FileFormat.FILE_TYPE_HDF5); 1395 FileFormat h4file = FileFormat.getFileFormat(FileFormat.FILE_TYPE_HDF4); 1396 FileFormat ncfile = FileFormat.getFileFormat(FileFormat.FILE_TYPE_NC3); 1397 1398 if (srcFile == null) { 1399 shell.getDisplay().beep(); 1400 Tools.showError(shell, "Copy", "Source file is null."); 1401 return; 1402 } 1403 else if (dstFile == null) { 1404 shell.getDisplay().beep(); 1405 Tools.showError(shell, "Copy", "Destination file is null."); 1406 return; 1407 } 1408 else if (srcFile.isThisType(h4file) && dstFile.isThisType(h5file)) { 1409 shell.getDisplay().beep(); 1410 Tools.showError(shell, "Copy", "Unsupported operation: cannot copy HDF4 object to HDF5 file"); 1411 return; 1412 } 1413 else if (srcFile.isThisType(h5file) && dstFile.isThisType(h4file)) { 1414 shell.getDisplay().beep(); 1415 Tools.showError(shell, "Copy", "Unsupported operation: cannot copy HDF5 object to HDF4 file"); 1416 return; 1417 } 1418 else if (srcFile.isThisType(ncfile) && dstFile.isThisType(ncfile)) { 1419 shell.getDisplay().beep(); 1420 Tools.showError(shell, "Copy", "Unsupported operation: cannot copy NetCDF3 objects"); 1421 return; 1422 } 1423 1424 if (moveFlag) { 1425 if (srcFile != dstFile) { 1426 shell.getDisplay().beep(); 1427 Tools.showError(shell, "Move", "Cannot move the selected object to different file"); 1428 moveFlag = false; 1429 currentSelectionsForMove = null; 1430 objectsToCopy = null; 1431 return; 1432 } 1433 } 1434 1435 /* 1436 if (pitem.getParentItem() != null) { 1437 pitem = pitem.getParentItem(); 1438 }*/ 1439 1440 Group pgroup = (Group) pitem.getData(); 1441 String fullPath = pgroup.getPath() + pgroup.getName(); 1442 if (pgroup.isRoot()) { 1443 fullPath = HObject.SEPARATOR; 1444 } 1445 1446 String msg = ""; 1447 if (srcFile.isThisType(h4file)) 1448 msg = "WARNING: object can not be deleted after it is copied.\n\n"; 1449 1450 msg += "Do you want to copy the selected object(s) to \nGroup: " + fullPath + "\nFile: " 1451 + dstFile.getFilePath(); 1452 1453 if (moveFlag) { 1454 String moveMsg = "Do you want to move the selected object(s) to \nGroup: " + fullPath + "\nFile: " 1455 + dstFile.getFilePath(); 1456 if(!Tools.showConfirm(shell, "Move Object", moveMsg)) 1457 return; 1458 } 1459 else { 1460 if(!Tools.showConfirm(shell, "Copy object", msg)) 1461 return; 1462 } 1463 1464 pasteObject(objectsToCopy, pitem, dstFile); 1465 1466 if (moveFlag) { 1467 removeSelectedObjects(); 1468 moveFlag = false; 1469 currentSelectionsForMove = null; 1470 objectsToCopy = null; 1471 } 1472 } 1473 1474 /** Paste selected objects */ 1475 private void pasteObject(TreeItem[] objList, TreeItem pobj, FileFormat dstFile) { 1476 if ((objList == null) || (objList.length <= 0) || (pobj == null)) return; 1477 1478 FileFormat srcFile = ((HObject) objList[0].getData()).getFileFormat(); 1479 Group pgroup = (Group) pobj.getData(); 1480 1481 HObject theObj = null; 1482 for (int i = 0; i < objList.length; i++) { 1483 theObj = (HObject) objList[i].getData(); 1484 1485 if ((theObj instanceof Group) && ((Group) theObj).isRoot()) { 1486 shell.getDisplay().beep(); 1487 Tools.showError(shell, "Paste", "Unsupported operation: cannot copy the root group"); 1488 return; 1489 } 1490 1491 // Check if it creates infinite loop 1492 Group pg = pgroup; 1493 while (!pg.isRoot()) { 1494 if (theObj.equals(pg)) { 1495 shell.getDisplay().beep(); 1496 Tools.showError(shell, "Paste", "Unsupported operation: cannot copy a group to itself."); 1497 return; 1498 } 1499 pg = pg.getParent(); 1500 } 1501 1502 try { 1503 log.trace("pasteObject(...): dstFile.copy({}, {}, null)", theObj, pgroup); 1504 1505 HObject newObj = null; 1506 if((newObj = srcFile.copy(theObj, pgroup, null)) != null) { 1507 // Add the node to the tree 1508 TreeItem newItem = insertObject(newObj, pobj); 1509 1510 // If this is a group, add its first level child items 1511 if(newObj instanceof Group) { 1512 Iterator<HObject> children = ((Group) newObj).getMemberList().iterator(); 1513 while(children.hasNext()) 1514 insertObject(children.next(), newItem); 1515 } 1516 } 1517 } 1518 catch (Exception ex) { 1519 shell.getDisplay().beep(); 1520 Tools.showError(shell, "Paste", ex.getMessage()); 1521 } 1522 } // (int i = 0; i < objList.length; i++) 1523 } 1524 1525 /** 1526 * Rename the currently selected object. 1527 */ 1528 private void renameObject() { 1529 if (moveFlag) { 1530 if(!Tools.showConfirm(shell, "Rename object", "Do you want to rename all the selected object(s) instead of move?")) 1531 return; 1532 } 1533 moveFlag = false; 1534 currentSelectionsForMove = null; 1535 1536 if (selectedObject == null) 1537 return; 1538 1539 if ((selectedObject instanceof Group) && ((Group) selectedObject).isRoot()) { 1540 shell.getDisplay().beep(); 1541 Tools.showError(shell, "Rename", "Cannot rename the root."); 1542 return; 1543 } 1544 1545 boolean isH4 = selectedObject.getFileFormat().isThisType(FileFormat.getFileFormat(FileFormat.FILE_TYPE_HDF4)); 1546 if (isH4) { 1547 shell.getDisplay().beep(); 1548 Tools.showError(shell, "Rename", "Cannot rename HDF4 object."); 1549 return; 1550 } 1551 1552 boolean isN3 = selectedObject.getFileFormat().isThisType(FileFormat.getFileFormat(FileFormat.FILE_TYPE_NC3)); 1553 if (isN3) { 1554 shell.getDisplay().beep(); 1555 Tools.showError(shell, "Rename", "Cannot rename NetCDF3 object."); 1556 return; 1557 } 1558 1559 String oldName = selectedObject.getName(); 1560 String newName = (new InputDialog(shell, "Rename Object", 1561 "Rename \"" + oldName + "\" to:", oldName)).open(); 1562 1563 if (newName == null) 1564 return; 1565 1566 newName = newName.trim(); 1567 if ((newName == null) || (newName.length() == 0) || newName.equals(oldName)) 1568 return; 1569 1570 try { 1571 selectedObject.setName(newName); 1572 } 1573 catch (Exception ex) { 1574 shell.getDisplay().beep(); 1575 Tools.showError(shell, "Rename Object", ex.getMessage()); 1576 } 1577 1578 selectedItem.setText(newName); 1579 } 1580 1581 private void removeSelectedObjects() { 1582 FileFormat theFile = getSelectedFile(); 1583 if (theFile.isThisType(FileFormat.getFileFormat(FileFormat.FILE_TYPE_HDF4))) { 1584 shell.getDisplay().beep(); 1585 Tools.showError(shell, "Remove object", "Unsupported operation: cannot delete HDF4 object."); 1586 return; 1587 } 1588 if (theFile.isThisType(FileFormat.getFileFormat(FileFormat.FILE_TYPE_NC3))) { 1589 shell.getDisplay().beep(); 1590 Tools.showError(shell, "Remove object", "Unsupported operation: cannot delete NetCDF3 object."); 1591 return; 1592 } 1593 1594 TreeItem[] currentSelections = tree.getSelection(); 1595 1596 if (moveFlag) 1597 currentSelections = currentSelectionsForMove; 1598 if ((currentSelections == null) || (currentSelections.length <= 0)) 1599 return; 1600 1601 if (!moveFlag) { 1602 if(!Tools.showConfirm(shell, "Remove object", "Do you want to remove all the selected object(s) ?")) 1603 return; 1604 } 1605 1606 HObject theObj = null; 1607 for (int i = 0; i < currentSelections.length; i++) { 1608 TreeItem currentItem = currentSelections[i]; 1609 theObj = (HObject) currentItem.getData(); 1610 1611 // Cannot delete a root object 1612 if (theObj instanceof Group && ((Group) theObj).isRoot()) { 1613 shell.getDisplay().beep(); 1614 Tools.showError(shell, "Delete Objects", "Unsupported operation: cannot delete the file root."); 1615 return; 1616 } 1617 1618 if (!moveFlag) { 1619 if (isObjectOpen(theObj)) { 1620 shell.getDisplay().beep(); 1621 Tools.showError(shell, "Delete Objects", "Cannot delete the selected object: " + theObj 1622 + "\nThe dataset or dataset in the group is in use." 1623 + "\n\nPlease close the dataset(s) and try again.\n"); 1624 continue; 1625 } 1626 } 1627 1628 try { 1629 theFile.delete(theObj); 1630 } 1631 catch (Exception ex) { 1632 shell.getDisplay().beep(); 1633 Tools.showError(shell, "Delete Objects", ex.getMessage()); 1634 continue; 1635 } 1636 1637 // When a TreeItem is disposed, it should be removed from its parent 1638 // items member list to prevent a bug when copying and deleting 1639 // groups/datasets 1640 ((Group) currentItem.getParentItem().getData()).removeFromMemberList(theObj); 1641 1642 if (currentItem.equals(selectedItem)) { 1643 selectedItem = null; 1644 selectedObject = null; 1645 selectedFile = null; 1646 } 1647 1648 currentItem.dispose(); 1649 } // (int i=0; i < currentSelections.length; i++) 1650 } 1651 1652 /** 1653 * Populates the TreeView with TreeItems corresponding to 1654 * the top-level user objects in the specified file. The rest 1655 * of the user objects in the file are populated as TreeItems 1656 * on demand when the user expands groups. 1657 * 1658 * @return the root TreeItem created in the Tree corresponding 1659 * to the file object. 1660 */ 1661 private TreeItem populateTree(FileFormat theFile) { 1662 if (theFile == null) { 1663 shell.getDisplay().beep(); 1664 Tools.showError(shell, "Open File", "Error opening file"); 1665 log.debug("Error populating tree, File object was null."); 1666 return null; 1667 } 1668 else if ((theFile.getFID() < 0) || (theFile.getRootObject() == null)) { 1669 if (theFile.isThisType(FileFormat.getFileFormat(FileFormat.FILE_TYPE_NC3))) { 1670 log.trace("populateTree(): FileID={} Null Root={}", theFile.getFID(), (theFile.getRootObject() == null)); 1671 } 1672 //TODO: Update FitsFile and NC2File to have a fid other than -1 1673 // so this check isn't needed 1674 if (theFile.isThisType(FileFormat.getFileFormat(FileFormat.FILE_TYPE_HDF4)) || 1675 //theFile.isThisType(FileFormat.getFileFormat(FileFormat.FILE_TYPE_NC3)) || 1676 theFile.isThisType(FileFormat.getFileFormat(FileFormat.FILE_TYPE_HDF5))) { 1677 shell.getDisplay().beep(); 1678 Tools.showError(shell, "Open File", "Error opening file " + theFile.getName()); 1679 log.debug("Error populating tree for {}, File ID was wrong or File root object was null.", theFile.getFilePath()); 1680 return null; 1681 } 1682 } 1683 1684 TreeItem rootItem = null; 1685 1686 try { 1687 rootItem = insertObject(theFile.getRootObject(), null); 1688 if (rootItem != null) { 1689 Iterator<HObject> it = ((Group) rootItem.getData()).getMemberList().iterator(); 1690 while (it.hasNext()) { 1691 TreeItem newItem = null; 1692 HObject obj = it.next(); 1693 1694 newItem = insertObject(obj, rootItem); 1695 1696 // Tell SWT how many members this group has so they can 1697 // be populated when the group is expanded 1698 if (obj instanceof Group) { 1699 newItem.setItemCount(((Group) obj).getMemberList().size()); 1700 log.debug("populateTree(): group members size {}:", ((Group) obj).getMemberList().size()); 1701 } 1702 } 1703 } 1704 } 1705 catch (Exception ex) { 1706 log.debug("populateTree(): Error populating Tree with members of file {}:", theFile.getFilePath(), ex); 1707 if (rootItem != null) 1708 rootItem.dispose(); 1709 shell.getDisplay().beep(); 1710 Tools.showError(shell, "Open File", "Error opening file " + theFile.getName() + "\n\n" + ex.getMessage()); 1711 return null; 1712 } 1713 1714 return rootItem; 1715 } 1716 1717 /** 1718 * Recursively expand/collapse a given selected TreeItem. 1719 * 1720 * @param item the selected tree item 1721 * @param expand 1722 * Expands the TreeItem and its children if true. 1723 * Collapse the TreeItem and its children if false. 1724 */ 1725 //TODO: some groups dont get expanded right, likely due to SetData not being 1726 // able to catch up with a large number of expanding 1727 private void recursiveExpand(TreeItem item, boolean expand) { 1728 if(item == null || !(item.getData() instanceof Group)) 1729 return; 1730 1731 TreeItem[] toExpand = item.getItems(); 1732 1733 item.setExpanded(expand); 1734 1735 // Make sure the TreeItem's icon gets set appropriately by 1736 // notifying its Expand or Collapse listener 1737 Event event = new Event(); 1738 event.item = item; 1739 tree.notifyListeners(expand ? SWT.Expand : SWT.Collapse, event); 1740 1741 // All SetData events for this group must be processed before any 1742 // child groups can be expanded, otherwise their data will be 1743 // null 1744 while(tree.getDisplay().readAndDispatch()); 1745 1746 for(int i = 0; i < toExpand.length; i++) 1747 recursiveExpand(toExpand[i], expand); 1748 } 1749 1750 /** 1751 * Gets the Image to set on the TreeItem for the specified HObject, 1752 * based on the type of HObject it is. 1753 * 1754 * @param obj 1755 * 1756 * @return the image for the specified HObject 1757 */ 1758 private Image getObjectTypeImage(HObject obj) { 1759 if (obj == null) 1760 return null; 1761 1762 // Should be safe to cast to a MetaDataContainer here because the 1763 // TreeView should never be able to select an object that does 1764 // not implement the MetaDataContainer interface 1765 boolean hasAttribute = ((MetaDataContainer) obj).hasAttribute(); 1766 1767 if(obj instanceof Dataset) { 1768 if (obj instanceof ScalarDS) { 1769 ScalarDS sd = (ScalarDS) obj; 1770 Datatype dt = sd.getDatatype(); 1771 1772 if (sd.isImage()) { 1773 if (hasAttribute) 1774 return imageIconA; 1775 else 1776 return imageIcon; 1777 } 1778 else if ((dt != null) && dt.isText()) { 1779 if (hasAttribute) 1780 return textIconA; 1781 else 1782 return textIcon; 1783 } 1784 else { 1785 if (hasAttribute) 1786 return datasetIconA; 1787 else 1788 return datasetIcon; 1789 } 1790 } 1791 else if (obj instanceof CompoundDS) { 1792 if (hasAttribute) 1793 return tableIconA; 1794 else 1795 return tableIcon; 1796 } 1797 } 1798 else if(obj instanceof Group) { 1799 if(((Group) obj).isRoot()) { 1800 FileFormat theFile = obj.getFileFormat(); 1801 1802 if(theFile.isThisType(FileFormat.getFileFormat(FileFormat.FILE_TYPE_NC3))) { 1803 if(theFile.isReadOnly()) 1804 return nc3IconR; 1805 else 1806 return nc3Icon; 1807 } 1808 else if(theFile.isThisType(FileFormat.getFileFormat(FileFormat.FILE_TYPE_HDF4))) { 1809 if(theFile.isReadOnly()) 1810 return h4IconR; 1811 else 1812 return h4Icon; 1813 } 1814 else { 1815 if(theFile.isReadOnly()) 1816 return h5IconR; 1817 else 1818 return h5Icon; 1819 } 1820 } 1821 else { 1822 if(hasAttribute) 1823 return folderCloseIconA; 1824 else 1825 return folderCloseIcon; 1826 } 1827 } 1828 else if(obj instanceof Datatype) { 1829 if(hasAttribute) 1830 return datatypeIconA; 1831 else 1832 return datatypeIcon; 1833 } 1834 1835 return questionIcon; 1836 } 1837 1838 /** 1839 * Checks if a file is already open. 1840 * 1841 * @param filename the file to query 1842 * 1843 * @return true if the file is open 1844 */ 1845 private boolean isFileOpen(String filename) { 1846 boolean isOpen = false; 1847 1848 // Find the file by matching its file name from the list of open files 1849 FileFormat theFile = null; 1850 Iterator<FileFormat> iterator = fileList.iterator(); 1851 while (iterator.hasNext()) { 1852 theFile = iterator.next(); 1853 if (theFile.getFilePath().equals(filename)) { 1854 isOpen = true; 1855 break; 1856 } 1857 } 1858 1859 return isOpen; 1860 } 1861 1862 /** 1863 * Checks if an object is already open. 1864 */ 1865 private boolean isObjectOpen(HObject obj) { 1866 boolean isOpen = false; 1867 1868 if (obj instanceof Group) { 1869 Group g = (Group) obj; 1870 List<?> members = g.getMemberList(); 1871 if ((members == null) || members.isEmpty()) { 1872 return false; 1873 } 1874 else { 1875 int n = members.size(); 1876 for (int i = 0; i < n; i++) { 1877 HObject theObj = (HObject) members.get(i); 1878 isOpen = (viewer.getDataView(theObj) != null); 1879 if (isOpen) 1880 break; 1881 } 1882 } 1883 } 1884 else { 1885 return (viewer.getDataView(obj) != null); 1886 } 1887 1888 return isOpen; 1889 } 1890 1891 /** 1892 * Returns a list that lists all TreeItems in the 1893 * current Tree that are children of the specified 1894 * TreeItem in a breadth-first manner. 1895 * 1896 * @param the current Tree item 1897 * 1898 * @return list of TreeItems 1899 */ 1900 private ArrayList<TreeItem> getItemsBreadthFirst(TreeItem item) { 1901 if (item == null) 1902 return null; 1903 1904 ArrayList<TreeItem> allItems = new ArrayList<>(); 1905 Queue<TreeItem> currentChildren = new LinkedList<>(); 1906 TreeItem currentItem = item; 1907 1908 // Add all root items in the Tree to a Queue 1909 currentChildren.addAll(Arrays.asList(currentItem.getItems())); 1910 1911 // For every item in the queue, remove it from the head of the queue, 1912 // add it to the list of all items, then add all of its possible children 1913 // TreeItems to the end of the queue. This produces a breadth-first 1914 // ordering of the Tree's TreeItems. 1915 while(!currentChildren.isEmpty()) { 1916 currentItem = currentChildren.remove(); 1917 allItems.add(currentItem); 1918 1919 if(currentItem.getItemCount() <= 0) 1920 continue; 1921 1922 currentChildren.addAll(Arrays.asList(currentItem.getItems())); 1923 } 1924 1925 return allItems; 1926 } 1927 1928 /** 1929 * Returns a list of all user objects that traverses the subtree rooted at 1930 * this item in breadth-first order.. 1931 * 1932 * @param item 1933 * the item to start with. 1934 */ 1935 private final List<Object> breadthFirstUserObjects(TreeItem item) { 1936 if (item == null) return null; 1937 1938 ArrayList<Object> list = new ArrayList<>(); 1939 list.add(item.getData()); // Add this item to the list first 1940 1941 Iterator<TreeItem> it = getItemsBreadthFirst(item).iterator(); 1942 TreeItem theItem = null; 1943 1944 while (it.hasNext()) { 1945 theItem = it.next(); 1946 list.add(theItem.getData()); 1947 } 1948 1949 return list; 1950 } 1951 1952 /** 1953 * Find first object that is matched by name under the specified 1954 * TreeItem. 1955 * 1956 * @param objName 1957 * -- the object name. 1958 * @return the object if found, otherwise, returns null. 1959 */ 1960 private final HObject find(String objName, TreeItem parentItem) { 1961 if (objName == null || objName.length() <= 0 || parentItem == null) return null; 1962 1963 HObject retObj = null; 1964 boolean isFound = false; 1965 boolean isPrefix = false; 1966 boolean isSuffix = false; 1967 boolean isContain = false; 1968 1969 if (objName.equals("*")) 1970 return null; 1971 1972 if (objName.startsWith("*")) { 1973 isSuffix = true; 1974 objName = objName.substring(1, objName.length()); 1975 } 1976 1977 if (objName.endsWith("*")) { 1978 isPrefix = true; 1979 objName = objName.substring(0, objName.length() - 1); 1980 } 1981 1982 if (isPrefix && isSuffix) { 1983 isContain = true; 1984 isPrefix = isSuffix = false; 1985 } 1986 1987 if (objName == null || objName.length() <= 0) 1988 return null; 1989 1990 HObject obj = null; 1991 String theName = null; 1992 TreeItem theItem = null; 1993 Iterator<TreeItem> it = getItemsBreadthFirst(parentItem).iterator(); 1994 while (it.hasNext()) { 1995 theItem = it.next(); 1996 obj = (HObject) theItem.getData(); 1997 if (obj != null && (theName = obj.getName()) != null) { 1998 if (isPrefix) 1999 isFound = theName.startsWith(objName); 2000 else if (isSuffix) 2001 isFound = theName.endsWith(objName); 2002 else if (isContain) 2003 isFound = theName.contains(objName); 2004 else 2005 isFound = theName.equals(objName); 2006 2007 if (isFound) { 2008 retObj = obj; 2009 break; 2010 } 2011 } 2012 } 2013 2014 if (retObj != null) { 2015 tree.deselectAll(); 2016 tree.setSelection(theItem); 2017 tree.showItem(theItem); 2018 } 2019 2020 return retObj; 2021 } 2022 2023 /** 2024 * Save the current file into a new HDF4 file. Since HDF4 does not 2025 * support packing, the source file is copied into the new file with 2026 * the exact same content. 2027 */ 2028 private final void saveAsHDF4(FileFormat srcFile) { 2029 if (srcFile == null) { 2030 shell.getDisplay().beep(); 2031 Tools.showError(shell, "Save", "Select a file to save."); 2032 return; 2033 } 2034 2035 HObject root = srcFile.getRootObject(); 2036 if (root == null) { 2037 shell.getDisplay().beep(); 2038 Tools.showError(shell, "Save", "The file is empty."); 2039 return; 2040 } 2041 2042 String currentDir = srcFile.getParent(); 2043 2044 if (currentDir != null) 2045 currentDir += File.separator; 2046 else 2047 currentDir = ""; 2048 2049 String filename = null; 2050 if (((HDFView) viewer).getTestState()) { 2051 filename = currentDir + File.separator + new InputDialog(shell, "Enter a file name", "").open(); 2052 } 2053 else { 2054 FileDialog fChooser = new FileDialog(shell, SWT.SAVE); 2055 fChooser.setFileName(Tools.checkNewFile(currentDir, ".hdf").getName()); 2056 2057 DefaultFileFilter filter = DefaultFileFilter.getFileFilterHDF4(); 2058 fChooser.setFilterExtensions(new String[] {filter.getExtensions()}); 2059 fChooser.setFilterNames(new String[] {filter.getDescription()}); 2060 fChooser.setFilterIndex(0); 2061 2062 filename = fChooser.open(); 2063 } 2064 if(filename == null) 2065 return; 2066 2067 try { 2068 Tools.createNewFile(filename, currentDir, FileFormat.FILE_TYPE_HDF4, fileList); 2069 } 2070 catch (Exception ex) { 2071 Tools.showError(shell, "Save", ex.getMessage()); 2072 } 2073 2074 // Since cannot pack hdf4, simply copy the whole physical file 2075 int length = 0; 2076 int bsize = 512; 2077 byte[] buffer; 2078 2079 try (BufferedInputStream bi = new BufferedInputStream(new FileInputStream(srcFile.getFilePath()))) { 2080 try (BufferedOutputStream bo = new BufferedOutputStream(new FileOutputStream(filename))) { 2081 buffer = new byte[bsize]; 2082 try { 2083 length = bi.read(buffer, 0, bsize); 2084 } 2085 catch (Exception ex) { 2086 length = 0; 2087 } 2088 while (length > 0) { 2089 try { 2090 bo.write(buffer, 0, length); 2091 length = bi.read(buffer, 0, bsize); 2092 } 2093 catch (Exception ex) { 2094 length = 0; 2095 } 2096 } 2097 2098 try { 2099 bo.flush(); 2100 } 2101 catch (Exception ex) { 2102 log.debug("Output file:", ex); 2103 } 2104 } 2105 catch (Exception ex) { 2106 shell.getDisplay().beep(); 2107 Tools.showError(shell, "Save", ex.getMessage()); 2108 return; 2109 } 2110 } 2111 catch (Exception ex) { 2112 shell.getDisplay().beep(); 2113 Tools.showError(shell, "Save", ex.getMessage() + "\n" + filename); 2114 return; 2115 } 2116 2117 try { 2118 openFile(filename, FileFormat.WRITE); 2119 } 2120 catch (Exception ex) { 2121 shell.getDisplay().beep(); 2122 Tools.showError(shell, "Save", ex.getMessage() + "\n" + filename); 2123 } 2124 } 2125 2126 /** 2127 * Copy the current file into a new HDF5 file. The new file does not include the 2128 * inaccessible objects. Values of reference dataset are not updated in the 2129 * new file. 2130 */ 2131 private void saveAsHDF5(FileFormat srcFile) { 2132 if (srcFile == null) { 2133 shell.getDisplay().beep(); 2134 Tools.showError(shell, "Save", "Select a file to save."); 2135 return; 2136 } 2137 2138 HObject root = srcFile.getRootObject(); 2139 if (root == null) { 2140 shell.getDisplay().beep(); 2141 Tools.showError(shell, "Save", "The file is empty."); 2142 return; 2143 } 2144 2145 String currentDir = srcFile.getParent(); 2146 2147 if (currentDir != null) 2148 currentDir += File.separator; 2149 else 2150 currentDir = ""; 2151 2152 String filename = null; 2153 if (((HDFView) viewer).getTestState()) { 2154 filename = currentDir + File.separator + new InputDialog(shell, "Enter a file name", "").open(); 2155 } 2156 else { 2157 FileDialog fChooser = new FileDialog(shell, SWT.SAVE); 2158 fChooser.setFileName(Tools.checkNewFile(currentDir, ".h5").getName()); 2159 2160 DefaultFileFilter filter = DefaultFileFilter.getFileFilterHDF5(); 2161 fChooser.setFilterExtensions(new String[] {filter.getExtensions()}); 2162 fChooser.setFilterNames(new String[] {filter.getDescription()}); 2163 fChooser.setFilterIndex(0); 2164 2165 filename = fChooser.open(); 2166 } 2167 if(filename == null) 2168 return; 2169 2170 try { 2171 Tools.createNewFile(filename, currentDir, FileFormat.FILE_TYPE_HDF5, fileList); 2172 } 2173 catch (Exception ex) { 2174 Tools.showError(shell, "Save", ex.getMessage()); 2175 } 2176 2177 TreeItem rootItem = findTreeItem(root); 2178 int n = rootItem.getItemCount(); 2179 ArrayList<TreeItem> objList = new ArrayList<>(n); 2180 2181 try { 2182 for (int i = 0; i < n; i++) objList.add(rootItem.getItem(i)); 2183 } 2184 catch (Exception ex) { 2185 log.debug("saveAsHDF5() objList add failure: ", ex); 2186 } 2187 2188 FileFormat newFile = null; 2189 try { 2190 newFile = openFile(filename, FileFormat.WRITE); 2191 } 2192 catch (Exception ex) { 2193 shell.getDisplay().beep(); 2194 Tools.showError(shell, "Save", ex.getMessage() + "\n" + filename); 2195 return; 2196 } 2197 2198 if (newFile == null) 2199 return; 2200 2201 HObject pitem = newFile.getRootObject(); 2202 2203 pasteObject(objList.toArray(new TreeItem[0]), findTreeItem(pitem), newFile); 2204 objList.clear(); 2205 2206 Group srcGroup = (Group) root; 2207 Group dstGroup = (Group) newFile.getRootObject(); 2208 Object[] parameter = new Object[2]; 2209 Class<?> classHOjbect = null; 2210 Class<?>[] parameterClass = new Class[2]; 2211 Method method = null; 2212 2213 // Copy attributes of the root group 2214 try { 2215 parameter[0] = srcGroup; 2216 parameter[1] = dstGroup; 2217 classHOjbect = Class.forName("hdf.object.HObject"); 2218 parameterClass[0] = parameterClass[1] = classHOjbect; 2219 method = newFile.getClass().getMethod("copyAttributes", parameterClass); 2220 method.invoke(newFile, parameter); 2221 } 2222 catch (Exception ex) { 2223 shell.getDisplay().beep(); 2224 Tools.showError(shell, "Save", ex.getMessage()); 2225 } 2226 2227 // Update reference datasets 2228 parameter[0] = srcGroup.getFileFormat(); 2229 parameter[1] = newFile; 2230 parameterClass[0] = parameterClass[1] = parameter[0].getClass(); 2231 try { 2232 method = newFile.getClass().getMethod("updateReferenceDataset", parameterClass); 2233 method.invoke(newFile, parameter); 2234 } 2235 catch (Exception ex) { 2236 shell.getDisplay().beep(); 2237 Tools.showError(shell, "Save", ex.getMessage()); 2238 } 2239 } 2240 2241 /** Save data as file. 2242 * 2243 * @throws Exception if a failure occurred 2244 */ 2245 private void saveDataAsFile() throws Exception { 2246 if (!(selectedObject instanceof Dataset) || (selectedObject == null) || (selectedItem == null)) 2247 return; 2248 2249 File chosenFile = null; 2250 String filename = null; 2251 Dataset dataset = (Dataset) selectedObject; 2252 String currentDir = dataset.getFile().substring(0, dataset.getFile().lastIndexOf(File.separator)); 2253 String msgtext = null; 2254 if(binaryOrder == 99) 2255 msgtext = "Save Dataset Data To Text File --- " + dataset.getName(); 2256 else 2257 msgtext = "Save Current Data To Binary File --- " + dataset.getName(); 2258 if (((HDFView) viewer).getTestState()) { 2259 filename = currentDir + File.separator + new InputDialog(shell, msgtext, "").open(); 2260 } 2261 else { 2262 FileDialog fChooser = new FileDialog(shell, SWT.SAVE); 2263 fChooser.setFilterPath(currentDir); 2264 2265 DefaultFileFilter filter = null; 2266 2267 if (binaryOrder == 99) { 2268 fChooser.setText(msgtext); 2269 fChooser.setFileName(dataset.getName() + ".txt"); 2270 filter = DefaultFileFilter.getFileFilterText(); 2271 } 2272 else { 2273 fChooser.setText(msgtext); 2274 fChooser.setFileName(dataset.getName() + ".bin"); 2275 filter = DefaultFileFilter.getFileFilterBinary(); 2276 } 2277 2278 fChooser.setFilterExtensions(new String[] {"*", filter.getExtensions()}); 2279 fChooser.setFilterNames(new String[] {"All Files", filter.getDescription()}); 2280 fChooser.setFilterIndex(1); 2281 2282 filename = fChooser.open(); 2283 } 2284 if(filename == null) 2285 return; 2286 2287 // Check if the file is in use 2288 List<?> saveFileList = viewer.getTreeView().getCurrentFiles(); 2289 if (saveFileList != null) { 2290 FileFormat theFile = null; 2291 Iterator<?> iterator = saveFileList.iterator(); 2292 while (iterator.hasNext()) { 2293 theFile = (FileFormat) iterator.next(); 2294 if (theFile.getFilePath().equals(filename)) { 2295 shell.getDisplay().beep(); 2296 Tools.showError(shell, "Export Dataset", "Unable to save data to file \"" + filename + "\". \nThe file is being used."); 2297 return; 2298 } 2299 } 2300 } 2301 2302 chosenFile = new File(filename); 2303 2304 if (chosenFile.exists()) { 2305 if(!Tools.showConfirm(shell, "Export Dataset", "File exists. Do you want to replace it?")) 2306 return; 2307 } 2308 2309 boolean isH4 = selectedObject.getFileFormat().isThisType(FileFormat.getFileFormat(FileFormat.FILE_TYPE_HDF4)); 2310 if (isH4) { 2311 shell.getDisplay().beep(); 2312 Tools.showError(shell, "Save", "Cannot export HDF4 object."); 2313 return; 2314 } 2315 2316 boolean isN3 = selectedObject.getFileFormat().isThisType(FileFormat.getFileFormat(FileFormat.FILE_TYPE_NC3)); 2317 if (isN3) { 2318 shell.getDisplay().beep(); 2319 Tools.showError(shell, "Save", "Cannot export netCDF3 object."); 2320 return; 2321 } 2322 2323 try { 2324 selectedObject.getFileFormat().exportDataset(filename, dataset, binaryOrder); 2325 viewer.showStatus("Data saved to: " + filename); 2326 } 2327 catch (Exception ex) { 2328 shell.getDisplay().beep(); 2329 Tools.showError(shell, "Save", "Unable to export dataset: " + ex.getMessage()); 2330 } 2331 } 2332 2333 /** enable/disable GUI components */ 2334 private static void setEnabled(List<MenuItem> list, boolean b) { 2335 if (list == null) 2336 return; 2337 2338 Iterator<MenuItem> it = list.iterator(); 2339 while (it.hasNext()) 2340 it.next().setEnabled(b); 2341 } 2342 2343 /** 2344 * Opens a file and retrieves the file structure of the file. It also can be 2345 * used to create a new file by setting the accessID to FileFormat.CREATE. 2346 * 2347 * Subclasses must implement this function to take appropriate steps to open 2348 * a file. 2349 * 2350 * @param filename 2351 * the name of the file to open. 2352 * @param accessID 2353 * identifier for the file access. Valid value of accessID is: 2354 * <ul> 2355 * <li>FileFormat.READ --- allow read-only access to file.</li> 2356 * <li>FileFormat.WRITE --- allow read and write access to file.</li> 2357 * <li>FileFormat.CREATE --- create a new file.</li> 2358 * </ul> 2359 * 2360 * @return the FileFormat of this file if successful; otherwise returns null. 2361 * 2362 * @throws Exception if a failure occurred 2363 */ 2364 @Override 2365 public FileFormat openFile(String filename, int accessID) throws Exception { 2366 log.trace("openFile: {},{}", filename, accessID); 2367 FileFormat fileFormat = null; 2368 boolean isSWMRFile = (FileFormat.MULTIREAD == (accessID & FileFormat.MULTIREAD)); 2369 log.trace("openFile: isSWMRFile={}", isSWMRFile); 2370 boolean isNewFile = (FileFormat.OPEN_NEW == (accessID & FileFormat.OPEN_NEW)); 2371 if (isNewFile) 2372 accessID = accessID - FileFormat.OPEN_NEW; //strip OPEN_NEW 2373 2374 if (isFileOpen(filename)) { 2375 viewer.showStatus("File is in use."); 2376 return null; 2377 } 2378 2379 File tmpFile = new File(filename); 2380 if (!tmpFile.exists()) 2381 throw new FileNotFoundException("File does not exist."); 2382 2383 if (!tmpFile.canWrite() && !isSWMRFile) 2384 accessID = FileFormat.READ; 2385 2386 Enumeration<?> keys = FileFormat.getFileFormatKeys(); 2387 2388 String theKey = null; 2389 while (keys.hasMoreElements()) { 2390 theKey = (String) keys.nextElement(); 2391 if (theKey.equals(FileFormat.FILE_TYPE_HDF4)) { 2392 log.trace("openFile: {} FILE_TYPE_HDF4", filename); 2393 try { 2394 FileFormat h4format = FileFormat.getFileFormat(FileFormat.FILE_TYPE_HDF4); 2395 if ((h4format != null) && h4format.isThisType(filename)) { 2396 fileFormat = h4format.createInstance(filename, accessID); 2397 break; 2398 } 2399 } 2400 catch (UnsatisfiedLinkError e) { 2401 log.debug("openFile({}): HDF4 library link error:", filename, e); 2402 viewer.showError("Unable to open file '" + filename + "': HDF4 library linking error"); 2403 } 2404 catch (Exception err) { 2405 log.debug("openFile: Error retrieving the file structure of {}:", filename, err); 2406 } 2407 continue; 2408 } 2409 else if (theKey.equals(FileFormat.FILE_TYPE_HDF5)) { 2410 log.trace("openFile: {} FILE_TYPE_HDF5", filename); 2411 try { 2412 FileFormat h5format = FileFormat.getFileFormat(FileFormat.FILE_TYPE_HDF5); 2413 if ((h5format != null) && h5format.isThisType(filename)) { 2414 fileFormat = h5format.createInstance(filename, accessID); 2415 break; 2416 } 2417 } 2418 catch (UnsatisfiedLinkError e) { 2419 log.debug("openFile({}): HDF5 library link error:", filename, e); 2420 viewer.showError("Unable to open file '" + filename + "': HDF5 library linking error"); 2421 } 2422 catch (Exception err) { 2423 log.debug("openFile: Error retrieving the file structure of {}:", filename, err); 2424 } 2425 continue; 2426 } 2427 else if (theKey.equals(FileFormat.FILE_TYPE_NC3)) { 2428 log.trace("openFile: {} FILE_TYPE_NC3", filename); 2429 try { 2430 FileFormat nc3format = FileFormat.getFileFormat(FileFormat.FILE_TYPE_NC3); 2431 if ((nc3format != null) && nc3format.isThisType(filename)) { 2432 fileFormat = nc3format.createInstance(filename, accessID); 2433 break; 2434 } 2435 } 2436 catch (UnsatisfiedLinkError e) { 2437 log.debug("openFile({}): NetCDF3 library link error:", filename, e); 2438 viewer.showError("Unable to open file '" + filename + "': NetCDF3 library linking error"); 2439 } 2440 catch (Exception err) { 2441 log.debug("openFile: Error retrieving the file structure of {}:", filename, err); 2442 } 2443 continue; 2444 } 2445 else { 2446 log.trace("openFile: {} Other", filename); 2447 try { 2448 FileFormat theformat = FileFormat.getFileFormat(theKey); 2449 if (theformat.isThisType(filename)) { 2450 fileFormat = theformat.createInstance(filename, accessID); 2451 break; 2452 } 2453 } 2454 catch (Exception err) { 2455 log.debug("openFile: Error retrieving the file structure of {}:", filename, err); 2456 } 2457 } 2458 } 2459 2460 if (fileFormat == null) 2461 throw new java.io.IOException("Unsupported fileformat - " + filename); 2462 2463 if (fileFormat.isThisType(FileFormat.getFileFormat(FileFormat.FILE_TYPE_HDF5))) { 2464 if (tempIdxType >= 0) { 2465 fileFormat.setIndexType(tempIdxType); 2466 2467 // Reset the temporary index type 2468 tempIdxType = -1; 2469 } 2470 else 2471 fileFormat.setIndexType(fileFormat.getIndexType(ViewProperties.getIndexType())); 2472 2473 if (tempIdxOrder >= 0) { 2474 fileFormat.setIndexOrder(tempIdxOrder); 2475 2476 // Reset the temporary index order 2477 tempIdxOrder = -1; 2478 } 2479 else 2480 fileFormat.setIndexOrder(fileFormat.getIndexOrder(ViewProperties.getIndexOrder())); 2481 } 2482 2483 return initFile(fileFormat); 2484 } 2485 2486 /** 2487 * Initializes a FileFormat object by opening it and populating the file tree structure. 2488 * 2489 * @param fileFormat 2490 * the file to open with an existing FileFormat instance. 2491 * 2492 * @return the initialized FileFormat of this file if successful; otherwise returns null. 2493 * 2494 * @throws Exception 2495 * if a failure occurred 2496 */ 2497 private FileFormat initFile(FileFormat fileFormat) throws Exception { 2498 log.trace("initFile[{}] - start", fileFormat.getAbsolutePath()); 2499 2500 TreeItem fileRoot = null; 2501 2502 shell.setCursor(Display.getCurrent().getSystemCursor(SWT.CURSOR_WAIT)); 2503 2504 try { 2505 fileFormat.setMaxMembers(ViewProperties.getMaxMembers()); 2506 fileFormat.setStartMembers(ViewProperties.getStartMembers()); 2507 2508 fileFormat.open(); 2509 2510 fileRoot = populateTree(fileFormat); 2511 2512 if (fileRoot != null) { 2513 /* Expand top level items of root object */ 2514 int currentRowCount = tree.getItemCount(); 2515 if (currentRowCount > 0) 2516 tree.getItem(currentRowCount - 1).setExpanded(true); 2517 2518 fileList.add(fileFormat); 2519 } 2520 2521 tree.setItemCount(fileList.size()); 2522 2523 log.trace("initFile[{}] - fileList items={}", fileFormat.getAbsolutePath(), fileList.size()); 2524 } 2525 catch (Exception ex) { 2526 log.debug("initFile: FileFormat init error:", ex); 2527 fileFormat = null; 2528 } 2529 finally { 2530 shell.setCursor(null); 2531 } 2532 2533 return fileFormat; 2534 } 2535 2536 @Override 2537 public FileFormat reopenFile(FileFormat fileFormat, int newFileAccessMode) throws Exception { 2538 String fileFormatName = fileFormat.getAbsolutePath(); 2539 2540 // Make sure to reload the file using the file's current indexing options 2541 tempIdxType = fileFormat.getIndexType(null); 2542 tempIdxOrder = fileFormat.getIndexOrder(null); 2543 2544 closeFile(fileFormat); 2545 ((HDFView) viewer).showMetaData(null); 2546 2547 if (newFileAccessMode < 0) { 2548 if (ViewProperties.isReadOnly()) 2549 return openFile(fileFormatName, FileFormat.READ); 2550 else if (ViewProperties.isReadSWMR()) 2551 return openFile(fileFormatName, FileFormat.READ | FileFormat.MULTIREAD); 2552 else 2553 return openFile(fileFormatName, FileFormat.WRITE); 2554 } 2555 else 2556 return openFile(fileFormatName, newFileAccessMode); 2557 } 2558 2559 /** 2560 * Close a file 2561 * 2562 * @param file 2563 * the file to close 2564 * 2565 * @throws Exception if a failure occurred 2566 */ 2567 @Override 2568 public void closeFile(FileFormat file) throws Exception { 2569 if (file == null) return; 2570 2571 // Find the file item in the tree and remove it 2572 FileFormat theFile = null; 2573 TreeItem[] openFiles = tree.getItems(); // Returns the top-level items of the tree 2574 2575 for (int i = 0; i < openFiles.length; i++) { 2576 theFile = ((Group) openFiles[i].getData()).getFileFormat(); 2577 2578 if (theFile.equals(file)) { 2579 // Remove TreeItem from the view 2580 openFiles[i].dispose(); 2581 log.trace("dispose({}):", theFile.getFilePath()); 2582 2583 try { 2584 theFile.close(); 2585 } 2586 catch (Exception ex) { 2587 log.debug("closeFile({}):", theFile.getFilePath(), ex); 2588 } 2589 2590 fileList.remove(theFile); 2591 if (theFile.equals(selectedFile)) { 2592 selectedFile = null; 2593 selectedObject = null; 2594 selectedItem = null; 2595 } 2596 2597 break; 2598 } 2599 } 2600 } 2601 2602 /** 2603 * Save a file 2604 * 2605 * @param file 2606 * the file to save 2607 * 2608 * @throws Exception if a failure occurred 2609 */ 2610 @Override 2611 public void saveFile(FileFormat file) throws Exception { 2612 if (file == null) { 2613 shell.getDisplay().beep(); 2614 Tools.showError(shell, "Save", "Select a file to save."); 2615 return; 2616 } 2617 2618 boolean isH4 = file.isThisType(FileFormat.getFileFormat(FileFormat.FILE_TYPE_HDF4)); 2619 boolean isH5 = file.isThisType(FileFormat.getFileFormat(FileFormat.FILE_TYPE_HDF5)); 2620 2621 if (!(isH4 || isH5)) { 2622 shell.getDisplay().beep(); 2623 Tools.showError(shell, "Save", "Saving file is not supported for this file type"); 2624 return; 2625 } 2626 2627 // Write the change of the data into the file before saving the file 2628 ((HDFView) viewer).writeDataToFile(file); 2629 2630 if (isH5) 2631 saveAsHDF5(file); 2632 else if (isH4) 2633 saveAsHDF4(file); 2634 } 2635 2636 /** 2637 * Returns the tree item that contains the given data object. 2638 */ 2639 @Override 2640 public TreeItem findTreeItem(HObject obj) { 2641 if (obj == null) 2642 return null; 2643 2644 if (obj.getFileFormat().getRootObject() == null) 2645 return null; 2646 2647 // Locate the item's file root to save on search time in large files 2648 TreeItem[] fileRoots = tree.getItems(); 2649 TreeItem rootItem = null; 2650 HObject rootObject = null; 2651 for (int i = 0; i < fileRoots.length; i++) { 2652 rootItem = fileRoots[i]; 2653 rootObject = (HObject) rootItem.getData(); 2654 2655 if (rootObject == null) 2656 continue; 2657 2658 if (rootObject.getFileFormat().equals(obj.getFileFormat())) { 2659 // If the object being looked for is a file root, return 2660 // this found TreeItem 2661 if (obj instanceof Group && ((Group) obj).isRoot()) 2662 return rootItem; 2663 2664 // Else the file root for the object being looked for has 2665 // been found, continue the search through only this TreeItem's 2666 // members 2667 break; 2668 } 2669 } 2670 2671 TreeItem theItem = null; 2672 HObject theObj = null; 2673 List<TreeItem> breadthFirstItems = getItemsBreadthFirst(rootItem); 2674 2675 if (breadthFirstItems != null) { 2676 Iterator<TreeItem> it = getItemsBreadthFirst(rootItem).iterator(); 2677 2678 while (it.hasNext()) { 2679 theItem = it.next(); 2680 theObj = (HObject) theItem.getData(); 2681 2682 if (theObj == null) 2683 continue; 2684 2685 if (theObj.equals(obj)) 2686 return theItem; 2687 } 2688 } 2689 2690 return null; 2691 } 2692 2693 /** 2694 * change the display option. 2695 */ 2696 @Override 2697 public void setDefaultDisplayMode(boolean displaymode) { 2698 isDefaultDisplay = displaymode; 2699 } 2700 2701 /** 2702 * Gets the selected file. When multiple files are open, we need to know 2703 * which file is currently selected. 2704 * 2705 * @return the FileFormat of the currently selected file. 2706 */ 2707 @Override 2708 public FileFormat getSelectedFile() { 2709 return selectedFile; 2710 } 2711 2712 /** 2713 * @return the currently selected object in the tree. 2714 */ 2715 @Override 2716 public HObject getCurrentObject() { 2717 return selectedObject; 2718 } 2719 2720 /** 2721 * @return the Tree which holds the file structure. 2722 */ 2723 @Override 2724 public Tree getTree() { 2725 return tree; 2726 } 2727 2728 /** 2729 * @return the list of currently open files. 2730 */ 2731 @Override 2732 public List<FileFormat> getCurrentFiles() { 2733 return fileList; 2734 } 2735 2736 /** 2737 * Display the content of a data object. 2738 * 2739 * @param dataObject 2740 * the data object 2741 * 2742 * @return the DataView that displays the data content 2743 * 2744 * @throws Exception if a failure occurred 2745 */ 2746 @Override 2747 public DataView showDataContent(HObject dataObject) throws Exception { 2748 /* Can only display objects with data */ 2749 if ((dataObject == null) || !(dataObject instanceof DataFormat)) 2750 return null; 2751 2752 log.trace("showDataContent({}): start", dataObject.getName()); 2753 2754 /* Set up the default display properties passed to the DataView instance */ 2755 DataView theView = null; 2756 DataFormat d = (DataFormat) dataObject; 2757 HashMap<DATA_VIEW_KEY, Serializable> map = new HashMap<>(8); 2758 2759 if (!d.isInited()) 2760 d.init(); 2761 2762 boolean isImage = ((d instanceof ScalarDS) && ((ScalarDS) d).isImage()); 2763 boolean isDisplayTypeChar = false; 2764 boolean isTransposed = false; 2765 boolean isIndexBase1 = ViewProperties.isIndexBase1(); 2766 BitSet bitmask = null; 2767 String dataViewName = null; 2768 2769 if (isDefaultDisplay) { /* Displaying a data object using the default display options */ 2770 DataView existingView = viewer.getDataView((HObject) d); 2771 2772 /* 2773 * Check to make sure this data object isn't already opened in an existing 2774 * DataView. If it is, just bring that DataView to focus. 2775 */ 2776 if (existingView != null) { 2777 Shell[] shells = Display.getDefault().getShells(); 2778 2779 if (shells.length >= 1) { 2780 for (int i = 0; i < shells.length; i++) { 2781 DataView view = (DataView) shells[i].getData(); 2782 2783 if (view != null) { 2784 if (view.equals(existingView)) { 2785 shells[i].forceActive(); 2786 2787 log.trace("showDataContent(): found existing DataView for data object {}", dataObject.getName()); 2788 2789 return view; 2790 } 2791 } 2792 } 2793 } 2794 } 2795 } 2796 else { /* Open Dialog to allow user to choose different data display options */ 2797 DataOptionDialog dialog = new DataOptionDialog(shell, d); 2798 dialog.open(); 2799 2800 if (dialog.isCancelled()) 2801 return null; 2802 2803 isImage = dialog.isImageDisplay(); 2804 isDisplayTypeChar = dialog.isDisplayTypeChar(); 2805 dataViewName = dialog.getDataViewName(); 2806 isTransposed = dialog.isTransposed(); 2807 bitmask = dialog.getBitmask(); 2808 isIndexBase1 = dialog.isIndexBase1(); 2809 isApplyBitmaskOnly = dialog.isApplyBitmaskOnly(); 2810 } 2811 2812 map.put(ViewProperties.DATA_VIEW_KEY.OBJECT, dataObject); 2813 map.put(ViewProperties.DATA_VIEW_KEY.VIEW_NAME, dataViewName); 2814 map.put(ViewProperties.DATA_VIEW_KEY.CHAR, isDisplayTypeChar); 2815 map.put(ViewProperties.DATA_VIEW_KEY.TRANSPOSED, isTransposed); 2816 map.put(ViewProperties.DATA_VIEW_KEY.INDEXBASE1, isIndexBase1); 2817 map.put(ViewProperties.DATA_VIEW_KEY.BITMASK, bitmask); 2818 if (isApplyBitmaskOnly) 2819 map.put(ViewProperties.DATA_VIEW_KEY.BITMASKOP, ViewProperties.BITMASK_OP.AND); 2820 2821 log.trace( 2822 "showDataContent(): object={} dataViewName={} isDisplayTypeChar={} isTransposed={} isIndexBase1={} bitmask={}", 2823 dataObject, dataViewName, isDisplayTypeChar, isTransposed, isIndexBase1, bitmask); 2824 2825 shell.setCursor(Display.getCurrent().getSystemCursor(SWT.CURSOR_WAIT)); 2826 2827 if (isImage) { 2828 DataViewFactory imageViewFactory = null; 2829 try { 2830 imageViewFactory = DataViewFactoryProducer.getFactory(DataViewType.IMAGE); 2831 } 2832 catch (Exception ex) { 2833 log.debug("showDataContent(): error occurred while instantiating ImageView factory class", ex); 2834 viewer.showError("Error occurred while instantiating ImageView factory class"); 2835 return null; 2836 } 2837 2838 if (imageViewFactory == null) { 2839 log.debug("showDataContent(): ImageView factory is null"); 2840 return null; 2841 } 2842 2843 try { 2844 theView = imageViewFactory.getImageView(viewer, map); 2845 2846 if (theView == null) { 2847 log.debug("showDataContent(): error occurred while instantiating ImageView class"); 2848 viewer.showError("Error occurred while instantiating ImageView class"); 2849 Tools.showError(shell, "Show Data", "Error occurred while instantiating ImageView class"); 2850 } 2851 } 2852 catch (ClassNotFoundException ex) { 2853 log.debug("showDataContent(): no suitable ImageView class found"); 2854 viewer.showError("Unable to find suitable ImageView class for object '" + dataObject.getName() + "'"); 2855 Tools.showError(shell, "Show Data", "Unable to find suitable ImageView class for object '" + dataObject.getName() + "'"); 2856 theView = null; 2857 } 2858 } 2859 else { 2860 DataViewFactory tableViewFactory = null; 2861 try { 2862 tableViewFactory = DataViewFactoryProducer.getFactory(DataViewType.TABLE); 2863 } 2864 catch (Exception ex) { 2865 log.debug("showDataContent(): error occurred while instantiating TableView factory class", ex); 2866 viewer.showError("Error occurred while instantiating TableView factory class"); 2867 return null; 2868 } 2869 2870 if (tableViewFactory == null) { 2871 log.debug("showDataContent(): TableView factory is null"); 2872 return null; 2873 } 2874 2875 try { 2876 theView = tableViewFactory.getTableView(viewer, map); 2877 2878 if (theView == null) { 2879 log.debug("showDataContent(): error occurred while instantiating TableView class"); 2880 viewer.showError("Error occurred while instantiating TableView class"); 2881 Tools.showError(shell, "Show Data", "Error occurred while instantiating TableView class"); 2882 } 2883 } 2884 catch (ClassNotFoundException ex) { 2885 log.debug("showDataContent(): no suitable TableView class found"); 2886 viewer.showError("Unable to find suitable TableView class for object '" + dataObject.getName() + "'"); 2887 Tools.showError(shell, "Show Data", "Unable to find suitable TableView class for object '" + dataObject.getName() + "'"); 2888 theView = null; 2889 } 2890 } 2891 2892 if (!shell.isDisposed()) 2893 shell.setCursor(null); 2894 2895 return theView; 2896 } 2897 2898 /** 2899 * Updates the current font. 2900 * 2901 * @param font 2902 * the new font 2903 */ 2904 public void updateFont(Font font) { 2905 if (curFont != null) 2906 curFont.dispose(); 2907 2908 log.trace("updateFont():"); 2909 curFont = font; 2910 2911 tree.setFont(font); 2912 tree.pack(); 2913 tree.requestLayout(); 2914 } 2915 2916 /** 2917 * Updates the icon for the TreeItem representing the given HObject. Used 2918 * to change the icon after a status update, such as adding an attribute to 2919 * an object. 2920 * 2921 * @param obj 2922 * the object to update the icon for 2923 */ 2924 public void updateItemIcon(HObject obj) { 2925 if (obj == null) { 2926 log.debug("updateItemIcon(): object is null"); 2927 return; 2928 } 2929 2930 TreeItem theItem = findTreeItem(obj); 2931 2932 if (theItem == null) { 2933 log.debug("updateItemIcon(): could not find TreeItem for HObject"); 2934 } 2935 else { 2936 if (obj instanceof Group && !(((Group) obj).isRoot())) { 2937 if (theItem.getExpanded()) { 2938 if (((MetaDataContainer) obj).hasAttribute()) 2939 theItem.setImage(folderOpenIconA); 2940 else 2941 theItem.setImage(folderOpenIcon); 2942 } 2943 else { 2944 if (((MetaDataContainer) obj).hasAttribute()) 2945 theItem.setImage(folderCloseIconA); 2946 else 2947 theItem.setImage(folderCloseIcon); 2948 } 2949 } 2950 else { 2951 theItem.setImage(getObjectTypeImage(obj)); 2952 } 2953 } 2954 } 2955 2956 /** 2957 * ChangeIndexingDialog displays file index options. 2958 */ 2959 private class ChangeIndexingDialog extends Dialog 2960 { 2961 private Button checkIndexByName; 2962 private Button checkIndexIncrements; 2963 private Button checkIndexNative; 2964 2965 private boolean reloadFile; 2966 2967 private FileFormat selectedFile; 2968 private int indexType; 2969 private int indexOrder; 2970 2971 private ChangeIndexingDialog(Shell parent, int style, FileFormat viewSelectedFile) { 2972 super(parent, style); 2973 2974 selectedFile = viewSelectedFile; 2975 try { 2976 indexType = selectedFile.getIndexType(null); 2977 } 2978 catch (Exception ex) { 2979 log.debug("ChangeIndexingDialog(): getIndexType failed: ", ex); 2980 } 2981 try { 2982 indexOrder = selectedFile.getIndexOrder(null); 2983 } 2984 catch (Exception ex) { 2985 log.debug("ChangeIndexingDialog(): getIndexOrder failed: ", ex); 2986 } 2987 reloadFile = false; 2988 } 2989 2990 private void setIndexOptions() { 2991 try { 2992 if (checkIndexByName.getSelection()) 2993 indexType = selectedFile.getIndexType("H5_INDEX_NAME"); 2994 else 2995 indexType = selectedFile.getIndexType("H5_INDEX_CRT_ORDER"); 2996 } 2997 catch (Exception ex) { 2998 log.debug("setIndexOptions(): getIndexType failed: ", ex); 2999 } 3000 3001 try { 3002 if (checkIndexIncrements.getSelection()) 3003 indexOrder = selectedFile.getIndexOrder("H5_ITER_INC"); 3004 else if (checkIndexNative.getSelection()) 3005 indexOrder = selectedFile.getIndexOrder("H5_ITER_NATIVE"); 3006 else 3007 indexOrder = selectedFile.getIndexOrder("H5_ITER_DEC"); 3008 } 3009 catch (Exception ex) { 3010 log.debug("setIndexOptions(): getIndexOrder failed: ", ex); 3011 } 3012 3013 reloadFile = true; 3014 } 3015 3016 /** @return the current value of the index type. */ 3017 public int getIndexType() { 3018 return indexType; 3019 } 3020 3021 /** @return the current value of the index order. */ 3022 public int getIndexOrder() { 3023 return indexOrder; 3024 } 3025 3026 /** @return the current value of the reloadFile. */ 3027 public boolean isReloadFile() { 3028 return reloadFile; 3029 } 3030 3031 /** open the ChangeIndexingDialog for setting the indexing values. */ 3032 public void open() { 3033 Shell parent = getParent(); 3034 final Shell openShell = new Shell(parent, SWT.DIALOG_TRIM | SWT.APPLICATION_MODAL); 3035 openShell.setFont(curFont); 3036 openShell.setText("Indexing options"); 3037 openShell.setImages(ViewProperties.getHdfIcons()); 3038 openShell.setLayout(new GridLayout(1, true)); 3039 3040 // Create main content region 3041 Composite content = new Composite(openShell, SWT.NONE); 3042 content.setLayout(new GridLayout(1, true)); 3043 content.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true)); 3044 3045 org.eclipse.swt.widgets.Group indexingTypeGroup = new org.eclipse.swt.widgets.Group(content, SWT.NONE); 3046 indexingTypeGroup.setFont(curFont); 3047 indexingTypeGroup.setText("Indexing Type"); 3048 indexingTypeGroup.setLayout(new GridLayout(2, true)); 3049 indexingTypeGroup.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true)); 3050 3051 int initIndexType = 0; 3052 try { 3053 initIndexType = selectedFile.getIndexType("H5_INDEX_NAME"); 3054 } 3055 catch (Exception ex) { 3056 log.debug("open(): getIndexType failed: ", ex); 3057 } 3058 checkIndexByName = new Button(indexingTypeGroup, SWT.RADIO); 3059 checkIndexByName.setFont(curFont); 3060 checkIndexByName.setText("By Name"); 3061 checkIndexByName.setSelection((indexType) == initIndexType); 3062 checkIndexByName.setLayoutData(new GridData(SWT.CENTER, SWT.FILL, false, false)); 3063 3064 try { 3065 initIndexType = selectedFile.getIndexType("H5_INDEX_CRT_ORDER"); 3066 } 3067 catch (Exception ex) { 3068 log.debug("open(): getIndexType failed: ", ex); 3069 } 3070 Button byOrder = new Button(indexingTypeGroup, SWT.RADIO); 3071 byOrder.setFont(curFont); 3072 byOrder.setText("By Creation Order"); 3073 byOrder.setSelection((indexType) == initIndexType); 3074 byOrder.setLayoutData(new GridData(SWT.CENTER, SWT.FILL, false, false)); 3075 3076 org.eclipse.swt.widgets.Group indexingOrderGroup = new org.eclipse.swt.widgets.Group(content, SWT.NONE); 3077 indexingOrderGroup.setFont(curFont); 3078 indexingOrderGroup.setText("Indexing Order"); 3079 indexingOrderGroup.setLayout(new GridLayout(3, true)); 3080 indexingOrderGroup.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true)); 3081 3082 int initIndexOrder = 0; 3083 try { 3084 initIndexOrder = selectedFile.getIndexOrder("H5_ITER_INC"); 3085 } 3086 catch (Exception ex) { 3087 log.debug("open(): getIndexOrder failed: ", ex); 3088 } 3089 checkIndexIncrements = new Button(indexingOrderGroup, SWT.RADIO); 3090 checkIndexIncrements.setFont(curFont); 3091 checkIndexIncrements.setText("Increments"); 3092 checkIndexIncrements.setSelection((indexOrder) == initIndexOrder); 3093 checkIndexIncrements.setLayoutData(new GridData(SWT.CENTER, SWT.FILL, false, false)); 3094 3095 try { 3096 initIndexOrder = selectedFile.getIndexOrder("H5_ITER_DEC"); 3097 } 3098 catch (Exception ex) { 3099 log.debug("open(): getIndexOrder failed: ", ex); 3100 } 3101 Button decrements = new Button(indexingOrderGroup, SWT.RADIO); 3102 decrements.setFont(curFont); 3103 decrements.setText("Decrements"); 3104 decrements.setSelection((indexOrder) == initIndexOrder); 3105 decrements.setLayoutData(new GridData(SWT.CENTER, SWT.FILL, false, false)); 3106 3107 try { 3108 initIndexOrder = selectedFile.getIndexOrder("H5_ITER_NATIVE"); 3109 } 3110 catch (Exception ex) { 3111 log.debug("open(): getIndexOrder failed: ", ex); 3112 } 3113 checkIndexNative = new Button(indexingOrderGroup, SWT.RADIO); 3114 checkIndexNative.setFont(curFont); 3115 checkIndexNative.setText("Native"); 3116 checkIndexNative.setSelection((indexOrder) == initIndexOrder); 3117 checkIndexNative.setLayoutData(new GridData(SWT.CENTER, SWT.FILL, false, false)); 3118 3119 3120 // Create Ok/Cancel button region 3121 Composite buttonComposite = new Composite(openShell, SWT.NONE); 3122 buttonComposite.setLayout(new GridLayout(2, true)); 3123 buttonComposite.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false)); 3124 3125 Button okButton = new Button(buttonComposite, SWT.PUSH); 3126 okButton.setFont(curFont); 3127 okButton.setText(" &Reload File "); 3128 okButton.setLayoutData(new GridData(SWT.END, SWT.FILL, true, false)); 3129 okButton.addSelectionListener(new SelectionAdapter() { 3130 @Override 3131 public void widgetSelected(SelectionEvent e) { 3132 setIndexOptions(); 3133 openShell.dispose(); 3134 } 3135 }); 3136 3137 Button cancelButton = new Button(buttonComposite, SWT.PUSH); 3138 cancelButton.setFont(curFont); 3139 cancelButton.setText(" &Cancel "); 3140 cancelButton.setLayoutData(new GridData(SWT.BEGINNING, SWT.FILL, true, false)); 3141 cancelButton.addSelectionListener(new SelectionAdapter() { 3142 @Override 3143 public void widgetSelected(SelectionEvent e) { 3144 reloadFile = false; 3145 openShell.dispose(); 3146 } 3147 }); 3148 3149 openShell.pack(); 3150 3151 Point minimumSize = openShell.computeSize(SWT.DEFAULT, SWT.DEFAULT); 3152 3153 openShell.setMinimumSize(minimumSize); 3154 3155 Rectangle parentBounds = parent.getBounds(); 3156 Point shellSize = openShell.getSize(); 3157 openShell.setLocation((parentBounds.x + (parentBounds.width / 2)) - (shellSize.x / 2), 3158 (parentBounds.y + (parentBounds.height / 2)) - (shellSize.y / 2)); 3159 3160 openShell.open(); 3161 3162 Display display = parent.getDisplay(); 3163 while (!openShell.isDisposed()) 3164 if (!display.readAndDispatch()) 3165 display.sleep(); 3166 } 3167 } 3168 3169 private class ChangeLibVersionDialog extends Dialog 3170 { 3171 public ChangeLibVersionDialog(Shell parent, int style) { 3172 super(parent, style); 3173 } 3174 3175 public void open() { 3176 Shell parent = getParent(); 3177 final Shell openShell = new Shell(parent, SWT.SHELL_TRIM | SWT.APPLICATION_MODAL); 3178 openShell.setFont(curFont); 3179 openShell.setText("Set the library version bounds: "); 3180 openShell.setImages(ViewProperties.getHdfIcons()); 3181 openShell.setLayout(new GridLayout(1, true)); 3182 3183 String[] lowValues = { "Earliest", "V18", "V110", "V112", "V114", "Latest" }; 3184 int[] lowConstants = { HDF5Constants.H5F_LIBVER_EARLIEST, HDF5Constants.H5F_LIBVER_V18, HDF5Constants.H5F_LIBVER_V110, HDF5Constants.H5F_LIBVER_V112, HDF5Constants.H5F_LIBVER_V114, HDF5Constants.H5F_LIBVER_LATEST }; 3185 String[] highValues = { "V18", "V110", "V112", "V114", "Latest" }; 3186 int[] highConstants = { HDF5Constants.H5F_LIBVER_V18, HDF5Constants.H5F_LIBVER_V110, HDF5Constants.H5F_LIBVER_V112, HDF5Constants.H5F_LIBVER_V114, HDF5Constants.H5F_LIBVER_LATEST }; 3187 3188 // Try to retrieve the existing version bounds 3189 int[] current = null; 3190 try { 3191 current = selectedObject.getFileFormat().getLibBounds(); 3192 } 3193 catch (Exception err) { 3194 openShell.getDisplay().beep(); 3195 Tools.showError(openShell, "Version bounds", "Error when getting lib version bounds, using default"); 3196 current = new int[]{HDF5Constants.H5F_LIBVER_EARLIEST, HDF5Constants.H5F_LIBVER_LATEST}; 3197 } 3198 int lowidx = 0; 3199 for(int i = 0; i < lowConstants.length; i++) { 3200 if(lowConstants[i] == current[0]){ 3201 lowidx = i; 3202 break; 3203 } 3204 } 3205 int highidx = 0; 3206 for(int i = 0; i < highConstants.length; i++) { 3207 if(highConstants[i] == current[1]){ 3208 highidx = i; 3209 break; 3210 } 3211 } 3212 3213 // Dummy label 3214 new Label(openShell, SWT.LEFT); 3215 3216 Label label = new Label(openShell, SWT.LEFT); 3217 label.setFont(curFont); 3218 label.setText("Earliest Version: "); 3219 3220 final Combo earliestCombo = new Combo(openShell, SWT.SINGLE | SWT.BORDER | SWT.READ_ONLY); 3221 earliestCombo.setFont(curFont); 3222 earliestCombo.setItems(lowValues); 3223 earliestCombo.select(lowidx); 3224 earliestCombo.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false)); 3225 3226 label = new Label(openShell, SWT.LEFT); 3227 label.setFont(curFont); 3228 label.setText("Latest Version: "); 3229 3230 final Combo latestCombo = new Combo(openShell, SWT.SINGLE | SWT.BORDER | SWT.READ_ONLY); 3231 latestCombo.setFont(curFont); 3232 latestCombo.setItems(highValues); 3233 latestCombo.select(highidx); 3234 latestCombo.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false)); 3235 3236 // Dummy label to consume remain space after resizing 3237 new Label(openShell, SWT.LEFT).setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true)); 3238 3239 // Create Ok/Cancel button region 3240 Composite buttonComposite = new Composite(openShell, SWT.NONE); 3241 buttonComposite.setLayout(new GridLayout(2, true)); 3242 buttonComposite.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false)); 3243 3244 Button okButton = new Button(buttonComposite, SWT.PUSH); 3245 okButton.setFont(curFont); 3246 okButton.setText(" &OK "); 3247 okButton.setLayoutData(new GridData(SWT.END, SWT.FILL, true, false)); 3248 okButton.addSelectionListener(new SelectionAdapter() { 3249 @Override 3250 public void widgetSelected(SelectionEvent e) { 3251 try { 3252 selectedObject.getFileFormat().setLibBounds(earliestCombo.getItem(earliestCombo.getSelectionIndex()), latestCombo.getItem(latestCombo.getSelectionIndex())); 3253 } 3254 catch (Exception err) { 3255 openShell.getDisplay().beep(); 3256 Tools.showError(openShell, "Version bounds", "Error when setting lib version bounds"); 3257 return; 3258 } 3259 3260 openShell.dispose(); 3261 3262 ((HDFView) viewer).showMetaData(selectedObject); 3263 } 3264 }); 3265 3266 Button cancelButton = new Button(buttonComposite, SWT.PUSH); 3267 cancelButton.setFont(curFont); 3268 cancelButton.setText(" &Cancel "); 3269 cancelButton.setLayoutData(new GridData(SWT.BEGINNING, SWT.FILL, true, false)); 3270 cancelButton.addSelectionListener(new SelectionAdapter() { 3271 @Override 3272 public void widgetSelected(SelectionEvent e) { 3273 openShell.dispose(); 3274 } 3275 }); 3276 3277 openShell.pack(); 3278 3279 Point minimumSize = openShell.computeSize(SWT.DEFAULT, SWT.DEFAULT); 3280 3281 openShell.setMinimumSize(minimumSize); 3282 openShell.setSize(minimumSize.x + 50, minimumSize.y); 3283 3284 Rectangle parentBounds = parent.getBounds(); 3285 Point shellSize = openShell.getSize(); 3286 openShell.setLocation((parentBounds.x + (parentBounds.width / 2)) - (shellSize.x / 2), 3287 (parentBounds.y + (parentBounds.height / 2)) - (shellSize.y / 2)); 3288 3289 openShell.open(); 3290 3291 Display display = parent.getDisplay(); 3292 while (!openShell.isDisposed()) 3293 if (!display.readAndDispatch()) 3294 display.sleep(); 3295 } 3296 } 3297 3298 private class LoadDataThread extends Thread 3299 { 3300 LoadDataThread() { 3301 super(); 3302 setDaemon(true); 3303 } 3304 3305 @Override 3306 public void run() { 3307 try { 3308 Display.getDefault().syncExec(new Runnable() { 3309 @Override 3310 public void run() { 3311 try { 3312 showDataContent(selectedObject); 3313 } 3314 catch (Exception ex) { 3315 log.debug("showDataContent failure: ", ex); 3316 } 3317 } 3318 }); 3319 } 3320 catch (Exception e) { 3321 log.debug("showDataContent loading manually interrupted"); 3322 } 3323 } 3324 } 3325}