/******************************************************************************* | |
* Copyright (c) 2008, 2020 SAP AG, IBM Corporation and others. | |
* All rights reserved. This program and the accompanying materials | |
* are made available under the terms of the Eclipse Public License v1.0 | |
* which accompanies this distribution, and is available at | |
* http://www.eclipse.org/legal/epl-v10.html | |
* | |
* Contributors: | |
* SAP AG - initial API and implementation | |
* Andrew Johnson/IBM Corporation - debug verbose option | |
*******************************************************************************/ | |
package org.eclipse.mat.ui.internal.query.arguments; | |
import java.math.BigInteger; | |
import java.util.ArrayList; | |
import java.util.Collections; | |
import java.util.HashMap; | |
import java.util.Iterator; | |
import java.util.List; | |
import java.util.Locale; | |
import java.util.Map; | |
import java.util.regex.Pattern; | |
import java.util.regex.PatternSyntaxException; | |
import org.eclipse.jface.layout.TableColumnLayout; | |
import org.eclipse.jface.resource.FontDescriptor; | |
import org.eclipse.jface.resource.JFaceResources; | |
import org.eclipse.jface.resource.LocalResourceManager; | |
import org.eclipse.jface.viewers.ColumnWeightData; | |
import org.eclipse.jface.window.DefaultToolTip; | |
import org.eclipse.jface.window.ToolTip; | |
import org.eclipse.mat.SnapshotException; | |
import org.eclipse.mat.internal.snapshot.ArgumentParser; | |
import org.eclipse.mat.internal.snapshot.HeapObjectContextArgument; | |
import org.eclipse.mat.internal.snapshot.HeapObjectParamArgument; | |
import org.eclipse.mat.query.IContextObject; | |
import org.eclipse.mat.query.IQueryContext; | |
import org.eclipse.mat.query.annotations.Argument; | |
import org.eclipse.mat.query.registry.ArgumentDescriptor; | |
import org.eclipse.mat.query.registry.ArgumentSet; | |
import org.eclipse.mat.snapshot.ISnapshot; | |
import org.eclipse.mat.snapshot.SnapshotFactory; | |
import org.eclipse.mat.snapshot.model.IObject; | |
import org.eclipse.mat.snapshot.query.IHeapObjectArgument; | |
import org.eclipse.mat.ui.MemoryAnalyserPlugin; | |
import org.eclipse.mat.ui.Messages; | |
import org.eclipse.mat.ui.accessibility.AccessibleCompositeAdapter; | |
import org.eclipse.mat.ui.internal.query.arguments.LinkEditor.Mode; | |
import org.eclipse.mat.ui.internal.query.arguments.TextEditor.DecoratorType; | |
import org.eclipse.mat.util.MessageUtil; | |
import org.eclipse.swt.SWT; | |
import org.eclipse.swt.custom.TableEditor; | |
import org.eclipse.swt.graphics.Font; | |
import org.eclipse.swt.graphics.Point; | |
import org.eclipse.swt.widgets.Composite; | |
import org.eclipse.swt.widgets.Control; | |
import org.eclipse.swt.widgets.Event; | |
import org.eclipse.swt.widgets.Listener; | |
import org.eclipse.swt.widgets.Table; | |
import org.eclipse.swt.widgets.TableColumn; | |
import org.eclipse.swt.widgets.TableItem; | |
public class ArgumentsTable implements ArgumentEditor.IEditorListener | |
{ | |
private static final int MIN_EDITOR_WIDTH = 50; | |
private static final String ADDRESS_PREFIX = "0x";//$NON-NLS-1$ | |
/** | |
* The end of line string for this machine. | |
*/ | |
protected static final String EOL = System.getProperty("line.separator", "\n");//$NON-NLS-1$//$NON-NLS-2$ | |
private static final String ARGUMENT = Messages.ArgumentsTable_Argument; | |
private static final String VALUE = Messages.ArgumentsTable_Value; | |
private LocalResourceManager resourceManager = new LocalResourceManager(JFaceResources.getResources()); | |
private Table table; | |
private Font boldFont; | |
private Font normalFont; | |
private int tableRowHeight = SWT.DEFAULT; | |
private List<ITableListener> listeners = Collections.synchronizedList(new ArrayList<ITableListener>()); | |
private Map<ArgumentEditor, String> errors = Collections.synchronizedMap(new HashMap<ArgumentEditor, String>()); | |
private Mode mode = Mode.SIMPLE_MODE; | |
private Map<ArgumentDescriptor, Mode> modeMap; | |
private IQueryContext context; | |
private ArgumentSet argumentSet; | |
public interface ITableListener | |
{ | |
void onInputChanged(); | |
void onError(String message); | |
void onFocus(String message); | |
void onModeChange(Mode mode); | |
} | |
public ArgumentsTable(Composite parent, int style, IQueryContext context, ArgumentSet argumentSet, Mode mode) | |
{ | |
this.context = context; | |
this.argumentSet = argumentSet; | |
this.mode = mode; | |
TableColumnLayout tableColumnLayout = new TableColumnLayout(); | |
parent.setLayout(tableColumnLayout); | |
table = new Table(parent, style); | |
Font parentFont = parent.getFont(); | |
table.setFont(parentFont); | |
table.setLinesVisible(true); | |
table.setHeaderVisible(true); | |
AccessibleCompositeAdapter.access(table); | |
TableColumn column = new TableColumn(table, SWT.NONE); | |
column.setText(ARGUMENT); | |
tableColumnLayout.setColumnData(column, new ColumnWeightData(0, 100)); | |
column = new TableColumn(table, SWT.NONE); | |
column.setText(VALUE); | |
tableColumnLayout.setColumnData(column, new ColumnWeightData(100, 100)); | |
boldFont = resourceManager.createFont(FontDescriptor.createFrom(parentFont).setStyle(SWT.BOLD)); | |
normalFont = resourceManager.createFont(FontDescriptor.createFrom(parentFont).setStyle(SWT.NORMAL)); | |
modeMap = new HashMap<ArgumentDescriptor, Mode>(argumentSet.getQueryDescriptor().getArguments().size()); | |
for (ArgumentDescriptor descriptor : argumentSet.getQueryDescriptor().getArguments()) | |
{ | |
if (isHeapObject(descriptor)) | |
modeMap.put(descriptor, mode); | |
} | |
table.addListener(SWT.MeasureItem, new Listener() | |
{ | |
public void handleEvent(Event event) | |
{ | |
event.height = tableRowHeight; | |
} | |
}); | |
createTableContent(); | |
new DefaultToolTip(table, ToolTip.NO_RECREATE, false) | |
{ | |
private ArgumentDescriptor getEntry(Event event) | |
{ | |
TableItem item = table.getItem(new Point(event.x, event.y)); | |
if (item != null && item.getData() != null) { return ((ArgumentEditor) item.getData()).getDescriptor(); } | |
return null; | |
} | |
protected String getText(Event event) | |
{ | |
ArgumentDescriptor entry = getEntry(event); | |
if (entry != null) { return entry.getHelp(); } | |
return null; | |
} | |
protected boolean shouldCreateToolTip(Event event) | |
{ | |
table.setToolTipText(""); //$NON-NLS-1$ | |
return getEntry(event) != null && super.shouldCreateToolTip(event); | |
} | |
protected Object getToolTipArea(Event event) | |
{ | |
return getEntry(event); | |
} | |
}.activate(); | |
} | |
private void setTableRowHeight(int height) | |
{ | |
tableRowHeight = Math.max(tableRowHeight, height); | |
} | |
public void dispose() | |
{ | |
// clear error messages | |
errors.clear(); | |
errors = null; | |
modeMap.clear(); | |
modeMap = null; | |
if (resourceManager != null) | |
{ | |
resourceManager.dispose(); | |
resourceManager = null; | |
} | |
} | |
// ////////////////////////////////////////////////////////////// | |
// private constructor methods | |
// ////////////////////////////////////////////////////////////// | |
private void createTableContent() | |
{ | |
table.setData(argumentSet); | |
for (ArgumentDescriptor descriptor : argumentSet.getQueryDescriptor().getArguments()) | |
{ | |
if (context.available(descriptor.getType(), descriptor.getAdvice())) | |
continue; | |
String flag = createArgumentLabel(descriptor); | |
boolean isHeapObject = isHeapObject(descriptor); | |
Object argumentValue = argumentSet.getArgumentValue(descriptor); | |
if (IContextObject.class.isAssignableFrom(descriptor.getType())) | |
{ | |
TableItem item = new TableItem(table, SWT.NONE); | |
item.setFont(normalFont); | |
item.setText(new String[] { flag, Messages.ArgumentsTable_selectedRows }); | |
} | |
else if (descriptor.isMultiple() && !isHeapObject) | |
{ | |
List<?> values = (List<?>) argumentValue; | |
if (values == null) | |
values = (List<?>) descriptor.getDefaultValue(); | |
if (values == null || values.isEmpty()) | |
{ | |
addEditorRow(descriptor, flag, null, -1); | |
} | |
else | |
{ | |
Iterator<?> valueIt = values.iterator(); | |
Object firstValue = valueIt.next(); | |
addEditorRow(descriptor, flag, firstValue, -1); | |
while (valueIt.hasNext()) | |
{ | |
Object objValue = valueIt.next(); | |
addEditorRow(descriptor, "..\"..", objValue, -1); //$NON-NLS-1$ | |
} | |
addEditorRow(descriptor, "..\"..", null, -1); //$NON-NLS-1$ | |
} | |
} | |
else if (isHeapObject && argumentValue instanceof HeapObjectContextArgument) | |
{ | |
// when query is called for the certain object instance (from | |
// the view). In that case hoa cannot be modified | |
TableItem item = new TableItem(table, SWT.NONE); | |
item.setFont(normalFont); | |
item.setText(new String[] { flag, String.valueOf(argumentValue) }); | |
} | |
else if (isHeapObject) | |
{ | |
Object value = argumentValue; | |
if (value == null) | |
{ | |
// Experimental | |
// No heap object provided, so see if query has a default heap object argument | |
value = descriptor.getDefaultValue(); | |
if (value instanceof IHeapObjectArgument) | |
{ | |
// The default heap object argument has a null snapshot, | |
ISnapshot snapshot = (ISnapshot) context.get(ISnapshot.class, null); | |
// So convert it to a string then reparse in this context. | |
// Only really makes sense for class name or OQL arguments as object | |
// addresses won't apply between snapshots (except boot classloader @0x0 ?) | |
String line = ((IHeapObjectArgument) value).getLabel(); | |
try | |
{ | |
HeapObjectParamArgument hoa = ArgumentParser.consumeHeapObjects(snapshot, line); | |
value = hoa; | |
} | |
catch (SnapshotException e) | |
{ | |
value = null; | |
} | |
} | |
else | |
{ | |
// Value could be Integer(0) for int argument, etc. so not suitable below | |
value = null; | |
} | |
} | |
addHeapObjectTableItems(descriptor, (HeapObjectParamArgument) value); | |
} | |
else | |
{ | |
Object value = argumentValue; | |
if (value == null) | |
value = descriptor.getDefaultValue(); | |
addEditorRow(descriptor, flag, value, -1); | |
} | |
} | |
for (Control control : table.getChildren()) | |
{ | |
if (control instanceof ArgumentEditor) | |
((ArgumentEditor) control).addListener(this); | |
} | |
try | |
{ | |
table.getChildren()[0].setFocus(); | |
} | |
catch (ArrayIndexOutOfBoundsException e) | |
{ | |
// $JL-EXC$ | |
// should not happen as we assume that table should have at least | |
// one child. | |
// If by any reason the exception occurs, the focus will not be set | |
} | |
} | |
private boolean isHeapObject(ArgumentDescriptor descriptor) | |
{ | |
boolean isHeapObject = descriptor.getAdvice() == Argument.Advice.HEAP_OBJECT // | |
|| IObject.class.isAssignableFrom(descriptor.getType()) // | |
|| IHeapObjectArgument.class.isAssignableFrom(descriptor.getType()); | |
return isHeapObject; | |
} | |
private String createArgumentLabel(ArgumentDescriptor descriptor) | |
{ | |
String flag = descriptor.getFlag(); | |
if (flag == null) | |
return descriptor.getName(); | |
else | |
return "-" + flag;//$NON-NLS-1$ | |
} | |
private void addEditorRow(ArgumentDescriptor descriptor, String flag, Object value, int index) | |
{ | |
TableItem item; | |
if (index > 0) | |
item = new TableItem(table, SWT.NONE, index); | |
else | |
item = new TableItem(table, SWT.NONE); | |
item.setText(flag); | |
setFont(descriptor, item); | |
TableEditor editor = createEditor(); | |
ArgumentEditor aec = TableEditorFactory.createTableEditor(table, context, descriptor, item); | |
aec.setFont(item.getFont()); | |
editor.setEditor(aec, item, 1); | |
item.setData(aec); | |
// Adjust the table height for the editor | |
setTableRowHeight(aec.computeSize(SWT.DEFAULT, SWT.DEFAULT).y); | |
// listener should be added only to the new rows, for rows with default | |
// values listeners are added after the table is created and filled with | |
// default values | |
if (index > 0) | |
{ | |
aec.addListener(this); | |
// ugly: w/o pack, the table does not redraw the editors correctly | |
table.pack(); | |
table.getParent().pack(); | |
setNewTabOrder(); | |
} | |
try | |
{ | |
if (value != null) | |
aec.setValue(value); | |
} | |
catch (SnapshotException e) | |
{ | |
// $JL-EXC$ | |
// leave editor empty | |
} | |
} | |
private void setFont(ArgumentDescriptor descriptor, TableItem item) | |
{ | |
// to make the rows in the table a little higher we set the bigger font | |
// to the whole table. | |
// and in this method we return the normal size and highlight mandatory | |
// arguments. | |
if (descriptor.isMandatory()) | |
{ | |
// Normal font for whole row | |
item.setFont(normalFont); | |
// Bold font for first box in row | |
item.setFont(0, boldFont); | |
} | |
else | |
item.setFont(normalFont); | |
} | |
private void setNewTabOrder() | |
{ | |
// this method is called when the new row was inserted to reassure that | |
// the "Tab" button works correct. | |
TableItem[] items = table.getItems(); | |
// we need to include to the TabList only those items that are editors | |
Control[] newTabOrder = new Control[table.getChildren().length]; | |
for (int i = 0, j = 0; i < items.length; i++, j++) | |
{ | |
if (items[i].getData() != null) | |
newTabOrder[j] = (ArgumentEditor) items[i].getData(); | |
else | |
j--; | |
} | |
table.setTabList(newTabOrder); | |
} | |
private void addHeapObjectTableItems(ArgumentDescriptor descriptor, int index, TextEditor.DecoratorType decorator) | |
{ | |
TableItem item = new TableItem(table, SWT.NONE, index); | |
setFont(descriptor, item); | |
TableEditor editor = createEditor(); | |
ImageTextEditor aec = new ImageTextEditor(table, context, descriptor, item, decorator); | |
aec.setFont(item.getFont()); | |
editor.setEditor(aec, item, 1); | |
item.setData(aec); | |
// Adjust the table height for the image button | |
setTableRowHeight(aec.computeSize(SWT.DEFAULT, SWT.DEFAULT).y); | |
aec.addListener(this); | |
// ugly: w/o pack, the table does not redraw the editors correctly | |
table.getParent().pack(); | |
setNewTabOrder(); | |
} | |
private void addHeapObjectTableItems(ArgumentDescriptor descriptor, HeapObjectParamArgument initialInput) | |
{ | |
// Some queries can only cope with one object | |
boolean singleObject = isSingleObjectDescriptor(descriptor); | |
DecoratorType types[] = TextEditor.DecoratorType.values(); | |
if (singleObject) | |
{ | |
// The query is only expecting one object so make the object address | |
// query first | |
types = new DecoratorType[] { TextEditor.DecoratorType.OBJECT_ADDRESS, TextEditor.DecoratorType.PATTERN, | |
TextEditor.DecoratorType.QUERY }; | |
} | |
// If an initial input would be hidden in simple mode then switch to advanced | |
if (initialInput != null && (types[0] != TextEditor.DecoratorType.OBJECT_ADDRESS && !initialInput.getAddresses().isEmpty() || | |
types[0] != TextEditor.DecoratorType.QUERY && !initialInput.getOqls().isEmpty() || | |
types[0] != TextEditor.DecoratorType.PATTERN && !initialInput.getPatterns().isEmpty())) | |
{ | |
// check whether Mode.ADVANCED_MODE and switch to it if not the case | |
verifyMode(); | |
} | |
if (modeMap.get(descriptor).equals(Mode.SIMPLE_MODE)) | |
{ | |
// Only have one type in simple mode | |
types = new TextEditor.DecoratorType[]{types[0]}; | |
} | |
String label = createArgumentLabel(descriptor); | |
for (TextEditor.DecoratorType decorator : types) | |
{ | |
boolean createExtra = true; | |
if (decorator.equals(TextEditor.DecoratorType.PATTERN)) | |
{ | |
// when the mode of the table is switched after a pattern was | |
// provided - keep the pattern | |
List<Pattern> patterns = null; | |
if (initialInput != null) | |
{ | |
patterns = initialInput.getPatterns(); | |
} | |
// create a new row for each pattern | |
if (patterns != null) | |
{ | |
for (Pattern pattern : patterns) | |
{ | |
createHeapObjectRow(descriptor, pattern.toString(), decorator, label); | |
// a work around: to avoid "objects" label for each of | |
// the three editors | |
label = "";//$NON-NLS-1$ | |
} | |
} | |
} | |
else if (decorator.equals(TextEditor.DecoratorType.OBJECT_ADDRESS)) | |
{ | |
List<Long> addresses = null; | |
if (initialInput != null) | |
{ | |
addresses = initialInput.getAddresses(); | |
} | |
// create a new row for each pattern | |
if (addresses != null) | |
{ | |
for (Long address : addresses) | |
{ | |
createHeapObjectRow(descriptor, ADDRESS_PREFIX + Long.toHexString(address), decorator, label); | |
label = "";//$NON-NLS-1$ | |
// Don't create a spare row when we only need one object | |
createExtra &= !singleObject; | |
} | |
} | |
} | |
else | |
// TextEditor.DecoratorType.QUERY | |
{ | |
List<String> oqls = null; | |
if (initialInput != null) | |
{ | |
oqls = initialInput.getOqls(); | |
} | |
// create a new row for each pattern | |
if (oqls != null) | |
{ | |
for (String oql : oqls) | |
{ | |
createHeapObjectRow(descriptor, oql, decorator, label); | |
label = "";//$NON-NLS-1$ | |
// Don't create a spare row when we only need one object | |
createExtra &= !singleObject; | |
} | |
} | |
} | |
if (createExtra) | |
{ | |
// plus one empty row for this type of decorator in any case | |
createHeapObjectRow(descriptor, null, decorator, label); | |
label = "";//$NON-NLS-1$ | |
} | |
} | |
for (TextEditor.DecoratorType decorator : types) | |
{ | |
if (decorator.equals(TextEditor.DecoratorType.PATTERN)) | |
{ | |
// Add the subclass instance box if the class name pattern is visible | |
addCheckBoxRows(descriptor, CheckBoxEditor.Type.INCLUDE_CLASS_INSTANCE, (initialInput != null) ? initialInput | |
.isIncludeClassInstance() : false); | |
break; | |
} | |
} | |
if (modeMap.get(descriptor).equals(Mode.ADVANCED_MODE)) | |
{ | |
addCheckBoxRows(descriptor, CheckBoxEditor.Type.INCLUDE_SUBCLASSES, (initialInput != null) ? initialInput | |
.isIncludeSubclasses() : false); | |
addCheckBoxRows(descriptor, CheckBoxEditor.Type.INTEPRET_AS_CLASSLOADER, | |
(initialInput != null) ? initialInput.isIncludeLoadedInstances() : false); | |
addCheckBoxRows(descriptor, CheckBoxEditor.Type.RETAINED, (initialInput != null) ? initialInput | |
.isRetained() : false); | |
if (MemoryAnalyserPlugin.getDefault().isDebugging()) | |
{ | |
addCheckBoxRows(descriptor, CheckBoxEditor.Type.VERBOSE, (initialInput != null) ? initialInput | |
.isVerbose() : false); | |
} | |
} | |
addLink(descriptor, modeMap.get(descriptor)); | |
table.getParent().pack(); | |
} | |
private boolean isSingleObjectDescriptor(ArgumentDescriptor descriptor) | |
{ | |
return !descriptor.isMultiple() | |
&& isHeapObject(descriptor) | |
&& (IObject.class.isAssignableFrom(descriptor.getType()) || | |
descriptor.getType() == int.class || | |
descriptor.getType() == Integer.class); | |
} | |
private void verifyMode() | |
{ | |
if (mode.equals(Mode.SIMPLE_MODE)) | |
mode = Mode.ADVANCED_MODE; | |
} | |
private void createHeapObjectRow(ArgumentDescriptor descriptor, String value, TextEditor.DecoratorType decorator, | |
String label) | |
{ | |
TableItem item = new TableItem(table, SWT.NONE); | |
item.setText(label); | |
setFont(descriptor, item); | |
TableEditor editor = createEditor(); | |
ImageTextEditor aec = new ImageTextEditor(table, context, descriptor, item, decorator); | |
aec.setFont(item.getFont()); | |
editor.setEditor(aec, item, 1); | |
item.setData(aec); | |
// Adjust the table height for the image button | |
setTableRowHeight(aec.computeSize(SWT.DEFAULT, SWT.DEFAULT).y); | |
if (value != null) | |
{ | |
try | |
{ | |
if (decorator.equals(TextEditor.DecoratorType.OBJECT_ADDRESS)) | |
{ | |
// This is a bit strange - the value gets resolved as an objectid | |
ISnapshot snapshot = (ISnapshot) context.get(ISnapshot.class, null); | |
long addr = new BigInteger(value.substring(2), 16).longValue(); | |
// Check address is valid | |
Integer ival = Integer.valueOf(snapshot.mapAddressToId(addr)); | |
// but set the hex value | |
aec.setValue(value); | |
} | |
else | |
{ | |
aec.setValue(value); | |
} | |
} | |
catch (SnapshotException e) | |
{ | |
// $JL-EXC$ | |
// leave editor empty | |
} | |
} | |
} | |
private void addLink(ArgumentDescriptor descriptor, Mode mode) | |
{ | |
TableItem item = new TableItem(table, SWT.NONE); | |
item.setFont(normalFont); | |
item.setText("");//$NON-NLS-1$ | |
TableEditor editor = createEditor(); | |
LinkEditor aec = new LinkEditor(table, context, descriptor, item, mode); | |
aec.setFont(item.getFont()); | |
editor.setEditor(aec, item, 1); | |
item.setData(aec); | |
// Adjust the table height for the editor | |
setTableRowHeight(aec.computeSize(SWT.DEFAULT, SWT.DEFAULT).y); | |
} | |
private TableEditor createEditor() | |
{ | |
TableEditor editor = new TableEditor(table); | |
editor.horizontalAlignment = SWT.LEFT; | |
editor.grabHorizontal = true; | |
editor.minimumWidth = MIN_EDITOR_WIDTH; | |
return editor; | |
} | |
private void addCheckBoxRows(ArgumentDescriptor descriptor, CheckBoxEditor.Type type, boolean selected) | |
{ | |
TableItem item = new TableItem(table, SWT.NONE); | |
item.setFont(normalFont); | |
item.setText("");//$NON-NLS-1$ | |
TableEditor editor = createEditor(); | |
CheckBoxEditor aec = new CheckBoxEditor(table, context, descriptor, item, type); | |
aec.setFont(item.getFont()); | |
editor.setEditor(aec, item, 1); | |
item.setData(aec); | |
// Adjust the table height for the editor | |
setTableRowHeight(aec.computeSize(SWT.DEFAULT, SWT.DEFAULT).y); | |
try | |
{ | |
aec.setValue(selected); | |
} | |
catch (SnapshotException e) | |
{ | |
// $JL-EXC$ | |
// leave unselected | |
} | |
} | |
public synchronized void onValueChanged(Object value, ArgumentDescriptor descriptor, TableItem item, | |
ArgumentEditor argEditor) | |
{ | |
int myIndex = table.indexOf(item); | |
// remove error message | |
onError(argEditor, null); | |
onError(null, null); | |
boolean isHeapObject = isHeapObject(descriptor); | |
boolean isLastOne = descriptor.isMultiple() | |
&& !isHeapObject | |
&& (myIndex + 1 == table.getItemCount() || ((ArgumentEditor) table.getItem(myIndex + 1) | |
.getData()).getDescriptor() != descriptor); | |
// update argument set -- heap objects | |
if (isHeapObject) | |
{ | |
// if (value == null) | |
// return; | |
if (argEditor instanceof ImageTextEditor) | |
{ | |
// verify current modification | |
verifyHeapObjectTextEditorInput((ImageTextEditor) argEditor); | |
} | |
// if there are no errors in all the fields describing the hoa - | |
// create HeapObject and add it to the argumentSet | |
if (noErrorsInHoa()) | |
{ | |
// overwrite hoa on every modification | |
argumentSet.removeArgumentValue(descriptor); | |
HeapObjectParamArgument hoa = createHeapObjectDefinition(descriptor); | |
if (hoa.isComplete()) | |
{ | |
argumentSet.setArgumentValue(descriptor, hoa); | |
// add new row only when there are no errors | |
if ((argEditor instanceof ImageTextEditor) | |
&& isNewRowNeeded(descriptor, ((ImageTextEditor) argEditor).getDecorator())) | |
{ | |
addHeapObjectTableItems(descriptor, myIndex + 1, ((ImageTextEditor) argEditor).getDecorator()); | |
} | |
} | |
else if (descriptor.isMandatory()) | |
{ | |
// add error message on null argument | |
onError(null, Messages.ArgumentsTable_ProvidePattern); | |
} | |
} | |
} | |
// update lists | |
else if (descriptor.isMultiple()) | |
{ | |
List<Object> values = new ArrayList<Object>(); | |
Control[] children = table.getChildren(); | |
for (int ii = 0; ii < children.length; ii++) | |
{ | |
if (!(children[ii] instanceof ArgumentEditor)) | |
continue; | |
ArgumentEditor editor = (ArgumentEditor) children[ii]; | |
if (editor.getDescriptor() == descriptor) | |
{ | |
Object v = editor.getValue(); | |
if (v != null) | |
values.add(v); | |
} | |
} | |
if (values.isEmpty()) | |
values = null; | |
Object defaultValue = descriptor.getDefaultValue(); | |
if (defaultValue == null || !defaultValue.equals(values)) | |
argumentSet.setArgumentValue(descriptor, values); | |
else | |
argumentSet.removeArgumentValue(descriptor); | |
// warn a/b mandatory arguments | |
if (descriptor.isMandatory() && values == null) | |
onError(argEditor, MessageUtil.format(Messages.ArgumentsTable_isMandatory, descriptor.getName())); | |
// insert new row at myIndex + 1 | |
if (isLastOne && value != null) | |
addEditorRow(descriptor, "..\"..", null, myIndex + 1);//$NON-NLS-1$ | |
} | |
else | |
{ | |
Object defaultValue = descriptor.getDefaultValue(); | |
if (defaultValue == null || !defaultValue.equals(value)) | |
{ | |
argumentSet.setArgumentValue(descriptor, value); | |
} | |
else | |
{ | |
argumentSet.removeArgumentValue(descriptor); | |
} | |
// warn a/b mandatory arguments | |
if (descriptor.isMandatory() && value == null) | |
onError(argEditor, MessageUtil.format(Messages.ArgumentsTable_isMandatory, descriptor.getName())); | |
} | |
// inform about value changes | |
fireInputChangedEvent(); | |
} | |
private boolean isNewRowNeeded(ArgumentDescriptor descriptor, DecoratorType decorator) | |
{ | |
boolean notEmpty = true; | |
Control[] children = table.getChildren(); | |
for (Control control : children) | |
{ | |
if ((control instanceof ImageTextEditor) && ((ImageTextEditor) control).getDescriptor().equals(descriptor) | |
&& ((ImageTextEditor) control).getDecorator().equals(decorator)) | |
{ | |
notEmpty = notEmpty && ((ImageTextEditor) control).getValue() != null | |
&& !((ImageTextEditor) control).getValue().equals("");//$NON-NLS-1$ | |
// If the decorator is not a pattern and we only need one argument then only have one | |
notEmpty &= decorator.equals(TextEditor.DecoratorType.PATTERN) || !isSingleObjectDescriptor(descriptor); | |
} | |
} | |
return notEmpty; | |
} | |
private HeapObjectParamArgument createHeapObjectDefinition(ArgumentDescriptor descriptor) | |
{ | |
ISnapshot snapshot = (ISnapshot) context.get(ISnapshot.class, null); | |
HeapObjectParamArgument hoa = new HeapObjectParamArgument(snapshot); | |
Control[] children = table.getChildren(); | |
for (Control control : children) | |
{ | |
if (!descriptor.equals(((ArgumentEditor) control).getDescriptor())) | |
continue; | |
if (control instanceof CheckBoxEditor) | |
{ | |
Boolean value = (Boolean) ((CheckBoxEditor) control).getValue(); | |
switch (((CheckBoxEditor) control).getType()) | |
{ | |
case INCLUDE_CLASS_INSTANCE: | |
{ | |
hoa.setIncludeClassInstance(value); | |
break; | |
} | |
case INCLUDE_SUBCLASSES: | |
{ | |
hoa.setIncludeSubclasses(value); | |
break; | |
} | |
case INTEPRET_AS_CLASSLOADER: | |
{ | |
hoa.setIncludeLoadedInstances(value); | |
break; | |
} | |
case RETAINED: | |
{ | |
hoa.setRetained(value); | |
break; | |
} | |
case VERBOSE: | |
{ | |
hoa.setVerbose(value); | |
break; | |
} | |
} | |
} | |
else if (control instanceof ImageTextEditor) | |
{ | |
ImageTextEditor editor = (ImageTextEditor) control; | |
if (editor.getValue() != null) | |
{ | |
DecoratorType decorator = editor.getDecorator(); | |
String line = ((ImageTextEditor) control).getValue().toString().trim(); | |
// Ensure that types and values are appropriate | |
if (decorator == TextEditor.DecoratorType.QUERY && line.toLowerCase(Locale.ENGLISH).startsWith("select")) //$NON-NLS-1$ | |
{ | |
hoa.addOql(line); | |
} | |
else if (decorator == TextEditor.DecoratorType.OBJECT_ADDRESS && line.startsWith(ADDRESS_PREFIX)) | |
{ | |
hoa.addObjectAddress(new BigInteger(line.substring(2), 16).longValue()); | |
} | |
else | |
// Pattern | |
{ | |
if (decorator == TextEditor.DecoratorType.PATTERN && !line.equals(""))//$NON-NLS-1$ | |
{ | |
hoa.addPattern(Pattern.compile(line)); | |
} | |
} | |
} | |
} // control of other types do not belong to the HeapObject | |
// arguments | |
} | |
return hoa; | |
} | |
private void verifyHeapObjectTextEditorInput(ImageTextEditor editor) | |
{ | |
TextEditor.DecoratorType decorator = editor.getDecorator(); | |
if (editor.getValue() != null) | |
{ | |
onError(editor, null); | |
String line = editor.getValue().toString().trim(); | |
if (decorator.equals(TextEditor.DecoratorType.QUERY)) | |
{ | |
if (line.equals(""))//$NON-NLS-1$ | |
return; | |
try | |
{ | |
SnapshotFactory.createQuery(line); | |
} | |
catch (SnapshotException e) | |
{ | |
// $JL-EXC$ | |
// fix: reformat message for proper displaying | |
String msg = e.getMessage(); | |
if (msg.startsWith(Messages.ArgumentsTable_Encountered)) | |
{ | |
int p = msg.indexOf(Messages.ArgumentsTable_WasExpecting); | |
if (p >= 0) | |
msg = msg.substring(p, msg.length()).replace(EOL, " ");//$NON-NLS-1$ | |
} | |
onError(editor, msg); | |
} | |
} | |
else if (decorator.equals(TextEditor.DecoratorType.OBJECT_ADDRESS)) | |
{ | |
if (line.length() > 2 && ADDRESS_PREFIX.equals(line.substring(0, 2))) | |
{ | |
try | |
{ | |
new BigInteger(line.substring(2), 16).longValue(); | |
} | |
catch (NumberFormatException e) | |
{ | |
// $JL-EXC$ | |
onError(editor, Messages.ArgumentsTable_InvalidAddress + e.getMessage()); | |
} | |
} | |
else if (line.length() < 2 && !line.startsWith(ADDRESS_PREFIX.substring(0, line.length())) | |
|| line.length() >= 2 && !line.startsWith(ADDRESS_PREFIX.substring(0, 2))) | |
{ | |
onError(editor, Messages.ArgumentsTable_InvalidAddress + line); | |
} | |
else if (line.length() != 0) | |
{ | |
onError(editor, Messages.ArgumentsTable_AddressNotComplete); | |
} | |
} | |
else | |
// Pattern | |
{ | |
if (!line.equals(""))//$NON-NLS-1$ | |
{ | |
try | |
{ | |
Pattern.compile(line); | |
} | |
catch (PatternSyntaxException e) | |
{ | |
// $JL-EXC$ | |
int idx = Math.max(0, Math.min(e.getIndex(), line.length())); | |
onError(editor, MessageUtil.format(Messages.ArgumentsTable_InvalidClassNamePattern, e.getIndex(), line.substring(0, idx), e.getDescription())); | |
} | |
} | |
} | |
} | |
} | |
private boolean noErrorsInHoa() | |
{ | |
for (Map.Entry<ArgumentEditor, String> entry : errors.entrySet()) | |
{ | |
if (entry.getKey() instanceof ImageTextEditor) { return false; } | |
} | |
return true; | |
} | |
public synchronized void onError(ArgumentEditor editor, String message) | |
{ | |
synchronized (errors) | |
{ | |
if (message == null) | |
{ | |
if (errors.remove(editor) != null) | |
{ | |
if (errors.isEmpty()) | |
fireErrorMessageEvent(null); | |
else | |
fireErrorMessageEvent(errors.values().iterator().next()); | |
} | |
} | |
else | |
{ | |
errors.put(editor, message); | |
fireErrorMessageEvent(message); | |
} | |
} | |
} | |
// ////////////////////////////////////////////////////////////// | |
// table listener implementation | |
// ////////////////////////////////////////////////////////////// | |
public void addListener(ITableListener listener) | |
{ | |
this.listeners.add(listener); | |
} | |
public void removeListener(ITableListener listener) | |
{ | |
this.listeners.remove(listener); | |
} | |
private void fireInputChangedEvent() | |
{ | |
synchronized (listeners) | |
{ | |
for (ITableListener listener : listeners) | |
listener.onInputChanged(); | |
} | |
} | |
private void fireErrorMessageEvent(String message) | |
{ | |
synchronized (listeners) | |
{ | |
for (ITableListener listener : listeners) | |
listener.onError(message); | |
} | |
} | |
public void fireFocusChangedEvent(String message) | |
{ | |
synchronized (listeners) | |
{ | |
for (ITableListener listener : listeners) | |
listener.onFocus(message); | |
} | |
} | |
public void onFocus(String message) | |
{ | |
fireFocusChangedEvent(message); | |
} | |
public void onModeChange(Mode mode, ArgumentDescriptor descriptor) | |
{ | |
// add new mode for this descriptor to the HashMap to be able to proceed | |
// different descriptors independently | |
if (modeMap == null) | |
{ | |
modeMap = new HashMap<ArgumentDescriptor, Mode>(); | |
} | |
modeMap.put(descriptor, mode); | |
List<ITableListener> copy = new ArrayList<ITableListener>(listeners); | |
for (ITableListener listener : copy) | |
listener.onModeChange(mode); | |
// clear error messages | |
errors.clear(); | |
fireErrorMessageEvent(null); | |
table.removeAll(); | |
Control[] children = table.getChildren(); | |
for (Control control : children) | |
{ | |
control.dispose(); | |
} | |
this.createTableContent(); | |
} | |
} |