ca.uvic.chisel.javasketch.ui.internal.presentation.ASTMessageGrouper.java Source code

Java tutorial

Introduction

Here is the source code for ca.uvic.chisel.javasketch.ui.internal.presentation.ASTMessageGrouper.java

Source

/*******************************************************************************
 * Copyright (c) 2009 the CHISEL group and contributors.
 * 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:
 *     Del Myers - initial API and implementation
 *******************************************************************************/
package ca.uvic.chisel.javasketch.ui.internal.presentation;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;

import org.eclipse.core.runtime.IAdaptable;
import org.eclipse.jdt.core.dom.ASTNode;
import org.eclipse.jdt.core.dom.CatchClause;
import org.eclipse.jdt.core.dom.DoStatement;
import org.eclipse.jdt.core.dom.EnhancedForStatement;
import org.eclipse.jdt.core.dom.ForStatement;
import org.eclipse.jdt.core.dom.IfStatement;
import org.eclipse.jdt.core.dom.Statement;
import org.eclipse.jdt.core.dom.WhileStatement;
import org.eclipse.jface.preference.IPreferenceStore;
import org.eclipse.swt.graphics.Color;
import org.eclipse.zest.custom.uml.viewers.IMessageGrouper;
import org.eclipse.zest.custom.uml.viewers.IMessageGrouping;
import org.eclipse.zest.custom.uml.viewers.MessageGrouping;
import org.eclipse.zest.custom.uml.viewers.UMLSequenceViewer;

import ca.uvic.chisel.javasketch.IProgramSketch;
import ca.uvic.chisel.javasketch.SketchPlugin;
import ca.uvic.chisel.javasketch.data.model.IActivation;
import ca.uvic.chisel.javasketch.data.model.IMessage;
import ca.uvic.chisel.javasketch.internal.ast.groups.ASTMessageGroupingTree;
import ca.uvic.chisel.javasketch.ui.ISketchColorConstants;
import ca.uvic.chisel.javasketch.ui.internal.preferences.ISketchPluginPreferences;
import ca.uvic.chisel.javasketch.ui.internal.presentation.metadata.PresentationData;

/**
 * @author Del Myers
 *
 */
public class ASTMessageGrouper implements IMessageGrouper {

    public static class ASTMessageGrouping extends MessageGrouping
            implements Comparable<ASTMessageGrouping>, IAdaptable {

        private ASTMessageGroupingTree node;

        /**
         * @param activationElement
         */
        public ASTMessageGrouping(Object activationElement, ASTMessageGroupingTree node) {
            super(activationElement);
            this.node = node;
        }

        /* (non-Javadoc)
         * @see java.lang.Comparable#compareTo(java.lang.Object)
         */
        @Override
        public int compareTo(ASTMessageGrouping that) {
            //first, compare by indexes
            if (this.getOffset() < that.getOffset()) {
                return -1;
            } else if (this.getOffset() > that.getOffset()) {
                return 1;
            } else {
                //the offsets are equal, the smaller one is the
                //one with the smaller length
                if (this.getLength() < that.getLength()) {
                    return -1;
                } else if (this.getLength() > that.getLength()) {
                    return 1;
                } else {
                    //the only other option is that they cover the same
                    //messages, use the AST to decide which one is 
                    //internal
                    if (this.node.getNode().getStartPosition() < that.node.getNode().getStartPosition()) {
                        return -1;
                    } else if (this.node.getNode().getStartPosition() > that.node.getNode().getStartPosition()) {
                        return 1;
                    } else {
                        if (this.node.getNode().getLength() < that.node.getNode().getLength()) {
                            return -1;
                        } else if (this.node.getNode().getLength() > that.node.getNode().getLength()) {
                            return 1;
                        }
                    }
                }
            }

            //can't decide which is greater.
            return 0;
        }

        /* (non-Javadoc)
         * @see org.eclipse.core.runtime.IAdaptable#getAdapter(java.lang.Class)
         */
        @SuppressWarnings("unchecked")
        @Override
        public Object getAdapter(Class adapter) {
            if (adapter.isAssignableFrom(ASTMessageGroupingTree.class)) {
                return node;
            }
            return null;
        }

        /**
         * @return the node
         */
        public ASTMessageGroupingTree getNode() {
            return node;
        }

    }

