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.MetaDataView; 016 017import java.io.File; 018import java.lang.reflect.Array; 019import java.math.BigInteger; 020import java.util.ArrayList; 021import java.util.Iterator; 022import java.util.List; 023import java.util.StringTokenizer; 024 025import org.slf4j.Logger; 026import org.slf4j.LoggerFactory; 027 028import org.eclipse.jface.dialogs.MessageDialog; 029import org.eclipse.swt.SWT; 030import org.eclipse.swt.custom.ScrolledComposite; 031import org.eclipse.swt.events.MenuAdapter; 032import org.eclipse.swt.events.MenuEvent; 033import org.eclipse.swt.events.SelectionAdapter; 034import org.eclipse.swt.events.SelectionEvent; 035import org.eclipse.swt.graphics.Font; 036import org.eclipse.swt.graphics.Point; 037import org.eclipse.swt.graphics.Rectangle; 038import org.eclipse.swt.layout.GridData; 039import org.eclipse.swt.layout.GridLayout; 040import org.eclipse.swt.widgets.Button; 041import org.eclipse.swt.widgets.Combo; 042import org.eclipse.swt.widgets.Composite; 043import org.eclipse.swt.widgets.Dialog; 044import org.eclipse.swt.widgets.Display; 045import org.eclipse.swt.widgets.Event; 046import org.eclipse.swt.widgets.FileDialog; 047import org.eclipse.swt.widgets.Label; 048import org.eclipse.swt.widgets.Listener; 049import org.eclipse.swt.widgets.Menu; 050import org.eclipse.swt.widgets.MenuItem; 051import org.eclipse.swt.widgets.Shell; 052import org.eclipse.swt.widgets.TabFolder; 053import org.eclipse.swt.widgets.TabItem; 054import org.eclipse.swt.widgets.Table; 055import org.eclipse.swt.widgets.TableColumn; 056import org.eclipse.swt.widgets.TableItem; 057import org.eclipse.swt.widgets.Text; 058 059import hdf.hdf5lib.H5; 060import hdf.hdf5lib.HDF5Constants; 061import hdf.object.Attribute; 062import hdf.object.CompoundDS; 063import hdf.object.Dataset; 064import hdf.object.Datatype; 065import hdf.object.FileFormat; 066import hdf.object.Group; 067import hdf.object.HObject; 068import hdf.object.MetaDataContainer; 069import hdf.object.ScalarDS; 070import hdf.object.h5.H5ReferenceType; 071import hdf.object.h5.H5ReferenceType.H5ReferenceData; 072 073import hdf.view.DefaultFileFilter; 074import hdf.view.Tools; 075import hdf.view.ViewProperties; 076import hdf.view.DataView.DataViewManager; 077import hdf.view.TreeView.DefaultTreeView; 078import hdf.view.TreeView.TreeView; 079import hdf.view.dialog.InputDialog; 080import hdf.view.dialog.NewStringAttributeDialog; 081import hdf.view.dialog.NewDataObjectDialog; 082import hdf.view.dialog.NewScalarAttributeDialog; 083//import hdf.view.dialog.NewCompoundAttributeDialog; 084 085/** 086 * DefaultBaseMetaDataView is a default implementation of the MetaDataView which 087 * is used to show data properties of an object. Data properties include 088 * attributes and general object information such as the object type, data type 089 * and data space. 090 * 091 * This base class is responsible for displaying an object's general information 092 * and attributes, since these are not object-specific. Subclasses of this class 093 * are responsible for displaying any extra object-specific content by 094 * overriding the addObjectSpecificContent() method. 095 * 096 * @author Jordan T. Henderson 097 * @version 1.0 4/20/2018 098 */ 099public abstract class DefaultBaseMetaDataView implements MetaDataView { 100 101 private static final Logger log = LoggerFactory.getLogger(DefaultBaseMetaDataView.class); 102 103 /** The default display */ 104 protected final Display display = Display.getDefault(); 105 106 /** The view manger reference */ 107 protected final DataViewManager viewManager; 108 109 private final Composite parent; 110 111 /** The metadata container */ 112 protected final TabFolder contentTabFolder; 113 114 /** The attribute metadata pane */ 115 protected final Composite attributeInfoPane; 116 117 /** The general metadata pane */ 118 protected final Composite generalObjectInfoPane; 119 120 /** The current font */ 121 protected Font curFont; 122 123 /** The HDF data object */ 124 protected HObject dataObject; 125 126 /* The table to hold the list of attributes attached to the HDF object */ 127 private Table attrTable; 128 129 private Label attrNumberLabel; 130 131 private List<?> attrList; 132 133 private int numAttributes; 134 135 /** The HDF data object is hdf5 type */ 136 protected boolean isH5; 137 /** The HDF data object is hdf4 type */ 138 protected boolean isH4; 139 /** The HDF data object is netcdf type */ 140 protected boolean isN3; 141 142 private static final String[] attrTableColNames = { "Name", "Type", "Array Size", "Value[50](...)" }; 143 144 private static final int ATTR_TAB_INDEX = 0; 145 private static final int GENERAL_TAB_INDEX = 1; 146 147 /** 148 *The metadata view interface for displaying metadata information 149 * 150 * @param parentComposite 151 * the parent visual object 152 * @param viewer 153 * the viewer to use 154 * @param theObj 155 * the object to display the metadata info 156 */ 157 public DefaultBaseMetaDataView(Composite parentComposite, DataViewManager viewer, HObject theObj) { 158 this.parent = parentComposite; 159 this.viewManager = viewer; 160 this.dataObject = theObj; 161 162 numAttributes = 0; 163 164 isH5 = dataObject.getFileFormat().isThisType(FileFormat.getFileFormat(FileFormat.FILE_TYPE_HDF5)); 165 isH4 = dataObject.getFileFormat().isThisType(FileFormat.getFileFormat(FileFormat.FILE_TYPE_HDF4)); 166 isN3 = dataObject.getFileFormat().isThisType(FileFormat.getFileFormat(FileFormat.FILE_TYPE_NC3)); 167 168 try { 169 curFont = new Font(display, ViewProperties.getFontType(), ViewProperties.getFontSize(), SWT.NORMAL); 170 } 171 catch (Exception ex) { 172 curFont = null; 173 } 174 175 /* Get the metadata information before adding GUI components */ 176 try { 177 attrList = ((MetaDataContainer) dataObject).getMetadata(); 178 if (attrList != null) 179 numAttributes = attrList.size(); 180 } 181 catch (Exception ex) { 182 attrList = null; 183 log.debug("Error retrieving metadata of object '" + dataObject.getName() + "':", ex); 184 } 185 for (int i = 0; i < numAttributes; i++) { 186 Attribute attr = (Attribute) attrList.get(i); 187 Datatype atype = attr.getAttributeDatatype(); 188 if (isH5 && atype.isRef()) { 189 H5ReferenceType rtype = (H5ReferenceType)atype; 190 try { 191 List<H5ReferenceData> refdata = (List)rtype.getData(); 192 for (int r = 0; r < (int)rtype.getRefSize(); r++) { 193 H5ReferenceData rf = refdata.get(r); 194 log.trace("constructor: refdata {}", rf.ref_array); 195 } 196 } 197 catch (Exception ex) { 198 log.trace("Error retrieving H5ReferenceData of object ", ex); 199 } 200 } 201 } 202 203 log.trace("dataObject={} isN3={} isH4={} isH5={} numAttributes={}", dataObject, isN3, isH4, isH5, numAttributes); 204 205 contentTabFolder = new TabFolder(parent, SWT.NONE); 206 contentTabFolder.addSelectionListener(new SelectionAdapter() { 207 @Override 208 public void widgetSelected(SelectionEvent e) { 209 switch (contentTabFolder.getSelectionIndex()) { 210 case ATTR_TAB_INDEX: 211 parent.setData("MetaDataView.LastTabIndex", ATTR_TAB_INDEX); 212 break; 213 case GENERAL_TAB_INDEX: 214 default: 215 parent.setData("MetaDataView.LastTabIndex", GENERAL_TAB_INDEX); 216 break; 217 } 218 } 219 }); 220 221 attributeInfoPane = createAttributeInfoPane(contentTabFolder, dataObject); 222 if (attributeInfoPane != null) { 223 TabItem attributeInfoItem = new TabItem(contentTabFolder, SWT.None, ATTR_TAB_INDEX); 224 attributeInfoItem.setText("Object Attribute Info"); 225 attributeInfoItem.setControl(attributeInfoPane); 226 } 227 228 generalObjectInfoPane = createGeneralObjectInfoPane(contentTabFolder, dataObject); 229 if (generalObjectInfoPane != null) { 230 TabItem generalInfoItem = new TabItem(contentTabFolder, SWT.None, GENERAL_TAB_INDEX); 231 generalInfoItem.setText("General Object Info"); 232 generalInfoItem.setControl(generalObjectInfoPane); 233 } 234 235 /* Add any extra information depending on the object type */ 236 try { 237 addObjectSpecificContent(); 238 } 239 catch (UnsupportedOperationException ex) { 240 } 241 242 if (parent instanceof ScrolledComposite) 243 ((ScrolledComposite) parent).setContent(contentTabFolder); 244 245 /* 246 * If the MetaDataView.LastTabIndex key data exists in the parent 247 * composite, retrieve its value to determine which remembered 248 * tab to select. 249 */ 250 Object lastTabObject = parent.getData("MetaDataView.LastTabIndex"); 251 if (lastTabObject != null) { 252 contentTabFolder.setSelection((int) lastTabObject); 253 } 254 } 255 256 /** 257 * Additional metadata to display 258 */ 259 protected abstract void addObjectSpecificContent(); 260 261 private Composite createAttributeInfoPane(Composite parent, final HObject dataObject) { 262 if (parent == null || dataObject == null) return null; 263 264 org.eclipse.swt.widgets.Group attributeInfoGroup = null; 265 266 attributeInfoGroup = new org.eclipse.swt.widgets.Group(parent, SWT.NONE); 267 attributeInfoGroup.setFont(curFont); 268 attributeInfoGroup.setBackground(Display.getCurrent().getSystemColor(SWT.COLOR_WIDGET_LIGHT_SHADOW)); 269 attributeInfoGroup.setLayout(new GridLayout(3, false)); 270 attributeInfoGroup.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true)); 271 272 if (isH5) { 273 StringBuilder objCreationStr = new StringBuilder("Creation Order NOT Tracked"); 274 long ocplID = -1; 275 long objid = -1; 276 int creationOrder = 0; 277 try { 278 objid = dataObject.open(); 279 if (objid >= 0) { 280 if (dataObject instanceof Group) { 281 ocplID = H5.H5Gget_create_plist(objid); 282 } 283 else if (dataObject instanceof Dataset) { 284 ocplID = H5.H5Dget_create_plist(objid); 285 } 286 if (ocplID >= 0) { 287 creationOrder = H5.H5Pget_attr_creation_order(ocplID); 288 log.trace("createAttributeInfoPane(): creationOrder={}", creationOrder); 289 if ((creationOrder & HDF5Constants.H5P_CRT_ORDER_TRACKED) > 0) { 290 objCreationStr.setLength(0); 291 objCreationStr.append("Creation Order Tracked"); 292 if ((creationOrder & HDF5Constants.H5P_CRT_ORDER_INDEXED) > 0) 293 objCreationStr.append(" and Indexed"); 294 } 295 } 296 } 297 } 298 finally { 299 H5.H5Pclose(ocplID); 300 dataObject.close(objid); 301 } 302 303 /* Creation order section */ 304 Label label; 305 label = new Label(attributeInfoGroup, SWT.LEFT); 306 label.setFont(curFont); 307 label.setText("Attribute Creation Order: "); 308 label.setLayoutData(new GridData(SWT.BEGINNING, SWT.FILL, false, false)); 309 310 Text text; 311 text = new Text(attributeInfoGroup, SWT.SINGLE | SWT.BORDER); 312 text.setEditable(false); 313 text.setFont(curFont); 314 text.setText(objCreationStr.toString()); 315 text.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false, 2, 1)); 316 } 317 318 log.trace("createAttributeInfoPane(): numAttributes={}", numAttributes); 319 320 attrNumberLabel = new Label(attributeInfoGroup, SWT.RIGHT); 321 attrNumberLabel.setFont(curFont); 322 attrNumberLabel.setText("Number of attributes = 0"); 323 attrNumberLabel.setLayoutData(new GridData(SWT.BEGINNING, SWT.FILL, false, false)); 324 325 Button addButton = new Button(attributeInfoGroup, SWT.PUSH); 326 addButton.setFont(curFont); 327 addButton.setText("Add Attribute"); 328 addButton.setEnabled(!(dataObject.getFileFormat().isReadOnly())); 329 addButton.setLayoutData(new GridData(SWT.END, SWT.FILL, true, false)); 330 addButton.addSelectionListener(new SelectionAdapter() { 331 @Override 332 public void widgetSelected(SelectionEvent e) { 333 addAttribute(dataObject); 334 } 335 }); 336 337 /* Deleting attributes is not supported by HDF4 */ 338 Button delButton = new Button(attributeInfoGroup, SWT.PUSH); 339 delButton.setFont(curFont); 340 delButton.setText("Delete Attribute"); 341 delButton.setEnabled(isH5 && !(dataObject.getFileFormat().isReadOnly())); 342 delButton.setLayoutData(new GridData(SWT.END, SWT.FILL, false, false)); 343 delButton.addSelectionListener(new SelectionAdapter() { 344 @Override 345 public void widgetSelected(SelectionEvent e) { 346 deleteAttribute(dataObject); 347 } 348 }); 349 350 attrTable = new Table(attributeInfoGroup, SWT.FULL_SELECTION | SWT.BORDER | SWT.V_SCROLL | SWT.H_SCROLL); 351 attrTable.setLinesVisible(true); 352 attrTable.setHeaderVisible(true); 353 attrTable.setFont(curFont); 354 attrTable.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true, 3, 1)); 355 356 Menu attrPopupMenu = createAttributePopupMenu(attrTable); 357 attrTable.setMenu(attrPopupMenu); 358 359 /* 360 * Add a double-click listener for editing attribute values in a separate 361 * TableView 362 */ 363 attrTable.addListener(SWT.MouseDoubleClick, new Listener() { 364 @Override 365 public void handleEvent(Event arg0) { 366 int selectionIndex = attrTable.getSelectionIndex(); 367 if (selectionIndex < 0) { 368 Tools.showError(Display.getDefault().getShells()[0], "Select", "No Attribute selected"); 369 return; 370 } 371 372 final TableItem item = attrTable.getItem(selectionIndex); 373 374 viewManager.getTreeView().setDefaultDisplayMode(true); 375 376 try { 377 Display.getDefault().syncExec(new Runnable() { 378 @Override 379 public void run() { 380 try { 381 viewManager.getTreeView().showDataContent((HObject) item.getData()); 382 } 383 catch (Exception ex) { 384 log.debug("Attribute showDataContent failure: ", ex); 385 } 386 } 387 }); 388 } 389 catch (Exception e) { 390 log.debug("Attribute showDataContent loading manually interrupted"); 391 } 392 } 393 }); 394 395 /* 396 * Add a right-click listener for showing a menu that has options for renaming 397 * an attribute, editing an attribute, or deleting an attribute 398 */ 399 attrTable.addListener(SWT.MenuDetect, new Listener() { 400 @Override 401 public void handleEvent(Event arg0) { 402 int index = attrTable.getSelectionIndex(); 403 if (index < 0) return; 404 405 attrTable.getMenu().setVisible(true); 406 } 407 }); 408 409 for (int i = 0; i < attrTableColNames.length; i++) { 410 TableColumn column = new TableColumn(attrTable, SWT.NONE); 411 column.setText(attrTableColNames[i]); 412 column.setMoveable(false); 413 414 /* 415 * Make sure all columns show even when the object in question has no attributes 416 */ 417 if (i == attrTableColNames.length - 1) 418 column.setWidth(200); 419 else 420 column.setWidth(50); 421 } 422 423 if (attrList != null) { 424 attrNumberLabel.setText("Number of attributes = " + numAttributes); 425 426 Attribute attr = null; 427 for (int i = 0; i < numAttributes; i++) { 428 attr = (Attribute) attrList.get(i); 429 430 log.trace("createAttributeInfoPane(): attr[{}] is {} of type {}", i, attr.getAttributeName(), 431 attr.getAttributeDatatype().getDescription()); 432 433 addAttributeTableItem(attrTable, attr); 434 } 435 } 436 437 for (int i = 0; i < attrTableColNames.length; i++) { 438 attrTable.getColumn(i).pack(); 439 } 440 441 // Prevent attributes with many values, such as array types, from making 442 // the window too wide 443 attrTable.getColumn(3).setWidth(200); 444 445 return attributeInfoGroup; 446 } 447 448 private Composite createGeneralObjectInfoPane(Composite parent, final HObject dataObject) { 449 if (parent == null || dataObject == null) return null; 450 451 FileFormat theFile = dataObject.getFileFormat(); 452 boolean isRoot = ((dataObject instanceof Group) && ((Group) dataObject).isRoot()); 453 String objTypeStr = "Unknown"; 454 Label label; 455 Text text; 456 457 /* Add an SWT Group to encompass all of the GUI components */ 458 org.eclipse.swt.widgets.Group generalInfoGroup = new org.eclipse.swt.widgets.Group(parent, SWT.NONE); 459 generalInfoGroup.setFont(curFont); 460 generalInfoGroup.setLayout(new GridLayout(2, false)); 461 generalInfoGroup.setBackground(Display.getCurrent().getSystemColor(SWT.COLOR_WIDGET_LIGHT_SHADOW)); 462 463 /* Object name section */ 464 label = new Label(generalInfoGroup, SWT.LEFT); 465 label.setFont(curFont); 466 label.setText("Name: "); 467 468 text = new Text(generalInfoGroup, SWT.SINGLE | SWT.BORDER); 469 text.setEditable(false); 470 text.setFont(curFont); 471 text.setText(dataObject.getName()); 472 text.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false)); 473 474 /* Object Path section */ 475 label = new Label(generalInfoGroup, SWT.LEFT); 476 label.setFont(curFont); 477 label.setText("Path: "); 478 479 text = new Text(generalInfoGroup, SWT.SINGLE | SWT.BORDER); 480 text.setEditable(false); 481 text.setFont(curFont); 482 text.setText(dataObject.getPath() == null ? "/" 483 : dataObject.getPath()); /* TODO: temporary workaround until Object Library is fixed */ 484 text.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false)); 485 486 /* Object Type section */ 487 label = new Label(generalInfoGroup, SWT.LEFT); 488 label.setFont(curFont); 489 label.setText("Type: "); 490 491 if (isH5) { 492 if (dataObject instanceof Group) { 493 objTypeStr = "HDF5 Group"; 494 } 495 else if (dataObject instanceof ScalarDS) { 496 objTypeStr = "HDF5 Dataset"; 497 } 498 else if (dataObject instanceof CompoundDS) { 499 objTypeStr = "HDF5 Dataset"; 500 } 501 else if (dataObject instanceof Datatype) { 502 objTypeStr = "HDF5 Named Datatype"; 503 } 504 else { 505 log.debug("createGeneralObjectInfoPane(): unknown HDF5 dataObject"); 506 } 507 } 508 else if (isH4) { 509 if (dataObject instanceof Group) { 510 objTypeStr = "HDF4 Group"; 511 } 512 else if (dataObject instanceof ScalarDS) { 513 ScalarDS ds = (ScalarDS) dataObject; 514 if (ds.isImage()) { 515 objTypeStr = "HDF4 Raster Image"; 516 } 517 else { 518 objTypeStr = "HDF4 SDS"; 519 } 520 } 521 else if (dataObject instanceof CompoundDS) { 522 objTypeStr = "HDF4 Vdata"; 523 } 524 else { 525 log.debug("createGeneralObjectInfoPane(): unknown HDF4 dataObject"); 526 } 527 } 528 else if (isN3) { 529 if (dataObject instanceof Group) { 530 objTypeStr = "netCDF3 Group"; 531 } 532 else if (dataObject instanceof ScalarDS) { 533 objTypeStr = "netCDF3 Dataset"; 534 } 535 else { 536 log.debug("createGeneralObjectInfoPane(): unknown netCDF3 dataObject"); 537 } 538 } 539 else { 540 if (dataObject instanceof Group) { 541 objTypeStr = "Group"; 542 } 543 else if (dataObject instanceof ScalarDS) { 544 objTypeStr = "Dataset"; 545 } 546 else if (dataObject instanceof CompoundDS) { 547 objTypeStr = "Dataset"; 548 } 549 else { 550 log.debug("createGeneralObjectInfoPane(): unknown dataObject"); 551 } 552 } 553 554 text = new Text(generalInfoGroup, SWT.SINGLE | SWT.BORDER); 555 text.setEditable(false); 556 text.setFont(curFont); 557 text.setText(objTypeStr); 558 text.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false)); 559 560 /* Object ID section */ 561 562 // bug #926 to remove the OID, put it back on Nov. 20, 2008, --PC 563 String oidStr = null; 564 long[] oID = dataObject.getOID(); 565 if (oID != null) { 566 oidStr = String.valueOf(oID[0]); 567 if (isH4) 568 oidStr += ", " + oID[1]; 569 570 if (isH5) { 571 label = new Label(generalInfoGroup, SWT.LEFT); 572 label.setFont(curFont); 573 label.setText("Object Ref: "); 574 } 575 else { 576 label = new Label(generalInfoGroup, SWT.LEFT); 577 label.setFont(curFont); 578 label.setText("Tag, Ref: "); 579 } 580 581 text = new Text(generalInfoGroup, SWT.SINGLE | SWT.BORDER); 582 text.setEditable(false); 583 text.setFont(curFont); 584 text.setText(oidStr); 585 text.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false)); 586 } 587 588 /* 589 * If this is the root group, add some special extra info, such as the Library 590 * Version bounds set for the file. 591 */ 592 if (isRoot) { 593 /* Get the file's size */ 594 long fileSize = 0; 595 try { 596 fileSize = (new File(dataObject.getFile())).length(); 597 } 598 catch (Exception ex) { 599 fileSize = -1; 600 } 601 fileSize /= 1024; 602 603 /* Retrieve the number of subgroups and datasets in the root group */ 604 HObject root = theFile.getRootObject(); 605 HObject theObj = null; 606 Iterator<HObject> it = ((Group) root).depthFirstMemberList().iterator(); 607 int groupCount = 0; 608 int datasetCount = 0; 609 610 while (it.hasNext()) { 611 theObj = it.next(); 612 613 if (theObj instanceof Group) 614 groupCount++; 615 else 616 datasetCount++; 617 } 618 619 /* Append all of the file's information to the general object info pane */ 620 String fileInfo = ""; 621 622 fileInfo = "size=" + fileSize + "K, groups=" + groupCount + ", datasets=" + datasetCount; 623 624 /* File name section */ 625 label = new Label(generalInfoGroup, SWT.LEFT); 626 label.setFont(curFont); 627 label.setText("File Name: "); 628 629 text = new Text(generalInfoGroup, SWT.SINGLE | SWT.BORDER); 630 text.setEditable(false); 631 text.setFont(curFont); 632 text.setText(dataObject.getFileFormat().getName()); 633 text.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false)); 634 635 /* File Path section */ 636 label = new Label(generalInfoGroup, SWT.LEFT); 637 label.setFont(curFont); 638 label.setText("File Path: "); 639 640 text = new Text(generalInfoGroup, SWT.SINGLE | SWT.BORDER); 641 text.setEditable(false); 642 text.setFont(curFont); 643 text.setText((new File(dataObject.getFile())).getParent()); 644 text.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false)); 645 646 label = new Label(generalInfoGroup, SWT.LEFT); 647 label.setFont(curFont); 648 label.setText("File Type: "); 649 650 if (isH5) 651 objTypeStr = "HDF5, " + fileInfo; 652 else if (isH4) 653 objTypeStr = "HDF4, " + fileInfo; 654 else if (isN3) 655 objTypeStr = "netCDF3, " + fileInfo; 656 else 657 objTypeStr = fileInfo; 658 659 text = new Text(generalInfoGroup, SWT.SINGLE | SWT.BORDER); 660 text.setEditable(false); 661 text.setFont(curFont); 662 text.setText(objTypeStr); 663 text.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false)); 664 665 if (isH5) { 666 log.trace("createGeneralObjectInfoPane(): get Library Version bounds info"); 667 String libversion = ""; 668 try { 669 libversion = dataObject.getFileFormat().getLibBoundsDescription(); 670 } 671 catch (Exception ex) { 672 log.debug("Get Library Bounds Description failure: ", ex); 673 } 674 675 if (libversion.length() > 0) { 676 label = new Label(generalInfoGroup, SWT.LEFT); 677 label.setFont(curFont); 678 label.setText("Library version bounds: "); 679 680 text = new Text(generalInfoGroup, SWT.SINGLE | SWT.BORDER); 681 text.setEditable(false); 682 text.setFont(curFont); 683 text.setText(libversion); 684 text.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false)); 685 } 686 687 Button userBlockButton = new Button(generalInfoGroup, SWT.PUSH); 688 userBlockButton.setText("Show User Block"); 689 userBlockButton.addSelectionListener(new SelectionAdapter() { 690 @Override 691 public void widgetSelected(SelectionEvent e) { 692 new UserBlockDialog(display.getShells()[0], SWT.NONE, dataObject).open(); 693 } 694 }); 695 } 696 } 697 698 /* Add a dummy label to take up some vertical space between sections */ 699 label = new Label(generalInfoGroup, SWT.LEFT); 700 label.setText(""); 701 label.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false, 2, 1)); 702 703 return generalInfoGroup; 704 } 705 706 @Override 707 public HObject getDataObject() { 708 return dataObject; 709 } 710 711 private Menu createAttributePopupMenu(final Table table) { 712 final Menu menu = new Menu(table); 713 MenuItem item; 714 715 item = new MenuItem(menu, SWT.PUSH); 716 item.setText("Rename Attribute"); 717 item.addSelectionListener(new SelectionAdapter() { 718 @Override 719 public void widgetSelected(SelectionEvent e) { 720 int selectionIndex = table.getSelectionIndex(); 721 if (selectionIndex < 0) { 722 Tools.showError(Display.getDefault().getShells()[0], "Select", "No Attribute selected"); 723 return; 724 } 725 726 HObject itemObj = (HObject) table.getItem(selectionIndex).getData(); 727 String result = new InputDialog(Display.getDefault().getShells()[0], 728 Display.getDefault().getShells()[0].getText() + " - Rename Attribute", "New Attribute Name", 729 itemObj.getName()).open(); 730 731 if ((result == null) || ((result = result.trim()) == null) || (result.length() < 1)) { 732 return; 733 } 734 735 Attribute attr = (Attribute) attrTable.getItem(selectionIndex).getData(); 736 renameAttribute(attr, result); 737 } 738 }); 739 740 item = new MenuItem(menu, SWT.PUSH); 741 item.setText("View/Edit Attribute Value"); 742 item.addSelectionListener(new SelectionAdapter() { 743 @Override 744 public void widgetSelected(SelectionEvent e) { 745 int selectionIndex = attrTable.getSelectionIndex(); 746 if (selectionIndex < 0) { 747 Tools.showError(Display.getDefault().getShells()[0], "Select", "No Attribute selected"); 748 return; 749 } 750 751 final TableItem item = attrTable.getItem(selectionIndex); 752 753 viewManager.getTreeView().setDefaultDisplayMode(true); 754 755 try { 756 Display.getDefault().syncExec(new Runnable() { 757 @Override 758 public void run() { 759 try { 760 viewManager.getTreeView().showDataContent((HObject) item.getData()); 761 } 762 catch (Exception ex) { 763 log.debug("Attribute showDataContent failure: ", ex); 764 } 765 } 766 }); 767 } 768 catch (Exception ex) { 769 log.debug("Attribute showDataContent loading manually interrupted"); 770 } 771 } 772 }); 773 774 item = new MenuItem(menu, SWT.PUSH); 775 item.setText("Delete Attribute"); 776 item.addSelectionListener(new SelectionAdapter() { 777 @Override 778 public void widgetSelected(SelectionEvent e) { 779 int selectionIndex = attrTable.getSelectionIndex(); 780 if (selectionIndex < 0) { 781 Tools.showError(Display.getDefault().getShells()[0], "Select", "No Attribute selected"); 782 return; 783 } 784 785 deleteAttribute(dataObject); 786 } 787 }); 788 789 menu.addMenuListener(new MenuAdapter() { 790 @Override 791 public void menuShown(MenuEvent e) { 792 /* 'Rename Attribute' MenuItem */ 793 menu.getItem(0).setEnabled(!dataObject.getFileFormat().isReadOnly() && !isH4); 794 795 /* 'Delete Attribute' MenuItem */ 796 menu.getItem(2).setEnabled(!dataObject.getFileFormat().isReadOnly() && !isH4); 797 } 798 }); 799 800 return menu; 801 } 802 803 @Override 804 public Attribute addAttribute(HObject obj) { 805 if (obj == null) return null; 806 807 HObject root = obj.getFileFormat().getRootObject(); 808 Attribute attr = null; 809 if (isH5) { 810 NewScalarAttributeDialog dialog = new NewScalarAttributeDialog(display.getShells()[0], obj, 811 ((Group) root).breadthFirstMemberList()); 812 dialog.open(); 813 attr = dialog.getAttribute(); 814 } 815 else { 816 NewStringAttributeDialog dialog = new NewStringAttributeDialog(display.getShells()[0], obj, 817 ((Group) root).breadthFirstMemberList()); 818 dialog.open(); 819 attr = dialog.getAttribute(); 820 } 821 822 if (attr == null) { 823 log.debug("addAttribute(): attr is null"); 824 return null; 825 } 826 827 addAttributeTableItem(attrTable, attr); 828 829 numAttributes++; 830 attrNumberLabel.setText("Number of attributes = " + numAttributes); 831 832 if (viewManager.getTreeView() instanceof DefaultTreeView) 833 ((DefaultTreeView) viewManager.getTreeView()).updateItemIcon(obj); 834 835 return attr; 836 } 837 838 @Override 839 public Attribute deleteAttribute(HObject obj) { 840 if (obj == null) return null; 841 842 int idx = attrTable.getSelectionIndex(); 843 if (idx < 0) { 844 log.debug("deleteAttribute(): no attribute is selected"); 845 Tools.showError(display.getShells()[0], "Delete", "No attribute is selected."); 846 return null; 847 } 848 849 int answer = SWT.NO; 850 if (Tools.showConfirm(display.getShells()[0], "Delete", 851 "Do you want to delete the selected attribute?")) 852 answer = SWT.YES; 853 if (answer == SWT.NO) { 854 log.trace("deleteAttribute(): attribute deletion cancelled"); 855 return null; 856 } 857 858 if (attrList == null) { 859 log.debug("deleteAttribute(): Attribute list was null; can't delete an attribute from it"); 860 return null; 861 } 862 863 Attribute attr = (Attribute) attrList.get(idx); 864 865 log.trace("deleteAttribute(): Attribute selected for deletion: {}", attr.getAttributeName()); 866 867 try { 868 ((MetaDataContainer) obj).removeMetadata(attr); 869 } 870 catch (Exception ex) { 871 log.debug("deleteAttribute(): attribute deletion failed for object '{}': ", obj.getName(), ex); 872 } 873 874 attrTable.remove(idx); 875 numAttributes--; 876 877 attrNumberLabel.setText("Number of attributes = " + numAttributes); 878 879 if (viewManager.getTreeView() instanceof DefaultTreeView) 880 ((DefaultTreeView) viewManager.getTreeView()).updateItemIcon(obj); 881 882 return attr; 883 } 884 885 private void renameAttribute(Attribute attr, String newName) { 886 if ((attr == null) || (newName == null) || (newName = newName.trim()) == null || (newName.length() < 1)) { 887 log.debug("renameAttribute(): Attribute is null or Attribute's new name is null"); 888 return; 889 } 890 891 String attrName = attr.getAttributeName(); 892 893 log.trace("renameAttribute(): oldName={} newName={}", attrName, newName); 894 895 if (isH5) { 896 try { 897 dataObject.getFileFormat().renameAttribute(dataObject, attrName, newName); 898 } 899 catch (Exception ex) { 900 log.debug("renameAttribute(): renaming failure:", ex); 901 Tools.showError(display.getShells()[0], "Delete", ex.getMessage()); 902 } 903 904 /* Update the attribute table */ 905 int selectionIndex = attrTable.getSelectionIndex(); 906 if (selectionIndex < 0) { 907 Tools.showError(Display.getDefault().getShells()[0], "Delete", "No Attribute selected"); 908 return; 909 } 910 911 attrTable.getItem(selectionIndex).setText(0, newName); 912 } 913 else { 914 log.debug("renameAttribute(): renaming attributes is only allowed for HDF5 files"); 915 } 916 917 if (dataObject instanceof MetaDataContainer) { 918 try { 919 ((MetaDataContainer) dataObject).updateMetadata(attr); 920 } 921 catch (Exception ex) { 922 log.debug("renameAttribute(): updateMetadata() failure:", ex); 923 Tools.showError(display.getShells()[0], "Delete", ex.getMessage()); 924 } 925 } 926 } 927 928 /** 929 * Update an attribute's value. Currently can only update a single data point. 930 * 931 * @param attr 932 * the selected attribute. 933 * @param newValue 934 * the string of the new value. 935 */ 936 private void updateAttributeValue(Attribute attr, String newValue) { 937 if ((attr == null) || (newValue == null) || (newValue = newValue.trim()) == null || (newValue.length() < 1)) { 938 log.debug("updateAttributeValue(): Attribute is null or Attribute's new value is null"); 939 return; 940 } 941 942 String attrName = attr.getAttributeName(); 943 Object data; 944 945 log.trace("updateAttributeValue(): changing value of attribute '{}'", attrName); 946 947 try { 948 data = attr.getAttributeData(); 949 } 950 catch (Exception ex) { 951 log.debug("updateAttributeValue(): getData() failure:", ex); 952 return; 953 } 954 955 if (data == null) { 956 log.debug("updateAttributeValue(): attribute's data was null"); 957 return; 958 } 959 960 int arrayLength = Array.getLength(data); 961 StringTokenizer st = new StringTokenizer(newValue, ","); 962 if (st.countTokens() < arrayLength) { 963 log.debug("updateAttributeValue(): More data values needed: {}", newValue); 964 Tools.showError(display.getShells()[0], "Update", "More data values needed: " + newValue); 965 return; 966 } 967 968 char cNT = ' '; 969 String cName = data.getClass().getName(); 970 int cIndex = cName.lastIndexOf('['); 971 if (cIndex >= 0) { 972 cNT = cName.charAt(cIndex + 1); 973 } 974 boolean isUnsigned = attr.getAttributeDatatype().isUnsigned(); 975 976 log.trace("updateAttributeValue(): array_length={} cName={} NT={} isUnsigned={}", arrayLength, cName, 977 cNT, isUnsigned); 978 979 double d = 0; 980 String theToken = null; 981 long max = 0; 982 long min = 0; 983 for (int i = 0; i < arrayLength; i++) { 984 max = min = 0; 985 theToken = st.nextToken().trim(); 986 try { 987 if (!(Array.get(data, i) instanceof String)) { 988 d = Double.parseDouble(theToken); 989 } 990 } 991 catch (NumberFormatException ex) { 992 log.debug("updateAttributeValue(): NumberFormatException: ", ex); 993 Tools.showError(display.getShells()[0], "Update", ex.getMessage()); 994 return; 995 } 996 997 if (isUnsigned && (d < 0)) { 998 log.debug("updateAttributeValue(): Negative value for unsigned integer: {}", theToken); 999 Tools.showError(display.getShells()[0], "Update", "Negative value for unsigned integer: " + theToken); 1000 return; 1001 } 1002 1003 switch (cNT) { 1004 case 'B': { 1005 if (isUnsigned) { 1006 min = 0; 1007 max = 255; 1008 } 1009 else { 1010 min = Byte.MIN_VALUE; 1011 max = Byte.MAX_VALUE; 1012 } 1013 1014 if ((d > max) || (d < min)) { 1015 Tools.showError(display.getShells()[0], "Update", 1016 "Data is out of range[" + min + ", " + max + "]: " + theToken); 1017 } 1018 else { 1019 Array.setByte(data, i, (byte) d); 1020 } 1021 break; 1022 } 1023 case 'S': { 1024 if (isUnsigned) { 1025 min = 0; 1026 max = 65535; 1027 } 1028 else { 1029 min = Short.MIN_VALUE; 1030 max = Short.MAX_VALUE; 1031 } 1032 1033 if ((d > max) || (d < min)) { 1034 Tools.showError(display.getShells()[0], "Update", 1035 "Data is out of range[" + min + ", " + max + "]: " + theToken); 1036 } 1037 else { 1038 Array.setShort(data, i, (short) d); 1039 } 1040 break; 1041 } 1042 case 'I': { 1043 if (isUnsigned) { 1044 min = 0; 1045 max = 4294967295L; 1046 } 1047 else { 1048 min = Integer.MIN_VALUE; 1049 max = Integer.MAX_VALUE; 1050 } 1051 1052 if ((d > max) || (d < min)) { 1053 Tools.showError(display.getShells()[0], "Update", 1054 "Data is out of range[" + min + ", " + max + "]: " + theToken); 1055 } 1056 else { 1057 Array.setInt(data, i, (int) d); 1058 } 1059 break; 1060 } 1061 case 'J': 1062 long lvalue = 0; 1063 if (isUnsigned) { 1064 if (theToken != null) { 1065 String theValue = theToken; 1066 BigInteger maxJ = new BigInteger("18446744073709551615"); 1067 BigInteger big = new BigInteger(theValue); 1068 if ((big.compareTo(maxJ) > 0) || (big.compareTo(BigInteger.ZERO) < 0)) { 1069 Tools.showError(display.getShells()[0], "Update", 1070 "Data is out of range[" + min + ", " + max + "]: " + theToken); 1071 } 1072 lvalue = big.longValue(); 1073 log.trace("updateAttributeValue(): big.longValue={}", lvalue); 1074 Array.setLong(data, i, lvalue); 1075 } 1076 else 1077 Array.set(data, i, theToken); 1078 } 1079 else { 1080 min = Long.MIN_VALUE; 1081 max = Long.MAX_VALUE; 1082 if ((d > max) || (d < min)) { 1083 Tools.showError(display.getShells()[0], "Update", 1084 "Data is out of range[" + min + ", " + max + "]: " + theToken); 1085 } 1086 lvalue = (long) d; 1087 log.trace("updateAttributeValue(): longValue={}", lvalue); 1088 Array.setLong(data, i, lvalue); 1089 } 1090 break; 1091 case 'F': 1092 Array.setFloat(data, i, (float) d); 1093 break; 1094 case 'D': 1095 Array.setDouble(data, i, d); 1096 break; 1097 default: 1098 Array.set(data, i, theToken); 1099 break; 1100 } 1101 } 1102 1103 try { 1104 dataObject.getFileFormat().writeAttribute(dataObject, attr, true); 1105 } 1106 catch (Exception ex) { 1107 log.debug("updateAttributeValue(): writeAttribute failure: ", ex); 1108 Tools.showError(display.getShells()[0], "Update", ex.getMessage()); 1109 return; 1110 } 1111 1112 /* Update the attribute table */ 1113 int selectionIndex = attrTable.getSelectionIndex(); 1114 if (selectionIndex < 0) { 1115 Tools.showError(Display.getDefault().getShells()[0], "Update", "No Attribute selected"); 1116 return; 1117 } 1118 1119 attrTable.getItem(selectionIndex).setText(3, attr.toAttributeString(", ")); 1120 1121 if (dataObject instanceof MetaDataContainer) { 1122 try { 1123 ((MetaDataContainer) dataObject).updateMetadata(attr); 1124 } 1125 catch (Exception ex) { 1126 log.debug("updateAttributeValue(): updateMetadata() failure:", ex); 1127 Tools.showError(display.getShells()[0], "Update", ex.getMessage()); 1128 } 1129 } 1130 } 1131 1132 private void addAttributeTableItem(Table table, Attribute attr) { 1133 if (table == null || attr == null) { 1134 log.debug("addAttributeTableItem(): table or attribute is null"); 1135 return; 1136 } 1137 1138 String attrName = attr.getAttributeName(); 1139 String attrType = attr.getAttributeDatatype().getDescription(); 1140 StringBuilder attrSize = new StringBuilder(); 1141 String attrValue = attr.toAttributeString(", ", 50); 1142 String[] rowData = new String[attrTableColNames.length]; 1143 1144 if (attrName == null) attrName = "null"; 1145 if (attrType == null) attrType = "null"; 1146 if (attrValue == null) attrValue = "null"; 1147 1148 TableItem item = new TableItem(attrTable, SWT.NONE); 1149 item.setFont(curFont); 1150 item.setData(attr); 1151 1152 if (attr.getProperty("field") != null) { 1153 rowData[0] = attrName + " {Field: " + attr.getProperty("field") + "}"; 1154 } 1155 else { 1156 rowData[0] = attrName; 1157 } 1158 1159 if (attr.isAttributeScalar()) { 1160 attrSize.append("Scalar"); 1161 } 1162 else { 1163 long[] dims = attr.getAttributeDims(); 1164 attrSize.append(String.valueOf(dims[0])); 1165 for (int j = 1; j < dims.length; j++) { 1166 attrSize.append(" x ").append(dims[j]); 1167 } 1168 } 1169 1170 rowData[1] = attrType; 1171 rowData[2] = attrSize.toString(); 1172 rowData[3] = attrValue; 1173 1174 item.setText(rowData); 1175 } 1176 1177 private class UserBlockDialog extends Dialog { 1178 private Shell shell; 1179 1180 private final HObject obj; 1181 1182 private byte[] userBlock; 1183 1184 private final String[] displayChoices = { "Text", "Binary", "Octal", "Hexadecimal", "Decimal" }; 1185 1186 private Button jamButton; 1187 private Text userBlockArea; 1188 1189 public UserBlockDialog(Shell parent, int style, HObject obj) { 1190 super(parent, style); 1191 1192 this.obj = obj; 1193 1194 userBlock = Tools.getHDF5UserBlock(obj.getFile()); 1195 } 1196 1197 public void open() { 1198 Shell openParent = getParent(); 1199 shell = new Shell(openParent, SWT.DIALOG_TRIM | SWT.RESIZE); 1200 shell.setFont(curFont); 1201 shell.setText("User Block - " + obj); 1202 shell.setLayout(new GridLayout(5, false)); 1203 1204 Label label = new Label(shell, SWT.RIGHT); 1205 label.setFont(curFont); 1206 label.setText("Display As: "); 1207 1208 Combo userBlockDisplayChoice = new Combo(shell, SWT.SINGLE | SWT.READ_ONLY); 1209 userBlockDisplayChoice.setFont(curFont); 1210 userBlockDisplayChoice.setItems(displayChoices); 1211 userBlockDisplayChoice.select(0); 1212 userBlockDisplayChoice.addSelectionListener(new SelectionAdapter() { 1213 @Override 1214 public void widgetSelected(SelectionEvent e) { 1215 Combo source = (Combo) e.widget; 1216 int type = 0; 1217 1218 String typeName = source.getItem(source.getSelectionIndex()); 1219 1220 jamButton.setEnabled(false); 1221 userBlockArea.setEditable(false); 1222 1223 if (typeName.equalsIgnoreCase("Text")) { 1224 type = 0; 1225 jamButton.setEnabled(true); 1226 userBlockArea.setEditable(true); 1227 } 1228 else if (typeName.equalsIgnoreCase("Binary")) { 1229 type = 2; 1230 } 1231 else if (typeName.equalsIgnoreCase("Octal")) { 1232 type = 8; 1233 } 1234 else if (typeName.equalsIgnoreCase("Hexadecimal")) { 1235 type = 16; 1236 } 1237 else if (typeName.equalsIgnoreCase("Decimal")) { 1238 type = 10; 1239 } 1240 1241 showUserBlockAs(type); 1242 } 1243 }); 1244 1245 Label dummyLabel = new Label(shell, SWT.RIGHT); 1246 dummyLabel.setFont(curFont); 1247 dummyLabel.setText(""); 1248 dummyLabel.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false)); 1249 1250 Label sizeLabel = new Label(shell, SWT.RIGHT); 1251 sizeLabel.setFont(curFont); 1252 sizeLabel.setText("Header Size (Bytes): 0"); 1253 1254 jamButton = new Button(shell, SWT.PUSH); 1255 jamButton.setFont(curFont); 1256 jamButton.setText("Save User Block"); 1257 jamButton.setLayoutData(new GridData(SWT.FILL, SWT.FILL, false, false)); 1258 jamButton.addSelectionListener(new SelectionAdapter() { 1259 @Override 1260 public void widgetSelected(SelectionEvent e) { 1261 writeUserBlock(); 1262 } 1263 }); 1264 1265 ScrolledComposite userBlockScroller = new ScrolledComposite(shell, SWT.V_SCROLL | SWT.BORDER); 1266 userBlockScroller.setExpandHorizontal(true); 1267 userBlockScroller.setExpandVertical(true); 1268 userBlockScroller.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true, 5, 1)); 1269 1270 userBlockArea = new Text(userBlockScroller, SWT.MULTI | SWT.WRAP); 1271 userBlockArea.setEditable(true); 1272 userBlockArea.setFont(curFont); 1273 userBlockScroller.setContent(userBlockArea); 1274 1275 Button closeButton = new Button(shell, SWT.CENTER); 1276 closeButton.setFont(curFont); 1277 closeButton.setText(" &Close "); 1278 closeButton.setLayoutData(new GridData(SWT.CENTER, SWT.FILL, true, false, 5, 1)); 1279 closeButton.addSelectionListener(new SelectionAdapter() { 1280 @Override 1281 public void widgetSelected(SelectionEvent e) { 1282 shell.dispose(); 1283 } 1284 }); 1285 1286 if (userBlock != null) { 1287 int headSize = showUserBlockAs(0); 1288 sizeLabel.setText("Header Size (Bytes): " + headSize); 1289 } 1290 else { 1291 userBlockDisplayChoice.setEnabled(false); 1292 } 1293 1294 shell.pack(); 1295 1296 Rectangle parentBounds = openParent.getBounds(); 1297 1298 Point shellSize = new Point((int) (0.5 * parentBounds.width), (int) (0.5 * parentBounds.height)); 1299 shell.setSize(shellSize); 1300 1301 shell.setLocation((parentBounds.x + (parentBounds.width / 2)) - (shellSize.x / 2), 1302 (parentBounds.y + (parentBounds.height / 2)) - (shellSize.y / 2)); 1303 1304 shell.open(); 1305 1306 Display openDisplay = openParent.getDisplay(); 1307 while (!shell.isDisposed()) { 1308 if (!openDisplay.readAndDispatch()) openDisplay.sleep(); 1309 } 1310 } 1311 1312 private int showUserBlockAs(int radix) { 1313 if (userBlock == null) return 0; 1314 1315 int headerSize = 0; 1316 1317 String userBlockInfo = null; 1318 if ((radix == 2) || (radix == 8) || (radix == 16) || (radix == 10)) { 1319 StringBuilder sb = new StringBuilder(); 1320 for (headerSize = 0; headerSize < userBlock.length; headerSize++) { 1321 int intValue = userBlock[headerSize]; 1322 if (intValue < 0) { 1323 intValue += 256; 1324 } 1325 else if (intValue == 0) { 1326 break; // null end 1327 } 1328 1329 sb.append(Integer.toString(intValue, radix)).append(" "); 1330 } 1331 userBlockInfo = sb.toString(); 1332 } 1333 else { 1334 userBlockInfo = new String(userBlock).trim(); 1335 if (userBlockInfo != null) { 1336 headerSize = userBlockInfo.length(); 1337 } 1338 } 1339 1340 userBlockArea.setText(userBlockInfo); 1341 1342 return headerSize; 1343 } 1344 1345 private void writeUserBlock() { 1346 if (!obj.getFileFormat().isThisType(FileFormat.getFileFormat(FileFormat.FILE_TYPE_HDF5))) { 1347 return; 1348 } 1349 1350 int blkSize0 = 0; 1351 if (userBlock != null) { 1352 blkSize0 = userBlock.length; 1353 // The super block space is allocated by offset 0, 512, 1024, 2048, etc 1354 if (blkSize0 > 0) { 1355 int offset = 512; 1356 while (offset < blkSize0) { 1357 offset *= 2; 1358 } 1359 blkSize0 = offset; 1360 } 1361 } 1362 1363 int blkSize1 = 0; 1364 String userBlockStr = userBlockArea.getText(); 1365 if (userBlockStr == null) { 1366 if (blkSize0 <= 0) { 1367 return; // nothing to write 1368 } 1369 else { 1370 userBlockStr = " "; // want to wipe out old userblock content 1371 } 1372 } 1373 byte[] buf = null; 1374 buf = userBlockStr.getBytes(); 1375 1376 blkSize1 = buf.length; 1377 if (blkSize1 <= blkSize0) { 1378 java.io.RandomAccessFile raf = null; 1379 try { 1380 raf = new java.io.RandomAccessFile(obj.getFile(), "rw"); 1381 } 1382 catch (Exception ex) { 1383 Tools.showError(shell, "Save", "Can't open output file: " + obj.getFile()); 1384 return; 1385 } 1386 1387 try { 1388 raf.seek(0); 1389 raf.write(buf, 0, buf.length); 1390 raf.seek(buf.length); 1391 if (blkSize0 > buf.length) { 1392 byte[] padBuf = new byte[blkSize0 - buf.length]; 1393 raf.write(padBuf, 0, padBuf.length); 1394 } 1395 } 1396 catch (Exception ex) { 1397 log.debug("raf write:", ex); 1398 } 1399 try { 1400 raf.close(); 1401 } 1402 catch (Exception ex) { 1403 log.debug("raf close:", ex); 1404 } 1405 1406 Tools.showInformation(shell, "Save", "Saving user block is successful."); 1407 } 1408 else { 1409 // must rewrite the whole file 1410 MessageDialog confirm = new MessageDialog(shell, "Save", null, 1411 "The user block to write is " + blkSize1 + " (bytes),\n" 1412 + "which is larger than the user block space in file " + blkSize0 + " (bytes).\n" 1413 + "To expand the user block, the file must be rewritten.\n\n" 1414 + "Do you want to replace the current file? Click " 1415 + "\n\"Yes\" to replace the current file," + "\n\"No\" to save to a different file, " 1416 + "\n\"Cancel\" to quit without saving the change.\n\n ", 1417 MessageDialog.QUESTION_WITH_CANCEL, new String[] { "Yes", "No", "Cancel" }, 0); 1418 int op = confirm.open(); 1419 1420 if (op == 2) return; 1421 1422 String fin = obj.getFile(); 1423 1424 String fout = fin + "~copy.h5"; 1425 if (fin.endsWith(".h5")) { 1426 fout = fin.substring(0, fin.length() - 3) + "~copy.h5"; 1427 } 1428 else if (fin.endsWith(".hdf5")) { 1429 fout = fin.substring(0, fin.length() - 5) + "~copy.h5"; 1430 } 1431 1432 File outFile = null; 1433 1434 if (op == 1) { 1435 FileDialog fChooser = new FileDialog(shell, SWT.SAVE); 1436 fChooser.setFileName(fout); 1437 1438 DefaultFileFilter filter = DefaultFileFilter.getFileFilterHDF5(); 1439 fChooser.setFilterExtensions(new String[] { "*", filter.getExtensions() }); 1440 fChooser.setFilterNames(new String[] { "All Files", filter.getDescription() }); 1441 fChooser.setFilterIndex(1); 1442 1443 if (fChooser.open() == null) return; 1444 1445 File chosenFile = new File(fChooser.getFileName()); 1446 1447 outFile = chosenFile; 1448 fout = outFile.getAbsolutePath(); 1449 } 1450 else { 1451 outFile = new File(fout); 1452 } 1453 1454 if (!outFile.exists()) { 1455 try { 1456 if (!outFile.createNewFile()) 1457 log.debug("Error creating file {}", fout); 1458 } 1459 catch (Exception ex) { 1460 Tools.showError(shell, "Save", "Failed to write user block into file."); 1461 return; 1462 } 1463 } 1464 1465 // close the file 1466 TreeView view = viewManager.getTreeView(); 1467 1468 try { 1469 view.closeFile(view.getSelectedFile()); 1470 } 1471 catch (Exception ex) { 1472 log.debug("Error closing file {}", fin); 1473 } 1474 1475 if (Tools.setHDF5UserBlock(fin, fout, buf)) { 1476 if (op == 1) { 1477 fin = fout; // open the new file 1478 } 1479 else { 1480 File oldFile = new File(fin); 1481 boolean status = oldFile.delete(); 1482 if (status) { 1483 if (!outFile.renameTo(oldFile)) 1484 log.debug("Error renaming file {}", fout); 1485 } 1486 else { 1487 Tools.showError(shell, "Save", "Cannot replace the current file.\nPlease save to a different file."); 1488 outFile.delete(); 1489 } 1490 } 1491 } 1492 else { 1493 Tools.showError(shell, "Save", "Failed to write user block into file."); 1494 outFile.delete(); 1495 } 1496 1497 // reopen the file 1498 shell.dispose(); 1499 1500 try { 1501 int access_mode = FileFormat.WRITE; 1502 if (ViewProperties.isReadOnly()) 1503 access_mode = FileFormat.READ; 1504 else if (ViewProperties.isReadSWMR()) 1505 access_mode = FileFormat.READ | FileFormat.MULTIREAD; 1506 view.openFile(fin, access_mode); 1507 } 1508 catch (Exception ex) { 1509 log.debug("Error opening file {}", fin); 1510 } 1511 } 1512 } 1513 } 1514}