com.ebmwebsourcing.petals.common.internal.provisional.utils.JavaUtils.java Source code

Java tutorial

Introduction

Here is the source code for com.ebmwebsourcing.petals.common.internal.provisional.utils.JavaUtils.java

Source

/******************************************************************************
 * Copyright (c) 2010-2013, Linagora
 *
 * 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:
 *       Linagora - initial API and implementation
 *******************************************************************************/

package com.ebmwebsourcing.petals.common.internal.provisional.utils;

import java.io.File;
import java.io.FilenameFilter;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IFolder;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IProjectDescription;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.NullProgressMonitor;
import org.eclipse.core.runtime.Path;
import org.eclipse.jdt.core.IClasspathEntry;
import org.eclipse.jdt.core.IJavaElement;
import org.eclipse.jdt.core.IJavaProject;
import org.eclipse.jdt.core.IPackageFragment;
import org.eclipse.jdt.core.IPackageFragmentRoot;
import org.eclipse.jdt.core.JavaCore;
import org.eclipse.jdt.core.JavaModelException;
import org.eclipse.jdt.launching.JavaRuntime;
import org.eclipse.jdt.ui.jarpackager.IJarExportRunnable;
import org.eclipse.jdt.ui.jarpackager.JarPackageData;

import com.ebmwebsourcing.petals.common.internal.PetalsCommonPlugin;

/**
 * @author Vincent Zurczak - EBM WebSourcing
 */
public final class JavaUtils {

    /**
     * Private constructor for utility class.
     */
    private JavaUtils() {
        // nothing
    }

    /**
     * Get the referenced projects from a Java project.
     * <p>The result includes the argument project.</p>
     *
     * @param javaProject
     * @return
     */
    public static List<IJavaProject> getJavaProjectDependencies(IJavaProject javaProject) {

        if (javaProject == null)
            return Collections.emptyList();

        List<IJavaProject> result = new ArrayList<IJavaProject>();
        result.add(javaProject);

        String[] projectNames;
        try {
            projectNames = javaProject.getRequiredProjectNames();
        } catch (JavaModelException e1) {
            PetalsCommonPlugin.log(e1, IStatus.WARNING);
            projectNames = new String[0];
        }

        for (String projectName : projectNames) {
            IProject project = ResourcesPlugin.getWorkspace().getRoot().getProject(projectName);
            try {
                if (!project.exists() || !project.isOpen() || !project.hasNature(JavaCore.NATURE_ID))
                    continue;

            } catch (CoreException e) {
                PetalsCommonPlugin.log(e, IStatus.ERROR);
                continue;
            }

            IJavaProject p = JavaCore.create(project);
            result.add(p);
        }

        return result;
    }

    /**
     * Updates the class path of a Java project with libraries embedded by the studio.
     * @param jp the Java project
     * @param folders the folder names
     * @throws IOException
     * @throws JavaModelException
     */
    public static void updateClasspathWithProjectLibraries(IJavaProject jp, IProgressMonitor monitor,
            String... folders) throws IOException, JavaModelException {

        if (folders == null)
            return;

        // Keep the current entries
        ArrayList<IClasspathEntry> entries = new ArrayList<IClasspathEntry>();
        entries.addAll(Arrays.asList(jp.getRawClasspath()));
        FilenameFilter filter = new FilenameFilter() {
            @Override
            public boolean accept(File dir, String name) {
                return name.endsWith(".jar") || name.endsWith(".zip");
            }
        };

        for (String folder : folders) {
            File pojoLibPath = ResourceUtils.getPluginBinaryPath("com.ebmwebsourcing.petals.libs.esb", folder); //$NON-NLS-1$
            if (pojoLibPath == null) {
                PetalsCommonPlugin.log("Could not find the Petals libraries in the distribution.", IStatus.ERROR);
                throw new IOException("Petals libraries could not be located.");
            }

            // Add the libraries in the project class path
            File[] jarFiles = pojoLibPath.listFiles(filter);
            if (jarFiles != null) {
                for (File jarFile : jarFiles) {
                    IPath path = new Path(jarFile.getAbsolutePath());
                    IClasspathEntry entry = JavaCore.newLibraryEntry(path, null, null);
                    entries.add(entry);
                }
            }
        }

        IClasspathEntry[] newEntries = CollectionUtils.convertToArray(entries, IClasspathEntry.class);
        if (!jp.hasClasspathCycle(newEntries))
            jp.setRawClasspath(newEntries, monitor);
    }

