org.key_project.util.jdt.JDTUtil.java Source code

Java tutorial

Introduction

Here is the source code for org.key_project.util.jdt.JDTUtil.java

Source

/*******************************************************************************
 * Copyright (c) 2014 Karlsruhe Institute of Technology, Germany
 *                    Technical University Darmstadt, Germany
 *                    Chalmers University of Technology, Sweden
 * 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:
 *    Technical University Darmstadt - initial API and implementation and/or initial documentation
 *******************************************************************************/

package org.key_project.util.jdt;

import java.io.File;
import java.io.IOException;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;

import org.eclipse.core.resources.IContainer;
import org.eclipse.core.resources.IFolder;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.Assert;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.jobs.Job;
import org.eclipse.jdt.core.IClasspathEntry;
import org.eclipse.jdt.core.ICompilationUnit;
import org.eclipse.jdt.core.IJavaElement;
import org.eclipse.jdt.core.IJavaProject;
import org.eclipse.jdt.core.ILocalVariable;
import org.eclipse.jdt.core.IMethod;
import org.eclipse.jdt.core.IPackageDeclaration;
import org.eclipse.jdt.core.IPackageFragment;
import org.eclipse.jdt.core.IPackageFragmentRoot;
import org.eclipse.jdt.core.ISourceRange;
import org.eclipse.jdt.core.IType;
import org.eclipse.jdt.core.JavaConventions;
import org.eclipse.jdt.core.JavaCore;
import org.eclipse.jdt.core.JavaModelException;
import org.eclipse.jdt.core.Signature;
import org.eclipse.jdt.core.dom.ASTNode;
import org.eclipse.jdt.core.dom.ASTParser;
import org.eclipse.jdt.core.dom.ASTVisitor;
import org.eclipse.jdt.core.dom.Block;
import org.eclipse.jdt.internal.corext.util.CodeFormatterUtil;
import org.eclipse.jdt.internal.corext.util.JavaConventionsUtil;
import org.eclipse.jdt.internal.corext.util.JavaModelUtil;
import org.eclipse.jdt.internal.ui.javaeditor.ASTProvider;
import org.eclipse.jdt.internal.ui.util.CoreUtility;
import org.eclipse.jdt.internal.ui.viewsupport.JavaElementLabelComposer;
import org.eclipse.jdt.ui.JavaElementLabels;
import org.eclipse.jdt.ui.PreferenceConstants;
import org.eclipse.jdt.ui.wizards.JavaCapabilityConfigurationPage;
import org.eclipse.swt.widgets.Display;
import org.key_project.util.Activator;
import org.key_project.util.eclipse.Logger;
import org.key_project.util.eclipse.ResourceUtil;
import org.key_project.util.java.ArrayUtil;
import org.key_project.util.java.CollectionUtil;
import org.key_project.util.java.IFilter;
import org.key_project.util.java.ObjectUtil;
import org.key_project.util.java.thread.AbstractRunnableWithException;
import org.key_project.util.java.thread.IRunnableWithException;

/**
 * Provides static methods to work with JDT.
 * @author Martin Hentschel
 */
@SuppressWarnings("restriction")
public class JDTUtil {
    /**
     * File extension of Java source files.
     */
    public static final String JAVA_FILE_EXTENSION = "java";

    /**
     * Forbid instances by this private constructor.
     */
    private JDTUtil() {
    }

    /**
     * Creates a new {@link IJavaProject} that is an {@link IProject} with a JDT nature.
     * @param name The project name.
     * @param referencedProjects Additional {@link IProject}s to include in the build path.
     * @return The created {@link IJavaProject}.
     * @throws CoreException Occurred Exception.
     */
    public static IJavaProject createJavaProject(String name, IProject... referencedProjects) throws CoreException {
        return createJavaProject(name, getOutputFolderName(), getSourceFolderName(), referencedProjects);
    }

    /**
     * Creates a new {@link IJavaProject} that is an {@link IProject} with a JDT nature.
     * @param name The project name.
     * @param outputFolderName The name of the output folder to create.
     * @param srcFolderName The name of the source folder to create.
     * @param referencedProjects Additional {@link IProject}s to include in the build path.
     * @return The created {@link IJavaProject}.
     * @throws CoreException Occurred Exception.
     */
    public static IJavaProject createJavaProject(String name, String outputFolderName, String srcFolderName,
            IProject... referencedProjects) throws CoreException {
        IProject project = ResourceUtil.createProject(name);
        IFolder bin = project.getFolder(outputFolderName);
        CoreUtility.createDerivedFolder(bin, true, true, null);
        IFolder src = project.getFolder(srcFolderName);
        CoreUtility.createFolder(src, true, true, null);
        return convertToJavaProject(project, bin, src, referencedProjects);
    }

