tinyos.dlrc.debug.views.NescVariablesViewer.java Source code

Java tutorial

Introduction

Here is the source code for tinyos.dlrc.debug.views.NescVariablesViewer.java

Source

/*
 * Dlrc 2, NesC development in Eclipse.
 * Copyright (C) 2009 DLRC
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 * 
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 * Web:  http://tos-ide.ethz.ch
 * Mail: tos-ide@tik.ee.ethz.ch
 */
package tinyos.dlrc.debug.views;

import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;

import org.eclipse.core.runtime.IAdaptable;
import org.eclipse.debug.core.DebugException;
import org.eclipse.debug.core.model.IStackFrame;
import org.eclipse.debug.core.model.IValue;
import org.eclipse.debug.core.model.IVariable;
import org.eclipse.debug.ui.AbstractDebugView;
import org.eclipse.debug.ui.DebugUITools;
import org.eclipse.debug.ui.contexts.DebugContextEvent;
import org.eclipse.debug.ui.contexts.IDebugContextListener;
import org.eclipse.jface.action.Action;
import org.eclipse.jface.action.IMenuListener;
import org.eclipse.jface.action.IMenuManager;
import org.eclipse.jface.action.IToolBarManager;
import org.eclipse.jface.action.MenuManager;
import org.eclipse.jface.action.Separator;
import org.eclipse.jface.viewers.DoubleClickEvent;
import org.eclipse.jface.viewers.IDoubleClickListener;
import org.eclipse.jface.viewers.ISelection;
import org.eclipse.jface.viewers.IStructuredContentProvider;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.jface.viewers.ITreeContentProvider;
import org.eclipse.jface.viewers.LabelProvider;
import org.eclipse.jface.viewers.TreePath;
import org.eclipse.jface.viewers.TreeViewer;
import org.eclipse.jface.viewers.Viewer;
import org.eclipse.jface.viewers.ViewerSorter;
import org.eclipse.swt.SWT;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Menu;
import org.eclipse.ui.ISharedImages;
import org.eclipse.ui.IWorkbenchActionConstants;
import org.eclipse.ui.PlatformUI;
import org.eclipse.ui.part.DrillDownAdapter;

import tinyos.dlrc.debug.NesCDebugIcons;
import tinyos.dlrc.debug.TinyOSDebugPlugin;
import tinyos.dlrc.debug.variables.IModuleDescriptor;
import tinyos.dlrc.debug.variables.internal.ModuleDescriptor;
import tinyos.dlrc.debug.variables.internal.NesCSeparatorFromCDTLaunch;
import tinyos.dlrc.debug.variables.internal.NesCVariableNameParser;
import tinyos.dlrc.debug.views.actions.ChangeValueAction;

/**
 * This viewer displays all NesC variables grouped by the components they are defined in.
 */

public class NescVariablesViewer extends AbstractDebugView implements IDebugContextListener {
    public static final String ASSIGNMENT = "=";

    private DrillDownAdapter drillDownAdapter;

    /*
     * The content provider class is responsible for
     * providing objects to the view. It can wrap
     * existing objects in adapters or simply return
     * objects as-is. These objects may be sensitive
     * to the current input of the view, or ignore
     * it and always show the same content 
     * (like Task List, for example).
     */

    public class TreeNode implements IAdaptable {
        private String name;
        private TreeParent parent;
        private Object data;

        public TreeNode(String name) {
            this.name = name;
        }

        public String getName() {
            return name;
        }

        public void setName(String name) {
            this.name = name;
        }

        public void setData(Object obj) {
            this.data = obj;
        }

        public void setParent(TreeParent parent) {
            this.parent = parent;
        }

        public TreeParent getParent() {
            return parent;
        }

        public String toString() {
            String res = getName();
            if (data instanceof IVariable) {
                IVariable var = (IVariable) data;
                try {
                    String value = var.getValue().getValueString();
                    res += " " + ASSIGNMENT + " " + value;
                } catch (DebugException e) {
                    TinyOSDebugPlugin.getDefault().log("Could not get value from variable", e);
                }
            }
            return res;
        }

