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