    /**
     * Makes an Eclipse project a Java project.
     * <p>
     * If the project does not exist, or is not accessible, then nothing is done.<br />
     * If the project does not have the Java nature, this nature is added.
     * </p>
     * <p>
     * The default output directory is "bin". If this directory does not exist, it is created.
     * If the creation fails, then no output directory is set.
     * </p>
     * <p>
     * The default source directory is "src/main/java". If this directory does not exist, it is created.
     * If the creation fails, then no source directory is set.
     * </p>
     *
     * @param project the project to transform into a Java project
     * @return the created Java project
     * @throws CoreException if something went wrong
     */
    public static IJavaProject createJavaProject(IProject project) throws CoreException {

        IJavaProject jp = null;
        if (project.isAccessible()) {

            // Add the Java nature?
            if (!project.hasNature(JavaCore.NATURE_ID)) {

                IProjectDescription description = project.getDescription();
                String[] natures = description.getNatureIds();
                String[] newNatures = new String[natures.length + 1];

                System.arraycopy(natures, 0, newNatures, 0, natures.length);
                newNatures[natures.length] = JavaCore.NATURE_ID;
                description.setNatureIds(newNatures);
                project.setDescription(description, null);
            }

            // Set the default class path
            jp = JavaCore.create(project);
            IProgressMonitor monitor = new NullProgressMonitor();

            //
            // Output location
            IPath ppath = project.getFullPath();
            IPath binPath = ppath.append(PetalsConstants.LOC_BIN_FOLDER);
            File binFile = ResourcesPlugin.getWorkspace().getRoot().getLocation().append(binPath).toFile();
            if (binFile.exists() && binFile.isDirectory() || !binFile.exists() && binFile.mkdirs()) {
                jp.setOutputLocation(binPath, monitor);
            }

            Set<IClasspathEntry> entries = new HashSet<IClasspathEntry>();
            entries.addAll(Arrays.asList(jp.getRawClasspath()));
            entries.add(JavaRuntime.getDefaultJREContainerEntry());

            //
            // Remove the "src" entry
            IClasspathEntry srcEntry = null;
            for (IClasspathEntry entry : entries) {
                if (entry.getEntryKind() == IClasspathEntry.CPE_SOURCE) {
                    srcEntry = entry;
                    break;
                }
            }

            //
            // Specify a new source entry
            if (srcEntry != null)
                entries.remove(srcEntry);

            String[] srcPaths = new String[] { PetalsConstants.LOC_SRC_FOLDER,
                    PetalsConstants.LOC_JAVA_RES_FOLDER };
            for (String s : srcPaths) {
                IPath srcPath = ppath.append(s);
                File srcFile = ResourcesPlugin.getWorkspace().getRoot().getLocation().append(srcPath).toFile();
                if (srcFile.exists() && srcFile.isDirectory() || !srcFile.exists() && srcFile.mkdirs()) {

                    project.refreshLocal(IResource.DEPTH_INFINITE, monitor);
                    srcEntry = JavaCore.newSourceEntry(srcPath);
                    entries.add(srcEntry);
                } else {
                    PetalsCommonPlugin.log("Could not set '" + s + "' as a source folder.", IStatus.ERROR);
                }
            }

            jp.setRawClasspath(entries.toArray(new IClasspathEntry[entries.size()]), monitor);
        }

        return jp;
    }

    /**
     * Gets the source folders of a IJavaProject.
     * @param javaProject
     * @return the list of source folders in this Java project
     */
    public static List<IClasspathEntry> getSourceFolders(IJavaProject javaProject) {

        List<IClasspathEntry> result = new ArrayList<IClasspathEntry>();
        try {
            for (IClasspathEntry entry : javaProject.getRawClasspath()) {
                if (entry.getEntryKind() == IClasspathEntry.CPE_SOURCE)
                    result.add(entry);
            }

        } catch (JavaModelException e) {
            PetalsCommonPlugin.log(e, IStatus.ERROR);
        }

        return result;
    }

    /**
     * Get the class path from Java project.
     *
     * @param javaProject
     * @param getReferencedProjectClasspath
     * @param binaryDirectory
     * @return the class path as a list of string locations.
     */
    public static List<String> getClasspath(IJavaProject javaProject, boolean getReferencedProjectClasspath,
            boolean binaryDirectory) {

        List<String> paths = new ArrayList<String>();
        try {
            if (javaProject != null) {

                // Get the raw class path
                IClasspathEntry[] entries = javaProject.getRawClasspath();
                for (IClasspathEntry entry : entries) {
                    switch (entry.getEntryKind()) {

                    case IClasspathEntry.CPE_PROJECT:
                        if (!getReferencedProjectClasspath)
                            break;

                        String projectName = entry.getPath().toString();
                        IProject project = ResourcesPlugin.getWorkspace().getRoot().getProject(projectName);
                        IJavaProject jProject = JavaCore.create(project);

                        List<String> subPaths = getClasspath(jProject, true, binaryDirectory);
                        paths.addAll(subPaths);
                        break;

                    case IClasspathEntry.CPE_LIBRARY:
                        IPath path = entry.getPath();
                        paths.add(path.toString());
                        break;

                    case IClasspathEntry.CPE_VARIABLE:
                        entry = JavaCore.getResolvedClasspathEntry(entry);
                        if (entry != null) {
                            path = entry.getPath();
                            paths.add(path.toString());
                        }
                        break;

                    }
                }

                // Add the "bin" directory?
                if (binaryDirectory && javaProject.getOutputLocation() != null) {
                    IPath path = ResourcesPlugin.getWorkspace().getRoot().getLocation();
                    path = path.append(javaProject.getOutputLocation());
                    paths.add(path.toString());
                }
            }

        } catch (JavaModelException e) {
            PetalsCommonPlugin.log(e, IStatus.ERROR);
        }

        return paths;
    }

