lami: set the min value when clamping to zero.
[deliverable/tracecompass.git] / analysis / org.eclipse.tracecompass.analysis.lami.ui / src / org / eclipse / tracecompass / internal / provisional / analysis / lami / ui / viewers / LamiXYChartViewer.java
index 4a0a776a4a5ff836f1a05c8aedb91049bdb42618..dbf5c313b8c48950b1d6c19c82e6c959877d4332 100644 (file)
@@ -12,6 +12,7 @@ package org.eclipse.tracecompass.internal.provisional.analysis.lami.ui.viewers;
 import static org.eclipse.tracecompass.common.core.NonNullUtils.checkNotNull;
 import static org.eclipse.tracecompass.common.core.NonNullUtils.nullToEmptyString;
 
+import java.math.BigDecimal;
 import java.text.Format;
 import java.util.ArrayList;
 import java.util.HashSet;
@@ -23,24 +24,33 @@ import java.util.function.ToDoubleFunction;
 import org.eclipse.jdt.annotation.NonNull;
 import org.eclipse.jdt.annotation.Nullable;
 import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.SelectionEvent;
+import org.eclipse.swt.events.SelectionListener;
 import org.eclipse.swt.graphics.Color;
 import org.eclipse.swt.graphics.Font;
 import org.eclipse.swt.graphics.GC;
+import org.eclipse.swt.graphics.Image;
 import org.eclipse.swt.graphics.Point;
 import org.eclipse.swt.graphics.Rectangle;
 import org.eclipse.swt.widgets.Composite;
 import org.eclipse.swt.widgets.Control;
 import org.eclipse.swt.widgets.Display;
+import org.eclipse.swt.widgets.Event;
 import org.eclipse.swt.widgets.Listener;
+import org.eclipse.swt.widgets.ToolBar;
+import org.eclipse.swt.widgets.ToolItem;
 import org.eclipse.tracecompass.common.core.format.DecimalUnitFormat;
 import org.eclipse.tracecompass.internal.provisional.analysis.lami.core.aspect.LamiTableEntryAspect;
 import org.eclipse.tracecompass.internal.provisional.analysis.lami.core.module.LamiChartModel;
 import org.eclipse.tracecompass.internal.provisional.analysis.lami.core.module.LamiResultTable;
 import org.eclipse.tracecompass.internal.provisional.analysis.lami.core.module.LamiTableEntry;
-import org.eclipse.tracecompass.internal.provisional.analysis.lami.core.module.LamiTimeStampFormat;
+import org.eclipse.tracecompass.internal.provisional.analysis.lami.ui.format.LamiDecimalUnitFormat;
+import org.eclipse.tracecompass.internal.provisional.analysis.lami.ui.format.LamiTimeStampFormat;
 import org.eclipse.tracecompass.internal.provisional.analysis.lami.ui.signals.LamiSelectionUpdateSignal;
 import org.eclipse.tracecompass.tmf.core.signal.TmfSignalHandler;
 import org.eclipse.tracecompass.tmf.ui.viewers.TmfViewer;
+import org.eclipse.ui.ISharedImages;
+import org.eclipse.ui.PlatformUI;
 import org.swtchart.Chart;
 import org.swtchart.ITitle;
 
@@ -63,14 +73,10 @@ public abstract class LamiXYChartViewer extends TmfViewer implements ILamiViewer
      */
     protected static final String UNKNOWN = "?"; //$NON-NLS-1$
 
-    /** Zero value */
-    protected static final double ZERO = 0.0;
-
-    /** Symbol for seconds (used in the custom ns -> s conversion) */
-    private static final String SECONDS_SYMBOL = "s"; //$NON-NLS-1$
-
-    /** Symbol for nanoseconds (used in the custom ns -> s conversion) */
-    private static final String NANOSECONDS_SYMBOL = "ns"; //$NON-NLS-1$
+    /** Zero long value */
+    protected static final long ZERO_LONG = 0L;
+    /** Zero double value */
+    protected static final double ZERO_DOUBLE = 0.0;
 
     /**
      * Function to use to map Strings read from the data table to doubles for
@@ -78,7 +84,7 @@ public abstract class LamiXYChartViewer extends TmfViewer implements ILamiViewer
      */
     protected static final ToDoubleFunction<@Nullable String> DOUBLE_MAPPER = str -> {
         if (str == null || str.equals(UNKNOWN)) {
-            return ZERO;
+            return ZERO_LONG;
         }
         return Double.parseDouble(str);
     };
