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