    /**
     * Returns the default output folder name.
     * @return The default output folder name.
     */
    public static String getOutputFolderName() {
        return PreferenceConstants.getPreferenceStore().getString(PreferenceConstants.SRCBIN_BINNAME);
    }

    /**
     * Returns the default source folder name.
     * @return The default source folder name.
     */
    public static String getSourceFolderName() {
        return PreferenceConstants.getPreferenceStore().getString(PreferenceConstants.SRCBIN_SRCNAME);
    }

    /**
     * Converts the given {@link IProject} into an {@link IJavaProject}.
     * @param project The {@link IProject} to convert.
     * @param bin The {@link IContainer} to use as output location.
     * @param src The {@link IContainer} which provides the source files.
     * @param referencedProjects Additional {@link IProject}s to include in the build path.
     * @return The created {@link IJavaProject}.
     * @throws CoreException Occurred Exception.
     */
    public static IJavaProject convertToJavaProject(IProject project, final IContainer bin, final IContainer src,
            final IProject... referencedProjects) throws CoreException {
        final IJavaProject javaProject = JavaCore.create(project);
        IRunnableWithException run = new AbstractRunnableWithException() {
            @Override
            public void run() {
                try {
                    JavaCapabilityConfigurationPage page = new JavaCapabilityConfigurationPage();
                    IClasspathEntry[] entries = new IClasspathEntry[1 + referencedProjects.length];
                    entries[0] = JavaCore.newSourceEntry(src.getFullPath());
                    for (int i = 0; i < referencedProjects.length; i++) {
                        entries[i + 1] = JavaCore.newProjectEntry(referencedProjects[i].getFullPath());
                    }
                    entries = ArrayUtil.addAll(entries, getDefaultJRELibrary());
                    page.init(javaProject, bin.getFullPath(), entries, false);
                    page.configureJavaProject(null);
                } catch (Exception e) {
                    setException(e);
                }
            }
        };
        Display.getDefault().syncExec(run);
        if (run.getException() instanceof CoreException) {
            throw (CoreException) run.getException();
        } else if (run.getException() != null) {
            throw new CoreException(
                    new Logger(Activator.getDefault(), Activator.PLUGIN_ID).createErrorStatus(run.getException()));
        }
        return javaProject;
    }

    /**
     * Returns the default JRE library entries.
     * @return The default JRE library entries.
     */
    public static IClasspathEntry[] getDefaultJRELibrary() {
        return PreferenceConstants.getDefaultJRELibrary();
    }

    /**
     * Searches the {@link IMethod} as JDT representation which ends
     * at the given index.
     * @param cu The {@link ICompilationUnit} to search in.
     * @param endIndex The index in the file at that the required method ends.
     * @return The found {@link IMethod} or {@code null} if the JDT representation is not available.
     * @throws JavaModelException Occurred Exception.
     * @throws IOException Occurred Exception.
     */
    public static IMethod findJDTMethod(ICompilationUnit cu, int endIndex) throws JavaModelException, IOException {
        IMethod result = null;
        if (cu != null) {
            IType[] types = cu.getAllTypes();
            int i = 0;
            while (result == null && i < types.length) {
                IMethod[] methods = types[i].getMethods();
                int j = 0;
                while (result == null && j < methods.length) {
                    ISourceRange methodRange = methods[j].getSourceRange();
                    if (endIndex == methodRange.getOffset() + methodRange.getLength()) {
                        result = methods[j];
                    }
                    j++;
                }
                i++;
            }
        }
        return result;
    }

    /**
     * Searches the {@link IType} as JDT representation which ends
     * at the given index.
     * @param cu The {@link ICompilationUnit} to search in.
     * @param endIndex The index in the file at that the required method ends.
     * @return The found {@link IType} or {@code null} if the JDT representation is not available.
     * @throws JavaModelException Occurred Exception.
     * @throws IOException Occurred Exception.
     */
    public static IType findJDTType(ICompilationUnit cu, int endIndex) throws JavaModelException, IOException {
        IType result = null;
        if (cu != null) {
            IType[] types = cu.getAllTypes();
            int i = 0;
            while (result == null && i < types.length) {
                ISourceRange typeRange = types[i].getSourceRange();
                if (endIndex == typeRange.getOffset() + typeRange.getLength()) {
                    result = types[i];
                }
                i++;
            }
        }
        return result;
    }

