org.eclipse.recommenders.internal.rcp.JavaElementSelections.java Source code

Java tutorial

Introduction

Here is the source code for org.eclipse.recommenders.internal.rcp.JavaElementSelections.java

Source

/**
 * Copyright (c) 2010, 2011 Darmstadt University of Technology.
 * 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:
 *    Marcel Bruch - initial API and implementation.
 */
package org.eclipse.recommenders.internal.rcp;

import static com.google.common.base.Optional.*;
import static org.eclipse.recommenders.internal.rcp.l10n.LogMessages.*;
import static org.eclipse.recommenders.rcp.JavaElementSelectionEvent.JavaElementSelectionLocation.*;
import static org.eclipse.recommenders.rcp.utils.JdtUtils.findTypeRoot;
import static org.eclipse.recommenders.utils.Checks.ensureIsNotNull;
import static org.eclipse.recommenders.utils.Logs.log;

import java.util.HashMap;
import java.util.Map;

import org.eclipse.jdt.core.IJavaElement;
import org.eclipse.jdt.core.IJavaProject;
import org.eclipse.jdt.core.ISourceRange;
import org.eclipse.jdt.core.ITypeRoot;
import org.eclipse.jdt.core.dom.ASTNode;
import org.eclipse.jdt.core.dom.CompilationUnit;
import org.eclipse.jdt.core.dom.FieldDeclaration;
import org.eclipse.jdt.core.dom.Initializer;
import org.eclipse.jdt.core.dom.MethodDeclaration;
import org.eclipse.jdt.core.dom.NodeFinder;
import org.eclipse.jdt.core.dom.StructuralPropertyDescriptor;
import org.eclipse.jdt.core.dom.TypeDeclaration;
import org.eclipse.jdt.core.dom.VariableDeclarationFragment;
import org.eclipse.jdt.internal.ui.javaeditor.JavaEditor;
import org.eclipse.jface.text.ITextSelection;
import org.eclipse.recommenders.rcp.JavaElementSelectionEvent.JavaElementSelectionLocation;
import org.eclipse.ui.IEditorPart;

import com.google.common.base.Optional;

/**
 * Utility class that resolves a selected java element from editor selection or structured selection.
 */
@SuppressWarnings("restriction")
public final class JavaElementSelections {

    private JavaElementSelections() {
        // Not meant to be instantiated
    }

    @SuppressWarnings("serial")
    private static final Map<StructuralPropertyDescriptor, JavaElementSelectionLocation> MAPPING = new HashMap<StructuralPropertyDescriptor, JavaElementSelectionLocation>() {
        {
            put(CompilationUnit.IMPORTS_PROPERTY, TYPE_DECLARATION);
            put(CompilationUnit.PACKAGE_PROPERTY, TYPE_DECLARATION);
            put(CompilationUnit.TYPES_PROPERTY, TYPE_DECLARATION);

            put(TypeDeclaration.BODY_DECLARATIONS_PROPERTY, TYPE_DECLARATION);
            put(TypeDeclaration.INTERFACE_PROPERTY, TYPE_DECLARATION);
            put(TypeDeclaration.JAVADOC_PROPERTY, TYPE_DECLARATION);
            put(TypeDeclaration.MODIFIERS2_PROPERTY, TYPE_DECLARATION);
            put(TypeDeclaration.NAME_PROPERTY, TYPE_DECLARATION);
            put(TypeDeclaration.SUPERCLASS_TYPE_PROPERTY, TYPE_DECLARATION_EXTENDS);
            put(TypeDeclaration.SUPER_INTERFACE_TYPES_PROPERTY, TYPE_DECLARATION_IMPLEMENTS);
            put(TypeDeclaration.TYPE_PARAMETERS_PROPERTY, UNKNOWN);

            put(MethodDeclaration.BODY_PROPERTY, METHOD_BODY);
            put(MethodDeclaration.CONSTRUCTOR_PROPERTY, METHOD_DECLARATION);
            put(MethodDeclaration.JAVADOC_PROPERTY, METHOD_DECLARATION);
            put(MethodDeclaration.MODIFIERS2_PROPERTY, METHOD_DECLARATION);
            put(MethodDeclaration.NAME_PROPERTY, METHOD_DECLARATION);
            put(MethodDeclaration.PARAMETERS_PROPERTY, METHOD_DECLARATION_PARAMETER);
            put(MethodDeclaration.RETURN_TYPE2_PROPERTY, METHOD_DECLARATION_RETURN);
            put(MethodDeclaration.THROWN_EXCEPTIONS_PROPERTY, METHOD_DECLARATION_THROWS);
            put(MethodDeclaration.TYPE_PARAMETERS_PROPERTY, UNKNOWN);

            put(Initializer.BODY_PROPERTY, METHOD_BODY);
            put(Initializer.MODIFIERS2_PROPERTY, METHOD_DECLARATION);

            put(FieldDeclaration.FRAGMENTS_PROPERTY, FIELD_DECLARATION_INITIALIZER);
            put(VariableDeclarationFragment.NAME_PROPERTY, FIELD_DECLARATION);
            put(FieldDeclaration.TYPE_PROPERTY, FIELD_DECLARATION);
            put(FieldDeclaration.JAVADOC_PROPERTY, FIELD_DECLARATION);
            put(FieldDeclaration.MODIFIERS2_PROPERTY, FIELD_DECLARATION);
        }
    };

