com.drgarbage.controlflowgraphfactory.actions.GenerateGraphAction.java Source code

Java tutorial

Introduction

Here is the source code for com.drgarbage.controlflowgraphfactory.actions.GenerateGraphAction.java

Source

/**
 * Copyright (c) 2008-2012, Dr. Garbage Community
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.drgarbage.controlflowgraphfactory.actions;

import java.io.StringWriter;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.List;

import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
import org.eclipse.jdt.core.Flags;
import org.eclipse.jdt.core.IJavaElement;
import org.eclipse.jdt.core.IJavaProject;
import org.eclipse.jdt.core.IMethod;
import org.eclipse.jdt.core.IType;
import org.eclipse.jdt.core.JavaModelException;
import org.eclipse.jdt.launching.JavaRuntime;
import org.eclipse.jface.action.IAction;
import org.eclipse.jface.viewers.ISelection;
import org.eclipse.jface.viewers.TreeSelection;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.ui.IActionDelegate;
import org.eclipse.ui.IObjectActionDelegate;
import org.eclipse.ui.IWorkbenchPage;
import org.eclipse.ui.IWorkbenchPart;

import com.drgarbage.controlflowgraphfactory.ControlFlowFactoryMessages;
import com.drgarbage.controlflowgraphfactory.ControlFlowFactoryPlugin;
import com.drgarbage.controlflowgraphfactory.dialogs.ExportGraphSaveAsDialog;
import com.drgarbage.controlflowgraphfactory.export.AbstractExport2;
import com.drgarbage.controlflowgraphfactory.export.ExportException;
import com.drgarbage.controlflowgraphfactory.export.GraphDOTExport;
import com.drgarbage.controlflowgraphfactory.export.GraphMlExport;
import com.drgarbage.controlflowgraphfactory.export.GraphXMLExport;
import com.drgarbage.core.CoreMessages;
import com.drgarbage.graph.GraphConstants;
import com.drgarbage.graph.IGraphSpecification;
import com.drgarbage.io.FileExtensions;
import com.drgarbage.utils.Messages;
import com.drgarbage.visualgraphic.model.ControlFlowGraphDiagram;

/**
 * Action for generation of graphs from the byte code
 *
 * @author Sergej Alekseev
 * @version $Revision$
 * $Id: GenerateGraphAction.java 1523 2012-04-13 14:34:24Z Sergej Alekseev $
 */
public abstract class GenerateGraphAction implements IObjectActionDelegate {
    private IGraphSpecification graphSpecification;

    private int graphType = GraphConstants.GRAPH_TYPE_SOURCE_GRAPH;

    /**
     * Active workbench page. 
     */
    private IWorkbenchPage page = null;

    /**
     * Current selection object.
     */
    private Object selection = null;

    public GenerateGraphAction(int graphType) {
        super();
        this.graphType = graphType;
    }