    /**
     * Returns the tab width used in the given {@link IJavaElement}.
     * @param element The {@link IJavaElement} to get its tab width.
     * @return The tab width.
     */
    public static int getTabWidth(IJavaElement element) {
        return element != null ? CodeFormatterUtil.getTabWidth(element.getJavaProject()) : 0;
    }

    /**
     * Returns the first {@link IJavaElement} from the given once that
     * has the given text label.
     * @param elements The {@link IJavaElement}s to search in.
     * @param textLabel The text label for that the {@link IJavaElement} is needed.
     * @return The first found {@link IJavaElement} or {@code null} if no one was found.
     * @throws JavaModelException Occurred Exception 
     */
    public static IMethod getElementForQualifiedMethodLabel(IMethod[] elements, String textLabel)
            throws JavaModelException {
        IMethod result = null;
        if (elements != null) {
            int i = 0;
            while (result == null && i < elements.length) {
                if (ObjectUtil.equals(textLabel, getQualifiedMethodLabel(elements[i]))) {
                    result = elements[i];
                }
                i++;
            }
        }
        return result;
    }

    /**
     * <p>
     * Returns the unique method signature for the given {@link IMethod}.
     * Parameter types are replaced with the full qualified type names.
     * </p>
     * <p>
     * Example method declaration: {@code public int foo(Date ud, java.sql.Date sd)}<br>
     * Created signature: {@code foo(java.util.Date, java.sql.Date)}
     * </p>
     * @param method The {@link IMethod} for that the signature label is needed.
     * @return The created label.
     * @throws JavaModelException Occurred Exception.
     */
    public static String getQualifiedMethodLabel(IMethod method) throws JavaModelException {
        try {
            if (method != null) {
                StringBuffer sb = new StringBuffer();
                sb.append(method.getElementName());
                sb.append("(");
                ILocalVariable[] parameters = method.getParameters();
                boolean afterFirst = false;
                JavaElementLabelComposerHelper c = new JavaElementLabelComposerHelper(sb,
                        method.getDeclaringType());
                for (ILocalVariable parameter : parameters) {
                    if (afterFirst) {
                        sb.append(", ");
                    } else {
                        afterFirst = true;
                    }
                    c.appendTypeSignatureLabel(parameter, parameter.getTypeSignature(),
                            JavaElementLabels.F_PRE_TYPE_SIGNATURE);
                }
                sb.append(")");
                return sb.toString();
            } else {
                return null;
            }
        } catch (JavaModelRuntimeException e) {
            throw e.getCause();
        }
    }

    /**
     * Utility class to compute the full qualified type of a method parameter.
     * @author Martin Hentschel
     */
    private static class JavaElementLabelComposerHelper extends JavaElementLabelComposer {
        /**
         * The Type that contains the method.
         */
        private IType declaringType;

        /**
         * Constructor.
         * @param buffer The {@link StringBuffer} to fill.
         * @param declaringType The Type that contains the method.
         */
        private JavaElementLabelComposerHelper(StringBuffer buffer, IType declaringType) {
            super(buffer);
            this.declaringType = declaringType;
        }

        /**
         * <p>
         * {@inheritDoc}
         * </p>
         * <p>
         * Changed visibility to public.
         * </p>
         */
        @Override
        public void appendTypeSignatureLabel(IJavaElement enclosingElement, String typeSig, long flags) {
            super.appendTypeSignatureLabel(enclosingElement, typeSig, flags);
        }

        /**
         * Overwritten to return the fulil qualified type name instead of the simple type name.
         */
        @Override
        protected String getSimpleTypeName(IJavaElement enclosingElement, String typeSig) {
            try {
                String simpleName = Signature.toString(Signature.getTypeErasure(typeSig));
                String[][] resolvedTypes = declaringType.resolveType(simpleName);
                if (resolvedTypes != null && resolvedTypes.length > 0) {
                    return (resolvedTypes[0][0].equals("") ? "" : resolvedTypes[0][0] + ".") + resolvedTypes[0][1];
                } else {
                    return simpleName;
                }
            } catch (JavaModelException e) {
                throw new JavaModelRuntimeException(e);
            }
        }
    }

    /**
     * A utility {@link RuntimeException} that is used to transfer
     * a {@link JavaModelException} back in the call hierarchy.
     * @author Martin Hentschel
     */
    private static class JavaModelRuntimeException extends RuntimeException {
        /**
         * Generated UID.
         */
        private static final long serialVersionUID = 9027197807876279139L;

