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 COPYING file, which can be found * 009 * at the root of the source code distribution tree, * 010 * or in https://www.hdfgroup.org/licenses. * 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 hdf.object.Attribute; 027import hdf.object.Datatype; 028import hdf.object.Group; 029import hdf.object.HObject; 030import hdf.object.MetaDataContainer; 031import hdf.object.h5.H5CompoundAttr; 032import hdf.object.h5.H5Datatype; 033import hdf.object.h5.H5ScalarAttr; 034import hdf.view.Tools; 035import hdf.view.ViewProperties; 036 037import org.eclipse.swt.SWT; 038import org.eclipse.swt.browser.Browser; 039import org.eclipse.swt.events.DisposeEvent; 040import org.eclipse.swt.events.DisposeListener; 041import org.eclipse.swt.events.SelectionAdapter; 042import org.eclipse.swt.events.SelectionEvent; 043import org.eclipse.swt.graphics.Point; 044import org.eclipse.swt.graphics.Rectangle; 045import org.eclipse.swt.layout.GridData; 046import org.eclipse.swt.layout.GridLayout; 047import org.eclipse.swt.widgets.Button; 048import org.eclipse.swt.widgets.Combo; 049import org.eclipse.swt.widgets.Composite; 050import org.eclipse.swt.widgets.Dialog; 051import org.eclipse.swt.widgets.Display; 052import org.eclipse.swt.widgets.Label; 053import org.eclipse.swt.widgets.Shell; 054import org.eclipse.swt.widgets.Text; 055 056/** 057 * NewScalarAttributeDialog displays components for adding a new attribute. 058 * 059 * @author Jordan T. Henderson 060 * @version 2.4 1/7/2016 061 */ 062public class NewScalarAttributeDialog extends NewDataObjectDialog { 063 064 private static final org.slf4j.Logger log = 065 org.slf4j.LoggerFactory.getLogger(NewScalarAttributeDialog.class); 066 067 /** the default length of a string attribute */ 068 public static final int DEFAULT_STRING_ATTRIBUTE_LENGTH = 256; 069 070 private Text currentSizeField; 071 072 private Combo rankChoice; 073 074 /** TextField for entering the name of the attribute */ 075 protected Text nameField; 076 077 /** 078 * Constructs a NewScalarAttributeDialog with specified object (dataset, group, or 079 * image) for the new attribute to be attached to. 080 * 081 * @param parent 082 * the parent shell of the dialog 083 * @param pObject 084 * the parent object which the new attribute is attached to. 085 * @param objs 086 * the list of all objects. 087 */ 088 public NewScalarAttributeDialog(Shell parent, HObject pObject, List<HObject> objs) 089 { 090 super(parent, pObject, objs); 091 } 092 093 /** 094 * Open the NewScalarAttributeDialog for adding a new attribute. 095 */ 096 public void open() 097 { 098 Shell parent = getParent(); 099 shell = new Shell(parent, SWT.SHELL_TRIM | SWT.APPLICATION_MODAL); 100 shell.setFont(curFont); 101 shell.setText("New Attribute..."); 102 shell.setImages(ViewProperties.getHdfIcons()); 103 shell.setLayout(new GridLayout(1, true)); 104 105 // Create Attribute name / Parent Object region 106 Composite fieldComposite = new Composite(shell, SWT.NONE); 107 fieldComposite.setLayout(new GridLayout(2, false)); 108 fieldComposite.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true)); 109 110 Label attributeNameLabel = new Label(fieldComposite, SWT.LEFT); 111 attributeNameLabel.setFont(curFont); 112 attributeNameLabel.setText("Attribute name: "); 113 114 nameField = new Text(fieldComposite, SWT.SINGLE | SWT.BORDER); 115 nameField.setFont(curFont); 116 GridData data = new GridData(SWT.FILL, SWT.FILL, true, false); 117 118 data.minimumWidth = 250; 119 nameField.setLayoutData(data); 120 121 Label parentObjectLabel = new Label(fieldComposite, SWT.LEFT); 122 parentObjectLabel.setFont(curFont); 123 parentObjectLabel.setText("Parent Object: "); 124 125 // Create Datatype region 126 createDatatypeWidget(); 127 128 // Create Dataspace region 129 org.eclipse.swt.widgets.Group dataspaceGroup = new org.eclipse.swt.widgets.Group(shell, SWT.NONE); 130 dataspaceGroup.setFont(curFont); 131 dataspaceGroup.setText("Dataspace"); 132 dataspaceGroup.setLayout(new GridLayout(3, true)); 133 dataspaceGroup.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true)); 134 135 Label label = new Label(dataspaceGroup, SWT.LEFT); 136 label.setFont(curFont); 137 label.setText("No. of dimensions"); 138 139 label = new Label(dataspaceGroup, SWT.LEFT); 140 label.setFont(curFont); 141 label.setText("Current size"); 142 143 // Dummy label 144 label = new Label(dataspaceGroup, SWT.LEFT); 145 label.setFont(curFont); 146 label.setText(""); 147 148 rankChoice = new Combo(dataspaceGroup, SWT.DROP_DOWN | SWT.READ_ONLY); 149 rankChoice.setFont(curFont); 150 rankChoice.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false)); 151 rankChoice.addSelectionListener(new SelectionAdapter() { 152 @Override 153 public void widgetSelected(SelectionEvent e) 154 { 155 int rank = rankChoice.getSelectionIndex() + 1; 156 StringBuilder currentSizeStr = new StringBuilder("1"); 157 158 for (int i = 1; i < rank; i++) { 159 currentSizeStr.append(" x 1"); 160 } 161 162 currentSizeField.setText(currentSizeStr.toString()); 163 164 String currentStr = currentSizeField.getText(); 165 int idx = currentStr.lastIndexOf('x'); 166 } 167 }); 168 169 for (int i = 1; i < 33; i++) { 170 rankChoice.add(String.valueOf(i)); 171 } 172 rankChoice.select(1); 173 174 currentSizeField = new Text(dataspaceGroup, SWT.SINGLE | SWT.BORDER); 175 currentSizeField.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false)); 176 currentSizeField.setFont(curFont); 177 currentSizeField.setText("1 x 1"); 178 179 // Create Ok/Cancel/Help button region 180 Composite buttonComposite = new Composite(shell, SWT.NONE); 181 buttonComposite.setLayout(new GridLayout(3, false)); 182 buttonComposite.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false)); 183 184 Button okButton = new Button(buttonComposite, SWT.PUSH); 185 okButton.setFont(curFont); 186 okButton.setText(" &OK "); 187 okButton.setLayoutData(new GridData(SWT.END, SWT.FILL, true, false)); 188 okButton.addSelectionListener(new SelectionAdapter() { 189 @Override 190 public void widgetSelected(SelectionEvent e) 191 { 192 if (createAttribute()) { 193 shell.dispose(); 194 } 195 } 196 }); 197 198 Button cancelButton = new Button(buttonComposite, SWT.PUSH); 199 cancelButton.setFont(curFont); 200 cancelButton.setText(" &Cancel "); 201 cancelButton.setLayoutData(new GridData(SWT.CENTER, SWT.FILL, false, false)); 202 cancelButton.addSelectionListener(new SelectionAdapter() { 203 @Override 204 public void widgetSelected(SelectionEvent e) 205 { 206 newObject = null; 207 shell.dispose(); 208 } 209 }); 210 211 Button helpButton = new Button(buttonComposite, SWT.PUSH); 212 helpButton.setFont(curFont); 213 helpButton.setText(" &Help "); 214 helpButton.setLayoutData(new GridData(SWT.BEGINNING, SWT.FILL, true, false)); 215 helpButton.addSelectionListener(new SelectionAdapter() { 216 @Override 217 public void widgetSelected(SelectionEvent e) 218 { 219 new HelpDialog(shell).open(); 220 } 221 }); 222 223 shell.pack(); 224 225 shell.addDisposeListener(new DisposeListener() { 226 @Override 227 public void widgetDisposed(DisposeEvent e) 228 { 229 if (curFont != null) 230 curFont.dispose(); 231 } 232 }); 233 234 shell.setMinimumSize(shell.computeSize(SWT.DEFAULT, SWT.DEFAULT)); 235 236 Rectangle parentBounds = parent.getBounds(); 237 Point shellSize = shell.getSize(); 238 shell.setLocation((parentBounds.x + (parentBounds.width / 2)) - (shellSize.x / 2), 239 (parentBounds.y + (parentBounds.height / 2)) - (shellSize.y / 2)); 240 241 shell.open(); 242 243 Display display = shell.getDisplay(); 244 while (!shell.isDisposed()) 245 if (!display.readAndDispatch()) 246 display.sleep(); 247 } 248 249 /** Check if the dim size is valid */ 250 private void checkDimSize() 251 { 252 String dimStr = currentSizeField.getText(); 253 StringTokenizer stDim = new StringTokenizer(dimStr, "x"); 254 255 int rank = stDim.countTokens(); 256 long dim = 0; 257 for (int i = 0; i < rank; i++) { 258 String token = stDim.nextToken().trim(); 259 260 token = token.toLowerCase(); 261 try { 262 dim = Long.parseLong(token); 263 } 264 catch (NumberFormatException ex) { 265 shell.getDisplay().beep(); 266 Tools.showError(shell, "Check", "Invalid dimension size: " + dimStr); 267 return; 268 } 269 } // (int i = 0; i < rank; i++) 270 } 271 272 @SuppressWarnings("unchecked") 273 private boolean createAttribute() 274 { 275 String attrName = null; 276 int rank = -1; 277 long[] dims; 278 279 attrName = nameField.getText(); 280 if (attrName != null) { 281 attrName = attrName.trim(); 282 } 283 284 if ((attrName == null) || (attrName.length() < 1)) { 285 shell.getDisplay().beep(); 286 Tools.showError(shell, "Create", "Attribute name is not specified."); 287 return false; 288 } 289 290 rank = rankChoice.getSelectionIndex() + 1; 291 StringTokenizer st = new StringTokenizer(currentSizeField.getText(), "x"); 292 if (st.countTokens() < rank) { 293 shell.getDisplay().beep(); 294 Tools.showError(shell, "Create", 295 "Number of values in the current dimension size is less than " + rank); 296 return false; 297 } 298 299 long lsize = 1; // The total size 300 long l = 0; 301 dims = new long[rank]; 302 String token = null; 303 for (int i = 0; i < rank; i++) { 304 token = st.nextToken().trim(); 305 try { 306 l = Long.parseLong(token); 307 } 308 catch (NumberFormatException ex) { 309 shell.getDisplay().beep(); 310 Tools.showError(shell, "Create", "Invalid dimension size: " + currentSizeField.getText()); 311 return false; 312 } 313 314 if (l <= 0) { 315 shell.getDisplay().beep(); 316 Tools.showError(shell, "Create", "Dimension size must be greater than zero."); 317 return false; 318 } 319 320 dims[i] = l; 321 lsize *= l; 322 } 323 log.trace("Create: lsize={}", lsize); 324 325 Attribute attr = null; 326 try { 327 H5Datatype datatype = (H5Datatype)createNewDatatype(null); 328 329 if (datatype.isCompound()) 330 attr = (Attribute) new H5CompoundAttr(parentObj, attrName, datatype, dims); 331 else 332 attr = (Attribute) new H5ScalarAttr(parentObj, attrName, datatype, dims); 333 Object value = H5Datatype.allocateArray(datatype, (int)lsize); 334 attr.setAttributeData(value); 335 336 log.trace("writeMetadata() via write()"); 337 attr.writeAttribute(); 338 } 339 catch (Exception ex) { 340 Tools.showError(shell, "Create", ex.getMessage()); 341 log.debug("createAttribute(): ", ex); 342 return false; 343 } 344 345 newObject = (HObject)attr; 346 347 return true; 348 } 349 350 private class HelpDialog extends Dialog { 351 private Shell helpShell; 352 353 public HelpDialog(Shell parent) { super(parent, SWT.APPLICATION_MODAL); } 354 355 public void open() 356 { 357 Shell parent = getParent(); 358 helpShell = 359 new Shell(parent, SWT.TITLE | SWT.CLOSE | SWT.RESIZE | SWT.BORDER | SWT.APPLICATION_MODAL); 360 helpShell.setFont(curFont); 361 helpShell.setText("Create New Attribute"); 362 helpShell.setImages(ViewProperties.getHdfIcons()); 363 helpShell.setLayout(new GridLayout(1, true)); 364 365 // Try to create a Browser on platforms that support it 366 try { 367 Browser browser = new Browser(helpShell, SWT.NONE); 368 browser.setFont(curFont); 369 browser.setBounds(0, 0, 500, 500); 370 browser.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true)); 371 372 if (ClassLoader.getSystemResource("hdf/view/HDFView.class").toString().startsWith("jar")) { 373 // Attempt to load HTML help file from jar 374 try (InputStream in = 375 getClass().getClassLoader().getResourceAsStream("hdf/view/NewAttrHelp.html")) { 376 Scanner scan = new Scanner(in); 377 StringBuilder buffer = new StringBuilder(); 378 while (scan.hasNextLine()) { 379 buffer.append(scan.nextLine()); 380 } 381 382 browser.setText(buffer.toString()); 383 384 scan.close(); 385 } 386 catch (Exception e) { 387 StringBuilder buff = new StringBuilder(); 388 buff.append("<html>") 389 .append("<body>") 390 .append("ERROR: cannot load help information.") 391 .append("</body>") 392 .append("</html>"); 393 browser.setText(buff.toString(), true); 394 } 395 } 396 else { 397 try { 398 URL url = null, url2 = null, url3 = null; 399 String rootPath = ViewProperties.getViewRoot(); 400 401 try { 402 url = new URL("file://" + rootPath + "/HDFView.jar"); 403 } 404 catch (java.net.MalformedURLException mfu) { 405 log.debug("help information:", mfu); 406 } 407 408 try { 409 url2 = new URL("file://" + rootPath + "/"); 410 } 411 catch (java.net.MalformedURLException mfu) { 412 log.debug("help information:", mfu); 413 } 414 415 try { 416 url3 = new URL("file://" + rootPath + "/src/"); 417 } 418 catch (java.net.MalformedURLException mfu) { 419 log.debug("help information:", mfu); 420 } 421 422 URL uu[] = {url, url2, url3}; 423 try (URLClassLoader cl = new URLClassLoader(uu)) { 424 URL u = cl.findResource("hdf/view/NewAttrHelp.html"); 425 426 browser.setUrl(u.toString()); 427 } 428 catch (Exception ex) { 429 log.trace("URLClassLoader failed:", ex); 430 } 431 } 432 catch (Exception e) { 433 StringBuilder buff = new StringBuilder(); 434 buff.append("<html>") 435 .append("<body>") 436 .append("ERROR: cannot load help information.") 437 .append("</body>") 438 .append("</html>"); 439 browser.setText(buff.toString(), true); 440 } 441 } 442 443 Button okButton = new Button(helpShell, SWT.PUSH); 444 okButton.setFont(curFont); 445 okButton.setText(" &OK "); 446 okButton.setLayoutData(new GridData(SWT.CENTER, SWT.FILL, true, false)); 447 okButton.addSelectionListener(new SelectionAdapter() { 448 @Override 449 public void widgetSelected(SelectionEvent e) 450 { 451 helpShell.dispose(); 452 } 453 }); 454 455 helpShell.pack(); 456 457 helpShell.setSize(new Point(500, 500)); 458 459 Rectangle parentBounds = parent.getBounds(); 460 Point shellSize = helpShell.getSize(); 461 helpShell.setLocation((parentBounds.x + (parentBounds.width / 2)) - (shellSize.x / 2), 462 (parentBounds.y + (parentBounds.height / 2)) - (shellSize.y / 2)); 463 464 helpShell.open(); 465 466 Display display = parent.getDisplay(); 467 while (!helpShell.isDisposed()) { 468 if (!display.readAndDispatch()) 469 display.sleep(); 470 } 471 } 472 catch (Error er) { 473 // Try opening help link in external browser if platform 474 // doesn't support SWT browser 475 Tools.showError(shell, "Browser support", 476 "Platform doesn't support Browser. Opening external link in web browser..."); 477 478 // TODO: Add support for launching in external browser 479 } 480 catch (Exception ex) { 481 log.debug("Open New Attribute Help failure: ", ex); 482 } 483 } 484 } 485 486 /** 487 * Get the new attribute created. 488 * 489 * @return the new attribute created. 490 */ 491 public Attribute getAttribute() { return (Attribute)newObject; } 492}