    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}