        /**
         * Constructor.
         * @param cause The {@link JavaModelException} to wrap.
         */
        private JavaModelRuntimeException(JavaModelException cause) {
            super(cause);
        }

        /**
         * {@inheritDoc}
         */
        @Override
        public JavaModelException getCause() {
            return (JavaModelException) super.getCause();
        }
    }

    /**
     * Returns a human readable text label for the given {@link IJavaElement}.
     * @param element The {@link IJavaElement} to convert,
     * @return The human readable text label. An empty {@link String} is returned if the given {@link IJavaElement} is {@code null}.
     */
    public static String getTextLabel(IJavaElement element) {
        return JavaElementLabels.getTextLabel(element, JavaElementLabels.ALL_DEFAULT);
    }

    /**
     * Returns the first {@link IJavaElement} from the given once that
     * has the given text label.
     * @param elements The {@link IJavaElement}s to search in.
     * @param textLabel The text label for that the {@link IJavaElement} is needed.
     * @return The first found {@link IJavaElement} or {@code null} if no one was found.
     */
    public static IJavaElement getElementForTextLabel(IJavaElement[] elements, String textLabel) {
        IJavaElement result = null;
        if (elements != null) {
            int i = 0;
            while (result == null && i < elements.length) {
                if (ObjectUtil.equals(textLabel, getTextLabel(elements[i]))) {
                    result = elements[i];
                }
                i++;
            }
        }
        return result;
    }

    /**
     * Adds the given {@link IClasspathEntry} to the {@link IJavaProject}.
     * @param javaProject The {@link IJavaProject} to add to.
     * @param entryToAdd The {@link IClasspathEntry} to add.
     * @throws JavaModelException Occurred Exception.
     */
    public static void addClasspathEntry(IJavaProject javaProject, IClasspathEntry entryToAdd)
            throws JavaModelException {
        if (javaProject != null && entryToAdd != null) {
            IClasspathEntry[] newEntries = ArrayUtil.add(javaProject.getRawClasspath(), entryToAdd);
            javaProject.setRawClasspath(newEntries, null);
        }
    }

    /**
     * Returns all {@link IJavaProject}s.
     * @return All {@link IJavaProject}s.
     * @throws JavaModelException
     */
    public static IJavaProject[] getAllJavaProjects() throws JavaModelException {
        return JavaCore.create(ResourcesPlugin.getWorkspace().getRoot()).getJavaProjects();
    }

    /**
     * Returns all available packages.
     * @return All packages.
     * @throws JavaModelException Occurred Exception.
     */
    public static IJavaElement[] getAllPackageFragmentRoot() throws JavaModelException {
        IJavaProject[] jProjects = getAllJavaProjects();
        Set<IJavaElement> packages = new HashSet<IJavaElement>();
        for (IJavaProject jProject : jProjects) {
            IPackageFragmentRoot[] froots = jProject.getAllPackageFragmentRoots();
            for (IPackageFragmentRoot froot : froots) {
                if (froot != null && froot.exists() && !froot.isReadOnly()) {
                    IJavaElement[] children = froot.getChildren();
                    for (IJavaElement element : children) {
                        packages.add(element);
                    }
                }
            }
        }
        return packages.toArray(new IJavaElement[packages.size()]);
    }

    /**
     * Returns the package that contains the {@link IJavaElement}.
     * @param element The {@link IJavaElement}.
     * @return The package that contains the given {@link IJavaElement}.
     */
    public static IJavaElement getPackage(IJavaElement element) {
        if (element != null) {
            if (element instanceof IPackageDeclaration) {
                return element;
            } else if (element instanceof IPackageFragment) {
                return element;
            } else if (element instanceof IPackageFragmentRoot) {
                return element;
            } else {
                return getPackage(element.getParent());
            }
        } else {
            return null;
        }
    }

    /**
     * <p>
     * Returns the {@link IJavaProject} for the given {@link IProject}.
     * </p>
     * <p>
     * <b>Attention:</b> It is also an {@link IJavaProject} returned even
     * if the {@link IProject} is no Java project (has no JDT nature).
     * To verify if an {@link IProject} is a real Java project use
     * {@link JDTUtil#isJavaProject(IProject)}.
     * </p>
     * @param project The {@link IProject} for that an {@link IJavaProject} is needed.
     * @return The {@link IJavaProject} representation of the {@link IProject} or {@code null} if the given {@link IProject} is {@code null}.
     */
    public static IJavaProject getJavaProject(IProject project) {
        return JavaCore.create(project);
    }