    /**
     * Returns the {@link IJavaElement} at the current offset or {@link Optional#absent()} if resolving fails.
     */
    public static Optional<IJavaElement> resolveJavaElementFromEditor(final IEditorPart editor,
            final ITextSelection selection) {
        ensureIsNotNull(editor);
        ensureIsNotNull(selection);
        if (!isValidSelection(selection)) {
            return absent();
        }
        if (editor instanceof JavaEditor) {
            final JavaEditor javaEditor = (JavaEditor) editor;
            return resolveJavaElementFromEditor(javaEditor, selection.getOffset());
        }
        return absent();
    }

    private static boolean isValidSelection(final ITextSelection selection) {
        return selection.getOffset() != -1;
    }

    /**
     * Returns the {@link IJavaElement} at the given offset in the editor.
     *
     */
    public static Optional<IJavaElement> resolveJavaElementFromEditor(final JavaEditor editor, final int offset) {
        ensureIsNotNull(editor);
        ITypeRoot root = findTypeRoot(editor).orNull();
        if (root != null && root.exists()) {
            return resolveJavaElementFromTypeRootInEditor(root, offset);
        }
        return absent();
    }

    /**
     * Returns the {@link IJavaElement} at the given offset. If no {@link IJavaElement} is selected, the innermost
     * enclosing {@link IJavaElement} is returned (e.g., the declaring method or type). If both selection resolutions
     * fail, {@link Optional#absent()} is returned.
     */
    public static Optional<IJavaElement> resolveJavaElementFromTypeRootInEditor(final ITypeRoot root,
            final int offset) {
        ensureIsNotNull(root);
        try {
            if (isInvalidSelection(root, offset)) {
                return absent();
            }

            // try resolve elements at current offset
            final IJavaElement[] elements = root.codeSelect(offset, 0);
            if (elements.length > 0) {
                // return java element under cursor/selection start
                return of(elements[0]);
            } else {
                // XXX MB: decided against selection changes because these
                // frequent changes were too disturbing
                return absent();
                // ignore that for a while:

                // // if no java element has been selected, return the innermost
                // Java element enclosing a given offset.
                // // This might evaluate to null.
                // IJavaElement enclosingElement = root.getElementAt(offset);
                // if (enclosingElement == null) {
                // // selection occurred in empty space somewhere before the
                // type declaration.
                // // return type-root then.
                // enclosingElement = root;
                // }
                // return of(enclosingElement);
            }
        } catch (final Exception e) {
            // actually, these can happen when using snipmatch's in-editor completion.
            // fractions of seconds seem potentially to lead to this exception, thus, we swallow them here.
            if (!isInvalidSelection(root, offset)) {
                log(ERROR_FAILED_TO_RESOLVE_SELECTION, root.getHandleIdentifier(), offset, e);
            }
            return absent();
        }
    }

    private static boolean isInvalidSelection(ITypeRoot root, final int offset) {
        try {
            if (!root.exists()) {
                return true;
            }
            // check whether the type root is part of an package fragment root. If not, it's an invalid selection and
            // all resolutions are likely to fail. Thus, return true (=invalid):
            IJavaElement ancestor = root.getAncestor(IJavaProject.PACKAGE_FRAGMENT_ROOT);
            if (!ancestor.exists()) {
                return true;
            }
            ISourceRange range = root.getSourceRange();
            return range == null || offset < 0 || offset > range.getLength();
        } catch (Exception e) {
            log(ERROR_EXCEPTION_WHILE_CHECKING_OFFSETS, e);
            return false;
        }
    }

