org.eclipse.mat.ui.internal.acquire.ProviderArgumentsTable.java Source code

Java tutorial

Introduction

Here is the source code for org.eclipse.mat.ui.internal.acquire.ProviderArgumentsTable.java

Source

/*******************************************************************************
 * Copyright (c) 2010, 2012 SAP AG 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
 *    IBM Corporation - refactor for new/import wizard
 *******************************************************************************/
package org.eclipse.mat.ui.internal.acquire;

import java.lang.reflect.Array;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

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.acquire.HeapDumpProviderDescriptor;
import org.eclipse.mat.internal.acquire.VmInfoDescriptor;
import org.eclipse.mat.query.IQueryContext;
import org.eclipse.mat.query.annotations.descriptors.IAnnotatedObjectDescriptor;
import org.eclipse.mat.query.registry.AnnotatedObjectArgumentsSet;
import org.eclipse.mat.query.registry.ArgumentDescriptor;
import org.eclipse.mat.ui.Messages;
import org.eclipse.mat.ui.internal.query.arguments.ArgumentEditor;
import org.eclipse.mat.ui.internal.query.arguments.ArgumentEditor.IEditorListener;
import org.eclipse.mat.ui.internal.query.arguments.LinkEditor.Mode;
import org.eclipse.mat.ui.internal.query.arguments.TableEditorFactory;
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 ProviderArgumentsTable implements IEditorListener/*, ProcessSelectionListener*/
{

    private static final int MIN_EDITOR_WIDTH = 50;

    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 IAnnotatedObjectDescriptor providerDescriptor;
    private AnnotatedObjectArgumentsSet argumentSet;
    private IQueryContext context;

    private List<ITableListener> listeners = Collections.synchronizedList(new ArrayList<ITableListener>());
    private Map<ArgumentEditor, String> errors = Collections.synchronizedMap(new HashMap<ArgumentEditor, String>());

    public interface ITableListener {
        void onInputChanged();

        void onValueChanged();

        void onError(String message);

        void onFocus(String message);
    }

    public ProviderArgumentsTable(Composite parent, int style/*, ProviderArgumentsWizzardPage wizzardPage*/) {
        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);

        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));

        table.addListener(SWT.MeasureItem, new Listener() {
            public void handleEvent(Event event) {
                event.height = tableRowHeight;
            }
        });

        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) {
        if (height > tableRowHeight) {
            tableRowHeight = height;
            table.pack();
            table.getParent().pack();
        }
    }

    public AnnotatedObjectArgumentsSet getArgumentSet() {
        return argumentSet;
    }

    public IAnnotatedObjectDescriptor getProviderDescriptor() {
        return providerDescriptor;
    }

    void createTableContent() {
        List<ArgumentDescriptor> argumentDescriptors = providerDescriptor.getArguments();

        for (ArgumentDescriptor descriptor : argumentDescriptors) {
            String flag = createArgumentLabel(descriptor);

            Object argumentValue = argumentSet.getArgumentValue(descriptor);

            if (descriptor.isMultiple()) {
                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 {
                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
        }

    }

    public void addListener(ITableListener listener) {
        this.listeners.add(listener);
    }

    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 TableEditor createEditor() {
        TableEditor editor = new TableEditor(table);
        editor.horizontalAlignment = SWT.LEFT;
        editor.grabHorizontal = true;
        editor.minimumWidth = MIN_EDITOR_WIDTH;

        return editor;
    }

    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 isLastOne = descriptor.isMultiple() && (myIndex + 1 == table.getItemCount()
                || ((ArgumentEditor) table.getItem(myIndex + 1).getData()).getDescriptor() != descriptor);

        // update lists
        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()));
        }

        if (providerDescriptor instanceof VmInfoDescriptor) {
            VmInfoDescriptor vmd = (VmInfoDescriptor) providerDescriptor;
            try {
                AcquireSnapshotAction.AcquireDumpOperation.setupVmInfo(vmd.getVmInfo(), argumentSet);
            } catch (SnapshotException e) {
                // ignore - set later on generating the dump
            }
        }

        // inform about value changes
        fireValueChangedEvent();
    }

    private void fireInputChangedEvent() {
        synchronized (listeners) {
            for (ITableListener listener : listeners)
                listener.onInputChanged();
        }
    }

    private void fireValueChangedEvent() {
        synchronized (listeners) {
            for (ITableListener listener : listeners)
                listener.onValueChanged();
        }
    }

    public void onFocus(String message) {
        fireFocusChangedEvent(message);
    }

    public void onModeChange(Mode mode, ArgumentDescriptor descriptor) {
        // do nothing
    }

    public void onError(ArgumentEditor editor, String message) {
        synchronized (errors) {
            if (message == null) {
                errors.remove(editor);
                // There can be stale argument editors and errors, so remove them.
                // Perhaps better done on leaving a wizard page, but this works.
                for (Iterator<Map.Entry<ArgumentEditor, String>> i = errors.entrySet().iterator(); i.hasNext();) {
                    Map.Entry<ArgumentEditor, String> e = i.next();
                    if (e.getKey().isDisposed()) {
                        i.remove();
                    }
                }
                if (errors.isEmpty())
                    fireErrorMessageEvent(null);
                else
                    fireErrorMessageEvent(errors.values().iterator().next());
            } else {
                errors.put(editor, message);
                fireErrorMessageEvent(message);
            }
        }
    }

    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 providerSelected(AnnotatedObjectArgumentsSet newArgumentsSet) {
        if (newArgumentsSet == null) {
            // The provider has been deselected
            providerDescriptor = null;
            clearTable();
            argumentSet = null;
            context = null;
            return;
        }
        IAnnotatedObjectDescriptor newProviderDescriptor = newArgumentsSet.getDescriptor();
        if (!newProviderDescriptor.equals(providerDescriptor)) {
            // Obtain some default values in the table based on the current
            // values of the provider
            for (ArgumentDescriptor ad : newProviderDescriptor.getArguments()) {
                try {
                    Object defaultValue;
                    if (newProviderDescriptor instanceof HeapDumpProviderDescriptor) {
                        defaultValue = ad.getField()
                                .get(((HeapDumpProviderDescriptor) newProviderDescriptor).getHeapDumpProvider());
                    } else if (newProviderDescriptor instanceof VmInfoDescriptor) {
                        defaultValue = ad.getField().get(((VmInfoDescriptor) newProviderDescriptor).getVmInfo());
                    } else {
                        // Should never happen
                        defaultValue = null;
                    }
                    if (ad.isArray() && defaultValue != null) {
                        // internally, all multiple values have their values held as arrays
                        // therefore we convert the array once and for all
                        int size = Array.getLength(defaultValue);
                        List<Object> l = new ArrayList<Object>(size);
                        for (int ii = 0; ii < size; ii++) {
                            l.add(Array.get(defaultValue, ii));
                        }
                        ad.setDefaultValue(Collections.unmodifiableList(l));
                    } else {
                        ad.setDefaultValue(defaultValue);
                    }
                } catch (IllegalAccessException e) {
                }
            }
            providerDescriptor = newProviderDescriptor;

            clearTable();

            //            argumentSet = new ProviderArgumentsSet(providerDescriptor);
            argumentSet = newArgumentsSet;
            context = new ProviderContextImpl();
            createTableContent();
        }
        //        wizzardPage.updateDescription();
        fireInputChangedEvent();
    }

    private void clearTable() {
        table.removeAll(); // remove the table items

        /* remove all created editors */
        Control[] controls = table.getChildren();
        for (Control control : controls) {
            if (control instanceof ArgumentEditor)
                control.dispose();
        }
    }
}