    /**
     * <p>
     * Returns the {@link IJavaProject} for the given {@link IProject}.
     * </p>
     * <p>
     * <b>Attention:</b> It is also an {@link IJavaProject} returned even
     * if the {@link IProject} is no Java project (has no JDT nature).
     * To verify if an {@link IProject} is a real Java project use
     * {@link JDTUtil#isJavaProject(IProject)}.
     * </p>
     * @param projectName The name of the {@link IProject} for that an {@link IJavaProject} is needed.
     * @return The {@link IJavaProject} representation of the {@link IProject} with the given name or {@code null} if the given project name is {@code null}/empty.
     */
    public static IJavaProject getJavaProject(String projectName) {
        IProject project = ResourceUtil.getProject(projectName);
        return getJavaProject(project);
    }

    /**
     * Checks if the given {@link IProject} is a Java project.
     * @param project The {@link IProject} to check.
     * @return {@code true} is Java project, {@code false} is no Java project.
     */
    public static boolean isJavaProject(IProject project) {
        if (project != null) {
            IJavaProject javaProject = getJavaProject(project);
            return javaProject != null && javaProject.exists();
        } else {
            return false;
        }
    }

    /**
     * Checks if the given {@link IResource} is a "Java" file.
     * @param file The {@link IResource} to check.
     * @return {@code true} is Java file, {@code false} is something else or {@code null}.
     */
    public static boolean isJavaFile(IResource file) {
        if (file != null) {
            String extension = file.getFileExtension();
            return extension != null && extension.equalsIgnoreCase(JAVA_FILE_EXTENSION);
        } else {
            return false;
        }
    }

    /**
     * Checks if the given {@link IResource} is or is contained in a source folder of its project.
     * @param resource The {@link IResource} to check.
     * @return {@code true} is source folder of its project or contained in a source folder of its project, {@code false} is somewhere else.
     * @throws JavaModelException Occurred Exception.
     */
    public static boolean isInSourceFolder(IResource resource) throws JavaModelException {
        boolean inSourceFolder = false;
        if (resource != null) {
            IJavaProject javaProject = getJavaProject(resource.getProject());
            if (javaProject != null && javaProject.exists()) {
                IClasspathEntry[] entries = javaProject.getRawClasspath();
                int i = 0;
                while (!inSourceFolder && i < entries.length) {
                    if (entries[i].getContentKind() == IPackageFragmentRoot.K_SOURCE) {
                        IPackageFragmentRoot[] roots = javaProject.findPackageFragmentRoots(entries[i]);
                        int j = 0;
                        while (!inSourceFolder && j < roots.length) {
                            IResource rootResource = roots[j].getResource();
                            if (rootResource != null && rootResource.contains(resource)) {
                                inSourceFolder = true;
                            }
                            j++;
                        }
                    }
                    i++;
                }
            }
        }
        return inSourceFolder;
    }

    /**
     * Returns all source {@link IPackageFragmentRoot}s.
     * @param javaProject The {@link IJavaProject} to read source {@link IPackageFragmentRoot}s from.
     * @return The found {@link IPackageFragmentRoot}s.
     * @throws JavaModelException Occurred Exception.
     */
    public static List<IPackageFragmentRoot> getSourcePackageFragmentRoots(IJavaProject javaProject)
            throws JavaModelException {
        List<IPackageFragmentRoot> result = new LinkedList<IPackageFragmentRoot>();
        if (javaProject != null && javaProject.exists()) {
            IClasspathEntry[] entries = javaProject.getRawClasspath();
            for (IClasspathEntry entry : entries) {
                if (entry.getContentKind() == IPackageFragmentRoot.K_SOURCE
                        && entry.getEntryKind() == IClasspathEntry.CPE_SOURCE) {
                    IPackageFragmentRoot[] roots = javaProject.findPackageFragmentRoots(entry);
                    CollectionUtil.addAll(result, roots);
                }
            }
        }
        return result;
    }

    /**
     * Returns the locations in the local file system of all used
     * source entries in the java build path of the given project.
     * @param project The given Project.
     * @return The found source locations in the file system.
     * @throws JavaModelException Occurred Exception.
     */
    public static List<File> getSourceLocations(IProject project) throws JavaModelException {
        return getSourceLocations(project, new HashSet<IProject>());
    }