@@ -135,12 +141,21 @@ public abstract class LamiXYChartViewer extends TmfViewer implements ILamiViewer
     /**
      * Decimal formatter to display nanoseconds as seconds.
      */
-    protected static final DecimalUnitFormat NANO_TO_SECS_FORMATTER = new DecimalUnitFormat(0.000000001);
+    protected static final DecimalUnitFormat NANO_TO_SECS_FORMATTER = new LamiDecimalUnitFormat(0.000000001);
 
     /**
      * Default decimal formatter.
      */
-    protected static final DecimalUnitFormat DECIMAL_FORMATTER = new DecimalUnitFormat();
+    protected static final DecimalUnitFormat DECIMAL_FORMATTER = new LamiDecimalUnitFormat();
+
+    /** Symbol for seconds (used in the custom ns -> s conversion) */
+    private static final String SECONDS_SYMBOL = "s"; //$NON-NLS-1$
+
+    /** Symbol for nanoseconds (used in the custom ns -> s conversion) */
+    private static final String NANOSECONDS_SYMBOL = "ns"; //$NON-NLS-1$
+
+    /** Maximum amount of digits that can be represented into a double */
+    private static final int BIG_DECIMAL_DIVISION_SCALE = 22;
 
     private final Listener fResizeListener = event -> {
         /* Refresh the titles to fit the current chart size */
@@ -156,12 +171,18 @@ public abstract class LamiXYChartViewer extends TmfViewer implements ILamiViewer
     private final Chart fChart;
 
     private final String fChartTitle;
-    private final String fXTitle;
-    private final String fYTitle;
+
+    private String fXLabel;
+    private @Nullable String fXUnits;
+
+    private String fYLabel;
+    private @Nullable String fYUnits;
 
     private boolean fSelected;
     private Set<Integer> fSelection;
 
+    private final ToolBar fToolBar;
+
     /**
      * Creates a Viewer instance based on SWTChart.
      *
@@ -181,6 +202,9 @@ public abstract class LamiXYChartViewer extends TmfViewer implements ILamiViewer
         fChartModel = chartModel;
         fSelection = new HashSet<>();
 
+        fXLabel = ""; //$NON-NLS-1$
+        fYLabel = ""; //$NON-NLS-1$
+
         fChart = new Chart(parent, SWT.NONE);
         fChart.addListener(SWT.Resize, fResizeListener);
 
@@ -191,37 +215,36 @@ public abstract class LamiXYChartViewer extends TmfViewer implements ILamiViewer
         if (fChartModel.getXSeriesColumns().size() == 1) {
             /*
              * There is only 1 series in the chart, we will use its name as the
-             * Y axis (and hide the legend).
+             * X axis.
              */
-            String seriesName = getChartModel().getXSeriesColumns().get(0);
-            // The time duration formatter converts ns to s on the axis
-            if (NANOSECONDS_SYMBOL.equals(getXAxisAspects().get(0).getUnits())) {
-                seriesName = getXAxisAspects().get(0).getName() + " (" + SECONDS_SYMBOL + ')'; //$NON-NLS-1$
-            }
-            fXTitle = seriesName;
+            innerSetXTitle(getXAxisAspects().get(0).getName(), getXAxisAspects().get(0).getUnits());
         } else {
             /*
              * There are multiple series in the chart, if they all share the same
              * units, display that.
              */
-            long nbDiffAspects = getXAxisAspects().stream()
+            long nbDiffAspectsUnits = getXAxisAspects().stream()
                 .map(aspect -> aspect.getUnits())
                 .distinct()
                 .count();
 
-            String units = getXAxisAspects().get(0).getUnits();
-            if (nbDiffAspects == 1 && units != null) {
-                /* All aspects use the same unit type */
+            long nbDiffAspectName = getXAxisAspects().stream()
+                    .map(aspect -> aspect.getName())
+                    .distinct()
+                    .count();
 
-                // The time duration formatter converts ns to s on the axis
-                if (NANOSECONDS_SYMBOL.equals(units)) {
-                    units = SECONDS_SYMBOL;
-                }
-                fXTitle = Messages.LamiViewer_DefaultValueName + " (" + units + ')'; //$NON-NLS-1$
-            } else {
-                /* Various unit types, just say "Value" */
-                fXTitle = nullToEmptyString(Messages.LamiViewer_DefaultValueName);
+            String xBaseTitle = Messages.LamiViewer_DefaultValueName;
+            if (nbDiffAspectName == 1) {
+                xBaseTitle = getXAxisAspects().get(0).getName();
             }
+
+            String units = null;
+            if (nbDiffAspectsUnits == 1) {
+                /* All aspects use the same unit type */
+                units = getXAxisAspects().get(0).getUnits();
+            }
+
+            innerSetXTitle(xBaseTitle, units);
         }
 
         /* Set Y axis title */
@@ -230,37 +253,38 @@ public abstract class LamiXYChartViewer extends TmfViewer implements ILamiViewer
              * There is only 1 series in the chart, we will use its name as the
              * Y axis (and hide the legend).
              */
-            String seriesName = getChartModel().getYSeriesColumns().get(0);
-            // The time duration formatter converts ns to s on the axis
-            if (NANOSECONDS_SYMBOL.equals(getYAxisAspects().get(0).getUnits())) {
-                seriesName = getYAxisAspects().get(0).getName() + " (" + SECONDS_SYMBOL + ')'; //$NON-NLS-1$
-            }
-            fYTitle = seriesName;
+            innerSetYTitle(getYAxisAspects().get(0).getName(), getYAxisAspects().get(0).getUnits());
+
+            /* Hide the legend */
             fChart.getLegend().setVisible(false);
         } else {
             /*
              * There are multiple series in the chart, if they all share the same
              * units, display that.
              */
-            long nbDiffAspects = getYAxisAspects().stream()
+            long nbDiffAspectsUnits = getYAxisAspects().stream()
                 .map(aspect -> aspect.getUnits())
                 .distinct()
                 .count();
 
-            String units = getYAxisAspects().get(0).getUnits();
-            if (nbDiffAspects == 1 && units != null) {
-                /* All aspects use the same unit type */
+            long nbDiffAspectName = getYAxisAspects().stream()
+                    .map(aspect -> aspect.getName())
+                    .distinct()
+                    .count();
 
-                // The time duration formatter converts ns to s on the axis
-                if (NANOSECONDS_SYMBOL.equals(units)) {
-                    units = SECONDS_SYMBOL;
-                }
-                fYTitle = Messages.LamiViewer_DefaultValueName + " (" + units + ')'; //$NON-NLS-1$
-            } else {
-                /* Various unit types, just say "Value" */
-                fYTitle = nullToEmptyString(Messages.LamiViewer_DefaultValueName);
+            String yBaseTitle = Messages.LamiViewer_DefaultValueName;
+            if (nbDiffAspectName == 1) {
+                yBaseTitle = getYAxisAspects().get(0).getName();
             }
 
+            String units = null;
+            if (nbDiffAspectsUnits == 1) {
+                /* All aspects use the same unit type */
+                units = getYAxisAspects().get(0).getUnits();
+            }
+
+            innerSetYTitle(yBaseTitle, units);
+
             /* Put legend at the bottom */
             fChart.getLegend().setPosition(SWT.BOTTOM);
         }
@@ -278,12 +302,130 @@ public abstract class LamiXYChartViewer extends TmfViewer implements ILamiViewer
         /* Refresh the titles to fit the current chart size */
         refreshDisplayTitles();
 
+        fToolBar = createChartToolBar();
+
         fChart.addDisposeListener(e -> {
                 /* Dispose resources of this class */
                 LamiXYChartViewer.super.dispose();
         });
     }
 
