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