    /**
     * Internal helper method that is used in {@link #getSourceLocations(IProject)}
     * to compute the source path. It is required to solve cycles in project dependencies.
     * @param project The given Project.
     * @param alreadyHandledProjects The already handled {@link IProject} that don't need to be analysed again.
     * @return The found source locations in the file system.
     * @throws JavaModelException Occurred Exception.
     */
    private static List<File> getSourceLocations(IProject project, Set<IProject> alreadyHandledProjects)
            throws JavaModelException {
        List<File> result = new LinkedList<File>();
        if (project != null) {
            Assert.isNotNull(alreadyHandledProjects);
            alreadyHandledProjects.add(project);
            IJavaProject javaProject = getJavaProject(project);
            if (javaProject != null && javaProject.exists()) {
                IClasspathEntry[] entries = javaProject.getRawClasspath();
                for (IClasspathEntry entry : entries) {
                    if (entry.getContentKind() == IPackageFragmentRoot.K_SOURCE) {
                        List<File> location = getLocationFor(javaProject, entry, IPackageFragmentRoot.K_SOURCE,
                                alreadyHandledProjects);
                        if (location != null) {
                            result.addAll(location);
                        }
                    }
                }
            }
        }
        return result;
    }

    /**
     * Returns the {@link IResource}s in the workspace of all used
     * source entries in the java build path of the given project.
     * @param project The given Project.
     * @return The found source {@link IResource}s in the workspace.
     * @throws JavaModelException Occurred Exception.
     */
    public static List<IResource> getSourceResources(IProject project) throws JavaModelException {
        return getSourceResources(project, new HashSet<IProject>());
    }

    /**
     * Internal helper method that is used in {@link #getSourceResources(IProject)}
     * to compute the source path. It is required to solve cycles in project dependencies.
     * @param project The given Project.
     * @param alreadyHandledProjects The already handled {@link IProject} that don't need to be analysed again.
     * @return The found source {@link IResource}s in the workspace.
     * @throws JavaModelException Occurred Exception.
     */
    private static List<IResource> getSourceResources(IProject project, Set<IProject> alreadyHandledProjects)
            throws JavaModelException {
        List<IResource> result = new LinkedList<IResource>();
        if (project != null) {
            Assert.isNotNull(alreadyHandledProjects);
            alreadyHandledProjects.add(project);
            IJavaProject javaProject = getJavaProject(project);
            if (javaProject != null && javaProject.exists()) {
                IClasspathEntry[] entries = javaProject.getRawClasspath();
                for (IClasspathEntry entry : entries) {
                    if (entry.getContentKind() == IPackageFragmentRoot.K_SOURCE) {
                        List<IResource> location = getResourceFor(javaProject, entry, IPackageFragmentRoot.K_SOURCE,
                                alreadyHandledProjects);
                        if (location != null) {
                            result.addAll(location);
                        }
                    }
                }
            }
        }
        return result;
    }

    /**
     * Returns the {@link IResource}s of the given {@link IClasspathEntry}.
     * @param javaProject The actual {@link IJavaProject} that provides the {@link IClasspathEntry}.
     * @param entry The given {@link IClasspathEntry}.
     * @param alreadyHandledProjects The already handled {@link IProject} that don't need to be analysed again.
     * @return The found {@link IResource}s.
     * @throws JavaModelException 
     */
    private static List<IResource> getResourceFor(IJavaProject javaProject, IClasspathEntry entry, int expectedKind,
            Set<IProject> alreadyHandledProjects) throws JavaModelException {
        if (entry != null) {
            if (entry.getEntryKind() == IClasspathEntry.CPE_CONTAINER
                    || entry.getEntryKind() == IClasspathEntry.CPE_SOURCE
                    || entry.getEntryKind() == IClasspathEntry.CPE_LIBRARY
                    || entry.getEntryKind() == IClasspathEntry.CPE_VARIABLE) {
                List<IResource> result = new LinkedList<IResource>();
                IPackageFragmentRoot[] roots = javaProject.findPackageFragmentRoots(entry);
                for (IPackageFragmentRoot root : roots) {
                    if (root.getKind() == expectedKind) {
                        if (root.getResource() != null) {
                            if (root.getResource().getLocationURI() != null) {
                                result.add(root.getResource());
                            }
                        } else if (root.getPath() != null) {
                            IResource resource = ResourcesPlugin.getWorkspace().getRoot()
                                    .findMember(root.getPath());
                            if (resource != null && resource.exists()) {
                                result.add(resource);
                            }
                        }
                    }
                }
                return result; // Ignore containers
            } else if (entry.getEntryKind() == IClasspathEntry.CPE_PROJECT) {
                Assert.isNotNull(entry.getPath());
                IResource project = ResourcesPlugin.getWorkspace().getRoot().findMember(entry.getPath());
                Assert.isTrue(project instanceof IProject);
                if (!alreadyHandledProjects.contains(project)) {
                    return getSourceResources((IProject) project, alreadyHandledProjects);
                } else {
                    return null; // Project was already analyzed, no need to do it again.
                }
            } else {
                Assert.isTrue(false, "Unknown content kind \"" + entry.getContentKind()
                        + "\" of class path entry \"" + entry + "\".");
                return null;
            }
        } else {
            return null;
        }
    }

