aboutsummaryrefslogtreecommitdiff
path: root/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/wizards/exportgradle/ProjectSetupBuilder.java
diff options
context:
space:
mode:
Diffstat (limited to 'eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/wizards/exportgradle/ProjectSetupBuilder.java')
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/wizards/exportgradle/ProjectSetupBuilder.java425
1 files changed, 425 insertions, 0 deletions
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/wizards/exportgradle/ProjectSetupBuilder.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/wizards/exportgradle/ProjectSetupBuilder.java
new file mode 100644
index 000000000..1fd6b74f6
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/wizards/exportgradle/ProjectSetupBuilder.java
@@ -0,0 +1,425 @@
+/*
+ * 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;
+ }
+}