        @SuppressWarnings("unchecked")
        public Object getAdapter(Class key) {
            if (key == IVariable.class && data instanceof IVariable) {
                return data;
            }
            if (key == IModuleDescriptor.class && data instanceof IModuleDescriptor) {
                return data;
            }
            return null;
        }

        public boolean containsModule() {
            return (data instanceof IModuleDescriptor);
        }

        public boolean containsVariable() {
            return (data instanceof IVariable);
        }
    }

    class TreeParent extends TreeNode {
        private ArrayList<TreeNode> children;

        public TreeParent(String name) {
            super(name);
            children = new ArrayList<TreeNode>();
        }

        public void addChild(TreeNode child) {
            children.add(child);
            child.setParent(this);
        }

        public void removeChild(TreeNode child) {
            children.remove(child);
            child.setParent(null);
        }

        public TreeNode[] getChildren() {
            return (TreeNode[]) children.toArray(new TreeNode[children.size()]);
        }

        public boolean hasChildren() {
            return children.size() > 0;
        }

        public TreeNode hasChild(String name) {
            for (TreeNode t : children) {
                if (t.getName().equals(name))
                    return t;
            }
            return null;
        }
    }

    class NescVariableContentProvider implements IStructuredContentProvider, ITreeContentProvider {
        private TreeParent invisibleRoot = new TreeParent("");
        private boolean isDisposed = false;

        public void inputChanged(Viewer v, Object oldInput, Object newInput) {
            if (newInput != oldInput) {
                populate(newInput);
            }
        }

        public void dispose() {
            isDisposed = true;
        }

        public boolean isDisposed() {
            return isDisposed;
        }

        // Build the tree using the given source.
        public Object[] getElements(Object source) {
            if (source != null) {
                populate(source);
                return getChildren(invisibleRoot);
            }
            return getChildren(source);
        }

        // Tree navigation functions
        public Object getParent(Object child) {
            if (child instanceof TreeNode) {
                return ((TreeNode) child).getParent();
            }
            return null;
        }

        public Object[] getChildren(Object parent) {
            if (parent instanceof TreeParent) {
                return ((TreeParent) parent).getChildren();
            }
            return new Object[0];
        }

        public boolean hasChildren(Object parent) {
            if (parent instanceof TreeParent)
                return ((TreeParent) parent).hasChildren();
            return false;
        }

        private void populate(Object o) {
            if (o instanceof IStackFrame) {
                IStackFrame f = (IStackFrame) o;
                NesCVariableNameParser varPars = new NesCVariableNameParser(
                        new NesCSeparatorFromCDTLaunch(f.getDebugTarget().getLaunch()));
                try {
                    if (f.hasVariables()) {
                        IVariable[] vars = f.getVariables();
                        populate(vars, varPars);
                    }
                } catch (DebugException e) {
                    TinyOSDebugPlugin.getDefault().log("Exception while populating viewer.", e);
                }
            } else {
                clear();
            }
        }

        private void clear() {
            invisibleRoot = new TreeParent("");
        }

        private void populate(IVariable[] variables, NesCVariableNameParser varPars) {
            HashMap<String, HashMap<String, IVariable>> modules = getModuleDescriptors(variables, varPars);
            for (TreeNode t : invisibleRoot.getChildren()) {
                if (!modules.containsKey(t.getName()))
                    invisibleRoot.removeChild(t);
            }
            for (String desc : modules.keySet()) {
                TreeNode p = invisibleRoot.hasChild(desc);
                if (p == null) {
                    p = new TreeParent(desc);
                    p.setData(new ModuleDescriptor(desc));
                    invisibleRoot.addChild(p);
                }
                if (p instanceof TreeParent) {
                    TreeParent parent = (TreeParent) p;
                    HashMap<String, IVariable> modVars = modules.get(desc);
                    for (TreeNode t : parent.getChildren()) {
                        if (!modVars.containsKey(t.getName()))
                            parent.removeChild(t);
                    }
                    Collection<IVariable> vars = modules.get(desc).values();
                    for (IVariable var : vars) {
                        addVariable(var, varPars, parent);
                    }
                }
            }
        }

