From: Jonathan Rajotte Date: Tue, 10 May 2016 22:42:14 +0000 (-0400) Subject: analysis.lami: correctly handle Number (double, long etc.) type graphing X-Git-Url: http://git.efficios.com/?p=deliverable%2Ftracecompass.git;a=commitdiff_plain;h=5b973e7c65f168e79139fdc0045dda56ff71f650 analysis.lami: correctly handle Number (double, long etc.) type graphing LTTng analysis return mostly long values either for the time stamps or actual measurements. SWTChart, on the other hand, deals only with "double" numerical values. Casting long to double causes a loss of precision for big long value (e.g time stamps). E.g: Loss of precision occurs when time ranges with a big minimal value (2^62) but with little delta (1 ns) between events exist. Graphs generated from such time ranges as an axis would result in a single dot which is a clear problem. The presented solution uses linear mapping to preserve resolution and precision when possible. The linear mapping requires two ranges: the internal range and the external range. Each graph is responsible to provide an internal LamiGraphRange. This range is the internal representation in double in which all raw (external) value are to be mapped. For now the default internal range is 0 to 1. The external range [raw values range] is generated by finding the minimal value and maximal values of aspects to be plotted. Each point is then mapped to a corresponding value from the internal range: eV = external value eR = external range iV = internal value iR = internal range iV = (( eV - eR.minimum ) * ( iR.delta / eR.delta )) + iR.minimum Since the default internal range is from 0 to 1 all raw values are mapped to a value from 0 to 1. On graph tick generation axis formatter transforms internal representation to external representation and formats the result. Other change: - Aspects now return their numerical values via resolveNumber to ensure no casting is done. - Move axis formatter to ui plugin. Bug: 493941 Change-Id: I289180e10a7f1cbf6ecdd1beba93549b8fbe4c23 Signed-off-by: Jonathan Rajotte Signed-off-by: Alexandre Montplaisir Reviewed-on: https://git.eclipse.org/r/73239 Reviewed-by: Patrick Tasse Tested-by: Patrick Tasse Reviewed-by: Hudson CI --- diff --git a/analysis/org.eclipse.tracecompass.analysis.lami.core.tests/src/org/eclipse/tracecompass/analysis/lami/core/tests/LamiJsonParserTest.java b/analysis/org.eclipse.tracecompass.analysis.lami.core.tests/src/org/eclipse/tracecompass/analysis/lami/core/tests/LamiJsonParserTest.java index 0f50cd5e28..577aeb0c35 100644 --- a/analysis/org.eclipse.tracecompass.analysis.lami.core.tests/src/org/eclipse/tracecompass/analysis/lami/core/tests/LamiJsonParserTest.java +++ b/analysis/org.eclipse.tracecompass.analysis.lami.core.tests/src/org/eclipse/tracecompass/analysis/lami/core/tests/LamiJsonParserTest.java @@ -201,12 +201,12 @@ public class LamiJsonParserTest { List aspects = perSyscallClass.getAspects(); assertEquals("read()", aspects.get(0).resolveString(readEntry)); - assertEquals(2398123.0, checkNotNull(aspects.get(1).resolveDouble(readEntry)).doubleValue(), DELTA); - assertEquals(8123982.0, checkNotNull(aspects.get(2).resolveDouble(readEntry)).doubleValue(), DELTA); - assertEquals(223232.0, checkNotNull(aspects.get(3).resolveDouble(readEntry)).doubleValue(), DELTA); - assertEquals(98233.0, checkNotNull(aspects.get(4).resolveDouble(readEntry)).doubleValue(), DELTA); - assertEquals(1293828.0, checkNotNull(aspects.get(5).resolveDouble(readEntry)).doubleValue(), DELTA); - assertEquals(1195595.0, checkNotNull(aspects.get(6).resolveDouble(readEntry)).doubleValue(), DELTA); + assertEquals(2398123.0, checkNotNull(aspects.get(1).resolveNumber(readEntry)).doubleValue(), DELTA); + assertEquals(8123982.0, checkNotNull(aspects.get(2).resolveNumber(readEntry)).doubleValue(), DELTA); + assertEquals(223232.0, checkNotNull(aspects.get(3).resolveNumber(readEntry)).doubleValue(), DELTA); + assertEquals(98233.0, checkNotNull(aspects.get(4).resolveNumber(readEntry)).doubleValue(), DELTA); + assertEquals(1293828.0, checkNotNull(aspects.get(5).resolveNumber(readEntry)).doubleValue(), DELTA); + assertEquals(1195595.0, checkNotNull(aspects.get(6).resolveNumber(readEntry)).doubleValue(), DELTA); assertNull(aspects.get(7).resolveString(readEntry)); } diff --git a/analysis/org.eclipse.tracecompass.analysis.lami.core/src/org/eclipse/tracecompass/internal/provisional/analysis/lami/core/aspect/LamiDurationAspect.java b/analysis/org.eclipse.tracecompass.analysis.lami.core/src/org/eclipse/tracecompass/internal/provisional/analysis/lami/core/aspect/LamiDurationAspect.java index 069b8f7b5e..bfdf3261d9 100644 --- a/analysis/org.eclipse.tracecompass.analysis.lami.core/src/org/eclipse/tracecompass/internal/provisional/analysis/lami/core/aspect/LamiDurationAspect.java +++ b/analysis/org.eclipse.tracecompass.analysis.lami.core/src/org/eclipse/tracecompass/internal/provisional/analysis/lami/core/aspect/LamiDurationAspect.java @@ -65,11 +65,11 @@ public class LamiDurationAspect extends LamiTableEntryAspect { } @Override - public @Nullable Double resolveDouble(@NonNull LamiTableEntry entry) { + public @Nullable Number resolveNumber(@NonNull LamiTableEntry entry) { LamiData data = entry.getValue(fColIndex); if (data instanceof LamiDuration) { LamiDuration range = (LamiDuration) data; - return Double.valueOf(range.getValue()); + return range.getValue(); } return null; } @@ -77,13 +77,21 @@ public class LamiDurationAspect extends LamiTableEntryAspect { @Override public @NonNull Comparator<@NonNull LamiTableEntry> getComparator() { return (o1, o2) -> { - Double dO1 = resolveDouble(o1); - Double dO2 = resolveDouble(o2); - if (dO1 == null || dO2 == null) { + Number d1 = resolveNumber(o1); + Number d2 = resolveNumber(o2); + + if (d1 == null && d2 == null) { return 0; } + if (d1 == null) { + return 1; + } + + if (d2 == null) { + return -1; + } - return dO1.compareTo(dO2); + return Long.compare(d1.longValue(), d2.longValue()); }; } diff --git a/analysis/org.eclipse.tracecompass.analysis.lami.core/src/org/eclipse/tracecompass/internal/provisional/analysis/lami/core/aspect/LamiEmptyAspect.java b/analysis/org.eclipse.tracecompass.analysis.lami.core/src/org/eclipse/tracecompass/internal/provisional/analysis/lami/core/aspect/LamiEmptyAspect.java index 49004a7177..444df6d87c 100644 --- a/analysis/org.eclipse.tracecompass.analysis.lami.core/src/org/eclipse/tracecompass/internal/provisional/analysis/lami/core/aspect/LamiEmptyAspect.java +++ b/analysis/org.eclipse.tracecompass.analysis.lami.core/src/org/eclipse/tracecompass/internal/provisional/analysis/lami/core/aspect/LamiEmptyAspect.java @@ -46,7 +46,7 @@ public class LamiEmptyAspect extends LamiTableEntryAspect { } @Override - public @Nullable Double resolveDouble(@NonNull LamiTableEntry entry) { + public @Nullable Number resolveNumber(@NonNull LamiTableEntry entry) { return null; } diff --git a/analysis/org.eclipse.tracecompass.analysis.lami.core/src/org/eclipse/tracecompass/internal/provisional/analysis/lami/core/aspect/LamiGenericAspect.java b/analysis/org.eclipse.tracecompass.analysis.lami.core/src/org/eclipse/tracecompass/internal/provisional/analysis/lami/core/aspect/LamiGenericAspect.java index 075bdccb08..d1db60823b 100644 --- a/analysis/org.eclipse.tracecompass.analysis.lami.core/src/org/eclipse/tracecompass/internal/provisional/analysis/lami/core/aspect/LamiGenericAspect.java +++ b/analysis/org.eclipse.tracecompass.analysis.lami.core/src/org/eclipse/tracecompass/internal/provisional/analysis/lami/core/aspect/LamiGenericAspect.java @@ -63,7 +63,7 @@ public class LamiGenericAspect extends LamiTableEntryAspect { } @Override - public @Nullable Double resolveDouble(@NonNull LamiTableEntry entry) { + public @Nullable Number resolveNumber(@NonNull LamiTableEntry entry) { if (fIsContinuous) { try { if (entry.getValue(fColIndex).toString() != null) { @@ -80,13 +80,21 @@ public class LamiGenericAspect extends LamiTableEntryAspect { public Comparator getComparator() { if (isContinuous()) { return (o1, o2) -> { - Double dO1 = resolveDouble(o1); - Double dO2 = resolveDouble(o2); - if (dO1 == null || dO2 == null) { + Number d1 = resolveNumber(o1); + Number d2 = resolveNumber(o2); + + if (d1 == null && d2 == null) { return 0; } + if (d1 == null) { + return 1; + } + + if (d2 == null) { + return -1; + } - return dO1.compareTo(dO2); + return Double.compare(d1.doubleValue(), d2.doubleValue()); }; } @@ -95,9 +103,16 @@ public class LamiGenericAspect extends LamiTableEntryAspect { String s1 = resolveString(o1); String s2 = resolveString(o2); - if (s1 == null || s2 == null) { + if (s1 == null && s2 == null) { return 0; } + if (s1 == null) { + return 1; + } + + if (s2 == null) { + return -1; + } return s1.compareTo(s2); }; diff --git a/analysis/org.eclipse.tracecompass.analysis.lami.core/src/org/eclipse/tracecompass/internal/provisional/analysis/lami/core/aspect/LamiIRQNameAspect.java b/analysis/org.eclipse.tracecompass.analysis.lami.core/src/org/eclipse/tracecompass/internal/provisional/analysis/lami/core/aspect/LamiIRQNameAspect.java index f4dc2016c8..7a470efe13 100644 --- a/analysis/org.eclipse.tracecompass.analysis.lami.core/src/org/eclipse/tracecompass/internal/provisional/analysis/lami/core/aspect/LamiIRQNameAspect.java +++ b/analysis/org.eclipse.tracecompass.analysis.lami.core/src/org/eclipse/tracecompass/internal/provisional/analysis/lami/core/aspect/LamiIRQNameAspect.java @@ -62,7 +62,7 @@ public class LamiIRQNameAspect extends LamiTableEntryAspect { } @Override - public @Nullable Double resolveDouble(LamiTableEntry entry) { + public @Nullable Number resolveNumber(LamiTableEntry entry) { return null; } @@ -72,9 +72,16 @@ public class LamiIRQNameAspect extends LamiTableEntryAspect { String s1 = resolveString(o1); String s2 = resolveString(o2); - if (s1 == null || s2 == null) { + if (s1 == null && s2 == null) { return 0; } + if (s1 == null) { + return 1; + } + + if (s2 == null) { + return -1; + } return s1.compareTo(s2); }; diff --git a/analysis/org.eclipse.tracecompass.analysis.lami.core/src/org/eclipse/tracecompass/internal/provisional/analysis/lami/core/aspect/LamiIRQNumberAspect.java b/analysis/org.eclipse.tracecompass.analysis.lami.core/src/org/eclipse/tracecompass/internal/provisional/analysis/lami/core/aspect/LamiIRQNumberAspect.java index 81da9b361d..88a9799106 100644 --- a/analysis/org.eclipse.tracecompass.analysis.lami.core/src/org/eclipse/tracecompass/internal/provisional/analysis/lami/core/aspect/LamiIRQNumberAspect.java +++ b/analysis/org.eclipse.tracecompass.analysis.lami.core/src/org/eclipse/tracecompass/internal/provisional/analysis/lami/core/aspect/LamiIRQNumberAspect.java @@ -61,10 +61,10 @@ public class LamiIRQNumberAspect extends LamiTableEntryAspect { } @Override - public @Nullable Double resolveDouble(LamiTableEntry entry) { + public @Nullable Number resolveNumber(LamiTableEntry entry) { LamiData data = entry.getValue(fColIndex); if (data instanceof LamiIRQ) { - return Double.valueOf(((LamiIRQ) data).getNumber()); + return (((LamiIRQ) data).getNumber()); } return null; @@ -73,13 +73,21 @@ public class LamiIRQNumberAspect extends LamiTableEntryAspect { @Override public Comparator getComparator() { return (o1, o2) -> { - Double dO1 = resolveDouble(o1); - Double dO2 = resolveDouble(o2); - if (dO1 == null || dO2 == null) { + Number d1 = resolveNumber(o1); + Number d2 = resolveNumber(o2); + + if (d1 == null && d2 == null) { return 0; } + if (d1 == null) { + return 1; + } + + if (d2 == null) { + return -1; + } - return dO1.compareTo(dO2); + return Integer.compare(d1.intValue(), d2.intValue()); }; } diff --git a/analysis/org.eclipse.tracecompass.analysis.lami.core/src/org/eclipse/tracecompass/internal/provisional/analysis/lami/core/aspect/LamiIRQTypeAspect.java b/analysis/org.eclipse.tracecompass.analysis.lami.core/src/org/eclipse/tracecompass/internal/provisional/analysis/lami/core/aspect/LamiIRQTypeAspect.java index dbd6fcb63f..375e7ee600 100644 --- a/analysis/org.eclipse.tracecompass.analysis.lami.core/src/org/eclipse/tracecompass/internal/provisional/analysis/lami/core/aspect/LamiIRQTypeAspect.java +++ b/analysis/org.eclipse.tracecompass.analysis.lami.core/src/org/eclipse/tracecompass/internal/provisional/analysis/lami/core/aspect/LamiIRQTypeAspect.java @@ -70,7 +70,7 @@ public class LamiIRQTypeAspect extends LamiTableEntryAspect { } @Override - public @Nullable Double resolveDouble(LamiTableEntry entry) { + public @Nullable Number resolveNumber(LamiTableEntry entry) { return null; } @@ -80,9 +80,16 @@ public class LamiIRQTypeAspect extends LamiTableEntryAspect { String s1 = resolveString(o1); String s2 = resolveString(o2); - if (s1 == null || s2 == null) { + if (s1 == null && s2 == null) { return 0; } + if (s1 == null) { + return 1; + } + + if (s2 == null) { + return -1; + } return s1.compareTo(s2); }; diff --git a/analysis/org.eclipse.tracecompass.analysis.lami.core/src/org/eclipse/tracecompass/internal/provisional/analysis/lami/core/aspect/LamiMixedAspect.java b/analysis/org.eclipse.tracecompass.analysis.lami.core/src/org/eclipse/tracecompass/internal/provisional/analysis/lami/core/aspect/LamiMixedAspect.java index 07b745b31d..ed61b96084 100644 --- a/analysis/org.eclipse.tracecompass.analysis.lami.core/src/org/eclipse/tracecompass/internal/provisional/analysis/lami/core/aspect/LamiMixedAspect.java +++ b/analysis/org.eclipse.tracecompass.analysis.lami.core/src/org/eclipse/tracecompass/internal/provisional/analysis/lami/core/aspect/LamiMixedAspect.java @@ -71,7 +71,7 @@ public class LamiMixedAspect extends LamiTableEntryAspect { } @Override - public @Nullable Double resolveDouble(LamiTableEntry entry) { + public @Nullable Number resolveNumber(LamiTableEntry entry) { return null; } @@ -81,9 +81,16 @@ public class LamiMixedAspect extends LamiTableEntryAspect { String s1 = resolveString(o1); String s2 = resolveString(o2); - if (s1 == null || s2 == null) { + if (s1 == null && s2 == null) { return 0; } + if (s1 == null) { + return 1; + } + + if (s2 == null) { + return -1; + } return s1.compareTo(s2); }; diff --git a/analysis/org.eclipse.tracecompass.analysis.lami.core/src/org/eclipse/tracecompass/internal/provisional/analysis/lami/core/aspect/LamiProcessNameAspect.java b/analysis/org.eclipse.tracecompass.analysis.lami.core/src/org/eclipse/tracecompass/internal/provisional/analysis/lami/core/aspect/LamiProcessNameAspect.java index c9278b3111..0335a41b16 100644 --- a/analysis/org.eclipse.tracecompass.analysis.lami.core/src/org/eclipse/tracecompass/internal/provisional/analysis/lami/core/aspect/LamiProcessNameAspect.java +++ b/analysis/org.eclipse.tracecompass.analysis.lami.core/src/org/eclipse/tracecompass/internal/provisional/analysis/lami/core/aspect/LamiProcessNameAspect.java @@ -59,7 +59,7 @@ public class LamiProcessNameAspect extends LamiTableEntryAspect { } @Override - public @Nullable Double resolveDouble(LamiTableEntry entry) { + public @Nullable Number resolveNumber(LamiTableEntry entry) { return null; } @@ -69,9 +69,16 @@ public class LamiProcessNameAspect extends LamiTableEntryAspect { String s1 = resolveString(o1); String s2 = resolveString(o2); - if (s1 == null || s2 == null) { + if (s1 == null && s2 == null) { return 0; } + if (s1 == null) { + return 1; + } + + if (s2 == null) { + return -1; + } return s1.compareTo(s2); }; diff --git a/analysis/org.eclipse.tracecompass.analysis.lami.core/src/org/eclipse/tracecompass/internal/provisional/analysis/lami/core/aspect/LamiProcessPIDAspect.java b/analysis/org.eclipse.tracecompass.analysis.lami.core/src/org/eclipse/tracecompass/internal/provisional/analysis/lami/core/aspect/LamiProcessPIDAspect.java index b242fa5400..1b8d0e737c 100644 --- a/analysis/org.eclipse.tracecompass.analysis.lami.core/src/org/eclipse/tracecompass/internal/provisional/analysis/lami/core/aspect/LamiProcessPIDAspect.java +++ b/analysis/org.eclipse.tracecompass.analysis.lami.core/src/org/eclipse/tracecompass/internal/provisional/analysis/lami/core/aspect/LamiProcessPIDAspect.java @@ -65,16 +65,11 @@ public class LamiProcessPIDAspect extends LamiTableEntryAspect { } @Override - public @Nullable Double resolveDouble(LamiTableEntry entry) { + public @Nullable Number resolveNumber(LamiTableEntry entry) { LamiData data = entry.getValue(fColIndex); if (data instanceof LamiProcess) { Long pid = ((LamiProcess) data).getPID(); - - if (pid == null) { - return null; - } - - return Double.valueOf(pid); + return pid; } return null; @@ -83,13 +78,21 @@ public class LamiProcessPIDAspect extends LamiTableEntryAspect { @Override public Comparator getComparator() { return (o1, o2) -> { - Double dO1 = resolveDouble(o1); - Double dO2 = resolveDouble(o2); - if (dO1 == null || dO2 == null) { + Number d1 = resolveNumber(o1); + Number d2 = resolveNumber(o2); + + if (d1 == null && d2 == null) { return 0; } + if (d1 == null) { + return 1; + } + + if (d2 == null) { + return -1; + } - return dO1.compareTo(dO2); + return Long.compare(d1.longValue(), d2.longValue()); }; } } diff --git a/analysis/org.eclipse.tracecompass.analysis.lami.core/src/org/eclipse/tracecompass/internal/provisional/analysis/lami/core/aspect/LamiProcessTIDAspect.java b/analysis/org.eclipse.tracecompass.analysis.lami.core/src/org/eclipse/tracecompass/internal/provisional/analysis/lami/core/aspect/LamiProcessTIDAspect.java index 5fea94a78b..74c38b369c 100644 --- a/analysis/org.eclipse.tracecompass.analysis.lami.core/src/org/eclipse/tracecompass/internal/provisional/analysis/lami/core/aspect/LamiProcessTIDAspect.java +++ b/analysis/org.eclipse.tracecompass.analysis.lami.core/src/org/eclipse/tracecompass/internal/provisional/analysis/lami/core/aspect/LamiProcessTIDAspect.java @@ -65,16 +65,11 @@ public class LamiProcessTIDAspect extends LamiTableEntryAspect { } @Override - public @Nullable Double resolveDouble(LamiTableEntry entry) { + public @Nullable Number resolveNumber(LamiTableEntry entry) { LamiData data = entry.getValue(fColIndex); if (data instanceof LamiProcess) { Long tid = ((LamiProcess) data).getTID(); - - if (tid == null) { - return null; - } - - return Double.valueOf(tid); + return tid; } return null; @@ -83,13 +78,21 @@ public class LamiProcessTIDAspect extends LamiTableEntryAspect { @Override public Comparator getComparator() { return (o1, o2) -> { - Double dO1 = resolveDouble(o1); - Double dO2 = resolveDouble(o2); - if (dO1 == null || dO2 == null) { + Number d1 = resolveNumber(o1); + Number d2 = resolveNumber(o2); + + if (d1 == null && d2 == null) { return 0; } + if (d1 == null) { + return 1; + } + + if (d2 == null) { + return -1; + } - return dO1.compareTo(dO2); + return Long.compare(d1.longValue(), d2.longValue()); }; } } diff --git a/analysis/org.eclipse.tracecompass.analysis.lami.core/src/org/eclipse/tracecompass/internal/provisional/analysis/lami/core/aspect/LamiTableEntryAspect.java b/analysis/org.eclipse.tracecompass.analysis.lami.core/src/org/eclipse/tracecompass/internal/provisional/analysis/lami/core/aspect/LamiTableEntryAspect.java index a0705625f7..6db4ca770b 100644 --- a/analysis/org.eclipse.tracecompass.analysis.lami.core/src/org/eclipse/tracecompass/internal/provisional/analysis/lami/core/aspect/LamiTableEntryAspect.java +++ b/analysis/org.eclipse.tracecompass.analysis.lami.core/src/org/eclipse/tracecompass/internal/provisional/analysis/lami/core/aspect/LamiTableEntryAspect.java @@ -117,7 +117,7 @@ public abstract class LamiTableEntryAspect { * The table row * @return The double value for the given cell */ - public abstract @Nullable Double resolveDouble(LamiTableEntry entry); + public abstract @Nullable Number resolveNumber(LamiTableEntry entry); /** * Get the comparator that should be used to compare this entry (or table diff --git a/analysis/org.eclipse.tracecompass.analysis.lami.core/src/org/eclipse/tracecompass/internal/provisional/analysis/lami/core/aspect/LamiTimeRangeBeginAspect.java b/analysis/org.eclipse.tracecompass.analysis.lami.core/src/org/eclipse/tracecompass/internal/provisional/analysis/lami/core/aspect/LamiTimeRangeBeginAspect.java index abb160e496..29a384cc75 100644 --- a/analysis/org.eclipse.tracecompass.analysis.lami.core/src/org/eclipse/tracecompass/internal/provisional/analysis/lami/core/aspect/LamiTimeRangeBeginAspect.java +++ b/analysis/org.eclipse.tracecompass.analysis.lami.core/src/org/eclipse/tracecompass/internal/provisional/analysis/lami/core/aspect/LamiTimeRangeBeginAspect.java @@ -64,11 +64,11 @@ public class LamiTimeRangeBeginAspect extends LamiTableEntryAspect { @Override - public @Nullable Double resolveDouble(@NonNull LamiTableEntry entry) { + public @Nullable Number resolveNumber(@NonNull LamiTableEntry entry) { LamiData data = entry.getValue(fColIndex); if (data instanceof LamiTimeRange) { LamiTimeRange range = (LamiTimeRange) data; - return Double.valueOf(range.getStart()); + return range.getStart(); } return null; } @@ -76,13 +76,21 @@ public class LamiTimeRangeBeginAspect extends LamiTableEntryAspect { @Override public Comparator getComparator() { return (o1, o2) -> { - Double dO1 = resolveDouble(o1); - Double dO2 = resolveDouble(o2); - if (dO1 == null || dO2 == null) { + Number d1 = resolveNumber(o1); + Number d2 = resolveNumber(o2); + + if (d1 == null && d2 == null) { return 0; } + if (d1 == null) { + return 1; + } + + if (d2 == null) { + return -1; + } - return dO1.compareTo(dO2); + return Long.compare(d1.longValue(), d2.longValue()); }; } diff --git a/analysis/org.eclipse.tracecompass.analysis.lami.core/src/org/eclipse/tracecompass/internal/provisional/analysis/lami/core/aspect/LamiTimeRangeDurationAspect.java b/analysis/org.eclipse.tracecompass.analysis.lami.core/src/org/eclipse/tracecompass/internal/provisional/analysis/lami/core/aspect/LamiTimeRangeDurationAspect.java index 6b1fe35c32..f70448eb6b 100644 --- a/analysis/org.eclipse.tracecompass.analysis.lami.core/src/org/eclipse/tracecompass/internal/provisional/analysis/lami/core/aspect/LamiTimeRangeDurationAspect.java +++ b/analysis/org.eclipse.tracecompass.analysis.lami.core/src/org/eclipse/tracecompass/internal/provisional/analysis/lami/core/aspect/LamiTimeRangeDurationAspect.java @@ -65,11 +65,11 @@ public class LamiTimeRangeDurationAspect extends LamiTableEntryAspect { } @Override - public @Nullable Double resolveDouble(@NonNull LamiTableEntry entry) { + public @Nullable Number resolveNumber(@NonNull LamiTableEntry entry) { LamiData data = entry.getValue(fColIndex); if (data instanceof LamiTimeRange) { LamiTimeRange range = (LamiTimeRange) data; - return Double.valueOf(range.getDuration()); + return Long.valueOf(range.getDuration()); } return null; } @@ -77,13 +77,21 @@ public class LamiTimeRangeDurationAspect extends LamiTableEntryAspect { @Override public Comparator getComparator() { return (o1, o2) -> { - Double dO1 = resolveDouble(o1); - Double dO2 = resolveDouble(o2); - if (dO1 == null || dO2 == null) { + Number d1 = resolveNumber(o1); + Number d2 = resolveNumber(o2); + + if (d1 == null && d2 == null) { return 0; } + if (d1 == null) { + return 1; + } + + if (d2 == null) { + return -1; + } - return dO1.compareTo(dO2); + return Long.compare(d1.longValue(), d2.longValue()); }; } diff --git a/analysis/org.eclipse.tracecompass.analysis.lami.core/src/org/eclipse/tracecompass/internal/provisional/analysis/lami/core/aspect/LamiTimeRangeEndAspect.java b/analysis/org.eclipse.tracecompass.analysis.lami.core/src/org/eclipse/tracecompass/internal/provisional/analysis/lami/core/aspect/LamiTimeRangeEndAspect.java index 997989a3db..3705314847 100644 --- a/analysis/org.eclipse.tracecompass.analysis.lami.core/src/org/eclipse/tracecompass/internal/provisional/analysis/lami/core/aspect/LamiTimeRangeEndAspect.java +++ b/analysis/org.eclipse.tracecompass.analysis.lami.core/src/org/eclipse/tracecompass/internal/provisional/analysis/lami/core/aspect/LamiTimeRangeEndAspect.java @@ -61,11 +61,11 @@ public class LamiTimeRangeEndAspect extends LamiTableEntryAspect { } @Override - public @Nullable Double resolveDouble(@NonNull LamiTableEntry entry) { + public @Nullable Number resolveNumber(@NonNull LamiTableEntry entry) { LamiData data = entry.getValue(fColIndex); if (data instanceof LamiTimeRange) { LamiTimeRange range = (LamiTimeRange) data; - return Double.valueOf(range.getEnd()); + return Long.valueOf(range.getEnd()); } return null; } @@ -73,13 +73,21 @@ public class LamiTimeRangeEndAspect extends LamiTableEntryAspect { @Override public Comparator getComparator() { return (o1, o2) -> { - Double dO1 = resolveDouble(o1); - Double dO2 = resolveDouble(o2); - if (dO1 == null || dO2 == null) { + Number d1 = resolveNumber(o1); + Number d2 = resolveNumber(o2); + + if (d1 == null && d2 == null) { return 0; } + if (d1 == null) { + return 1; + } + + if (d2 == null) { + return -1; + } - return dO1.compareTo(dO2); + return Long.compare(d1.longValue(), d2.longValue()); }; } diff --git a/analysis/org.eclipse.tracecompass.analysis.lami.core/src/org/eclipse/tracecompass/internal/provisional/analysis/lami/core/aspect/LamiTimestampAspect.java b/analysis/org.eclipse.tracecompass.analysis.lami.core/src/org/eclipse/tracecompass/internal/provisional/analysis/lami/core/aspect/LamiTimestampAspect.java index 6f41e48977..31acda91fe 100644 --- a/analysis/org.eclipse.tracecompass.analysis.lami.core/src/org/eclipse/tracecompass/internal/provisional/analysis/lami/core/aspect/LamiTimestampAspect.java +++ b/analysis/org.eclipse.tracecompass.analysis.lami.core/src/org/eclipse/tracecompass/internal/provisional/analysis/lami/core/aspect/LamiTimestampAspect.java @@ -61,11 +61,11 @@ public class LamiTimestampAspect extends LamiTableEntryAspect { } @Override - public @Nullable Double resolveDouble(@NonNull LamiTableEntry entry) { + public @Nullable Number resolveNumber(@NonNull LamiTableEntry entry) { LamiData data = entry.getValue(fColIndex); if (data instanceof LamiInteger) { LamiInteger range = (LamiInteger) data; - return Double.valueOf(range.getValue()); + return Long.valueOf(range.getValue()); } return null; } @@ -73,13 +73,21 @@ public class LamiTimestampAspect extends LamiTableEntryAspect { @Override public Comparator getComparator() { return (o1, o2) -> { - Double dO1 = resolveDouble(o1); - Double dO2 = resolveDouble(o2); - if (dO1 == null || dO2 == null) { + Number d1 = resolveNumber(o1); + Number d2 = resolveNumber(o2); + + if (d1 == null && d2 == null) { return 0; } + if (d1 == null) { + return 1; + } + + if (d2 == null) { + return -1; + } - return dO1.compareTo(dO2); + return Long.compare(d1.longValue(), d2.longValue()); }; } diff --git a/analysis/org.eclipse.tracecompass.analysis.lami.core/src/org/eclipse/tracecompass/internal/provisional/analysis/lami/core/module/LamiLabelFormat.java b/analysis/org.eclipse.tracecompass.analysis.lami.core/src/org/eclipse/tracecompass/internal/provisional/analysis/lami/core/module/LamiLabelFormat.java deleted file mode 100644 index 11d8be15e1..0000000000 --- a/analysis/org.eclipse.tracecompass.analysis.lami.core/src/org/eclipse/tracecompass/internal/provisional/analysis/lami/core/module/LamiLabelFormat.java +++ /dev/null @@ -1,83 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2016 EfficiOS Inc., Jonathan Rajotte-Julien - * - * All rights reserved. This program and the accompanying materials are - * made available under the terms of the Eclipse Public License v1.0 which - * accompanies this distribution, and is available at - * http://www.eclipse.org/legal/epl-v10.html - *******************************************************************************/ - -package org.eclipse.tracecompass.internal.provisional.analysis.lami.core.module; - -import java.text.FieldPosition; -import java.text.Format; -import java.text.ParsePosition; -import java.util.Map.Entry; - - -import org.eclipse.jdt.annotation.Nullable; - -import com.google.common.collect.BiMap; - -/** - * Format label based on a given Map - * - * @author Jonathan Rajotte-Julien - */ -public class LamiLabelFormat extends Format { - - private static final long serialVersionUID = 4939553034329681316L; - - private static final String SWTCHART_EMPTY_LABEL = " "; //$NON-NLS-1$ - private static final String UNKNOWN_REPRESENTATION = "?"; //$NON-NLS-1$ - private final BiMap<@Nullable String, Integer> fMap; - - /** - * Constructor - * - * @param map - * Map of indices to labels - */ - public LamiLabelFormat(BiMap<@Nullable String, Integer> map) { - super(); - fMap = map; - } - - @Override - public @Nullable StringBuffer format(@Nullable Object obj, @Nullable StringBuffer toAppendTo, @Nullable FieldPosition pos) { - if (obj == null || toAppendTo == null) { - return new StringBuffer(SWTCHART_EMPTY_LABEL); - } - - Double doubleObj = (Double) obj; - - /* - * Return a string buffer with a space in it since SWT does not like to - * draw empty strings. - */ - if ((doubleObj % 1 != 0) || !fMap.containsValue((doubleObj.intValue()))) { - return new StringBuffer(SWTCHART_EMPTY_LABEL); - } - - for (Entry<@Nullable String, Integer> entry : fMap.entrySet()) { - /* - * FIXME: Find if the elements are the same, based on their double - * value, because SWTChart uses double values so we do the same - * check. The loss of precision could lead to false positives. - */ - if (Double.compare(entry.getValue().doubleValue(), doubleObj.doubleValue()) == 0) { - if (entry.getKey() == null) { - return new StringBuffer(UNKNOWN_REPRESENTATION); - } - return toAppendTo.append(entry.getKey()); - } - } - return new StringBuffer(SWTCHART_EMPTY_LABEL); - } - - @Override - public @Nullable Object parseObject(@Nullable String source, @Nullable ParsePosition pos) { - return fMap.get(source); - } - -} diff --git a/analysis/org.eclipse.tracecompass.analysis.lami.core/src/org/eclipse/tracecompass/internal/provisional/analysis/lami/core/module/LamiTimeStampFormat.java b/analysis/org.eclipse.tracecompass.analysis.lami.core/src/org/eclipse/tracecompass/internal/provisional/analysis/lami/core/module/LamiTimeStampFormat.java deleted file mode 100644 index 49c13a8d4b..0000000000 --- a/analysis/org.eclipse.tracecompass.analysis.lami.core/src/org/eclipse/tracecompass/internal/provisional/analysis/lami/core/module/LamiTimeStampFormat.java +++ /dev/null @@ -1,62 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2015, 2016 EfficiOS Inc. and others - * - * All rights reserved. This program and the accompanying materials are - * made available under the terms of the Eclipse Public License v1.0 which - * accompanies this distribution, and is available at - * http://www.eclipse.org/legal/epl-v10.html - *******************************************************************************/ - -package org.eclipse.tracecompass.internal.provisional.analysis.lami.core.module; - -import java.text.FieldPosition; -import java.text.SimpleDateFormat; -import java.util.Date; - -import org.eclipse.jdt.annotation.Nullable; -import org.eclipse.tracecompass.tmf.core.timestamp.TmfTimestampFormat; - -/** - * Formatter for time stamps - */ -public class LamiTimeStampFormat extends SimpleDateFormat { - - private static final long serialVersionUID = 4285447886537779762L; - - private final TmfTimestampFormat fFormat; - - // ------------------------------------------------------------------------ - // Constructors - // ------------------------------------------------------------------------ - - /** - * The default constructor (uses the default time format) - */ - public LamiTimeStampFormat() { - fFormat = TmfTimestampFormat.getDefaulTimeFormat(); - } - - /** - * The normal constructor - * - * @param pattern the format pattern - */ - public LamiTimeStampFormat(String pattern) { - fFormat = new TmfTimestampFormat(pattern); - } - - // ------------------------------------------------------------------------ - // Operations - // ------------------------------------------------------------------------ - - @Override - public StringBuffer format(@Nullable Date date, @Nullable StringBuffer toAppendTo, - @Nullable FieldPosition fieldPosition) { - if (date != null && toAppendTo != null) { - long time = date.getTime(); - toAppendTo.append(fFormat.format(time)); - return toAppendTo; - } - return new StringBuffer(); - } -} diff --git a/analysis/org.eclipse.tracecompass.analysis.lami.core/src/org/eclipse/tracecompass/internal/provisional/analysis/lami/core/types/LamiIRQ.java b/analysis/org.eclipse.tracecompass.analysis.lami.core/src/org/eclipse/tracecompass/internal/provisional/analysis/lami/core/types/LamiIRQ.java index ce71dc0dda..2fdacceaa4 100644 --- a/analysis/org.eclipse.tracecompass.analysis.lami.core/src/org/eclipse/tracecompass/internal/provisional/analysis/lami/core/types/LamiIRQ.java +++ b/analysis/org.eclipse.tracecompass.analysis.lami.core/src/org/eclipse/tracecompass/internal/provisional/analysis/lami/core/types/LamiIRQ.java @@ -71,7 +71,7 @@ public class LamiIRQ extends LamiData { * * @return The IRQ number */ - public int getNumber() { + public Integer getNumber() { return fNumber; } diff --git a/analysis/org.eclipse.tracecompass.analysis.lami.ui/META-INF/MANIFEST.MF b/analysis/org.eclipse.tracecompass.analysis.lami.ui/META-INF/MANIFEST.MF index a7603e5a8d..2f91a965bf 100644 --- a/analysis/org.eclipse.tracecompass.analysis.lami.ui/META-INF/MANIFEST.MF +++ b/analysis/org.eclipse.tracecompass.analysis.lami.ui/META-INF/MANIFEST.MF @@ -17,6 +17,7 @@ Require-Bundle: org.eclipse.ui, org.eclipse.tracecompass.tmf.ui, org.eclipse.tracecompass.analysis.lami.core Export-Package: org.eclipse.tracecompass.internal.analysis.lami.ui;x-internal:=true, + org.eclipse.tracecompass.internal.provisional.analysis.lami.ui.format;x-internal:=true, org.eclipse.tracecompass.internal.provisional.analysis.lami.ui.handler;x-internal:=true, org.eclipse.tracecompass.internal.provisional.analysis.lami.ui.signals;x-internal:=true, org.eclipse.tracecompass.internal.provisional.analysis.lami.ui.viewers;x-internal:=true, diff --git a/analysis/org.eclipse.tracecompass.analysis.lami.ui/src/org/eclipse/tracecompass/internal/provisional/analysis/lami/ui/format/LamiDecimalUnitFormat.java b/analysis/org.eclipse.tracecompass.analysis.lami.ui/src/org/eclipse/tracecompass/internal/provisional/analysis/lami/ui/format/LamiDecimalUnitFormat.java new file mode 100644 index 0000000000..c3bd73d3a4 --- /dev/null +++ b/analysis/org.eclipse.tracecompass.analysis.lami.ui/src/org/eclipse/tracecompass/internal/provisional/analysis/lami/ui/format/LamiDecimalUnitFormat.java @@ -0,0 +1,153 @@ +/******************************************************************************* + * Copyright (c) 2016 EfficiOS Inc., Jonathan Rajotte-Julien + * + * All rights reserved. This program and the accompanying materials are + * made available under the terms of the Eclipse Public License v1.0 which + * accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + *******************************************************************************/ + +package org.eclipse.tracecompass.internal.provisional.analysis.lami.ui.format; + +import java.math.BigDecimal; +import java.text.FieldPosition; + +import org.eclipse.jdt.annotation.Nullable; +import org.eclipse.tracecompass.common.core.format.DecimalUnitFormat; +import org.eclipse.tracecompass.internal.provisional.analysis.lami.ui.viewers.LamiGraphRange; + +/** + * Decimal formatter for Lami graph + * + * Since the graph use normalized internal value the initial (external) + * representation needs to be obtained. Subsequent formatting is done based on a + * Double. Loss of precision could occurs based on the size. For now, loss of + * precision for decimal values is not a big concern. If it ever become one the + * use of Long while formatting might come in handy. + * + * @author Jonathan Rajotte-Julien + */ +public class LamiDecimalUnitFormat extends DecimalUnitFormat { + + /** Maximum amount of digits that can be represented into a double */ + private static final int BIG_DECIMAL_DIVISION_SCALE = 22; + + private static final long serialVersionUID = 977671266270661188L; + + private @Nullable LamiGraphRange fInternalRange = null; + private @Nullable LamiGraphRange fExternalRange = null; + + /** + * Default constructor + */ + public LamiDecimalUnitFormat() { + super(); + } + + /** + * Constructor with internal and external LamiRange for scale transformation + * + * @param internalRange + * The internal range used for graph representation + * + * @param externalRange + * The external (real value) range shown to the user + */ + public LamiDecimalUnitFormat(LamiGraphRange internalRange, LamiGraphRange externalRange) { + super(); + fInternalRange = internalRange; + fExternalRange = externalRange; + } + + /** + * Constructor with multiplication factor. + * + * @param factor + * Multiplication factor to apply to the value + */ + public LamiDecimalUnitFormat(double factor) { + super(factor); + } + + /** + * Constructor with multiplication factor and internal and external + * LamiRange for scale transformation. + * + * @param factor + * Multiplication factor to apply to the value + * @param internalRange + * The internal range used for graph representation + * @param externalRange + * The external (real value) range shown to the user + */ + public LamiDecimalUnitFormat(double factor, LamiGraphRange internalRange, LamiGraphRange externalRange) { + super(factor); + fInternalRange = internalRange; + fExternalRange = externalRange; + } + + /** + * @return the internal range definition + */ + public @Nullable LamiGraphRange getInternalRange() { + return fInternalRange; + } + + /** + * @param internalRange + * The internal range definition to be used by the formatter + */ + public void setInternalRange(@Nullable LamiGraphRange internalRange) { + fInternalRange = internalRange; + } + + /** + * @return the external range definition + */ + public @Nullable LamiGraphRange getExternalRange() { + return fExternalRange; + } + + /** + * @param externalRange + * The external range definition to be used by the formatter + */ + public void setExternalRange(@Nullable LamiGraphRange externalRange) { + fExternalRange = externalRange; + } + + @Override + public StringBuffer format(@Nullable Object obj, @Nullable StringBuffer toAppendTo, @Nullable FieldPosition pos) { + if (!(obj instanceof Number) || toAppendTo == null) { + throw new IllegalArgumentException("Cannot format given Object as a Number: " + obj); //$NON-NLS-1$ + } + + @Nullable LamiGraphRange internalRange = fInternalRange; + @Nullable LamiGraphRange externalRange = fExternalRange; + if (internalRange == null || externalRange == null) { + StringBuffer buffer = super.format(obj, toAppendTo, pos); + return (buffer == null ? new StringBuffer() : buffer); + } + + if (internalRange.getDelta().compareTo(BigDecimal.ZERO) == 0) { + StringBuffer buffer = super.format(externalRange.getMinimum().doubleValue(), toAppendTo, pos); + return (buffer == null ? new StringBuffer() : buffer); + } + + if (externalRange.getDelta().compareTo(BigDecimal.ZERO) == 0) { + StringBuffer buffer = super.format(externalRange.getMinimum().doubleValue(), toAppendTo, pos); + return (buffer == null ? new StringBuffer() : buffer); + } + + /* Find external value before formatting */ + BigDecimal externalValue = (new BigDecimal(obj.toString())) + .subtract(internalRange.getMinimum()) + .multiply(externalRange.getDelta()) + .divide(internalRange.getDelta(), BIG_DECIMAL_DIVISION_SCALE, BigDecimal.ROUND_DOWN) + .add(externalRange.getMinimum()); + + Double value = externalValue.doubleValue(); + StringBuffer buffer = super.format(value, toAppendTo, pos); + return (buffer == null ? new StringBuffer() : buffer); + } +} diff --git a/analysis/org.eclipse.tracecompass.analysis.lami.ui/src/org/eclipse/tracecompass/internal/provisional/analysis/lami/ui/format/LamiLabelFormat.java b/analysis/org.eclipse.tracecompass.analysis.lami.ui/src/org/eclipse/tracecompass/internal/provisional/analysis/lami/ui/format/LamiLabelFormat.java new file mode 100644 index 0000000000..95747899c4 --- /dev/null +++ b/analysis/org.eclipse.tracecompass.analysis.lami.ui/src/org/eclipse/tracecompass/internal/provisional/analysis/lami/ui/format/LamiLabelFormat.java @@ -0,0 +1,83 @@ +/******************************************************************************* + * Copyright (c) 2016 EfficiOS Inc., Jonathan Rajotte-Julien + * + * All rights reserved. This program and the accompanying materials are + * made available under the terms of the Eclipse Public License v1.0 which + * accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + *******************************************************************************/ + +package org.eclipse.tracecompass.internal.provisional.analysis.lami.ui.format; + +import java.text.FieldPosition; +import java.text.Format; +import java.text.ParsePosition; +import java.util.Map.Entry; + + +import org.eclipse.jdt.annotation.Nullable; + +import com.google.common.collect.BiMap; + +/** + * Format label based on a given Map + * + * @author Jonathan Rajotte-Julien + */ +public class LamiLabelFormat extends Format { + + private static final long serialVersionUID = 4939553034329681316L; + + private static final String SWTCHART_EMPTY_LABEL = " "; //$NON-NLS-1$ + private static final String UNKNOWN_REPRESENTATION = "?"; //$NON-NLS-1$ + private final BiMap<@Nullable String, Integer> fMap; + + /** + * Constructor + * + * @param map + * Map of indices to labels + */ + public LamiLabelFormat(BiMap<@Nullable String, Integer> map) { + super(); + fMap = map; + } + + @Override + public @Nullable StringBuffer format(@Nullable Object obj, @Nullable StringBuffer toAppendTo, @Nullable FieldPosition pos) { + if (obj == null || toAppendTo == null) { + return new StringBuffer(SWTCHART_EMPTY_LABEL); + } + + Double doubleObj = (Double) obj; + + /* + * Return a string buffer with a space in it since SWT does not like to + * draw empty strings. + */ + if ((doubleObj % 1 != 0) || !fMap.containsValue((doubleObj.intValue()))) { + return new StringBuffer(SWTCHART_EMPTY_LABEL); + } + + for (Entry<@Nullable String, Integer> entry : fMap.entrySet()) { + /* + * FIXME: Find if the elements are the same, based on their double + * value, because SWTChart uses double values so we do the same + * check. The loss of precision could lead to false positives. + */ + if (Double.compare(entry.getValue().doubleValue(), doubleObj.doubleValue()) == 0) { + if (entry.getKey() == null) { + return new StringBuffer(UNKNOWN_REPRESENTATION); + } + return toAppendTo.append(entry.getKey()); + } + } + return new StringBuffer(SWTCHART_EMPTY_LABEL); + } + + @Override + public @Nullable Object parseObject(@Nullable String source, @Nullable ParsePosition pos) { + return fMap.get(source); + } + +} diff --git a/analysis/org.eclipse.tracecompass.analysis.lami.ui/src/org/eclipse/tracecompass/internal/provisional/analysis/lami/ui/format/LamiTimeStampFormat.java b/analysis/org.eclipse.tracecompass.analysis.lami.ui/src/org/eclipse/tracecompass/internal/provisional/analysis/lami/ui/format/LamiTimeStampFormat.java new file mode 100644 index 0000000000..87c607b39a --- /dev/null +++ b/analysis/org.eclipse.tracecompass.analysis.lami.ui/src/org/eclipse/tracecompass/internal/provisional/analysis/lami/ui/format/LamiTimeStampFormat.java @@ -0,0 +1,158 @@ +/******************************************************************************* + * Copyright (c) 2015, 2016 EfficiOS Inc. and others + * + * All rights reserved. This program and the accompanying materials are + * made available under the terms of the Eclipse Public License v1.0 which + * accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + *******************************************************************************/ + +package org.eclipse.tracecompass.internal.provisional.analysis.lami.ui.format; + +import static org.eclipse.tracecompass.common.core.NonNullUtils.checkNotNull; + +import java.math.BigDecimal; +import java.text.FieldPosition; +import java.text.Format; +import java.text.ParsePosition; + +import org.eclipse.jdt.annotation.Nullable; +import org.eclipse.tracecompass.internal.provisional.analysis.lami.ui.viewers.LamiGraphRange; +import org.eclipse.tracecompass.tmf.core.timestamp.TmfTimestampFormat; + +/** + * Formatter for time stamps + */ +public class LamiTimeStampFormat extends Format { + + private static final int BIG_DECIMAL_DIVISION_SCALE = 22; + + private static final long serialVersionUID = 4285447886537779762L; + + private final TmfTimestampFormat fFormat; + + private @Nullable LamiGraphRange fInternalRange = null; + private @Nullable LamiGraphRange fExternalRange = null; + + // ------------------------------------------------------------------------ + // Constructors + // ------------------------------------------------------------------------ + + /** + * The default constructor + */ + public LamiTimeStampFormat() { + fFormat = checkNotNull(TmfTimestampFormat.getDefaulTimeFormat()); + } + + /** + * The base constructor + * + * @param internalRange + * The internal range used for graph representation + * @param externalRange + * The external (real value) range shown to the user + */ + public LamiTimeStampFormat(LamiGraphRange internalRange, LamiGraphRange externalRange) { + fFormat = checkNotNull(TmfTimestampFormat.getDefaulTimeFormat()); + fInternalRange = internalRange; + fExternalRange = externalRange; + } + + /** + * The normal constructor + * + * @param pattern + * The format pattern + */ + public LamiTimeStampFormat(String pattern) { + fFormat = new TmfTimestampFormat(pattern); + } + + /** + * The normal constructor + * + * @param pattern + * the format pattern + * @param internalRange + * The internal range used for graph representation + * @param externalRange + * The external (real value) range shown to the user + */ + public LamiTimeStampFormat(String pattern, LamiGraphRange internalRange, LamiGraphRange externalRange) { + fFormat = new TmfTimestampFormat(pattern); + fInternalRange = internalRange; + fExternalRange = externalRange; + } + + // ------------------------------------------------------------------------ + // Operations + // ------------------------------------------------------------------------ + + /** + * @return the internal range definition + */ + public @Nullable LamiGraphRange getInternalRange() { + return fInternalRange; + } + + /** + * @param internalRange + * The internal range definition to be used by the formatter + */ + public void setInternalRange(@Nullable LamiGraphRange internalRange) { + fInternalRange = internalRange; + } + + /** + * @return the external range definition + */ + public @Nullable LamiGraphRange getExternalRange() { + return fExternalRange; + } + + /** + * @param externalRange + * The external range definition to be used by the formatter + */ + public void setExternalRange(@Nullable LamiGraphRange externalRange) { + fExternalRange = externalRange; + } + + + @Override + public StringBuffer format(@Nullable Object obj, @Nullable StringBuffer toAppendTo, @Nullable FieldPosition pos) { + if (obj != null && obj instanceof Number && toAppendTo != null) { + @Nullable LamiGraphRange internalRange = fInternalRange; + @Nullable LamiGraphRange externalRange = fExternalRange; + if (internalRange == null || externalRange == null) { + long time = ((Number)obj).longValue(); + return checkNotNull(toAppendTo.append(fFormat.format(time))); + } + + if (internalRange.getDelta().compareTo(BigDecimal.ZERO) == 0) { + return checkNotNull(toAppendTo.append(fFormat.format(externalRange.getMinimum().doubleValue()))); + } + + if (externalRange.getDelta().compareTo(BigDecimal.ZERO) == 0) { + return checkNotNull(toAppendTo.append(fFormat.format(externalRange.getMinimum().doubleValue()))); + } + + /* Find external value before formatting */ + BigDecimal externalValue = (new BigDecimal(obj.toString())) + .subtract(internalRange.getMinimum()) + .multiply(externalRange.getDelta()) + .divide(internalRange.getDelta(), BIG_DECIMAL_DIVISION_SCALE, BigDecimal.ROUND_DOWN) + .add(externalRange.getMinimum()); + + return checkNotNull(toAppendTo.append(fFormat.format(externalValue.longValue()))); + } + return new StringBuffer(); + } + + @Override + public @Nullable Object parseObject(@Nullable String source, @Nullable ParsePosition pos) { + return null; + } + +} diff --git a/analysis/org.eclipse.tracecompass.analysis.lami.ui/src/org/eclipse/tracecompass/internal/provisional/analysis/lami/ui/format/package-info.java b/analysis/org.eclipse.tracecompass.analysis.lami.ui/src/org/eclipse/tracecompass/internal/provisional/analysis/lami/ui/format/package-info.java new file mode 100644 index 0000000000..c91e0f030a --- /dev/null +++ b/analysis/org.eclipse.tracecompass.analysis.lami.ui/src/org/eclipse/tracecompass/internal/provisional/analysis/lami/ui/format/package-info.java @@ -0,0 +1,11 @@ +/******************************************************************************* + * Copyright (c) 2016 EfficiOS Inc., Jonathan Rajotte-Julien + * + * All rights reserved. This program and the accompanying materials are + * made available under the terms of the Eclipse Public License v1.0 which + * accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + *******************************************************************************/ + +@org.eclipse.jdt.annotation.NonNullByDefault +package org.eclipse.tracecompass.internal.provisional.analysis.lami.ui.format; \ No newline at end of file diff --git a/analysis/org.eclipse.tracecompass.analysis.lami.ui/src/org/eclipse/tracecompass/internal/provisional/analysis/lami/ui/viewers/LamiBarChartViewer.java b/analysis/org.eclipse.tracecompass.analysis.lami.ui/src/org/eclipse/tracecompass/internal/provisional/analysis/lami/ui/viewers/LamiBarChartViewer.java index bd48497410..0795713fea 100644 --- a/analysis/org.eclipse.tracecompass.analysis.lami.ui/src/org/eclipse/tracecompass/internal/provisional/analysis/lami/ui/viewers/LamiBarChartViewer.java +++ b/analysis/org.eclipse.tracecompass.analysis.lami.ui/src/org/eclipse/tracecompass/internal/provisional/analysis/lami/ui/viewers/LamiBarChartViewer.java @@ -11,6 +11,7 @@ package org.eclipse.tracecompass.internal.provisional.analysis.lami.ui.viewers; import static org.eclipse.tracecompass.common.core.NonNullUtils.checkNotNull; +import java.math.BigDecimal; import java.util.ArrayList; import java.util.Arrays; import java.util.Comparator; @@ -82,6 +83,10 @@ public class LamiBarChartViewer extends LamiXYChartViewer { private final Map> fIndexPerSeriesMapping; private final Map fEntryToCategoriesMap; + private LamiGraphRange fYInternalRange = new LamiGraphRange(checkNotNull(BigDecimal.ZERO), checkNotNull(BigDecimal.ONE)); + private LamiGraphRange fYExternalRange; + + /** * Creates a bar chart Viewer instance based on SWTChart. * @@ -127,6 +132,10 @@ public class LamiBarChartViewer extends LamiXYChartViewer { } fCategories = xCategories.toArray(new String[0]); + /* The y values range */ + /* Clamp minimum to zero or negative value */ + fYExternalRange = getRange(yAxisAspects, true); + /* * Log scale magic course 101: * @@ -141,14 +150,18 @@ public class LamiBarChartViewer extends LamiXYChartViewer { */ double min = Double.MAX_VALUE; double max = Double.MIN_VALUE; - double logScaleEpsilon = ZERO; + double logScaleEpsilon = ZERO_DOUBLE; if (logscale) { - /* Find minimum and maximum values */ + /* Find minimum and maximum values excluding <= 0 values */ for (LamiTableEntryAspect aspect : yAxisAspects) { for (LamiTableEntry entry : entries) { - Double value = aspect.resolveDouble(entry); - if (value == null || value <= 0) { + Number externalValue = aspect.resolveNumber(entry); + if (externalValue == null) { + continue; + } + Double value = getInternalDoubleValue(externalValue, fYInternalRange, fYExternalRange); + if (value <= 0) { continue; } min = Math.min(min, value); @@ -156,6 +169,11 @@ public class LamiBarChartViewer extends LamiXYChartViewer { } } + if (min == Double.MAX_VALUE) { + /* Series are empty in log scale*/ + return; + } + double delta = max - min; logScaleEpsilon = min - ((min * delta) / (LOGSCALE_EPSILON_FACTOR * max)); } @@ -172,21 +190,26 @@ public class LamiBarChartViewer extends LamiXYChartViewer { for (int i = 0; i < entries.size(); i++) { Integer categoryIndex = checkNotNull(fEntryToCategoriesMap.get(checkNotNull(entries.get(i)))).fInternalValue; - Double yValue = yAxisAspect.resolveDouble(entries.get(i)); + if (categoryIndex == null) { /* Invalid value do not show */ continue; } - if (yValue == null) { + Double yValue = ZERO_DOUBLE; + @Nullable Number number = yAxisAspect.resolveNumber(entries.get(i)); + + if (number == null) { /* * Null value for y is the same as zero since this is a bar * chart */ - yValue = Double.valueOf(ZERO); + yValue = ZERO_DOUBLE; + } else { + yValue = getInternalDoubleValue(number, fYInternalRange, fYExternalRange); } - if (logscale && yValue <= ZERO) { + if (logscale && yValue <= ZERO_DOUBLE) { /* * Less or equal to 0 values can't be plotted on a log * scale. We map them to the mean of the >=0 minimal value @@ -220,12 +243,27 @@ public class LamiBarChartViewer extends LamiXYChartViewer { /* Set the formatter on the Y axis */ IAxisTick yTick = getChart().getAxisSet().getYAxis(0).getTick(); - yTick.setFormat(getContinuousAxisFormatter(yAxisAspects, entries)); + yTick.setFormat(getContinuousAxisFormatter(yAxisAspects, entries, fYInternalRange, fYExternalRange)); + + /* + * SWTChart workaround: SWTChart fiddles with tick mark visibility based + * on the fact that it can parse the label to double or not. + * + * If the label happens to be a double, it checks for the presence of + * that value in its own tick labels to decide if it should add it or + * not. If it happens that the parsed value is already present in its + * map, the tick gets a visibility of false. + * + * The X axis does not have this problem since SWTCHART checks on label + * angle, and if it is != 0 simply does no logic regarding visibility. + * So simply set a label angle of 1 to the axis. + */ yTick.setTickLabelAngle(1); /* Adjust the chart range */ getChart().getAxisSet().adjustRange(); - if (logscale) { + + if (logscale && logScaleEpsilon != max) { getChart().getAxisSet().getYAxis(0).setRange(new Range(logScaleEpsilon, max)); } diff --git a/analysis/org.eclipse.tracecompass.analysis.lami.ui/src/org/eclipse/tracecompass/internal/provisional/analysis/lami/ui/viewers/LamiGraphRange.java b/analysis/org.eclipse.tracecompass.analysis.lami.ui/src/org/eclipse/tracecompass/internal/provisional/analysis/lami/ui/viewers/LamiGraphRange.java new file mode 100644 index 0000000000..62287aa290 --- /dev/null +++ b/analysis/org.eclipse.tracecompass.analysis.lami.ui/src/org/eclipse/tracecompass/internal/provisional/analysis/lami/ui/viewers/LamiGraphRange.java @@ -0,0 +1,61 @@ +/******************************************************************************* + * Copyright (c) 2016 EfficiOS Inc., Jonathan Rajotte-Julien + * + * All rights reserved. This program and the accompanying materials are + * made available under the terms of the Eclipse Public License v1.0 which + * accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + *******************************************************************************/ + +package org.eclipse.tracecompass.internal.provisional.analysis.lami.ui.viewers; + +import static org.eclipse.tracecompass.common.core.NonNullUtils.checkNotNull; + +import java.math.BigDecimal; + +/** + * BigDecimal based range representation + * + * @author Jonathan Rajotte-Julien + */ +public class LamiGraphRange { + + private final BigDecimal fMinimum; + private final BigDecimal fMaximum; + private final BigDecimal fRange; + + /** + * Constructor + * + * @param minimum + * The minimum value of the range + * @param maximum + * The maximum value of the range + */ + public LamiGraphRange(BigDecimal minimum, BigDecimal maximum) { + fMinimum = minimum; + fMaximum = maximum; + fRange = checkNotNull(maximum.subtract(minimum)); + } + + /** + * @return the minimum value of the range + */ + public BigDecimal getMinimum() { + return fMinimum; + } + + /** + * @return the maximum value of the range + */ + public BigDecimal getMaximum() { + return fMaximum; + } + + /** + * @return the range delta + */ + public BigDecimal getDelta() { + return fRange; + } +} \ No newline at end of file diff --git a/analysis/org.eclipse.tracecompass.analysis.lami.ui/src/org/eclipse/tracecompass/internal/provisional/analysis/lami/ui/viewers/LamiScatterViewer.java b/analysis/org.eclipse.tracecompass.analysis.lami.ui/src/org/eclipse/tracecompass/internal/provisional/analysis/lami/ui/viewers/LamiScatterViewer.java index 20ba7b3b89..ea3869c2d4 100644 --- a/analysis/org.eclipse.tracecompass.analysis.lami.ui/src/org/eclipse/tracecompass/internal/provisional/analysis/lami/ui/viewers/LamiScatterViewer.java +++ b/analysis/org.eclipse.tracecompass.analysis.lami.ui/src/org/eclipse/tracecompass/internal/provisional/analysis/lami/ui/viewers/LamiScatterViewer.java @@ -11,6 +11,7 @@ package org.eclipse.tracecompass.internal.provisional.analysis.lami.ui.viewers; import static org.eclipse.tracecompass.common.core.NonNullUtils.checkNotNull; +import java.math.BigDecimal; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; @@ -41,9 +42,9 @@ import org.eclipse.swt.widgets.Listener; 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.LamiChartModel.ChartType; -import org.eclipse.tracecompass.internal.provisional.analysis.lami.core.module.LamiLabelFormat; 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.ui.format.LamiLabelFormat; import org.eclipse.tracecompass.internal.provisional.analysis.lami.ui.signals.LamiSelectionUpdateSignal; import org.eclipse.tracecompass.tmf.core.signal.TmfSignalManager; import org.swtchart.IAxisTick; @@ -68,6 +69,13 @@ public class LamiScatterViewer extends LamiXYChartViewer { private final Map> fIndexMapping; + /* Use a scale from 0 to 1 internally for both axes */ + private LamiGraphRange fXInternalRange = new LamiGraphRange(checkNotNull(BigDecimal.ZERO), checkNotNull(BigDecimal.ONE)); + private LamiGraphRange fYInternalRange = new LamiGraphRange(checkNotNull(BigDecimal.ZERO), checkNotNull(BigDecimal.ONE)); + + private @Nullable LamiGraphRange fXExternalRange = null; + private @Nullable LamiGraphRange fYExternalRange = null; + /* The current data point for the hovering cross */ private Point fHoveringCrossDataPoint; @@ -121,6 +129,15 @@ public class LamiScatterViewer extends LamiXYChartViewer { */ if (!areXAspectsContinuous) { generateLabelMap(xAxisAspects, checkNotNull(xMap)); + } else { + /* + * Always clamp the range to min and max + * + * TODO: in the future this could be based on the result of the + * delta between max and min multiplied by a ratio like it is done in + * LibreOffice Calc + */ + fXExternalRange = getRange(xAxisAspects, false); } /* @@ -148,12 +165,18 @@ public class LamiScatterViewer extends LamiXYChartViewer { */ if (!areYAspectsContinuous) { generateLabelMap(yAxisAspects, yMap); + } else { + /* + * Only clamp the range to the minimum value if it is a time stamp since + * plotting from 1970 would make little sense. + */ + fYExternalRange = getRange(yAxisAspects, areYAspectsTimeStamp); } /* Plot the series */ int index = 0; for (LamiTableEntryAspect yAspect : getYAxisAspects()) { - String name = ""; //$NON-NLS-1$ + String name; LamiTableEntryAspect xAspect; if (xAxisAspects.size() == 1) { /* Always map to the same x series */ @@ -164,35 +187,53 @@ public class LamiScatterViewer extends LamiXYChartViewer { name = (yAspect.getName() + ' ' + Messages.LamiScatterViewer_by + ' ' + xAspect.getName()); } - List<@Nullable Double> xDoubleSeries = new ArrayList<>(); - List<@Nullable Double> yDoubleSeries = new ArrayList<>(); + List<@Nullable Double> xDoubleSeries; + List<@Nullable Double> yDoubleSeries; if (xAspect.isContinuous()) { - xDoubleSeries = getResultTable().getEntries().stream().map((entry -> xAspect.resolveDouble(entry))).collect(Collectors.toList()); + xDoubleSeries = getResultTable().getEntries().stream() + .map(entry -> { + Number number = xAspect.resolveNumber(entry); + if (number != null && fXExternalRange != null) { + return getInternalDoubleValue(number, fXInternalRange, fXExternalRange); + } + return null; + }) + .collect(Collectors.toList()); } else { - xDoubleSeries = getResultTable().getEntries().stream().map(entry -> { - String string = xAspect.resolveString(entry); - Integer value = xMap.get(string); - if (value != null) { - return Double.valueOf(value.doubleValue()); - } - return null; - - }).collect(Collectors.toList()); + xDoubleSeries = getResultTable().getEntries().stream() + .map(entry -> { + String string = xAspect.resolveString(entry); + Integer value = xMap.get(string); + if (value != null) { + return Double.valueOf(value.doubleValue()); + } + return null; + }) + .collect(Collectors.toList()); } if (yAspect.isContinuous()) { - yDoubleSeries = getResultTable().getEntries().stream().map((entry -> yAspect.resolveDouble(entry))).collect(Collectors.toList()); + yDoubleSeries = getResultTable().getEntries().stream() + .map(entry -> { + Number number = yAspect.resolveNumber(entry); + if (number != null && fYExternalRange != null) { + return getInternalDoubleValue(number, fYInternalRange, fYExternalRange); + } + return null; + }) + .collect(Collectors.toList()); } else { - yDoubleSeries = getResultTable().getEntries().stream().map(entry -> { - String string = yAspect.resolveString(entry); - Integer value = yMap.get(string); - if (value != null) { - return Double.valueOf(value.doubleValue()); - } - return null; - - }).collect(Collectors.toList()); + yDoubleSeries = getResultTable().getEntries().stream() + .map(entry -> { + String string = yAspect.resolveString(entry); + Integer value = yMap.get(string); + if (value != null) { + return Double.valueOf(value.doubleValue()); + } + return null; + }) + .collect(Collectors.toList()); } List<@Nullable Double> validXDoubleSeries = new ArrayList<>(); @@ -211,7 +252,7 @@ public class LamiScatterViewer extends LamiXYChartViewer { /* Reject this tuple */ continue; } - if ((xIsLog && xValue <= ZERO) || (yIsLog && yValue <= ZERO)) { + if ((xIsLog && xValue <= ZERO_DOUBLE) || (yIsLog && yValue <= ZERO_DOUBLE)) { /* * Equal or less than 0 values can't be plotted on log scale */ @@ -242,7 +283,7 @@ public class LamiScatterViewer extends LamiXYChartViewer { /* Modify x axis related chart styling */ IAxisTick xTick = getChart().getAxisSet().getXAxis(0).getTick(); if (areXAspectsContinuous) { - xTick.setFormat(getContinuousAxisFormatter(xAxisAspects, getResultTable().getEntries())); + xTick.setFormat(getContinuousAxisFormatter(xAxisAspects, getResultTable().getEntries(), fXInternalRange, fXExternalRange)); } else { xTick.setFormat(new LamiLabelFormat(checkNotNull(xMap))); updateTickMark(checkNotNull(xMap), xTick, getChart().getPlotArea().getSize().x); @@ -254,7 +295,7 @@ public class LamiScatterViewer extends LamiXYChartViewer { /* Modify Y axis related chart styling */ IAxisTick yTick = getChart().getAxisSet().getYAxis(0).getTick(); if (areYAspectsContinuous) { - yTick.setFormat(getContinuousAxisFormatter(yAxisAspects, getResultTable().getEntries())); + yTick.setFormat(getContinuousAxisFormatter(yAxisAspects, getResultTable().getEntries(), fYInternalRange, fYExternalRange)); } else { yTick.setFormat(new LamiLabelFormat(checkNotNull(yMap))); updateTickMark(checkNotNull(yMap), yTick, getChart().getPlotArea().getSize().y); diff --git a/analysis/org.eclipse.tracecompass.analysis.lami.ui/src/org/eclipse/tracecompass/internal/provisional/analysis/lami/ui/viewers/LamiXYChartViewer.java b/analysis/org.eclipse.tracecompass.analysis.lami.ui/src/org/eclipse/tracecompass/internal/provisional/analysis/lami/ui/viewers/LamiXYChartViewer.java index 874e3b5a35..a958302ff2 100644 --- a/analysis/org.eclipse.tracecompass.analysis.lami.ui/src/org/eclipse/tracecompass/internal/provisional/analysis/lami/ui/viewers/LamiXYChartViewer.java +++ b/analysis/org.eclipse.tracecompass.analysis.lami.ui/src/org/eclipse/tracecompass/internal/provisional/analysis/lami/ui/viewers/LamiXYChartViewer.java @@ -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; @@ -43,7 +44,8 @@ import org.eclipse.tracecompass.internal.provisional.analysis.lami.core.aspect.L 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; @@ -71,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 @@ -86,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); }; @@ -143,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 */ @@ -360,45 +367,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 axisAspects, List entries) { + protected static Format getContinuousAxisFormatter(List axisAspects, List 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; } /** @@ -715,4 +741,81 @@ public abstract class LamiXYChartViewer extends TmfViewer implements ILamiViewer return toolBar; } + + /** + * Get a {@link LamiGraphRange} that covers all data points in the result + * table. + *

+ * The returned range will be the minimum and maximum of the resolved values + * of the passed aspects for all result entries. If clampToZero + * 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 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(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(); + } }