com.android.ide.eclipse.adt.internal.wizards.exportgradle.ProjectSetupBuilder.java Source code

Java tutorial

Introduction

Here is the source code for com.android.ide.eclipse.adt.internal.wizards.exportgradle.ProjectSetupBuilder.java

Source

/*
 * Copyright (C) 2013 The Android Open Source Project
 *
 * Licensed under the Eclipse Public License, Version 1.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.eclipse.org/org/documents/epl-v10.php
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.android.ide.eclipse.adt.internal.wizards.exportgradle;

import com.android.annotations.NonNull;
import com.android.annotations.Nullable;
import com.android.ide.eclipse.adt.AdtConstants;
import com.android.ide.eclipse.adt.internal.project.BaseProjectHelper;
import com.android.ide.eclipse.adt.internal.sdk.ProjectState;
import com.android.ide.eclipse.adt.internal.sdk.ProjectState.LibraryState;
import com.android.ide.eclipse.adt.internal.sdk.Sdk;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;

import org.eclipse.core.resources.IProject;
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.Path;
import org.eclipse.jdt.core.IClasspathEntry;
import org.eclipse.jdt.core.IJavaProject;
import org.eclipse.jdt.core.IPackageFragmentRoot;
import org.eclipse.jdt.core.JavaCore;
import org.eclipse.jdt.core.JavaModelException;

import java.io.File;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.regex.Pattern;

/**
 * Class to setup the project and its modules.
 */
public class ProjectSetupBuilder {

    private final static class InternalException extends Exception {
        private static final long serialVersionUID = 1L;

        InternalException(String message) {
            super(message);
        }
    }

    private boolean mCanFinish = false;
    private boolean mCanGenerate = false;
    private final List<GradleModule> mOriginalModules = Lists.newArrayList();
    private final Map<IJavaProject, GradleModule> mModules = Maps.newHashMap();
    private IPath mCommonRoot;
    private ExportStatus mStatus;

    public ProjectSetupBuilder() {

    }

    public void setCanGenerate(boolean generate) {
        mCanGenerate = generate;
    }

    public void setCanFinish(boolean canFinish) {
        mCanFinish = canFinish;
    }

    public boolean canFinish() {
        return mCanFinish;
    }

    public boolean canGenerate() {
        return mCanGenerate;
    }

    public void setStatus(ExportStatus status) {
        mStatus = status;
    }

    public ExportStatus getStatus() {
        return mStatus;
    }

    @NonNull
    public String setProject(@NonNull List<IJavaProject> selectedProjects) throws CoreException {
        mModules.clear();

        // build a list of all projects that must be included. This is in case
        // some dependencies have not been included in the selected projects. We also include
        // parent projects so that the full multi-project setup is correct.
        // Note that if two projects are selected that are not related, both will be added
        // in the same multi-project anyway.
        try {
            for (IJavaProject javaProject : selectedProjects) {
                GradleModule module;

                if (javaProject.getProject().hasNature(AdtConstants.NATURE_DEFAULT)) {
                    module = processAndroidProject(javaProject);
                } else {
                    module = processJavaProject(javaProject);
                }

                mOriginalModules.add(module);
            }

            Collection<GradleModule> modules = mModules.values();
            computeRootAndPaths(modules);

            return null;
        } catch (InternalException e) {
            return e.getMessage();
        }
    }

    @NonNull
    public Collection<GradleModule> getModules() {
        return mModules.values();
    }

    public int getModuleCount() {
        return mModules.size();
    }

    @Nullable
    public IPath getCommonRoot() {
        return mCommonRoot;
    }

    @Nullable
    public GradleModule getModule(IJavaProject javaProject) {
        return mModules.get(javaProject);
    }

    public boolean isOriginalProject(@NonNull IJavaProject javaProject) {
        GradleModule module = mModules.get(javaProject);
        return mOriginalModules.contains(module);
    }

    @NonNull
    public List<GradleModule> getOriginalModules() {
        return mOriginalModules;
    }

    @Nullable
    public List<GradleModule> getShortestDependencyTo(GradleModule module) {
        return findModule(module, mOriginalModules);
    }

