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.Dimension;
018import java.awt.GridLayout;
019import java.awt.Point;
020import java.awt.Toolkit;
021import java.awt.event.ActionEvent;
022import java.awt.event.ActionListener;
023import java.awt.event.KeyEvent;
024import java.lang.reflect.Array;
025
026import javax.swing.BorderFactory;
027import javax.swing.JButton;
028import javax.swing.JDialog;
029import javax.swing.JFrame;
030import javax.swing.JLabel;
031import javax.swing.JList;
032import javax.swing.JOptionPane;
033import javax.swing.JPanel;
034import javax.swing.JScrollPane;
035import javax.swing.JTextArea;
036import javax.swing.JTextField;
037import javax.swing.ListSelectionModel;
038import javax.swing.border.TitledBorder;
039import javax.swing.event.ListSelectionEvent;
040import javax.swing.event.ListSelectionListener;
041
042/**
043 * MathConversionDialog shows a message dialog requesting user input for math
044 * conversion.
045 *
046 */
047public class MathConversionDialog extends JDialog implements ActionListener,
048        ListSelectionListener {
049    private static final long serialVersionUID = 5136554941147830371L;
050
051    private JTextField  aField, bField;
052
053    private JTextArea   infoArea;
054
055    private JList       functionList;
056
057    private Object      dataValue;
058
059    private char        NT;
060
061    private String[]    functionDescription;
062
063    private boolean     isConverted;
064
065    private final Toolkit toolkit;
066
067    /**
068     * Constructs MathConversionDialog.
069     *
070     * @param parent
071     *            the owner of the input
072     * @param data
073     *            the data array to convert.
074     */
075    public MathConversionDialog(JFrame parent, Object data) {
076        super(parent, "Convert Data...", true);
077
078        toolkit = Toolkit.getDefaultToolkit();
079        isConverted = false;
080        dataValue = data;
081        NT = ' ';
082
083        String cName = data.getClass().getName();
084        int cIndex = cName.lastIndexOf("[");
085        if (cIndex >= 0) {
086            NT = cName.charAt(cIndex + 1);
087        }
088
089        String[] functionNames = { "[a, b]", "abs (x)", "a + b * x",
090                "pow (x, a)", "exp (x)", "ln (x)", "log (a, x)", "sin (x)",
091                "cos (x)", "tan (x)" };
092        functionList = new JList(functionNames);
093        functionList.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
094        functionList.addListSelectionListener(this);
095
096        String[] tmpStrs = {
097                "The filter by lower and upper bounds. x=a if x<a; x=b if x>b."
098                        + "\ne.g.\n x=5, [0, 127]=5\n x=-5, [0, 127]=0\n x=255, [0, 127]=127.",
099                "The absolute value of a number, the number without its sign."
100                        + "\ne.g.\n abs(5)=5\n abs(-5)=5.",
101                "Linear function." + "\ne.g.\n a=5, b=2, x=2.5, a+b*x=10.",
102                "The result of a number raised to power of a."
103                        + "\ne.g.\n x=2.5, a=10, pow(x, a)=9536.743\n x=25, a=0.5, pow(x, a)=5.",
104                "The exponential number e (i.e., 2.718...) raised to the power of x."
105                        + "\ne.g.\n exp(5.0)=148.41316\n exp(5.5)=244.69193",
106                "The natural logarithm (base e) of x."
107                        + "\ne.g.\n ln(20.085541)=3\n ln(10)=2.302585",
108                "The logarithm of x to the base of a, \"a\" must be an integer > 0."
109                        + "\ne.g.\n log(10, 2)=3.321928\n log(2, 10)=0.30103",
110                "The trigonometric sine of angle x in radians."
111                        + "\ne.g.\n sin(0.523599)=0.5\n sin(1.047198)=0.866025",
112                "The trigonometric cosine of angle x in radians."
113                        + "\ne.g.\n cos(0.523599)=0.866025\n cos(1.047198)=0.5",
114                "The trigonometric tangent of angle x in radians."
115                        + "\ne.g.\n tan(0.785398)=1\n tan(1.047198)=1.732051" };
116
117        functionDescription = tmpStrs;
118
119        JPanel contentPane = (JPanel) getContentPane();
120        contentPane.setLayout(new BorderLayout(5, 5));
121        contentPane.setBorder(BorderFactory.createEmptyBorder(10, 5, 5, 5));
122        int w = 500 + (ViewProperties.getFontSize() - 12) * 15;
123        int h = 300 + (ViewProperties.getFontSize() - 12) * 10;
124        contentPane.setPreferredSize(new Dimension(w, h));
125
126        JButton okButton = new JButton("   Ok   ");
127        okButton.setActionCommand("Ok");
128        okButton.setMnemonic(KeyEvent.VK_O);
129        okButton.addActionListener(this);
130
131        JButton cancelButton = new JButton("Cancel");
132        cancelButton.setMnemonic(KeyEvent.VK_C);
133        cancelButton.setActionCommand("Cancel");
134        cancelButton.addActionListener(this);
135
136        // set OK and CANCEL buttons
137        JPanel buttonPanel = new JPanel();
138        buttonPanel.add(okButton);
139        buttonPanel.add(cancelButton);
140        contentPane.add(buttonPanel, BorderLayout.SOUTH);
141
142        // set name, parent, width and height panel
143        JPanel centerP = new JPanel();
144        centerP.setLayout(new BorderLayout(10, 10));
145        JScrollPane scroller = new JScrollPane(functionList);
146        centerP.add(scroller, BorderLayout.CENTER);
147
148        JPanel tmpP = new JPanel();
149        tmpP.setLayout(new BorderLayout(5, 5));
150
151        JPanel tmpP0 = new JPanel();
152        tmpP0.setLayout(new GridLayout(4, 1, 5, 5));
153        tmpP0.add(new JLabel("a = "));
154        tmpP0.add(new JLabel("b = "));
155        tmpP0.add(new JLabel("                     "));
156        tmpP0.add(new JLabel("                     "));
157        tmpP.add(tmpP0, BorderLayout.WEST);
158
159        tmpP0 = new JPanel();
160        tmpP0.setLayout(new GridLayout(4, 1, 5, 5));
161        tmpP0.add(aField = new JTextField("0"));
162        tmpP0.add(bField = new JTextField("1"));
163        tmpP0.add(new JLabel("                     "));
164        tmpP0.add(new JLabel("                     "));
165        tmpP.add(tmpP0, BorderLayout.CENTER);
166
167        centerP.add(tmpP, BorderLayout.EAST);
168
169        tmpP0 = new JPanel();
170        tmpP0.setLayout(new BorderLayout());
171        tmpP0.add(infoArea = new JTextArea(4, 80), BorderLayout.CENTER);
172        infoArea.setEditable(false);
173        infoArea.setLineWrap(true);
174        infoArea.setBackground(java.awt.Color.lightGray);
175        infoArea.setWrapStyleWord(true);
176
177        centerP.setBorder(new TitledBorder(
178                "Converting Data With A Mathematic Function"));
179        centerP.add(tmpP0, BorderLayout.SOUTH);
180        aField.setEnabled(false);
181        bField.setEnabled(false);
182
183        contentPane.add(centerP, BorderLayout.CENTER);
184
185        // locate the H5Property dialog
186        Point l = parent.getLocation();
187        l.x += 250;
188        l.y += 80;
189        setLocation(l);
190        validate();
191        pack();
192    }
193
194    private boolean convertData() {
195        double a = 0, b = 1;
196
197        int index = functionList.getSelectedIndex();
198        try {
199            if ((index == 0) || (index == 2)) {
200                a = Double.parseDouble(aField.getText().trim());
201                b = Double.parseDouble(bField.getText().trim());
202            }
203            else if (index == 3) {
204                a = Double.parseDouble(aField.getText().trim());
205            }
206            else if (index == 6) {
207                a = Integer.parseInt(aField.getText().trim());
208                if (a <= 0) {
209                    toolkit.beep();
210                    JOptionPane.showMessageDialog(this,
211                            "a must be an integer greater than zero.",
212                            getTitle(), JOptionPane.ERROR_MESSAGE);
213                    return false;
214                }
215            }
216        }
217        catch (Exception ex) {
218            toolkit.beep();
219            JOptionPane.showMessageDialog(this, ex.getMessage(), getTitle(),
220                    JOptionPane.ERROR_MESSAGE);
221            return false;
222        }
223
224        int n = Array.getLength(dataValue);
225        double value = 0, x = 0;
226
227        switch (NT) {
228        case 'B':
229            byte[] bdata = (byte[]) dataValue;
230            for (int i = 0; i < n; i++) {
231                x = bdata[i];
232                value = y(index, x, a, b);
233                if ((value > Byte.MAX_VALUE) || (value < Byte.MIN_VALUE)) {
234                    JOptionPane.showMessageDialog(this, "Invalid byte value: "
235                            + (long) value, getTitle(),
236                            JOptionPane.ERROR_MESSAGE);
237                    return false;
238                }
239
240                bdata[i] = (byte) value;
241            } // for (int i=0; i<n; i++)
242            break;
243        case 'S':
244            short[] sdata = (short[]) dataValue;
245            for (int i = 0; i < n; i++) {
246                x = sdata[i];
247                value = y(index, x, a, b);
248                if ((value > Short.MAX_VALUE) || (value < Short.MIN_VALUE)) {
249                    JOptionPane.showMessageDialog(this, "Invalid short value: "
250                            + (long) value, getTitle(),
251                            JOptionPane.ERROR_MESSAGE);
252                    return false;
253                }
254
255                sdata[i] = (short) value;
256            } // for (int i=0; i<n; i++)
257            break;
258        case 'I':
259            int[] idata = (int[]) dataValue;
260            for (int i = 0; i < n; i++) {
261                x = idata[i];
262                value = y(index, x, a, b);
263                if ((value > Integer.MAX_VALUE) || (value < Integer.MIN_VALUE)) {
264                    JOptionPane.showMessageDialog(this, "Invalid int value: "
265                            + (long) value, getTitle(),
266                            JOptionPane.ERROR_MESSAGE);
267                    return false;
268                }
269
270                idata[i] = (int) value;
271            } // for (int i=0; i<n; i++)
272            break;
273        case 'J':
274            long[] ldata = (long[]) dataValue;
275            for (int i = 0; i < n; i++) {
276                x = ldata[i];
277                value = y(index, x, a, b);
278                if ((value > Long.MAX_VALUE) || (value < Long.MIN_VALUE)) {
279                    JOptionPane.showMessageDialog(this, "Invalid long value: "
280                            + (long) value, getTitle(),
281                            JOptionPane.ERROR_MESSAGE);
282                    return false;
283                }
284
285                ldata[i] = (long) value;
286            } // for (int i=0; i<n; i++)
287            break;
288        case 'F':
289            float[] fdata = (float[]) dataValue;
290            for (int i = 0; i < n; i++) {
291                x = fdata[i];
292                value = y(index, x, a, b);
293                if ((value > Float.MAX_VALUE) || (value < -Float.MAX_VALUE)
294                        || (value == Float.NaN)) {
295                    JOptionPane.showMessageDialog(this, "Invalid float value: "
296                            + value, getTitle(), JOptionPane.ERROR_MESSAGE);
297                    return false;
298                }
299
300                fdata[i] = (float) value;
301            } // for (int i=0; i<n; i++)
302            break;
303        case 'D':
304            double[] ddata = (double[]) dataValue;
305            for (int i = 0; i < n; i++) {
306                x = ddata[i];
307                value = y(index, x, a, b);
308                if ((value > Double.MAX_VALUE) || (value < -Double.MAX_VALUE)
309                        || (value == Double.NaN)) {
310                    JOptionPane.showMessageDialog(this,
311                            "Invalid double value: " + value, getTitle(),
312                            JOptionPane.ERROR_MESSAGE);
313                    return false;
314                }
315
316                ddata[i] = value;
317            } // for (int i=0; i<n; i++)
318            break;
319        default:
320            break;
321        }
322
323        return true;
324    }
325
326    public void actionPerformed(ActionEvent e) {
327        Object source = e.getSource();
328        String cmd = e.getActionCommand();
329
330        if (cmd.equals("Ok")) {
331            isConverted = convertData();
332            // if (isConverted)
333            dispose();
334        }
335        if (cmd.equals("Cancel")) {
336            isConverted = false;
337            dispose();
338        }
339    }
340
341    public void valueChanged(ListSelectionEvent e) {
342        if (e.getValueIsAdjusting()) {
343            return;
344        }
345
346        if (!e.getSource().equals(functionList)) {
347            return;
348        }
349
350        if (functionList.isSelectionEmpty()) {
351            return;
352        }
353
354        int index = functionList.getSelectedIndex();
355        infoArea.setText(functionDescription[index]);
356
357        if ((index == 0) || (index == 2)) {
358            aField.setEnabled(true);
359            bField.setEnabled(true);
360        }
361        else if ((index == 3) || (index == 6)) {
362            aField.setEnabled(true);
363            bField.setEnabled(false);
364        }
365        else {
366            aField.setEnabled(false);
367            bField.setEnabled(false);
368        }
369    }
370
371    private double y(int index, double x, double a, double b) {
372        double y = x;
373        switch (index) {
374        case 0:
375            if (x < a) {
376                y = a;
377            }
378            else if (x > b) {
379                y = b;
380            }
381            break;
382        case 1:
383            y = Math.abs(x);
384            break;
385        case 2:
386            y = (a + b * x);
387            break;
388        case 3:
389            y = Math.pow(x, a);
390            break;
391        case 4:
392            y = Math.exp(x);
393            break;
394        case 5:
395            y = Math.log(x);
396            break;
397        case 6:
398            y = (Math.log(x) / Math.log(a));
399            break;
400        case 7:
401            y = Math.sin(x);
402            break;
403        case 8:
404            y = Math.cos(x);
405            break;
406        case 9:
407            y = Math.tan(x);
408            break;
409        default:
410            y = x;
411            break;
412        }
413
414        return y;
415    }
416
417    /** @return true if the data is successfully converted. */
418    public boolean isConverted() {
419        return isConverted;
420    }
421}