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