        private void addVariable(IVariable var, NesCVariableNameParser varPars, TreeParent parent) {
            try {
                IValue val = var.getValue();
                String cVarName = var.getName();
                String name = varPars.getVariableName(cVarName);
                TreeNode node = parent.hasChild(name);
                if (val.hasVariables()) {
                    // var consists of multiple inner variables... (like for example a struct)
                    if (node == null) {
                        node = new TreeParent(name);
                        node.setData(var);
                        parent.addChild(node);
                    }
                    for (IVariable innerVar : val.getVariables()) {
                        addVariable(innerVar, varPars, (TreeParent) node);
                    }
                } else {
                    if (node == null) {
                        node = new TreeParent(name); // Leaf
                        node.setData(var);
                        parent.addChild(node);
                    }
                }

            } catch (DebugException e) {
                TinyOSDebugPlugin.getDefault().log("Exception while adding variable to tree.", e);
            }
        }

        private HashMap<String, HashMap<String, IVariable>> getModuleDescriptors(IVariable[] variables,
                NesCVariableNameParser varPars) {
            HashMap<String, HashMap<String, IVariable>> res = new HashMap<String, HashMap<String, IVariable>>();
            for (IVariable var : variables) {
                try {
                    if (varPars.isNesCVariable(var.getName())) {
                        String name = var.getName();
                        String desc = varPars.getModuleName(name);
                        String varName = varPars.getVariableName(name);
                        if (desc != null) {
                            HashMap<String, IVariable> modVars = res.get(desc);
                            if (modVars == null) {
                                modVars = new HashMap<String, IVariable>();
                                res.put(desc, modVars);
                            }
                            modVars.put(varName, var);
                        }
                    }
                } catch (DebugException e) {
                    TinyOSDebugPlugin.getDefault().log("Exception while extracting module descriptors.", e);
                }
            }
            return res;
        }
    }

    class ViewLabelProvider extends LabelProvider {

        public String getText(Object obj) {
            return obj.toString();
        }

        public Image getImage(Object obj) {
            if (obj instanceof TreeNode) {
                TreeNode to = (TreeNode) obj;
                IVariable var = (IVariable) to.getAdapter(IVariable.class);
                if (var != null) {
                    try {
                        if (var.getValue().hasVariables()) {
                            return NesCDebugIcons.get(NesCDebugIcons.ICON_VAR_AGGR);
                        } else {
                            return NesCDebugIcons.get(NesCDebugIcons.ICON_VAR_SIMPLE);
                        }
                    } catch (DebugException e) {
                        TinyOSDebugPlugin.getDefault().log("Exception while getting image.", e);
                    }
                } else if (to.getAdapter(IModuleDescriptor.class) != null) {
                    return NesCDebugIcons.get(NesCDebugIcons.ICON_COMPONENT);
                }
            }
            String imageKey = ISharedImages.IMG_OBJ_ELEMENT;
            return PlatformUI.getWorkbench().getSharedImages().getImage(imageKey);
        }
    }

    class NameSorter extends ViewerSorter {
        private String sub;
        private int index;
        private boolean indexed;

        @Override
        public int compare(Viewer viewer, Object e1, Object e2) {
            String s1 = e1.toString();
            String s2 = e2.toString();

            indexed(s1);
            if (!indexed)
                return super.compare(viewer, e1, e2);

            int i1 = index;
            s1 = sub;

            indexed(s2);
            if (!indexed)
                return super.compare(viewer, e1, e2);

            int i2 = index;
            s2 = sub;

            if (s1.equals(s2)) {
                return i1 - i2;
            } else {
                return super.compare(viewer, e1, e2);
            }
        }

