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 file COPYING.                     *
009 * COPYING can be found at the root of the source code distribution tree.    *
010 * If you do not have access to this file, you may request a copy from       *
011 * help@hdfgroup.org.                                                        *
012 ****************************************************************************/
013
014package hdf.view;
015
016import java.awt.BorderLayout;
017import java.awt.Color;
018import java.awt.Dimension;
019import java.awt.GridLayout;
020import java.awt.Point;
021import java.awt.Toolkit;
022import java.awt.event.ActionEvent;
023import java.awt.event.ActionListener;
024import java.awt.event.ItemEvent;
025import java.awt.event.ItemListener;
026import java.awt.event.KeyEvent;
027import java.net.URL;
028import java.net.URLClassLoader;
029import java.util.Iterator;
030import java.util.List;
031import java.util.StringTokenizer;
032import java.util.Vector;
033
034import javax.swing.BorderFactory;
035import javax.swing.ButtonGroup;
036import javax.swing.JButton;
037import javax.swing.JCheckBox;
038import javax.swing.JComboBox;
039import javax.swing.JDialog;
040import javax.swing.JEditorPane;
041import javax.swing.JFrame;
042import javax.swing.JLabel;
043import javax.swing.JOptionPane;
044import javax.swing.JPanel;
045import javax.swing.JRadioButton;
046import javax.swing.JScrollPane;
047import javax.swing.JTextField;
048import javax.swing.border.TitledBorder;
049import javax.swing.event.HyperlinkEvent;
050import javax.swing.event.HyperlinkListener;
051import javax.swing.text.html.HTMLDocument;
052import javax.swing.text.html.HTMLFrameHyperlinkEvent;
053
054import hdf.object.DataFormat;
055import hdf.object.Dataset;
056import hdf.object.Datatype;
057import hdf.object.FileFormat;
058import hdf.object.Group;
059import hdf.object.HObject;
060import hdf.object.ScalarDS;
061
062/**
063 * NewDatasetDialog shows a message dialog requesting user input for creating a
064 * new HDF4/5 dataset.
065 *
066 */
067public class NewDatasetDialog extends JDialog implements ActionListener, ItemListener, HyperlinkListener {
068    private static final long serialVersionUID = 5381164938654184532L;
069
070    private final static org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(NewDatasetDialog.class);
071
072    private JTextField        nameField, currentSizeField, chunkSizeField,
073                              stringLengthField, fillValueField, maxSizeField;
074
075    @SuppressWarnings("rawtypes")
076    private JComboBox         parentChoice, classChoice, sizeChoice, endianChoice,
077                              rankChoice, compressionLevel;
078
079    private JCheckBox         checkUnsigned, checkCompression, checkFillValue;
080
081    private JRadioButton      checkContinguous, checkChunked;
082
083    private boolean           isH5;
084
085    /** a list of current groups */
086    private List<Object>      groupList;
087
088    private HObject           newObject;
089
090    private FileFormat        fileFormat;
091
092    private final DataView    dataView;
093
094    private final Toolkit     toolkit;
095
096    private JDialog           helpDialog;
097
098    /**
099     * Constructs a NewDatasetDialog with specified list of possible parent
100     * groups.
101     *
102     * @param parent
103     *            the parent shell of the dialog
104     * @param pGroup
105     *            the parent group which the new group is added to.
106     * @param objs
107     *            the list of all objects.
108     */
109    @SuppressWarnings({ "rawtypes", "unchecked" })
110    public NewDatasetDialog(JFrame parent, Group pGroup, List<?> objs) {
111        super(parent, "New Dataset...", true);
112
113        toolkit = Toolkit.getDefaultToolkit();
114
115        helpDialog = null;
116        newObject = null;
117        dataView = null;
118
119        fileFormat = pGroup.getFileFormat();
120        isH5 = pGroup.getFileFormat().isThisType(FileFormat.getFileFormat(FileFormat.FILE_TYPE_HDF5));
121
122        parentChoice = new JComboBox();
123        groupList = new Vector<Object>();
124        Object obj = null;
125        Iterator<?> iterator = objs.iterator();
126        while (iterator.hasNext()) {
127            obj = iterator.next();
128            if (obj instanceof Group) {
129                Group g = (Group) obj;
130                groupList.add(obj);
131                if (g.isRoot()) {
132                    parentChoice.addItem(HObject.separator);
133                }
134                else {
135                    parentChoice.addItem(g.getPath() + g.getName() + HObject.separator);
136                }
137            }
138        }
139
140        if (pGroup.isRoot()) {
141            parentChoice.setSelectedItem(HObject.separator);
142        }
143        else {
144            parentChoice.setSelectedItem(pGroup.getPath() + pGroup.getName() + HObject.separator);
145        }
146
147        JPanel contentPane = (JPanel) getContentPane();
148        contentPane.setLayout(new BorderLayout(5, 5));
149        contentPane.setBorder(BorderFactory.createEmptyBorder(15, 5, 5, 5));
150        int w = 600 + (ViewProperties.getFontSize() - 12) * 15;
151        int h = 350 + (ViewProperties.getFontSize() - 12) * 10;
152        contentPane.setPreferredSize(new Dimension(w, h));
153
154        JButton okButton = new JButton("   Ok   ");
155        okButton.setName("OK");
156        okButton.setActionCommand("Ok");
157        okButton.setMnemonic(KeyEvent.VK_O);
158        okButton.addActionListener(this);
159
160        JButton cancelButton = new JButton("Cancel");
161        cancelButton.setName("Cancel");
162        cancelButton.setMnemonic(KeyEvent.VK_C);
163        cancelButton.setActionCommand("Cancel");
164        cancelButton.addActionListener(this);
165
166        JButton helplButton = new JButton("Help");
167        helplButton.setName("Help");
168        helplButton.setMnemonic(KeyEvent.VK_H);
169        helplButton.setActionCommand("Show help");
170        helplButton.addActionListener(this);
171
172        // set OK and CANCEL buttons
173        JPanel buttonPanel = new JPanel();
174        buttonPanel.add(okButton);
175        buttonPanel.add(cancelButton);
176        buttonPanel.add(helplButton);
177        contentPane.add(buttonPanel, BorderLayout.SOUTH);
178
179        // set NAME and PARENT GROUP panel
180        JPanel namePanel = new JPanel();
181        namePanel.setLayout(new BorderLayout(5, 5));
182        JPanel tmpP = new JPanel();
183        tmpP.setLayout(new GridLayout(2, 1));
184        tmpP.add(new JLabel("Dataset name: "));
185        tmpP.add(new JLabel("Parent group: "));
186        namePanel.add(tmpP, BorderLayout.WEST);
187        tmpP = new JPanel();
188        tmpP.setLayout(new GridLayout(2, 1));
189        tmpP.add(nameField = new JTextField());
190        nameField.setName("datasetname");
191        tmpP.add(parentChoice);
192        namePanel.add(tmpP, BorderLayout.CENTER);
193        contentPane.add(namePanel, BorderLayout.NORTH);
194
195        // set DATATYPE
196        JPanel typePanel = new JPanel();
197        typePanel.setLayout(new GridLayout(2, 4, 15, 3));
198        TitledBorder border = new TitledBorder("Datatype");
199        border.setTitleColor(Color.gray);
200        typePanel.setBorder(border);
201
202        stringLengthField = new JTextField("String length");
203        stringLengthField.setName("datasetstringlen");
204        stringLengthField.setEnabled(false);
205
206        endianChoice = new JComboBox();
207        endianChoice.setName("datasetendian");
208        classChoice = new JComboBox();
209        classChoice.setName("datasetclass");
210        sizeChoice = new JComboBox();
211        sizeChoice.setName("datasetsize");
212        endianChoice.setEnabled(isH5);
213
214        classChoice.addItem("INTEGER");
215        classChoice.addItem("FLOAT");
216        classChoice.addItem("CHAR");
217
218        if (isH5) {
219            classChoice.addItem("STRING");
220            classChoice.addItem("REFERENCE");
221            classChoice.addItem("ENUM");
222            classChoice.addItem("VLEN_INTEGER");
223            classChoice.addItem("VLEN_FLOAT");
224            classChoice.addItem("VLEN_STRING");
225            sizeChoice.addItem("NATIVE");
226            endianChoice.addItem("NATIVE");
227            endianChoice.addItem("LITTLE ENDIAN");
228            endianChoice.addItem("BIG ENDIAN");
229        }
230        else {
231            sizeChoice.addItem("DEFAULT");
232            endianChoice.addItem("DEFAULT");
233        }
234        sizeChoice.addItem("8");
235        sizeChoice.addItem("16");
236        sizeChoice.addItem("32");
237        sizeChoice.addItem("64");
238
239        typePanel.add(new JLabel("Datatype class"));
240        typePanel.add(new JLabel("Size (bits)"));
241        typePanel.add(new JLabel("Byte ordering"));
242        checkUnsigned = new JCheckBox("Unsigned");
243        checkUnsigned.setName("datasetchkunsigned");
244        typePanel.add(checkUnsigned);
245
246        typePanel.add(classChoice);
247        typePanel.add(sizeChoice);
248        typePanel.add(endianChoice);
249        typePanel.add(stringLengthField);
250
251        // set DATATSPACE
252        JPanel spacePanel = new JPanel();
253        spacePanel.setLayout(new GridLayout(2, 3, 15, 3));
254        border = new TitledBorder("Dataspace");
255        border.setTitleColor(Color.gray);
256        spacePanel.setBorder(border);
257
258        rankChoice = new JComboBox();
259        rankChoice.setName("datasetrank");
260        for (int i = 1; i < 33; i++) {
261            rankChoice.addItem(String.valueOf(i));
262        }
263        rankChoice.setSelectedIndex(1);
264
265        currentSizeField = new JTextField("1 x 1");
266        currentSizeField.setName("currentsize");
267        maxSizeField = new JTextField("");
268        spacePanel.add(new JLabel("No. of dimensions"));
269        spacePanel.add(new JLabel("Current size"));
270        spacePanel.add(new JLabel(""));
271        spacePanel.add(rankChoice);
272        spacePanel.add(currentSizeField);
273        JButton jb = new JButton("Set Max Size");
274        jb.setActionCommand("Set max size");
275        jb.addActionListener(this);
276        spacePanel.add(jb);
277        // spacePanel.add(maxSizeField);
278
279        // set storage layout and data compression
280        JPanel layoutPanel = new JPanel();
281        layoutPanel.setLayout(new BorderLayout());
282        border = new TitledBorder("Storage Properties");
283        border.setTitleColor(Color.gray);
284        layoutPanel.setBorder(border);
285
286        checkContinguous = new JRadioButton("Contiguous");
287        checkContinguous.setName("datasetcontinguous");
288        checkContinguous.setSelected(true);
289        checkChunked = new JRadioButton("Chunked (size) ");
290        checkChunked.setName("datasetchunk");
291        ButtonGroup bgroup = new ButtonGroup();
292        bgroup.add(checkChunked);
293        bgroup.add(checkContinguous);
294        chunkSizeField = new JTextField("1 x 1");
295        chunkSizeField.setName("datasetchunksize");
296        chunkSizeField.setEnabled(false);
297        checkCompression = new JCheckBox("gzip (level) ");
298        checkCompression.setName("datasetgzip");
299
300        compressionLevel = new JComboBox();
301        compressionLevel.setName("datasetlevel");
302        for (int i = 0; i < 10; i++) {
303            compressionLevel.addItem(String.valueOf(i));
304        }
305        compressionLevel.setSelectedIndex(6);
306        compressionLevel.setEnabled(false);
307
308        tmpP = new JPanel();
309        tmpP.setLayout(new GridLayout(2, 1));
310        tmpP.add(new JLabel("Storage layout:  "));
311        tmpP.add(new JLabel("Compression:  "));
312        layoutPanel.add(tmpP, BorderLayout.WEST);
313
314        tmpP = new JPanel();
315        tmpP.setLayout(new GridLayout(2, 1));
316
317        // storage layout
318        JPanel tmpP0 = new JPanel();
319        tmpP0.setLayout(new GridLayout(1, 3, 0, 5));
320        tmpP0.add(checkContinguous);
321        JPanel tmpP00 = new JPanel();
322        tmpP00.setLayout(new BorderLayout());
323        tmpP00.add(checkChunked, BorderLayout.WEST);
324        tmpP00.add(chunkSizeField, BorderLayout.CENTER);
325        tmpP0.add(tmpP00);
326        tmpP0.add(new JLabel(""));
327        tmpP.add(tmpP0);
328
329        tmpP0 = new JPanel();
330        tmpP0.setLayout(new GridLayout(1, 2, 30, 5));
331
332        // compression
333        tmpP00 = new JPanel();
334        tmpP00.setLayout(new BorderLayout());
335        tmpP00.add(checkCompression, BorderLayout.WEST);
336        tmpP00.add(compressionLevel, BorderLayout.CENTER);
337        tmpP0.add(tmpP00);
338
339        // fill values
340        checkFillValue = new JCheckBox("Fill Value ");
341        checkFillValue.setName("datasetchkfill");
342        fillValueField = new JTextField("0");
343        fillValueField.setName("datasetfillval");
344        fillValueField.setEnabled(false);
345        checkFillValue.setSelected(false);
346        tmpP00 = new JPanel();
347        tmpP00.setLayout(new BorderLayout());
348        tmpP00.add(checkFillValue, BorderLayout.WEST);
349        tmpP00.add(fillValueField, BorderLayout.CENTER);
350
351        if (isH5)
352            tmpP0.add(tmpP00);
353        else
354            tmpP0.add(new JLabel(""));
355
356        tmpP.add(tmpP0);
357
358        layoutPanel.add(tmpP, BorderLayout.CENTER);
359
360        JPanel infoPanel = new JPanel();
361        infoPanel.setLayout(new GridLayout(3, 1, 5, 10));
362        infoPanel.add(typePanel);
363        infoPanel.add(spacePanel);
364        infoPanel.add(layoutPanel);
365        contentPane.add(infoPanel, BorderLayout.CENTER);
366
367        classChoice.addItemListener(this);
368        sizeChoice.addItemListener(this);
369        rankChoice.addItemListener(this);
370        checkCompression.addItemListener(this);
371        checkFillValue.addItemListener(this);
372        checkContinguous.addItemListener(this);
373        checkChunked.addItemListener(this);
374
375        // locate the H5Property dialog
376        Point l = parent.getLocation();
377        l.x += 250;
378        l.y += 80;
379        setLocation(l);
380        validate();
381        pack();
382    }
383
384    /**
385     * Constructs NewDatasetDialog with specified list of possible parent
386     * groups.
387     *
388     * @param owner
389     *            the owner of the input
390     * @param pGroup
391     *            the parent group which the new group is added to.
392     * @param objs
393     *            the list of all objects.
394     * @param observer
395     *            the parent DataView
396     */
397    @SuppressWarnings({ "rawtypes", "unchecked" })
398    public NewDatasetDialog(JFrame owner, Group pGroup, List<?> objs, DataView observer) {
399        super(owner, "New Dataset...", true);
400
401        helpDialog = null;
402        newObject = null;
403        dataView = observer;
404
405        fileFormat = pGroup.getFileFormat();
406        toolkit = Toolkit.getDefaultToolkit();
407        isH5 = pGroup.getFileFormat().isThisType(FileFormat.getFileFormat(FileFormat.FILE_TYPE_HDF5));
408
409        parentChoice = new JComboBox();
410        groupList = new Vector<Object>();
411        Object obj = null;
412        Iterator<?> iterator = objs.iterator();
413        while (iterator.hasNext()) {
414            obj = iterator.next();
415            if (obj instanceof Group) {
416                Group g = (Group) obj;
417                groupList.add(obj);
418                if (g.isRoot()) {
419                    parentChoice.addItem(HObject.separator);
420                }
421                else {
422                    parentChoice.addItem(g.getPath() + g.getName() + HObject.separator);
423                }
424            }
425        }
426
427        if (pGroup.isRoot()) {
428            parentChoice.setSelectedItem(HObject.separator);
429        }
430        else {
431            parentChoice.setSelectedItem(pGroup.getPath() + pGroup.getName() + HObject.separator);
432        }
433
434        JPanel contentPane = (JPanel) getContentPane();
435        contentPane.setLayout(new BorderLayout(5, 5));
436        contentPane.setBorder(BorderFactory.createEmptyBorder(15, 5, 5, 5));
437        int w = 400 + (ViewProperties.getFontSize() - 12) * 15;
438        int h = 120 + (ViewProperties.getFontSize() - 12) * 10;
439        contentPane.setPreferredSize(new Dimension(w, h));
440
441        JButton okButton = new JButton("   Ok   ");
442        okButton.setActionCommand("Ok");
443        okButton.setMnemonic(KeyEvent.VK_O);
444        okButton.addActionListener(this);
445
446        JButton cancelButton = new JButton("Cancel");
447        cancelButton.setMnemonic(KeyEvent.VK_C);
448        cancelButton.setActionCommand("Cancel");
449        cancelButton.addActionListener(this);
450
451        // set OK and CANCEL buttons
452        JPanel buttonPanel = new JPanel();
453        buttonPanel.add(okButton);
454        buttonPanel.add(cancelButton);
455        contentPane.add(buttonPanel, BorderLayout.SOUTH);
456
457        // set NAME and PARENT GROUP panel
458        JPanel namePanel = new JPanel();
459        namePanel.setLayout(new BorderLayout(5, 5));
460        JPanel tmpP = new JPanel();
461        tmpP.setLayout(new GridLayout(2, 1));
462        tmpP.add(new JLabel("Dataset name: "));
463        tmpP.add(new JLabel("Parent group: "));
464        namePanel.add(tmpP, BorderLayout.WEST);
465        tmpP = new JPanel();
466        tmpP.setLayout(new GridLayout(2, 1));
467        tmpP.add(nameField = new JTextField(((HObject) observer.getDataObject()).getName() + "~copy", 40));
468        tmpP.add(parentChoice);
469        namePanel.add(tmpP, BorderLayout.CENTER);
470        contentPane.add(namePanel, BorderLayout.CENTER);
471
472        // locate the H5Property dialog
473        Point l = owner.getLocation();
474        l.x += 250;
475        l.y += 80;
476        setLocation(l);
477        pack();
478    }
479
480    public void actionPerformed(ActionEvent e) {
481        String cmd = e.getActionCommand();
482
483        if (cmd.equals("Ok")) {
484            if (dataView instanceof TableView) {
485                newObject = createFromTable();
486            }
487            else if (dataView instanceof ImageView) {
488                newObject = createFromImage();
489            }
490            else if (dataView == null) {
491                newObject = createFromScratch();
492            }
493
494            if (newObject != null) {
495                dispose();
496            }
497        }
498        if (cmd.equals("Cancel")) {
499            newObject = null;
500            dispose();
501            ((Vector<Object>) groupList).setSize(0);
502        }
503        else if (cmd.equals("Show help")) {
504            if (helpDialog == null) {
505                createHelpDialog();
506            }
507            helpDialog.setVisible(true);
508        }
509        else if (cmd.equals("Hide help")) {
510            if (helpDialog != null) {
511                helpDialog.setVisible(false);
512            }
513        }
514        else if (cmd.equals("Set max size")) {
515            String strMax = maxSizeField.getText();
516            if (strMax == null || strMax.length() < 1) strMax = currentSizeField.getText();
517
518            String msg = JOptionPane.showInputDialog(this, "Enter max dimension sizes. \n"
519                    + "Use \"unlimited\" for unlimited dimension size.\n\n" + "For example,\n" + "    200 x 100\n"
520                    + "    100 x unlimited\n\n", strMax);
521
522            if (msg == null || msg.length() < 1)
523                maxSizeField.setText(currentSizeField.getText());
524            else
525                maxSizeField.setText(msg);
526
527            checkMaxSize();
528        }
529    }
530
531    @SuppressWarnings("unchecked")
532    public void itemStateChanged(ItemEvent e) {
533        Object source = e.getSource();
534
535        if (source.equals(classChoice)) {
536            int idx = classChoice.getSelectedIndex();
537            sizeChoice.setSelectedIndex(0);
538            endianChoice.setSelectedIndex(0);
539            stringLengthField.setEnabled(false);
540
541            if ((idx == 0) || (idx == 6)) { // INTEGER
542                sizeChoice.setEnabled(true);
543                endianChoice.setEnabled(isH5);
544                checkUnsigned.setEnabled(true);
545
546                if (sizeChoice.getItemCount() == 3) {
547                    sizeChoice.removeItem("32");
548                    sizeChoice.removeItem("64");
549                    sizeChoice.addItem("8");
550                    sizeChoice.addItem("16");
551                    sizeChoice.addItem("32");
552                    sizeChoice.addItem("64");
553                }
554            }
555            else if ((idx == 1) || (idx == 7)) { // FLOAT
556                sizeChoice.setEnabled(true);
557                endianChoice.setEnabled(isH5);
558                checkUnsigned.setEnabled(false);
559
560                if (sizeChoice.getItemCount() == 5) {
561                    sizeChoice.removeItem("16");
562                    sizeChoice.removeItem("8");
563                }
564            }
565            else if (idx == 2) { // CHAR
566                sizeChoice.setEnabled(false);
567                endianChoice.setEnabled(isH5);
568                checkUnsigned.setEnabled(true);
569            }
570            else if (idx == 3) { // STRING
571                sizeChoice.setEnabled(false);
572                endianChoice.setEnabled(false);
573                checkUnsigned.setEnabled(false);
574                stringLengthField.setEnabled(true);
575                stringLengthField.setText("String length");
576            }
577            else if (idx == 4) { // REFERENCE
578                sizeChoice.setEnabled(false);
579                endianChoice.setEnabled(false);
580                checkUnsigned.setEnabled(false);
581                stringLengthField.setEnabled(false);
582            }
583            else if (idx == 5) { // ENUM
584                sizeChoice.setEnabled(true);
585                checkUnsigned.setEnabled(true);
586                stringLengthField.setEnabled(true);
587                stringLengthField.setText("R=0,G=1,B=2,...");
588            }
589            else if (idx == 8) {
590                sizeChoice.setEnabled(false);
591                endianChoice.setEnabled(false);
592                checkUnsigned.setEnabled(false);
593                stringLengthField.setEnabled(false);
594            }
595        }
596        else if (source.equals(sizeChoice)) {
597            if (classChoice.getSelectedIndex() == 0) {
598                checkUnsigned.setEnabled(true);
599            }
600        }
601        else if (source.equals(rankChoice)) {
602            int rank = rankChoice.getSelectedIndex() + 1;
603            String currentSizeStr = "1";
604            String maxSizeStr = "0";
605
606            for (int i = 1; i < rank; i++) {
607                currentSizeStr += " x 1";
608                maxSizeStr += " x 0";
609            }
610
611            currentSizeField.setText(currentSizeStr);
612            maxSizeField.setText(maxSizeStr);
613
614            String currentStr = currentSizeField.getText();
615            int idx = currentStr.lastIndexOf("x");
616            String chunkStr = "1";
617
618            if (rank <= 1) {
619                chunkStr = currentStr;
620            }
621            else {
622                for (int i = 1; i < rank - 1; i++) {
623                    chunkStr += " x 1";
624                }
625                if (idx > 0) {
626                    chunkStr += " x " + currentStr.substring(idx + 1);
627                }
628            }
629
630            chunkSizeField.setText(chunkStr);
631        }
632        else if (source.equals(checkContinguous)) {
633            chunkSizeField.setEnabled(false);
634        }
635        else if (source.equals(checkChunked)) {
636            chunkSizeField.setEnabled(true);
637            String chunkStr = "";
638            StringTokenizer st = new StringTokenizer(currentSizeField.getText(), "x");
639            int rank = rankChoice.getSelectedIndex() + 1;
640            while (st.hasMoreTokens()) {
641                long l = Math.max(1, Long.valueOf(st.nextToken().trim()) / (2 * rank));
642                chunkStr += String.valueOf(l) + "x";
643            }
644            chunkStr = chunkStr.substring(0, chunkStr.lastIndexOf('x'));
645            chunkSizeField.setText(chunkStr);
646        }
647        else if (source.equals(checkCompression)) {
648            boolean isCompressed = checkCompression.isSelected();
649
650            if (isCompressed && isH5) {
651                if (!checkChunked.isSelected()) {
652                    String currentStr = currentSizeField.getText();
653                    int idx = currentStr.lastIndexOf("x");
654                    String chunkStr = "1";
655
656                    int rank = rankChoice.getSelectedIndex() + 1;
657                    if (rank <= 1) {
658                        chunkStr = currentStr;
659                    }
660                    else {
661                        for (int i = 1; i < rank - 1; i++) {
662                            chunkStr += " x 1";
663                        }
664                        if (idx > 0) {
665                            chunkStr += " x " + currentStr.substring(idx + 1);
666                        }
667                    }
668
669                    chunkSizeField.setText(chunkStr);
670                }
671                compressionLevel.setEnabled(true);
672                checkContinguous.setEnabled(false);
673                checkChunked.setSelected(true);
674                chunkSizeField.setEnabled(true);
675            }
676            else {
677                compressionLevel.setEnabled(isCompressed);
678                checkContinguous.setEnabled(true);
679            }
680        }
681        else if (source.equals(checkFillValue)) {
682            fillValueField.setEnabled(checkFillValue.isSelected());
683        }
684    }
685
686    /** Check if the max size is valid */
687    private void checkMaxSize() {
688        boolean isChunkNeeded = false;
689        String dimStr = currentSizeField.getText();
690        String maxStr = maxSizeField.getText();
691        StringTokenizer stMax = new StringTokenizer(maxStr, "x");
692        StringTokenizer stDim = new StringTokenizer(dimStr, "x");
693
694        if (stMax.countTokens() != stDim.countTokens()) {
695            toolkit.beep();
696            JOptionPane.showMessageDialog(this, "Wrong number of values in the max dimension size " + maxStr,
697                    getTitle(), JOptionPane.ERROR_MESSAGE);
698            maxSizeField.setText(null);
699            return;
700        }
701
702        int rank = stDim.countTokens();
703        long max = 0, dim = 0;
704        long[] maxdims = new long[rank];
705        for (int i = 0; i < rank; i++) {
706            String token = stMax.nextToken().trim();
707
708            token = token.toLowerCase();
709            if (token.startsWith("u")) {
710                max = -1;
711                isChunkNeeded = true;
712            }
713            else {
714                try {
715                    max = Long.parseLong(token);
716                }
717                catch (NumberFormatException ex) {
718                    toolkit.beep();
719                    JOptionPane.showMessageDialog(this, "Invalid max dimension size: " + maxStr, getTitle(),
720                            JOptionPane.ERROR_MESSAGE);
721                    maxSizeField.setText(null);
722                    return;
723                }
724            }
725
726            token = stDim.nextToken().trim();
727            try {
728                dim = Long.parseLong(token);
729            }
730            catch (NumberFormatException ex) {
731                toolkit.beep();
732                JOptionPane.showMessageDialog(this, "Invalid dimension size: " + dimStr, getTitle(),
733                        JOptionPane.ERROR_MESSAGE);
734                return;
735            }
736
737            if (max != -1 && max < dim) {
738                toolkit.beep();
739                JOptionPane.showMessageDialog(this, "Invalid max dimension size: " + maxStr, getTitle(),
740                        JOptionPane.ERROR_MESSAGE);
741                maxSizeField.setText(null);
742                return;
743            }
744            else if (max > dim) {
745                isChunkNeeded = true;
746            }
747
748            maxdims[i] = max;
749        } // for (int i = 0; i < rank; i++)
750
751        if (isH5) {
752            if (isChunkNeeded && !checkChunked.isSelected()) {
753                toolkit.beep();
754                JOptionPane.showMessageDialog(this, "Chunking is required for the max dimensions of " + maxStr,
755                        getTitle(), JOptionPane.ERROR_MESSAGE);
756                checkChunked.setSelected(true);
757            }
758        }
759        else {
760            for (int i = 1; i < rank; i++) {
761                if (maxdims[i] <= 0) {
762                    maxSizeField.setText(currentSizeField.getText());
763                    toolkit.beep();
764                    JOptionPane.showMessageDialog(this, "Only dim[0] can be unlimited." + maxStr, getTitle(),
765                            JOptionPane.ERROR_MESSAGE);
766                    return;
767                }
768            }
769        }
770    }
771
772    public void hyperlinkUpdate(HyperlinkEvent e) {
773        if (e.getEventType() == HyperlinkEvent.EventType.ACTIVATED) {
774            JEditorPane pane = (JEditorPane) e.getSource();
775
776            if (e instanceof HTMLFrameHyperlinkEvent) {
777                HTMLFrameHyperlinkEvent evt = (HTMLFrameHyperlinkEvent) e;
778                HTMLDocument doc = (HTMLDocument) pane.getDocument();
779                doc.processHTMLFrameHyperlinkEvent(evt);
780            }
781            else {
782                try {
783                    pane.setPage(e.getURL());
784                }
785                catch (Throwable t) {
786                    log.debug("JEditorPane hyperlink:", t);
787                }
788            }
789        }
790    }
791
792    private HObject createFromScratch() {
793        String name = null;
794        Group pgroup = null;
795        boolean isVLen = false;
796        int rank = -1, gzip = -1, tclass = -1, tsize = -1, torder = -1, tsign = -1;
797        long dims[], maxdims[] = null, chunks[] = null;
798
799        name = nameField.getText().trim();
800        if ((name == null) || (name.length() < 1)) {
801            toolkit.beep();
802            JOptionPane
803            .showMessageDialog(this, "Dataset name is not specified.", getTitle(), JOptionPane.ERROR_MESSAGE);
804            return null;
805        }
806
807        if (name.indexOf(HObject.separator) >= 0) {
808            toolkit.beep();
809            JOptionPane.showMessageDialog(this, "Dataset name cannot contain path.", getTitle(),
810                    JOptionPane.ERROR_MESSAGE);
811            return null;
812        }
813
814        pgroup = (Group) groupList.get(parentChoice.getSelectedIndex());
815
816        if (pgroup == null) {
817            toolkit.beep();
818            JOptionPane.showMessageDialog(this, "Parent group is null.", getTitle(), JOptionPane.ERROR_MESSAGE);
819            return null;
820        }
821
822        // set datatype class
823        int idx = classChoice.getSelectedIndex();
824        if (idx == 0) {
825            tclass = Datatype.CLASS_INTEGER;
826            if (checkUnsigned.isSelected()) {
827                tsign = Datatype.SIGN_NONE;
828            }
829        }
830        else if (idx == 1) {
831            tclass = Datatype.CLASS_FLOAT;
832        }
833        else if (idx == 2) {
834            tclass = Datatype.CLASS_CHAR;
835            if (checkUnsigned.isSelected()) {
836                tsign = Datatype.SIGN_NONE;
837            }
838        }
839        else if (idx == 3) {
840            tclass = Datatype.CLASS_STRING;
841        }
842        else if (idx == 4) {
843            tclass = Datatype.CLASS_REFERENCE;
844        }
845        else if (idx == 5) {
846            tclass = Datatype.CLASS_ENUM;
847        }
848        else if (idx == 6) {
849            isVLen = true;
850            tclass = Datatype.CLASS_INTEGER;
851            if (checkUnsigned.isSelected()) {
852                tsign = Datatype.SIGN_NONE;
853            }
854        }
855        else if (idx == 7) {
856            isVLen = true;
857            tclass = Datatype.CLASS_FLOAT;
858        }
859        else if (idx == 8) {
860            isVLen = true;
861            tclass = Datatype.CLASS_STRING;
862        }
863
864        // set datatype size/order
865        idx = sizeChoice.getSelectedIndex();
866        if (tclass == Datatype.CLASS_STRING) {
867            if (isVLen) {
868                tsize = -1;
869            }
870            else {
871                int stringLength = 0;
872                try {
873                    stringLength = Integer.parseInt(stringLengthField.getText());
874                }
875                catch (NumberFormatException ex) {
876                    stringLength = -1;
877                }
878
879                if (stringLength <= 0) {
880                    toolkit.beep();
881                    JOptionPane.showMessageDialog(this, "Invalid string length: " + stringLengthField.getText(),
882                            getTitle(), JOptionPane.ERROR_MESSAGE);
883                    return null;
884                }
885                tsize = stringLength;
886            }
887        }
888        else if (tclass == Datatype.CLASS_ENUM) {
889            String enumStr = stringLengthField.getText();
890            if ((enumStr == null) || (enumStr.length() < 1) || enumStr.endsWith("...")) {
891                toolkit.beep();
892                JOptionPane.showMessageDialog(this, "Invalid member values: " + stringLengthField.getText(),
893                        getTitle(), JOptionPane.ERROR_MESSAGE);
894                return null;
895            }
896        }
897        else if (tclass == Datatype.CLASS_REFERENCE) {
898            tsize = 1;
899        }
900        else if (idx == 0) {
901            tsize = Datatype.NATIVE;
902        }
903        else if (tclass == Datatype.CLASS_FLOAT) {
904            tsize = idx * 4;
905        }
906        else {
907            tsize = 1 << (idx - 1);
908        }
909
910        if ((tsize == 8) && !isH5 && (tclass == Datatype.CLASS_INTEGER)) {
911            toolkit.beep();
912            JOptionPane.showMessageDialog(this, "HDF4 does not support 64-bit integer.", getTitle(),
913                    JOptionPane.ERROR_MESSAGE);
914            return null;
915        }
916
917        // set order
918        idx = endianChoice.getSelectedIndex();
919        if (idx == 0) {
920            torder = Datatype.NATIVE;
921        }
922        else if (idx == 1) {
923            torder = Datatype.ORDER_LE;
924        }
925        else {
926            torder = Datatype.ORDER_BE;
927        }
928
929        rank = rankChoice.getSelectedIndex() + 1;
930        StringTokenizer st = new StringTokenizer(currentSizeField.getText(), "x");
931        if (st.countTokens() < rank) {
932            toolkit.beep();
933            JOptionPane.showMessageDialog(this, "Number of values in the current dimension size is less than " + rank,
934                    getTitle(), JOptionPane.ERROR_MESSAGE);
935            return null;
936        }
937
938        long l = 0;
939        dims = new long[rank];
940        String token = null;
941        for (int i = 0; i < rank; i++) {
942            token = st.nextToken().trim();
943            try {
944                l = Long.parseLong(token);
945            }
946            catch (NumberFormatException ex) {
947                toolkit.beep();
948                JOptionPane.showMessageDialog(this, "Invalid dimension size: " + currentSizeField.getText(),
949                        getTitle(), JOptionPane.ERROR_MESSAGE);
950                return null;
951            }
952
953            if (l <= 0) {
954                toolkit.beep();
955                JOptionPane.showMessageDialog(this, "Dimension size must be greater than zero.", getTitle(),
956                        JOptionPane.ERROR_MESSAGE);
957                return null;
958            }
959
960            dims[i] = l;
961        }
962
963        String maxFieldStr = maxSizeField.getText();
964        if (maxFieldStr != null && maxFieldStr.length() > 1) {
965            st = new StringTokenizer(maxFieldStr, "x");
966            if (st.countTokens() < rank) {
967                toolkit.beep();
968                JOptionPane.showMessageDialog(this, "Number of values in the max dimension size is less than " + rank,
969                        getTitle(), JOptionPane.ERROR_MESSAGE);
970                return null;
971            }
972
973            l = 0;
974            maxdims = new long[rank];
975            for (int i = 0; i < rank; i++) {
976                token = st.nextToken().trim();
977
978                token = token.toLowerCase();
979                if (token.startsWith("u"))
980                    l = -1;
981                else {
982                    try {
983                        l = Long.parseLong(token);
984                    }
985                    catch (NumberFormatException ex) {
986                        toolkit.beep();
987                        JOptionPane.showMessageDialog(this, "Invalid max dimension size: " + maxSizeField.getText(),
988                                getTitle(), JOptionPane.ERROR_MESSAGE);
989                        return null;
990                    }
991                }
992
993                if (l < -1) {
994                    toolkit.beep();
995                    JOptionPane.showMessageDialog(this, "Dimension size cannot be less than -1.", getTitle(),
996                            JOptionPane.ERROR_MESSAGE);
997                    return null;
998                }
999                else if (l == 0) {
1000                    l = dims[i];
1001                }
1002
1003                maxdims[i] = l;
1004            }
1005        }
1006
1007        chunks = null;
1008        if (checkChunked.isSelected()) {
1009            st = new StringTokenizer(chunkSizeField.getText(), "x");
1010            if (st.countTokens() < rank) {
1011                toolkit.beep();
1012                JOptionPane.showMessageDialog(this, "Number of values in the chunk size is less than " + rank,
1013                        getTitle(), JOptionPane.ERROR_MESSAGE);
1014                return null;
1015            }
1016
1017            l = 0;
1018            chunks = new long[rank];
1019            for (int i = 0; i < rank; i++) {
1020                token = st.nextToken().trim();
1021                try {
1022                    l = Long.parseLong(token);
1023                }
1024                catch (NumberFormatException ex) {
1025                    toolkit.beep();
1026                    JOptionPane.showMessageDialog(this, "Invalid chunk dimension size: " + chunkSizeField.getText(),
1027                            getTitle(), JOptionPane.ERROR_MESSAGE);
1028                    return null;
1029                }
1030
1031                if (l < 1) {
1032                    toolkit.beep();
1033                    JOptionPane.showMessageDialog(this, "Chunk size cannot be less than 1.", getTitle(),
1034                            JOptionPane.ERROR_MESSAGE);
1035                    return null;
1036                }
1037
1038                chunks[i] = l;
1039            } // for (int i=0; i<rank; i++)
1040
1041            long tchunksize = 1, tdimsize = 1;
1042            for (int i = 0; i < rank; i++) {
1043                tchunksize *= chunks[i];
1044                tdimsize *= dims[i];
1045            }
1046
1047            if (tchunksize >= tdimsize) {
1048                toolkit.beep();
1049                int status = JOptionPane.showConfirmDialog(this, "Chunk size is equal/greater than the current size. "
1050                        + "\nAre you sure you want to set chunk size to " + chunkSizeField.getText() + "?", getTitle(),
1051                        JOptionPane.YES_NO_OPTION);
1052                if (status == JOptionPane.NO_OPTION) {
1053                    return null;
1054                }
1055            }
1056
1057            if (tchunksize == 1) {
1058                toolkit.beep();
1059                int status = JOptionPane.showConfirmDialog(this,
1060                        "Chunk size is one, which may cause large memory overhead for large dataset."
1061                                + "\nAre you sure you want to set chunk size to " + chunkSizeField.getText() + "?",
1062                                getTitle(), JOptionPane.YES_NO_OPTION);
1063                if (status == JOptionPane.NO_OPTION) {
1064                    return null;
1065                }
1066            }
1067
1068        } // if (checkChunked.isSelected())
1069
1070        if (checkCompression.isSelected()) {
1071            gzip = compressionLevel.getSelectedIndex();
1072        }
1073        else {
1074            gzip = 0;
1075        }
1076
1077        HObject obj = null;
1078        try {
1079            Datatype basedatatype = null;
1080            if (isVLen) {
1081                basedatatype = fileFormat.createDatatype(tclass, tsize, torder, tsign);
1082                tclass = Datatype.CLASS_VLEN;
1083            }
1084            Datatype datatype = fileFormat.createDatatype(tclass, tsize, torder, tsign, basedatatype);
1085            if (tclass == Datatype.CLASS_ENUM) {
1086                datatype.setEnumMembers(stringLengthField.getText());
1087            }
1088            String fillValue = null;
1089
1090            if (fillValueField != null) {
1091                if (fillValueField.isEnabled()) fillValue = fillValueField.getText();
1092            }
1093
1094            obj = fileFormat.createScalarDS(name, pgroup, datatype, dims, maxdims, chunks, gzip, fillValue, null);
1095        }
1096        catch (Exception ex) {
1097            toolkit.beep();
1098            JOptionPane.showMessageDialog(this, ex, getTitle(), JOptionPane.ERROR_MESSAGE);
1099            return null;
1100        }
1101
1102        return obj;
1103    }
1104
1105    private HObject createFromTable() {
1106        HObject obj = null;
1107
1108        String name = null;
1109        Group pgroup = null;
1110
1111        name = nameField.getText();
1112        if (name == null) {
1113            toolkit.beep();
1114            JOptionPane
1115            .showMessageDialog(this, "Dataset name is not specified.", getTitle(), JOptionPane.ERROR_MESSAGE);
1116            return null;
1117        }
1118
1119        if (name.indexOf(HObject.separator) >= 0) {
1120            toolkit.beep();
1121            JOptionPane.showMessageDialog(this, "Dataset name cannot contain path.", getTitle(),
1122                    JOptionPane.ERROR_MESSAGE);
1123            return null;
1124        }
1125
1126        pgroup = (Group) groupList.get(parentChoice.getSelectedIndex());
1127        if (pgroup == null) {
1128            toolkit.beep();
1129            JOptionPane.showMessageDialog(this, "Parent group is null.", getTitle(), JOptionPane.ERROR_MESSAGE);
1130            return null;
1131        }
1132
1133        TableView tableView = (TableView) dataView;
1134        Object theData = tableView.getSelectedData();
1135        if (theData == null) {
1136            return null;
1137        }
1138
1139        int w = tableView.getTable().getSelectedColumnCount();
1140        int h = tableView.getTable().getSelectedRowCount();
1141        Dataset dataset = (Dataset) tableView.getDataObject();
1142        if (dataset instanceof ScalarDS) {
1143            ScalarDS sd = (ScalarDS) dataset;
1144            if (sd.isUnsigned()) {
1145                theData = Dataset.convertToUnsignedC(theData, null);
1146            }
1147        }
1148
1149        try {
1150            long[] dims = { h, w };
1151            obj = dataset.copy(pgroup, name, dims, theData);
1152        }
1153        catch (Exception ex) {
1154            toolkit.beep();
1155            JOptionPane.showMessageDialog(this, ex.getMessage(), getTitle(), JOptionPane.ERROR_MESSAGE);
1156            return null;
1157        }
1158
1159        return obj;
1160    }
1161
1162    private HObject createFromImage() {
1163        HObject obj = null;
1164        String name = null;
1165        Group pgroup = null;
1166
1167        name = nameField.getText();
1168        if (name == null) {
1169            toolkit.beep();
1170            JOptionPane
1171            .showMessageDialog(this, "Dataset name is not specified.", getTitle(), JOptionPane.ERROR_MESSAGE);
1172            return null;
1173        }
1174
1175        if (name.indexOf(HObject.separator) >= 0) {
1176            toolkit.beep();
1177            JOptionPane.showMessageDialog(this, "Dataset name cannot contain path.", getTitle(),
1178                    JOptionPane.ERROR_MESSAGE);
1179            return null;
1180        }
1181
1182        pgroup = (Group) groupList.get(parentChoice.getSelectedIndex());
1183        if (pgroup == null) {
1184            toolkit.beep();
1185            JOptionPane.showMessageDialog(this, "Parent group is null.", getTitle(), JOptionPane.ERROR_MESSAGE);
1186            return null;
1187        }
1188
1189        ImageView imageView = (ImageView) dataView;
1190        ScalarDS dataset = (ScalarDS) imageView.getDataObject();
1191        Object theData = imageView.getSelectedData();
1192
1193        if (theData == null) {
1194            return null;
1195        }
1196
1197        // in version 2.4, unsigned image data is converted to signed data
1198        // to write data, the data needs to be converted back to unsigned.
1199        if (dataset.isUnsigned()) {
1200            theData = Dataset.convertToUnsignedC(theData, null);
1201        }
1202
1203        int w = imageView.getSelectedArea().width;
1204        int h = imageView.getSelectedArea().height;
1205
1206        try {
1207            long[] dims = null;
1208            if (isH5) {
1209                if (imageView.isTrueColor()) {
1210                    dims = new long[3];
1211                    if (imageView.isPlaneInterlace()) {
1212                        dims[0] = 3;
1213                        dims[1] = h;
1214                        dims[2] = w;
1215                    }
1216                    else {
1217                        dims[0] = h;
1218                        dims[1] = w;
1219                        dims[2] = 3;
1220                    }
1221                }
1222                else {
1223                    dims = new long[2];
1224                    dims[0] = h;
1225                    dims[1] = w;
1226                }
1227            }
1228            else {
1229                dims = new long[2];
1230                dims[0] = w;
1231                dims[1] = h;
1232            }
1233
1234            obj = dataset.copy(pgroup, name, dims, theData);
1235        }
1236        catch (Exception ex) {
1237            toolkit.beep();
1238            JOptionPane.showMessageDialog(this, ex.getMessage(), getTitle(), JOptionPane.ERROR_MESSAGE);
1239            return null;
1240        }
1241
1242        return obj;
1243    }
1244
1245    /** Creates a dialog to show the help information. */
1246    private void createHelpDialog() {
1247        helpDialog = new JDialog(this, "Create New Dataset");
1248
1249        JPanel contentPane = (JPanel) helpDialog.getContentPane();
1250        contentPane.setLayout(new BorderLayout(5, 5));
1251        contentPane.setBorder(BorderFactory.createEmptyBorder(15, 5, 5, 5));
1252        int w = 500 + (ViewProperties.getFontSize() - 12) * 15;
1253        int h = 400 + (ViewProperties.getFontSize() - 12) * 10;
1254        contentPane.setPreferredSize(new Dimension(w, h));
1255
1256        JButton b = new JButton("  Ok  ");
1257        b.addActionListener(this);
1258        b.setActionCommand("Hide help");
1259        JPanel tmpP = new JPanel();
1260        tmpP.add(b);
1261        contentPane.add(tmpP, BorderLayout.SOUTH);
1262
1263        JEditorPane infoPane = new JEditorPane();
1264        infoPane.setEditable(false);
1265        JScrollPane editorScrollPane = new JScrollPane(infoPane);
1266        contentPane.add(editorScrollPane, BorderLayout.CENTER);
1267
1268        try {
1269            URL url = null, url2 = null, url3 = null;
1270            String rootPath = ViewProperties.getViewRoot();
1271
1272            try {
1273                url = new URL("file://" + rootPath + "/lib/jhdfview.jar");
1274            }
1275            catch (java.net.MalformedURLException mfu) {
1276                log.debug("help information:", mfu);
1277            }
1278
1279            try {
1280                url2 = new URL("file://" + rootPath + "/");
1281            }
1282            catch (java.net.MalformedURLException mfu) {
1283                log.debug("help information:", mfu);
1284            }
1285
1286            try {
1287                url3 = new URL("file://" + rootPath + "/src/");
1288            }
1289            catch (java.net.MalformedURLException mfu) {
1290                log.debug("help information:", mfu);
1291            }
1292
1293            URL uu[] = { url, url2, url3 };
1294            URLClassLoader cl = new URLClassLoader(uu);
1295            URL u = cl.findResource("hdf/view/NewDatasetHelp.html");
1296
1297            if (u == null) {
1298                u = ClassLoader.getSystemResource("hdf/view/NewDatasetHelp.html");
1299            }
1300
1301            cl.close();
1302            infoPane.setPage(u);
1303            infoPane.addHyperlinkListener(this);
1304        }
1305        catch (Exception e) {
1306            infoPane.setContentType("text/html");
1307            StringBuffer buff = new StringBuffer();
1308            buff.append("<html>");
1309            buff.append("<body>");
1310            buff.append("ERROR: cannot load help information.");
1311            buff.append("</body>");
1312            buff.append("</html>");
1313            infoPane.setText(buff.toString());
1314        }
1315
1316        Point l = helpDialog.getOwner().getLocation();
1317        l.x += 50;
1318        l.y += 80;
1319        helpDialog.setLocation(l);
1320        helpDialog.validate();
1321        helpDialog.pack();
1322    }
1323
1324    /** @return the new dataset created. */
1325    public DataFormat getObject() {
1326        return newObject;
1327    }
1328
1329    /** @return the parent group of the new dataset. */
1330    public Group getParentGroup() {
1331        return (Group) groupList.get(parentChoice.getSelectedIndex());
1332    }
1333}