    /**
     * Creates a JAR file containing all the resources contained in the source folders of a Java project.
     * @param jp the Java project
     * @param monitor the progress monitor
     * @return the JAR file, which was saved in the temporary folder (and should be deleted after use)
     * @throws InvocationTargetException if the creation failed
     * @throws InterruptedException if the creation was interrupted
     */
    public static File createDefaultJar(IJavaProject jp, IProgressMonitor monitor)
            throws InvocationTargetException, InterruptedException {

        // Create a default JAR for the implementation
        JarPackageData jarDescription = new JarPackageData();
        jarDescription.setIncludeDirectoryEntries(true);
        jarDescription.setExportClassFiles(true);
        jarDescription.setOverwrite(true);
        jarDescription.setIncludeDirectoryEntries(true);

        jarDescription.setExportJavaFiles(false);
        jarDescription.setCompress(true);
        jarDescription.setExportErrors(true);
        jarDescription.setExportWarnings(true);

        // Add all the files in the source folders
        List<Object> filesToPackage = new ArrayList<Object>();
        for (IClasspathEntry entry : getSourceFolders(jp)) {

            // PETALSSTUD-130: use the project location and not the workspace root as a reference
            File f = jp.getProject().getLocation().append(entry.getPath().removeFirstSegments(1)).toFile();
            // PETALSSTUD-130

            IFolder folder = (IFolder) ResourceUtils.getResource(f);
            if (folder != null) {
                List<IFile> files = ResourceUtils.getFilesByRegexp(folder, ".*");
                filesToPackage.addAll(files);
            }
        }

        Object[] elements = new Object[filesToPackage.size()];
        jarDescription.setElements(filesToPackage.toArray(elements));

        // Create the JAR
        IPath jarLocation = new Path(System.getProperty("java.io.tmpdir"))
                .append(jp.getProject().getName() + ".jar");

        // Bug: Windows => path separator = "\"
        // But IPath#isAbsolute() only checks for "/" path separators
        // => We replace the path separator
        // Otherwise, the JAR is created, but not at the location we would expect
        jarLocation = new Path(jarLocation.toString().replaceAll("\\\\", "/"));
        jarDescription.setJarLocation(jarLocation);

        IJarExportRunnable runnable = jarDescription.createJarExportRunnable(null);
        runnable.run(monitor);
        IStatus status = runnable.getStatus();
        if (!status.isOK())
            PetalsCommonPlugin.getDefault().getLog().log(status);

        return jarLocation.toFile();
    }

    /**
     * Creates a new source folder.
     * <p>
     * If the folder does not exist, it is created.<br />
     * If the directory is already in the class path, then this method does nothing.
     * </p>
     *
     * @param jp the Java project
     * @param folderName the folder name
     * @param monitor the progress monitor
     * @return the created folder
     * @throws CoreException if something went wrong
     */
    public static IFolder createSourceFolder(IJavaProject jp, String folderName, IProgressMonitor monitor)
            throws CoreException {

        IFolder newSourceFolder = jp.getProject().getFolder(folderName);
        if (!newSourceFolder.exists())
            newSourceFolder.create(true, true, monitor);

        IClasspathEntry srcEntry = JavaCore.newSourceEntry(newSourceFolder.getFullPath());
        Set<IClasspathEntry> entries = new HashSet<IClasspathEntry>();
        entries.addAll(Arrays.asList(jp.getRawClasspath()));
        entries.add(srcEntry);
        jp.setRawClasspath(entries.toArray(new IClasspathEntry[entries.size()]), monitor);

        return newSourceFolder;
    }

