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.dialog; 016 017import java.io.InputStream; 018import java.math.BigInteger; 019import java.net.URL; 020import java.net.URLClassLoader; 021import java.util.Iterator; 022import java.util.List; 023import java.util.Scanner; 024import java.util.StringTokenizer; 025 026import org.eclipse.swt.SWT; 027import org.eclipse.swt.browser.Browser; 028import org.eclipse.swt.events.DisposeEvent; 029import org.eclipse.swt.events.DisposeListener; 030import org.eclipse.swt.events.SelectionAdapter; 031import org.eclipse.swt.events.SelectionEvent; 032import org.eclipse.swt.graphics.Point; 033import org.eclipse.swt.graphics.Rectangle; 034import org.eclipse.swt.layout.GridData; 035import org.eclipse.swt.layout.GridLayout; 036import org.eclipse.swt.widgets.Button; 037import org.eclipse.swt.widgets.Combo; 038import org.eclipse.swt.widgets.Composite; 039import org.eclipse.swt.widgets.Dialog; 040import org.eclipse.swt.widgets.Display; 041import org.eclipse.swt.widgets.Label; 042import org.eclipse.swt.widgets.Shell; 043import org.eclipse.swt.widgets.Text; 044 045import hdf.object.Attribute; 046import hdf.object.Datatype; 047import hdf.object.Group; 048import hdf.object.HObject; 049import hdf.object.MetaDataContainer; 050import hdf.view.Tools; 051import hdf.view.ViewProperties; 052 053/** 054 * NewAttributeDialog displays components for adding a new attribute. 055 * 056 * @author Jordan T. Henderson 057 * @version 2.4 1/7/2016 058 */ 059public class NewAttributeDialog extends NewDataObjectDialog { 060 061 private static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(NewAttributeDialog.class); 062 063 /** the default length of a string attribute */ 064 public static final int DEFAULT_STRING_ATTRIBUTE_LENGTH = 256; 065 066 /** TextField for entering the name of the dataset */ 067 private Text nameField; 068 069 /** TextField for entering the attribute value. */ 070 private Text valueField; 071 072 /** The Choice of the object list */ 073 private Combo objChoice; 074 075 private Button h4GrAttrRadioButton; 076 077 private Label arrayLengthLabel; 078 079 /** 080 * Constructs a NewAttributeDialog with specified object (dataset, group, or 081 * image) for the new attribute to be attached to. 082 * 083 * @param parent 084 * the parent shell of the dialog 085 * @param obj 086 * the object for the attribute to be attached to. 087 * @param objs 088 * the specified objects. 089 */ 090 public NewAttributeDialog(Shell parent, HObject obj, List<HObject> objs) { 091 super(parent, obj, objs); 092 } 093 094 public void open() { 095 Shell parent = getParent(); 096 shell = new Shell(parent, SWT.SHELL_TRIM | SWT.APPLICATION_MODAL); 097 shell.setFont(curFont); 098 shell.setText("New Attribute..."); 099 shell.setImage(ViewProperties.getHdfIcon()); 100 shell.setLayout(new GridLayout(1, true)); 101 102 103 // Create content region 104 Composite content = new Composite(shell, SWT.NONE); 105 content.setLayout(new GridLayout(2, false)); 106 content.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true)); 107 108 Label label = new Label(content, SWT.LEFT); 109 label.setFont(curFont); 110 label.setText("Name: "); 111 112 nameField = new Text(content, SWT.SINGLE | SWT.BORDER); 113 nameField.setFont(curFont); 114 nameField.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false)); 115 116 label = new Label(content, SWT.LEFT); 117 label.setFont(curFont); 118 label.setText("Type: "); 119 120 Composite optionsComposite = new Composite(content, SWT.NONE); 121 optionsComposite.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false)); 122 optionsComposite.setLayout(new GridLayout( 123 (!isH5 && (parentObj instanceof Group) && ((Group) parentObj).isRoot()) ? 5 : 3, 124 false) 125 ); 126 127 // Dummy label 128 label = new Label(optionsComposite, SWT.LEFT); 129 label.setFont(curFont); 130 label.setText(""); 131 132 if (!isH5 && (parentObj instanceof Group) && ((Group) parentObj).isRoot()) { 133 label = new Label(optionsComposite, SWT.LEFT); 134 label.setFont(curFont); 135 label.setText(""); 136 137 label = new Label(optionsComposite, SWT.LEFT); 138 label.setFont(curFont); 139 label.setText(""); 140 } 141 142 createDatatypeWidget(); 143 144 if (!isH5 && (parentObj instanceof Group) && ((Group) parentObj).isRoot()) { 145 Button h4SdAttrRadioButton = new Button(optionsComposite, SWT.RADIO); 146 h4SdAttrRadioButton.setFont(curFont); 147 h4SdAttrRadioButton.setText("SD"); 148 h4SdAttrRadioButton.setSelection(true); 149 150 h4GrAttrRadioButton = new Button(optionsComposite, SWT.RADIO); 151 h4GrAttrRadioButton.setFont(curFont); 152 h4GrAttrRadioButton.setText("GR"); 153 } 154 155 arrayLengthLabel = new Label(content, SWT.LEFT); 156 arrayLengthLabel.setFont(curFont); 157 arrayLengthLabel.setText("Array Size: "); 158 159 lengthField = new Text(content, SWT.SINGLE | SWT.BORDER); 160 lengthField.setFont(curFont); 161 lengthField.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false)); 162 lengthField.setTextLimit(30); 163 lengthField.setText("1"); 164 165 label = new Label(content, SWT.LEFT); 166 label.setFont(curFont); 167 label.setText("Value: "); 168 169 valueField = new Text(content, SWT.SINGLE | SWT.BORDER); 170 valueField.setFont(curFont); 171 valueField.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false)); 172 valueField.setText("0"); 173 174 label = new Label(content, SWT.LEFT); 175 label.setFont(curFont); 176 label.setText("Object List: "); 177 178 objChoice = new Combo(content, SWT.DROP_DOWN | SWT.READ_ONLY); 179 objChoice.setFont(curFont); 180 objChoice.setEnabled(true); 181 objChoice.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false)); 182 objChoice.addSelectionListener(new SelectionAdapter() { 183 @Override 184 public void widgetSelected(SelectionEvent e) { 185 String objName = objChoice.getItem(objChoice.getSelectionIndex()); 186 187 long ref = -1; 188 try { 189 HObject obj = fileFormat.get(objName); 190 ref = obj.getOID()[0]; 191 } 192 catch (Exception ex) { 193 log.debug("object id:", ex); 194 } 195 196 if (ref > 0) { 197 if (valueField.getText().length() > 1) { 198 valueField.setText(valueField.getText() + "," + ref); 199 StringTokenizer st = new StringTokenizer(valueField.getText(), ","); 200 lengthField.setText(String.valueOf(st.countTokens())); 201 } 202 else { 203 valueField.setText(String.valueOf(ref)); 204 lengthField.setText("1"); 205 } 206 } 207 } 208 }); 209 210 Iterator<?> it = objList.iterator(); 211 HObject hobj; 212 while (it.hasNext()) { 213 hobj = (HObject) it.next(); 214 215 if (hobj instanceof Group) { 216 if (((Group) hobj).isRoot()) continue; 217 } 218 219 objChoice.add(hobj.getFullName()); 220 } 221 222 // Add label to take up extra space when resizing dialog 223 label = new Label(content, SWT.LEFT); 224 label.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true, 2, 1)); 225 226 // Create Ok/Cancel/Help button region 227 Composite buttonComposite = new Composite(shell, SWT.NONE); 228 buttonComposite.setLayout(new GridLayout(3, false)); 229 buttonComposite.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false)); 230 231 Button okButton = new Button(buttonComposite, SWT.PUSH); 232 okButton.setFont(curFont); 233 okButton.setText(" &OK "); 234 okButton.setLayoutData(new GridData(SWT.END, SWT.FILL, true, false)); 235 okButton.addSelectionListener(new SelectionAdapter() { 236 @Override 237 public void widgetSelected(SelectionEvent e) { 238 if (createAttribute()) { 239 shell.dispose(); 240 } 241 } 242 }); 243 244 Button cancelButton = new Button(buttonComposite, SWT.PUSH); 245 cancelButton.setFont(curFont); 246 cancelButton.setText(" &Cancel "); 247 cancelButton.setLayoutData(new GridData(SWT.CENTER, SWT.FILL, false, false)); 248 cancelButton.addSelectionListener(new SelectionAdapter() { 249 @Override 250 public void widgetSelected(SelectionEvent e) { 251 newObject = null; 252 shell.dispose(); 253 } 254 }); 255 256 Button helpButton = new Button(buttonComposite, SWT.PUSH); 257 helpButton.setFont(curFont); 258 helpButton.setText(" &Help "); 259 helpButton.setLayoutData(new GridData(SWT.BEGINNING, SWT.FILL, true, false)); 260 helpButton.addSelectionListener(new SelectionAdapter() { 261 @Override 262 public void widgetSelected(SelectionEvent e) { 263 new HelpDialog(shell).open(); 264 } 265 }); 266 267 shell.pack(); 268 269 shell.addDisposeListener(new DisposeListener() { 270 @Override 271 public void widgetDisposed(DisposeEvent e) { 272 if (curFont != null) curFont.dispose(); 273 } 274 }); 275 276 shell.setMinimumSize(shell.computeSize(SWT.DEFAULT, SWT.DEFAULT)); 277 278 Rectangle parentBounds = parent.getBounds(); 279 Point shellSize = shell.getSize(); 280 shell.setLocation((parentBounds.x + (parentBounds.width / 2)) - (shellSize.x / 2), 281 (parentBounds.y + (parentBounds.height / 2)) - (shellSize.y / 2)); 282 283 shell.open(); 284 285 Display display = shell.getDisplay(); 286 while (!shell.isDisposed()) 287 if (!display.readAndDispatch()) 288 display.sleep(); 289 } 290 291 @SuppressWarnings("unchecked") 292 private boolean createAttribute() { 293 Object value = null; 294 String strValue = valueField.getText(); 295 296 String attrName = nameField.getText(); 297 if (attrName != null) { 298 attrName = attrName.trim(); 299 } 300 301 if ((attrName == null) || (attrName.length() < 1)) { 302 Tools.showError(shell, "Create", "No attribute name specified."); 303 return false; 304 } 305 306 String lengthStr = lengthField.getText(); 307 log.trace("Name is {} : Length={} and Value={}", attrName, lengthStr, strValue); 308 309 int arraySize = 0; 310 if ((lengthStr == null) || (lengthStr.length() <= 0)) { 311 arraySize = 1; 312 } 313 else { 314 try { 315 arraySize = Integer.parseInt(lengthStr); 316 } 317 catch (Exception e) { 318 arraySize = -1; 319 } 320 } 321 322 if (arraySize <= 0) { 323 Tools.showError(shell, "Create", "Invalid attribute length."); 324 return false; 325 } 326 327 StringTokenizer st = new StringTokenizer(strValue, ","); 328 int count = Math.min(arraySize, st.countTokens()); 329 String theToken; 330 log.trace("Count of Values is {}", count); 331 332 // set datatype class 333 Datatype datatype = super.createNewDatatype(null); 334 if (isVLen) { 335 log.trace("Attribute isVLen={} and tsize={}", isVLen, tsize); 336 String[] strArray = { strValue }; 337 value = strArray; 338 } 339 else { 340 if (tclass == Datatype.CLASS_STRING) { 341 if (!isVlenStr) { 342 if (strValue.length() > tsize) { 343 strValue = strValue.substring(0, tsize); 344 } 345 } 346 347 String[] strArray = { strValue }; 348 value = strArray; 349 350 if (isH5) { 351 arraySize = 1; // support string type 352 } 353 else { 354 arraySize = tsize; // array of characters 355 } 356 log.trace("Attribute CLASS_STRING: isVLen={} and tsize={} and arraySize={}", isVLen, tsize, arraySize); 357 } 358 else if (tclass == Datatype.CLASS_REFERENCE) { 359 arraySize = st.countTokens(); 360 long[] ref = new long[arraySize]; 361 for (int j = 0; j < arraySize; j++) { 362 theToken = st.nextToken().trim(); 363 try { 364 ref[j] = Long.parseLong(theToken); 365 } 366 catch (NumberFormatException ex) { 367 Tools.showError(shell, "Create", ex.getMessage()); 368 return false; 369 } 370 } 371 372 value = ref; 373 log.trace("Attribute CLASS_REFERENCE: tsize={} and arraySize={}", tsize, arraySize); 374 } 375 else if (tclass == Datatype.CLASS_INTEGER) { 376 if (tsign == Datatype.SIGN_NONE) { 377 if (tsize == 1) { 378 byte[] b = new byte[arraySize]; 379 short sv = 0; 380 for (int j = 0; j < count; j++) { 381 theToken = st.nextToken().trim(); 382 try { 383 sv = Short.parseShort(theToken); 384 } 385 catch (NumberFormatException ex) { 386 Tools.showError(shell, "Create", ex.getMessage()); 387 return false; 388 } 389 if (sv < 0) { 390 sv = 0; 391 } 392 else if (sv > 255) { 393 sv = 255; 394 } 395 b[j] = (byte) sv; 396 } 397 value = b; 398 } 399 else if (tsize == 2) { 400 short[] s = new short[arraySize]; 401 int iv = 0; 402 for (int j = 0; j < count; j++) { 403 theToken = st.nextToken().trim(); 404 try { 405 iv = Integer.parseInt(theToken); 406 } 407 catch (NumberFormatException ex) { 408 Tools.showError(shell, "Create", ex.getMessage()); 409 return false; 410 } 411 if (iv < 0) { 412 iv = 0; 413 } 414 else if (iv > 65535) { 415 iv = 65535; 416 } 417 s[j] = (short) iv; 418 } 419 value = s; 420 } 421 else if (tsize == 4) { 422 int[] i = new int[arraySize]; 423 long lv = 0; 424 for (int j = 0; j < count; j++) { 425 theToken = st.nextToken().trim(); 426 try { 427 lv = Long.parseLong(theToken); 428 } 429 catch (NumberFormatException ex) { 430 Tools.showError(shell, "Create", ex.getMessage()); 431 return false; 432 } 433 if (lv < 0) { 434 lv = 0; 435 } 436 if (lv > 4294967295L) { 437 lv = 4294967295L; 438 } 439 i[j] = (int) lv; 440 } 441 value = i; 442 } 443 else if (tsize == 8) { 444 long[] i = new long[arraySize]; 445 BigInteger lv = BigInteger.valueOf(0); 446 for (int j = 0; j < count; j++) { 447 theToken = st.nextToken().trim(); 448 try { 449 lv = new BigInteger(theToken); 450 } 451 catch (NumberFormatException ex) { 452 Tools.showError(shell, "Create", ex.getMessage()); 453 return false; 454 } 455 i[j] = lv.longValue(); 456 } 457 value = i; 458 } 459 } 460 else { 461 if (tsize == 1) { 462 byte[] b = new byte[arraySize]; 463 for (int j = 0; j < count; j++) { 464 theToken = st.nextToken().trim(); 465 try { 466 b[j] = Byte.parseByte(theToken); 467 } 468 catch (NumberFormatException ex) { 469 Tools.showError(shell, "Create", ex.getMessage()); 470 return false; 471 } 472 } 473 value = b; 474 } 475 else if (tsize == 2) { 476 short[] s = new short[arraySize]; 477 478 for (int j = 0; j < count; j++) { 479 theToken = st.nextToken().trim(); 480 try { 481 s[j] = Short.parseShort(theToken); 482 } 483 catch (NumberFormatException ex) { 484 Tools.showError(shell, "Create", ex.getMessage()); 485 return false; 486 } 487 } 488 value = s; 489 } 490 else if (tsize == 4) { 491 int[] i = new int[arraySize]; 492 493 for (int j = 0; j < count; j++) { 494 theToken = st.nextToken().trim(); 495 try { 496 i[j] = Integer.parseInt(theToken); 497 } 498 catch (NumberFormatException ex) { 499 Tools.showError(shell, "Create", ex.getMessage()); 500 return false; 501 } 502 } 503 value = i; 504 } 505 else if (tsize == 8) { 506 long[] l = new long[arraySize]; 507 for (int j = 0; j < count; j++) { 508 theToken = st.nextToken().trim(); 509 try { 510 l[j] = Long.parseLong(theToken); 511 } 512 catch (NumberFormatException ex) { 513 Tools.showError(shell, "Create", ex.getMessage()); 514 return false; 515 } 516 } 517 value = l; 518 } 519 } 520 } 521 522 if (tclass == Datatype.CLASS_FLOAT) { 523 if (tsize == 4) { 524 float[] f = new float[arraySize]; 525 for (int j = 0; j < count; j++) { 526 theToken = st.nextToken().trim(); 527 try { 528 f[j] = Float.parseFloat(theToken); 529 } 530 catch (NumberFormatException ex) { 531 Tools.showError(shell, "Create", ex.getMessage()); 532 return false; 533 } 534 if (Float.isInfinite(f[j]) || Float.isNaN(f[j])) { 535 f[j] = 0; 536 } 537 } 538 value = f; 539 } 540 else if (tsize == 8) { 541 double[] d = new double[arraySize]; 542 for (int j = 0; j < count; j++) { 543 theToken = st.nextToken().trim(); 544 try { 545 d[j] = Double.parseDouble(theToken); 546 } 547 catch (NumberFormatException ex) { 548 Tools.showError(shell, "Create", ex.getMessage()); 549 return false; 550 } 551 if (Double.isInfinite(d[j]) || Double.isNaN(d[j])) { 552 d[j] = 0; 553 } 554 } 555 value = d; 556 } 557 } 558 } 559 560 long[] dims = { arraySize }; 561 Attribute attr = new Attribute(parentObj, attrName, datatype, dims); 562 attr.setData(value); 563 564 try { 565 parentObj.getFileFormat().writeAttribute(parentObj, attr, false); 566 if (!isH5 && (parentObj instanceof Group) && ((Group) parentObj).isRoot() && h4GrAttrRadioButton.getSelection()) { 567 // don't find a good way to write HDF4 global 568 // attribute. Use the isExisted to separate the 569 // global attribute is GR or SD 570 571 if (((MetaDataContainer) parentObj).getMetadata() == null) { 572 ((MetaDataContainer) parentObj).getMetadata().add(attr); 573 } 574 } 575 else { 576 log.trace("writeMetadata()"); 577 attr.write(); 578 } 579 } 580 catch (Exception ex) { 581 Tools.showError(shell, "Create", ex.getMessage()); 582 log.debug("createAttribute(): ", ex); 583 return false; 584 } 585 586 newObject = attr; 587 588 return true; 589 } 590 591 private class HelpDialog extends Dialog { 592 private Shell helpShell; 593 594 public HelpDialog(Shell parent) { 595 super(parent, SWT.APPLICATION_MODAL); 596 } 597 598 public void open() { 599 Shell parent = getParent(); 600 helpShell = new Shell(parent, SWT.TITLE | SWT.CLOSE | 601 SWT.RESIZE | SWT.BORDER | SWT.APPLICATION_MODAL); 602 helpShell.setFont(curFont); 603 helpShell.setText("Create New Attribute"); 604 helpShell.setImage(ViewProperties.getHdfIcon()); 605 helpShell.setLayout(new GridLayout(1, true)); 606 607 // Try to create a Browser on platforms that support it 608 try { 609 Browser browser = new Browser(helpShell, SWT.NONE); 610 browser.setFont(curFont); 611 browser.setBounds(0, 0, 500, 500); 612 browser.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true)); 613 614 if (ClassLoader.getSystemResource("hdf/view/HDFView.class").toString().startsWith("jar")) { 615 // Attempt to load HTML help file from jar 616 try (InputStream in = getClass().getClassLoader().getResourceAsStream("hdf/view/NewAttrHelp.html")) { 617 Scanner scan = new Scanner(in); 618 StringBuilder buffer = new StringBuilder(); 619 while(scan.hasNextLine()) { 620 buffer.append(scan.nextLine()); 621 } 622 623 browser.setText(buffer.toString()); 624 625 scan.close(); 626 } 627 catch (Exception e) { 628 StringBuilder buff = new StringBuilder(); 629 buff.append("<html>") 630 .append("<body>") 631 .append("ERROR: cannot load help information.") 632 .append("</body>") 633 .append("</html>"); 634 browser.setText(buff.toString(), true); 635 } 636 } 637 else { 638 try { 639 URL url = null, url2 = null, url3 = null; 640 String rootPath = ViewProperties.getViewRoot(); 641 642 try { 643 url = new URL("file://" + rootPath + "/HDFView.jar"); 644 } 645 catch (java.net.MalformedURLException mfu) { 646 log.debug("help information:", mfu); 647 } 648 649 try { 650 url2 = new URL("file://" + rootPath + "/"); 651 } 652 catch (java.net.MalformedURLException mfu) { 653 log.debug("help information:", mfu); 654 } 655 656 try { 657 url3 = new URL("file://" + rootPath + "/src/"); 658 } 659 catch (java.net.MalformedURLException mfu) { 660 log.debug("help information:", mfu); 661 } 662 663 URL uu[] = { url, url2, url3 }; 664 try (URLClassLoader cl = new URLClassLoader(uu)) { 665 URL u = cl.findResource("hdf/view/NewAttrHelp.html"); 666 667 browser.setUrl(u.toString()); 668 } 669 catch (Exception ex) { 670 log.trace("URLClassLoader failed:", ex); 671 } 672 } 673 catch (Exception e) { 674 StringBuilder buff = new StringBuilder(); 675 buff.append("<html>") 676 .append("<body>") 677 .append("ERROR: cannot load help information.") 678 .append("</body>") 679 .append("</html>"); 680 browser.setText(buff.toString(), true); 681 } 682 } 683 684 Button okButton = new Button(helpShell, SWT.PUSH); 685 okButton.setFont(curFont); 686 okButton.setText(" &OK "); 687 okButton.setLayoutData(new GridData(SWT.CENTER, SWT.FILL, true, false)); 688 okButton.addSelectionListener(new SelectionAdapter() { 689 @Override 690 public void widgetSelected(SelectionEvent e) { 691 helpShell.dispose(); 692 } 693 }); 694 695 helpShell.pack(); 696 697 helpShell.setSize(new Point(500, 500)); 698 699 Rectangle parentBounds = parent.getBounds(); 700 Point shellSize = helpShell.getSize(); 701 helpShell.setLocation((parentBounds.x + (parentBounds.width / 2)) - (shellSize.x / 2), 702 (parentBounds.y + (parentBounds.height / 2)) - (shellSize.y / 2)); 703 704 helpShell.open(); 705 706 Display display = parent.getDisplay(); 707 while(!helpShell.isDisposed()) { 708 if (!display.readAndDispatch()) 709 display.sleep(); 710 } 711 } 712 catch (Error er) { 713 // Try opening help link in external browser if platform 714 // doesn't support SWT browser 715 Tools.showError(shell, "Browser support", 716 "Platform doesn't support Browser. Opening external link in web browser..."); 717 718 //TODO: Add support for launching in external browser 719 } 720 catch (Exception ex) { 721 log.debug("Open New Attribute Help failure: ", ex); 722 } 723 } 724 } 725 726 /** @return the new attribute created. */ 727 public Attribute getAttribute() { 728 return (Attribute)newObject; 729 } 730}