    /* (non-Javadoc)
     * @see org.eclipse.zest.custom.uml.viewers.IMessageGrouper#dispose()
     */
    @Override
    public void dispose() {

    }

    /* (non-Javadoc)
     * @see org.eclipse.zest.custom.uml.viewers.IMessageGrouper#calculateGroups(org.eclipse.zest.custom.uml.viewers.UMLSequenceViewer, java.lang.Object, java.lang.Object[])
     */
    @Override
    public IMessageGrouping[] calculateGroups(UMLSequenceViewer viewer, Object activationElement,
            Object[] children) {
        //      if (true) {
        //         return calculateGroups2(viewer, activationElement, children);
        //      }
        HashMap<ASTMessageGroupingTree, ASTMessageGrouping> groups = new HashMap<ASTMessageGroupingTree, ASTMessageGrouping>();
        IPreferenceStore store = SketchPlugin.getDefault().getPreferenceStore();
        LinkedList<ASTMessageGroupingTree> unusedLoops = new LinkedList<ASTMessageGroupingTree>();
        LinkedList<ASTMessageGroupingTree> unusedGroups = new LinkedList<ASTMessageGroupingTree>();

        boolean compactLoops = store.getBoolean(ISketchPluginPreferences.COMPACT_LOOPS_PREFERENCE);
        boolean useCombinedFragments = store.getBoolean(ISketchPluginPreferences.DISPLAY_GROUPS_PREFERENCE);
        if (!useCombinedFragments) {
            return new IMessageGrouping[0];
        }
        if (activationElement instanceof IActivation) {
            IActivation parent = (IActivation) activationElement;
            IProgramSketch sketch = SketchPlugin.getDefault().getSketch(parent);
            if (sketch != null) {
                PresentationData pd = PresentationData.connect(sketch);
                if (pd != null) {
                    try {
                        ASTMessageGroupingTree tree = pd.getGroups(parent);

                        if (tree == null) {
                            return new IMessageGrouping[0];
                        }

                        //search through the tree to find all loops
                        if (compactLoops) {
                            unusedGroups.add(tree);
                            while (unusedGroups.size() > 0) {
                                ASTMessageGroupingTree node = unusedGroups.removeFirst();
                                if (pd.isGroupVisible(parent, node)) {
                                    if (node.isLoop()) {
                                        unusedLoops.add(node);
                                    }
                                    for (ASTMessageGroupingTree child : node.getChildren()) {
                                        unusedGroups.add(child);
                                    }
                                }

                            }
                        }
                        for (int i = 0; i < children.length; i++) {
                            Object child = children[i];
                            if (child instanceof IMessage) {
                                ASTMessageGroupingTree node = tree.getMessageContainer((IMessage) child);
                                //put in the node and all of its parents.

                                while (node != null && node.getParent() != null) {
                                    ASTMessageGrouping grouping = groups.get(node);
                                    if (grouping == null) {
                                        //                              if (compactLoops && node.isLoop()) {
                                        grouping = new ASTMessageGrouping(activationElement, node);
                                        grouping.setOffset(i);
                                        groups.put(node, grouping);
                                        //                              } else if (useCombinedFragments && !node.isLoop()) {
                                        //                                 grouping = new ASTMessageGrouping(activationElement, node);
                                        //                                 grouping.setOffset(i);
                                        //                                 groups.put(node, grouping);
                                        //                              }
                                    }
                                    if (grouping != null) {
                                        grouping.setLength(i - grouping.getOffset() + 1);
                                    }
                                    node = node.getParent();
                                }
                            }
                        }
                    } finally {
                        pd.disconnect();
                    }
                }
            }
        }
        for (ASTMessageGroupingTree node : groups.keySet()) {
            updateGrouping(groups.get(node), node.getNode());
        }
        ASTMessageGrouping[] result = groups.values().toArray(new ASTMessageGrouping[groups.values().size()]);
        Arrays.sort(result);
        return result;
    }

