org.eclipse.recommenders.utils.rcp.JdtUtils.java Source code

Java tutorial

Introduction

Here is the source code for org.eclipse.recommenders.utils.rcp.JdtUtils.java

Source

/**
 * Copyright (c) 2010 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.utils.rcp;

import static com.google.common.base.Optional.*;
import static org.eclipse.jdt.internal.corext.util.JdtFlags.*;
import static org.eclipse.jdt.ui.SharedASTProvider.*;
import static org.eclipse.recommenders.utils.Checks.*;
import static org.eclipse.recommenders.utils.Throws.*;
import static org.eclipse.recommenders.utils.rcp.internal.RecommendersUtilsPlugin.logError;

import java.io.File;
import java.util.Collection;
import java.util.LinkedHashMap;

import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.StringUtils;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.runtime.IPath;
import org.eclipse.jdt.core.IField;
import org.eclipse.jdt.core.IJavaElement;
import org.eclipse.jdt.core.IJavaProject;
import org.eclipse.jdt.core.ILocalVariable;
import org.eclipse.jdt.core.IMember;
import org.eclipse.jdt.core.IMethod;
import org.eclipse.jdt.core.IPackageFragmentRoot;
import org.eclipse.jdt.core.IType;
import org.eclipse.jdt.core.ITypeHierarchy;
import org.eclipse.jdt.core.ITypeParameter;
import org.eclipse.jdt.core.ITypeRoot;
import org.eclipse.jdt.core.Signature;
import org.eclipse.jdt.core.dom.ASTNode;
import org.eclipse.jdt.core.dom.CompilationUnit;
import org.eclipse.jdt.core.dom.IMethodBinding;
import org.eclipse.jdt.core.dom.MethodDeclaration;
import org.eclipse.jdt.internal.compiler.lookup.Binding;
import org.eclipse.jdt.internal.compiler.lookup.FieldBinding;
import org.eclipse.jdt.internal.compiler.lookup.MethodBinding;
import org.eclipse.jdt.internal.compiler.lookup.TypeBinding;
import org.eclipse.jdt.internal.compiler.lookup.VariableBinding;
import org.eclipse.jdt.internal.core.JavaElement;
import org.eclipse.jdt.internal.core.LocalVariable;
import org.eclipse.jdt.internal.core.util.Util;
import org.eclipse.jdt.internal.corext.dom.NodeFinder;
import org.eclipse.jdt.internal.corext.util.JavaModelUtil;
import org.eclipse.jdt.internal.corext.util.MethodOverrideTester;
import org.eclipse.jdt.internal.corext.util.SuperTypeHierarchyCache;
import org.eclipse.jdt.internal.ui.javaeditor.EditorUtility;
import org.eclipse.jdt.internal.ui.javaeditor.JavaEditor;
import org.eclipse.jface.text.IRegion;
import org.eclipse.jface.text.ITextSelection;
import org.eclipse.jface.text.Region;
import org.eclipse.recommenders.utils.annotations.Nullable;
import org.eclipse.recommenders.utils.names.ITypeName;
import org.eclipse.recommenders.utils.names.VmTypeName;
import org.eclipse.recommenders.utils.rcp.internal.RecommendersUtilsPlugin;
import org.eclipse.ui.IEditorPart;
import org.eclipse.ui.IWorkbenchPage;

import com.google.common.base.Optional;
import com.google.common.base.Predicate;

@SuppressWarnings({ "restriction", "unchecked", "deprecation" })
public class JdtUtils {
    private static final Util.BindingsToNodesMap EMPTY_NODE_MAP = new Util.BindingsToNodesMap() {
        @Override
        public org.eclipse.jdt.internal.compiler.ast.ASTNode get(final Binding binding) {
            return null;
        }
    };
    private static final IJavaElement[] EMPTY_RESULT = new IJavaElement[0];

    private static Predicate<IField> STATIC_PUBLIC_FIELDS_ONLY_FILTER = new Predicate<IField>() {

        @Override
        public boolean apply(final IField m) {
            try {
                // filter these:
                return !isStatic(m) || !isPublic(m);
            } catch (final Exception e) {
                // filter!
                return true;
            }
        }
    };
    private static Predicate<IField> PUBLIC_FIELDS_ONLY_FILTER = new Predicate<IField>() {

        @Override
        public boolean apply(final IField m) {
            try {
                // filter these:
                return isStatic(m) || !isPublic(m);
            } catch (final Exception e) {
                // filter!
                return true;
            }
        }
    };
    private static Predicate<IMethod> STATIC_PUBLIC_METHODS_ONLY_FILTER = new Predicate<IMethod>() {

        @Override
        public boolean apply(final IMethod m) {
            try {
                // filter these:
                return !isStatic(m) || !isPublic(m);
            } catch (final Exception e) {
                // filter!
                return true;
            }
        }
    };

    private static Predicate<IMethod> PUBLIC_INSTANCE_METHODS_ONLY_FILTER = new Predicate<IMethod>() {

        @Override
        public boolean apply(final IMethod m) {
            try {
                // filter these:
                return isStatic(m) || !isPublic(m) || m.isConstructor();
            } catch (final Exception e) {
                // filter!
                return true;
            }
        }
    };

    private static Predicate<IMethod> STATIC_NON_VOID_NON_PRIMITIVE_PUBLIC_METHODS_FILTER = new Predicate<IMethod>() {

        @Override
        public boolean apply(final IMethod m) {
            try {
                // filter these:
                return !isStatic(m) || isVoid(m) || !isPublic(m) || hasPrimitiveReturnType(m);
            } catch (final Exception e) {
                // filter!
                return true;
            }
        }
    };

    private static String createFieldKey(final IField field) {
        try {
            return field.getElementName() + field.getTypeSignature();
        } catch (final Exception e) {
            throw throwUnhandledException(e);
        }
    }

    private static String createMethodKey(final IMethod method) {
        try {
            final String signature = method.getSignature();
            final String signatureWithoutReturnType = StringUtils.substringBeforeLast(signature, ")");
            final String methodName = method.getElementName();
            return methodName + signatureWithoutReturnType;
        } catch (final Exception e) {
            throw throwUnhandledException(e);
        }
    }

    public static IRegion createRegion(final ASTNode node) {
        ensureIsNotNull(node);
        return new Region(node.getStartPosition(), node.getLength());
    }

    public static Optional<IField> createUnresolvedField(final FieldBinding compilerBinding) {
        ensureIsNotNull(compilerBinding);
        final IField f = (IField) Util.getUnresolvedJavaElement(compilerBinding, null, EMPTY_NODE_MAP);
        return fromNullable(f);
    }

    public static ILocalVariable createUnresolvedLocaVariable(final VariableBinding compilerBinding,
            final JavaElement parent) {
        ensureIsNotNull(compilerBinding);
        ensureIsNotNull(parent);

        final String name = new String(compilerBinding.name);
        final String type = new String(compilerBinding.type.signature());
        return new LocalVariable(parent, name, 0, 0, 0, 0, type, null, compilerBinding.modifiers,
                compilerBinding.isParameter());
    }

    public static Optional<IMethod> createUnresolvedMethod(final MethodBinding compilerBinding) {
        ensureIsNotNull(compilerBinding);
        final IMethod m = (IMethod) Util.getUnresolvedJavaElement(compilerBinding, null, EMPTY_NODE_MAP);
        return fromNullable(m);
    }

    public static Optional<IType> createUnresolvedType(final TypeBinding compilerBinding) {
        final JavaElement e = Util.getUnresolvedJavaElement(compilerBinding, null, EMPTY_NODE_MAP);
        if (e instanceof IType) {
            return of((IType) e);
        } else if (e instanceof ITypeParameter) {
            return resolveTypeParameter((ITypeParameter) e);
        }
        return absent();
    }

    private static Optional<IType> resolveTypeParameter(final ITypeParameter t) {
        IType type = null;
        try {
            final IJavaProject project = t.getJavaProject();
            final String[] bounds = t.getBoundsSignatures();
            if (ArrayUtils.isEmpty(bounds)) {
                type = project.findType("java.lang.Object");
            } else {
                final IMember declaringMember = t.getDeclaringMember();
                final Optional<String> typename = resolveUnqualifiedTypeNamesAndStripOffGenericsAndArrayDimension(
                        bounds[0], declaringMember);
                if (typename.isPresent()) {
                    type = project.findType(typename.get());
                }
            }
        } catch (final Exception e) {
            logError(e, "Failed to resolve type parameter '%s'", t.getElementName());
        }
        return fromNullable(type);
    }

    /**
     * Returns a list of all public instance methods and fields declared in the given type or any of its super-types
     */
    public static Collection<IMember> findAllPublicInstanceFieldsAndNonVoidNonPrimitiveInstanceMethods(
            final IType type) {
        final LinkedHashMap<String, IMember> tmp = new LinkedHashMap<String, IMember>();

        try {
            final IType[] returnTypeAndSupertypes = findAllSupertypesIncludeingArgument(type);
            for (final IType cur : returnTypeAndSupertypes) {
                for (final IMethod m : cur.getMethods()) {
                    if (isVoid(m) || !isPublic(m) || m.isConstructor() || isStatic(m)
                            || hasPrimitiveReturnType(m)) {
                        continue;
                    }
                    final String key = createMethodKey(m);
                    if (!tmp.containsKey(key)) {
                        tmp.put(key, m);
                    }
                }
                for (final IField field : cur.getFields()) {
                    if (!isPublic(field) || isStatic(field)) {
                        continue;
                    }
                    final String key = createFieldKey(field);
                    if (!tmp.containsKey(key)) {
                        tmp.put(key, field);
                    }
                }
            }
        } catch (final Exception e) {
            log(e);
        }
        return tmp.values();
    }

    public static Collection<IMember> findAllPublicInstanceFieldsAndPublicInstanceMethods(final IType type) {
        return findAllRelevanFieldsAndMethods(type, PUBLIC_FIELDS_ONLY_FILTER, PUBLIC_INSTANCE_METHODS_ONLY_FILTER);
    }

    /**
     * Returns a list of all public static fields and methods declared in the given class or any of its super-classes.
     */
    public static Collection<IMember> findAllPublicStaticFieldsAndNonVoidNonPrimitiveStaticMethods(
            final IType type) {

        return findAllRelevanFieldsAndMethods(type, STATIC_PUBLIC_FIELDS_ONLY_FILTER,
                STATIC_NON_VOID_NON_PRIMITIVE_PUBLIC_METHODS_FILTER);
    }

    public static Collection<IMember> findAllRelevanFieldsAndMethods(final IType type,
            final Predicate<IField> fieldFilter, final Predicate<IMethod> methodFilter) {
        final LinkedHashMap<String, IMember> tmp = new LinkedHashMap<String, IMember>();
        for (final IType cur : findAllSupertypesIncludeingArgument(type)) {

            try {
                for (final IMethod method : cur.getMethods()) {
                    if (methodFilter.apply(method)) {
                        continue;
                    }
                    final String key = createMethodKey(method);
                    if (!tmp.containsKey(key)) {
                        tmp.put(key, method);
                    }
                }
                for (final IField field : cur.getFields()) {
                    if (fieldFilter.apply(field)) {
                        continue;
                    }
                    final String key = createFieldKey(field);
                    if (!tmp.containsKey(key)) {
                        tmp.put(key, field);
                    }
                }
            } catch (final Exception e) {
                log(e);
            }
        }

        return tmp.values();

    }

    public static Collection<IMember> findAllPublicStaticFieldsAndStaticMethods(final IType type) {
        return findAllRelevanFieldsAndMethods(type, STATIC_PUBLIC_FIELDS_ONLY_FILTER,
                STATIC_PUBLIC_METHODS_ONLY_FILTER);
    }

    private static IType[] findAllSupertypesIncludeingArgument(final IType returnType) {
        try {
            ITypeHierarchy typeHierarchy;
            typeHierarchy = SuperTypeHierarchyCache.getTypeHierarchy(returnType);
            final IType[] allSupertypes = typeHierarchy.getAllSupertypes(returnType);
            return ArrayUtils.add(allSupertypes, 0, returnType);
        } catch (final Exception e) {
            log(e);
            return new IType[0];
        }
    }

    public static ASTNode findClosestMethodOrTypeDeclarationAroundOffset(final CompilationUnit cuNode,
            final ITextSelection selection) {
        ensureIsNotNull(cuNode, "cuNode");
        ensureIsNotNull(selection, "selection");
        ASTNode node = NodeFinder.perform(cuNode, selection.getOffset(), selection.getLength());
        while (node != null) {
            switch (node.getNodeType()) {
            case ASTNode.METHOD_DECLARATION:
            case ASTNode.TYPE_DECLARATION:
                return node;
            }
            node = node.getParent();
        }
        return cuNode;
    }

    public static IMethod findFirstDeclaration(final IMethod method) {
        IMethod res = method;
        while (true) {
            final Optional<IMethod> oFind = findOverriddenMethod(res);
            if (!oFind.isPresent()) {
                break;
            } else {
                res = oFind.get();
            }
        }
        return res;
    }

    public static Optional<IMethod> findOverriddenMethod(final IMethod jdtMethod) {
        try {
            final IType jdtDeclaringType = jdtMethod.getDeclaringType();
            final MethodOverrideTester methodOverrideTester = SuperTypeHierarchyCache
                    .getMethodOverrideTester(jdtDeclaringType);
            final IMethod overriddenMethod = methodOverrideTester.findOverriddenMethod(jdtMethod, false);
            return fromNullable(overriddenMethod);
        } catch (final Exception e) {
            log(e);
            return absent();
        }
    }

    public static Optional<ITypeName> findSuperclassName(final IType type) {
        try {
            final String superclassName = type.getSuperclassTypeSignature();
            if (superclassName == null) {
                return absent();
            }
            final Optional<String> opt = resolveUnqualifiedTypeNamesAndStripOffGenericsAndArrayDimension(
                    superclassName, type);
            if (!opt.isPresent()) {
                return absent();
            }
            final String vmSuperclassName = toVMTypeDescriptor(opt.get());
            final ITypeName vmTypeName = VmTypeName.get(vmSuperclassName);
            return of(vmTypeName);
        } catch (final Exception e) {
            log(e);
            return absent();
        }
    }

    public static Optional<ITypeName> resolveUnqualifiedJDTType(String qName, final IJavaElement parent) {
        try {
            qName = Signature.getTypeErasure(qName);
            qName = StringUtils.removeEnd(qName, ";");
            final int dimensions = Signature.getArrayCount(qName);
            if (dimensions > 0) {
                qName = Signature.getElementType(qName);
            }

            if (isPrimitiveTypeSignature(qName)) {
                final ITypeName t = VmTypeName.get(StringUtils.repeat('[', dimensions) + qName);
                return of(t);
            }

            final IType type = findClosestTypeOrThis(parent);
            if (type == null) {
                return absent();
            }

            if (qName.charAt(0) == Signature.C_TYPE_VARIABLE) {
                String literal = StringUtils.repeat('[', dimensions) + VmTypeName.OBJECT.getIdentifier();
                ITypeName name = VmTypeName.get(literal);
                return of(name);
            }
            if (qName.charAt(0) == Signature.C_UNRESOLVED) {

                final String[][] resolvedNames = type.resolveType(qName.substring(1));
                if (resolvedNames == null || resolvedNames.length == 0) {
                    return of((ITypeName) VmTypeName.OBJECT);
                }
                final String pkg = new String(resolvedNames[0][0]);
                final String name = new String(resolvedNames[0][1]).replace('.', '$');
                qName = StringUtils.repeat('[', dimensions) + "L" + pkg + "." + name;
            }
            qName = qName.replace('.', '/');
            final ITypeName typeName = VmTypeName.get(qName);
            return of(typeName);
        } catch (final Exception e) {
            log(e);
            return absent();
        }
    }

    private static String toVMTypeDescriptor(final String fqjdtName) {
        return fqjdtName == null ? "Ljava/lang/Object" : "L" + fqjdtName.replace('.', '/');
    }

    public static Optional<IType> findSuperclass(final IType type) {
        ensureIsNotNull(type);
        try {
            final String superclassTypeSignature = type.getSuperclassTypeSignature();
            if (superclassTypeSignature == null) {
                return absent();
            }
            return findTypeFromSignature(superclassTypeSignature, type);
        } catch (final Exception e) {
            log(e);
            return absent();
        }
    }

    public static Optional<IType> findTypeFromSignature(final String typeSignature, final IJavaElement parent) {
        ensureIsNotNull(typeSignature);
        ensureIsNotNull(parent);
        try {
            final Optional<String> opt = resolveUnqualifiedTypeNamesAndStripOffGenericsAndArrayDimension(
                    typeSignature, parent);
            if (!opt.isPresent()) {
                return absent();
            }
            final IType res = parent.getJavaProject().findType(opt.get());
            return Optional.fromNullable(res);
        } catch (final Exception e) {
            log(e);
            return Optional.absent();
        }
    }

    public static Optional<IType> findTypeOfField(final IField field) {
        try {
            return findTypeFromSignature(field.getTypeSignature(), field);
        } catch (final Exception e) {
            log(e);
            return Optional.absent();
        }
    }

    public static Optional<ITypeRoot> findTypeRoot(final IEditorPart editor) {
        final ITypeRoot root = EditorUtility.getEditorInputJavaElement(editor, true);
        return fromNullable(root);
    }

    public static Optional<JavaEditor> getActiveJavaEditor() {
        final Optional<IWorkbenchPage> page = RCPUtils.getActiveWorkbenchPage();
        if (page.isPresent()) {
            final IEditorPart editor = page.get().getActiveEditor();
            if (editor instanceof JavaEditor) {
                return of((JavaEditor) editor);
            }
        }
        return absent();
    }

    public static boolean hasPrimitiveReturnType(final IMethod method) {
        try {
            return !method.getReturnType().endsWith(";");
        } catch (final Exception e) {
            throw throwUnhandledException(e);
        }
    }

    public static boolean isAssignable(final IType lhsType, final IType rhsType) {
        ensureIsNotNull(lhsType);
        ensureIsNotNull(rhsType);
        final IType[] supertypes = findAllSupertypesIncludeingArgument(rhsType);
        for (final IType supertype : supertypes) {
            if (supertype.equals(lhsType)) {
                return true;
            }
        }
        return false;
    }

    public static boolean isVoid(final IMethod method) {
        try {
            return Signature.SIG_VOID.equals(method.getReturnType());
        } catch (final Exception e) {
            throw throwUnhandledException(e);
        }
    }

    public static void log(final Exception e) {
        RecommendersUtilsPlugin.logError(e, "Exception occurred.");
    }

    public static Optional<IMethod> resolveMethod(@Nullable final MethodDeclaration node) {
        if (node == null) {
            return absent();
        }
        final IMethodBinding b = node.resolveBinding();
        if (b == null) {
            return absent();
        }
        final IMethod method = cast(b.getJavaElement());
        return Optional.fromNullable(method);
    }

    public static <T extends IJavaElement> T resolveJavaElementProxy(final IJavaElement element) {
        return (T) element.getPrimaryElement();
    }

    /**
     * @param parent
     *            must be an {@link IType} or something that has an {@link IType} as parent.
     */
    public static Optional<String> resolveUnqualifiedTypeNamesAndStripOffGenericsAndArrayDimension(
            String typeSignature, final IJavaElement parent) {
        ensureIsNotNull(typeSignature);
        ensureIsNotNull(parent);
        // remove generics information if available:
        typeSignature = Signature.getTypeErasure(typeSignature);

        if (isPrimitiveTypeSignature(typeSignature)) {
            return of(typeSignature);
        }
        try {
            typeSignature = typeSignature.replace('/', '.');
            final IType type = findClosestTypeOrThis(parent);
            if (type == null) {
                throwIllegalArgumentException("parent could not be resolved to an IType: %s", parent);
            }
            final String resolvedTypeSignature = JavaModelUtil.getResolvedTypeName(typeSignature, type);
            if (resolvedTypeSignature == null) {
                // return fall-back. This happens for instance when giving <T>
                // or QT; respectively.
                return of("java.lang.Object");
            }
            return of(resolvedTypeSignature);
        } catch (final Exception e) {
            log(e);
            return absent();
        }
    }

    private static IType findClosestTypeOrThis(final IJavaElement parent) {
        return (IType) (parent instanceof IType ? parent : parent.getAncestor(IJavaElement.TYPE));
    }

    private static boolean isPrimitiveTypeSignature(final String typeSignature) {
        return typeSignature.length() == 1;
    }

    public static Optional<ASTNode> findAstNodeFromEditorSelection(final JavaEditor editor,
            final ITextSelection textSelection) {
        final Optional<ITypeRoot> root = findTypeRoot(editor);
        if (!root.isPresent()) {
            return Optional.absent();
        }
        final CompilationUnit astRoot = getAST(root.get(), WAIT_YES, null);
        if (astRoot == null) {
            return absent();
        }
        final ASTNode node = org.eclipse.jdt.core.dom.NodeFinder.perform(astRoot, textSelection.getOffset(), 0);
        return Optional.fromNullable(node);
    }

    public static Optional<File> getLocation(@Nullable final IPackageFragmentRoot packageRoot) {
        if (packageRoot == null) {
            return absent();
        }
        File res = null;
        final IResource resource = packageRoot.getResource();
        if (resource != null) {
            if (resource.getLocation() == null) {
                res = resource.getRawLocation().toFile().getAbsoluteFile();
            } else {
                res = resource.getLocation().toFile().getAbsoluteFile();
            }
        }
        if (packageRoot.isExternal()) {
            res = packageRoot.getPath().toFile().getAbsoluteFile();
        }

        // if the file (for whatever reasons) does not exist, skip it.
        if (res != null && !res.exists()) {
            res = null;
        }
        return Optional.fromNullable(res);
    }

    /**
     * Returns the absolute location of the given java project or {@link Optional#absent} if the project is
     * <code>null</code> or could not be mapped to any existing location on disk.
     */
    public static Optional<File> getLocation(@Nullable final IJavaProject javaProject) {
        if (javaProject == null) {
            return absent();
        }
        IProject project = javaProject.getProject();
        IPath location = project.getLocation();
        if (location == null) {
            return absent();
        }
        File file = location.toFile().getAbsoluteFile();
        if (file.exists()) {
            return of(file);
        } else {
            return absent();
        }
    }

    public static boolean isInitializer(final IMethod m) {
        return m.getElementName().equals("<clinit>");
    }
}