    @Nullable
    public List<GradleModule> findModule(GradleModule toFind, GradleModule rootModule) {
        if (toFind == rootModule) {
            List<GradleModule> list = Lists.newArrayList();
            list.add(toFind);
            return list;
        }

        List<GradleModule> shortestChain = findModule(toFind, rootModule.getDependencies());

        if (shortestChain != null) {
            shortestChain.add(0, rootModule);
        }

        return shortestChain;
    }

    @Nullable
    public List<GradleModule> findModule(GradleModule toFind, List<GradleModule> modules) {
        List<GradleModule> currentChain = null;

        for (GradleModule child : modules) {
            List<GradleModule> newChain = findModule(toFind, child);
            if (currentChain == null) {
                currentChain = newChain;
            } else if (newChain != null) {
                if (currentChain.size() > newChain.size()) {
                    currentChain = newChain;
                }
            }
        }

        return currentChain;
    }

    @NonNull
    private GradleModule processAndroidProject(@NonNull IJavaProject javaProject)
            throws InternalException, CoreException {

        // get/create the module
        GradleModule module = createModuleOnDemand(javaProject);
        if (module.isConfigured()) {
            return module;
        }

        module.setType(GradleModule.Type.ANDROID);

        ProjectState projectState = Sdk.getProjectState(javaProject.getProject());
        assert projectState != null;

        // add library project dependencies
        List<LibraryState> libraryProjects = projectState.getLibraries();
        for (LibraryState libraryState : libraryProjects) {
            ProjectState libProjectState = libraryState.getProjectState();
            if (libProjectState != null) {
                IJavaProject javaLib = getJavaProject(libProjectState);
                if (javaLib != null) {
                    GradleModule libModule = processAndroidProject(javaLib);
                    module.addDependency(libModule);
                } else {
                    throw new InternalException(String.format(
                            "Project %1$s is missing. Needed by %2$s.\n" + "Make sure all dependencies are opened.",
                            libraryState.getRelativePath(), javaProject.getProject().getName()));
                }
            } else {
                throw new InternalException(String.format(
                        "Project %1$s is missing. Needed by %2$s.\n" + "Make sure all dependencies are opened.",
                        libraryState.getRelativePath(), javaProject.getProject().getName()));
            }
        }

        // add java project dependencies
        List<IJavaProject> javaDepProjects = getReferencedProjects(javaProject);
        for (IJavaProject javaDep : javaDepProjects) {
            GradleModule libModule = processJavaProject(javaDep);
            module.addDependency(libModule);
        }

        return module;
    }

    @NonNull
    private GradleModule processJavaProject(@NonNull IJavaProject javaProject)
            throws InternalException, CoreException {
        // get/create the module
        GradleModule module = createModuleOnDemand(javaProject);

        if (module.isConfigured()) {
            return module;
        }

        module.setType(GradleModule.Type.JAVA);

        // add java project dependencies
        List<IJavaProject> javaDepProjects = getReferencedProjects(javaProject);
        for (IJavaProject javaDep : javaDepProjects) {
            // Java project should not reference Android project!
            if (javaDep.getProject().hasNature(AdtConstants.NATURE_DEFAULT)) {
                throw new InternalException(String.format(
                        "Java project %1$s depends on Android project %2$s!\n" + "This is not a valid dependency",
                        javaProject.getProject().getName(), javaDep.getProject().getName()));
            }
            GradleModule libModule = processJavaProject(javaDep);
            module.addDependency(libModule);
        }

        return module;
    }

    private void computeRootAndPaths(Collection<GradleModule> modules) throws InternalException {
        // compute the common root.
        mCommonRoot = determineCommonRoot(modules);

        // compute all the relative paths.
        for (GradleModule module : modules) {
            String path = getGradlePath(module.getJavaProject().getProject().getLocation(), mCommonRoot);

            module.setPath(path);
        }
    }