    /**
     * Gets a set of JAR files from a Java project, in order to package them.
     * @param jp the Java project
     * @param monitor the progress monitor
     * @return an instance of {@link ExportableClassPath} (never null)
     */
    public static ExportableClassPath getExportableClasspath(IJavaProject jp, IProgressMonitor monitor) {

        ExportableClassPath result = new ExportableClassPath();
        try {
            // Check the class path
            IClasspathEntry[] entries = jp.getRawClasspath();
            for (IClasspathEntry entry : entries)
                updateExportableResult(result, entry, monitor);

            // Package the implementation
            File jarFile = createDefaultJar(jp, monitor);
            result.implementationJars.add(jarFile);
            monitor.worked(1);

        } catch (JavaModelException e) {
            PetalsCommonPlugin.log(e, IStatus.ERROR);

        } catch (InvocationTargetException e) {
            PetalsCommonPlugin.log(e, IStatus.ERROR);

        } catch (InterruptedException e) {
            PetalsCommonPlugin.log(e, IStatus.ERROR);
        }

        return result;
    }

    /**
     * Populates an {@link ExportableClassPath} instance from a class path entry.
     * @param result the {@link ExportableClassPath} instance to populate
     * @param entry a class path entry
     * @param monitor the progress monitor
     */
    private static void updateExportableResult(ExportableClassPath result, IClasspathEntry entry,
            IProgressMonitor monitor) {

        switch (entry.getEntryKind()) {

        case IClasspathEntry.CPE_PROJECT:
            String projectName = entry.getPath().toString();
            IProject project = ResourcesPlugin.getWorkspace().getRoot().getProject(projectName);
            IJavaProject jProject = JavaCore.create(project);

            ExportableClassPath subResult = getExportableClasspath(jProject, monitor);
            result.implementationJars.addAll(subResult.implementationJars);
            result.libraryJars.addAll(subResult.libraryJars);
            monitor.worked(1);
            break;

        case IClasspathEntry.CPE_LIBRARY:
            IPath path = entry.getPath();
            if (path != null) {
                File f = path.toFile();
                if (f.exists()) {
                    if (f.getName().endsWith(".zip") || f.getName().endsWith(".jar"))
                        result.libraryJars.add(f);
                }
            }
            break;

        case IClasspathEntry.CPE_VARIABLE:
            entry = JavaCore.getResolvedClasspathEntry(entry);
            if (entry != null)
                updateExportableResult(result, entry, monitor);

            break;
        }
    }

    /**
     * This class is used to manage the class path elements to export for a Java project.
     * <p>
     * It manages through 2 distinct lists the libraries used in the project, and the
     * implementations JAR that are built during the export operation.
     * </p>
     * <p>
     * Once this class has been used, the method {@link #deleteImplementationJars()} should
     * be invoked.
     * </p>
     */
    public static class ExportableClassPath {

        private final Set<File> libraryJars = new HashSet<File>();
        private final Set<File> implementationJars = new HashSet<File>();

        /**
         * @return the class path, including both the library and the
         */
        public Set<File> getExportableClassPath() {

            Set<File> result = new HashSet<File>();
            result.addAll(this.libraryJars);
            result.addAll(this.implementationJars);

            return result;
        }

        /**
         * Deletes the all (temporary) implementation JAR files.
         */
        public void deleteImplementationJars() {

            for (File f : this.implementationJars) {

                if (!f.exists())
                    PetalsCommonPlugin.log(f.getAbsolutePath() + " does not exist.", IStatus.WARNING);

                if (f.exists() && !f.delete())
                    f.deleteOnExit();
            }
        }
    }

    /**
     * Finds the direct children for a given package.
     * <p>
     * Copied (or almost) from the JDT.
     * </p>
     *
     * @param parent the package fragment root (will be searched from <code>fragment</code> if null)
     * @param fragment the fragment to analyze (can be null if <code>parent</code> is not)
     * @return a list of package fragments
     * @throws JavaModelException if an error occurred with the Java model
     */
    public static List<IPackageFragment> findDirectSubPackages(IPackageFragmentRoot parent,
            IPackageFragment fragment) throws JavaModelException {

        // Special case for the default package
        if (fragment != null && fragment.isDefaultPackage())
            return Collections.emptyList();

        // Find the package parent?
        if (parent == null) {
            IJavaElement elt = fragment;
            while (elt.getParent() != null) {
                elt = elt.getParent();
                if (elt instanceof IPackageFragmentRoot) {
                    parent = (IPackageFragmentRoot) elt;
                    break;
                }
            }
        }

        // Find the direct children
        List<IPackageFragment> result = new ArrayList<IPackageFragment>();
        if (parent != null) {
            IJavaElement[] children = parent.getChildren();
            String prefix = fragment != null ? fragment.getElementName() + '.' : ""; //$NON-NLS-1$
            int prefixLen = prefix.length();
            for (IJavaElement element : children) {
                IPackageFragment curr = (IPackageFragment) element;
                String name = curr.getElementName();
                if (name.startsWith(prefix) && name.length() > prefixLen && name.indexOf('.', prefixLen) == -1)
                    result.add(curr);
                else if (fragment == null && curr.isDefaultPackage())
                    result.add(curr);
            }
        }

        return result;
    }
}