    /**
     * Returns the locations of the given {@link IClasspathEntry}.
     * @param javaProject The actual {@link IJavaProject} that provides the {@link IClasspathEntry}.
     * @param entry The given {@link IClasspathEntry}.
     * @param alreadyHandledProjects The already handled {@link IProject} that don't need to be analysed again.
     * @return The found locations.
     * @throws JavaModelException 
     */
    private static List<File> getLocationFor(IJavaProject javaProject, IClasspathEntry entry, int expectedKind,
            Set<IProject> alreadyHandledProjects) throws JavaModelException {
        if (entry != null) {
            if (entry.getEntryKind() == IClasspathEntry.CPE_CONTAINER
                    || entry.getEntryKind() == IClasspathEntry.CPE_SOURCE
                    || entry.getEntryKind() == IClasspathEntry.CPE_LIBRARY
                    || entry.getEntryKind() == IClasspathEntry.CPE_VARIABLE) {
                List<File> result = new LinkedList<File>();
                IPackageFragmentRoot[] roots = javaProject.findPackageFragmentRoots(entry);
                for (IPackageFragmentRoot root : roots) {
                    if (root.getKind() == expectedKind) {
                        if (root.getResource() != null) {
                            if (root.getResource().getLocationURI() != null) {
                                result.add(ResourceUtil.getLocation(root.getResource()));
                            }
                        } else if (root.getPath() != null) {
                            File location = new File(root.getPath().toString());
                            if (location.exists()) {
                                result.add(location);
                            }
                        }
                    }
                }
                return result; // Ignore containers
            } else if (entry.getEntryKind() == IClasspathEntry.CPE_PROJECT) {
                Assert.isNotNull(entry.getPath());
                IResource project = ResourcesPlugin.getWorkspace().getRoot().findMember(entry.getPath());
                Assert.isTrue(project instanceof IProject);
                if (!alreadyHandledProjects.contains(project)) {
                    return getSourceLocations((IProject) project, alreadyHandledProjects);
                } else {
                    return null; // Project was already analyzed, no need to do it again.
                }
            } else {
                Assert.isTrue(false, "Unknown content kind \"" + entry.getContentKind()
                        + "\" of class path entry \"" + entry + "\".");
                return null;
            }
        } else {
            return null;
        }
    }

    /**
     * Parses the given {@link ICompilationUnit} in the specified range into an AST. 
     * @param compilationUnit The {@link ICompilationUnit} to parse.
     * @param offset The start index in the text to parse.
     * @param length The length of the text to parse.
     * @return The {@link ASTNode} which is the root of the AST.
     */
    public static ASTNode parse(ICompilationUnit compilationUnit, int offset, int length) {
        ASTParser parser = ASTParser.newParser(ASTProvider.SHARED_AST_LEVEL); // Hopefully always the newest AST level (e.g. AST.JLS4)
        parser.setSource(compilationUnit);
        parser.setSourceRange(offset, length);
        return parser.createAST(null);
    }

    /**
     * Returns the {@link Block} which represents the method body
     * of the given {@link IMethod} in an {@link ASTParser}.
     * @param method The {@link IMethod} to lookup its body.
     * @return The found {@link Block} or {@code null} if not available.
     * @throws JavaModelException Occurred Exception.
     */
    public static Block getMethodBody(IMethod method) throws JavaModelException {
        if (method != null) {
            int methodStart = method.getSourceRange().getOffset();
            int methodLength = method.getSourceRange().getLength();
            ASTNode node = JDTUtil.parse(method.getCompilationUnit(), methodStart, methodLength);
            MethodBodySearcher searcher = new MethodBodySearcher(methodStart, methodLength);
            node.accept(searcher);
            return searcher.getResult();
        } else {
            return null;
        }
    }

    /**
     * Utility class used by {@link JDTUtil#getMethodBody(IMethod)}
     * to compute the result.
     * @author Martin Hentschel
     */
    private static class MethodBodySearcher extends ASTVisitor {
        /**
         * The result.
         */
        private Block result;

