List<LamiTableEntryAspect> 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));
}
}
@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;
}
@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());
};
}
}
@Override
- public @Nullable Double resolveDouble(@NonNull LamiTableEntry entry) {
+ public @Nullable Number resolveNumber(@NonNull LamiTableEntry entry) {
return null;
}
}
@Override
- public @Nullable Double resolveDouble(@NonNull LamiTableEntry entry) {
+ public @Nullable Number resolveNumber(@NonNull LamiTableEntry entry) {
if (fIsContinuous) {
try {
if (entry.getValue(fColIndex).toString() != null) {
public Comparator<LamiTableEntry> 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());
};
}
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);
};
}
@Override
- public @Nullable Double resolveDouble(LamiTableEntry entry) {
+ public @Nullable Number resolveNumber(LamiTableEntry entry) {
return null;
}
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);
};
}
@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;
@Override
public Comparator<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 Integer.compare(d1.intValue(), d2.intValue());
};
}
}
@Override
- public @Nullable Double resolveDouble(LamiTableEntry entry) {
+ public @Nullable Number resolveNumber(LamiTableEntry entry) {
return null;
}
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);
};
}
@Override
- public @Nullable Double resolveDouble(LamiTableEntry entry) {
+ public @Nullable Number resolveNumber(LamiTableEntry entry) {
return null;
}
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);
};
}
@Override
- public @Nullable Double resolveDouble(LamiTableEntry entry) {
+ public @Nullable Number resolveNumber(LamiTableEntry entry) {
return null;
}
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);
};
}
@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;
@Override
public Comparator<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());
};
}
}
}
@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;
@Override
public Comparator<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());
};
}
}
* 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
@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;
}
@Override
public Comparator<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());
};
}
}
@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;
}
@Override
public Comparator<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());
};
}
}
@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;
}
@Override
public Comparator<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());
};
}
}
@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;
}
@Override
public Comparator<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());
};
}
+++ /dev/null
-/*******************************************************************************
- * 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<String, Integer>
- *
- * @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);
- }
-
-}
+++ /dev/null
-/*******************************************************************************
- * 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();
- }
-}
*
* @return The IRQ number
*/
- public int getNumber() {
+ public Integer getNumber() {
return fNumber;
}
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,
--- /dev/null
+/*******************************************************************************
+ * 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);
+ }
+}
--- /dev/null
+/*******************************************************************************
+ * 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<String, Integer>
+ *
+ * @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);
+ }
+
+}
--- /dev/null
+/*******************************************************************************
+ * 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;
+ }
+
+}
--- /dev/null
+/*******************************************************************************
+ * 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
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;
private final Map<ISeries, List<Mapping>> fIndexPerSeriesMapping;
private final Map<LamiTableEntry, Mapping> fEntryToCategoriesMap;
+ private LamiGraphRange fYInternalRange = new LamiGraphRange(checkNotNull(BigDecimal.ZERO), checkNotNull(BigDecimal.ONE));
+ private LamiGraphRange fYExternalRange;
+
+
/**
* Creates a bar chart Viewer instance based on SWTChart.
*
}
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:
*
*/
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);
}
}
+ if (min == Double.MAX_VALUE) {
+ /* Series are empty in log scale*/
+ return;
+ }
+
double delta = max - min;
logScaleEpsilon = min - ((min * delta) / (LOGSCALE_EPSILON_FACTOR * max));
}
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
/* 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));
}
--- /dev/null
+/*******************************************************************************
+ * 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
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;
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;
private final Map<ISeries, List<Integer>> 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;
*/
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);
}
/*
*/
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 */
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<>();
/* 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
*/
/* 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);
/* 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);
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;
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;
*/
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
*/
protected static final ToDoubleFunction<@Nullable String> DOUBLE_MAPPER = str -> {
if (str == null || str.equals(UNKNOWN)) {
- return ZERO;
+ return ZERO_LONG;
}
return Double.parseDouble(str);
};
/**
* 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 */
* 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;
}
/**
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(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();
+ }
}