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