        private void indexed(String string) {
            int indexEq = string.indexOf(ASSIGNMENT);
            if (indexEq >= 0)
                string = string.substring(0, indexEq);

            int beginB = string.indexOf("[");
            int endB = string.indexOf("]");

            if (beginB == -1 || endB == -1 || beginB + 1 >= endB) {
                beginB = string.indexOf("(");
                endB = string.indexOf(")");
            }

            if (beginB == -1 || endB == -1 || beginB + 1 >= endB) {
                indexed = false;
                return;
            }

            String sub = string.substring(beginB + 1, endB);
            try {
                index = Integer.valueOf(sub);
                indexed = true;
                this.sub = string.substring(0, beginB);
            } catch (NumberFormatException ex) {
                indexed = false;
            }
        }
    }

    /**
     * This is a callback that will allow us
     * to clean up when the viewer is closed.
     */
    public void dispose() {
        DebugUITools.getDebugContextManager().getContextService(getSite().getWorkbenchWindow())
                .removeDebugContextListener(this);
        super.dispose();
    }

    private void hookContextMenu(TreeViewer viewer) {
        MenuManager menuMgr = new MenuManager("#PopupMenu");
        menuMgr.setRemoveAllWhenShown(true);
        menuMgr.addMenuListener(new IMenuListener() {
            public void menuAboutToShow(IMenuManager manager) {
                NescVariablesViewer.this.fillContextMenu(manager);
            }
        });
        Menu menu = menuMgr.createContextMenu(viewer.getControl());
        viewer.getControl().setMenu(menu);
        getSite().registerContextMenu(menuMgr, viewer);
    }

    protected void fillContextMenu(IMenuManager manager) {
        manager.add(getAction("change_value"));
        manager.add(new Separator());
        drillDownAdapter.addNavigationActions(manager);
        // Other plug-ins can contribute there actions here
        manager.add(new Separator(IWorkbenchActionConstants.MB_ADDITIONS));
    }

    private void hookDoubleClickAction(TreeViewer viewer) {
        viewer.addDoubleClickListener(new IDoubleClickListener() {
            public void doubleClick(DoubleClickEvent event) {
                getAction("doubleClickAction").run();
            }
        });
    }

    @Override
    public void debugContextChanged(DebugContextEvent event) {
        ISelection selection = event.getContext();
        contextActivated(selection);
    }

    /**
     * Updates actions and sets the viewer input when a context is activated.
     * @param selection
     */
    protected void contextActivated(ISelection selection) {
        if (!isAvailable() || !isVisible()) {
            return;
        }
        if (selection instanceof IStructuredSelection) {
            Object source = ((IStructuredSelection) selection).getFirstElement();
            TreePath[] paths = ((TreeViewer) getViewer()).getExpandedTreePaths();
            this.getViewer().setInput(source);
            ((TreeViewer) getViewer()).setExpandedTreePaths(paths);
        }
    }

    @Override
    protected void configureToolBar(IToolBarManager manager) {
        //manager.add(getAction("action2"));
        manager.add(new Separator());
        drillDownAdapter.addNavigationActions(manager);
    }

    @Override
    protected void createActions() {
        Action changeValueAction = new ChangeValueAction(this);

        setAction("change_value", changeValueAction);
        setAction("doubleClickAction", changeValueAction);
    }

    @Override
    protected Viewer createViewer(Composite parent) {
        TreeViewer viewer = new TreeViewer(parent, SWT.MULTI | SWT.H_SCROLL | SWT.V_SCROLL);
        drillDownAdapter = new DrillDownAdapter(viewer);
        viewer.setContentProvider(new NescVariableContentProvider());
        viewer.setLabelProvider(new ViewLabelProvider());
        viewer.setSorter(new NameSorter());

        hookContextMenu(viewer);
        hookDoubleClickAction(viewer);

        DebugUITools.getDebugContextManager().getContextService(getSite().getWorkbenchWindow())
                .addDebugContextListener(this);

        return viewer;
    }

    @Override
    protected String getHelpContextId() {
        return "TinyOS_Debugger.viewer";
    }

    protected void becomesVisible() {
        super.becomesVisible();
        ISelection selection = DebugUITools.getDebugContextManager()
                .getContextService(getSite().getWorkbenchWindow()).getActiveContext();
        contextActivated(selection);
    }

    protected void becomesHidden() {
        super.becomesHidden();
    }
}