    public static JavaElementSelectionLocation resolveSelectionLocationFromAst(final CompilationUnit astRoot,
            final int offset) {
        ensureIsNotNull(astRoot);
        final ASTNode selectedNode = NodeFinder.perform(astRoot, offset, 0);
        if (selectedNode == null) {
            // this *should* never happen but it *can* happen...
            return JavaElementSelectionLocation.UNKNOWN;
        }
        return resolveSelectionLocationFromAstNode(selectedNode);
    }

    public static JavaElementSelectionLocation resolveSelectionLocationFromAstNode(final ASTNode node) {
        if (node == null) {
            return JavaElementSelectionLocation.UNKNOWN;
        }

        // handle a direct selection on a declaration node, i.e., the users
        // select a whitespace as in
        // "public $ void do(){}":
        // TODO Review: create second(?) mapping
        switch (node.getNodeType()) {
        case ASTNode.COMPILATION_UNIT:
        case ASTNode.TYPE_DECLARATION:
            return TYPE_DECLARATION;
        case ASTNode.METHOD_DECLARATION:
        case ASTNode.INITIALIZER:
            return METHOD_DECLARATION;
        case ASTNode.FIELD_DECLARATION:
            return FIELD_DECLARATION;
        default:
        }

        return resolveSelectionLocationFromNonMemberDeclarationNode(node);
    }

    /**
     * some inner node that is not a method, a type or a field declaration node...
     */
    private static JavaElementSelectionLocation resolveSelectionLocationFromNonMemberDeclarationNode(ASTNode node) {
        // deal with special case that no parent exists: for instance, if empty
        // spaces before the package declaration
        // are selected, we translate this to type declaration:
        ASTNode parent = node.getParent();
        if (parent == null) {
            return JavaElementSelectionLocation.TYPE_DECLARATION;
        }
        // we have a child node selected. Let's figure out which location this
        // translates best:
        while (node != null) {
            final StructuralPropertyDescriptor locationInParent = node.getLocationInParent();
            switch (parent.getNodeType()) {
            case ASTNode.VARIABLE_DECLARATION_FRAGMENT:
                if (isVariableNameSelectionInFieldDeclaration(parent, locationInParent)) {
                    return FIELD_DECLARATION;
                }
                break;
            case ASTNode.COMPILATION_UNIT:
            case ASTNode.TYPE_DECLARATION:
            case ASTNode.METHOD_DECLARATION:
            case ASTNode.FIELD_DECLARATION:
            case ASTNode.INITIALIZER:
                return mapLocationInParent(locationInParent);
            default:
                break;
            }
            node = parent;
            parent = parent.getParent();
        }
        return JavaElementSelectionLocation.UNKNOWN;
    }

    private static boolean isVariableNameSelectionInFieldDeclaration(final ASTNode parent,
            final StructuralPropertyDescriptor locationInParent) {
        final ASTNode superparent = parent.getParent();
        return superparent instanceof FieldDeclaration
                && VariableDeclarationFragment.NAME_PROPERTY == locationInParent;
    }

    private static JavaElementSelectionLocation mapLocationInParent(
            final StructuralPropertyDescriptor locationInParent) {
        final JavaElementSelectionLocation res = MAPPING.get(locationInParent);
        return res != null ? res : JavaElementSelectionLocation.UNKNOWN;
    }

    // TODO Review: rename method
    public static JavaElementSelectionLocation resolveSelectionLocationFromJavaElement(final IJavaElement element) {
        ensureIsNotNull(element);

        switch (element.getElementType()) {
        case IJavaElement.CLASS_FILE:
        case IJavaElement.COMPILATION_UNIT:
        case IJavaElement.PACKAGE_DECLARATION:
        case IJavaElement.IMPORT_DECLARATION:
        case IJavaElement.IMPORT_CONTAINER:
        case IJavaElement.TYPE:
            return TYPE_DECLARATION;
        case IJavaElement.METHOD:
        case IJavaElement.INITIALIZER:
            return METHOD_DECLARATION;
        case IJavaElement.FIELD:
            return FIELD_DECLARATION;
        case IJavaElement.LOCAL_VARIABLE:
            // shouldn't happen in a viewer selection, right?
            return METHOD_BODY;
        case IJavaElement.JAVA_MODEL:
        case IJavaElement.PACKAGE_FRAGMENT:
        case IJavaElement.PACKAGE_FRAGMENT_ROOT:
        case IJavaElement.ANNOTATION:
        default:
            return JavaElementSelectionLocation.UNKNOWN;
        }
    }
}