347648: Retained size column is not compared
Use RefinedStructuredResult, and have approximation
symbols.
Change-Id: Ib772a245a4087aa1c0427bcbde31f2f193350a24
Task-Url: https://bugs.eclipse.org/bugs/show_bug.cgi?id=347648
diff --git a/plugins/org.eclipse.mat.api/src/org/eclipse/mat/internal/Messages.java b/plugins/org.eclipse.mat.api/src/org/eclipse/mat/internal/Messages.java
index 347df74..cd57429 100644
--- a/plugins/org.eclipse.mat.api/src/org/eclipse/mat/internal/Messages.java
+++ b/plugins/org.eclipse.mat.api/src/org/eclipse/mat/internal/Messages.java
@@ -96,6 +96,8 @@
public static String Column_RetainedHeap;
public static String Column_ShallowHeap;
+ public static String CompareTablesQuery_APPROX;
+
public static String CompareTablesQuery_Comparing;
public static String CompareTablesQuery_Initial;
@@ -109,11 +111,15 @@
public static String CompareTablesQuery_DifferenceLast;
public static String CompareTablesQuery_DifferenceMiddle;
public static String CompareTablesQuery_DifferenceOf2;
+
+ public static String CompareTablesQuery_GE;
public static String CompareTablesQuery_IntersectionFirst;
public static String CompareTablesQuery_IntersectionLast;
public static String CompareTablesQuery_IntersectionMiddle;
public static String CompareTablesQuery_IntersectionOf2;
+ public static String CompareTablesQuery_LE;
+
public static String CompareTablesQuery_MissingKeyColumn;
public static String CompareTablesQuery_Table;
public static String CompareTablesQuery_SymmetricDifferenceFirst;
diff --git a/plugins/org.eclipse.mat.api/src/org/eclipse/mat/internal/messages.properties b/plugins/org.eclipse.mat.api/src/org/eclipse/mat/internal/messages.properties
index 62aa867..cb3e6e2 100644
--- a/plugins/org.eclipse.mat.api/src/org/eclipse/mat/internal/messages.properties
+++ b/plugins/org.eclipse.mat.api/src/org/eclipse/mat/internal/messages.properties
@@ -75,6 +75,7 @@
Column_Percentage=Percentage
Column_RetainedHeap=Retained Heap
Column_ShallowHeap=Shallow Heap
+CompareTablesQuery_APPROX=\u2248\u0020
CompareTablesQuery_Comparing=Comparing
CompareTablesQuery_Initial=Initial comparison
CompareTablesQuery_InitialComparisonForTable=Initial comparison for table {0}
@@ -105,10 +106,12 @@
CompareTablesQuery_DifferenceLast={0} and Table {1}
CompareTablesQuery_DifferenceMiddle={0}, Table {1}
CompareTablesQuery_DifferenceOf2=Table {0} without Table {1}
+CompareTablesQuery_GE=\u2265\u0020
CompareTablesQuery_IntersectionFirst=Intersection of Table {0}, Table {1}
CompareTablesQuery_IntersectionLast={0} and Table {1}
CompareTablesQuery_IntersectionMiddle={0}, Table {1}
CompareTablesQuery_IntersectionOf2=Intersection of Table {0} and Table {1}
+CompareTablesQuery_LE=\u2264\u0020
CompareTablesQuery_MissingKeyColumn=Missing key column {0} for table \#{1}
CompareTablesQuery_Table=Table {0}
CompareTablesQuery_SymmetricDifferenceFirst=Symmetric difference of Table {0}, Table {1}
diff --git a/plugins/org.eclipse.mat.api/src/org/eclipse/mat/internal/snapshot/inspections/CompareTablesQuery.java b/plugins/org.eclipse.mat.api/src/org/eclipse/mat/internal/snapshot/inspections/CompareTablesQuery.java
index 216d8bd..e687d33 100644
--- a/plugins/org.eclipse.mat.api/src/org/eclipse/mat/internal/snapshot/inspections/CompareTablesQuery.java
+++ b/plugins/org.eclipse.mat.api/src/org/eclipse/mat/internal/snapshot/inspections/CompareTablesQuery.java
@@ -16,10 +16,14 @@
import java.io.IOException;
import java.net.URISyntaxException;
import java.net.URL;
+import java.text.FieldPosition;
import java.text.Format;
+import java.text.ParsePosition;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
+import java.util.Comparator;
+import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.LinkedList;
@@ -54,6 +58,7 @@
import org.eclipse.mat.query.annotations.Icon;
import org.eclipse.mat.query.annotations.Menu;
import org.eclipse.mat.query.annotations.Menu.Entry;
+import org.eclipse.mat.query.refined.Filter;
import org.eclipse.mat.snapshot.ISnapshot;
import org.eclipse.mat.snapshot.OQL;
import org.eclipse.mat.snapshot.model.IObject;
@@ -71,6 +76,8 @@
@HelpUrl("/org.eclipse.mat.ui.help/tasks/comparingdata.html")
@Menu({ @Entry(options = "-setop ALL")
,@Entry(options = "-mode DIFF_TO_PREVIOUS -prefix -mask \"\\s@ 0x[0-9a-f]+|^\\[[0-9]+\\]$\" -x java.util.HashMap$Node:key java.util.Hashtable$Entry:key java.util.WeakHashMap$Entry:referent java.util.concurrent.ConcurrentHashMap$Node:key")
+,@Entry(options = "-mode DIFF_TO_PREVIOUS -prefix -mask \"\\s@ 0x[0-9a-f]+|^\\[[0-9]+\\]$|(?<=\\p{javaJavaIdentifierPart}\\[)\\d+(?=\\])\" -x java.util.HashMap$Node:key java.util.Hashtable$Entry:key java.util.WeakHashMap$Entry:referent java.util.concurrent.ConcurrentHashMap$Node:key")
+,@Entry(options = "-mode DIFF_TO_PREVIOUS -prefix -mask \"\\s@ 0x[0-9a-f]+|^\\[[0-9]+\\]$|(?<=\\p{javaJavaIdentifierPart}\\[)\\d+(?=\\])\" -x java.util.HashMap$Node:key java.util.Hashtable$Entry:key java.util.WeakHashMap$Entry:referent java.util.concurrent.ConcurrentHashMap$Node:key -setop ALL")
})
public class CompareTablesQuery implements IQuery
{
@@ -175,10 +182,30 @@
{
if (i == keyColumn-1)
continue;
+ // Check for duplicate column names
+ int prevDuplicateCol = -1;
+ for (int k = 0; k < i - 1; ++k)
+ {
+ if (baseColumns[i].getLabel().equals(baseColumns[k].getLabel()))
+ prevDuplicateCol = k > keyColumn - 1 ? k - 1 : k; // Adjust for key column
+ }
int[] indexes = new int[tables.length];
for (int j = 0; j < indexes.length; j++)
{
- indexes[j] = getColumnIndex(baseColumns[i].getLabel(), tables[j]);
+ if (prevDuplicateCol >= 0)
+ {
+ // Start search after previous found column
+ int pc = attributes.get(prevDuplicateCol).getColumnIndexes()[j];
+ int ci = getColumnIndex(baseColumns[i].getLabel(), tables[j], pc + 1);
+ // Not found, so duplicate the last column
+ if (ci == -1)
+ ci = pc;
+ indexes[j] = ci;
+ }
+ else
+ {
+ indexes[j] = getColumnIndex(baseColumns[i].getLabel(), tables[j], 0);
+ }
}
attributes.add(new ComparedColumn(baseColumns[i], indexes, true));
}
@@ -188,10 +215,10 @@
: new ComparisonResultTable(mergeKeys(null, listener), key, attributes, mode, setOp);
}
- private int getColumnIndex(String name, IStructuredResult table)
+ private int getColumnIndex(String name, IStructuredResult table, int colstart)
{
Column[] columns = table.getColumns();
- for (int i = 0; i < columns.length; i++)
+ for (int i = colstart; i < columns.length; i++)
{
if (columns[i].getLabel().equals(name)) return i;
}
@@ -350,7 +377,7 @@
/**
* Hold a place for a row when the key is a duplicate.
*/
- class PlaceHolder
+ static class PlaceHolder
{
Object key;
int pos;
@@ -429,6 +456,7 @@
{
treeRows = null;
}
+ Map<Object, Integer>lastcache = new HashMap<Object,Integer>();
for (int j = 0; j < size; j++)
{
@@ -454,6 +482,10 @@
map.put(key, rows);
}
int ii = i;
+ if (lastcache.containsKey(key))
+ {
+ ii = lastcache.get(key);
+ }
while (rows[ii] != null)
{
/*
@@ -471,10 +503,12 @@
// Add a placeholder so that a row goes here
map.put(new PlaceHolder(key, rows.length), new Object[0]);
// Grow the row array
- rows = Arrays.copyOf(rows, rows.length + tables.length);
+ rows = Arrays.copyOf(rows, rows.length + (rows.length / tables.length / 2 + 1) * tables.length);
map.put(key, rows);
sortwork += rows.length * rows.length;
}
+ // With many duplicates it can take a long time to find a free slot, so cache the last used
+ lastcache.put(key, ii);
}
rows[ii] = row;
if (listener.isCanceled())
@@ -547,7 +581,7 @@
if (mask != null && key != null)
{
String keystr = key.toString();
- String keystr2 = keystr.replaceAll(mask.pattern(), replace == null ? "" : replace); //$NON-NLS-1$
+ String keystr2 = mask.matcher(keystr).replaceAll(replace == null ? "" : replace); //$NON-NLS-1$
if (!keystr.equals(keystr2))
{
key = keystr2;
@@ -570,7 +604,7 @@
}
if (sfx != null)
{
- sfx = sfx.replaceAll(mask.pattern(), replace == null ? "" : replace); //$NON-NLS-1$
+ sfx = mask.matcher(sfx).replaceAll(replace == null ? "" : replace); //$NON-NLS-1$
if (sfx.length() == 0)
sfx = null;
}
@@ -700,7 +734,7 @@
* 3 3
* 4
* </pre>
- *
+ *
* not
*
* <pre>
@@ -856,7 +890,7 @@
}
}
- public class ComparedColumn
+ public static class ComparedColumn
{
Column description;
int[] columnIndexes;
@@ -1028,6 +1062,30 @@
}
}
+ /**
+ * Types of delta retained size.
+ */
+ enum DeltaEncoding {
+ /**
+ * The size delta is exact.
+ */
+ EXACT,
+ /**
+ * The size delta is at least this much.
+ */
+ GE,
+ /**
+ * The size delta is not more than this.
+ */
+ LE,
+ /**
+ * The size delta is uncertain, but this is an
+ * estimate. For example the difference between
+ * two sizes given as greater than or equal to.
+ */
+ APPROX
+ }
+
public class TableComparisonResult implements IStructuredResult, IIconProvider
{
private Column key;
@@ -1086,13 +1144,13 @@
case ABSOLUTE:
return getAbsoluteValue(cr, comparedColumnIdx, tableIdx);
case DIFF_TO_FIRST:
- return getDiffToFirst(cr, comparedColumnIdx, tableIdx, false);
+ return getDiffToPrevious(cr, columnIndex, comparedColumnIdx, tableIdx, 0, false);
case DIFF_TO_PREVIOUS:
- return getDiffToPrevious(cr, comparedColumnIdx, tableIdx, false);
+ return getDiffToPrevious(cr, columnIndex, comparedColumnIdx, tableIdx, tableIdx - 1, false);
case DIFF_RATIO_TO_FIRST:
- return getDiffToFirst(cr, comparedColumnIdx, (tableIdx + 1) / 2, tableIdx % 2 == 0);
+ return getDiffToPrevious(cr, columnIndex, comparedColumnIdx, (tableIdx + 1) / 2, 0, tableIdx % 2 == 0);
case DIFF_RATIO_TO_PREVIOUS:
- return getDiffToPrevious(cr, comparedColumnIdx, (tableIdx + 1) / 2, tableIdx % 2 == 0);
+ return getDiffToPrevious(cr, columnIndex, comparedColumnIdx, (tableIdx + 1) / 2, (tableIdx + 1) / 2 - 1, tableIdx % 2 == 0);
default:
break;
@@ -1769,51 +1827,148 @@
}
}
- private Object getDiffToFirst(ComparedRow cr, int comparedColumnIdx, int tableIdx, boolean ratio)
+ /**
+ * Convert a encoded value from a cell.
+ * The value can be encoded, and needs to be
+ * decoded before arithmetic or filtering.
+ * The formatter can handle the encoded
+ * value.
+ * @param value
+ * @param tableIdx
+ * @param comparedColumnIdx
+ * @return the converted value, of the same type
+ * as the original.
+ */
+ private Object valueConvert(Object value, int tableIdx, int comparedColumnIdx)
{
- Object tableRow = cr.getRows()[tableIdx];
- if (tableRow == null) return null;
-
+ // Optimization, presume no converter will change null
+ if (value == null)
+ return null;
int tableColumnIdx = displayedColumns.get(comparedColumnIdx).getColumnIndexes()[tableIdx];
- if (tableColumnIdx == -1) return null;
-
- Object value = tables[tableIdx].getColumnValue(tableRow, tableColumnIdx);
- Object firstTableValue = getAbsoluteValue(cr, comparedColumnIdx, 0);
-
- if (value == null && firstTableValue == null) return null;
-
- if (value == null && (firstTableValue instanceof Number || firstTableValue instanceof Bytes)) return null;
-
- if ((value instanceof Number || value instanceof Bytes) && firstTableValue == null)
+ if (tableColumnIdx == -1)
+ return value;
+ Filter.ValueConverter vc = (Filter.ValueConverter) tables[tableIdx].getColumns()[tableColumnIdx]
+ .getData(Filter.ValueConverter.class);
+ if (vc != null)
{
- return ratio ? null : value;
- }
-
- boolean returnBytes = value instanceof Bytes && firstTableValue instanceof Bytes;
- if (value instanceof Bytes)
- value = Long.valueOf(((Bytes)value).getValue());
-
- if (firstTableValue instanceof Bytes)
- firstTableValue = Long.valueOf(((Bytes)firstTableValue).getValue());
-
- if (value instanceof Number && firstTableValue instanceof Number)
- {
- Object ret = computeDiff((Number) firstTableValue, (Number) value);
- if (ratio && ret instanceof Number)
+ if (value instanceof Bytes)
{
- return percentDivide((Number)ret, (Number)firstTableValue);
+ double v0 = ((Bytes) value).getValue();
+ double v = vc.convert(v0);
+ if (v != v0)
+ value = new Bytes(Double.valueOf(v).longValue());
}
- else
+ else if (value instanceof Number)
{
- if (returnBytes)
- return new Bytes(((Number)ret).longValue());
- return ret;
+ double v0 = ((Number) value).doubleValue();
+ double v = vc.convert(v0);
+ if (v != v0)
+ {
+ // Try to convert back to original type
+ if (value instanceof Long)
+ {
+ long v1 = (long)v;
+ if (v1 == v)
+ value = v1;
+ else
+ value = v;
+ }
+ else if (value instanceof Integer)
+ {
+ int v1 = (int)v;
+ if (v1 == v)
+ value = v1;
+ else
+ value = v;
+ }
+ else if (value instanceof Short)
+ {
+ short v1 = (short)v;
+ if (v1 == v)
+ value = v1;
+ else
+ value = v;
+ }
+ else if (value instanceof Byte)
+ {
+ byte v1 = (byte)v;
+ if (v1 == v)
+ value = v1;
+ else
+ value = v;
+ }
+ else if (value instanceof Double)
+ {
+ double v1 = (double)v;
+ if (v1 == v)
+ value = v1;
+ else
+ value = v;
+ }
+ else if (value instanceof Float)
+ {
+ float v1 = (float)v;
+ if (v1 == v)
+ value = v1;
+ else
+ value = v;
+ }
+ else
+ value = v;
+ }
}
}
- return null;
+ return value;
}
- private Object getDiffToPrevious(ComparedRow cr, int comparedColumnIdx, int tableIdx, boolean ratio)
+ /**
+ * Did the converted value look like an approximate value?
+ * @param value original value
+ * @param value2 converted value
+ * @param tableIdx the table
+ * @param comparedColumnIdx the column index
+ * @return the approximation type
+ */
+ private DeltaEncoding approxValue(Object value, Object value2, int tableIdx, int comparedColumnIdx)
+ {
+ if (value != null && !value.equals(value2))
+ {
+ int tableColumnIdx = displayedColumns.get(comparedColumnIdx).getColumnIndexes()[tableIdx];
+ if (tableColumnIdx == -1)
+ return DeltaEncoding.EXACT;
+ try
+ {
+ String fv = tables[tableIdx].getColumns()[tableColumnIdx].getFormatter().format(value);
+ if (fv != null)
+ {
+ if (fv.startsWith(Messages.RetainedSizeDerivedData_Approximate))
+ return DeltaEncoding.GE;
+ else if (fv.startsWith(Messages.CompareTablesQuery_GE))
+ return DeltaEncoding.GE;
+ else if (fv.startsWith(Messages.CompareTablesQuery_LE))
+ return DeltaEncoding.LE;
+ else if (fv.startsWith(Messages.CompareTablesQuery_APPROX))
+ return DeltaEncoding.APPROX;
+ }
+ }
+ catch (IllegalArgumentException e)
+ {}
+ }
+ return DeltaEncoding.EXACT;
+ }
+
+ /**
+ * Get the value for a row and column which is a difference to a previous table
+ * (either the first or the immediately previous table).
+ * @param cr the row of created compared table/tree
+ * @param columnIdx the column index of the created compared table/tree
+ * @param comparedColumnIdx the index for the set of columns of the same name which are compared
+ * @param tableIdx the table to read
+ * @param prevTableIdx the previous table to compare to
+ * @param ratio calculate a ration, not a difference
+ * @return
+ */
+ private Object getDiffToPrevious(ComparedRow cr, int columnIdx, int comparedColumnIdx, int tableIdx, int prevTableIdx, boolean ratio)
{
Object tableRow = cr.getRows()[tableIdx];
if (tableRow == null) return null;
@@ -1822,7 +1977,13 @@
if (tableColumnIdx == -1) return null;
Object value = tables[tableIdx].getColumnValue(tableRow, tableColumnIdx);
- Object previousTableValue = getAbsoluteValue(cr, comparedColumnIdx, tableIdx - 1);
+ Object previousTableValue = getAbsoluteValue(cr, comparedColumnIdx, prevTableIdx);
+ Object value2 = valueConvert(value, tableIdx, comparedColumnIdx);
+ DeltaEncoding approxValue = approxValue(value, value2, tableIdx, comparedColumnIdx);
+ value = value2;
+ value2 = valueConvert(previousTableValue, prevTableIdx, comparedColumnIdx);
+ DeltaEncoding approxPreviousValue = approxValue(previousTableValue, value2, prevTableIdx, comparedColumnIdx);
+ previousTableValue = value2;
if (value == null && previousTableValue == null) return null;
@@ -1830,7 +1991,20 @@
if ((value instanceof Number || value instanceof Bytes) && previousTableValue == null)
{
- return ratio ? null : value;
+ if (ratio)
+ return null;
+ /*
+ * Fix up encoding of single value
+ * The source value has a difference formatter to the output so could need conversion.
+ */
+ if (approxValue != DeltaEncoding.EXACT)
+ {
+ if (value instanceof Bytes)
+ return encodeResult(((Bytes)value).getValue(), true, approxValue, approxPreviousValue, columnIdx);
+ else
+ return encodeResult(value, false, approxValue, approxPreviousValue, columnIdx);
+ }
+ return value;
}
boolean returnBytes = value instanceof Bytes && previousTableValue instanceof Bytes;
@@ -1849,14 +2023,48 @@
}
else
{
- if (returnBytes)
- return new Bytes(((Number)ret).longValue());
- return ret;
+ return encodeResult(ret, returnBytes, approxValue, approxPreviousValue, columnIdx);
}
}
return null;
}
+ /**
+ * Encode the result for a delta retained size formatter.
+ * @param ret
+ * @param returnBytes
+ * @param approxValue
+ * @param approxPreviousValue
+ * @param columnIdx
+ * @return
+ */
+ private Object encodeResult(Object ret, boolean returnBytes, DeltaEncoding approxValue, DeltaEncoding approxPreviousValue,
+ int columnIdx)
+ {
+ if (returnBytes || ret instanceof Long)
+ {
+ long val = ((Number)ret).longValue();
+ Format fmt = columns[columnIdx].getFormatter();
+ if (fmt instanceof DeltaRetainedBytesFormat)
+ {
+ DeltaRetainedBytesFormat dfmt = (DeltaRetainedBytesFormat)fmt;
+ if (approxValue == DeltaEncoding.EXACT && approxPreviousValue == DeltaEncoding.EXACT)
+ ;
+ else if ((approxValue == DeltaEncoding.GE || approxValue == DeltaEncoding.EXACT) && (approxPreviousValue == DeltaEncoding.EXACT || approxPreviousValue == DeltaEncoding.LE))
+ val = dfmt.encodege(val);
+ else if ((approxValue == DeltaEncoding.LE || approxValue == DeltaEncoding.EXACT) && (approxPreviousValue == DeltaEncoding.EXACT || approxPreviousValue == DeltaEncoding.GE))
+ val = dfmt.encodele(val);
+ else if (approxValue != DeltaEncoding.EXACT || approxPreviousValue != DeltaEncoding.EXACT)
+ val = dfmt.encodeun(val);
+ }
+ if (returnBytes)
+ return new Bytes(val);
+ else
+ return val;
+ }
+ return ret;
+ }
+
private Object computeDiff(Number o1, Number o2)
{
if (o1 instanceof Long && o2 instanceof Long) return (o2.longValue() - o1.longValue());
@@ -1955,6 +2163,224 @@
}
}
+ /**
+ * A class to format the difference between two retained sizes.
+ * Similar to {@link org.eclipse.mat.snapshot.query.RetainedSizeDerivedData.RetainedSizeFormat}.
+ * Sorting should use {@link Filter.ValueConverter} so that a dedicated comparator is
+ * not required. See {@link org.eclipse.mat.query.refined.RefinedStructuredResult.NaturalComparator}.
+ */
+ private class DeltaRetainedBytesFormat extends BytesFormat
+ {
+ /**
+ *
+ */
+ private static final long serialVersionUID = 1L;
+ /*
+ * encode >=, <= for +ve -ve byte values
+ * convert long to double for Filter.ValueConverter
+ * > 1,000,000,000,000,000 means >=
+ * <-1,000,000,000,000,000 means <=
+ * <-3,000,000,000,000,000 means ~
+ *
+ * e.g.
+ * ~= : 39
+ * ~=3 : 33
+ * ~=0 : 30
+ * >=9 : 29
+ * >=3 : 23
+ * >=-3 : 17
+ * >=-10: 10
+ *
+ * <=9 : -11
+ * <=3 : -17
+ * <=-3 : -23
+ * <=-10: -30
+ *
+ * ~-3 : -33
+ * ~-9 : -39
+ */
+ /**
+ * Break point for special encoding.
+ * Chosen to be big, but to fit precisely in a double
+ * as well as a long.
+ */
+ private long SPECIAL = 1000000000000000L;
+ /**
+ * How much to adjust a value to move it into a different range.
+ */
+ private long SPECIAL2 = SPECIAL * 2;
+ /**
+ * Convert the encoded value to a normal value.
+ */
+ final Filter.ValueConverter converter = new Filter.ValueConverter()
+ {
+ public double convert(double source)
+ {
+ if (source >= SPECIAL + SPECIAL2)
+ return source - SPECIAL - SPECIAL2; // +ve approx
+ else if (source >= SPECIAL)
+ return source - SPECIAL2; // >=
+ else if (source < -SPECIAL - SPECIAL2)
+ return source + SPECIAL + SPECIAL2; // -ve approx
+ else if (source < -SPECIAL)
+ return source + SPECIAL2; // <=
+ return source;
+ }
+ };
+
+ /**
+ * Encode a value as greater than or equal.
+ * @param l the raw value
+ * @return the encoded value
+ */
+ private long encodege(long l)
+ {
+ if (l >= SPECIAL)
+ return SPECIAL - 1 + SPECIAL2; // saturate
+ else if (l < -SPECIAL)
+ return encodeun(l); // can't be encoded as GE
+ else
+ return l + SPECIAL2;
+ }
+
+ /**
+ * Encode a value as less than or equal.
+ * @param l the raw value
+ * @return the encoded value
+ */
+ private long encodele(long l)
+ {
+ if (l >= SPECIAL)
+ return encodeun(l); // can't be coded as LE
+ else if (l < -SPECIAL)
+ return -SPECIAL - SPECIAL2; // saturate
+ else
+ return l - SPECIAL2;
+ }
+
+ /**
+ * Encode a value as inexact.
+ * @param l the raw value
+ * @return the encoded value
+ */
+ private long encodeun(long l)
+ {
+ if (l >= 0)
+ if (l >= Long.MAX_VALUE - SPECIAL - SPECIAL2)
+ return Long.MAX_VALUE; // saturate
+ else
+ return l + SPECIAL2 + SPECIAL;
+ else if (l <= Long.MIN_VALUE + SPECIAL + SPECIAL2)
+ return Long.MIN_VALUE; // saturate
+ else
+ return l - SPECIAL2 - SPECIAL;
+ }
+
+ /**
+ * Create a formatter for the difference between two retained sizes.
+ * @param encapsulatedNumberFormat
+ * @param encapsulatedDecimalFormat
+ */
+ public DeltaRetainedBytesFormat(Format encapsulatedNumberFormat, Format encapsulatedDecimalFormat)
+ {
+ super(encapsulatedNumberFormat, encapsulatedDecimalFormat);
+ }
+
+ @Override
+ public StringBuffer format(Object obj, StringBuffer toAppendTo, FieldPosition pos)
+ {
+ Number v;
+ if (obj instanceof Bytes)
+ v = ((Bytes)obj).getValue();
+ else
+ v = (Number) obj;
+
+
+ if (v.longValue() >= SPECIAL)
+ {
+ if (v.longValue() >= SPECIAL + SPECIAL2)
+ {
+ String approx = Messages.CompareTablesQuery_APPROX;
+ toAppendTo.append(approx);
+ return super.format(new Bytes(v.longValue() - SPECIAL - SPECIAL2), toAppendTo, pos);
+ }
+ else
+ {
+ String approx = Messages.CompareTablesQuery_GE;
+ toAppendTo.append(approx);
+ return super.format(new Bytes(v.longValue() - SPECIAL2), toAppendTo, pos);
+ }
+ }
+ else if (v.longValue() < -SPECIAL)
+ {
+ if (v.longValue() < -SPECIAL - SPECIAL2)
+ {
+ String approx = Messages.CompareTablesQuery_APPROX;
+ toAppendTo.append(approx);
+ return super.format(new Bytes(v.longValue() + SPECIAL + SPECIAL2), toAppendTo, pos);
+ }
+ else
+ {
+ String approx = Messages.CompareTablesQuery_LE;
+ toAppendTo.append(approx);
+ return super.format(new Bytes(v.longValue() + SPECIAL2), toAppendTo, pos);
+ }
+ }
+ else
+ {
+ return super.format(new Bytes(v.longValue()), toAppendTo, pos);
+ }
+ }
+
+ @Override
+ public Object parseObject(String source, ParsePosition pos)
+ {
+ Object ret;
+ for (String match : new String[] {Messages.CompareTablesQuery_GE, Messages.CompareTablesQuery_LE, Messages.CompareTablesQuery_APPROX})
+ {
+ if (source.regionMatches(pos.getIndex(), match, 0, match.length()))
+ {
+ int pi = pos.getIndex();
+ pos.setIndex(pi + match.length());
+ ret = super.parseObject(source, pos);
+ if (ret != null)
+ {
+ long v;
+ if (ret instanceof Bytes)
+ {
+ v = ((Bytes)ret).getValue();
+ if (match == Messages.CompareTablesQuery_GE)
+ v = encodege(v);
+ else if (match == Messages.CompareTablesQuery_LE)
+ v = encodele(v);
+ else if (match == Messages.CompareTablesQuery_APPROX)
+ v = encodeun(v);
+ return new Bytes(v);
+ }
+ else if (ret instanceof Number)
+ {
+ v = ((Number)ret).longValue();
+ if (match == Messages.CompareTablesQuery_GE)
+ v = encodege(v);
+ else if (match == Messages.CompareTablesQuery_LE)
+ v = encodele(v);
+ else if (match == Messages.CompareTablesQuery_APPROX)
+ v = encodeun(v);
+ return new Bytes(v);
+ }
+ }
+ // >= in front of something else
+ pos.setErrorIndex(pi + match.length());
+ pos.setIndex(pi);
+ ret = null;
+ return ret;
+ }
+ }
+ ret = super.parseObject(source, pos);
+ return ret;
+ }
+ }
+
private void setFormatter()
{
int i = 1;
@@ -1977,6 +2403,7 @@
bcf = bcf2;
}
BytesFormat bfm = new BytesFormat(formatter, bcf);
+ DeltaRetainedBytesFormat drbfm = new DeltaRetainedBytesFormat(formatter, bcf);
for (ComparedColumn comparedColumn : displayedColumns)
{
@@ -1994,6 +2421,10 @@
columns[i].getComparator());
columns[i].decorator(decorator);
}
+ // Set the converter
+ Object converter = c.getData(Filter.ValueConverter.class);
+ if (converter != null || columns[i].getData(Filter.ValueConverter.class) != null)
+ columns[i].setData(Filter.ValueConverter.class, converter);
if (c.getFormatter() instanceof DecimalFormat)
{
DecimalFormat fm = ((DecimalFormat) c.getFormatter().clone());
@@ -2004,7 +2435,15 @@
{
//BytesFormat fm = ((BytesFormat) c.getFormatter().clone());
// Force the sign - can't retrieve information from existing formatter
- columns[i].formatting(bfm);
+ if (c.getFormatter().getClass() == BytesFormat.class)
+ {
+ columns[i].formatting(bfm);
+ }
+ else
+ {
+ columns[i].formatting(drbfm);
+ columns[i].setData(Filter.ValueConverter.class, drbfm.converter);
+ }
}
else
{
@@ -2023,6 +2462,10 @@
columns[i].decorator(decorator);
}
columns[i].formatting(c.getFormatter());
+ // Set the converter
+ Object converter = c.getData(Filter.ValueConverter.class);
+ if (converter != null || columns[i].getData(Filter.ValueConverter.class) != null)
+ columns[i].setData(Filter.ValueConverter.class, converter);
}
i++;
if ((mode == Mode.DIFF_RATIO_TO_FIRST || mode == Mode.DIFF_RATIO_TO_PREVIOUS) && j > 0)
@@ -2094,18 +2537,43 @@
{
String label;
final int prev = mode == Mode.DIFF_TO_PREVIOUS || mode == Mode.DIFF_RATIO_TO_PREVIOUS ? j - 1 : 0;
+ Comparator<?>comparator;
if (j == 0 || mode == Mode.ABSOLUTE)
{
label = MessageUtil.format(Messages.CompareTablesQuery_ColumnAbsolute, c.getLabel(), j + 1);
+ final Comparator<Object> cmp = (Comparator<Object>) c.getComparator();
+ if (cmp != null)
+ {
+ final int tab = j;
+ comparator = new Comparator<ComparedRow>(){
+ public int compare(ComparedRow o1, ComparedRow o2)
+ {
+ Object row1 = o1.rows[tab];
+ Object row2 = o2.rows[tab];
+ // Compare nulls - sort first
+ if (row1 == null)
+ return row2 == null ? 0 : -1;
+ else if (row2 == null)
+ return 1;
+ else
+ return cmp.compare(row1, row2);
+ }
+ };
+ }
+ else
+ {
+ comparator = null;
+ }
}
else
{
label = MessageUtil.format(Messages.CompareTablesQuery_ColumnDifference,
c.getLabel(), j + 1,
prev + 1);
+ comparator = null;
}
result.add(new Column(label, c.getType(), c.getAlign(), c.getSortDirection(), c.getFormatter(),
- null));
+ comparator));
// Pass through the decorator
if (c.getDecorator() != null)
result.get(result.size() - 1).decorator(new Decorator(c.getDecorator(), j));
@@ -2140,6 +2608,12 @@
return ret;
}
+ /**
+ * Add the derived operations from the source tables.
+ * No needed as using the RefinedResult versions of the tables
+ * already has them refined.
+ * @param answer
+ */
void derivedops(ResultMetaData.Builder answer)
{
int found = 0;
diff --git a/plugins/org.eclipse.mat.api/src/org/eclipse/mat/internal/snapshot/inspections/MultiplePath2GCRootsQuery.java b/plugins/org.eclipse.mat.api/src/org/eclipse/mat/internal/snapshot/inspections/MultiplePath2GCRootsQuery.java
index b73662b..c370881 100644
--- a/plugins/org.eclipse.mat.api/src/org/eclipse/mat/internal/snapshot/inspections/MultiplePath2GCRootsQuery.java
+++ b/plugins/org.eclipse.mat.api/src/org/eclipse/mat/internal/snapshot/inspections/MultiplePath2GCRootsQuery.java
@@ -208,7 +208,7 @@
public boolean hasChildren(Object element)
{
- // to expensive to calculate
+ // too expensive to calculate
return true;
}
@@ -263,6 +263,39 @@
return result;
}
+
+ /**
+ * Needed as getElements returns new Nodes each time.
+ */
+ @Override
+ public int hashCode()
+ {
+ final int prime = 31;
+ int result = 1;
+ result = prime * result + level;
+ result = prime * result + objectId;
+ return result;
+ }
+
+ /**
+ * Needed as getElements returns new Nodes each time.
+ */
+ @Override
+ public boolean equals(Object obj)
+ {
+ if (this == obj)
+ return true;
+ if (obj == null)
+ return false;
+ if (getClass() != obj.getClass())
+ return false;
+ Node other = (Node) obj;
+ if (level != other.level)
+ return false;
+ if (objectId != other.objectId)
+ return false;
+ return true;
+ }
}
private static class ClassNode extends Node
diff --git a/plugins/org.eclipse.mat.api/src/org/eclipse/mat/internal/snapshot/inspections/annotations.properties b/plugins/org.eclipse.mat.api/src/org/eclipse/mat/internal/snapshot/inspections/annotations.properties
index 7bd16b6..24870fc 100644
--- a/plugins/org.eclipse.mat.api/src/org/eclipse/mat/internal/snapshot/inspections/annotations.properties
+++ b/plugins/org.eclipse.mat.api/src/org/eclipse/mat/internal/snapshot/inspections/annotations.properties
@@ -63,8 +63,10 @@
merged paths from objects to garbage collection roots grouped by class, so all objects of the same class are shown as one
CompareTablesQuery.name = Compare Tables and Trees
-CompareTablesQuery.menu.0.label = Compare tables and trees with all set operations
-CompareTablesQuery.menu.1.label = Compare tables and trees ignoring addresses when matching keys
+CompareTablesQuery.menu.0.label = 1|Compare tables and trees with all set operations
+CompareTablesQuery.menu.1.label = 2|Compare tables and trees ignoring addresses when matching keys
+CompareTablesQuery.menu.2.label = 3|Compare tables and trees ignoring addresses and array sizes
+CompareTablesQuery.menu.3.label = 4|Compare tables and trees ignoring addresses and array sizes with all set operations
CompareTablesQuery.help = Compares two or more tables or trees. If the tables or trees are from the same snapshot \
as the current snapshot then the context menu shows objects from those tables or trees. It also can perform \
the following operations depending on the setop argument:\n\
@@ -73,6 +75,29 @@
Table 1\u2296Table 2 The symmetric difference of that row from table 1 and table 2, so all the objects from that row in only one table (or an odd number of tables)\n\
Table 1\u2216Table 2 The difference of that row between table 1 and table 2, so all the objects from that row in table 1 but not table 2\n\
Table 2\u2216Table 1 The difference of that row between table 2 and table 1, so all the objects from that row in table 2 but not table 1
+CompareTablesQuery.menu.0.help = Compares two or more tables or trees with set operations. If the tables or trees are from the same snapshot \
+as the current snapshot then the context menu shows objects from those tables or trees. It also performs \
+the following operations via the context menu:\n\
+Table 1\u2229Table 2 The intersection of that row from table 1 and table 2, so only the objects from that row in both tables\n\
+Table 1\u222ATable 2 The intersection of that row from table 1 and table 2, so all the objects from that row in either table\n\
+Table 1\u2296Table 2 The symmetric difference of that row from table 1 and table 2, so all the objects from that row in only one table (or an odd number of tables)\n\
+Table 1\u2216Table 2 The difference of that row between table 1 and table 2, so all the objects from that row in table 1 but not table 2\n\
+Table 2\u2216Table 1 The difference of that row between table 2 and table 1, so all the objects from that row in table 2 but not table 1
+CompareTablesQuery.menu.1.help = Compares two or more tables or trees, matching different objects. Matches keys with different object addresses of the form \
+' @ 0x12345678' and array indices of the form '[1234]' in the prefix so is useful for comparisons of objects between different snapshots.
+CompareTablesQuery.menu.2.help = Compares two or more tables or trees, matching different objects and arrays. Matches keys with different object addresses of the form \
+' @ 0x12345678' and array indices of the form '[1234]' in the prefix so is useful for comparisons of objects between different snapshots. \
+Also matches keys with arrays of different sizes.
+CompareTablesQuery.menu.3.help = Compares two or more tables or trees, matching different objects and arrays and with set operations. Matches keys with different object addresses of the form \
+' @ 0x12345678' and array indices of the form '[1234]' in the prefix so is useful for comparisons of objects between different snapshots \
+or different parts of the same snapshot. \
+Also matches keys with arrays of different sizes, and for tables and trees from the current snapshot performs \
+the following operations via the context menu:\n\
+Table 1\u2229Table 2 The intersection of that row from table 1 and table 2, so only the objects from that row in both tables\n\
+Table 1\u222ATable 2 The intersection of that row from table 1 and table 2, so all the objects from that row in either table\n\
+Table 1\u2296Table 2 The symmetric difference of that row from table 1 and table 2, so all the objects from that row in only one table (or an odd number of tables)\n\
+Table 1\u2216Table 2 The difference of that row between table 1 and table 2, so all the objects from that row in table 1 but not table 2\n\
+Table 2\u2216Table 1 The difference of that row between table 2 and table 1, so all the objects from that row in table 2 but not table 1
CompareTablesQuery.tables.help = The tables or trees to be compared
CompareTablesQuery.contents.help = The query contexts corresponding to the tables
CompareTablesQuery.context.help = The query context of the current snapshot
@@ -86,7 +111,7 @@
using: \\s@ 0x[0-9a-f]+ or an array index using ^\\[[0-9]+\\]$
CompareTablesQuery.replace.help = Replacement text for mask matches.
CompareTablesQuery.prefix.help = Whether to include the prefix of the key column in the match \
-- for example the field name in a path.
+- for example the field name or array index in a path.
CompareTablesQuery.suffix.help = Whether to include the suffix of the key column in the match \
- for example the GC root type.
CompareTablesQuery.extraReferences.help = The key can be extended by adding field references to be \
diff --git a/plugins/org.eclipse.mat.report/src/org/eclipse/mat/query/refined/RefinedStructuredResult.java b/plugins/org.eclipse.mat.report/src/org/eclipse/mat/query/refined/RefinedStructuredResult.java
index 0b234d1..ef6714d 100644
--- a/plugins/org.eclipse.mat.report/src/org/eclipse/mat/query/refined/RefinedStructuredResult.java
+++ b/plugins/org.eclipse.mat.report/src/org/eclipse/mat/query/refined/RefinedStructuredResult.java
@@ -1,5 +1,5 @@
/*******************************************************************************
- * Copyright (c) 2008, 2018 SAP AG and IBM Corporation.
+ * Copyright (c) 2008, 2020 SAP AG and IBM Corporation.
* 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
@@ -20,6 +20,7 @@
import org.eclipse.mat.SnapshotException;
import org.eclipse.mat.collect.ArrayInt;
+import org.eclipse.mat.query.Bytes;
import org.eclipse.mat.query.Column;
import org.eclipse.mat.query.Column.SortDirection;
import org.eclipse.mat.query.ContextDerivedData;
@@ -107,11 +108,13 @@
{
private RefinedStructuredResult refinedResult;
private int sortColumn;
+ private Filter.ValueConverter converter;
public NaturalComparator(RefinedStructuredResult refinedResult, int sortColumn)
{
this.refinedResult = refinedResult;
this.sortColumn = sortColumn;
+ this.converter = (Filter.ValueConverter)refinedResult.columns.get(sortColumn).getData(Filter.ValueConverter.class);
}
@SuppressWarnings("unchecked")
@@ -119,6 +122,17 @@
{
Object d1 = refinedResult.getColumnValue(o1, sortColumn);
Object d2 = refinedResult.getColumnValue(o2, sortColumn);
+ if (converter != null)
+ {
+ if (d1 instanceof Bytes)
+ d1 = converter.convert(((Bytes)d1).getValue());
+ else if (d1 instanceof Number)
+ d1 = converter.convert(((Number)d1).doubleValue());
+ if (d2 instanceof Bytes)
+ d2 = converter.convert(((Bytes)d2).getValue());
+ else if (d2 instanceof Number)
+ d2 = converter.convert(((Number)d2).doubleValue());
+ }
// Compare nulls - sort first
if (d1 == null)
diff --git a/plugins/org.eclipse.mat.report/src/org/eclipse/mat/query/refined/TotalsCalculator.java b/plugins/org.eclipse.mat.report/src/org/eclipse/mat/query/refined/TotalsCalculator.java
index 0e23684..6df4f0c 100644
--- a/plugins/org.eclipse.mat.report/src/org/eclipse/mat/query/refined/TotalsCalculator.java
+++ b/plugins/org.eclipse.mat.report/src/org/eclipse/mat/query/refined/TotalsCalculator.java
@@ -1,5 +1,5 @@
/*******************************************************************************
- * Copyright (c) 2008, 2018 SAP AG and IBM Corporation.
+ * Copyright (c) 2008, 2020 SAP AG and IBM Corporation.
* 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
@@ -68,13 +68,22 @@
double[] sums = new double[thisNumericColumns.size()];
+ Filter.ValueConverter converters[] = new Filter.ValueConverter[thisNumericColumns.size()];
+ for (int ii = 0; ii < thisNumericColumns.size(); ii++)
+ {
+ int columnIndex = thisNumericColumns.get(ii);
+ if (columnIndex < 0)
+ continue;
+ converters[ii] = (Filter.ValueConverter)columns.get(columnIndex).getData(Filter.ValueConverter.class);
+ }
+
int counter = 0;
ForEachRowLoop: for (Object row : elements)
{
// check if canceled
if (++counter % 100 == 0)
if (listener.isCanceled())
- throw new IProgressListener.OperationCanceledException();
+ return answer;
for (int ii = 0; ii < thisNumericColumns.size(); ii++)
{
@@ -119,6 +128,8 @@
}
}
+ if (converters[ii] != null)
+ v = converters[ii].convert(v);
sums[ii] += v;
}
}
diff --git a/plugins/org.eclipse.mat.tests/src/org/eclipse/mat/tests/snapshot/QueryLookupTest.java b/plugins/org.eclipse.mat.tests/src/org/eclipse/mat/tests/snapshot/QueryLookupTest.java
index 3a235b3..8705415 100644
--- a/plugins/org.eclipse.mat.tests/src/org/eclipse/mat/tests/snapshot/QueryLookupTest.java
+++ b/plugins/org.eclipse.mat.tests/src/org/eclipse/mat/tests/snapshot/QueryLookupTest.java
@@ -12,6 +12,8 @@
package org.eclipse.mat.tests.snapshot;
import static org.hamcrest.core.IsEqual.equalTo;
+import static org.hamcrest.core.IsInstanceOf.instanceOf;
+import static org.hamcrest.number.IsCloseTo.closeTo;
import static org.hamcrest.number.OrderingComparison.greaterThan;
import static org.hamcrest.number.OrderingComparison.greaterThanOrEqualTo;
import static org.junit.Assert.assertFalse;
@@ -19,6 +21,10 @@
import static org.junit.Assert.assertThat;
import static org.junit.Assert.assertTrue;
+import java.io.IOException;
+import java.net.URL;
+import java.text.Format;
+import java.text.ParseException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
@@ -27,6 +33,8 @@
import java.util.Map;
import org.eclipse.mat.SnapshotException;
+import org.eclipse.mat.query.Bytes;
+import org.eclipse.mat.query.Column;
import org.eclipse.mat.query.ContextProvider;
import org.eclipse.mat.query.IContextObject;
import org.eclipse.mat.query.IContextObjectSet;
@@ -36,11 +44,16 @@
import org.eclipse.mat.query.IStructuredResult;
import org.eclipse.mat.query.annotations.descriptors.IAnnotatedObjectDescriptor;
import org.eclipse.mat.query.annotations.descriptors.IArgumentDescriptor;
+import org.eclipse.mat.query.refined.Filter;
+import org.eclipse.mat.query.refined.RefinedResultBuilder;
+import org.eclipse.mat.query.refined.RefinedTable;
+import org.eclipse.mat.query.results.TextResult;
import org.eclipse.mat.snapshot.IOQLQuery;
import org.eclipse.mat.snapshot.ISnapshot;
import org.eclipse.mat.snapshot.OQLParseException;
import org.eclipse.mat.snapshot.SnapshotFactory;
import org.eclipse.mat.snapshot.model.IClass;
+import org.eclipse.mat.snapshot.query.RetainedSizeDerivedData;
import org.eclipse.mat.snapshot.query.SnapshotQuery;
import org.eclipse.mat.tests.TestSnapshots;
import org.eclipse.mat.util.VoidProgressListener;
@@ -120,7 +133,6 @@
assertTrue(r2 != null);
}
-
/**
* Test that subtraction comparisons are done, even for sizes.
*/
@@ -374,7 +386,7 @@
rc = 0;
for (int i = 0; i < rc; ++i)
{
- processRow(snapshot1, r2, i, null, 3, 100, 0.1);
+ processRow(snapshot1, r2, i, null, 3, 50, 0.15);
}
}
@@ -403,9 +415,9 @@
}
for (ContextProvider cp : r2.getResultMetaData().getContextProviders())
{
- if (cp.getLabel().equals("Union of Table 1, Table 2 and Table 3") && row.toString().equals("java.lang.invoke.LambdaForm$MH:[1019, 513, 715]"))
- System.out.println(cp.getLabel());
- IContextObject context2 = cp.getContext(row);
+ //if (cp.getLabel().equals("Union of Table 1, Table 2 and Table 3") && row.toString().equals("java.lang.invoke.LambdaForm$MH:[1019, 513, 715]"))
+ // System.out.println(cp.getLabel());
+ IContextObject context2 = cp.getContext(row);
if (context2 instanceof IContextObjectSet)
{
IContextObjectSet ic = (IContextObjectSet)context2;
@@ -456,6 +468,295 @@
}
}
+ @Test
+ public void testCompareDiffRatioPreviousRetained1() throws SnapshotException, ParseException
+ {
+ testCompareDiffRatioPreviousRetained("histogram .*");
+ }
+
+ @Test
+ public void testCompareDiffRatioPreviousRetained2() throws SnapshotException, ParseException
+ {
+ testCompareDiffRatioPreviousRetained("oql \"select toString(c), c.@objectId, c.@objectAddress, c.@objectId.shortValue(), c.@objectId.byteValue(), c.@objectId.floatValue(), c.@objectId.doubleValue() from java.lang.Integer c\"");
+ }
+
+ public void testCompareDiffRatioPreviousRetained(String query) throws SnapshotException, ParseException
+ {
+ boolean verbose = true;
+ String queryId = "comparetablesquery";
+ ISnapshot snapshot1 = TestSnapshots.getSnapshot(TestSnapshots.SUN_JDK6_18_64BIT, false);
+ ISnapshot snapshot2 = TestSnapshots.getSnapshot(TestSnapshots.SUN_JDK6_18_32BIT, false);
+ //ISnapshot snapshot1 = TestSnapshots.getSnapshot(TestSnapshots.OPENJDK_JDK11_04_64BIT, false);
+ //ISnapshot snapshot2 = TestSnapshots.getSnapshot(TestSnapshots.ADOPTOPENJDK_HOTSPOT_JDK11_0_4_11_64BIT, false);
+
+ SnapshotQuery query1 = SnapshotQuery.parse(query, snapshot1);
+ RefinedResultBuilder rb1 = query1.refine(new VoidProgressListener());
+ rb1.setInlineRetainedSizeCalculation(true);
+ rb1.addDefaultContextDerivedColumn(RetainedSizeDerivedData.APPROXIMATE);
+ RefinedTable result1 = (RefinedTable)rb1.build();
+ List<Object>elements1 = new ArrayList<Object>();
+ // Calculate some retained sizes exactly
+ for (int i = 0; i < result1.getRowCount() - 3; i += 4)
+ {
+ elements1.add(result1.getRow(i + 1));
+ elements1.add(result1.getRow(i + 3));
+ }
+ result1.calculate(result1.getJobs().get(0).getContextProvider(), RetainedSizeDerivedData.PRECISE, elements1, null, new VoidProgressListener());
+
+ SnapshotQuery query2 = SnapshotQuery.parse(query, snapshot2);
+ RefinedResultBuilder rb2 = query2.refine(new VoidProgressListener());
+ rb2.setInlineRetainedSizeCalculation(true);
+ rb2.addDefaultContextDerivedColumn(RetainedSizeDerivedData.APPROXIMATE);
+ RefinedTable result2 = (RefinedTable)rb2.build();
+ // Calculate some retained sizes exactly
+ List<Object>elements2 = new ArrayList<Object>();
+ for (int i = 0; i < result2.getRowCount() - 3; i += 4)
+ {
+ elements2.add(result2.getRow(i + 2));
+ elements2.add(result2.getRow(i + 3));
+ }
+ result2.calculate(result2.getJobs().get(0).getContextProvider(), RetainedSizeDerivedData.PRECISE, elements2, null, new VoidProgressListener());
+
+ // Another query without retained sizes
+ SnapshotQuery query3 = SnapshotQuery.parse(query, snapshot1);
+ RefinedResultBuilder rb3 = query3.refine(new VoidProgressListener());
+ //rb3.setInlineRetainedSizeCalculation(true);
+ rb3.addDefaultContextDerivedColumn(RetainedSizeDerivedData.APPROXIMATE);
+ RefinedTable result3 = (RefinedTable)rb3.build();
+ // Calculate some retained sizes exactly
+ List<Object>elements3 = new ArrayList<Object>();
+ for (int i = 0; i < result3.getRowCount() - 3; i += 4)
+ {
+ elements3.add(result3.getRow(i + 1));
+ elements3.add(result3.getRow(i + 2));
+ }
+ result3.calculate(result3.getJobs().get(0).getContextProvider(), RetainedSizeDerivedData.PRECISE, elements3, null, new VoidProgressListener());
+
+ SnapshotQuery queryc = SnapshotQuery.parse(queryId+" -mode DIFF_RATIO_TO_PREVIOUS", snapshot1);
+
+ List<IResultTable> r = new ArrayList<IResultTable>();
+ r.add((IResultTable) result1);
+ r.add((IResultTable) result2);
+ r.add((IResultTable) result3);
+ queryc.setArgument("tables", r);
+ ArrayList<ISnapshot> snapshots = new ArrayList<ISnapshot>();
+ snapshots.add(snapshot1);
+ snapshots.add(snapshot2);
+ snapshots.add(snapshot1);
+ queryc.setArgument("snapshots", snapshots);
+ RefinedResultBuilder rbc = queryc.refine(new VoidProgressListener());
+ rbc.setSortOrder(4, null);
+ IResultTable r2 = (IResultTable)rbc.build();
+ assertTrue(r2 != null);
+ //System.out.println(Arrays.toString(r2.getColumns()));
+ int count1 = 0;
+ int count2 = 0;
+ for (int i = 0; i < r2.getRowCount(); ++i)
+ {
+ Object v0 = r2.getColumnValue(r2.getRow(i), 0);
+ for (int j = 1; j < r2.getColumns().length - 2; j += 5)
+ {
+ /*
+ * 3 tables, 5 results.
+ * Table 1
+ * Table 2 - Table 1
+ * Table 2 / Table 1
+ * Table 3 - Table 2
+ * Table 3 / Table 2
+ */
+ Object v1 = r2.getColumnValue(r2.getRow(i), j);
+ Object v2 = r2.getColumnValue(r2.getRow(i), j + 1);
+ Object v3 = r2.getColumnValue(r2.getRow(i), j + 2);
+ Object v4 = r2.getColumnValue(r2.getRow(i), j + 3);
+ Object v5 = r2.getColumnValue(r2.getRow(i), j + 4);
+ Column cols[] = r2.getColumns();
+ if (verbose) System.out.println("Row "+i+" "+cols[0].getLabel()+" "+cols[j].getLabel()+" "+cols[j+1].getLabel()+" "+cols[j+2].getLabel()+" "+cols[j+3].getLabel()+" "+cols[j+4].getLabel());
+ if (verbose) System.out.println("Row "+i+" "+v0+" "+v1+" "+v2+" "+v3+" "+v4+" "+v5);
+ // With a difference, if there is a value from table 1 then the difference has a value
+ if (v1 != null)
+ {
+ if (v2 != null)
+ ++count1;
+ if (v3 != null)
+ ++count2;
+ }
+ /*
+ * Check formatting and parsing.
+ */
+ if (v1 != null)
+ {
+ Format formatter1 = r2.getColumns()[j].getFormatter();
+ if (formatter1 != null)
+ {
+ String fv1 = formatter1.format(v1);
+ if (verbose) System.out.println(fv1);
+ assertNotNull(fv1);
+ Object vo1 = formatter1.parseObject(fv1);
+ if (v1 instanceof Double)
+ {
+ if (vo1 instanceof Long)
+ assertThat(fv1, ((Long)vo1).doubleValue(), closeTo((Double)v1, 0.01));
+ else
+ assertThat(fv1, (Double)vo1, closeTo((Double)v1, 0.01));
+ }
+ else if (v1 instanceof Number)
+ {
+ // A Bytes formatter can receive long, but return bytes
+ if (vo1 instanceof Bytes)
+ assertThat(fv1, ((Bytes)vo1).getValue(), equalTo(((Number)v1).longValue()));
+ else
+ assertThat(fv1, ((Number)vo1).doubleValue(), equalTo(((Number)v1).doubleValue()));
+ }
+ else
+ assertThat(fv1, vo1, equalTo(v1));
+ checkFormat(formatter1,r2.getColumns()[j],j);
+ }
+ }
+ if (v2 != null)
+ {
+ Format formatter2 = r2.getColumns()[j + 1].getFormatter();
+ String fv2 = formatter2.format(v2);
+ if (verbose) System.out.println(fv2);
+ assertNotNull(fv2);
+ Object vo2 = formatter2.parseObject(fv2);
+ if (v2 instanceof Double)
+ {
+ if (vo2 instanceof Long)
+ assertThat(fv2, ((Long)vo2).doubleValue(), closeTo((Double)v2, 0.01));
+ else
+ {
+ // E.g. percent can be returned as com.ibm.icu.math.BigDecimal
+ assertThat(fv2, vo2, instanceOf(Double.class));
+ assertThat(fv2, (Double)vo2, closeTo((Double)v2, 0.01));
+ }
+ }
+ else if (v2 instanceof Number)
+ {
+ // A Bytes formatter can receive long, but return bytes
+ if (vo2 instanceof Bytes)
+ assertThat(fv2, ((Bytes)vo2).getValue(), equalTo(((Number)v2).longValue()));
+ else
+ assertThat(fv2, ((Number)vo2).doubleValue(), equalTo(((Number)v2).doubleValue()));
+ }
+ else
+ assertThat(fv2, vo2, equalTo(v2));
+ checkFormat(formatter2, r2.getColumns()[j+1],j+1);
+ }
+ if (v3 != null)
+ {
+ Format formatter3 = r2.getColumns()[j + 2].getFormatter();
+ String fv3 = formatter3.format(v3);
+ if (verbose) System.out.println(fv3);
+ assertNotNull(fv3);
+ Object vo3 = formatter3.parseObject(fv3);
+ if (v3 instanceof Double)
+ {
+ // E.g. percent can be returned as com.ibm.icu.math.BigDecimal
+ assertThat(fv3, vo3, instanceOf(Number.class));
+ // Hamcrest closeTo matcher problem with infinity
+ if (Double.isInfinite((Double)v3))
+ assertThat(fv3, vo3, equalTo(v3));
+ else
+ assertThat(fv3, ((Number)vo3).doubleValue(), closeTo((Double)v3, 0.01));
+ }
+ else
+ assertThat(fv3, vo3, equalTo(v3));
+ checkFormat(formatter3, r2.getColumns()[j+2],j+2);
+ }
+ if (v4 != null)
+ {
+ Format formatter4 = r2.getColumns()[j + 3].getFormatter();
+ String fv4 = formatter4.format(v4);
+ if (verbose) System.out.println(fv4);
+ assertNotNull(fv4);
+ Object vo4 = formatter4.parseObject(fv4);
+ if (v4 instanceof Double)
+ {
+ if (vo4 instanceof Long)
+ assertThat(fv4, ((Long)vo4).doubleValue(), closeTo((Double)v4, 0.01));
+ else
+ {
+ // E.g. percent can be returned as com.ibm.icu.math.BigDecimal
+ assertThat(fv4, vo4, instanceOf(Double.class));
+ assertThat(fv4, (Double)vo4, closeTo((Double)v4, 0.01));
+ }
+ }
+ else if (v4 instanceof Number)
+ {
+ // A Bytes formatter can receive long, but return bytes
+ if (vo4 instanceof Bytes)
+ assertThat(fv4, ((Bytes)vo4).getValue(), equalTo(((Number)v4).longValue()));
+ else
+ assertThat(fv4, ((Number)vo4).doubleValue(), equalTo(((Number)v4).doubleValue()));
+ }
+ else
+ assertThat(fv4, vo4, equalTo(v4));
+ checkFormat(formatter4, r2.getColumns()[j+3],j+3);
+ }
+ if (v5 != null)
+ {
+ Format formatter5 = r2.getColumns()[j + 4].getFormatter();
+ String fv5 = formatter5.format(v5);
+ if (verbose) System.out.println(fv5);
+ assertNotNull(fv5);
+ Object vo5 = formatter5.parseObject(fv5);
+ if (v5 instanceof Double)
+ {
+ // E.g. percent can be returned as com.ibm.icu.math.BigDecimal
+ assertThat(fv5, vo5, instanceOf(Number.class));
+ // Hamcrest closeTo matcher problem with infinity
+ if (Double.isInfinite((Double)v5))
+ assertThat(fv5, vo5, equalTo(v5));
+ else
+ assertThat(fv5, ((Number)vo5).doubleValue(), closeTo((Double)v5, 0.01));
+ }
+ else
+ assertThat(fv5, vo5, equalTo(v5));
+ checkFormat(formatter5, r2.getColumns()[j+4],j+4);
+ }
+ }
+ }
+ assertThat(count1, greaterThan(0));
+ assertThat(count2, greaterThan(0));
+ }
+
+ void checkFormat(Format f, Column c, int colIdx) throws ParseException
+ {
+ final long special = 1000000000000000L;
+ long values[]= {Long.MIN_VALUE, -(1L<<53), -(1L<<52), -(1L<<51), -(1L<<50), -special*3, -special*2, -special, 0, special, special * 2, special * 3, 1L>>50, 1L>>51, 1L>>52, 1L>>53, Long.MAX_VALUE};
+ long dl[] = {-special, -special + 1, -1, 0, 1, special - 1, special};
+ Filter.ValueConverter vc = (Filter.ValueConverter)c.getData(Filter.ValueConverter.class);
+ for (long v1 : values)
+ {
+ for (long v2 : dl)
+ {
+ long v = v1 + v2;
+ String s = f.format(v);
+ Object vx = f.parseObject(s);
+ //System.out.println("formatted "+v+" as "+s);
+ if (vx instanceof Bytes)
+ assertThat(f.toString() + " " + s, ((Bytes)vx).getValue(), equalTo(((Number)v).longValue()));
+ else
+ assertThat(f.toString() + " " + s, ((Number)vx).longValue(), equalTo(v));
+ if (vc != null)
+ {
+ // After conversion should be an ordinary number
+ double d2 = vc.convert(v);
+ String s2 = f.format(d2);
+ // but some still might not convert
+ long limit = 4000000000000000L;
+ /*
+ * approximate values outside limit range can't be
+ * converted to a plain number which can be printed
+ * without conversion.
+ */
+ if (v < limit && v >= -limit || !s.contains("\u2248"))
+ assertTrue(colIdx+":"+c.getLabel()+" "+f.toString() +" " + vc + "\n" + v + "\n" + d2 + "\n" + s + "\n" + s2, s2.matches("[+-]?[0-9,]+(\\.[0-9]+)?(\\s?%)?"));
+ }
+ }
+ }
+ }
+
/**
* Test that set operations intersection is done.
*/
@@ -604,6 +905,7 @@
/**
* Test that set operations are done.
+ * @throws IOException
*/
public void testCompareSetOperations(ISnapshot snapshot1, String setOp, int o1[], int o2[], int o3[], int ei1, int e1[], int ei2, int e2[]) throws SnapshotException
{
@@ -646,6 +948,20 @@
for (ContextProvider cp : r2.getResultMetaData().getContextProviders())
{
//System.out.println(cp+ " " + cp.getLabel());
+ cp.getLabel();
+ URL u = cp.getIcon();
+ if (u != null)
+ {
+ try
+ {
+ Object o = u.getContent();
+ }
+ catch (IOException e)
+ {
+ throw new SnapshotException(e);
+ }
+ assertNotNull(u);
+ }
}
ContextProvider cp1 = r2.getResultMetaData().getContextProviders().get(ei1);
diff --git a/plugins/org.eclipse.mat.ui/src/org/eclipse/mat/ui/compare/CompareBasketView.java b/plugins/org.eclipse.mat.ui/src/org/eclipse/mat/ui/compare/CompareBasketView.java
index ed0a1e4..70a1598 100644
--- a/plugins/org.eclipse.mat.ui/src/org/eclipse/mat/ui/compare/CompareBasketView.java
+++ b/plugins/org.eclipse.mat.ui/src/org/eclipse/mat/ui/compare/CompareBasketView.java
@@ -1,5 +1,5 @@
/*******************************************************************************
- * Copyright (c) 2010, 2019 SAP AG and IBM Corporation
+ * Copyright (c) 2010, 2020 SAP AG and IBM Corporation
* 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
@@ -36,6 +36,7 @@
import org.eclipse.mat.query.IResultTable;
import org.eclipse.mat.query.IResultTree;
import org.eclipse.mat.query.IStructuredResult;
+import org.eclipse.mat.query.refined.RefinedStructuredResult;
import org.eclipse.mat.query.registry.ArgumentDescriptor;
import org.eclipse.mat.query.registry.ArgumentSet;
import org.eclipse.mat.query.registry.QueryDescriptor;
@@ -162,24 +163,32 @@
ComparedResult entry = null;
if (pane != null)
{
- QueryResult qr = pane.getAdapter(QueryResult.class);
- if (qr != null && qr.getSubject() instanceof IResultTree)
- entry = new ComparedResult(state, editor, (IResultTree)qr.getSubject());
- if (qr != null && qr.getSubject() instanceof IResultTable)
- entry = new ComparedResult(state, editor, (IResultTable)qr.getSubject());
+ RefinedStructuredResult rsr = pane.getAdapter(RefinedStructuredResult.class);
+ if (rsr != null && rsr instanceof IResultTree)
+ entry = new ComparedResult(state, editor, (IResultTree)rsr);
+ else if (rsr != null && rsr instanceof IResultTable)
+ entry = new ComparedResult(state, editor, (IResultTable)rsr);
+ else
+ {
+ QueryResult qr = pane.getAdapter(QueryResult.class);
+ if (qr != null && qr.getSubject() instanceof IResultTree)
+ entry = new ComparedResult(state, editor, (IResultTree)qr.getSubject());
+ else if (qr != null && qr.getSubject() instanceof IResultTable)
+ entry = new ComparedResult(state, editor, (IResultTable)qr.getSubject());
+ else if (pane instanceof HistogramPane)
+ {
+ entry = new ComparedResult(state, editor, ((HistogramPane) pane).getHistogram());
+ }
+ else if (pane instanceof TableResultPane)
+ {
+ entry = new ComparedResult(state, editor, (IResultTable) ((TableResultPane) pane).getSrcQueryResult().getSubject());
+ }
+ else if (pane instanceof QueryResultPane)
+ {
+ entry = new ComparedResult(state, editor, (IResultTree) ((QueryResultPane) pane).getSrcQueryResult().getSubject());
+ }
+ }
}
- if (pane instanceof HistogramPane)
- {
- entry = new ComparedResult(state, editor, ((HistogramPane) pane).getHistogram());
- }
- else if (pane instanceof TableResultPane)
- {
- entry = new ComparedResult(state, editor, (IResultTable) ((TableResultPane) pane).getSrcQueryResult().getSubject());
- }
- else if (pane instanceof QueryResultPane)
- {
- entry = new ComparedResult(state, editor, (IResultTree) ((QueryResultPane) pane).getSrcQueryResult().getSubject());
- }
if (entry != null)
{
@@ -214,7 +223,7 @@
return true;
}
return (pane instanceof HistogramPane) || (pane instanceof TableResultPane) ||
- pane instanceof QueryResultPane &&
+ pane instanceof QueryResultPane &&
((QueryResultPane) pane).getSrcQueryResult().getSubject() instanceof IResultTree;
}
@@ -601,7 +610,7 @@
private final IQueryContext currentContext;
private final List<ISnapshot> snapshots;
private final ISnapshot currentSnapshot;
-
+
private ComparePolicy(List<IStructuredResult> tables, List<IQueryContext> contexts, IQueryContext currentContext)
{
this.tables = tables;
@@ -614,7 +623,7 @@
}
currentSnapshot = (ISnapshot)currentContext.get(ISnapshot.class, null);
}
-
+
/**
* Only operate on queries with multiple tables and query contexts
*/
@@ -632,8 +641,19 @@
{
if (!argument.getType().isAssignableFrom(res.getClass()))
{
- // Can't convert table/tree
- return false;
+ if (res instanceof RefinedStructuredResult)
+ {
+ // Perhaps the query needs a specific sort of result, not the refined version
+ IStructuredResult isr = ((RefinedStructuredResult)res).unwrap();
+ if (!argument.getType().isAssignableFrom(isr.getClass()))
+ // Can't convert unwrapped table/tree
+ return false;
+ }
+ else
+ {
+ // Can't convert table/tree
+ return false;
+ }
}
foundTables = true;
}
@@ -682,19 +702,34 @@
}
return foundTables && (foundContexts || foundSnapshots);
}
-
+
public void fillInObjectArguments(ISnapshot snapshot, QueryDescriptor query, ArgumentSet set)
{
for (ArgumentDescriptor argument : query.getArguments()) {
+
if (IStructuredResult.class.isAssignableFrom(argument.getType()))
{
+ List<IStructuredResult> tables1 = new ArrayList<IStructuredResult>();
+ // Do we need to unwrap some results?
+ for (IStructuredResult res : tables)
+ {
+ // Perhaps the query needs a specific sort of result, not the refined version
+ if (!argument.getType().isAssignableFrom(res.getClass()) && res instanceof RefinedStructuredResult)
+ {
+ // Already tested in accept() that this works
+ IStructuredResult isr = ((RefinedStructuredResult)res).unwrap();
+ tables1.add(isr);
+ }
+ else
+ tables1.add(res);
+ }
if (argument.isMultiple())
{
- set.setArgumentValue(argument, tables);
+ set.setArgumentValue(argument, tables1);
}
else
{
- set.setArgumentValue(argument, tables.get(0));
+ set.setArgumentValue(argument, tables1.get(0));
}
}
else if (IQueryContext.class.isAssignableFrom(argument.getType()))
diff --git a/plugins/org.eclipse.mat.ui/src/org/eclipse/mat/ui/internal/panes/QueryResultPane.java b/plugins/org.eclipse.mat.ui/src/org/eclipse/mat/ui/internal/panes/QueryResultPane.java
index 7df9ce3..7303635 100644
--- a/plugins/org.eclipse.mat.ui/src/org/eclipse/mat/ui/internal/panes/QueryResultPane.java
+++ b/plugins/org.eclipse.mat.ui/src/org/eclipse/mat/ui/internal/panes/QueryResultPane.java
@@ -1,5 +1,5 @@
/*******************************************************************************
- * Copyright (c) 2008, 2019 SAP AG and IBM Corporation.
+ * Copyright (c) 2008, 2020 SAP AG and IBM Corporation.
* 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
@@ -34,6 +34,7 @@
import org.eclipse.mat.query.IResultTree;
import org.eclipse.mat.query.IStructuredResult;
import org.eclipse.mat.query.refined.RefinedResultBuilder;
+import org.eclipse.mat.query.refined.RefinedStructuredResult;
import org.eclipse.mat.query.refined.RefinedTable;
import org.eclipse.mat.query.refined.RefinedTree;
import org.eclipse.mat.query.registry.ArgumentSet;
@@ -386,6 +387,10 @@
{
return (adapter.cast(srcQueryResult));
}
+ if (adapter.isAssignableFrom(RefinedStructuredResult.class))
+ {
+ return (adapter.cast(viewer.getResult()));
+ }
if (adapter.isAssignableFrom(srcQueryResult.getSubject().getClass()))
{
return (adapter.cast(srcQueryResult.getSubject()));