blob: 6dd2a82ce4558e7a54d1662a223b9ccce6982ce2 [file] [log] [blame]
/*******************************************************************************
* 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();
}
}