    public IMessageGrouping[] calculateGroups2(UMLSequenceViewer viewer, Object activationElement,
            Object[] children) {
        HashMap<ASTMessageGroupingTree, ASTMessageGrouping> groups = new HashMap<ASTMessageGroupingTree, ASTMessageGrouping>();
        IPreferenceStore store = SketchPlugin.getDefault().getPreferenceStore();
        ArrayList<ASTMessageGrouping> unusedGroups = new ArrayList<ASTMessageGrouper.ASTMessageGrouping>();
        boolean compactLoops = store.getBoolean(ISketchPluginPreferences.COMPACT_LOOPS_PREFERENCE);
        boolean useCombinedFragments = store.getBoolean(ISketchPluginPreferences.DISPLAY_GROUPS_PREFERENCE);
        if (!useCombinedFragments) {
            return new IMessageGrouping[0];
        }
        if (activationElement instanceof IActivation) {
            IActivation parent = (IActivation) activationElement;
            IProgramSketch sketch = SketchPlugin.getDefault().getSketch(parent);
            if (sketch != null) {
                PresentationData pd = PresentationData.connect(sketch);
                if (pd != null) {
                    try {
                        ASTMessageGroupingTree tree = pd.getGroups(parent);

                        if (tree == null) {
                            return new IMessageGrouping[0];
                        }
                        LinkedList<ASTMessageGroupingTree> groupStack = new LinkedList<ASTMessageGroupingTree>();

                        //search through the tree to find all loops
                        groupStack.add(tree);
                        while (groupStack.size() > 0) {
                            ASTMessageGroupingTree node = groupStack.removeFirst();
                            if (pd.isGroupVisible(parent, node) || (!compactLoops && node.isLoop())) {
                                ASTMessageGrouping g = new ASTMessageGrouping(activationElement, node);
                                g.setOffset(-1);
                                g.setLength(0);
                                groups.put(node, g);
                                unusedGroups.add(g);
                                for (ASTMessageGroupingTree child : node.getChildren()) {
                                    groupStack.add(child);
                                }
                            }
                        }

                        int unusedIndex = 0;
                        for (int i = 0; i < children.length; i++) {
                            Object child = children[i];
                            if (child instanceof IMessage) {
                                ASTMessageGroupingTree node = tree.getMessageContainer((IMessage) child);
                                ASTMessageGrouping grouping = groups.get(node);
                                int messageLineNumber = ((IMessage) child).codeLine();
                                if (grouping != null) {
                                    if (grouping.getOffset() < 0) {
                                        //update all the preceding offsets to
                                        //be equal to this one.
                                        for (int u = unusedIndex; u < unusedGroups.size(); unusedIndex++, u++) {
                                            ASTMessageGrouping unused = unusedGroups.get(u);
                                            if (unused.node.getFirstCodeLine() <= messageLineNumber) {
                                                unused.setOffset(i);
                                            } else {
                                                unusedIndex = u;
                                                break;
                                            }
                                        }
                                    }
                                }
                                //update all the parents for the length
                                while (node != null && node.getParent() != null) {
                                    grouping = groups.get(node);
                                    if (grouping != null) {
                                        grouping.setLength(i - grouping.getOffset() + 1);
                                    }
                                    node = node.getParent();
                                }
                            }
                        }
                        for (int i = unusedIndex; i < unusedGroups.size(); i++) {
                            ASTMessageGrouping unused = unusedGroups.get(i);
                            unused.setOffset(children.length);
                        }
                    } finally {
                        pd.disconnect();
                    }
                }
            }
        }
        for (Iterator<ASTMessageGrouping> i = unusedGroups.iterator(); i.hasNext();) {
            ASTMessageGrouping group = i.next();
            if (group.node.getNode().getNodeType() == ASTNode.METHOD_DECLARATION) {
                i.remove();
            } else if (group.getLength() <= 0) {
                //remove non-loop elements
                if (!group.node.isLoop()) {
                    i.remove();
                } else {
                    //walk up the list and remove children of empty
                    //loops
                    ASTMessageGroupingTree parent = group.node.getParent();

                    boolean remove = false;
                    while (parent != null) {
                        ASTMessageGrouping grouping = groups.get(parent);
                        if (grouping.node.isLoop() && grouping.getLength() <= 0) {
                            remove = true;
                            break;
                        }
                        parent = parent.getParent();
                    }
                    if (remove) {
                        i.remove();
                    }
                }
            }
        }

        for (ASTMessageGrouping group : unusedGroups) {
            updateGrouping(group, group.node.getNode());
        }
        return unusedGroups.toArray(new ASTMessageGrouping[unusedGroups.size()]);
        //return result;
    }

