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 https://support.hdfgroup.org/products/licenses.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.dialog;
016
017import java.lang.reflect.Array;
018
019import org.eclipse.swt.SWT;
020import org.eclipse.swt.events.DisposeEvent;
021import org.eclipse.swt.events.DisposeListener;
022import org.eclipse.swt.events.SelectionAdapter;
023import org.eclipse.swt.events.SelectionEvent;
024import org.eclipse.swt.graphics.Font;
025import org.eclipse.swt.graphics.Point;
026import org.eclipse.swt.graphics.Rectangle;
027import org.eclipse.swt.layout.GridData;
028import org.eclipse.swt.layout.GridLayout;
029import org.eclipse.swt.widgets.Button;
030import org.eclipse.swt.widgets.Composite;
031import org.eclipse.swt.widgets.Dialog;
032import org.eclipse.swt.widgets.Display;
033import org.eclipse.swt.widgets.Label;
034import org.eclipse.swt.widgets.List;
035import org.eclipse.swt.widgets.Shell;
036import org.eclipse.swt.widgets.Text;
037
038import hdf.view.Tools;
039import hdf.view.ViewProperties;
040
041/**
042 * MathConversionDialog shows a message dialog requesting user input for math
043 * conversion.
044 *
045 * @author Jordan T. Henderson
046 * @version 2.4 1/28/2016
047 */
048public class MathConversionDialog extends Dialog {
049    private Shell       shell;
050
051    private Font        curFont;
052
053    private Text        aField;
054
055    private Text        bField;
056
057    private Text        infoArea;
058
059    private List        functionList;
060
061    private Object      dataValue;
062
063    private char        NT;
064
065    private String[]    functionDescription;
066
067    private boolean     isConverted;
068
069    /**
070     * Constructs MathConversionDialog.
071     *
072     * @param parent
073     *            the owner of the input
074     * @param data
075     *            the data array to convert.
076     */
077    public MathConversionDialog(Shell parent, Object data) {
078        super(parent, SWT.APPLICATION_MODAL);
079
080        try {
081            curFont = new Font(
082                    Display.getCurrent(),
083                    ViewProperties.getFontType(),
084                    ViewProperties.getFontSize(),
085                    SWT.NORMAL);
086        }
087        catch (Exception ex) {
088            curFont = null;
089        }
090
091        isConverted = false;
092        dataValue = data;
093        NT = ' ';
094
095        String cName = data.getClass().getName();
096        int cIndex = cName.lastIndexOf('[');
097        if (cIndex >= 0) {
098            NT = cName.charAt(cIndex + 1);
099        }
100
101        String[] tmpStrs = {
102                "The filter by lower and upper bounds. x=a if x<a; x=b if x>b."
103                        + "\ne.g.\n x=5, [0, 127]=5\n x=-5, [0, 127]=0\n x=255, [0, 127]=127.",
104                "The absolute value of a number, the number without its sign."
105                        + "\ne.g.\n abs(5)=5\n abs(-5)=5.",
106                "Linear function." + "\ne.g.\n a=5, b=2, x=2.5, a+b*x=10.",
107                "The result of a number raised to power of a."
108                        + "\ne.g.\n x=2.5, a=10, pow(x, a)=9536.743\n x=25, a=0.5, pow(x, a)=5.",
109                "The exponential number e (i.e., 2.718...) raised to the power of x."
110                        + "\ne.g.\n exp(5.0)=148.41316\n exp(5.5)=244.69193",
111                "The natural logarithm (base e) of x."
112                        + "\ne.g.\n ln(20.085541)=3\n ln(10)=2.302585",
113                "The logarithm of x to the base of a, \"a\" must be an integer > 0."
114                        + "\ne.g.\n log(10, 2)=3.321928\n log(2, 10)=0.30103",
115                "The trigonometric sine of angle x in radians."
116                        + "\ne.g.\n sin(0.523599)=0.5\n sin(1.047198)=0.866025",
117                "The trigonometric cosine of angle x in radians."
118                        + "\ne.g.\n cos(0.523599)=0.866025\n cos(1.047198)=0.5",
119                "The trigonometric tangent of angle x in radians."
120                        + "\ne.g.\n tan(0.785398)=1\n tan(1.047198)=1.732051" };
121
122        functionDescription = tmpStrs;
123    }
124
125    /**
126     * Open the MathConversionDialog for converting data.
127     */
128    public void open() {
129        Shell parent = getParent();
130        shell = new Shell(parent, SWT.SHELL_TRIM | SWT.APPLICATION_MODAL);
131        shell.setFont(curFont);
132        shell.setText("Convert Data...");
133        shell.setImage(ViewProperties.getHdfIcon());
134        shell.setLayout(new GridLayout(1, true));
135
136        // Create content region
137        org.eclipse.swt.widgets.Group contentGroup = new org.eclipse.swt.widgets.Group(shell, SWT.NONE);
138        contentGroup.setFont(curFont);
139        contentGroup.setText("Converting Data With A Mathematic Function");
140        contentGroup.setLayout(new GridLayout(2, false));
141        contentGroup.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
142
143        String[] functionNames = { "[a, b]", "abs (x)", "a + b * x",
144                "pow (x, a)", "exp (x)", "ln (x)", "log (a, x)", "sin (x)",
145                "cos (x)", "tan (x)" };
146
147        functionList = new List(contentGroup, SWT.SINGLE | SWT.BORDER);
148        functionList.setFont(curFont);
149        functionList.setItems(functionNames);
150        GridData functionListData = new GridData(SWT.FILL, SWT.FILL, true, false);
151        functionListData.minimumWidth = 350;
152        functionList.setLayoutData(functionListData);
153        functionList.addSelectionListener(new SelectionAdapter() {
154            @Override
155            public void widgetSelected(SelectionEvent e) {
156                int index = functionList.getSelectionIndex();
157                infoArea.setText(functionDescription[index]);
158
159                if ((index == 0) || (index == 2)) {
160                    aField.setEnabled(true);
161                    bField.setEnabled(true);
162                }
163                else if ((index == 3) || (index == 6)) {
164                    aField.setEnabled(true);
165                    bField.setEnabled(false);
166                }
167                else {
168                    aField.setEnabled(false);
169                    bField.setEnabled(false);
170                }
171            }
172        });
173
174        Composite fieldComposite = new Composite(contentGroup, SWT.NONE);
175        fieldComposite.setLayout(new GridLayout(2, false));
176        fieldComposite.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false));
177
178        Label label = new Label(fieldComposite, SWT.RIGHT);
179        label.setFont(curFont);
180        label.setText("a = ");
181
182        aField = new Text(fieldComposite, SWT.SINGLE | SWT.BORDER);
183        GridData aFieldData = new GridData(SWT.FILL, SWT.FILL, true, false);
184        aFieldData.minimumWidth = 100;
185        aField.setLayoutData(aFieldData);
186        aField.setFont(curFont);
187        aField.setText("0");
188        aField.setEnabled(false);
189
190        label = new Label(fieldComposite, SWT.RIGHT);
191        label.setFont(curFont);
192        label.setText("b = ");
193
194        bField = new Text(fieldComposite, SWT.SINGLE | SWT.BORDER);
195        bField.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false));
196        bField.setFont(curFont);
197        bField.setText("1");
198        bField.setEnabled(false);
199
200        infoArea = new Text(contentGroup, SWT.MULTI | SWT.BORDER | SWT.WRAP);
201        infoArea.setEditable(false);
202        infoArea.setFont(curFont);
203        infoArea.setBackground(Display.getCurrent().getSystemColor(SWT.COLOR_GRAY));
204        GridData infoAreaData = new GridData(SWT.FILL, SWT.FILL, true, true, 2, 1);
205        infoAreaData.minimumHeight = 150;
206        infoArea.setLayoutData(infoAreaData);
207
208        // Create Ok/Cancel button region
209        Composite buttonComposite = new Composite(shell, SWT.NONE);
210        buttonComposite.setLayout(new GridLayout(2, true));
211        buttonComposite.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false, 2, 1));
212
213        Button okButton = new Button(buttonComposite, SWT.PUSH);
214        okButton.setFont(curFont);
215        okButton.setText("   &OK   ");
216        okButton.setLayoutData(new GridData(SWT.END, SWT.FILL, true, false));
217        okButton.addSelectionListener(new SelectionAdapter() {
218            @Override
219            public void widgetSelected(SelectionEvent e) {
220                isConverted = convertData();
221
222                shell.dispose();
223            }
224        });
225
226        Button cancelButton = new Button(buttonComposite, SWT.PUSH);
227        cancelButton.setFont(curFont);
228        cancelButton.setText(" &Cancel ");
229        cancelButton.setLayoutData(new GridData(SWT.BEGINNING, SWT.FILL, true, false));
230        cancelButton.addSelectionListener(new SelectionAdapter() {
231            @Override
232            public void widgetSelected(SelectionEvent e) {
233                isConverted = false;
234
235                shell.dispose();
236            }
237        });
238
239        shell.pack();
240
241        shell.addDisposeListener(new DisposeListener() {
242            @Override
243            public void widgetDisposed(DisposeEvent e) {
244                if (curFont != null) curFont.dispose();
245            }
246        });
247
248        shell.setMinimumSize(shell.computeSize(SWT.DEFAULT, SWT.DEFAULT));
249
250        Rectangle parentBounds = parent.getBounds();
251        Point shellSize = shell.getSize();
252        shell.setLocation((parentBounds.x + (parentBounds.width / 2)) - (shellSize.x / 2),
253                          (parentBounds.y + (parentBounds.height / 2)) - (shellSize.y / 2));
254
255        shell.open();
256
257        Display display = parent.getDisplay();
258        while(!shell.isDisposed()) {
259            if (!display.readAndDispatch())
260                display.sleep();
261        }
262    }
263
264    private boolean convertData() {
265        double a = 0, b = 1;
266
267        int index = functionList.getSelectionIndex();
268
269        try {
270            if ((index == 0) || (index == 2)) {
271                a = Double.parseDouble(aField.getText().trim());
272                b = Double.parseDouble(bField.getText().trim());
273            }
274            else if (index == 3) {
275                a = Double.parseDouble(aField.getText().trim());
276            }
277            else if (index == 6) {
278                a = Integer.parseInt(aField.getText().trim());
279                if (a <= 0) {
280                    shell.getDisplay().beep();
281                    Tools.showError(shell, "Convert", "a must be an integer greater than zero.");
282                    return false;
283                }
284            }
285        }
286        catch (Exception ex) {
287            shell.getDisplay().beep();
288            Tools.showError(shell, "Convert", ex.getMessage());
289            return false;
290        }
291
292        int n = Array.getLength(dataValue);
293        double value = 0, x = 0;
294
295        switch (NT) {
296            case 'B':
297                byte[] bdata = (byte[]) dataValue;
298                for (int i = 0; i < n; i++) {
299                    x = bdata[i];
300                    value = y(index, x, a, b);
301                    if ((value > Byte.MAX_VALUE) || (value < Byte.MIN_VALUE)) {
302                        Tools.showError(shell, "Convert", "Invalid byte value: " + (long) value);
303                        return false;
304                    }
305
306                    bdata[i] = (byte) value;
307                }
308                break;
309            case 'S':
310                short[] sdata = (short[]) dataValue;
311                for (int i = 0; i < n; i++) {
312                    x = sdata[i];
313                    value = y(index, x, a, b);
314                    if ((value > Short.MAX_VALUE) || (value < Short.MIN_VALUE)) {
315                        Tools.showError(shell, "Convert", "Invalid short value: " + (long) value);
316                        return false;
317                    }
318
319                    sdata[i] = (short) value;
320                }
321                break;
322            case 'I':
323                int[] idata = (int[]) dataValue;
324                for (int i = 0; i < n; i++) {
325                    x = idata[i];
326                    value = y(index, x, a, b);
327                    if ((value > Integer.MAX_VALUE) || (value < Integer.MIN_VALUE)) {
328                        Tools.showError(shell, "Convert", "Invalid int value: " + (long) value);
329                        return false;
330                    }
331
332                    idata[i] = (int) value;
333                }
334                break;
335            case 'J':
336                long[] ldata = (long[]) dataValue;
337                for (int i = 0; i < n; i++) {
338                    x = ldata[i];
339                    value = y(index, x, a, b);
340                    if ((value > Long.MAX_VALUE) || (value < Long.MIN_VALUE)) {
341                        Tools.showError(shell, "Convert", "Invalid long value: " + (long) value);
342                        return false;
343                    }
344
345                    ldata[i] = (long) value;
346                }
347                break;
348            case 'F':
349                float[] fdata = (float[]) dataValue;
350                for (int i = 0; i < n; i++) {
351                    x = fdata[i];
352                    value = y(index, x, a, b);
353                    if ((value > Float.MAX_VALUE) || (value < -Float.MAX_VALUE)
354                        || (value == Float.NaN)) {
355                        Tools.showError(shell, "Convert", "Invalid float value: " + value);
356                        return false;
357                    }
358
359                    fdata[i] = (float) value;
360                }
361                break;
362            case 'D':
363                double[] ddata = (double[]) dataValue;
364                for (int i = 0; i < n; i++) {
365                    x = ddata[i];
366                    value = y(index, x, a, b);
367                    if ((value > Double.MAX_VALUE) || (value < -Double.MAX_VALUE)
368                        || (value == Double.NaN)) {
369                        Tools.showError(shell, "Convert", "Invalid double value: " + value);
370                        return false;
371                    }
372
373                    ddata[i] = value;
374                }
375                break;
376            default:
377                break;
378        }
379
380        return true;
381    }
382
383    private double y(int index, double x, double a, double b) {
384        double y = x;
385
386        switch (index) {
387            case 0:
388                if (x < a) {
389                    y = a;
390                }
391                else if (x > b) {
392                    y = b;
393                }
394                break;
395            case 1:
396                y = Math.abs(x);
397                break;
398            case 2:
399                y = (a + b * x);
400                break;
401            case 3:
402                y = Math.pow(x, a);
403                break;
404            case 4:
405                y = Math.exp(x);
406                break;
407            case 5:
408                y = Math.log(x);
409                break;
410            case 6:
411                y = (Math.log(x) / Math.log(a));
412                break;
413            case 7:
414                y = Math.sin(x);
415                break;
416            case 8:
417                y = Math.cos(x);
418                break;
419            case 9:
420                y = Math.tan(x);
421                break;
422            default:
423                break;
424        }
425
426        return y;
427    }
428
429    /** @return true if the data is successfully converted. */
430    public boolean isConverted() {
431        return isConverted;
432    }
433}