        /**
         * The start index of the method.
         */
        private int methodStart;

        /**
         * The end index of the method.
         */
        private int methodEnd;

        /**
         * Constructor.
         * @param methodStart The start index of the method.
         * @param methodLength The end index of the method.
         */
        public MethodBodySearcher(int methodStart, int methodLength) {
            this.methodStart = methodStart;
            this.methodEnd = methodStart + methodLength;
        }

        /**
         * {@inheritDoc}
         */
        @Override
        public boolean visit(Block node) {
            if (node.getStartPosition() >= methodStart && node.getStartPosition() + node.getLength() <= methodEnd) {
                result = node;
                return false;
            } else {
                return true;
            }
        }

        /**
         * Returns the result.
         * @return The result.
         */
        public Block getResult() {
            return result;
        }
    }

    /**
     * Searches the {@link IMethod} with the given name and full qualified parameter types.
     * @param jdtType The {@link IType} which provides the available methods.
     * @param name The name of the methods.
     * @param parameterTypes The full qualified parameter types.
     * @return The found {@link IMethod} if available or {@code null} otherwise.
     * @throws JavaModelException Occurred Exception.
     */
    public static IMethod findJDTMethod(final IType jdtType, final String name, final String[] parameterTypes)
            throws JavaModelException {
        try {
            if (jdtType != null) {
                IMethod[] methods = jdtType.getMethods();
                return ArrayUtil.search(methods, new IFilter<IMethod>() {
                    @Override
                    public boolean select(IMethod element) {
                        try {
                            if (ObjectUtil.equals(name, element.getElementName())) {
                                String[] parameters = element.getParameterTypes();
                                if (parameters.length == parameterTypes.length) {
                                    boolean parametersMatches = true;
                                    int i = 0;
                                    while (parametersMatches && i < parameters.length) {
                                        String resolvedType = JavaModelUtil.getResolvedTypeName(parameters[i],
                                                jdtType);
                                        if (!ObjectUtil.equals(resolvedType, parameterTypes[i])) {
                                            parametersMatches = false;
                                        }
                                        i++;
                                    }
                                    return parametersMatches;
                                } else {
                                    return false;
                                }
                            } else {
                                return false;
                            }
                        } catch (JavaModelException e) {
                            throw new RuntimeException(e);
                        }
                    }
                });
            } else {
                return null;
            }
        } catch (RuntimeException e) {
            throw (JavaModelException) e.getCause();
        }
    }

    /**
     * Returns the full qualified type name of the given {@link IType}.
     * @param type The {@link IType}.
     * @return The full qualified {@link IType} or {@code null} if no {@link IType} is defined.
     */
    public static String getQualifiedTypeName(IType type) {
        if (type != null) {
            StringBuffer sb = new StringBuffer();
            JavaElementLabelComposerHelper c = new JavaElementLabelComposerHelper(sb, type.getDeclaringType());
            c.appendTypeLabel(type, JavaElementLabels.T_FULLY_QUALIFIED);
            return sb.toString();
        } else {
            return null;
        }
    }

    /**
     * Builds the given {@link IJavaProject} in the background {@link Job}.
     * @param project The {@link IJavaProject} to build in background.
     */
    public static void buildInBackground(IJavaProject javaProject) {
        buildInBackground(javaProject.getProject());
    }

    /**
     * Builds the given {@link IProject} in the background {@link Job}.
     * @param project The {@link IProject} to build in background.
     */
    public static void buildInBackground(IProject project) {
        CoreUtility.startBuildInBackground(project.getProject());
    }

    /**
     * Ensures that the given name is a valid Java type name by replacing
     * all invalid characters with {@code '_'}.
     * @param name The name to validate.
     * @param project The {@link IJavaProject} in which the name will be used.
     * @return The validated name.
     */
    public static String ensureValidJavaTypeName(String name, IJavaProject project) {
        if (name != null) {
            StringBuffer sb = new StringBuffer();
            char[] characters = name.toCharArray();
            for (int i = 0; i < characters.length; i++) {
                String nameToValidate = sb.toString() + characters[i];
                IStatus status = project != null ? JavaConventionsUtil.validateJavaTypeName(nameToValidate, project)
                        : JavaConventions.validateJavaTypeName(nameToValidate, JavaCore.VERSION_1_3,
                                JavaCore.VERSION_1_3);
                ;
                if (status.isOK()) {
                    sb.append(characters[i]);
                } else {
                    sb.append("_");
                }
            }
            return sb.toString();
        } else {
            return null;
        }
    }
}