+    /**
+     * Set the Y axis title and refresh the chart.
+     *
+     * @param label
+     *            the label string.
+     * @param units
+     *            the units string.
+     */
+    protected void setYTitle(@Nullable String label, @Nullable String units) {
+        innerSetYTitle(label, units);
+    }
+
+    private void innerSetYTitle(@Nullable String label, @Nullable String units) {
+        fYLabel = nullToEmptyString(label);
+        innerSetYUnits(units);
+        refreshDisplayTitles();
+    }
+
+    /**
+     * Set the units on the Y Axis title and refresh the chart.
+     *
+     * @param units
+     *            the units string.
+     */
+    protected void setYUnits(@Nullable String units) {
+        innerSetYUnits(units);
+    }
+
+    private void innerSetYUnits(@Nullable String units) {
+        /*
+         * All time durations in the Lami protocol are nanoseconds, on the
+         * charts we use an axis formater that converts back to seconds as a
+         * base unit and then uses prefixes like nano and milli depending on the
+         * range.
+         *
+         * So set the units to seconds in the title to match the base unit of
+         * the formater.
+         */
+        if (NANOSECONDS_SYMBOL.equals(units)) {
+            fYUnits = SECONDS_SYMBOL;
+        } else {
+            fYUnits = units;
+        }
+        refreshDisplayTitles();
+    }
+
+    /**
+     * Get the Y axis title string.
+     *
+     * If the units is non-null, the title will be: "label (units)"
+     *
+     * If the units is null, the title will be: "label"
+     *
+     * @return the title of the Y axis.
+     */
+    protected String getYTitle() {
+        if (fYUnits == null) {
+            return fYLabel;
+        }
+        return fYLabel + " (" + fYUnits + ")"; //$NON-NLS-1$ //$NON-NLS-2$
+    }
+
+    /**
+     * Set the X axis title and refresh the chart.
+     *
+     * @param label
+     *            the label string.
+     * @param units
+     *            the units string.
+     */
+    protected void setXTitle(@Nullable String label, @Nullable String units) {
+        innerSetXTitle(label, units);
+    }
+
+    private void innerSetXTitle(@Nullable String label, @Nullable String units) {
+        fXLabel = nullToEmptyString(label);
+        innerSetXUnits(units);
+        refreshDisplayTitles();
+    }
+
+    /**
+     * Set the units on the X Axis title.
+     *
+     * @param units
+     *            the units string
+     */
+    protected void setXUnits(@Nullable String units) {
+        innerSetXUnits(units);
+    }
+
+    private void innerSetXUnits(@Nullable String units) {
+        /* The time duration formatter converts ns to s on the axis */
+        if (NANOSECONDS_SYMBOL.equals(units)) {
+            fXUnits = SECONDS_SYMBOL;
+        } else {
+            fXUnits = units;
+        }
+        refreshDisplayTitles();
+    }
+
+    /**
+     * Get the X axis title string.
+     *
+     * If the units is non-null, the title will be: "label (units)"
+     *
+     * If the units is null, the title will be: "label"
+     *
+     * @return the title of the Y axis.
+     */
+    protected String getXTitle() {
+        if (fXUnits == null) {
+            return fXLabel;
+        }
+        return fXLabel + " (" + fXUnits + ")"; //$NON-NLS-1$ //$NON-NLS-2$
+    }
+
     /**
      * Util method to check if a list of aspects are all continuous.
      *
@@ -328,45 +470,64 @@ public abstract class LamiXYChartViewer extends TmfViewer implements ILamiViewer
      *            The list of aspects of the axis.
      * @param entries
      *            The list of entries of the chart.
+     * @param internalRange
+     *            The internal range for value transformation
+     * @param externalRange
+     *            The external range for value transformation
      * @return a formatter for the axis.
      */
