001/*****************************************************************************
002 * Copyright by The HDF Group.                                               *
003 * Copyright by the Board of Trustees of the University of Illinois.         *
004 * All rights reserved.                                                      *
005 *                                                                           *
006 * This file is part of the HDF Java Products distribution.                  *
007 * The full copyright notice, including terms governing use, modification,   *
008 * and redistribution, is contained in the files COPYING and Copyright.html. *
009 * COPYING can be found at the root of the source code distribution tree.    *
010 * Or, see http://hdfgroup.org/products/hdf-java/doc/Copyright.html.         *
011 * If you do not have access to either file, you may request a copy from     *
012 * help@hdfgroup.org.                                                        *
013 ****************************************************************************/
014
015package hdf.view;
016
017import java.awt.BorderLayout;
018import java.awt.Color;
019import java.awt.Dimension;
020import java.awt.GridLayout;
021import java.awt.Point;
022import java.awt.Toolkit;
023import java.awt.event.ActionEvent;
024import java.awt.event.ActionListener;
025import java.awt.event.ItemEvent;
026import java.awt.event.ItemListener;
027import java.awt.event.KeyEvent;
028import java.util.Hashtable;
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.DefaultCellEditor;
037import javax.swing.JButton;
038import javax.swing.JCheckBox;
039import javax.swing.JComboBox;
040import javax.swing.JDialog;
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.JTable;
048import javax.swing.JTextField;
049import javax.swing.border.TitledBorder;
050import javax.swing.table.DefaultTableModel;
051import javax.swing.table.TableCellEditor;
052
053import hdf.object.CompoundDS;
054import hdf.object.DataFormat;
055import hdf.object.Dataset;
056import hdf.object.Datatype;
057import hdf.object.FileFormat;
058import hdf.object.Group;
059import hdf.object.HObject;
060
061/**
062 * NewTableDataDialog shows a message dialog requesting user input for creating
063 * a new HDF4/5 dataset.
064 * 
065 * @author Peter X. Cao
066 * @version 2.4 9/6/2007
067 */
068public class NewTableDataDialog extends JDialog implements ActionListener, ItemListener {
069    private static final long     serialVersionUID = -6786877503226330821L;
070
071    private final static org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(NewTableDataDialog.class);
072
073    private static final String[] DATATYPE_NAMES   = { 
074        "byte (8-bit)", // 0
075        "short (16-bit)", // 1
076        "int (32-bit)", // 2
077        "unsigned byte (8-bit)", // 3
078        "unsigned short (16-bit)", // 4
079        "unsigned int (32-bit)", // 5
080        "long (64-bit)", // 6
081        "float", // 7
082        "double", // 8
083        "string", // 9
084        "enum", // 10
085        "unsigned long (64-bit)" // 11
086    };
087
088    private FileFormat            fileformat;
089
090    @SuppressWarnings("rawtypes")
091    private JComboBox             parentChoice, nFieldBox, templateChoice;
092
093    /** a list of current groups */
094    private Vector<Object>        groupList, compoundDSList;
095
096    private HObject               newObject;
097
098    private final Toolkit         toolkit;
099
100    private int                   numberOfMembers;
101
102    private JTable                table;
103
104    private DefaultTableModel     tableModel;
105
106    private RowEditorModel        rowEditorModel;
107
108    private DefaultCellEditor     cellEditor;
109
110    private JTextField            nameField, currentSizeField, maxSizeField, chunkSizeField;
111    @SuppressWarnings("rawtypes")
112    private JComboBox             compressionLevel, rankChoice, memberTypeChoice;
113    private JCheckBox             checkCompression;
114    private JRadioButton          checkContinguous, checkChunked;
115
116    /**
117     * Constructs NewTableDataDialog with specified list of possible parent
118     * groups.
119     * 
120     * @param owner
121     *            the owner of the input
122     * @param pGroup
123     *            the parent group which the new group is added to.
124     * @param objs
125     *            the list of all objects.
126     */
127    @SuppressWarnings({ "unchecked", "rawtypes" })
128    public NewTableDataDialog(JFrame owner, Group pGroup, List<?> objs) {
129        super(owner, "New Compound Dataset...", true);
130
131        newObject = null;
132        numberOfMembers = 2;
133        fileformat = pGroup.getFileFormat();
134
135        memberTypeChoice = new JComboBox(DATATYPE_NAMES);
136        cellEditor = new DefaultCellEditor(memberTypeChoice);
137        rowEditorModel = new RowEditorModel(numberOfMembers, cellEditor);
138        String[] colNames = { "Name", "Datatype", "Array size / String length / Enum names" };
139        tableModel = new DefaultTableModel(colNames, numberOfMembers);
140        table = new JTable(tableModel) {
141            private static final long serialVersionUID = 7141605060652738476L;
142            RowEditorModel            rm               = rowEditorModel;
143
144            @Override
145            public TableCellEditor getCellEditor(int row, int col) {
146                TableCellEditor cellEditor = rm.getEditor(row);
147
148                if ((cellEditor == null) || !(col == 1)) {
149                    cellEditor = super.getCellEditor(row, col);
150                }
151
152                return cellEditor;
153            }
154        };
155        table.setName("CompoundDataset");
156        table.setRowSelectionAllowed(false);
157        table.setColumnSelectionAllowed(false);
158
159        // set cell height for large fonts
160        int cellRowHeight = Math.max(16, table.getFontMetrics(table.getFont()).getHeight());
161        table.setRowHeight(cellRowHeight);
162
163        toolkit = Toolkit.getDefaultToolkit();
164
165        parentChoice = new JComboBox();
166        String[] memberSizes = new String[100];
167        for (int i = 0; i < 100; i++) {
168            memberSizes[i] = String.valueOf(i + 1);
169        }
170
171        nFieldBox = new JComboBox(memberSizes);
172        nFieldBox.setName("numbermembers");
173        nFieldBox.setEditable(true);
174        nFieldBox.addActionListener(this);
175        nFieldBox.setActionCommand("Change number of members");
176        nFieldBox.setSelectedItem(String.valueOf(numberOfMembers));
177
178        groupList = new Vector<Object>(objs.size());
179        Object obj = null;
180        Iterator<?> iterator = objs.iterator();
181
182        compoundDSList = new Vector<Object>(objs.size());
183
184        while (iterator.hasNext()) {
185            obj = iterator.next();
186            if (obj instanceof Group) {
187                Group g = (Group) obj;
188                groupList.add(obj);
189                if (g.isRoot()) {
190                    parentChoice.addItem(HObject.separator);
191                }
192                else {
193                    parentChoice.addItem(g.getPath() + g.getName() + HObject.separator);
194                }
195            }
196            else if (obj instanceof CompoundDS) {
197                compoundDSList.add(obj);
198            }
199        }
200
201        templateChoice = new JComboBox(compoundDSList);
202        templateChoice.setName("templateChoice");
203        templateChoice.setSelectedIndex(-1);
204        templateChoice.addItemListener(this);
205
206        if (pGroup.isRoot()) {
207            parentChoice.setSelectedItem(HObject.separator);
208        }
209        else {
210            parentChoice.setSelectedItem(pGroup.getPath() + pGroup.getName() + HObject.separator);
211        }
212
213        JPanel contentPane = (JPanel) getContentPane();
214        contentPane.setLayout(new BorderLayout(5, 5));
215        contentPane.setBorder(BorderFactory.createEmptyBorder(15, 5, 5, 5));
216        int w = 700 + (ViewProperties.getFontSize() - 12) * 15;
217        int h = 500 + (ViewProperties.getFontSize() - 12) * 10;
218        contentPane.setPreferredSize(new Dimension(w, h));
219
220        JButton okButton = new JButton("   Ok   ");
221        okButton.setName("OK");
222        okButton.setActionCommand("Ok");
223        okButton.setMnemonic(KeyEvent.VK_O);
224        okButton.addActionListener(this);
225
226        JButton cancelButton = new JButton("Cancel");
227        cancelButton.setName("Cancel");
228        cancelButton.setMnemonic(KeyEvent.VK_C);
229        cancelButton.setActionCommand("Cancel");
230        cancelButton.addActionListener(this);
231
232        // set NAME and PARENT GROUP panel
233        JPanel namePanel = new JPanel();
234        namePanel.setLayout(new BorderLayout(5, 5));
235        JPanel tmpP = new JPanel();
236        tmpP.setLayout(new GridLayout(3, 1));
237        tmpP.add(new JLabel("   Dataset name: "));
238        tmpP.add(new JLabel("   Parent group: "));
239        tmpP.add(new JLabel("Import template: "));
240        namePanel.add(tmpP, BorderLayout.WEST);
241        tmpP = new JPanel();
242        tmpP.setLayout(new GridLayout(3, 1));
243        tmpP.add(nameField = new JTextField());
244        nameField.setName("datasetname");
245        tmpP.add(parentChoice);
246        tmpP.add(templateChoice);
247        namePanel.add(tmpP, BorderLayout.CENTER);
248
249        // set DATATSPACE
250        JPanel spacePanel = new JPanel();
251        spacePanel.setLayout(new GridLayout(2, 3, 15, 3));
252        TitledBorder border = new TitledBorder("Dataspace");
253        border.setTitleColor(Color.blue);
254        spacePanel.setBorder(border);
255
256        rankChoice = new JComboBox();
257        for (int i = 1; i < 33; i++) {
258            rankChoice.addItem(String.valueOf(i));
259        }
260        rankChoice.setSelectedIndex(0);
261
262        currentSizeField = new JTextField("1");
263        maxSizeField = new JTextField("0");
264        spacePanel.add(new JLabel("No. of dimensions"));
265        spacePanel.add(new JLabel("Current size"));
266        spacePanel.add(new JLabel("Max size (-1 for unlimited)"));
267        spacePanel.add(rankChoice);
268        spacePanel.add(currentSizeField);
269        spacePanel.add(maxSizeField);
270
271        // set storage layout and data compression
272        JPanel layoutPanel = new JPanel();
273        layoutPanel.setLayout(new BorderLayout());
274        border = new TitledBorder("Data Layout and Compression");
275        border.setTitleColor(Color.BLUE);
276        layoutPanel.setBorder(border);
277
278        checkContinguous = new JRadioButton("Contiguous");
279        checkContinguous.setSelected(true);
280        checkChunked = new JRadioButton("Chunked");
281        ButtonGroup bgroup = new ButtonGroup();
282        bgroup.add(checkChunked);
283        bgroup.add(checkContinguous);
284        chunkSizeField = new JTextField("1");
285        chunkSizeField.setEnabled(false);
286        checkCompression = new JCheckBox("gzip");
287
288        compressionLevel = new JComboBox();
289        for (int i = 0; i < 10; i++) {
290            compressionLevel.addItem(String.valueOf(i));
291        }
292        compressionLevel.setSelectedIndex(6);
293        compressionLevel.setEnabled(false);
294
295        tmpP = new JPanel();
296        tmpP.setLayout(new GridLayout(2, 1));
297        tmpP.add(new JLabel("Storage layout:  "));
298        tmpP.add(new JLabel("Compression:  "));
299        layoutPanel.add(tmpP, BorderLayout.WEST);
300
301        tmpP = new JPanel();
302        tmpP.setLayout(new GridLayout(2, 1));
303
304        JPanel tmpP0 = new JPanel();
305        tmpP0.setLayout(new GridLayout(1, 2));
306        tmpP0.add(checkContinguous);
307
308        JPanel tmpP00 = new JPanel();
309        tmpP00.setLayout(new GridLayout(1, 3));
310        tmpP00.add(checkChunked);
311        tmpP00.add(new JLabel("          Size: "));
312        tmpP00.add(chunkSizeField);
313        tmpP0.add(tmpP00);
314
315        tmpP.add(tmpP0);
316
317        tmpP0 = new JPanel();
318        tmpP0.setLayout(new GridLayout(1, 7));
319        tmpP0.add(checkCompression);
320        tmpP0.add(new JLabel("      Level: "));
321        tmpP0.add(compressionLevel);
322        tmpP0.add(new JLabel(""));
323        tmpP0.add(new JLabel(""));
324        tmpP0.add(new JLabel(""));
325        tmpP0.add(new JLabel(""));
326        tmpP.add(tmpP0);
327
328        layoutPanel.add(tmpP, BorderLayout.CENTER);
329
330        // add name, space and layout panels
331        tmpP = new JPanel();
332        tmpP.setLayout(new BorderLayout(5, 5));
333        tmpP.add(namePanel, BorderLayout.NORTH);
334        tmpP.add(spacePanel, BorderLayout.CENTER);
335        tmpP.add(layoutPanel, BorderLayout.SOUTH);
336
337        contentPane.add(tmpP, BorderLayout.NORTH);
338
339        // add field table
340        tmpP = new JPanel();
341        tmpP.setLayout(new BorderLayout(5, 5));
342        tmpP0 = new JPanel();
343        tmpP0.setLayout(new BorderLayout(5, 5));
344        tmpP0.add(new JLabel(" Number of Members:"), BorderLayout.WEST);
345        tmpP0.add(nFieldBox, BorderLayout.CENTER);
346        tmpP.add(tmpP0, BorderLayout.NORTH);
347        JScrollPane scroller = new JScrollPane(table);
348        border = new TitledBorder("Compound Datatype Properties");
349        border.setTitleColor(Color.BLUE);
350        tmpP.setBorder(border);
351        tmpP.add(scroller, BorderLayout.CENTER);
352        contentPane.add(tmpP, BorderLayout.CENTER);
353
354        // set OK and CANCEL buttons
355        JPanel buttonPanel = new JPanel();
356        buttonPanel.add(okButton);
357        buttonPanel.add(cancelButton);
358        contentPane.add(buttonPanel, BorderLayout.SOUTH);
359
360        rankChoice.addItemListener(this);
361        checkCompression.addItemListener(this);
362        checkContinguous.addItemListener(this);
363        checkChunked.addItemListener(this);
364        memberTypeChoice.addItemListener(this);
365
366        // locate the H5Property dialog
367        Point l = owner.getLocation();
368        l.x += 250;
369        l.y += 120;
370        setLocation(l);
371        validate();
372        pack();
373    }
374
375    public void actionPerformed(ActionEvent e) {
376        String cmd = e.getActionCommand();
377
378        if (cmd.equals("Ok")) {
379            try {
380                newObject = createCompoundDS();
381            }
382            catch (Exception ex) {
383                JOptionPane.showMessageDialog(this, ex, getTitle(), JOptionPane.ERROR_MESSAGE);
384            }
385
386            if (newObject != null) {
387                dispose();
388            }
389        }
390        else if (cmd.equals("Cancel")) {
391            newObject = null;
392            dispose();
393            (groupList).setSize(0);
394        }
395        else if (cmd.equals("Change number of members")) {
396            int n = 0;
397
398            try {
399                n = Integer.valueOf((String) nFieldBox.getSelectedItem()).intValue();
400            }
401            catch (Exception ex) {
402                log.debug("Change number of members:", ex);
403            }
404
405            if (n == numberOfMembers) {
406                return;
407            }
408
409            tableModel.setRowCount(n);
410            for (int i = numberOfMembers; i < n; i++) {
411                rowEditorModel.addEditorForRow(i, cellEditor);
412            }
413            numberOfMembers = n;
414        }
415    }
416
417    public void itemStateChanged(ItemEvent e) {
418        Object source = e.getSource();
419
420        if (source.equals(rankChoice)) {
421            int rank = (int)rankChoice.getSelectedIndex() + 1;
422            String currentSizeStr = "1";
423            String maxSizeStr = "0";
424
425            for (int i = 1; i < rank; i++) {
426                currentSizeStr += " x 1";
427                maxSizeStr += " x 0";
428            }
429
430            currentSizeField.setText(currentSizeStr);
431            maxSizeField.setText(maxSizeStr);
432
433            String currentStr = currentSizeField.getText();
434            int idx = currentStr.lastIndexOf("x");
435            String chunkStr = "1";
436
437            if (rank <= 1) {
438                chunkStr = currentStr;
439            }
440            else {
441                for (int i = 1; i < rank - 1; i++) {
442                    chunkStr += " x 1";
443                }
444                if (idx > 0) {
445                    chunkStr += " x " + currentStr.substring(idx + 1);
446                }
447            }
448
449            chunkSizeField.setText(chunkStr);
450        }
451        else if (source.equals(checkContinguous)) {
452            chunkSizeField.setEnabled(false);
453        }
454        else if (source.equals(checkChunked)) {
455            chunkSizeField.setEnabled(true);
456            String currentStr = currentSizeField.getText();
457            int idx = currentStr.lastIndexOf("x");
458            String chunkStr = "1";
459
460            int rank = (int)rankChoice.getSelectedIndex() + 1;
461            if (rank <= 1) {
462                chunkStr = currentStr;
463            }
464            else {
465                for (int i = 1; i < rank - 1; i++) {
466                    chunkStr += " x 1";
467                }
468                if (idx > 0) {
469                    chunkStr += " x " + currentStr.substring(idx + 1);
470                }
471            }
472
473            chunkSizeField.setText(chunkStr);
474        }
475        else if (source.equals(checkCompression)) {
476            boolean isCompressed = checkCompression.isSelected();
477
478            if (isCompressed) {
479                if (!checkChunked.isSelected()) {
480                    String currentStr = currentSizeField.getText();
481                    int idx = currentStr.lastIndexOf("x");
482                    String chunkStr = "1";
483
484                    int rank = (int)rankChoice.getSelectedIndex() + 1;
485                    if (rank <= 1) {
486                        chunkStr = currentStr;
487                    }
488                    else {
489                        for (int i = 1; i < rank - 1; i++) {
490                            chunkStr += " x 1";
491                        }
492                        if (idx > 0) {
493                            chunkStr += " x " + currentStr.substring(idx + 1);
494                        }
495                    }
496
497                    chunkSizeField.setText(chunkStr);
498                }
499                compressionLevel.setEnabled(true);
500                checkContinguous.setEnabled(false);
501                checkChunked.setSelected(true);
502                chunkSizeField.setEnabled(true);
503            }
504            else {
505                compressionLevel.setEnabled(false);
506                checkContinguous.setEnabled(true);
507            }
508        }
509        else if (source.equals(memberTypeChoice)) {
510            String item = (String) memberTypeChoice.getSelectedItem();
511            if ((item == null) || !item.equals("enum")) {
512                return;
513            }
514
515            int row = table.getSelectedRow();
516            table.setValueAt("mb1=0,mb=1,...", row, 2);
517        }
518        else if (source.equals(templateChoice)) {
519            Object obj = templateChoice.getSelectedItem();
520            if (!(obj instanceof CompoundDS)) {
521                return;
522            }
523
524            CompoundDS dset = (CompoundDS) obj;
525            int rank = dset.getRank();
526            if (rank < 1) {
527                dset.init();
528            }
529
530            rank = dset.getRank();
531            rankChoice.setSelectedIndex(rank - 1);
532            long[] dims = dset.getDims();
533            String[] mNames = dset.getMemberNames();
534            int[] mOrders = dset.getMemberOrders();
535            Datatype[] mTypes = dset.getMemberTypes();
536
537            String sizeStr = String.valueOf(dims[0]);
538            for (int i = 1; i < rank; i++) {
539                sizeStr += "x" + dims[i];
540            }
541            currentSizeField.setText(sizeStr);
542
543            try {
544                dset.getMetadata();
545            } // get chunking and compression info
546            catch (Exception ex) {
547                log.debug("get chunking and compression info:", ex);
548            }
549            long[] chunks = dset.getChunkSize();
550            if (chunks != null) {
551                checkChunked.setSelected(true);
552                sizeStr = String.valueOf(chunks[0]);
553                for (int i = 1; i < rank; i++) {
554                    sizeStr += "x" + chunks[i];
555                }
556                chunkSizeField.setText(sizeStr);
557            }
558
559            String compression = dset.getCompression();
560            if (compression != null) {
561                int clevel = -1;
562                int comp_pos = Dataset.compression_gzip_txt.length();
563                int idx = compression.indexOf(Dataset.compression_gzip_txt);
564                if (idx >= 0) {
565                    try {
566                        clevel = Integer.parseInt(compression.substring(idx + comp_pos, idx + comp_pos +1));
567                    }
568                    catch (NumberFormatException ex) {
569                        clevel = -1;
570                    }
571                }
572                if (clevel > 0) {
573                    checkCompression.setSelected(true);
574                    compressionLevel.setSelectedIndex(clevel);
575                }
576            }
577
578            numberOfMembers = dset.getMemberCount();
579            nFieldBox.setSelectedIndex(numberOfMembers - 1);
580            tableModel.setRowCount(numberOfMembers);
581            for (int i = 0; i < numberOfMembers; i++) {
582                rowEditorModel.addEditorForRow(i, cellEditor);
583
584                tableModel.setValueAt(mNames[i], i, 0);
585
586                int typeIdx = -1;
587                int tclass = mTypes[i].getDatatypeClass();
588                int tsize = mTypes[i].getDatatypeSize();
589                int tsigned = mTypes[i].getDatatypeSign();
590                if (tclass == Datatype.CLASS_ARRAY) {
591                    tclass = mTypes[i].getBasetype().getDatatypeClass();
592                    tsize = mTypes[i].getBasetype().getDatatypeSize();
593                    tsigned = mTypes[i].getBasetype().getDatatypeSign();
594                }
595                if (tclass == Datatype.CLASS_CHAR) {
596                    if (tsigned == Datatype.SIGN_NONE) {
597                        if (tsize == 1) {
598                            typeIdx = 3;
599                        }
600                    }
601                    else {
602                        if (tsize == 1) {
603                            typeIdx = 0;
604                        }
605                    }
606                }
607                if (tclass == Datatype.CLASS_INTEGER) {
608                    if (tsigned == Datatype.SIGN_NONE) {
609                        if (tsize == 1) {
610                            typeIdx = 3;
611                        }
612                        else if (tsize == 2) {
613                            typeIdx = 4;
614                        }
615                        else if (tsize == 4) {
616                            typeIdx = 5;
617                        }
618                        else {
619                            typeIdx = 11;
620                        }
621                    }
622                    else {
623                        if (tsize == 1) {
624                            typeIdx = 0;
625                        }
626                        else if (tsize == 2) {
627                            typeIdx = 1;
628                        }
629                        else if (tsize == 4) {
630                            typeIdx = 2;
631                        }
632                        else {
633                            typeIdx = 6;
634                        }
635                    }
636                }
637                else if (tclass == Datatype.CLASS_FLOAT) {
638                    if (tsize == 4) {
639                        typeIdx = 7;
640                    }
641                    else {
642                        typeIdx = 8;
643                    }
644                }
645                else if (tclass == Datatype.CLASS_STRING) {
646                    typeIdx = 9;
647                }
648                else if (tclass == Datatype.CLASS_ENUM) {
649                    typeIdx = 10;
650                }
651                if (typeIdx < 0) {
652                    continue;
653                }
654
655                memberTypeChoice.setSelectedIndex(typeIdx);
656                tableModel.setValueAt(memberTypeChoice.getSelectedItem(), i, 1);
657
658                if (tclass == Datatype.CLASS_STRING) {
659                    tableModel.setValueAt(String.valueOf(tsize), i, 2);
660                }
661                else if (tclass == Datatype.CLASS_ENUM) {
662                    tableModel.setValueAt(mTypes[i].getEnumMembers(), i, 2);
663                }
664                else {
665                    tableModel.setValueAt(String.valueOf(mOrders[i]), i, 2);
666                }
667
668            } // for (int i=0; i<numberOfMembers; i++)
669        } // else if (source.equals(templateChoice))
670    }
671
672    private HObject createCompoundDS() throws Exception {
673        HObject obj = null;
674        long dims[], maxdims[], chunks[];
675        int rank;
676
677        // stop editing the last selected cell
678        int row = table.getSelectedRow();
679        int col = table.getSelectedColumn();
680        if ((row >= 0) && (col > -0)) {
681            TableCellEditor ed = table.getCellEditor(row, col);
682            if (ed != null) {
683                ed.stopCellEditing();
684            }
685        }
686
687        maxdims = chunks = null;
688        String dname = nameField.getText();
689        if ((dname == null) || (dname.length() <= 0)) {
690            throw new IllegalArgumentException("Dataset name is empty");
691        }
692
693        Group pgroup = (Group) groupList.get(parentChoice.getSelectedIndex());
694        if (pgroup == null) {
695            throw new IllegalArgumentException("Invalid parent group");
696        }
697
698        int n = table.getRowCount();
699        if (n <= 0) {
700            return null;
701        }
702
703        String[] mNames = new String[n];
704        Datatype[] mDatatypes = new Datatype[n];
705        int[] mOrders = new int[n];
706
707        for (int i = 0; i < n; i++) {
708            String name = (String) table.getValueAt(i, 0);
709            if ((name == null) || (name.length() <= 0)) {
710                throw new IllegalArgumentException("Member name is empty");
711            }
712            mNames[i] = name;
713
714            int order = 1;
715            String orderStr = (String) table.getValueAt(i, 2);
716            if (orderStr != null) {
717                try {
718                    order = Integer.parseInt(orderStr);
719                }
720                catch (Exception ex) {
721                        log.debug("compound order:", ex);
722                }
723            }
724            mOrders[i] = order;
725
726            String typeName = (String) table.getValueAt(i, 1);
727            Datatype type = null;
728            if (DATATYPE_NAMES[0].equals(typeName)) {
729                type = fileformat.createDatatype(Datatype.CLASS_INTEGER, 1, Datatype.NATIVE, Datatype.NATIVE);
730            }
731            else if (DATATYPE_NAMES[1].equals(typeName)) {
732                type = fileformat.createDatatype(Datatype.CLASS_INTEGER, 2, Datatype.NATIVE, Datatype.NATIVE);
733            }
734            else if (DATATYPE_NAMES[2].equals(typeName)) {
735                type = fileformat.createDatatype(Datatype.CLASS_INTEGER, 4, Datatype.NATIVE, Datatype.NATIVE);
736            }
737            else if (DATATYPE_NAMES[3].equals(typeName)) {
738                type = fileformat.createDatatype(Datatype.CLASS_INTEGER, 1, Datatype.NATIVE, Datatype.SIGN_NONE);
739            }
740            else if (DATATYPE_NAMES[4].equals(typeName)) {
741                type = fileformat.createDatatype(Datatype.CLASS_INTEGER, 2, Datatype.NATIVE, Datatype.SIGN_NONE);
742            }
743            else if (DATATYPE_NAMES[5].equals(typeName)) {
744                type = fileformat.createDatatype(Datatype.CLASS_INTEGER, 4, Datatype.NATIVE, Datatype.SIGN_NONE);
745            }
746            else if (DATATYPE_NAMES[6].equals(typeName)) {
747                type = fileformat.createDatatype(Datatype.CLASS_INTEGER, 8, Datatype.NATIVE, Datatype.NATIVE);
748            }
749            else if (DATATYPE_NAMES[7].equals(typeName)) {
750                type = fileformat.createDatatype(Datatype.CLASS_FLOAT, 4, Datatype.NATIVE, Datatype.NATIVE);
751            }
752            else if (DATATYPE_NAMES[8].equals(typeName)) {
753                type = fileformat.createDatatype(Datatype.CLASS_FLOAT, 8, Datatype.NATIVE, Datatype.NATIVE);
754            }
755            else if (DATATYPE_NAMES[9].equals(typeName)) {
756                type = fileformat.createDatatype(Datatype.CLASS_STRING, order, Datatype.NATIVE, Datatype.NATIVE);
757            }
758            else if (DATATYPE_NAMES[10].equals(typeName)) { // enum
759                type = fileformat.createDatatype(Datatype.CLASS_ENUM, 4, Datatype.NATIVE, Datatype.NATIVE);
760                if ((orderStr == null) || (orderStr.length() < 1) || orderStr.endsWith("...")) {
761                    toolkit.beep();
762                    JOptionPane.showMessageDialog(this, "Invalid member values: " + orderStr, getTitle(),
763                            JOptionPane.ERROR_MESSAGE);
764                    return null;
765                }
766                else {
767                    type.setEnumMembers(orderStr);
768                }
769            }
770            else if (DATATYPE_NAMES[11].equals(typeName)) {
771                type = fileformat.createDatatype(Datatype.CLASS_INTEGER, 8, Datatype.NATIVE, Datatype.SIGN_NONE);
772            }
773            else {
774                throw new IllegalArgumentException("Invalid data type.");
775            }
776            mDatatypes[i] = type;
777        } // for (int i=0; i<n; i++)
778
779        rank = (int)rankChoice.getSelectedIndex() + 1;
780        StringTokenizer st = new StringTokenizer(currentSizeField.getText(), "x");
781        if (st.countTokens() < rank) {
782            toolkit.beep();
783            JOptionPane.showMessageDialog(this, "Number of values in the current dimension size is less than " + rank,
784                    getTitle(), JOptionPane.ERROR_MESSAGE);
785            return null;
786        }
787
788        long l = 0;
789        dims = new long[rank];
790        String token = null;
791        for (int i = 0; i < rank; i++) {
792            token = st.nextToken().trim();
793            try {
794                l = Long.parseLong(token);
795            }
796            catch (NumberFormatException ex) {
797                toolkit.beep();
798                JOptionPane.showMessageDialog(this, "Invalid dimension size: " + currentSizeField.getText(),
799                        getTitle(), JOptionPane.ERROR_MESSAGE);
800                return null;
801            }
802
803            if (l <= 0) {
804                toolkit.beep();
805                JOptionPane.showMessageDialog(this, "Dimension size must be greater than zero.", getTitle(),
806                        JOptionPane.ERROR_MESSAGE);
807                return null;
808            }
809
810            dims[i] = l;
811        }
812
813        st = new StringTokenizer(maxSizeField.getText(), "x");
814        if (st.countTokens() < rank) {
815            toolkit.beep();
816            JOptionPane.showMessageDialog(this, "Number of values in the max dimension size is less than " + rank,
817                    getTitle(), JOptionPane.ERROR_MESSAGE);
818            return null;
819        }
820
821        l = 0;
822        maxdims = new long[rank];
823        for (int i = 0; i < rank; i++) {
824            token = st.nextToken().trim();
825            try {
826                l = Long.parseLong(token);
827            }
828            catch (NumberFormatException ex) {
829                toolkit.beep();
830                JOptionPane.showMessageDialog(this, "Invalid max dimension size: " + maxSizeField.getText(),
831                        getTitle(), JOptionPane.ERROR_MESSAGE);
832                return null;
833            }
834
835            if (l < -1) {
836                toolkit.beep();
837                JOptionPane.showMessageDialog(this, "Dimension size cannot be less than -1.", getTitle(),
838                        JOptionPane.ERROR_MESSAGE);
839                return null;
840            }
841            else if (l == 0) {
842                l = dims[i];
843            }
844
845            maxdims[i] = l;
846        }
847
848        chunks = null;
849        if (checkChunked.isSelected()) {
850            st = new StringTokenizer(chunkSizeField.getText(), "x");
851            if (st.countTokens() < rank) {
852                toolkit.beep();
853                JOptionPane.showMessageDialog(this, "Number of values in the chunk size is less than " + rank,
854                        getTitle(), JOptionPane.ERROR_MESSAGE);
855                return null;
856            }
857
858            l = 0;
859            chunks = new long[rank];
860            token = null;
861            for (int i = 0; i < rank; i++) {
862                token = st.nextToken().trim();
863                try {
864                    l = Long.parseLong(token);
865                }
866                catch (NumberFormatException ex) {
867                    toolkit.beep();
868                    JOptionPane.showMessageDialog(this, "Invalid chunk dimension size: " + chunkSizeField.getText(),
869                            getTitle(), JOptionPane.ERROR_MESSAGE);
870                    return null;
871                }
872
873                if (l < 1) {
874                    toolkit.beep();
875                    JOptionPane.showMessageDialog(this, "Chunk size cannot be less than 1.", getTitle(),
876                            JOptionPane.ERROR_MESSAGE);
877                    return null;
878                }
879
880                chunks[i] = l;
881            } // for (int i=0; i<rank; i++)
882
883            long tchunksize = 1, tdimsize = 1;
884            for (int i = 0; i < rank; i++) {
885                tchunksize *= chunks[i];
886                tdimsize *= dims[i];
887            }
888
889            if (tchunksize >= tdimsize) {
890                toolkit.beep();
891                int status = JOptionPane.showConfirmDialog(this, "Chunk size is equal/greater than the current size. "
892                        + "\nAre you sure you want to set chunk size to " + chunkSizeField.getText() + "?", getTitle(),
893                        JOptionPane.YES_NO_OPTION);
894                if (status == JOptionPane.NO_OPTION) {
895                    return null;
896                }
897            }
898
899            if (tchunksize == 1) {
900                toolkit.beep();
901                int status = JOptionPane.showConfirmDialog(this,
902                        "Chunk size is one, which may cause large memory overhead for large dataset."
903                                + "\nAre you sure you want to set chunk size to " + chunkSizeField.getText() + "?",
904                                getTitle(), JOptionPane.YES_NO_OPTION);
905                if (status == JOptionPane.NO_OPTION) {
906                    return null;
907                }
908            }
909
910        } // if (checkChunked.isSelected())
911
912        int gzip = 0;
913        if (checkCompression.isSelected()) {
914            gzip = compressionLevel.getSelectedIndex();
915        }
916
917        if (checkChunked.isSelected()) {
918            obj = fileformat.createCompoundDS(dname, pgroup, dims, maxdims, chunks, gzip, mNames, mDatatypes, mOrders,
919                    null);
920        }
921        else {
922            obj = fileformat
923                    .createCompoundDS(dname, pgroup, dims, maxdims, null, -1, mNames, mDatatypes, mOrders, null);
924        }
925
926        return obj;
927    }
928
929    /**
930     * Returns the new dataset created.
931     * 
932     * @return The new Dataset created
933     */
934    public DataFormat getObject() {
935        return newObject;
936    }
937
938    /**
939     * Returns the parent group of the new dataset.
940     * 
941     * @return The parent group of the new Dataset
942     */
943    public Group getParentGroup() {
944        return (Group) groupList.get(parentChoice.getSelectedIndex());
945    }
946
947    private class RowEditorModel {
948        private Hashtable<Integer, TableCellEditor> data;
949
950        public RowEditorModel() {
951            data = new Hashtable<Integer, TableCellEditor>();
952        }
953
954        // all rows has the same cell editor
955        public RowEditorModel(int rows, TableCellEditor ed) {
956            data = new Hashtable<Integer, TableCellEditor>();
957            for (int i = 0; i < rows; i++) {
958                data.put(new Integer(i), ed);
959            }
960        }
961
962        public void addEditorForRow(int row, TableCellEditor e) {
963            data.put(new Integer(row), e);
964        }
965
966        public void removeEditorForRow(int row) {
967            data.remove(new Integer(row));
968        }
969
970        public TableCellEditor getEditor(int row) {
971            return (TableCellEditor) data.get(new Integer(row));
972        }
973    }
974}