    /**
     * Process all parents from the selected node by calling getParent() method.
     * Tree                             | Interfaces
     * ---------------------------------+-----------
     * Project                          | IJavaElement, IJavaProject
     *   + Package                      | IJavaElement, IPackageFragment
     *     + Source: *.java or *.class  | IJavaElement
     *       + Class                    | IJavaElement, IType
     *         + Method               | IJavaElement, IMethod
     *         
     * Classpath for the selected class files can be resolved from the 
     * project tree node by calling getPath() method.
     * Classpath for the source files should be resolved via Java runtime,
     * because it can be different with the source path.
     * 
     */
    private void createControlFlowGraph(TreeSelection treeSel) {

        String mMethodName = null;
        String mMethodSignature = null;
        String mClassName = null;
        String mPackage = null;
        List<String> mPath = new ArrayList<String>();

        /* java model elements */
        IMethod iMethod = null;
        IJavaProject jp = null;

        try {

            /* Method Name */
            iMethod = (IMethod) treeSel.getFirstElement();

            if (!hasCode(iMethod)) {
                Messages.info(MessageFormat.format(CoreMessages.CannotGenerateGraph_MethodIsAnAbstractMethod,
                        new Object[] { iMethod.getElementName() }));
                return;

            }

            if (iMethod.isConstructor()) {
                mMethodName = "<init>";
            } else {
                mMethodName = iMethod.getElementName();
            }

            /** 
             * Method Signature:
             * NOTE: if class file is selected then the method signature is resolved. 
             */
            if (iMethod.isBinary()) {
                mMethodSignature = iMethod.getSignature();
            } else {
                try {
                    /* resolve parameter signature */
                    StringBuffer buf = new StringBuffer("(");
                    String[] parameterTypes = iMethod.getParameterTypes();
                    String res = null;
                    for (int i = 0; i < parameterTypes.length; i++) {
                        res = ActionUtils.getResolvedTypeName(parameterTypes[i], iMethod.getDeclaringType());
                        buf.append(res);
                    }
                    buf.append(")");

                    res = ActionUtils.getResolvedTypeName(iMethod.getReturnType(), iMethod.getDeclaringType());
                    buf.append(res);

                    mMethodSignature = buf.toString();

                } catch (IllegalArgumentException e) {
                    ControlFlowFactoryPlugin.getDefault().getLog()
                            .log(new Status(IStatus.ERROR, ControlFlowFactoryPlugin.PLUGIN_ID, e.getMessage(), e));
                    Messages.error(e.getMessage() + CoreMessages.ExceptionAdditionalMessage);
                    return;
                }
            }

            IType type = iMethod.getDeclaringType();
            mClassName = type.getFullyQualifiedName();

            mPackage = type.getPackageFragment().getElementName();
            mClassName = mClassName.replace(mPackage + ".", "");

            if (iMethod.isBinary()) {
                /* Classpath for selected class files */
                mPath.add(type.getPackageFragment().getPath().toString());
            }

            /* Classpath for selected source code files */
            jp = iMethod.getJavaProject();
            try {
                String[] str = JavaRuntime.computeDefaultRuntimeClassPath(jp);
                for (int i = 0; i < str.length; i++) {
                    mPath.add(str[i]);
                }
            } catch (CoreException e) {
                ControlFlowFactoryPlugin.getDefault().getLog()
                        .log(new Status(IStatus.ERROR, ControlFlowFactoryPlugin.PLUGIN_ID, e.getMessage(), e));
                Messages.error(e.getMessage() + CoreMessages.ExceptionAdditionalMessage);
                return;
            }

        } catch (ClassCastException e) {
            ControlFlowFactoryPlugin.getDefault().getLog()
                    .log(new Status(IStatus.ERROR, ControlFlowFactoryPlugin.PLUGIN_ID, e.getMessage(), e));
            Messages.error(e.getMessage() + CoreMessages.ExceptionAdditionalMessage);
            return;
        } catch (JavaModelException e) {
            ControlFlowFactoryPlugin.getDefault().getLog()
                    .log(new Status(IStatus.ERROR, ControlFlowFactoryPlugin.PLUGIN_ID, e.getMessage(), e));
            Messages.error(e.getMessage() + CoreMessages.ExceptionAdditionalMessage);
            return;
        }

        /* convert classpath to String array */
        String[] classPath = new String[mPath.size()];
        for (int i = 0; i < mPath.size(); i++) {
            classPath[i] = mPath.get(i);
        }

        /* create control flow graph diagram */
        final ControlFlowGraphDiagram controlFlowGraphDiagram = createDiagram(classPath, mPackage, mClassName,
                mMethodName, mMethodSignature);

        if (controlFlowGraphDiagram == null) {
            Messages.warning(ControlFlowFactoryMessages.DiagramIsNullMessage);
            return;
        }

        /* create empty shell */
        Shell shell = page.getActivePart().getSite().getShell();

        /* Show a SaveAs dialog */
        ExportGraphSaveAsDialog dialog = new ExportGraphSaveAsDialog(shell);

        try {
            IPath path = jp.getCorrespondingResource().getFullPath();
            if (iMethod.isConstructor()) {
                /* use class name for constructor */
                path = path.append(IPath.SEPARATOR + mClassName + "." + mClassName + "."
                        + GraphConstants.graphTypeSuffixes[getGraphType()]);
            } else {
                path = path.append(IPath.SEPARATOR + mClassName + "." + mMethodName + "."
                        + GraphConstants.graphTypeSuffixes[getGraphType()]);
            }
            path = path.addFileExtension(FileExtensions.GRAPH);

            /* get file and set in the dialog */
            IFile file = ResourcesPlugin.getWorkspace().getRoot().getFile(path);
            dialog.setOriginalFile(file);

        } catch (JavaModelException e) {
            ControlFlowFactoryPlugin.getDefault().getLog()
                    .log(new Status(IStatus.ERROR, ControlFlowFactoryPlugin.PLUGIN_ID, e.getMessage(), e));
            Messages.error(e.getMessage() + CoreMessages.ExceptionAdditionalMessage);
            return;
        }

        /* open SaveAS dialog */
        dialog.open();

        IPath path = dialog.getResult();
        if (path == null) {/* action canceled */
            return;
        }

        graphSpecification = dialog.getGraphSpecification();

        /* convert if necessary and start an editor */
        switch (graphSpecification.getExportFormat()) {
        case GraphConstants.EXPORT_FORMAT_DRGARBAGE_GRAPH:
            ActionUtils.saveDiagramInFileAndOpenEditor(path, shell, page, controlFlowGraphDiagram,
                    graphSpecification.isOpenInEditor());
            break;
        default:

            AbstractExport2 exporter = null;

            switch (graphSpecification.getExportFormat()) {
            case GraphConstants.EXPORT_FORMAT_DOT:
                exporter = new GraphDOTExport();
                break;
            case GraphConstants.EXPORT_FORMAT_GRAPHXML:
                exporter = new GraphXMLExport();
                break;
            case GraphConstants.EXPORT_FORMAT_GRAPHML:
                exporter = new GraphMlExport();
                break;
            default:
                throw new IllegalStateException(
                        "Unexpected export format '" + graphSpecification.getExportFormat() + "'.");
            }
            exporter.setGraphSpecification(graphSpecification);
            StringWriter sb = new StringWriter();
            try {
                exporter.write(controlFlowGraphDiagram, sb);
            } catch (ExportException e) {
                /* This will never happen as
                 * StringBuilder.append(*) does not throw IOException*/
                throw new RuntimeException(e);
            }
            ActionUtils.saveContentInFileAndOpenEditor(path, shell, page, sb.toString(),
                    graphSpecification.isOpenInEditor());
            break;

        }

        dialog = null;
    }