-    protected static Format getContinuousAxisFormatter(List<LamiTableEntryAspect> axisAspects, List<LamiTableEntry> entries) {
+    protected static Format getContinuousAxisFormatter(List<LamiTableEntryAspect> axisAspects, List<LamiTableEntry> entries , @Nullable LamiGraphRange internalRange, @Nullable LamiGraphRange externalRange) {
+
+        Format formatter = DECIMAL_FORMATTER;
 
         if (areAspectsTimeStamp(axisAspects)) {
             /* Set a TimeStamp formatter depending on the duration between the first and last value */
-            double max = Double.MIN_VALUE;
-            double min = Double.MAX_VALUE;
+            BigDecimal max = new BigDecimal(Long.MIN_VALUE);
+            BigDecimal min = new BigDecimal(Long.MAX_VALUE);
 
             for (LamiTableEntry entry : entries) {
                 for (LamiTableEntryAspect aspect : axisAspects) {
-                    Double current = aspect.resolveDouble(entry);
-                    if (current != null) {
-                        max = Math.max(max, current);
-                        min = Math.min(min, current);
+                    @Nullable Number number = aspect.resolveNumber(entry);
+                    if (number != null) {
+                        BigDecimal current = new BigDecimal(number.toString());
+                        max = current.max(max);
+                        min = current.min(min);
                     }
                 }
             }
-            long duration = (long) max - (long) min;
 
+            long duration = max.subtract(min).longValue();
             if (duration > TimeUnit.DAYS.toNanos(1)) {
-                return DAYS_FORMATTER;
+                formatter = DAYS_FORMATTER;
             } else if (duration > TimeUnit.HOURS.toNanos(1)) {
-                return HOURS_FORMATTER;
+                formatter = HOURS_FORMATTER;
             } else if (duration > TimeUnit.MINUTES.toNanos(1)) {
-                return MINUTES_FORMATTER;
+                formatter = MINUTES_FORMATTER;
             } else if (duration > TimeUnit.SECONDS.toNanos(15)) {
-                return SECONDS_FORMATTER;
+                formatter = SECONDS_FORMATTER;
             } else {
-                return MILLISECONDS_FORMATTER;
+                formatter = MILLISECONDS_FORMATTER;
             }
+            ((LamiTimeStampFormat) formatter).setInternalRange(internalRange);
+            ((LamiTimeStampFormat) formatter).setExternalRange(externalRange);
+
         } else if (areAspectsTimeDuration(axisAspects)) {
-            /* Set the time duration formatter */
-            return NANO_TO_SECS_FORMATTER;
+            /* Set the time duration formatter. */
+            formatter = NANO_TO_SECS_FORMATTER;
+            ((LamiDecimalUnitFormat) formatter).setInternalRange(internalRange);
+            ((LamiDecimalUnitFormat) formatter).setExternalRange(externalRange);
 
         } else {
-            /* For other numeric aspects, use the default decimal unit formatter */
-            return DECIMAL_FORMATTER;
+            /*
+             * For other numeric aspects, use the default lami decimal unit
+             * formatter.
+             */
+            formatter = DECIMAL_FORMATTER;
+            ((LamiDecimalUnitFormat) formatter).setInternalRange(internalRange);
+            ((LamiDecimalUnitFormat) formatter).setExternalRange(externalRange);
         }
+
+        return formatter;
     }
 
     /**
@@ -395,6 +556,13 @@ public abstract class LamiXYChartViewer extends TmfViewer implements ILamiViewer
         return fChart;
     }
 
+    /**
+     * @return the toolBar
+     */
+    public ToolBar getToolBar() {
+        return fToolBar;
+    }
+
     /**
      * Is a selection made in the chart.
      *
@@ -538,10 +706,10 @@ public abstract class LamiXYChartViewer extends TmfViewer implements ILamiViewer
         refreshDisplayTitle(chartTitle, fChartTitle, chartRect.width);
 
         ITitle xTitle = checkNotNull(fChart.getAxisSet().getXAxis(0).getTitle());
-        refreshDisplayTitle(xTitle, fXTitle, plotRect.width);
+        refreshDisplayTitle(xTitle, getXTitle(), plotRect.width);
 
         ITitle yTitle = checkNotNull(fChart.getAxisSet().getYAxis(0).getTitle());
-        refreshDisplayTitle(yTitle, fYTitle, plotRect.height);
+        refreshDisplayTitle(yTitle, getYTitle(), plotRect.height);
     }
 
     /**
@@ -592,4 +760,165 @@ public abstract class LamiXYChartViewer extends TmfViewer implements ILamiViewer
 
         redraw();
     }
+
+    /**
+     * Create a tool bar on top right of the chart. Contained actions:
+     * <ul>
+     * <li>Dispose the current viewer, also known as "Close the chart"</li>
+     * </ul>
+     *
+     * This tool bar should only appear when the mouse enters the composite.
+     *
+     * @return the tool bar
+     */
+    protected ToolBar createChartToolBar() {
+        Image removeImage = PlatformUI.getWorkbench().getSharedImages().getImage(ISharedImages.IMG_ELCL_REMOVE);
+        ToolBar toolBar = new ToolBar(getChart(), SWT.HORIZONTAL);
+
+        /* Default state */
+        toolBar.moveAbove(null);
+        toolBar.setVisible(false);
+
+        /*
+         * Close chart button
+         */
+        ToolItem closeButton = new ToolItem(toolBar, SWT.PUSH);
+        closeButton.setImage(removeImage);
+        closeButton.setToolTipText(Messages.LamiXYChartViewer_CloseChartToolTip);
+        closeButton.addSelectionListener(new SelectionListener() {
+            @Override
+            public void widgetSelected(@Nullable SelectionEvent e) {
+                Composite parent = getParent();
+                dispose();
+                parent.layout();
+            }
+
+            @Override
+            public void widgetDefaultSelected(@Nullable SelectionEvent e) {
+            }
+        });
+
+        toolBar.pack();
+        toolBar.setLocation(new Point(getChart().getSize().x - toolBar.getSize().x, 0));
+
+        /* Visibility toggle filter */
+        Listener toolBarVisibilityToggleListener = e -> {
+            if (e.widget instanceof Control) {
+                Control control = (Control) e.widget;
+                Point display = control.toDisplay(e.x, e.y);
+                Point location = getChart().getParent().toControl(display);
+
+                /*
+                 * Only set to visible if we are at the right location, in the
+                 * right shell.
+                 */
+                boolean visible = getChart().getBounds().contains(location) &&
+                        control.getShell().equals(getChart().getShell());
+                getToolBar().setVisible(visible);
+            }
+        };
+
+        /* Filter to make sure we hide the toolbar if we exit the window */
+        Listener hideToolBarListener = (e -> getToolBar().setVisible(false));
+
+        /*
+         * Add the filters to the main Display, and remove them when we dispose
+         * the chart.
+         */
+        Display display = getChart().getDisplay();
+        display.addFilter(SWT.MouseEnter, toolBarVisibilityToggleListener);
+        display.addFilter(SWT.MouseExit, hideToolBarListener);
+
+        getChart().addDisposeListener(e -> {
+            display.removeFilter(SWT.MouseEnter, toolBarVisibilityToggleListener);
+            display.removeFilter(SWT.MouseExit, hideToolBarListener);
+        });
+
+        /* Reposition the tool bar on resize */
+        getChart().addListener(SWT.Resize, new Listener() {
+            @Override
+            public void handleEvent(@Nullable Event event) {
+                toolBar.setLocation(new Point(getChart().getSize().x - toolBar.getSize().x, 0));
+            }
+        });
+
+        return toolBar;
+    }
+
+    /**
+     * Get a {@link LamiGraphRange} that covers all data points in the result
+     * table.
+     * <p>
+     * The returned range will be the minimum and maximum of the resolved values
+     * of the passed aspects for all result entries. If <code>clampToZero</code>
+     * is true, a positive minimum value will be clamped down to zero.
+     *
+     * @param aspects
+     *            The aspects that the range will represent
+     * @param clampToZero
+     *            If true, a positive minimum value will be clamped down to zero
+     * @return the range
+     */
+    protected LamiGraphRange getRange(List<LamiTableEntryAspect> aspects, boolean clampToZero) {
+        /* Find the minimum and maximum values */
+        BigDecimal min = new BigDecimal(Long.MAX_VALUE);
+        BigDecimal max = new BigDecimal(Long.MIN_VALUE);
+        for (LamiTableEntryAspect lamiTableEntryAspect : aspects) {
+            for (LamiTableEntry entry : getResultTable().getEntries()) {
+                @Nullable Number number = lamiTableEntryAspect.resolveNumber(entry);
+                if (number != null) {
+                    BigDecimal current = new BigDecimal(number.toString());
+                    min = current.min(min);
+                    max = current.max(max);
+                }
+            }
+        }
+
+        if (clampToZero) {
+            min = min.min(BigDecimal.ZERO);
+        }
+
+        /* Do not allow a range with a zero delta default to 1 */
+        if (max.equals(min)) {
+            max = min.add(BigDecimal.ONE);
+        }
+
+        return new LamiGraphRange(checkNotNull(min), checkNotNull(max));
+    }
+
+    /**
+     * Transform an external value into an internal value. Since SWTChart only
+     * support Double and Lami can pass Long values, loss of precision might
+     * happen. To minimize this, transform the raw values to an internal
+     * representation based on a linear transformation.
+     *
+     * The internal value =
+     *
+     * ((rawValue - rawMinimum) * (internalRangeDelta/rawRangeDelta)) +
+     * internalMinimum
+     *
+     * @param number
+     *            The number to transform
+     * @param internalRange
+     *            The internal range definition to be used
+     * @param externalRange
+     *            The external range definition to be used
+     * @return the transformed value in Double comprised inside the internal
+     *         range
+     */
+    protected static double getInternalDoubleValue(Number number, LamiGraphRange internalRange, LamiGraphRange externalRange) {
+        BigDecimal value = new BigDecimal(number.toString());
+
+        if (externalRange.getDelta().compareTo(BigDecimal.ZERO) == 0) {
+            return internalRange.getMinimum().doubleValue();
+        }
+
+        BigDecimal internalValue = value
+                .subtract(externalRange.getMinimum())
+                .multiply(internalRange.getDelta())
+                .divide(externalRange.getDelta(), BIG_DECIMAL_DIVISION_SCALE, BigDecimal.ROUND_DOWN)
+                .add(internalRange.getMinimum());
+
+        return internalValue.doubleValue();
+    }
 }
This page took 0.032175 seconds and 5 git commands to generate.