    /**
     * Updates labels and colours for the grouping.
     * @param currentGrouping
     */
    private void updateGrouping(ASTMessageGrouping grouping, ASTNode node) {
        String text = "";
        int i;
        Color bg = null;
        Color fg = null;
        switch (node.getNodeType()) {
        case ASTNode.IF_STATEMENT:
            IfStatement ifStatement = (IfStatement) node;
            text = "if (" + ifStatement.getExpression().toString() + ")";
            //it could be an else-if, make sure
            if (ifStatement.getParent().getNodeType() == ASTNode.IF_STATEMENT) {
                if (ifStatement.equals(((IfStatement) ifStatement.getParent()).getElseStatement())) {
                    text = "else " + text;
                }
            }
            fg = ISketchColorConstants.CONDITION_FG;
            bg = ISketchColorConstants.CONDITION_BG;
            break;
        case ASTNode.WHILE_STATEMENT:
            WhileStatement whileStatement = (WhileStatement) node;
            text = "while (" + whileStatement.getExpression().toString() + ")";
            fg = ISketchColorConstants.LOOP_FG;
            bg = ISketchColorConstants.LOOP_BG;
            break;
        case ASTNode.DO_STATEMENT:
            DoStatement doStatement = (DoStatement) node;
            text = "do..while (" + doStatement.getExpression().toString() + ")";
            fg = ISketchColorConstants.LOOP_FG;
            bg = ISketchColorConstants.LOOP_BG;
            break;
        case ASTNode.FOR_STATEMENT:
            ForStatement forStatement = (ForStatement) node;
            List<?> initializers = forStatement.initializers();
            List<?> updaters = forStatement.updaters();
            text = "for (";
            for (i = 0; i < initializers.size(); i++) {
                text += initializers.get(i).toString();
                if (i < initializers.size() - 1) {
                    text += ",";
                }
            }
            text += ";";
            if (forStatement.getExpression() != null) {
                text += forStatement.getExpression();
            }
            text += ";";
            for (i = 0; i < updaters.size(); i++) {
                text += updaters.get(i).toString();
                if (i < updaters.size() - 1) {
                    text += ",";
                }
            }
            text += ")";
            fg = ISketchColorConstants.LOOP_FG;
            bg = ISketchColorConstants.LOOP_BG;
            break;
        case ASTNode.ENHANCED_FOR_STATEMENT:
            EnhancedForStatement enhancedForStatement = (EnhancedForStatement) node;
            text = "for (" + enhancedForStatement.getExpression().toString() + ")";
            fg = ISketchColorConstants.LOOP_FG;
            bg = ISketchColorConstants.LOOP_BG;
            break;
        case ASTNode.TRY_STATEMENT:
            text = "try";
            fg = ISketchColorConstants.ERROR_FG;
            bg = ISketchColorConstants.ERROR_BG;
            break;
        case ASTNode.CATCH_CLAUSE:
            CatchClause catchClause = (CatchClause) node;
            text = "catch (" + catchClause.getException().toString() + ")";
            fg = ISketchColorConstants.ERROR_FG;
            bg = ISketchColorConstants.ERROR_BG;
            break;
        default:
            //get the else blocks
            if (node instanceof Statement) {
                Statement statement = (Statement) node;
                if (statement.getParent() instanceof IfStatement) {
                    if (((IfStatement) statement.getParent()).getElseStatement() == statement) {
                        text = "else";
                        fg = ISketchColorConstants.CONDITION_FG;
                        bg = ISketchColorConstants.CONDITION_BG;
                    }
                }
            }
            break;
        }
        if (grouping.node.isLoop()) {
            ASTMessageGroupingTree[] siblings = grouping.node.getSiblings();
            text = text + "[" + grouping.node.getIteration() + " of " + (siblings.length + 1) + "]";
        }

        grouping.setName(text);
        grouping.setForeground(fg);
        grouping.setBackground(bg);
    }

}