    /**
     * Creates the diagrapm
     */
    abstract protected ControlFlowGraphDiagram createDiagram(String[] classPath, String mPackage, String mClassName,
            String mMethodName, String mMethodSignature);

    public IGraphSpecification getGraphSpecification() {
        return graphSpecification;
    }

    /**
     * Gets graph type: <code>BYTECODE_GRAPH</code>, <code>BASICBLOCK_GRAPH</code> 
     * or <code>SOURCE_GRAPH</code>.
     * @return graph type
     */
    public int getGraphType() {
        return graphType;
    }

    /**
     * Returns true if the method has code.
     * @param method
     * @return true or false
     * @throws JavaModelException
     */
    public boolean hasCode(IMethod method) throws JavaModelException {

        int flags = method.getFlags();

        if (Flags.isAbstract(flags)) {
            return false;
        }

        IJavaElement parent = method.getParent();

        if (parent.getElementType() == IJavaElement.TYPE) {
            IType type = (IType) parent;
            if (type.isInterface() || type.isEnum() || type.isAnnotation()) {
                return false;
            }
        }

        return true;
    }

    /**
     * @see IActionDelegate#run(IAction)
     */
    public void run(IAction action) {
        if (selection instanceof TreeSelection) {
            createControlFlowGraph((TreeSelection) selection);
        } else {
            Messages.error("TreeSelection-Interface is not compartible.");
        }
    }

    /**
     * @see IActionDelegate#selectionChanged(IAction, ISelection)
     */
    public void selectionChanged(IAction action, ISelection selection) {
        this.selection = selection;
    }

    /**
     * @see IObjectActionDelegate#setActivePart(IAction, IWorkbenchPart)
     */
    public void setActivePart(IAction action, IWorkbenchPart targetPart) {

        page = targetPart.getSite().getWorkbenchWindow().getActivePage();
    }

    /**
     *    Sets graph type. The valid values are: <code>BYTECODE_GRAPH</code>, 
     * <code>BASICBLOCK_GRAPH</code> or <code>SOURCE_GRAPH</code>.
     * @param graph type
     */
    public void setGraphType(int graphType) {
        this.graphType = graphType;
    }
}