Skip to main content
aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAndrew Johnson2020-03-06 17:54:16 +0000
committerAndrew Johnson2020-03-08 11:50:46 +0000
commit5f7e344d50048335334c9b514d218c081e43915d (patch)
tree492a8a28ba71c9dcebeb2e92d0ed55a13d21b23b
parentd8e9667ef3bbdc2b3d9f669fdf7133fd0c3c452b (diff)
downloadorg.eclipse.mat-5f7e344d50048335334c9b514d218c081e43915d.tar.gz
org.eclipse.mat-5f7e344d50048335334c9b514d218c081e43915d.tar.xz
org.eclipse.mat-5f7e344d50048335334c9b514d218c081e43915d.zip
283778: Diff Heap Dumps
Tree comparisons and allow duplicate keys Change-Id: I9d9ff7d72dca4cf28a3bcf5fc1e7687318fcbaa0 Task-Url: https://bugs.eclipse.org/bugs/show_bug.cgi?id=283778
-rw-r--r--plugins/org.eclipse.mat.api/src/org/eclipse/mat/internal/Messages.java8
-rw-r--r--plugins/org.eclipse.mat.api/src/org/eclipse/mat/internal/messages.properties6
-rw-r--r--plugins/org.eclipse.mat.api/src/org/eclipse/mat/internal/snapshot/inspections/CompareTablesQuery.java1931
-rw-r--r--plugins/org.eclipse.mat.api/src/org/eclipse/mat/internal/snapshot/inspections/annotations.properties27
-rw-r--r--plugins/org.eclipse.mat.tests/src/org/eclipse/mat/tests/snapshot/OQLTest.java15
-rw-r--r--plugins/org.eclipse.mat.tests/src/org/eclipse/mat/tests/snapshot/QueryLookupTest.java525
-rw-r--r--plugins/org.eclipse.mat.ui/plugin.xml2
7 files changed, 1996 insertions, 518 deletions
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 90cbbc19..347df744 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,12 @@ public class Messages extends NLS
public static String Column_RetainedHeap;
public static String Column_ShallowHeap;
+ public static String CompareTablesQuery_Comparing;
+ public static String CompareTablesQuery_Initial;
+
+ public static String CompareTablesQuery_InitialComparisonForTable;
+ public static String CompareTablesQuery_ResolvingDuplicateKey;
+ public static String CompareTablesQuery_BuildingResult;
public static String CompareTablesQuery_ColumnAbsolute;
public static String CompareTablesQuery_ColumnDifference;
public static String CompareTablesQuery_ColumnPercentDifference;
@@ -107,6 +113,8 @@ public class Messages extends NLS
public static String CompareTablesQuery_IntersectionLast;
public static String CompareTablesQuery_IntersectionMiddle;
public static String CompareTablesQuery_IntersectionOf2;
+
+ public static String CompareTablesQuery_MissingKeyColumn;
public static String CompareTablesQuery_Table;
public static String CompareTablesQuery_SymmetricDifferenceFirst;
public static String CompareTablesQuery_SymmetricDifferenceLast;
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 6e44751b..62aa8672 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,11 @@ Column_Objects=Objects
Column_Percentage=Percentage
Column_RetainedHeap=Retained Heap
Column_ShallowHeap=Shallow Heap
+CompareTablesQuery_Comparing=Comparing
+CompareTablesQuery_Initial=Initial comparison
+CompareTablesQuery_InitialComparisonForTable=Initial comparison for table {0}
+CompareTablesQuery_ResolvingDuplicateKey=Resolving duplicate key {0}
+CompareTablesQuery_BuildingResult=Building result
# These are for if the Unicode set operations are available
#CompareTablesQuery_DifferenceFirst=Table {0}\u2216Table {1}
#CompareTablesQuery_DifferenceLast={0}\u2216Table {1}
@@ -104,6 +109,7 @@ 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_MissingKeyColumn=Missing key column {0} for table \#{1}
CompareTablesQuery_Table=Table {0}
CompareTablesQuery_SymmetricDifferenceFirst=Symmetric difference of Table {0}, Table {1}
CompareTablesQuery_SymmetricDifferenceLast={0} and 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 880f39c6..216d8bde 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
@@ -1,5 +1,5 @@
/*******************************************************************************
- * Copyright (c) 2010, 2018 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
@@ -10,23 +10,37 @@
******************************************************************************/
package org.eclipse.mat.internal.snapshot.inspections;
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileReader;
+import java.io.IOException;
import java.net.URISyntaxException;
import java.net.URL;
import java.text.Format;
import java.util.ArrayList;
-import java.util.HashMap;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.LinkedHashMap;
import java.util.LinkedList;
import java.util.List;
+import java.util.Locale;
import java.util.Map;
+import java.util.Set;
+import java.util.regex.Pattern;
+import org.eclipse.mat.SnapshotException;
import org.eclipse.mat.collect.ArrayInt;
import org.eclipse.mat.internal.Messages;
import org.eclipse.mat.query.Bytes;
import org.eclipse.mat.query.BytesFormat;
import org.eclipse.mat.query.Column;
+import org.eclipse.mat.query.ContextDerivedData;
+import org.eclipse.mat.query.ContextDerivedData.DerivedOperation;
import org.eclipse.mat.query.ContextProvider;
import org.eclipse.mat.query.IContextObject;
import org.eclipse.mat.query.IContextObjectSet;
+import org.eclipse.mat.query.IDecorator;
import org.eclipse.mat.query.IIconProvider;
import org.eclipse.mat.query.IQuery;
import org.eclipse.mat.query.IQueryContext;
@@ -42,9 +56,12 @@ import org.eclipse.mat.query.annotations.Menu;
import org.eclipse.mat.query.annotations.Menu.Entry;
import org.eclipse.mat.snapshot.ISnapshot;
import org.eclipse.mat.snapshot.OQL;
+import org.eclipse.mat.snapshot.model.IObject;
import org.eclipse.mat.snapshot.query.Icons;
import org.eclipse.mat.util.IProgressListener;
import org.eclipse.mat.util.MessageUtil;
+import org.eclipse.mat.util.SimpleMonitor;
+import org.eclipse.mat.util.VoidProgressListener;
import com.ibm.icu.text.DecimalFormat;
import com.ibm.icu.text.DecimalFormatSymbols;
@@ -53,286 +70,1002 @@ import com.ibm.icu.text.NumberFormat;
@Icon("/META-INF/icons/compare.gif")
@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")
})
public class CompareTablesQuery implements IQuery
{
- @Argument
- public IStructuredResult[] tables;
+ @Argument
+ public IStructuredResult[] tables;
@Argument
public IQueryContext queryContext;
-
- @Argument(isMandatory = false)
+
+ @Argument(isMandatory = false)
public ISnapshot[] snapshots;
- @Argument(isMandatory = false)
- public Mode mode = Mode.ABSOLUTE;
+ @Argument(isMandatory = false)
+ public Mode mode = Mode.ABSOLUTE;
- @Argument(isMandatory = false)
- public Operation setOp = Operation.NONE;
+ @Argument(isMandatory = false)
+ public Operation setOp = Operation.NONE;
@Argument(isMandatory = false)
public int keyColumn = 1;
-
+
+ @Argument(isMandatory = false)
+ public Pattern mask;
+
+ @Argument(isMandatory = false)
+ public String replace;
+
+ @Argument(isMandatory = false)
+ public boolean prefix;
+
+ @Argument(isMandatory = false)
+ public boolean suffix;
+
+ @Argument(isMandatory = false, flag = "x")
+ public String[] extraReferences;
+
+ @Argument(isMandatory = false, flag = "xfile")
+ public File extraReferencesListFile;
+
private boolean[] sameSnapshot;
- public enum Mode
- {
- ABSOLUTE("ABSOLUTE"), // //$NON-NLS-1$
- DIFF_TO_FIRST("DIFF_TO_FIRST"), // //$NON-NLS-1$
- DIFF_TO_PREVIOUS("DIFF_TO_PREVIOUS"), //$NON-NLS-1$
- DIFF_RATIO_TO_FIRST("DIFF_RATIO_TO_FIRST"), // //$NON-NLS-1$
- DIFF_RATIO_TO_PREVIOUS("DIFF_RATIO_TO_PREVIOUS"); //$NON-NLS-1$
-
- String label;
-
- private Mode(String label)
- {
- this.label = label;
- }
-
- public String toString()
- {
- return label;
- }
- }
-
- public enum Operation
- {
- NONE,
- ALL,
- INTERSECTION,
- UNION,
- SYMMETRIC_DIFFERENCE,
- DIFFERENCE
- }
-
- public IResult execute(IProgressListener listener) throws Exception
- {
- if (tables == null) return null;
-
- // Length 1 table is valid, and we need to process it in case it is from a different snapshot
-
- IStructuredResult base = tables[0];
- Column[] baseColumns = base.getColumns();
- Column key = baseColumns[keyColumn-1];
-
- sameSnapshot = new boolean[tables.length];
- ISnapshot sn = (ISnapshot)queryContext.get(ISnapshot.class, null);
- for (int i = 0; i < tables.length; ++i)
- {
- sameSnapshot[i] = (snapshots[i] == null || sn.equals(snapshots[i]));
- }
-
- List<ComparedColumn> attributes = new ArrayList<ComparedColumn>();
- for (int i = 0; i < baseColumns.length; i++)
- {
- if (i == keyColumn-1)
- continue;
- int[] indexes = new int[tables.length];
- for (int j = 0; j < indexes.length; j++)
- {
- indexes[j] = getColumnIndex(baseColumns[i].getLabel(), tables[j]);
- }
- attributes.add(new ComparedColumn(baseColumns[i], indexes, true));
- }
-
- return new TableComparisonResult(mergeKeys(), key, attributes, mode, setOp);
- }
-
- private int getColumnIndex(String name, IStructuredResult table)
- {
- Column[] columns = table.getColumns();
- for (int i = 0; i < columns.length; i++)
- {
- if (columns[i].getLabel().equals(name)) return i;
- }
- return -1;
- }
-
- private List<ComparedRow> mergeKeys()
- {
- Map<Object, Object[]> map = new HashMap<Object, Object[]>();
- for (int i = 0; i < tables.length; i++)
- {
- IStructuredResult table = tables[i];
- int size;
- if (table instanceof IResultTable)
- {
- size = ((IResultTable)table).getRowCount();
- }
- else if (table instanceof IResultTree)
- {
- size = ((IResultTree)table).getElements().size();
- }
- else
- {
- size = 0;
- }
-
- for (int j = 0; j < size; j++)
- {
-
- Object row;
- if (table instanceof IResultTable)
- {
- row = ((IResultTable)table).getRow(j);
- }
- else if (table instanceof IResultTree)
- {
- row = ((IResultTree)table).getElements().get(j);
- }
- else
- {
- row = null;
- }
- Object key = table.getColumnValue(row, keyColumn-1);
- Object[] rows = map.get(key);
- if (rows == null)
- {
- rows = new Object[tables.length];
- map.put(key, rows);
- }
- rows[i] = row;
- }
- }
-
- List<ComparedRow> result = new ArrayList<ComparedRow>(map.size());
- for (Map.Entry<Object, Object[]> entry : map.entrySet())
- {
- result.add(new ComparedRow(entry.getKey(), entry.getValue()));
- }
-
- return result;
- }
-
- public class ComparedColumn
- {
- Column description;
- int[] columnIndexes;
- boolean displayed;
-
- public ComparedColumn(Column description, int[] columnIndexes, boolean displayed)
- {
- super();
- this.displayed = displayed;
- this.description = description;
- this.columnIndexes = columnIndexes;
- }
-
- public Column getDescription()
- {
- return description;
- }
-
- public void setDescription(Column description)
- {
- this.description = description;
- }
-
- public int[] getColumnIndexes()
- {
- return columnIndexes;
- }
-
- public void setColumnIndexes(int[] columnIndexes)
- {
- this.columnIndexes = columnIndexes;
- }
-
- public boolean isDisplayed()
- {
- return displayed;
- }
-
- public void setDisplayed(boolean displayed)
- {
- this.displayed = displayed;
- }
-
- }
-
- class ComparedRow
- {
- Object key;
- Object[] rows;
-
- public ComparedRow(Object key, Object[] rows)
- {
- super();
- this.key = key;
- this.rows = rows;
- }
-
- public Object getKey()
- {
- return key;
- }
-
- public void setKey(Object key)
- {
- this.key = key;
- }
-
- public Object[] getRows()
- {
- return rows;
- }
-
- public void setRows(Object[] rows)
- {
- this.rows = rows;
- }
- }
-
- public class TableComparisonResult implements IResultTable, IIconProvider
- {
- private Column key;
- private List<ComparedRow> rows;
- /** each compared column is a column from the original table, and is displayed as several actual columns, one or more for each table */
- private List<ComparedColumn> comparedColumns;
- /** each displayed column is a column from the original table, and is displayed as several actual columns, one or more for each table */
- private List<ComparedColumn> displayedColumns;
- /** Actual columns displayed */
- private Column[] columns;
- private Mode mode;
- private Operation setOp;
-
- public TableComparisonResult(List<ComparedRow> rows, Column key, List<ComparedColumn> comparedColumns, Mode mode, Operation setOp)
- {
- this.key = key;
- this.mode = mode;
- this.rows = rows;
- this.comparedColumns = comparedColumns;
- updateColumns();
- setMode(mode);
- this.setOp = setOp;
- }
-
- public Object getRow(int rowId)
- {
- return rows.get(rowId);
- }
-
- public int getRowCount()
- {
- return rows.size();
- }
-
- public List<ComparedColumn> getComparedColumns()
- {
- return comparedColumns;
- }
-
- public void setComparedColumns(List<ComparedColumn> comparedColumns)
- {
- this.comparedColumns = comparedColumns;
- }
-
- public Object getColumnValue(Object row, int columnIndex)
- {
- ComparedRow cr = (ComparedRow) row;
- if (columnIndex == 0) return cr.getKey();
+ public enum Mode
+ {
+ ABSOLUTE("ABSOLUTE"), // //$NON-NLS-1$
+ DIFF_TO_FIRST("DIFF_TO_FIRST"), // //$NON-NLS-1$
+ DIFF_TO_PREVIOUS("DIFF_TO_PREVIOUS"), //$NON-NLS-1$
+ DIFF_RATIO_TO_FIRST("DIFF_RATIO_TO_FIRST"), // //$NON-NLS-1$
+ DIFF_RATIO_TO_PREVIOUS("DIFF_RATIO_TO_PREVIOUS"); //$NON-NLS-1$
+
+ String label;
+
+ private Mode(String label)
+ {
+ this.label = label;
+ }
+
+ public String toString()
+ {
+ return label;
+ }
+ }
+
+ public enum Operation
+ {
+ NONE,
+ ALL,
+ INTERSECTION,
+ UNION,
+ SYMMETRIC_DIFFERENCE,
+ DIFFERENCE
+ }
+
+ public IResult execute(IProgressListener listener) throws Exception
+ {
+ if (tables == null) return null;
+
+ // Length 1 table is valid, and we need to process it in case it is from a different snapshot
+
+ // Check key column is present
+ for (int i = 0; i < tables.length; ++i)
+ {
+ if (keyColumn > tables[i].getColumns().length || keyColumn < 1)
+ throw new IllegalArgumentException(MessageUtil.format(Messages.CompareTablesQuery_MissingKeyColumn, keyColumn, i + 1));
+ }
+
+ IStructuredResult base = tables[0];
+ Column[] baseColumns = base.getColumns();
+ Column key = baseColumns[keyColumn-1];
+
+ sameSnapshot = new boolean[tables.length];
+ ISnapshot sn = (ISnapshot)queryContext.get(ISnapshot.class, null);
+ boolean foundTree = false;
+ for (int i = 0; i < tables.length; ++i)
+ {
+ sameSnapshot[i] = (snapshots[i] == null || sn.equals(snapshots[i]));
+ if (tables[i] instanceof IResultTree)
+ foundTree = true;
+ }
+
+ List<ComparedColumn> attributes = new ArrayList<ComparedColumn>();
+ for (int i = 0; i < baseColumns.length; i++)
+ {
+ if (i == keyColumn-1)
+ continue;
+ int[] indexes = new int[tables.length];
+ for (int j = 0; j < indexes.length; j++)
+ {
+ indexes[j] = getColumnIndex(baseColumns[i].getLabel(), tables[j]);
+ }
+ attributes.add(new ComparedColumn(baseColumns[i], indexes, true));
+ }
+
+ collectExtraRefs();
+ return foundTree ? new ComparisonResultTree(mergeKeys(null, listener), key, attributes, mode, setOp)
+ : new ComparisonResultTable(mergeKeys(null, listener), key, attributes, mode, setOp);
+ }
+
+ private int getColumnIndex(String name, IStructuredResult table)
+ {
+ Column[] columns = table.getColumns();
+ for (int i = 0; i < columns.length; i++)
+ {
+ if (columns[i].getLabel().equals(name)) return i;
+ }
+ return -1;
+ }
+
+ /**
+ * Calculate extra parts for the key.
+ * @param table index
+ * @param row
+ * @return a String or null
+ */
+ String extraKey(int table, Object row)
+ {
+ final String noextra = null;
+ if (extraReferences == null || extraReferences.length == 0)
+ return noextra;
+ if (snapshots[table] == null)
+ return noextra;
+ IContextObject ctx = tables[table].getContext(row);
+ if (ctx == null)
+ return noextra;
+ int objId = ctx.getObjectId();
+ if (objId == -1)
+ return noextra;
+ IObject obj;
+ try
+ {
+ obj = snapshots[table].getObject(ctx.getObjectId());
+ }
+ catch (SnapshotException e)
+ {
+ return noextra;
+ }
+ StringBuilder sb = new StringBuilder();
+ for (String s : extraReferences)
+ {
+ String p1[] = s.split(":", 2); //$NON-NLS-1$
+ if (p1.length < 1)
+ continue;
+ else if (p1.length < 2)
+ {
+ // Just the class name, so resolve just the object
+ String val = obj.getClassSpecificName();
+ if (val != null)
+ {
+ if (sb.length() > 0)
+ sb.append(' ');
+ sb.append(val);
+ }
+ continue;
+ }
+ try
+ {
+ if (obj.getClazz().doesExtend(p1[0]))
+ {
+ for (String field : p1[1].split(",")) //$NON-NLS-1$
+ {
+ Object o;
+ try
+ {
+ o = obj.resolveValue(field);
+ }
+ catch (SnapshotException e)
+ {
+ continue;
+ }
+ String val;
+ if (o instanceof IObject)
+ {
+ val = ((IObject)o).getClassSpecificName();
+ }
+ else if (o != null)
+ {
+ val = o.toString();
+ }
+ else
+ {
+ val = null;
+ }
+
+ if (val != null)
+ {
+ if (sb.length() > 0)
+ sb.append(' ');
+ sb.append(val);
+ }
+ }
+ }
+ }
+ catch (SnapshotException e)
+ {
+ }
+ }
+ if (sb.length() > 0)
+ return sb.toString();
+ return noextra;
+ }
+
+ /**
+ * Collect the extra type+field references.
+ */
+ void collectExtraRefs() throws IOException
+ {
+ // extra key refs
+ // read the file (if any)
+ String[] fromFile = getLinesFromFile();
+ if (fromFile != null && fromFile.length > 0)
+ {
+ if (extraReferences != null)
+ {
+ // merge from file and manually entered entries
+ String[] tmp = new String[fromFile.length + extraReferences.length];
+ System.arraycopy(fromFile, 0, tmp, 0, fromFile.length);
+ System.arraycopy(extraReferences, 0, tmp, fromFile.length, extraReferences.length);
+ extraReferences = tmp;
+ }
+ else
+ {
+ extraReferences = fromFile;
+ }
+ }
+ }
+
+ /**
+ * Read from a file.
+ * @return
+ * @throws IOException
+ */
+ private String[] getLinesFromFile() throws IOException
+ {
+ if (extraReferencesListFile == null)
+ return null;
+
+ BufferedReader in = null;
+ List<String> result = new ArrayList<String>();
+ try
+ {
+ in = new BufferedReader(new FileReader(extraReferencesListFile));
+ String line = null;
+ while ((line = in.readLine()) != null)
+ {
+ result.add(line);
+ }
+ }
+ finally
+ {
+ if (in != null)
+ {
+ in.close();
+ }
+ }
+ return result.toArray(new String[0]);
+ }
+
+ /**
+ * Hold a place for a row when the key is a duplicate.
+ */
+ class PlaceHolder
+ {
+ Object key;
+ int pos;
+ PlaceHolder(Object key, int pos)
+ {
+ this.key = key;
+ this.pos = pos;
+ }
+ }
+
+ private List<ComparedRow> mergeKeys(ComparedRow parent, IProgressListener listener)
+ {
+ SimpleMonitor sm = new SimpleMonitor(Messages.CompareTablesQuery_Comparing, listener, new int[] {60,30,10});
+ Map<Object, Object[]> map = new LinkedHashMap<Object, Object[]>();
+ int sizes[] = new int[tables.length];
+ // Only get elements once
+ List<?>elements[] = new List<?>[tables.length];
+ // Calculate the total number of rows
+ int totalsize = 0;
+ for (int i = 0; i < tables.length; i++)
+ {
+ IStructuredResult table = tables[i];
+ int size;
+ if (table instanceof IResultTable)
+ {
+ if (parent == null)
+ size = ((IResultTable)table).getRowCount();
+ else
+ size = 0;
+ }
+ else if (table instanceof IResultTree)
+ {
+ if (parent == null)
+ {
+ elements[i] = ((IResultTree)table).getElements();
+ size = elements[i].size();
+ }
+ else
+ {
+ Object treerow = parent.getRows()[i];
+ if (treerow != null && ((IResultTree)table).hasChildren(treerow))
+ {
+ elements[i] = ((IResultTree)table).getChildren(treerow);
+ size = elements[i].size();
+ }
+ else
+ {
+ size = 0;
+ }
+ }
+ }
+ else
+ {
+ size = 0;
+ }
+ sizes[i] = size;
+ totalsize += size;
+ // For overflow
+ if (totalsize < 0)
+ totalsize = Integer.MAX_VALUE;
+ }
+ listener = sm.nextMonitor();
+ listener.beginTask(Messages.CompareTablesQuery_Initial, totalsize);
+ long sortwork = 0;
+ for (int i = 0; i < tables.length; i++)
+ {
+ listener.subTask(MessageUtil.format(Messages.CompareTablesQuery_InitialComparisonForTable, i + 1));
+ IStructuredResult table = tables[i];
+ int size = sizes[i];
+ List<?>treeRows;
+ if (table instanceof IResultTree)
+ {
+ treeRows = elements[i];
+ }
+ else
+ {
+ treeRows = null;
+ }
+ for (int j = 0; j < size; j++)
+ {
+
+ Object row;
+ if (table instanceof IResultTable)
+ {
+ row = ((IResultTable)table).getRow(j);
+ }
+ else if (table instanceof IResultTree)
+ {
+ row = treeRows.get(j);
+ }
+ else
+ {
+ row = null;
+ }
+ Object key = table.getColumnValue(row, keyColumn-1);
+ key = modifyKey(i, row, key);
+ Object[] rows = map.get(key);
+ if (rows == null)
+ {
+ rows = new Object[tables.length];
+ map.put(key, rows);
+ }
+ int ii = i;
+ while (rows[ii] != null)
+ {
+ /*
+ * Normally:
+ * rows[rowTable1, rowTableb]
+ * With duplicate keys from one Table we extend the array
+ * rows[rowTable1, rowTable2, rowTable1v2, rowTable2v2, rowTable1v3, null]
+ * etc.
+ * and later convert to multiple rows
+ */
+ ii += tables.length;
+ if (ii >= rows.length)
+ {
+ sortwork -= rows.length * rows.length;
+ // 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);
+ map.put(key, rows);
+ sortwork += rows.length * rows.length;
+ }
+ }
+ rows[ii] = row;
+ if (listener.isCanceled())
+ throw new IProgressListener.OperationCanceledException();
+ listener.worked(1);
+ }
+ }
+ listener.done();
+
+ listener = sm.nextMonitor();
+ listener.beginTask(Messages.CompareTablesQuery_Initial, totalsize);
+ // Match up duplicate keys
+ for (Map.Entry<Object, Object[]> entry : map.entrySet())
+ {
+ Object rows[] = entry.getValue();
+ if (rows.length > tables.length)
+ {
+ listener.subTask(MessageUtil.format(Messages.CompareTablesQuery_ResolvingDuplicateKey, entry.getKey()));
+ // Duplicated key, so expand to separate rows
+ sortRows(rows);
+ // Guess n-squared work
+ listener.worked((int)(totalsize * (long)rows.length * rows.length / sortwork));
+ }
+ if (listener.isCanceled())
+ throw new IProgressListener.OperationCanceledException();
+ }
+ listener.done();
+
+ listener = sm.nextMonitor();
+ listener.beginTask(Messages.CompareTablesQuery_BuildingResult, totalsize);
+ List<ComparedRow> result = new ArrayList<ComparedRow>(map.size());
+ for (Map.Entry<Object, Object[]> entry : map.entrySet())
+ {
+ Object key = entry.getKey();
+ Object rows[] = entry.getValue();
+ if (key instanceof PlaceHolder)
+ {
+ // A subsequent duplicated key
+ PlaceHolder ph = (PlaceHolder)key;
+ // Find the real, sorted row list
+ rows = map.get(ph.key);
+ // Extract the appropriate part
+ Object rows1[] = Arrays.copyOfRange(rows, ph.pos, ph.pos + tables.length);
+ result.add(new ComparedRow(ph.key, rows1));
+ }
+ else if (rows.length <= tables.length)
+ result.add(new ComparedRow(entry.getKey(), entry.getValue()));
+ else
+ {
+ // Duplicated key, but this is the first record
+ Object rows1[] = Arrays.copyOfRange(rows, 0, tables.length);
+ result.add(new ComparedRow(entry.getKey(), rows1));
+ }
+ if (listener.isCanceled())
+ throw new IProgressListener.OperationCanceledException();
+ listener.worked(1);
+ }
+ listener.done();
+
+ return result;
+ }
+
+ private Object modifyKey(int i, Object row, Object key)
+ {
+ String extrakey = extraKey(i, row);
+ if (extrakey != null)
+ {
+ key = key + " " + extrakey; //$NON-NLS-1$
+ }
+ if (mask != null && key != null)
+ {
+ String keystr = key.toString();
+ String keystr2 = keystr.replaceAll(mask.pattern(), replace == null ? "" : replace); //$NON-NLS-1$
+ if (!keystr.equals(keystr2))
+ {
+ key = keystr2;
+ }
+ }
+ // Fix up decoration
+ if (prefix || suffix)
+ {
+ Column c = tables[i].getColumns()[keyColumn - 1];
+ IDecorator id = c.getDecorator();
+ if (id != null)
+ {
+ String pfx = prefix ? id.prefix(row) : null;
+ String sfx = suffix ? id.suffix(row) : null;
+ if (pfx != null)
+ {
+ pfx = pfx.replaceAll(mask.pattern(), replace == null ? "" : replace); //$NON-NLS-1$
+ if (pfx.length() == 0)
+ pfx = null;
+ }
+ if (sfx != null)
+ {
+ sfx = sfx.replaceAll(mask.pattern(), replace == null ? "" : replace); //$NON-NLS-1$
+ if (sfx.length() == 0)
+ sfx = null;
+ }
+ if (pfx != null || sfx != null)
+ key = new ComparedRow(pfx, key, sfx, null);
+ }
+ }
+ return key;
+ }
+
+ /**
+ * See if two rows from two tables match.
+ * Do a bit more matching via IContextObject.
+ * Match via context object ID or address.
+ * @param table1 index
+ * @param row1
+ * @param table2 index
+ * @param row2
+ * @param matchType - the higher the number the more likely a match
+ * @return true if they seem the same
+ */
+ boolean rowMatches(int table1, Object row1, int table2, Object row2, int matchType)
+ {
+ IContextObject ctx1 = tables[table1].getContext(row1);
+ IContextObject ctx2 = tables[table2].getContext(row2);
+ if (ctx1 == null && ctx2 == null)
+ return true;
+ if (ctx1 == null || ctx2 == null)
+ return false;
+ int objectId1 = ctx1.getObjectId();
+ int objectId2 = ctx2.getObjectId();
+ if (sameSnapshot[table1] && sameSnapshot[table2] ||
+ snapshots[table1].equals(snapshots[table2]))
+ {
+ //System.out.println("compare "+ctx1.getObjectId() +" "+ ctx2.getObjectId());
+ return objectId1 == objectId2;
+ }
+ if (objectId1 == -1 && objectId2 == -1)
+ return true;
+ if (objectId1 == -1 || objectId2 == -1)
+ return false;
+ try
+ {
+ long addr1 = snapshots[table1].mapIdToAddress(objectId1);
+ long addr2 = snapshots[table2].mapIdToAddress(objectId2);
+ // Address match, guess the same?
+ if (addr1 == addr2)
+ return true;
+ // Classes don't match, so different
+ if (snapshots[table1].getClassOf(objectId1).equals(snapshots[table2].getClassOf(objectId2)))
+ return false;
+ if (matchType >= 1)
+ {
+ // Guess match on retained size, presuming it retains more than itself!
+ if (snapshots[table1].getRetainedHeapSize(objectId1) > snapshots[table1].getHeapSize(1)
+ && snapshots[table1].getRetainedHeapSize(objectId1) == snapshots[table2]
+ .getRetainedHeapSize(objectId2))
+ return true;
+ }
+ // Don't compare using getClassSpecificName() as this could be done
+ // on the key
+ if (matchType >= 2)
+ {
+ String val1 = snapshots[table1].getObject(objectId1).getClassSpecificName();
+ String val2 = snapshots[table1].getObject(objectId1).getClassSpecificName();
+ if (val1 != null && val1.equals(val2))
+ return true;
+ }
+ return false;
+ }
+ catch (SnapshotException e)
+ {
+ return false;
+ }
+ }
+
+ /**
+ * Try to match rows from different tables. Have definite matches in same
+ * row. Unmatched can be anywhere.
+ *
+ * <pre>
+ * A2=B3,A2=C1,A3=B5,A4=C2
+ * [A1,B1,C1,A2,B2,C2,A3,B3,null,A4,B4,null,null,B5,null]
+ * A1 B1 C1
+ * A2 B2 C2
+ * A3 B3
+ * A4 B4
+ * B5
+ * </pre>
+ *
+ * Goes to:
+ *
+ * <pre>
+ * A1 B1
+ * A2 B3 C1
+ * A3 B5
+ * A4 B2 C2
+ * B4
+ * </pre>
+ * <ol>
+ * <li>find first match of B[1]..B[5] with A[1] and put in first slot (&
+ * shift others up)</li>
+ * <li>find next match of B[2]..B[5] in with A[2] and put in second slot (&
+ * shift others up)</li>
+ * <li>..</li>
+ * <li>find first match of C[1]..C[2] with A[1] and put in first slot (&
+ * shift others up)</li>
+ * <li>if no match find first match of C[1]..C[2] with B[1] and put in first
+ * slot (& shift others up)</li>
+ * <li>if no match find first match of C[2]..C[2] with A[2] and put in first
+ * slot (& shift others up)</li>
+ * </ol>
+ * Not perfect though:
+ *
+ * <pre>
+ * 1 1 2
+ * 2 3 5
+ * 3 5
+ * 4
+ * </pre>
+ *
+ * gives
+ *
+ * <pre>
+ * 1 1 5
+ * 2 5 2
+ * 3 3
+ * 4
+ * </pre>
+ *
+ * not
+ *
+ * <pre>
+ * 1 1
+ * 2 2
+ * 3 3
+ * 4 5 5
+ * </pre>
+ * Try a second pass to fix up those.
+ * @param rows
+ */
+ void sortRows(Object rows[])
+ {
+ int rn = rows.length;
+ int tn = tables.length;
+ int n = rn / tn;
+ Object queue[] = new Object[n];
+ // This entry is matched to another table
+ boolean matched[] = new boolean[rows.length];
+ //System.out.println("Sorting " + n);
+ // Consider each table other than the first
+ for (int i = 1; i < tn; ++i)
+ {
+ // Distribute this table to matching slots
+ // Fill the queue
+ int q1 = 0, q2 = 0;
+ for (int l = 0; l < n; ++l)
+ {
+ Object o = rows[l * tn + i];
+ if (o != null)
+ {
+ queue[q2++] = o;
+ rows[l * tn + i] = null;
+ }
+ }
+ // We don't need to clear the remainder of the queue
+ // Matching slot in preceding table
+ slot: for (int j = 0; j < n; ++j)
+ {
+ // System.out.println("Slot "+i+":"+j);
+ for (int pass = 0; pass < 2; ++pass)
+ {
+ // Choose preceding table
+ for (int k = 0; k < i; ++k)
+ {
+ // Preceding value
+ Object rowPrev = rows[j * tn + k];
+ if (rowPrev == null)
+ continue;
+ // Matching preceding tables
+ // Choose entry from the queue
+ for (int l = q1; l < q2; ++l)
+ {
+ Object rowThis = queue[l];
+ if (rowThis == null)
+ {
+ // Optimization - adjust the queue limit
+ if (l == q1)
+ ++q1;
+ continue;
+ }
+ //System.out.println("Comparing " + i + ":" + l + " to " + k + ":" + j);
+ if (rowMatches(i, rowThis, k, rowPrev, pass))
+ {
+ //System.out.println("Matched " + i + ":" + l + " to " + k + ":" + j);
+ matched[j * tn + i] = true;
+ matched[j * tn + k] = true;
+ rows[j * tn + i] = rowThis;
+ // stop reuse
+ queue[l] = null;
+ // optimization: last queue entry, so avoid looking here again
+ if (l == q2 - 1)
+ --q2;
+ continue slot;
+ }
+ }
+ }
+ }
+ // Say this slot might need to be filled later
+ rows[j * tn + i] = null;
+ }
+ // Distribute the unused
+ for (int l = 0, lq = q1; l < n && lq < q2; ++l)
+ {
+ // Read slot
+ Object o = rows[l * tn + i];
+ if (o == null)
+ {
+ // Read unused element from queue
+ while (lq < q2)
+ {
+ Object q = queue[lq++];
+ if (q != null)
+ {
+ // and fill the slot
+ rows[l * tn + i] = q;
+ break;
+ }
+ }
+ }
+ }
+ }
+ // Try some polishing of the result
+ // Consider each table other than the first
+ for (int i = 1; i < tn; ++i)
+ {
+ // Matching slot in preceding table
+ for (int j = 0; j < n; ++j)
+ {
+ for (int pass = 0; pass < 2; ++pass)
+ {
+ // Choose preceding table
+ for (int k = 0; k < i; ++k)
+ {
+ if (matched[j * tn + k])
+ continue;
+ // Preceding value
+ Object rowPrev = rows[j * tn + k];
+ if (rowPrev == null)
+ continue;
+ for (int l = 0; l < n; ++l)
+ {
+ if (matched[l * tn + i])
+ continue;
+ Object rowThis = rows[l * tn + i];
+ if (rowThis == null)
+ continue;
+ if (rowMatches(i, rowThis, k, rowPrev, 0))
+ {
+ // System.out.println("extra match " + i + ":" + l + " " + k + ":" + j);
+ // Find a corresponding free slot in both tables
+ for (int m = 0; m < n; ++m)
+ {
+ if (!matched[m * tn + i] && !matched[m * tn + k])
+ {
+ // System.out.println("Swapped " + i + ":" + l + " " + i + ":" + m + " " + k + ":" + j + " " + k + ":" + m);
+ // Swap and mark as matched
+ Object oldThis = rows[m * tn + i];
+ rows[m * tn + i] = rowThis;
+ rows[l * tn + i] = oldThis;
+ matched[m * tn + i] = true;
+ Object oldPrev = rows[m * tn + k];
+ rows[m * tn + k] = rowPrev;
+ rows[j * tn + i] = oldPrev;
+ matched[m * tn + k] = true;
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ public class ComparedColumn
+ {
+ Column description;
+ int[] columnIndexes;
+ boolean displayed;
+
+ public ComparedColumn(Column description, int[] columnIndexes, boolean displayed)
+ {
+ super();
+ this.displayed = displayed;
+ this.description = description;
+ this.columnIndexes = columnIndexes;
+ }
+
+ public Column getDescription()
+ {
+ return description;
+ }
+
+ public void setDescription(Column description)
+ {
+ this.description = description;
+ }
+
+ public int[] getColumnIndexes()
+ {
+ return columnIndexes;
+ }
+
+ public void setColumnIndexes(int[] columnIndexes)
+ {
+ this.columnIndexes = columnIndexes;
+ }
+
+ public boolean isDisplayed()
+ {
+ return displayed;
+ }
+
+ public void setDisplayed(boolean displayed)
+ {
+ this.displayed = displayed;
+ }
+
+ }
+
+ static class ComparedRow
+ {
+ Object key;
+ private String prefix;
+ private String suffix;
+ Object[] rows;
+
+ public ComparedRow(Object key, Object[] rows)
+ {
+ super();
+ if (key instanceof ComparedRow)
+ {
+ ComparedRow cr = (ComparedRow)key;
+ this.key = cr.key;
+ this.prefix = cr.prefix;
+ this.suffix = cr.suffix;
+ }
+ else
+ {
+ this.key = key;
+ }
+ this.rows = rows;
+ }
+
+ public ComparedRow(String prefix, Object key, String suffix, Object[] rows)
+ {
+ super();
+ this.prefix = prefix;
+ this.key = key;
+ this.suffix = suffix;
+ this.rows = rows;
+ }
+
+ public Object getKey()
+ {
+ return key;
+ }
+
+ public void setKey(Object key)
+ {
+ this.key = key;
+ }
+
+ public Object[] getRows()
+ {
+ return rows;
+ }
+
+ public void setRows(Object[] rows)
+ {
+ this.rows = rows;
+ }
+
+ String getPrefix()
+ {
+ return prefix;
+ }
+
+ void setPrefix(String prefix)
+ {
+ this.prefix = prefix;
+ }
+
+ String getSuffix()
+ {
+ return suffix;
+ }
+
+ void setSuffix(String suffix)
+ {
+ this.suffix = suffix;
+ }
+
+ @Override
+ public int hashCode()
+ {
+ final int prime = 31;
+ int result = 1;
+ result = prime * result + ((key == null) ? 0 : key.hashCode());
+ result = prime * result + ((prefix == null) ? 0 : prefix.hashCode());
+ result = prime * result + ((suffix == null) ? 0 : suffix.hashCode());
+ return result;
+ }
+
+ @Override
+ public boolean equals(Object obj)
+ {
+ if (this == obj)
+ return true;
+ if (obj == null)
+ return false;
+ if (getClass() != obj.getClass())
+ return false;
+ ComparedRow other = (ComparedRow) obj;
+ if (key == null)
+ {
+ if (other.key != null)
+ return false;
+ }
+ else if (!key.equals(other.key))
+ return false;
+ if (prefix == null)
+ {
+ if (other.prefix != null)
+ return false;
+ }
+ else if (!prefix.equals(other.prefix))
+ return false;
+ if (suffix == null)
+ {
+ if (other.suffix != null)
+ return false;
+ }
+ else if (!suffix.equals(other.suffix))
+ return false;
+ return true;
+ }
+
+ public String toString()
+ {
+ String p1 = prefix != null ? "(" + prefix + ") " : ""; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+ String s1 = suffix != null ? " (" + suffix + ")" : ""; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+ return p1 + key + s1 + ":" + Arrays.toString(rows); //$NON-NLS-1$
+ }
+ }
+
+ public class TableComparisonResult implements IStructuredResult, IIconProvider
+ {
+ private Column key;
+ protected List<ComparedRow> rows;
+ /** each compared column is a column from the original table, and is displayed as several actual columns, one or more for each table */
+ private List<ComparedColumn> comparedColumns;
+ /** each displayed column is a column from the original table, and is displayed as several actual columns, one or more for each table */
+ private List<ComparedColumn> displayedColumns;
+ /** Actual columns displayed */
+ private Column[] columns;
+ private Mode mode;
+ private Operation setOp;
+
+ public TableComparisonResult(List<ComparedRow> rows, Column key, List<ComparedColumn> comparedColumns, Mode mode, Operation setOp)
+ {
+ this.key = key;
+ this.mode = mode;
+ this.rows = rows;
+ this.comparedColumns = comparedColumns;
+ updateColumns();
+ setMode(mode);
+ this.setOp = setOp;
+ }
+
+ public List<ComparedColumn> getComparedColumns()
+ {
+ return comparedColumns;
+ }
+
+ public void setComparedColumns(List<ComparedColumn> comparedColumns)
+ {
+ this.comparedColumns = comparedColumns;
+ }
+
+ public Object getColumnValue(Object row, int columnIndex)
+ {
+ ComparedRow cr = (ComparedRow) row;
+ if (columnIndex == 0) return cr.getKey();
/*
* Each compared column has several subcolumns for data from each
@@ -346,36 +1079,36 @@ public class CompareTablesQuery implements IQuery
int comparedColumnIdx = (columnIndex - 1) / subCols;
int tableIdx = (columnIndex - 1) % subCols;
- if (tableIdx == 0) return getAbsoluteValue(cr, comparedColumnIdx, tableIdx);
+ if (tableIdx == 0) return getAbsoluteValue(cr, comparedColumnIdx, tableIdx);
- switch (mode)
- {
- case ABSOLUTE:
- return getAbsoluteValue(cr, comparedColumnIdx, tableIdx);
- case DIFF_TO_FIRST:
- return getDiffToFirst(cr, comparedColumnIdx, tableIdx, false);
- case DIFF_TO_PREVIOUS:
- return getDiffToPrevious(cr, comparedColumnIdx, tableIdx, false);
- case DIFF_RATIO_TO_FIRST:
- return getDiffToFirst(cr, comparedColumnIdx, (tableIdx + 1) / 2, tableIdx % 2 == 0);
- case DIFF_RATIO_TO_PREVIOUS:
- return getDiffToPrevious(cr, comparedColumnIdx, (tableIdx + 1) / 2, tableIdx % 2 == 0);
-
- default:
- break;
- }
+ switch (mode)
+ {
+ case ABSOLUTE:
+ return getAbsoluteValue(cr, comparedColumnIdx, tableIdx);
+ case DIFF_TO_FIRST:
+ return getDiffToFirst(cr, comparedColumnIdx, tableIdx, false);
+ case DIFF_TO_PREVIOUS:
+ return getDiffToPrevious(cr, comparedColumnIdx, tableIdx, false);
+ case DIFF_RATIO_TO_FIRST:
+ return getDiffToFirst(cr, comparedColumnIdx, (tableIdx + 1) / 2, tableIdx % 2 == 0);
+ case DIFF_RATIO_TO_PREVIOUS:
+ return getDiffToPrevious(cr, comparedColumnIdx, (tableIdx + 1) / 2, tableIdx % 2 == 0);
+
+ default:
+ break;
+ }
- return null;
+ return null;
- }
+ }
- public Column[] getColumns()
- {
- return columns;
- }
+ public Column[] getColumns()
+ {
+ return columns;
+ }
- public IContextObject getContext(Object row)
- {
+ public IContextObject getContext(Object row)
+ {
// Find a context from one of the tables
IContextObject ret = null;
for (int i = 0; i < tables.length && ret == null; ++i)
@@ -383,10 +1116,10 @@ public class CompareTablesQuery implements IQuery
ret = getContextFromTable(i, row);
}
return ret;
- }
+ }
- public ResultMetaData getResultMetaData()
- {
+ public ResultMetaData getResultMetaData()
+ {
ResultMetaData.Builder bb = new ResultMetaData.Builder();
int previous = -1;
for (int i = 0; i < tables.length; ++i)
@@ -413,6 +1146,8 @@ public class CompareTablesQuery implements IQuery
{
for (int k = previous + 1; k <= i; ++k)
{
+ if (!sameSnapshot[k])
+ continue;
toDo.addFirst(k);
}
}
@@ -420,6 +1155,8 @@ public class CompareTablesQuery implements IQuery
{
for (int k = previous + 1; k <= i; ++k)
{
+ if (!sameSnapshot[k])
+ continue;
toDo.add(k);
}
}
@@ -442,20 +1179,20 @@ public class CompareTablesQuery implements IQuery
{
switch (op)
{
- case 0:
- title1 = MessageUtil.format(Messages.CompareTablesQuery_IntersectionOf2, toDo.get(0)+1, toDo.get(1)+1);
- break;
- case 1:
- title1 = MessageUtil.format(Messages.CompareTablesQuery_UnionOf2, toDo.get(0)+1, toDo.get(1)+1);
- break;
- case 2:
- title1 = MessageUtil.format(Messages.CompareTablesQuery_SymmetricDifferenceOf2, toDo.get(0)+1, toDo.get(1)+1);
- break;
- case 3:
- case 4:
- default:
- title1 = MessageUtil.format(Messages.CompareTablesQuery_DifferenceOf2, toDo.get(0)+1, toDo.get(1)+1);
- break;
+ case 0:
+ title1 = MessageUtil.format(Messages.CompareTablesQuery_IntersectionOf2, toDo.get(0)+1, toDo.get(1)+1);
+ break;
+ case 1:
+ title1 = MessageUtil.format(Messages.CompareTablesQuery_UnionOf2, toDo.get(0)+1, toDo.get(1)+1);
+ break;
+ case 2:
+ title1 = MessageUtil.format(Messages.CompareTablesQuery_SymmetricDifferenceOf2, toDo.get(0)+1, toDo.get(1)+1);
+ break;
+ case 3:
+ case 4:
+ default:
+ title1 = MessageUtil.format(Messages.CompareTablesQuery_DifferenceOf2, toDo.get(0)+1, toDo.get(1)+1);
+ break;
}
} else {
String soFar;
@@ -480,58 +1217,59 @@ public class CompareTablesQuery implements IQuery
{
switch (op)
{
+ case 0:
+ soFar = MessageUtil.format(Messages.CompareTablesQuery_IntersectionMiddle, soFar, toDo.get(t)+1);
+ break;
+ case 1:
+ soFar = MessageUtil.format(Messages.CompareTablesQuery_UnionMiddle, soFar, toDo.get(t)+1);
+ break;
+ case 2:
+ soFar = MessageUtil.format(Messages.CompareTablesQuery_SymmetricDifferenceMiddle, soFar, toDo.get(t)+1);
+ break;
+ case 3:
+ case 4:
+ default:
+ soFar = MessageUtil.format(Messages.CompareTablesQuery_DifferenceMiddle, soFar, toDo.get(t)+1);
+ break;
+ }
+ }
+ int t = toDo.size() - 1;
+ switch (op)
+ {
case 0:
- soFar = MessageUtil.format(Messages.CompareTablesQuery_IntersectionMiddle, soFar, toDo.get(t)+1);
- break;
+ title1 = MessageUtil.format(Messages.CompareTablesQuery_IntersectionLast, soFar, toDo.get(t)+1);
+ break;
case 1:
- soFar = MessageUtil.format(Messages.CompareTablesQuery_UnionMiddle, soFar, toDo.get(t)+1);
+ title1 = MessageUtil.format(Messages.CompareTablesQuery_UnionLast, soFar, toDo.get(t)+1);
break;
case 2:
- soFar = MessageUtil.format(Messages.CompareTablesQuery_SymmetricDifferenceMiddle, soFar, toDo.get(t)+1);
+ title1 = MessageUtil.format(Messages.CompareTablesQuery_SymmetricDifferenceLast, soFar, toDo.get(t)+1);
break;
case 3:
case 4:
default:
- soFar = MessageUtil.format(Messages.CompareTablesQuery_DifferenceMiddle, soFar, toDo.get(t)+1);
+ title1 = MessageUtil.format(Messages.CompareTablesQuery_DifferenceLast, soFar, toDo.get(t)+1);
break;
- }
- }
- int t = toDo.size() - 1;
- switch (op)
- {
- case 0:
- title1 = MessageUtil.format(Messages.CompareTablesQuery_IntersectionLast, soFar, toDo.get(t)+1);
- break;
- case 1:
- title1 = MessageUtil.format(Messages.CompareTablesQuery_UnionLast, soFar, toDo.get(t)+1);
- break;
- case 2:
- title1 = MessageUtil.format(Messages.CompareTablesQuery_SymmetricDifferenceLast, soFar, toDo.get(t)+1);
- break;
- case 3:
- case 4:
- default:
- title1 = MessageUtil.format(Messages.CompareTablesQuery_DifferenceLast, soFar, toDo.get(t)+1);
- break;
}
}
ContextProvider prov2 = new ContextProvider(title1)
{
+ private static final String EMPTY_QUERY = "SELECT * FROM OBJECTS 0 WHERE false"; //$NON-NLS-1$
public URL getIcon()
{
switch (op1)
{
- case 0:
- return Icons.getURL("set_intersection.gif"); //$NON-NLS-1$
- case 1:
- return Icons.getURL("set_union.gif"); //$NON-NLS-1$
- case 2:
- return Icons.getURL("set_symmetric_difference.gif"); //$NON-NLS-1$
- case 3:
- return Icons.getURL("set_differenceA.gif"); //$NON-NLS-1$
- case 4:
- return Icons.getURL("set_differenceB.gif"); //$NON-NLS-1$
+ case 0:
+ return Icons.getURL("set_intersection.gif"); //$NON-NLS-1$
+ case 1:
+ return Icons.getURL("set_union.gif"); //$NON-NLS-1$
+ case 2:
+ return Icons.getURL("set_symmetric_difference.gif"); //$NON-NLS-1$
+ case 3:
+ return Icons.getURL("set_differenceA.gif"); //$NON-NLS-1$
+ case 4:
+ return Icons.getURL("set_differenceB.gif"); //$NON-NLS-1$
}
return null;
}
@@ -554,7 +1292,7 @@ public class CompareTablesQuery implements IQuery
return null;
// First non-null context
final IContextObject cb = getContextFromTable(nullContexts, row);
-
+
return new IContextObjectSet()
{
public int getObjectId()
@@ -574,7 +1312,10 @@ public class CompareTablesQuery implements IQuery
{
IContextObject cb = getContextFromTable(j, row);
if (cb == null)
- continue;
+ {
+ // No objects so intersection empty
+ return EMPTY_QUERY;
+ }
String oql = getOQLfromContext(cb);
if (oql != null)
{
@@ -591,7 +1332,7 @@ public class CompareTablesQuery implements IQuery
}
}
return resultOQL0;
- // Union
+ // Union
case 1:
StringBuilder resultOQL = new StringBuilder();
for (int j : toDo2)
@@ -610,10 +1351,8 @@ public class CompareTablesQuery implements IQuery
}
}
// Remove duplicates
- resultOQL.insert(0, "SELECT DISTINCT OBJECTS @objectId FROM OBJECTS ("); //$NON-NLS-1$
- resultOQL.append(")"); //$NON-NLS-1$
- return resultOQL.toString();
- // Symmetric Difference
+ return OQLdistinct(resultOQL.toString());
+ // Symmetric Difference
case 2:
String resultOQL2 = null;
for (int j : toDo2)
@@ -641,7 +1380,7 @@ public class CompareTablesQuery implements IQuery
}
}
return resultOQL2;
- // Difference
+ // Difference
case 3:
case 4:
String resultOQL3 = null;
@@ -649,7 +1388,14 @@ public class CompareTablesQuery implements IQuery
{
IContextObject cb = getContextFromTable(j, row);
if (cb == null)
+ {
+ if (resultOQL3 == null)
+ {
+ // First table has no objects so subtraction empty
+ return EMPTY_QUERY;
+ }
continue;
+ }
String oql = getOQLfromContext(cb);
if (oql != null)
{
@@ -679,8 +1425,12 @@ public class CompareTablesQuery implements IQuery
private String OQLexcept(String oql1, String oql2)
{
//SELECT * FROM OBJECTS (a) where @objectid in (b)
- oql1 = "SELECT * FROM OBJECTS ("+oql1+")" + //$NON-NLS-1$//$NON-NLS-2$
- " WHERE @objectId not in ("+oql2+")"; //$NON-NLS-1$//$NON-NLS-2$
+ if ((OQLobjectQuery(oql1)))
+ oql1 = "SELECT * FROM OBJECTS ("+oql1+")" + //$NON-NLS-1$//$NON-NLS-2$
+ " WHERE @objectId in ("+oql2+") = false"; //$NON-NLS-1$//$NON-NLS-2$
+ else
+ oql1 = "SELECT s as \"\" FROM OBJECTS ("+oql1+") s" + //$NON-NLS-1$//$NON-NLS-2$
+ " WHERE s in ("+oql2+") = false"; //$NON-NLS-1$//$NON-NLS-2$
return oql1;
}
@@ -693,12 +1443,43 @@ public class CompareTablesQuery implements IQuery
private String OQLintersection(String oql1, String oql2)
{
//SELECT * FROM OBJECTS (a) where @objectid in (b)
- oql1 = "SELECT * FROM OBJECTS (" + oql1 + ")" + //$NON-NLS-1$//$NON-NLS-2$
- " WHERE @objectId in (" + oql2 + ")"; //$NON-NLS-1$//$NON-NLS-2$
+ if ((OQLobjectQuery(oql1)))
+ oql1 = "SELECT * FROM OBJECTS (" + oql1 + ")" + //$NON-NLS-1$//$NON-NLS-2$
+ " WHERE @objectId in (" + oql2 + ")"; //$NON-NLS-1$//$NON-NLS-2$
+ else
+ oql1 = "SELECT s as \"\" FROM OBJECTS (" + oql1 + ") s" + //$NON-NLS-1$//$NON-NLS-2$
+ " WHERE s in (" + oql2 + ")"; //$NON-NLS-1$//$NON-NLS-2$
return oql1;
}
/**
+ * Handle DISTINCT
+ * @param oql
+ * @return
+ */
+ private String OQLdistinct(String oql)
+ {
+ //SELECT DISTINCT OBJECTS @objectId FROM OBJECTS (a)
+ if (OQLobjectQuery(oql))
+ return "SELECT DISTINCT OBJECTS @objectId FROM OBJECTS (" + oql +")"; //$NON-NLS-1$ //$NON-NLS-2$
+ else
+ return "SELECT DISTINCT s as \"\" FROM OBJECTS (" + oql +") s"; //$NON-NLS-1$ //$NON-NLS-2$
+ }
+
+ /**
+ * Is this a query returning a list of objects
+ * or one with columns?
+ * @param oql
+ * @return
+ */
+ public boolean OQLobjectQuery(String oql)
+ {
+ oql = oql.toLowerCase(Locale.ENGLISH).replaceAll("\\s+", " "); //$NON-NLS-1$ //$NON-NLS-2$
+ return oql.startsWith("select * ") || oql.startsWith("select distinct objects ") //$NON-NLS-1$ //$NON-NLS-2$
+ || oql.startsWith("select as retained set "); //$NON-NLS-1$
+ }
+
+ /**
* Calculate the object ids
*/
public int[] getObjectIds()
@@ -756,10 +1537,10 @@ public class CompareTablesQuery implements IQuery
}
/**
- * Union of aa from bb
- *
- * @param aa
- * @param bb
+ * Union of aa and bb
+ *
+ * @param aa sorted
+ * @param bb sorted
* @return
*/
private ArrayInt unionArray(ArrayInt aa, ArrayInt bb)
@@ -783,14 +1564,19 @@ public class CompareTablesQuery implements IQuery
++j;
}
}
+ while (j < aa.size())
+ {
+ cc.add(aa.get(j));
+ ++j;
+ }
return cc;
}
/**
* Remove aa from bb
- *
- * @param aa
- * @param bb
+ *
+ * @param aa sorted
+ * @param bb sorted
* @return
*/
private ArrayInt diffArray(ArrayInt aa, ArrayInt bb)
@@ -819,9 +1605,9 @@ public class CompareTablesQuery implements IQuery
/**
* Intersection of aa and bb
- *
- * @param aa
- * @param bb
+ *
+ * @param aa sorted
+ * @param bb sorted
* @return
*/
private ArrayInt intersectionArray(ArrayInt aa, ArrayInt bb)
@@ -847,9 +1633,9 @@ public class CompareTablesQuery implements IQuery
/**
* Symmetric difference of aa and bb
- *
- * @param aa
- * @param bb
+ *
+ * @param aa sorted
+ * @param bb sorted
* @return
*/
private ArrayInt symdiffArray(ArrayInt aa, ArrayInt bb)
@@ -876,6 +1662,11 @@ public class CompareTablesQuery implements IQuery
cc.add(bb.get(i));
}
}
+ while (j < aa.size())
+ {
+ cc.add(aa.get(j));
+ ++j;
+ }
return cc;
}
@@ -944,19 +1735,24 @@ public class CompareTablesQuery implements IQuery
if (mode == Mode.DIFF_TO_PREVIOUS || mode == Mode.DIFF_RATIO_TO_PREVIOUS ||previous == -1)
previous = i;
}
+ /*
+ * No need to add derived operations as a refined table
+ * will have the extra columns already.
+ */
+ //derivedops(bb);
return bb.build();
- }
+ }
- private Object getAbsoluteValue(ComparedRow cr, int comparedColumnIdx, int tableIdx)
- {
- Object tableRow = cr.getRows()[tableIdx];
- if (tableRow == null) return null;
+ private Object getAbsoluteValue(ComparedRow cr, int comparedColumnIdx, int tableIdx)
+ {
+ Object tableRow = cr.getRows()[tableIdx];
+ if (tableRow == null) return null;
- int tableColumnIdx = displayedColumns.get(comparedColumnIdx).getColumnIndexes()[tableIdx];
- if (tableColumnIdx == -1) return null;
+ int tableColumnIdx = displayedColumns.get(comparedColumnIdx).getColumnIndexes()[tableIdx];
+ if (tableColumnIdx == -1) return null;
- return tables[tableIdx].getColumnValue(tableRow, tableColumnIdx);
- }
+ return tables[tableIdx].getColumnValue(tableRow, tableColumnIdx);
+ }
private Object percentDivide(Number d1, Number d2)
{
@@ -972,40 +1768,40 @@ public class CompareTablesQuery implements IQuery
return Double.valueOf(d1.doubleValue() / d2.doubleValue());
}
}
-
- private Object getDiffToFirst(ComparedRow cr, int comparedColumnIdx, int tableIdx, boolean ratio)
- {
- Object tableRow = cr.getRows()[tableIdx];
- if (tableRow == null) return null;
- int tableColumnIdx = displayedColumns.get(comparedColumnIdx).getColumnIndexes()[tableIdx];
- if (tableColumnIdx == -1) return null;
+ private Object getDiffToFirst(ComparedRow cr, int comparedColumnIdx, int tableIdx, boolean ratio)
+ {
+ Object tableRow = cr.getRows()[tableIdx];
+ if (tableRow == 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);
+ Object value = tables[tableIdx].getColumnValue(tableRow, tableColumnIdx);
+ Object firstTableValue = getAbsoluteValue(cr, comparedColumnIdx, 0);
- if (value == null && firstTableValue == null) return null;
+ if (value == null && firstTableValue == null) return null;
- if (value == null && (firstTableValue instanceof Number || firstTableValue instanceof Bytes)) return null;
+ if (value == null && (firstTableValue instanceof Number || firstTableValue instanceof Bytes)) return null;
- if ((value instanceof Number || value instanceof Bytes) && firstTableValue == null)
- {
+ if ((value instanceof Number || value instanceof Bytes) && firstTableValue == null)
+ {
return ratio ? null : value;
- }
+ }
- boolean returnBytes = value instanceof Bytes && firstTableValue instanceof Bytes;
- if (value instanceof Bytes)
- value = Long.valueOf(((Bytes)value).getValue());
+ 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 (firstTableValue instanceof Bytes)
+ firstTableValue = Long.valueOf(((Bytes)firstTableValue).getValue());
- if (value instanceof Number && firstTableValue instanceof Number)
- {
+ if (value instanceof Number && firstTableValue instanceof Number)
+ {
Object ret = computeDiff((Number) firstTableValue, (Number) value);
if (ratio && ret instanceof Number)
{
- return percentDivide((Number)ret, (Number)firstTableValue);
+ return percentDivide((Number)ret, (Number)firstTableValue);
}
else
{
@@ -1013,43 +1809,43 @@ public class CompareTablesQuery implements IQuery
return new Bytes(((Number)ret).longValue());
return ret;
}
- }
- return null;
- }
+ }
+ return null;
+ }
- private Object getDiffToPrevious(ComparedRow cr, int comparedColumnIdx, int tableIdx, boolean ratio)
- {
- Object tableRow = cr.getRows()[tableIdx];
- if (tableRow == null) return null;
+ private Object getDiffToPrevious(ComparedRow cr, int comparedColumnIdx, int tableIdx, boolean ratio)
+ {
+ Object tableRow = cr.getRows()[tableIdx];
+ if (tableRow == null) return null;
- int tableColumnIdx = displayedColumns.get(comparedColumnIdx).getColumnIndexes()[tableIdx];
- if (tableColumnIdx == -1) return null;
+ int tableColumnIdx = displayedColumns.get(comparedColumnIdx).getColumnIndexes()[tableIdx];
+ if (tableColumnIdx == -1) return null;
- Object value = tables[tableIdx].getColumnValue(tableRow, tableColumnIdx);
- Object previousTableValue = getAbsoluteValue(cr, comparedColumnIdx, tableIdx - 1);
+ Object value = tables[tableIdx].getColumnValue(tableRow, tableColumnIdx);
+ Object previousTableValue = getAbsoluteValue(cr, comparedColumnIdx, tableIdx - 1);
- if (value == null && previousTableValue == null) return null;
+ if (value == null && previousTableValue == null) return null;
- if (value == null && (previousTableValue instanceof Number || previousTableValue instanceof Bytes)) return null;
+ if (value == null && (previousTableValue instanceof Number || previousTableValue instanceof Bytes)) return null;
- if ((value instanceof Number || value instanceof Bytes) && previousTableValue == null)
- {
+ if ((value instanceof Number || value instanceof Bytes) && previousTableValue == null)
+ {
return ratio ? null : value;
- }
+ }
- boolean returnBytes = value instanceof Bytes && previousTableValue instanceof Bytes;
- if (value instanceof Bytes)
- value = Long.valueOf(((Bytes)value).getValue());
+ boolean returnBytes = value instanceof Bytes && previousTableValue instanceof Bytes;
+ if (value instanceof Bytes)
+ value = Long.valueOf(((Bytes)value).getValue());
- if (previousTableValue instanceof Bytes)
- previousTableValue = Long.valueOf(((Bytes)previousTableValue).getValue());
+ if (previousTableValue instanceof Bytes)
+ previousTableValue = Long.valueOf(((Bytes)previousTableValue).getValue());
- if (value instanceof Number && previousTableValue instanceof Number)
- {
+ if (value instanceof Number && previousTableValue instanceof Number)
+ {
Object ret = computeDiff((Number) previousTableValue, (Number) value);
if (ratio && ret instanceof Number)
{
- return percentDivide((Number)ret, (Number)previousTableValue);
+ return percentDivide((Number)ret, (Number)previousTableValue);
}
else
{
@@ -1057,35 +1853,35 @@ public class CompareTablesQuery implements IQuery
return new Bytes(((Number)ret).longValue());
return ret;
}
- }
- return null;
- }
+ }
+ return null;
+ }
- private Object computeDiff(Number o1, Number o2)
- {
- if (o1 instanceof Long && o2 instanceof Long) return (o2.longValue() - o1.longValue());
+ private Object computeDiff(Number o1, Number o2)
+ {
+ if (o1 instanceof Long && o2 instanceof Long) return (o2.longValue() - o1.longValue());
- if (o1 instanceof Integer && o2 instanceof Integer) return (o2.intValue() - o1.intValue());
+ if (o1 instanceof Integer && o2 instanceof Integer) return (o2.intValue() - o1.intValue());
- if (o1 instanceof Short && o2 instanceof Short) return (o2.shortValue() - o1.shortValue());
+ if (o1 instanceof Short && o2 instanceof Short) return (o2.shortValue() - o1.shortValue());
- if (o1 instanceof Byte && o2 instanceof Byte) return (o2.byteValue() - o1.byteValue());
+ if (o1 instanceof Byte && o2 instanceof Byte) return (o2.byteValue() - o1.byteValue());
- if (o1 instanceof Float && o2 instanceof Float) return (o2.floatValue() - o1.floatValue());
+ if (o1 instanceof Float && o2 instanceof Float) return (o2.floatValue() - o1.floatValue());
- if (o1 instanceof Double && o2 instanceof Double) return (o2.doubleValue() - o1.doubleValue());
+ if (o1 instanceof Double && o2 instanceof Double) return (o2.doubleValue() - o1.doubleValue());
- return null;
- }
+ return null;
+ }
- /**
- * Get the icon for the row.
- * Chose the icon from the underlying tables if they all agree,
- * others choose a special compare icon.
- */
- public URL getIcon(Object row)
- {
- URL ret = null;
+ /**
+ * Get the icon for the row.
+ * Chose the icon from the underlying tables if they all agree,
+ * others choose a special compare icon.
+ */
+ public URL getIcon(Object row)
+ {
+ URL ret = null;
final ComparedRow cr = (ComparedRow) row;
// Find the rows from the tables
boolean foundIcon = false;
@@ -1122,19 +1918,19 @@ public class CompareTablesQuery implements IQuery
}
}
}
- return ret;
- }
+ return ret;
+ }
- public Mode getMode()
- {
- return mode;
- }
+ public Mode getMode()
+ {
+ return mode;
+ }
- public void setMode(Mode mode)
- {
- this.mode = mode;
- updateColumns();
- }
+ public void setMode(Mode mode)
+ {
+ this.mode = mode;
+ updateColumns();
+ }
private void addPositiveIndicator(Format formatter)
{
@@ -1144,7 +1940,7 @@ public class CompareTablesQuery implements IQuery
if ((pctFmt.getPositivePrefix().length() == 0
|| pctFmt.getPositivePrefix().equals(pctFmt.getNegativePrefix()))
&& (pctFmt.getPositiveSuffix().length() == 0
- || pctFmt.getPositiveSuffix().equals(pctFmt.getNegativeSuffix())))
+ || pctFmt.getPositiveSuffix().equals(pctFmt.getNegativeSuffix())))
{
// No positive prefix, or positive suffix
DecimalFormatSymbols sym = DecimalFormatSymbols.getInstance();
@@ -1159,12 +1955,12 @@ public class CompareTablesQuery implements IQuery
}
}
- private void setFormatter()
- {
- int i = 1;
- // Rather than giving a format, modify the default
- Format formatter = NumberFormat.getIntegerInstance();
- addPositiveIndicator(formatter);
+ private void setFormatter()
+ {
+ int i = 1;
+ // Rather than giving a format, modify the default
+ Format formatter = NumberFormat.getIntegerInstance();
+ addPositiveIndicator(formatter);
NumberFormat formatterPercent = NumberFormat.getPercentInstance();
addPositiveIndicator(formatterPercent);
// Force the sign for Bytes formatting
@@ -1182,19 +1978,21 @@ public class CompareTablesQuery implements IQuery
}
BytesFormat bfm = new BytesFormat(formatter, bcf);
- for (ComparedColumn comparedColumn : displayedColumns)
- {
- Column c = comparedColumn.description;
- for (int j = 0; j < comparedColumn.getColumnIndexes().length; j++)
- {
+ for (ComparedColumn comparedColumn : displayedColumns)
+ {
+ Column c = comparedColumn.description;
+ for (int j = 0; j < comparedColumn.getColumnIndexes().length; j++)
+ {
if (mode != Mode.ABSOLUTE && j > 0)
{
if (!columns[i].getCalculateTotals() && c.getCalculateTotals())
{
// Set the totals mode
+ IDecorator decorator = columns[i].getDecorator();
columns[i] = new Column(columns[i].getLabel(), columns[i].getType(), columns[i].getAlign(),
columns[i].getSortDirection(), columns[i].getFormatter(),
columns[i].getComparator());
+ columns[i].decorator(decorator);
}
if (c.getFormatter() instanceof DecimalFormat)
{
@@ -1218,68 +2016,115 @@ public class CompareTablesQuery implements IQuery
if (!columns[i].getCalculateTotals() && c.getCalculateTotals())
{
// Set the totals mode
+ IDecorator decorator = columns[i].getDecorator();
columns[i] = new Column(columns[i].getLabel(), columns[i].getType(), columns[i].getAlign(),
columns[i].getSortDirection(), columns[i].getFormatter(),
columns[i].getComparator());
+ columns[i].decorator(decorator);
}
columns[i].formatting(c.getFormatter());
}
- i++;
- if ((mode == Mode.DIFF_RATIO_TO_FIRST || mode == Mode.DIFF_RATIO_TO_PREVIOUS) && j > 0)
+ i++;
+ if ((mode == Mode.DIFF_RATIO_TO_FIRST || mode == Mode.DIFF_RATIO_TO_PREVIOUS) && j > 0)
{
columns[i].formatting(formatterPercent);
columns[i].noTotals();
i++;
}
- }
- }
- }
-
- public void updateColumns()
- {
- List<Column> result = new ArrayList<Column>();
- result.add(new Column(key.getLabel(), key.getType(), key.getAlign(), null, key.getFormatter(), null));
-
- displayedColumns = new ArrayList<ComparedColumn>();
-
- for (ComparedColumn comparedColumn : comparedColumns)
- {
- Column c = comparedColumn.description;
- if (comparedColumn.isDisplayed())
- {
- displayedColumns.add(comparedColumn);
- for (int j = 0; j < comparedColumn.getColumnIndexes().length; j++)
- {
+ }
+ }
+ }
+
+ class KeyDecorator implements IDecorator
+ {
+
+ public String prefix(Object row)
+ {
+ final ComparedRow cr = (ComparedRow) row;
+ return cr.prefix;
+ }
+
+ public String suffix(Object row)
+ {
+ final ComparedRow cr = (ComparedRow) row;
+ return cr.suffix;
+ }
+ }
+
+ class Decorator implements IDecorator
+ {
+ int table;
+ IDecorator dec;
+ Decorator(IDecorator dec, int table)
+ {
+ this.table = table;
+ this.dec = dec;
+ }
+
+ public String prefix(Object row)
+ {
+ final ComparedRow cr = (ComparedRow) row;
+ Object r = cr.getRows()[table];
+ return r != null ? dec.prefix(r) : null;
+ }
+
+ public String suffix(Object row)
+ {
+ final ComparedRow cr = (ComparedRow) row;
+ Object r = cr.getRows()[table];
+ return r != null ? dec.suffix(r) : null;
+ }
+ }
+
+ public void updateColumns()
+ {
+ List<Column> result = new ArrayList<Column>();
+ result.add(new Column(key.getLabel(), key.getType(), key.getAlign(), null, key.getFormatter(), null));
+ result.get(result.size() - 1).decorator(new KeyDecorator());
+
+ displayedColumns = new ArrayList<ComparedColumn>();
+
+ for (ComparedColumn comparedColumn : comparedColumns)
+ {
+ Column c = comparedColumn.description;
+ if (comparedColumn.isDisplayed())
+ {
+ displayedColumns.add(comparedColumn);
+ for (int j = 0; j < comparedColumn.getColumnIndexes().length; j++)
+ {
String label;
final int prev = mode == Mode.DIFF_TO_PREVIOUS || mode == Mode.DIFF_RATIO_TO_PREVIOUS ? j - 1 : 0;
if (j == 0 || mode == Mode.ABSOLUTE)
{
- label = MessageUtil.format(Messages.CompareTablesQuery_ColumnAbsolute, c.getLabel(), j);
+ label = MessageUtil.format(Messages.CompareTablesQuery_ColumnAbsolute, c.getLabel(), j + 1);
}
else
{
label = MessageUtil.format(Messages.CompareTablesQuery_ColumnDifference,
- c.getLabel(), j,
- prev);
+ c.getLabel(), j + 1,
+ prev + 1);
}
result.add(new Column(label, c.getType(), c.getAlign(), c.getSortDirection(), c.getFormatter(),
null));
+ // Pass through the decorator
+ if (c.getDecorator() != null)
+ result.get(result.size() - 1).decorator(new Decorator(c.getDecorator(), j));
// For percentage modes also add a percent change column for subsequent tables
if (j > 0 && (mode == Mode.DIFF_RATIO_TO_FIRST || mode == Mode.DIFF_RATIO_TO_PREVIOUS))
{
label = MessageUtil.format(Messages.CompareTablesQuery_ColumnPercentDifference,
- c.getLabel(), j,
- prev);
+ c.getLabel(), j + 1,
+ prev + 1);
result.add(new Column(label, c.getType(), c.getAlign(), c.getSortDirection(), c
.getFormatter(), null));
}
- }
- }
- }
+ }
+ }
+ }
- columns = result.toArray(new Column[result.size()]);
- setFormatter();
- }
+ columns = result.toArray(new Column[result.size()]);
+ setFormatter();
+ }
IContextObject getContextFromTable(int i, Object row)
{
@@ -1294,5 +2139,91 @@ public class CompareTablesQuery implements IQuery
}
return ret;
}
+
+ void derivedops(ResultMetaData.Builder answer)
+ {
+ int found = 0;
+ Set<DerivedOperation>allops = new HashSet<DerivedOperation>();
+ for (int i = 0; i < tables.length; ++i)
+ {
+ IStructuredResult rr = tables[i];
+ ResultMetaData data = rr.getResultMetaData();
+ if (data != null)
+ {
+ ++found;
+ Collection<DerivedOperation>ops = data.getDerivedOperations();
+ if (ops != null)
+ {
+ for (DerivedOperation op : data.getDerivedOperations())
+ {
+ allops.add(op);
+ }
+ }
+ }
+ }
+ if (found == 0)
+ return;
+ if (allops.size() == 0)
+ return;
+
+ for (ContextDerivedData.DerivedOperation operation : allops)
+ answer.addDerivedData(operation);
+
+ return;
+ }
+
+ }
+
+ public class ComparisonResultTable extends TableComparisonResult implements IResultTable
+ {
+ public ComparisonResultTable(List<ComparedRow> rows, Column key, List<ComparedColumn> comparedColumns,
+ Mode mode, Operation setOp)
+ {
+ super(rows, key, comparedColumns, mode, setOp);
+ }
+
+ public Object getRow(int rowId)
+ {
+ return rows.get(rowId);
+ }
+
+ public int getRowCount()
+ {
+ return rows.size();
+ }
+ }
+
+ public class ComparisonResultTree extends TableComparisonResult implements IResultTree
+ {
+ public ComparisonResultTree(List<ComparedRow> rows, Column key, List<ComparedColumn> comparedColumns,
+ Mode mode, Operation setOp)
+ {
+ super(rows, key, comparedColumns, mode, setOp);
+ }
+
+ public List<?> getElements()
+ {
+ return rows;
+ }
+
+ public boolean hasChildren(Object element)
+ {
+ for (int i = 0; i < tables.length; i++)
+ {
+ IStructuredResult table = tables[i];
+ if (table instanceof IResultTree)
+ {
+ Object treerow = ((ComparedRow)element).getRows()[i];
+ if (treerow != null && ((IResultTree)table).hasChildren(treerow))
+ return true;
+ }
+ }
+ return false;
+ }
+
+ public List<?> getChildren(Object parent)
+ {
+ return mergeKeys((ComparedRow)parent, new VoidProgressListener());
+ }
}
}
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 22e1d163..7bd16b6e 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
@@ -62,17 +62,18 @@ merged paths from garbage collection roots to the objects\n\
merged paths from garbage collection roots to the objects grouped by class, so all objects of the same class are shown as one\n\
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
-CompareTablesQuery.menu.0.label = Compare Tables with all set operations
-CompareTablesQuery.help = Compares two or more tables. If the tables are from the same snapshot \
-as the current snapshot then the context menu shows objects from those tables. It also can perform \
+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.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\
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 to be compared
+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
CompareTablesQuery.mode.help = Whether to show absolute values in columns, or the difference from the base table, \
@@ -81,3 +82,19 @@ CompareTablesQuery.setOp.help = Whether to just show the raw tables, or only a p
and the set operations as context menus.
CompareTablesQuery.snapshots.help = The snapshots corresponding to the tables. Optional if all the tables are from the current snapshot.
CompareTablesQuery.keyColumn.help = The number of the column to be matched between the tables (default of column 1).
+CompareTablesQuery.mask.help = Regular expression to mask part of the key, for example addresses \
+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.
+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 \
+matched. The extra key values are specified as follows:\n\
+\textra ::= <className> [: <field> [, <field> ]]\n\
+Example:\n\
+\tjava.lang.Module:name\n\
+If the object is an instance of that type then the values of those fields are resolved and added to the key. \
+If the fields are omitted then the class specific name of the object itself is resolved.
+CompareTablesQuery.extraReferencesListFile.help = Read extra key values from the file. Each line must have the same format \
+as for the -x argument.
diff --git a/plugins/org.eclipse.mat.tests/src/org/eclipse/mat/tests/snapshot/OQLTest.java b/plugins/org.eclipse.mat.tests/src/org/eclipse/mat/tests/snapshot/OQLTest.java
index 4e3f8820..0ef72aa1 100644
--- a/plugins/org.eclipse.mat.tests/src/org/eclipse/mat/tests/snapshot/OQLTest.java
+++ b/plugins/org.eclipse.mat.tests/src/org/eclipse/mat/tests/snapshot/OQLTest.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
@@ -1583,9 +1583,9 @@ public class OQLTest
* @param dump the snapshot to test against
* @throws SnapshotException
*/
- void checkGetOQL(IResultTable rt, String dump) throws SnapshotException
+ static void checkGetOQL(IResultTable rt, String dump) throws SnapshotException
{
- // Check the default contextx
+ // Check the default context
for (int i = 0; i < rt.getRowCount(); ++i)
{
IContextObject c = rt.getContext(rt.getRow(i));
@@ -1725,7 +1725,7 @@ public class OQLTest
}
}
- private void checkDefaultContext(IResultTable rt, int row, IContextObject c, String dump) throws SnapshotException
+ private static void checkDefaultContext(IResultTable rt, int row, IContextObject c, String dump) throws SnapshotException
{
if (c instanceof IContextObjectSet)
{
@@ -1735,6 +1735,7 @@ public class OQLTest
else {
int os[] = cs.getObjectIds();
String oql = cs.getOQL();
+ assertNotNull(rt+" row="+row+" dump="+dump, oql);
Object res = execute(oql, dump);
if (res instanceof int[])
{
@@ -1774,7 +1775,7 @@ public class OQLTest
}
}
- private void checkSingleObjectContext(int ioid, IContextObject c, String dump) throws SnapshotException
+ private static void checkSingleObjectContext(int ioid, IContextObject c, String dump) throws SnapshotException
{
assertThat(c, instanceOf(IContextObjectSet.class));
IContextObjectSet cs = (IContextObjectSet)c;
@@ -1801,7 +1802,7 @@ public class OQLTest
}
}
- private void checkSingleObjectContext(IResultTable rt, int row, int ioid, IContextObject c, String dump) throws SnapshotException
+ private static void checkSingleObjectContext(IResultTable rt, int row, int ioid, IContextObject c, String dump) throws SnapshotException
{
assertThat(c, instanceOf(IContextObjectSet.class));
IContextObjectSet cs = (IContextObjectSet)c;
@@ -2062,7 +2063,7 @@ public class OQLTest
return execute(oql, TestSnapshots.SUN_JDK5_64BIT);
}
- private Object execute(String oql, String snapshotName) throws SnapshotException
+ private static Object execute(String oql, String snapshotName) throws SnapshotException
{
try
{
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 24f334d1..3a235b35 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
@@ -1,5 +1,5 @@
/*******************************************************************************
- * Copyright (c) 2010, 2018 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
@@ -11,29 +11,42 @@
*******************************************************************************/
package org.eclipse.mat.tests.snapshot;
+import static org.hamcrest.core.IsEqual.equalTo;
+import static org.hamcrest.number.OrderingComparison.greaterThan;
+import static org.hamcrest.number.OrderingComparison.greaterThanOrEqualTo;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertTrue;
import static org.junit.Assert.assertThat;
-import static org.hamcrest.number.OrderingComparison.greaterThan;
+import static org.junit.Assert.assertTrue;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
+import java.util.HashMap;
import java.util.List;
+import java.util.Map;
import org.eclipse.mat.SnapshotException;
+import org.eclipse.mat.query.ContextProvider;
+import org.eclipse.mat.query.IContextObject;
+import org.eclipse.mat.query.IContextObjectSet;
import org.eclipse.mat.query.IResult;
import org.eclipse.mat.query.IResultTable;
+import org.eclipse.mat.query.IResultTree;
+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.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.SnapshotQuery;
import org.eclipse.mat.tests.TestSnapshots;
import org.eclipse.mat.util.VoidProgressListener;
import org.junit.Test;
+@SuppressWarnings("nls")
public class QueryLookupTest
{
@Test
@@ -136,7 +149,7 @@ public class QueryLookupTest
query3.setArgument("snapshots", snapshots);
IResultTable r2 = (IResultTable) query3.execute(new VoidProgressListener());
assertTrue(r2 != null);
- System.out.println(Arrays.toString(r2.getColumns()));
+ //System.out.println(Arrays.toString(r2.getColumns()));
int count = 0;
for (int i = 0; i < r2.getRowCount(); ++i)
{
@@ -185,7 +198,7 @@ public class QueryLookupTest
query3.setArgument("snapshots", snapshots);
IResultTable r2 = (IResultTable) query3.execute(new VoidProgressListener());
assertTrue(r2 != null);
- System.out.println(Arrays.toString(r2.getColumns()));
+ //System.out.println(Arrays.toString(r2.getColumns()));
int count1 = 0;
int count2 = 0;
for (int i = 0; i < r2.getRowCount(); ++i)
@@ -209,7 +222,507 @@ public class QueryLookupTest
assertThat(count1, greaterThan(0));
assertThat(count2, greaterThan(0));
}
-
+
+ /**
+ * Test that duplicate records are recorded.
+ */
+ @Test
+ public void testCompareDuplicateRecords1() throws SnapshotException
+ {
+ String queryId = "comparetablesquery -setop ALL";
+ ISnapshot snapshot1 = TestSnapshots.getSnapshot(TestSnapshots.OPENJDK_JDK11_04_64BIT, false);
+
+ SnapshotQuery query1 = SnapshotQuery.lookup("histogram", snapshot1);
+ IResultTable result1 = (IResultTable)query1.execute(new VoidProgressListener());
+
+ SnapshotQuery query3 = SnapshotQuery.parse(queryId, snapshot1);
+
+ List<IResultTable> r = new ArrayList<IResultTable>();
+ r.add((IResultTable) result1);
+ query3.setArgument("tables", r);
+ ArrayList<ISnapshot> snapshots = new ArrayList<ISnapshot>();
+ snapshots.add(snapshot1);
+ query3.setArgument("snapshots", snapshots);
+ IResultTable r2 = (IResultTable) query3.execute(new VoidProgressListener());
+ assertTrue(r2 != null);
+
+ assertThat(r2.getRowCount(), equalTo(result1.getRowCount()));
+ OQLTest.checkGetOQL(r2, TestSnapshots.OPENJDK_JDK11_04_64BIT);
+ }
+
+ /**
+ * Test that duplicate rows are recorded for two dumps.
+ */
+ @Test
+ public void testCompareDuplicateRecords2() throws SnapshotException
+ {
+ String queryId = "comparetablesquery -setop ALL";
+ 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.lookup("histogram", snapshot1);
+ IResultTable result1 = (IResultTable)query1.execute(new VoidProgressListener());
+
+ SnapshotQuery query2 = SnapshotQuery.lookup("histogram", snapshot2);
+ IResultTable result2 = (IResultTable)query2.execute(new VoidProgressListener());
+
+ SnapshotQuery query3 = SnapshotQuery.parse(queryId, snapshot1);
+
+
+ List<IResultTable> r = new ArrayList<IResultTable>();
+ r.add((IResultTable) result1);
+ r.add((IResultTable) result2);
+ query3.setArgument("tables", r);
+ ArrayList<ISnapshot> snapshots = new ArrayList<ISnapshot>();
+ snapshots.add(snapshot1);
+ snapshots.add(snapshot2);
+ query3.setArgument("snapshots", snapshots);
+ IResultTable r2 = (IResultTable) query3.execute(new VoidProgressListener());
+ assertTrue(r2 != null);
+
+ checkTable(result1, r2, true);
+ checkTable(result2, r2, false);
+ OQLTest.checkGetOQL(r2, TestSnapshots.OPENJDK_JDK11_04_64BIT);
+ }
+
+ /**
+ * Test that duplicate rows are recorded for two dumps.
+ */
+ @Test
+ public void testCompareDuplicateRecords3() throws SnapshotException
+ {
+ String queryId = "comparetablesquery -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";
+ 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("merge_shortest_paths .* -excludes java.lang.ref.WeakReference:referent java.lang.ref.SoftReference:referent", snapshot1);
+ IResultTree result1 = (IResultTree)query1.execute(new VoidProgressListener());
+
+ SnapshotQuery query2 = SnapshotQuery.parse("merge_shortest_paths .* -excludes java.lang.ref.WeakReference:referent java.lang.ref.SoftReference:referent", snapshot2);
+ IResultTree result2 = (IResultTree)query2.execute(new VoidProgressListener());
+
+ SnapshotQuery query3 = SnapshotQuery.parse(queryId, snapshot1);
+
+ List<IStructuredResult> r = new ArrayList<IStructuredResult>();
+ r.add(result1);
+ r.add(result2);
+ query3.setArgument("tables", r);
+ ArrayList<ISnapshot> snapshots = new ArrayList<ISnapshot>();
+ snapshots.add(snapshot1);
+ snapshots.add(snapshot2);
+ query3.setArgument("snapshots", snapshots);
+ IResultTree r2 = (IResultTree) query3.execute(new VoidProgressListener());
+ assertTrue(r2 != null);
+
+ //checkTable(result1, r2, true);
+ //checkTable(result2, r2, false);
+ checkOQL(snapshot1, r2);
+ }
+
+ /**
+ * Test that OQL works for compared table
+ */
+ @Test
+ public void testCompareOQL() throws SnapshotException
+ {
+ String queryId = "comparetablesquery -setop ALL";
+ ISnapshot snapshot1 = TestSnapshots.getSnapshot(TestSnapshots.OPENJDK_JDK11_04_64BIT, false);
+
+ SnapshotQuery query1 = SnapshotQuery.lookup("oql", snapshot1);
+ query1.setArgument("queryString", "SELECT c.@name, c.@numberOfObjects AS Objects, c.@objectId FROM java.lang.Class c WHERE (${snapshot}.isClass(c.@objectId) and ((eval((c.@objectId / 4)).intValue() * 4) != c.@objectId))");
+ IResultTable result1 = (IResultTable)query1.execute(new VoidProgressListener());
+
+ SnapshotQuery query2 = SnapshotQuery.lookup("oql", snapshot1);
+ query2.setArgument("queryString", "SELECT c.@name, c.@numberOfObjects AS Objects, c.@objectId FROM OBJECTS INSTANCEOF java.lang.Object c WHERE (${snapshot}.isClass(c.@objectId) and ((eval(((c.@objectId) / 5)).intValue() * 5) != c.@objectId))");
+ IResultTable result2 = (IResultTable)query2.execute(new VoidProgressListener());
+
+ SnapshotQuery query3 = SnapshotQuery.lookup("oql", snapshot1);
+ query3.setArgument("queryString", "SELECT c.@name, c.@numberOfObjects AS Objects, c.@objectId FROM OBJECTS INSTANCEOF java.lang.Object c WHERE (${snapshot}.isClass(c.@objectId) and ((eval((c.@objectId / 3)).intValue() * 3) != c.@objectId))");
+ IResultTable result3 = (IResultTable)query3.execute(new VoidProgressListener());
+
+ SnapshotQuery queryc = SnapshotQuery.parse(queryId, 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(snapshot1);
+ snapshots.add(snapshot1);
+ queryc.setArgument("snapshots", snapshots);
+ IResultTable r2 = (IResultTable) queryc.execute(new VoidProgressListener());
+ assertTrue(r2 != null);
+
+ // Check any OQL from IContextObjectSet is valid
+ //OQLTest.checkGetOQL(r2, TestSnapshots.OPENJDK_JDK11_04_64BIT);
+ checkTable(result1, r2, true);
+ checkTable(result2, r2, false);
+ checkTable(result3, r2, false);
+ checkOQL(snapshot1, r2);
+ }
+
+ private void checkOQL(ISnapshot snapshot1, IStructuredResult r2) throws OQLParseException, SnapshotException
+ {
+ int rc;
+ if (r2 instanceof IResultTable)
+ rc = ((IResultTable)r2).getRowCount();
+ else if (r2 instanceof IResultTree)
+ rc = ((IResultTree)r2).getElements().size();
+ else
+ rc = 0;
+ for (int i = 0; i < rc; ++i)
+ {
+ processRow(snapshot1, r2, i, null, 3, 100, 0.1);
+ }
+ }
+
+ void processRow(ISnapshot snapshot1, IStructuredResult r2, int i, Object rn, int depth, int max, double decay) throws SnapshotException
+ {
+ Object row;
+ if (r2 instanceof IResultTable)
+ row = ((IResultTable)r2).getRow(i);
+ else if (r2 instanceof IResultTree)
+ if (rn == null)
+ row = ((IResultTree)r2).getElements().get(i);
+ else
+ row = ((IResultTree)r2).getChildren(rn).get(i);
+ else
+ return;
+ IContextObject context = r2.getContext(row);
+ if ((context instanceof IContextObjectSet))
+ {
+
+ IContextObjectSet ic = (IContextObjectSet)context;
+ String oql = ic.getOQL();
+ IOQLQuery query = SnapshotFactory.createQuery(oql);
+ //System.out.println(oql);
+ Object ret = query.execute(snapshot1, new VoidProgressListener());
+ assertNotNull(oql, ret);
+ }
+ 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 (context2 instanceof IContextObjectSet)
+ {
+ IContextObjectSet ic = (IContextObjectSet)context2;
+ int os[] = ic.getObjectIds();
+ String oql = ic.getOQL();
+ IOQLQuery query = SnapshotFactory.createQuery(oql);
+ if (cp.getLabel().length() >= 0)
+ {
+ //System.out.println(cp.getLabel()+" "+oql);
+ Object ret = query.execute(snapshot1, new VoidProgressListener());
+ String message = cp.getLabel()+" "+row+" "+oql;
+ if (os.length > 0)
+ {
+ // Results expected
+ assertNotNull(message, ret);
+ if (ret instanceof int[])
+ assertThat(message, ((int[])ret).length, equalTo(os.length));
+ else
+ assertThat(message, ((IResultTable)ret).getRowCount(), equalTo(os.length));
+ }
+ else
+ {
+ // No results expected
+ if (ret instanceof int[])
+ assertThat(message, ((int[])ret).length, equalTo(os.length));
+ else if (ret != null)
+ assertThat(message, ((IResultTable)ret).getRowCount(), equalTo(os.length));
+ }
+ }
+ }
+ }
+ if (depth > 1 && r2 instanceof IResultTree)
+ {
+ IResultTree tree = (IResultTree)r2;
+ if (tree.hasChildren(row))
+ {
+ int done = 0;
+ List<?> children = tree.getChildren(row);
+ for (int j = 0; j < children.size(); ++j)
+ {
+ if ((long)done * children.size() < (long)j * max)
+ {
+ processRow(snapshot1, r2, j, row, depth - 1, (int)(max*decay), decay);
+ ++done;
+ }
+ }
+ }
+ }
+ }
+
+ /**
+ * Test that set operations intersection is done.
+ */
+ @Test
+ public void testCompareSetOperationsIntersection() throws SnapshotException
+ {
+ ISnapshot snapshot1 = TestSnapshots.getSnapshot(TestSnapshots.OPENJDK_JDK11_04_64BIT, false);
+ int s[] = snapshot1.getClassesByName("java.lang.String", false).iterator().next().getObjectIds();
+ int o1[] = Arrays.copyOfRange(s, 0, 60);
+ int o2[] = Arrays.copyOfRange(s, 20, 80);
+ int o3[] = Arrays.copyOfRange(s, 10, 50);
+ int e1[] = Arrays.copyOfRange(s, 20, 60);
+ int e2[] = Arrays.copyOfRange(s, 20, 50);
+ testCompareSetOperations(snapshot1, "INTERSECTION" , o1, o2, o3, 0, e1, 1, e2);
+ }
+
+ /**
+ * Test that set operations union is done.
+ */
+ @Test
+ public void testCompareSetOperationsUnion() throws SnapshotException
+ {
+ ISnapshot snapshot1 = TestSnapshots.getSnapshot(TestSnapshots.OPENJDK_JDK11_04_64BIT, false);
+ int s[] = snapshot1.getClassesByName("java.lang.String", false).iterator().next().getObjectIds();
+ int o1[] = Arrays.copyOfRange(s, 5, 60);
+ int o2[] = Arrays.copyOfRange(s, 20, 80);
+ int o3[] = Arrays.copyOfRange(s, 10, 85);
+ int e1[] = Arrays.copyOfRange(s, 5, 80);
+ int e2[] = Arrays.copyOfRange(s, 5, 85);
+ testCompareSetOperations(snapshot1, "UNION" , o1, o2, o3, 0, e1, 1, e2);
+ }
+
+ /**
+ * Test that set operations symmetric difference is done.
+ */
+ @Test
+ public void testCompareSetOperationsSymDiff() throws SnapshotException
+ {
+ ISnapshot snapshot1 = TestSnapshots.getSnapshot(TestSnapshots.OPENJDK_JDK11_04_64BIT, false);
+ int s[] = snapshot1.getClassesByName("java.lang.String", false).iterator().next().getObjectIds();
+ int o1[] = Arrays.copyOfRange(s, 5, 50);
+ int o2[] = Arrays.copyOfRange(s, 20, 50);
+ int o3[] = Arrays.copyOfRange(s, 20, 50);
+ int e1[] = Arrays.copyOfRange(s, 5, 20);
+ int e2[] = Arrays.copyOfRange(s, 5, 50);
+ testCompareSetOperations(snapshot1, "SYMMETRIC_DIFFERENCE" , o1, o2, o3, 0, e1, 1, e2);
+ }
+
+ /**
+ * Test that set operations difference is done.
+ */
+ @Test
+ public void testCompareSetOperationsDiff1() throws SnapshotException
+ {
+ ISnapshot snapshot1 = TestSnapshots.getSnapshot(TestSnapshots.OPENJDK_JDK11_04_64BIT, false);
+ int s[] = snapshot1.getClassesByName("java.lang.String", false).iterator().next().getObjectIds();
+ int o1[] = Arrays.copyOfRange(s, 5, 50);
+ int o2[] = Arrays.copyOfRange(s, 20, 50);
+ int o3[] = Arrays.copyOfRange(s, 10, 60);
+ int e1[] = Arrays.copyOfRange(s, 5, 20);
+ int e2[] = Arrays.copyOfRange(s, 5, 10);
+ testCompareSetOperations(snapshot1, "DIFFERENCE" , o1, o2, o3, 0, e1, 1, e2);
+ }
+
+ /**
+ * Test that set operations difference is done.
+ */
+ @Test
+ public void testCompareSetOperationsDiff2() throws SnapshotException
+ {
+ ISnapshot snapshot1 = TestSnapshots.getSnapshot(TestSnapshots.OPENJDK_JDK11_04_64BIT, false);
+ int s[] = snapshot1.getClassesByName("java.lang.String", false).iterator().next().getObjectIds();
+ int o1[] = Arrays.copyOfRange(s, 5, 50);
+ int o2[] = Arrays.copyOfRange(s, 20, 50);
+ int o3[] = Arrays.copyOfRange(s, 5, 30);
+ int e1[] = Arrays.copyOfRange(s, 5, 20);
+ int e2[] = Arrays.copyOfRange(s, 30, 50);
+ testCompareSetOperations(snapshot1, "DIFFERENCE -mode DIFF_TO_FIRST" , o1, o2, o3, 0, e1, 1, e2);
+ }
+
+ /**
+ * Test that set operations difference is done.
+ */
+ @Test
+ public void testCompareSetOperationsDiff3() throws SnapshotException
+ {
+ ISnapshot snapshot1 = TestSnapshots.getSnapshot(TestSnapshots.OPENJDK_JDK11_04_64BIT, false);
+ int s[] = snapshot1.getClassesByName("java.lang.String", false).iterator().next().getObjectIds();
+ int o1[] = Arrays.copyOfRange(s, 5, 50);
+ int o2[] = Arrays.copyOfRange(s, 20, 50);
+ int o3[] = Arrays.copyOfRange(s, 5, 30);
+ int e1[] = Arrays.copyOfRange(s, 5, 20);
+ int e2[] = Arrays.copyOfRange(s, 30, 50);
+ testCompareSetOperations(snapshot1, "DIFFERENCE -mode DIFF_TO_PREVIOUS" , o1, o2, o3, 0, e1, 1, e2);
+ }
+
+ /**
+ * Test that set operations difference is done.
+ * Reverse difference
+ */
+ @Test
+ public void testCompareSetOperationsDiff4() throws SnapshotException
+ {
+ ISnapshot snapshot1 = TestSnapshots.getSnapshot(TestSnapshots.OPENJDK_JDK11_04_64BIT, false);
+ int s[] = snapshot1.getClassesByName("java.lang.String", false).iterator().next().getObjectIds();
+ int o1[] = Arrays.copyOfRange(s, 5, 30);
+ int o2[] = Arrays.copyOfRange(s, 20, 40);
+ int o3[] = Arrays.copyOfRange(s, 10, 60);
+ int e1[] = Arrays.copyOfRange(s, 30, 40);
+ int e2[] = Arrays.copyOfRange(s, 40, 60);
+ testCompareSetOperations(snapshot1, "ALL" , o1, o2, o3, 5, e1, 11, e2);
+ }
+
+ /**
+ * Test that set operations difference is done.
+ * Reverse difference
+ */
+ @Test
+ public void testCompareSetOperationsDiff5() throws SnapshotException
+ {
+ ISnapshot snapshot1 = TestSnapshots.getSnapshot(TestSnapshots.OPENJDK_JDK11_04_64BIT, false);
+ int s[] = snapshot1.getClassesByName("java.lang.String", false).iterator().next().getObjectIds();
+ int o1[] = Arrays.copyOfRange(s, 5, 30);
+ int o2[] = Arrays.copyOfRange(s, 20, 50);
+ int o3[] = Arrays.copyOfRange(s, 10, 40);
+ int e1[] = Arrays.copyOfRange(s, 30, 50);
+ int e2[] = Arrays.copyOfRange(s, 30, 40);
+ testCompareSetOperations(snapshot1, "ALL -mode DIFF_TO_FIRST" , o1, o2, o3, 5, e1, 11, e2);
+ }
+
+ /**
+ * Test that set operations difference is done.
+ */
+ @Test
+ public void testCompareSetOperationsDiff6() throws SnapshotException
+ {
+ ISnapshot snapshot1 = TestSnapshots.getSnapshot(TestSnapshots.OPENJDK_JDK11_04_64BIT, false);
+ int s[] = snapshot1.getClassesByName("java.lang.String", false).iterator().next().getObjectIds();
+ int o1[] = Arrays.copyOfRange(s, 5, 30);
+ int o2[] = Arrays.copyOfRange(s, 20, 50);
+ int o3[] = Arrays.copyOfRange(s, 5, 30);
+ int e1[] = Arrays.copyOfRange(s, 30, 50);
+ int e2[] = Arrays.copyOfRange(s, 5, 20);
+ testCompareSetOperations(snapshot1, "ALL -mode DIFF_TO_PREVIOUS" , o1, o2, o3, 5, e1, 11, e2);
+ }
+
+ /**
+ * Test that set operations are done.
+ */
+ public void testCompareSetOperations(ISnapshot snapshot1, String setOp, int o1[], int o2[], int o3[], int ei1, int e1[], int ei2, int e2[]) throws SnapshotException
+ {
+ String queryId = "comparetablesquery";
+
+ SnapshotQuery query1 = SnapshotQuery.lookup("histogram", snapshot1);
+ query1.setArgument("objects", o1);
+ IResultTable result1 = (IResultTable)query1.execute(new VoidProgressListener());
+
+ SnapshotQuery query2 = SnapshotQuery.lookup("histogram", snapshot1);
+ query2.setArgument("objects", o2);
+ IResultTable result2 = (IResultTable)query2.execute(new VoidProgressListener());
+
+ SnapshotQuery query3 = SnapshotQuery.lookup("histogram", snapshot1);
+ query3.setArgument("objects", o3);
+ IResultTable result3 = (IResultTable)query3.execute(new VoidProgressListener());
+
+ SnapshotQuery query4 = SnapshotQuery.parse(queryId + " -setop " + setOp, snapshot1);
+ List<IResultTable> r = new ArrayList<IResultTable>();
+ r.add((IResultTable) result1);
+ r.add((IResultTable) result2);
+ r.add((IResultTable) result3);
+ query4.setArgument("tables", r);
+ ArrayList<ISnapshot> snapshots = new ArrayList<ISnapshot>();
+ snapshots.add(snapshot1);
+ snapshots.add(snapshot1);
+ snapshots.add(snapshot1);
+ query4.setArgument("snapshots", snapshots);
+ IResultTable r2 = (IResultTable) query4.execute(new VoidProgressListener());
+ assertTrue(r2 != null);
+
+ /*
+ * 0: table 1
+ * 1: table 1 Op table 2
+ * 2: table 2
+ * 3: table 1 Op table 2, table 3
+ * 4: table 3
+ */
+ //System.out.println(r2.getResultMetaData().getContextProviders());
+ for (ContextProvider cp : r2.getResultMetaData().getContextProviders())
+ {
+ //System.out.println(cp+ " " + cp.getLabel());
+ }
+
+ ContextProvider cp1 = r2.getResultMetaData().getContextProviders().get(ei1);
+ //System.out.println(cp1+ " " + cp1.getLabel());
+ int c1[] = ((IContextObjectSet)cp1.getContext(r2.getRow(0))).getObjectIds();
+ int c1s[] = c1.clone();
+ Arrays.sort(c1s);
+ int e1s[] = e1.clone();
+ Arrays.sort(e1s);
+ assertThat(c1s, equalTo(e1s));
+
+ ContextProvider cp3 = r2.getResultMetaData().getContextProviders().get(ei2);
+ //System.out.println(cp3+ " " + cp3.getLabel());
+ int c3[] = ((IContextObjectSet)cp3.getContext(r2.getRow(0))).getObjectIds();
+ int c3s[] = c3.clone();
+ Arrays.sort(c3s);
+ int e2s[] = e2.clone();
+ Arrays.sort(e2s);
+ assertThat(c3s, equalTo(e2s));
+
+ // Histogram doesn't have OQL for set of objects
+ //OQLTest.checkGetOQL(r2, TestSnapshots.OPENJDK_JDK11_04_64BIT);
+ }
+
+ private void checkTable(IResultTable result1, IResultTable r2, boolean check2)
+ {
+ assertThat(r2.getRowCount(), greaterThanOrEqualTo(result1.getRowCount()));
+ HashMap<Object,List<Integer>> cnt1 = countKeys(result1);
+ HashMap<Object,List<Integer>> cnt2 = countKeys(r2);
+ for (Map.Entry<Object, List<Integer>> e : cnt1.entrySet())
+ {
+ assertThat(cnt2.get(e.getKey()).size(), greaterThanOrEqualTo(e.getValue().size()));
+ }
+
+ if (check2)
+ {
+ for (int i = 0; i < result1.getRowCount(); ++i)
+ {
+ boolean found = false;
+ Object row1 = result1.getRow(i);
+ IContextObject c1 = result1.getContext(row1);
+ Object keyval = result1.getColumnValue(row1, 0);
+ for (int j : cnt2.get(keyval))
+ {
+ Object row2 = r2.getRow(j);
+ if (keyval.equals(r2.getColumnValue(row2, 0)))
+ {
+ IContextObject c2 = r2.getContext(row2);
+ if (c2 != null && c1.getObjectId() == c2.getObjectId())
+ {
+ found = true;
+ break;
+ }
+ }
+ }
+ // Either check exact match if context found
+ // or at least as many as in the original table
+ assertTrue("Row "+i+" "+keyval+" objId "+c1.getObjectId(), found);
+ }
+ }
+ }
+
+ private HashMap<Object,List<Integer>> countKeys(IResultTable r2)
+ {
+ HashMap<Object,List<Integer>>hm = new HashMap<Object,List<Integer>>();
+ for (int j = 0; j < r2.getRowCount(); ++j)
+ {
+ Object row2 = r2.getRow(j);
+ Object key = r2.getColumnValue(row2, 0);
+ if (!hm.containsKey(key))
+ hm.put(key, new ArrayList<Integer>());
+ List<Integer>l = hm.get(key);
+ l.add(j);
+ }
+ return hm;
+ }
+
@Test()
public void testParse() throws SnapshotException
{
diff --git a/plugins/org.eclipse.mat.ui/plugin.xml b/plugins/org.eclipse.mat.ui/plugin.xml
index 1f4e8f4d..cfad8470 100644
--- a/plugins/org.eclipse.mat.ui/plugin.xml
+++ b/plugins/org.eclipse.mat.ui/plugin.xml
@@ -110,6 +110,8 @@
</pane>
<pane id="CompareTablesPane" class="org.eclipse.mat.ui.snapshot.panes.CompareTablesPane">
<result type="org.eclipse.mat.internal.snapshot.inspections.CompareTablesQuery$TableComparisonResult"/>
+ <result type="org.eclipse.mat.internal.snapshot.inspections.CompareTablesQuery$ComparisonResultTable"/>
+ <result type="org.eclipse.mat.internal.snapshot.inspections.CompareTablesQuery$ComparisonResultTree"/>
</pane>
<pane
class="org.eclipse.mat.ui.snapshot.panes.BundlesPane"

Back to the top