    /**
     * Finds the common parent directory shared by this project and all its dependencies.
     * If there's only one project, returns the single project's folder.
     * @throws InternalException
     */
    @NonNull
    private static IPath determineCommonRoot(Collection<GradleModule> modules) throws InternalException {
        IPath commonRoot = null;
        for (GradleModule module : modules) {
            if (commonRoot == null) {
                commonRoot = module.getJavaProject().getProject().getLocation();
            } else {
                commonRoot = findCommonRoot(commonRoot, module.getJavaProject().getProject().getLocation());
            }
        }

        return commonRoot;
    }

    /**
     * Converts the given path to be relative to the given root path, and converts it to
     * Gradle project notation, such as is used in the settings.gradle file.
     */
    @NonNull
    private static String getGradlePath(IPath path, IPath root) {
        IPath relativePath = path.makeRelativeTo(root);
        String relativeString = relativePath.toOSString();
        return ":" + relativeString.replaceAll(Pattern.quote(File.separator), ":"); //$NON-NLS-1$
    }

    /**
     * Given two IPaths, finds the parent directory of both of them.
     * @throws InternalException
     */
    @NonNull
    private static IPath findCommonRoot(@NonNull IPath path1, @NonNull IPath path2) throws InternalException {
        if (path1.getDevice() != null && !path1.getDevice().equals(path2.getDevice())) {
            throw new InternalException("Different modules have been detected on different drives.\n"
                    + "This prevents finding a common root to all modules.");
        }

        IPath result = path1.uptoSegment(0);

        final int count = Math.min(path1.segmentCount(), path2.segmentCount());
        for (int i = 0; i < count; i++) {
            if (path1.segment(i).equals(path2.segment(i))) {
                result = result.append(Path.SEPARATOR + path2.segment(i));
            }
        }
        return result;
    }

    @Nullable
    private IJavaProject getJavaProject(ProjectState projectState) {
        try {
            return BaseProjectHelper.getJavaProject(projectState.getProject());
        } catch (CoreException e) {
            return null;
        }
    }

    @NonNull
    private GradleModule createModuleOnDemand(@NonNull IJavaProject javaProject) {
        GradleModule module = mModules.get(javaProject);
        if (module == null) {
            module = new GradleModule(javaProject);
            mModules.put(javaProject, module);
        }

        return module;
    }

    @NonNull
    private static List<IJavaProject> getReferencedProjects(IJavaProject javaProject)
            throws JavaModelException, InternalException {

        List<IJavaProject> projects = Lists.newArrayList();

        IClasspathEntry entries[] = javaProject.getRawClasspath();
        for (IClasspathEntry classpathEntry : entries) {
            if (classpathEntry.getContentKind() == IPackageFragmentRoot.K_SOURCE
                    && classpathEntry.getEntryKind() == IClasspathEntry.CPE_PROJECT) {
                // found required project on build path
                String subProjectRoot = classpathEntry.getPath().toString();
                IJavaProject subProject = getJavaProject(subProjectRoot);
                // is project available in workspace?
                if (subProject != null) {
                    projects.add(subProject);
                } else {
                    throw new InternalException(String.format(
                            "Project '%s' is missing project dependency '%s' in Eclipse workspace.\n"
                                    + "Make sure all dependencies are opened.",
                            javaProject.getProject().getName(), classpathEntry.getPath().toString()));
                }
            }
        }

        return projects;
    }

    /**
     * Get Java project for given root.
     */
    @Nullable
    private static IJavaProject getJavaProject(String root) {
        IPath path = new Path(root);
        if (path.segmentCount() == 1) {
            return getJavaProjectByName(root);
        }
        IResource resource = ResourcesPlugin.getWorkspace().getRoot().findMember(path);
        if (resource != null && resource.getType() == IResource.PROJECT) {
            if (resource.exists()) {
                return (IJavaProject) JavaCore.create(resource);
            }
        }
        return null;
    }

    /**
     * Get Java project from resource.
     */
    private static IJavaProject getJavaProjectByName(String name) {
        try {
            IProject project = ResourcesPlugin.getWorkspace().getRoot().getProject(name);
            if (project.exists()) {
                return JavaCore.create(project);
            }
        } catch (IllegalArgumentException iae) {
        }
        return null;
    }
}