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    public void open() {
126        Shell parent = getParent();
127        shell = new Shell(parent, SWT.SHELL_TRIM | SWT.APPLICATION_MODAL);
128        shell.setFont(curFont);
129        shell.setText("Convert Data...");
130        shell.setImage(ViewProperties.getHdfIcon());
131        shell.setLayout(new GridLayout(1, true));
132
133        // Create content region
134        org.eclipse.swt.widgets.Group contentGroup = new org.eclipse.swt.widgets.Group(shell, SWT.NONE);
135        contentGroup.setFont(curFont);
136        contentGroup.setText("Converting Data With A Mathematic Function");
137        contentGroup.setLayout(new GridLayout(2, false));
138        contentGroup.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
139
140        String[] functionNames = { "[a, b]", "abs (x)", "a + b * x",
141                "pow (x, a)", "exp (x)", "ln (x)", "log (a, x)", "sin (x)",
142                "cos (x)", "tan (x)" };
143
144        functionList = new List(contentGroup, SWT.SINGLE | SWT.BORDER);
145        functionList.setFont(curFont);
146        functionList.setItems(functionNames);
147        GridData functionListData = new GridData(SWT.FILL, SWT.FILL, true, false);
148        functionListData.minimumWidth = 350;
149        functionList.setLayoutData(functionListData);
150        functionList.addSelectionListener(new SelectionAdapter() {
151            @Override
152            public void widgetSelected(SelectionEvent e) {
153                int index = functionList.getSelectionIndex();
154                infoArea.setText(functionDescription[index]);
155
156                if ((index == 0) || (index == 2)) {
157                    aField.setEnabled(true);
158                    bField.setEnabled(true);
159                }
160                else if ((index == 3) || (index == 6)) {
161                    aField.setEnabled(true);
162                    bField.setEnabled(false);
163                }
164                else {
165                    aField.setEnabled(false);
166                    bField.setEnabled(false);
167                }
168            }
169        });
170
171        Composite fieldComposite = new Composite(contentGroup, SWT.NONE);
172        fieldComposite.setLayout(new GridLayout(2, false));
173        fieldComposite.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false));
174
175        Label label = new Label(fieldComposite, SWT.RIGHT);
176        label.setFont(curFont);
177        label.setText("a = ");
178
179        aField = new Text(fieldComposite, SWT.SINGLE | SWT.BORDER);
180        GridData aFieldData = new GridData(SWT.FILL, SWT.FILL, true, false);
181        aFieldData.minimumWidth = 100;
182        aField.setLayoutData(aFieldData);
183        aField.setFont(curFont);
184        aField.setText("0");
185        aField.setEnabled(false);
186
187        label = new Label(fieldComposite, SWT.RIGHT);
188        label.setFont(curFont);
189        label.setText("b = ");
190
191        bField = new Text(fieldComposite, SWT.SINGLE | SWT.BORDER);
192        bField.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false));
193        bField.setFont(curFont);
194        bField.setText("1");
195        bField.setEnabled(false);
196
197        infoArea = new Text(contentGroup, SWT.MULTI | SWT.BORDER | SWT.WRAP);
198        infoArea.setEditable(false);
199        infoArea.setFont(curFont);
200        infoArea.setBackground(Display.getCurrent().getSystemColor(SWT.COLOR_GRAY));
201        GridData infoAreaData = new GridData(SWT.FILL, SWT.FILL, true, true, 2, 1);
202        infoAreaData.minimumHeight = 150;
203        infoArea.setLayoutData(infoAreaData);
204
205        // Create Ok/Cancel button region
206        Composite buttonComposite = new Composite(shell, SWT.NONE);
207        buttonComposite.setLayout(new GridLayout(2, true));
208        buttonComposite.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false, 2, 1));
209
210        Button okButton = new Button(buttonComposite, SWT.PUSH);
211        okButton.setFont(curFont);
212        okButton.setText("   &OK   ");
213        okButton.setLayoutData(new GridData(SWT.END, SWT.FILL, true, false));
214        okButton.addSelectionListener(new SelectionAdapter() {
215            @Override
216            public void widgetSelected(SelectionEvent e) {
217                isConverted = convertData();
218
219                shell.dispose();
220            }
221        });
222
223        Button cancelButton = new Button(buttonComposite, SWT.PUSH);
224        cancelButton.setFont(curFont);
225        cancelButton.setText(" &Cancel ");
226        cancelButton.setLayoutData(new GridData(SWT.BEGINNING, SWT.FILL, true, false));
227        cancelButton.addSelectionListener(new SelectionAdapter() {
228            @Override
229            public void widgetSelected(SelectionEvent e) {
230                isConverted = false;
231
232                shell.dispose();
233            }
234        });
235
236        shell.pack();
237
238        shell.addDisposeListener(new DisposeListener() {
239            @Override
240            public void widgetDisposed(DisposeEvent e) {
241                if (curFont != null) curFont.dispose();
242            }
243        });
244
245        shell.setMinimumSize(shell.computeSize(SWT.DEFAULT, SWT.DEFAULT));
246
247        Rectangle parentBounds = parent.getBounds();
248        Point shellSize = shell.getSize();
249        shell.setLocation((parentBounds.x + (parentBounds.width / 2)) - (shellSize.x / 2),
250                          (parentBounds.y + (parentBounds.height / 2)) - (shellSize.y / 2));
251
252        shell.open();
253
254        Display display = parent.getDisplay();
255        while(!shell.isDisposed()) {
256            if (!display.readAndDispatch())
257                display.sleep();
258        }
259    }
260
261    private boolean convertData() {
262        double a = 0, b = 1;
263
264        int index = functionList.getSelectionIndex();
265
266        try {
267            if ((index == 0) || (index == 2)) {
268                a = Double.parseDouble(aField.getText().trim());
269                b = Double.parseDouble(bField.getText().trim());
270            }
271            else if (index == 3) {
272                a = Double.parseDouble(aField.getText().trim());
273            }
274            else if (index == 6) {
275                a = Integer.parseInt(aField.getText().trim());
276                if (a <= 0) {
277                    shell.getDisplay().beep();
278                    Tools.showError(shell, "Convert", "a must be an integer greater than zero.");
279                    return false;
280                }
281            }
282        }
283        catch (Exception ex) {
284            shell.getDisplay().beep();
285            Tools.showError(shell, "Convert", ex.getMessage());
286            return false;
287        }
288
289        int n = Array.getLength(dataValue);
290        double value = 0, x = 0;
291
292        switch (NT) {
293            case 'B':
294                byte[] bdata = (byte[]) dataValue;
295                for (int i = 0; i < n; i++) {
296                    x = bdata[i];
297                    value = y(index, x, a, b);
298                    if ((value > Byte.MAX_VALUE) || (value < Byte.MIN_VALUE)) {
299                        Tools.showError(shell, "Convert", "Invalid byte value: " + (long) value);
300                        return false;
301                    }
302
303                    bdata[i] = (byte) value;
304                }
305                break;
306            case 'S':
307                short[] sdata = (short[]) dataValue;
308                for (int i = 0; i < n; i++) {
309                    x = sdata[i];
310                    value = y(index, x, a, b);
311                    if ((value > Short.MAX_VALUE) || (value < Short.MIN_VALUE)) {
312                        Tools.showError(shell, "Convert", "Invalid short value: " + (long) value);
313                        return false;
314                    }
315
316                    sdata[i] = (short) value;
317                }
318                break;
319            case 'I':
320                int[] idata = (int[]) dataValue;
321                for (int i = 0; i < n; i++) {
322                    x = idata[i];
323                    value = y(index, x, a, b);
324                    if ((value > Integer.MAX_VALUE) || (value < Integer.MIN_VALUE)) {
325                        Tools.showError(shell, "Convert", "Invalid int value: " + (long) value);
326                        return false;
327                    }
328
329                    idata[i] = (int) value;
330                }
331                break;
332            case 'J':
333                long[] ldata = (long[]) dataValue;
334                for (int i = 0; i < n; i++) {
335                    x = ldata[i];
336                    value = y(index, x, a, b);
337                    if ((value > Long.MAX_VALUE) || (value < Long.MIN_VALUE)) {
338                        Tools.showError(shell, "Convert", "Invalid long value: " + (long) value);
339                        return false;
340                    }
341
342                    ldata[i] = (long) value;
343                }
344                break;
345            case 'F':
346                float[] fdata = (float[]) dataValue;
347                for (int i = 0; i < n; i++) {
348                    x = fdata[i];
349                    value = y(index, x, a, b);
350                    if ((value > Float.MAX_VALUE) || (value < -Float.MAX_VALUE)
351                        || (value == Float.NaN)) {
352                        Tools.showError(shell, "Convert", "Invalid float value: " + value);
353                        return false;
354                    }
355
356                    fdata[i] = (float) value;
357                }
358                break;
359            case 'D':
360                double[] ddata = (double[]) dataValue;
361                for (int i = 0; i < n; i++) {
362                    x = ddata[i];
363                    value = y(index, x, a, b);
364                    if ((value > Double.MAX_VALUE) || (value < -Double.MAX_VALUE)
365                        || (value == Double.NaN)) {
366                        Tools.showError(shell, "Convert", "Invalid double value: " + value);
367                        return false;
368                    }
369
370                    ddata[i] = value;
371                }
372                break;
373            default:
374                break;
375        }
376
377        return true;
378    }
379
380    private double y(int index, double x, double a, double b) {
381        double y = x;
382
383        switch (index) {
384            case 0:
385                if (x < a) {
386                    y = a;
387                }
388                else if (x > b) {
389                    y = b;
390                }
391                break;
392            case 1:
393                y = Math.abs(x);
394                break;
395            case 2:
396                y = (a + b * x);
397                break;
398            case 3:
399                y = Math.pow(x, a);
400                break;
401            case 4:
402                y = Math.exp(x);
403                break;
404            case 5:
405                y = Math.log(x);
406                break;
407            case 6:
408                y = (Math.log(x) / Math.log(a));
409                break;
410            case 7:
411                y = Math.sin(x);
412                break;
413            case 8:
414                y = Math.cos(x);
415                break;
416            case 9:
417                y = Math.tan(x);
418                break;
419            default:
420                break;
421        }
422
423        return y;
424    }
425
426    /** @return true if the data is successfully converted. */
427    public boolean isConverted() {
428        return isConverted;
429    }
430}