aboutsummaryrefslogtreecommitdiff
path: root/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/project/ProjectHelper.java
diff options
context:
space:
mode:
Diffstat (limited to 'eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/project/ProjectHelper.java')
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/project/ProjectHelper.java1153
1 files changed, 1153 insertions, 0 deletions
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/project/ProjectHelper.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/project/ProjectHelper.java
new file mode 100644
index 000000000..a32b4ca8b
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/project/ProjectHelper.java
@@ -0,0 +1,1153 @@
+/*
+ * Copyright (C) 2007 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.project;
+
+import static com.android.ide.eclipse.adt.AdtConstants.COMPILER_COMPLIANCE_PREFERRED;
+
+import com.android.SdkConstants;
+import com.android.annotations.NonNull;
+import com.android.ide.common.xml.ManifestData;
+import com.android.ide.eclipse.adt.AdtConstants;
+import com.android.ide.eclipse.adt.AdtPlugin;
+import com.android.ide.eclipse.adt.internal.build.builders.PostCompilerBuilder;
+import com.android.ide.eclipse.adt.internal.build.builders.PreCompilerBuilder;
+import com.android.ide.eclipse.adt.internal.preferences.AdtPrefs;
+import com.android.ide.eclipse.adt.internal.sdk.ProjectState;
+import com.android.ide.eclipse.adt.internal.sdk.Sdk;
+import com.android.sdklib.BuildToolInfo;
+import com.android.sdklib.IAndroidTarget;
+import com.android.utils.Pair;
+
+import org.eclipse.core.resources.ICommand;
+import org.eclipse.core.resources.IFile;
+import org.eclipse.core.resources.IFolder;
+import org.eclipse.core.resources.IMarker;
+import org.eclipse.core.resources.IProject;
+import org.eclipse.core.resources.IProjectDescription;
+import org.eclipse.core.resources.IResource;
+import org.eclipse.core.resources.IWorkspace;
+import org.eclipse.core.resources.IncrementalProjectBuilder;
+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.NullProgressMonitor;
+import org.eclipse.core.runtime.Path;
+import org.eclipse.core.runtime.QualifiedName;
+import org.eclipse.jdt.core.IClasspathEntry;
+import org.eclipse.jdt.core.IJavaModel;
+import org.eclipse.jdt.core.IJavaProject;
+import org.eclipse.jdt.core.JavaCore;
+import org.eclipse.jdt.core.JavaModelException;
+import org.eclipse.jdt.internal.corext.util.JavaModelUtil;
+import org.eclipse.jdt.launching.IVMInstall;
+import org.eclipse.jdt.launching.IVMInstall2;
+import org.eclipse.jdt.launching.IVMInstallType;
+import org.eclipse.jdt.launching.JavaRuntime;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.TreeMap;
+
+/**
+ * Utility class to manipulate Project parameters/properties.
+ */
+public final class ProjectHelper {
+ public final static int COMPILER_COMPLIANCE_OK = 0;
+ public final static int COMPILER_COMPLIANCE_LEVEL = 1;
+ public final static int COMPILER_COMPLIANCE_SOURCE = 2;
+ public final static int COMPILER_COMPLIANCE_CODEGEN_TARGET = 3;
+
+ /**
+ * Adds the given ClasspathEntry object to the class path entries.
+ * This method does not check whether the entry is already defined in the project.
+ *
+ * @param entries The class path entries to read. A copy will be returned.
+ * @param newEntry The new class path entry to add.
+ * @return A new class path entries array.
+ */
+ public static IClasspathEntry[] addEntryToClasspath(
+ IClasspathEntry[] entries, IClasspathEntry newEntry) {
+ int n = entries.length;
+ IClasspathEntry[] newEntries = new IClasspathEntry[n + 1];
+ System.arraycopy(entries, 0, newEntries, 0, n);
+ newEntries[n] = newEntry;
+ return newEntries;
+ }
+
+ /**
+ * Replaces the given ClasspathEntry in the classpath entries.
+ *
+ * If the classpath does not yet exists (Check is based on entry path), then it is added.
+ *
+ * @param entries The class path entries to read. The same array (replace) or a copy (add)
+ * will be returned.
+ * @param newEntry The new class path entry to add.
+ * @return The same array (replace) or a copy (add) will be returned.
+ *
+ * @see IClasspathEntry#getPath()
+ */
+ public static IClasspathEntry[] replaceEntryInClasspath(
+ IClasspathEntry[] entries, IClasspathEntry newEntry) {
+
+ IPath path = newEntry.getPath();
+ for (int i = 0, count = entries.length; i < count ; i++) {
+ if (path.equals(entries[i].getPath())) {
+ entries[i] = newEntry;
+ return entries;
+ }
+ }
+
+ return addEntryToClasspath(entries, newEntry);
+ }
+
+ /**
+ * Adds the corresponding source folder to the project's class path entries.
+ * This method does not check whether the entry is already defined in the project.
+ *
+ * @param javaProject The java project of which path entries to update.
+ * @param newEntry The new class path entry to add.
+ * @throws JavaModelException
+ */
+ public static void addEntryToClasspath(IJavaProject javaProject, IClasspathEntry newEntry)
+ throws JavaModelException {
+
+ IClasspathEntry[] entries = javaProject.getRawClasspath();
+ entries = addEntryToClasspath(entries, newEntry);
+ javaProject.setRawClasspath(entries, new NullProgressMonitor());
+ }
+
+ /**
+ * Checks whether the given class path entry is already defined in the project.
+ *
+ * @param javaProject The java project of which path entries to check.
+ * @param newEntry The parent source folder to remove.
+ * @return True if the class path entry is already defined.
+ * @throws JavaModelException
+ */
+ public static boolean isEntryInClasspath(IJavaProject javaProject, IClasspathEntry newEntry)
+ throws JavaModelException {
+
+ IClasspathEntry[] entries = javaProject.getRawClasspath();
+ for (IClasspathEntry entry : entries) {
+ if (entry.equals(newEntry)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Remove a classpath entry from the array.
+ * @param entries The class path entries to read. A copy will be returned
+ * @param index The index to remove.
+ * @return A new class path entries array.
+ */
+ public static IClasspathEntry[] removeEntryFromClasspath(
+ IClasspathEntry[] entries, int index) {
+ int n = entries.length;
+ IClasspathEntry[] newEntries = new IClasspathEntry[n-1];
+
+ // copy the entries before index
+ System.arraycopy(entries, 0, newEntries, 0, index);
+
+ // copy the entries after index
+ System.arraycopy(entries, index + 1, newEntries, index,
+ entries.length - index - 1);
+
+ return newEntries;
+ }
+
+ /**
+ * Converts a OS specific path into a path valid for the java doc location
+ * attributes of a project.
+ * @param javaDocOSLocation The OS specific path.
+ * @return a valid path for the java doc location.
+ */
+ public static String getJavaDocPath(String javaDocOSLocation) {
+ // first thing we do is convert the \ into /
+ String javaDoc = javaDocOSLocation.replaceAll("\\\\", //$NON-NLS-1$
+ AdtConstants.WS_SEP);
+
+ // then we add file: at the beginning for unix path, and file:/ for non
+ // unix path
+ if (javaDoc.startsWith(AdtConstants.WS_SEP)) {
+ return "file:" + javaDoc; //$NON-NLS-1$
+ }
+
+ return "file:/" + javaDoc; //$NON-NLS-1$
+ }
+
+ /**
+ * Look for a specific classpath entry by full path and return its index.
+ * @param entries The entry array to search in.
+ * @param entryPath The OS specific path of the entry.
+ * @param entryKind The kind of the entry. Accepted values are 0
+ * (no filter), IClasspathEntry.CPE_LIBRARY, IClasspathEntry.CPE_PROJECT,
+ * IClasspathEntry.CPE_SOURCE, IClasspathEntry.CPE_VARIABLE,
+ * and IClasspathEntry.CPE_CONTAINER
+ * @return the index of the found classpath entry or -1.
+ */
+ public static int findClasspathEntryByPath(IClasspathEntry[] entries,
+ String entryPath, int entryKind) {
+ for (int i = 0 ; i < entries.length ; i++) {
+ IClasspathEntry entry = entries[i];
+
+ int kind = entry.getEntryKind();
+
+ if (kind == entryKind || entryKind == 0) {
+ // get the path
+ IPath path = entry.getPath();
+
+ String osPathString = path.toOSString();
+ if (osPathString.equals(entryPath)) {
+ return i;
+ }
+ }
+ }
+
+ // not found, return bad index.
+ return -1;
+ }
+
+ /**
+ * Look for a specific classpath entry for file name only and return its
+ * index.
+ * @param entries The entry array to search in.
+ * @param entryName The filename of the entry.
+ * @param entryKind The kind of the entry. Accepted values are 0
+ * (no filter), IClasspathEntry.CPE_LIBRARY, IClasspathEntry.CPE_PROJECT,
+ * IClasspathEntry.CPE_SOURCE, IClasspathEntry.CPE_VARIABLE,
+ * and IClasspathEntry.CPE_CONTAINER
+ * @param startIndex Index where to start the search
+ * @return the index of the found classpath entry or -1.
+ */
+ public static int findClasspathEntryByName(IClasspathEntry[] entries,
+ String entryName, int entryKind, int startIndex) {
+ if (startIndex < 0) {
+ startIndex = 0;
+ }
+ for (int i = startIndex ; i < entries.length ; i++) {
+ IClasspathEntry entry = entries[i];
+
+ int kind = entry.getEntryKind();
+
+ if (kind == entryKind || entryKind == 0) {
+ // get the path
+ IPath path = entry.getPath();
+ String name = path.segment(path.segmentCount()-1);
+
+ if (name.equals(entryName)) {
+ return i;
+ }
+ }
+ }
+
+ // not found, return bad index.
+ return -1;
+ }
+
+ public static boolean updateProject(IJavaProject project) {
+ return updateProjects(new IJavaProject[] { project});
+ }
+
+ /**
+ * Update the android-specific projects's classpath containers.
+ * @param projects the projects to update
+ * @return
+ */
+ public static boolean updateProjects(IJavaProject[] projects) {
+ boolean r = AndroidClasspathContainerInitializer.updateProjects(projects);
+ if (r) {
+ return LibraryClasspathContainerInitializer.updateProjects(projects);
+ }
+ return false;
+ }
+
+ /**
+ * Fix the project. This checks the SDK location.
+ * @param project The project to fix.
+ * @throws JavaModelException
+ */
+ public static void fixProject(IProject project) throws JavaModelException {
+ if (AdtPlugin.getOsSdkFolder().length() == 0) {
+ AdtPlugin.printToConsole(project, "Unknown SDK Location, project not fixed.");
+ return;
+ }
+
+ // get a java project
+ IJavaProject javaProject = JavaCore.create(project);
+ fixProjectClasspathEntries(javaProject);
+ }
+
+ /**
+ * Fix the project classpath entries. The method ensures that:
+ * <ul>
+ * <li>The project does not reference any old android.zip/android.jar archive.</li>
+ * <li>The project does not use its output folder as a sourc folder.</li>
+ * <li>The project does not reference a desktop JRE</li>
+ * <li>The project references the AndroidClasspathContainer.
+ * </ul>
+ * @param javaProject The project to fix.
+ * @throws JavaModelException
+ */
+ public static void fixProjectClasspathEntries(IJavaProject javaProject)
+ throws JavaModelException {
+
+ // get the project classpath
+ IClasspathEntry[] entries = javaProject.getRawClasspath();
+ IClasspathEntry[] oldEntries = entries;
+ boolean forceRewriteOfCPE = false;
+
+ // check if the JRE is set as library
+ int jreIndex = ProjectHelper.findClasspathEntryByPath(entries, JavaRuntime.JRE_CONTAINER,
+ IClasspathEntry.CPE_CONTAINER);
+ if (jreIndex != -1) {
+ // the project has a JRE included, we remove it
+ entries = ProjectHelper.removeEntryFromClasspath(entries, jreIndex);
+ }
+
+ // get the output folder
+ IPath outputFolder = javaProject.getOutputLocation();
+
+ boolean foundFrameworkContainer = false;
+ IClasspathEntry foundLibrariesContainer = null;
+ IClasspathEntry foundDependenciesContainer = null;
+
+ for (int i = 0 ; i < entries.length ;) {
+ // get the entry and kind
+ IClasspathEntry entry = entries[i];
+ int kind = entry.getEntryKind();
+
+ if (kind == IClasspathEntry.CPE_SOURCE) {
+ IPath path = entry.getPath();
+
+ if (path.equals(outputFolder)) {
+ entries = ProjectHelper.removeEntryFromClasspath(entries, i);
+
+ // continue, to skip the i++;
+ continue;
+ }
+ } else if (kind == IClasspathEntry.CPE_CONTAINER) {
+ String path = entry.getPath().toString();
+ if (AdtConstants.CONTAINER_FRAMEWORK.equals(path)) {
+ foundFrameworkContainer = true;
+ } else if (AdtConstants.CONTAINER_PRIVATE_LIBRARIES.equals(path)) {
+ foundLibrariesContainer = entry;
+ } else if (AdtConstants.CONTAINER_DEPENDENCIES.equals(path)) {
+ foundDependenciesContainer = entry;
+ }
+ }
+
+ i++;
+ }
+
+ // look to see if we have the m2eclipse nature
+ boolean m2eNature = false;
+ try {
+ m2eNature = javaProject.getProject().hasNature("org.eclipse.m2e.core.maven2Nature");
+ } catch (CoreException e) {
+ AdtPlugin.log(e, "Failed to query project %s for m2e nature",
+ javaProject.getProject().getName());
+ }
+
+
+ // if the framework container is not there, we add it
+ if (!foundFrameworkContainer) {
+ // add the android container to the array
+ entries = ProjectHelper.addEntryToClasspath(entries,
+ JavaCore.newContainerEntry(new Path(AdtConstants.CONTAINER_FRAMEWORK)));
+ }
+
+ // same thing for the library container
+ if (foundLibrariesContainer == null) {
+ // add the exported libraries android container to the array
+ entries = ProjectHelper.addEntryToClasspath(entries,
+ JavaCore.newContainerEntry(
+ new Path(AdtConstants.CONTAINER_PRIVATE_LIBRARIES), true));
+ } else if (!m2eNature && !foundLibrariesContainer.isExported()) {
+ // the container is present but it's not exported and since there's no m2e nature
+ // we do want it to be exported.
+ // keep all the other parameters the same.
+ entries = ProjectHelper.replaceEntryInClasspath(entries,
+ JavaCore.newContainerEntry(
+ new Path(AdtConstants.CONTAINER_PRIVATE_LIBRARIES),
+ foundLibrariesContainer.getAccessRules(),
+ foundLibrariesContainer.getExtraAttributes(),
+ true));
+ forceRewriteOfCPE = true;
+ }
+
+ // same thing for the dependencies container
+ if (foundDependenciesContainer == null) {
+ // add the android dependencies container to the array
+ entries = ProjectHelper.addEntryToClasspath(entries,
+ JavaCore.newContainerEntry(
+ new Path(AdtConstants.CONTAINER_DEPENDENCIES), true));
+ } else if (!m2eNature && !foundDependenciesContainer.isExported()) {
+ // the container is present but it's not exported and since there's no m2e nature
+ // we do want it to be exported.
+ // keep all the other parameters the same.
+ entries = ProjectHelper.replaceEntryInClasspath(entries,
+ JavaCore.newContainerEntry(
+ new Path(AdtConstants.CONTAINER_DEPENDENCIES),
+ foundDependenciesContainer.getAccessRules(),
+ foundDependenciesContainer.getExtraAttributes(),
+ true));
+ forceRewriteOfCPE = true;
+ }
+
+ // set the new list of entries to the project
+ if (entries != oldEntries || forceRewriteOfCPE) {
+ javaProject.setRawClasspath(entries, new NullProgressMonitor());
+ }
+
+ // If needed, check and fix compiler compliance and source compatibility
+ ProjectHelper.checkAndFixCompilerCompliance(javaProject);
+ }
+
+
+ /**
+ * Checks the project compiler compliance level is supported.
+ * @param javaProject The project to check
+ * @return A pair with the first integer being an error code, and the second value
+ * being the invalid value found or null. The error code can be: <ul>
+ * <li><code>COMPILER_COMPLIANCE_OK</code> if the project is properly configured</li>
+ * <li><code>COMPILER_COMPLIANCE_LEVEL</code> for unsupported compiler level</li>
+ * <li><code>COMPILER_COMPLIANCE_SOURCE</code> for unsupported source compatibility</li>
+ * <li><code>COMPILER_COMPLIANCE_CODEGEN_TARGET</code> for unsupported .class format</li>
+ * </ul>
+ */
+ public static final Pair<Integer, String> checkCompilerCompliance(IJavaProject javaProject) {
+ // get the project compliance level option
+ String compliance = javaProject.getOption(JavaCore.COMPILER_COMPLIANCE, true);
+
+ // check it against a list of valid compliance level strings.
+ if (!checkCompliance(javaProject, compliance)) {
+ // if we didn't find the proper compliance level, we return an error
+ return Pair.of(COMPILER_COMPLIANCE_LEVEL, compliance);
+ }
+
+ // otherwise we check source compatibility
+ String source = javaProject.getOption(JavaCore.COMPILER_SOURCE, true);
+
+ // check it against a list of valid compliance level strings.
+ if (!checkCompliance(javaProject, source)) {
+ // if we didn't find the proper compliance level, we return an error
+ return Pair.of(COMPILER_COMPLIANCE_SOURCE, source);
+ }
+
+ // otherwise check codegen level
+ String codeGen = javaProject.getOption(JavaCore.COMPILER_CODEGEN_TARGET_PLATFORM, true);
+
+ // check it against a list of valid compliance level strings.
+ if (!checkCompliance(javaProject, codeGen)) {
+ // if we didn't find the proper compliance level, we return an error
+ return Pair.of(COMPILER_COMPLIANCE_CODEGEN_TARGET, codeGen);
+ }
+
+ return Pair.of(COMPILER_COMPLIANCE_OK, null);
+ }
+
+ /**
+ * Checks the project compiler compliance level is supported.
+ * @param project The project to check
+ * @return A pair with the first integer being an error code, and the second value
+ * being the invalid value found or null. The error code can be: <ul>
+ * <li><code>COMPILER_COMPLIANCE_OK</code> if the project is properly configured</li>
+ * <li><code>COMPILER_COMPLIANCE_LEVEL</code> for unsupported compiler level</li>
+ * <li><code>COMPILER_COMPLIANCE_SOURCE</code> for unsupported source compatibility</li>
+ * <li><code>COMPILER_COMPLIANCE_CODEGEN_TARGET</code> for unsupported .class format</li>
+ * </ul>
+ */
+ public static final Pair<Integer, String> checkCompilerCompliance(IProject project) {
+ // get the java project from the IProject resource object
+ IJavaProject javaProject = JavaCore.create(project);
+
+ // check and return the result.
+ return checkCompilerCompliance(javaProject);
+ }
+
+
+ /**
+ * Checks, and fixes if needed, the compiler compliance level, and the source compatibility
+ * level
+ * @param project The project to check and fix.
+ */
+ public static final void checkAndFixCompilerCompliance(IProject project) {
+ // FIXME This method is never used. Shall we just removed it?
+ // {@link #checkAndFixCompilerCompliance(IJavaProject)} is used instead.
+
+ // get the java project from the IProject resource object
+ IJavaProject javaProject = JavaCore.create(project);
+
+ // Now we check the compiler compliance level and make sure it is valid
+ checkAndFixCompilerCompliance(javaProject);
+ }
+
+ /**
+ * Checks, and fixes if needed, the compiler compliance level, and the source compatibility
+ * level
+ * @param javaProject The Java project to check and fix.
+ */
+ public static final void checkAndFixCompilerCompliance(IJavaProject javaProject) {
+ Pair<Integer, String> result = checkCompilerCompliance(javaProject);
+ if (result.getFirst().intValue() != COMPILER_COMPLIANCE_OK) {
+ // setup the preferred compiler compliance level.
+ javaProject.setOption(JavaCore.COMPILER_COMPLIANCE,
+ AdtConstants.COMPILER_COMPLIANCE_PREFERRED);
+ javaProject.setOption(JavaCore.COMPILER_SOURCE,
+ AdtConstants.COMPILER_COMPLIANCE_PREFERRED);
+ javaProject.setOption(JavaCore.COMPILER_CODEGEN_TARGET_PLATFORM,
+ AdtConstants.COMPILER_COMPLIANCE_PREFERRED);
+
+ // clean the project to make sure we recompile
+ try {
+ javaProject.getProject().build(IncrementalProjectBuilder.CLEAN_BUILD,
+ new NullProgressMonitor());
+ } catch (CoreException e) {
+ AdtPlugin.printErrorToConsole(javaProject.getProject(),
+ "Project compiler settings changed. Clean your project.");
+ }
+ }
+ }
+
+ /**
+ * Makes the given project use JDK 6 (or more specifically,
+ * {@link AdtConstants#COMPILER_COMPLIANCE_PREFERRED} as the compilation
+ * target, regardless of what the default IDE JDK level is, provided a JRE
+ * of the given level is installed.
+ *
+ * @param javaProject the Java project
+ * @throws CoreException if the IDE throws an exception setting the compiler
+ * level
+ */
+ @SuppressWarnings("restriction") // JDT API for setting compliance options
+ public static void enforcePreferredCompilerCompliance(@NonNull IJavaProject javaProject)
+ throws CoreException {
+ String compliance = javaProject.getOption(JavaCore.COMPILER_COMPLIANCE, true);
+ if (compliance == null ||
+ JavaModelUtil.isVersionLessThan(compliance, COMPILER_COMPLIANCE_PREFERRED)) {
+ IVMInstallType[] types = JavaRuntime.getVMInstallTypes();
+ for (int i = 0; i < types.length; i++) {
+ IVMInstallType type = types[i];
+ IVMInstall[] installs = type.getVMInstalls();
+ for (int j = 0; j < installs.length; j++) {
+ IVMInstall install = installs[j];
+ if (install instanceof IVMInstall2) {
+ IVMInstall2 install2 = (IVMInstall2) install;
+ // Java version can be 1.6.0, and preferred is 1.6
+ if (install2.getJavaVersion().startsWith(COMPILER_COMPLIANCE_PREFERRED)) {
+ Map<String, String> options = javaProject.getOptions(false);
+ JavaCore.setComplianceOptions(COMPILER_COMPLIANCE_PREFERRED, options);
+ JavaModelUtil.setDefaultClassfileOptions(options,
+ COMPILER_COMPLIANCE_PREFERRED);
+ javaProject.setOptions(options);
+ return;
+ }
+ }
+ }
+ }
+ }
+ }
+
+ /**
+ * Returns a {@link IProject} by its running application name, as it returned by the AVD.
+ * <p/>
+ * <var>applicationName</var> will in most case be the package declared in the manifest, but
+ * can, in some cases, be a custom process name declared in the manifest, in the
+ * <code>application</code>, <code>activity</code>, <code>receiver</code>, or
+ * <code>service</code> nodes.
+ * @param applicationName The application name.
+ * @return a project or <code>null</code> if no matching project were found.
+ */
+ public static IProject findAndroidProjectByAppName(String applicationName) {
+ // Get the list of project for the current workspace
+ IWorkspace workspace = ResourcesPlugin.getWorkspace();
+ IProject[] projects = workspace.getRoot().getProjects();
+
+ // look for a project that matches the packageName of the app
+ // we're trying to debug
+ for (IProject p : projects) {
+ if (p.isOpen()) {
+ try {
+ if (p.hasNature(AdtConstants.NATURE_DEFAULT) == false) {
+ // ignore non android projects
+ continue;
+ }
+ } catch (CoreException e) {
+ // failed to get the nature? skip project.
+ continue;
+ }
+
+ // check that there is indeed a manifest file.
+ IFile manifestFile = getManifest(p);
+ if (manifestFile == null) {
+ // no file? skip this project.
+ continue;
+ }
+
+ ManifestData data = AndroidManifestHelper.parseForData(manifestFile);
+ if (data == null) {
+ // skip this project.
+ continue;
+ }
+
+ String manifestPackage = data.getPackage();
+
+ if (manifestPackage != null && manifestPackage.equals(applicationName)) {
+ // this is the project we were looking for!
+ return p;
+ } else {
+ // if the package and application name don't match,
+ // we look for other possible process names declared in the manifest.
+ String[] processes = data.getProcesses();
+ for (String process : processes) {
+ if (process.equals(applicationName)) {
+ return p;
+ }
+ }
+ }
+ }
+ }
+
+ return null;
+
+ }
+
+ public static void fixProjectNatureOrder(IProject project) throws CoreException {
+ IProjectDescription description = project.getDescription();
+ String[] natures = description.getNatureIds();
+
+ // if the android nature is not the first one, we reorder them
+ if (AdtConstants.NATURE_DEFAULT.equals(natures[0]) == false) {
+ // look for the index
+ for (int i = 0 ; i < natures.length ; i++) {
+ if (AdtConstants.NATURE_DEFAULT.equals(natures[i])) {
+ // if we try to just reorder the array in one pass, this doesn't do
+ // anything. I guess JDT check that we are actually adding/removing nature.
+ // So, first we'll remove the android nature, and then add it back.
+
+ // remove the android nature
+ removeNature(project, AdtConstants.NATURE_DEFAULT);
+
+ // now add it back at the first index.
+ description = project.getDescription();
+ natures = description.getNatureIds();
+
+ String[] newNatures = new String[natures.length + 1];
+
+ // first one is android
+ newNatures[0] = AdtConstants.NATURE_DEFAULT;
+
+ // next the rest that was before the android nature
+ System.arraycopy(natures, 0, newNatures, 1, natures.length);
+
+ // set the new natures
+ description.setNatureIds(newNatures);
+ project.setDescription(description, null);
+
+ // and stop
+ break;
+ }
+ }
+ }
+ }
+
+
+ /**
+ * Removes a specific nature from a project.
+ * @param project The project to remove the nature from.
+ * @param nature The nature id to remove.
+ * @throws CoreException
+ */
+ public static void removeNature(IProject project, String nature) throws CoreException {
+ IProjectDescription description = project.getDescription();
+ String[] natures = description.getNatureIds();
+
+ // check if the project already has the android nature.
+ for (int i = 0; i < natures.length; ++i) {
+ if (nature.equals(natures[i])) {
+ String[] newNatures = new String[natures.length - 1];
+ if (i > 0) {
+ System.arraycopy(natures, 0, newNatures, 0, i);
+ }
+ System.arraycopy(natures, i + 1, newNatures, i, natures.length - i - 1);
+ description.setNatureIds(newNatures);
+ project.setDescription(description, null);
+
+ return;
+ }
+ }
+
+ }
+
+ /**
+ * Returns if the project has error level markers.
+ * @param includeReferencedProjects flag to also test the referenced projects.
+ * @throws CoreException
+ */
+ public static boolean hasError(IProject project, boolean includeReferencedProjects)
+ throws CoreException {
+ IMarker[] markers = project.findMarkers(IMarker.PROBLEM, true, IResource.DEPTH_INFINITE);
+ if (markers != null && markers.length > 0) {
+ // the project has marker(s). even though they are "problem" we
+ // don't know their severity. so we loop on them and figure if they
+ // are warnings or errors
+ for (IMarker m : markers) {
+ int s = m.getAttribute(IMarker.SEVERITY, -1);
+ if (s == IMarker.SEVERITY_ERROR) {
+ return true;
+ }
+ }
+ }
+
+ // test the referenced projects if needed.
+ if (includeReferencedProjects) {
+ List<IProject> projects = getReferencedProjects(project);
+
+ for (IProject p : projects) {
+ if (hasError(p, false)) {
+ return true;
+ }
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * Saves a String property into the persistent storage of a resource.
+ * @param resource The resource into which the string value is saved.
+ * @param propertyName the name of the property. The id of the plug-in is added to this string.
+ * @param value the value to save
+ * @return true if the save succeeded.
+ */
+ public static boolean saveStringProperty(IResource resource, String propertyName,
+ String value) {
+ QualifiedName qname = new QualifiedName(AdtPlugin.PLUGIN_ID, propertyName);
+
+ try {
+ resource.setPersistentProperty(qname, value);
+ } catch (CoreException e) {
+ return false;
+ }
+
+ return true;
+ }
+
+ /**
+ * Loads a String property from the persistent storage of a resource.
+ * @param resource The resource from which the string value is loaded.
+ * @param propertyName the name of the property. The id of the plug-in is added to this string.
+ * @return the property value or null if it was not found.
+ */
+ public static String loadStringProperty(IResource resource, String propertyName) {
+ QualifiedName qname = new QualifiedName(AdtPlugin.PLUGIN_ID, propertyName);
+
+ try {
+ String value = resource.getPersistentProperty(qname);
+ return value;
+ } catch (CoreException e) {
+ return null;
+ }
+ }
+
+ /**
+ * Saves a property into the persistent storage of a resource.
+ * @param resource The resource into which the boolean value is saved.
+ * @param propertyName the name of the property. The id of the plug-in is added to this string.
+ * @param value the value to save
+ * @return true if the save succeeded.
+ */
+ public static boolean saveBooleanProperty(IResource resource, String propertyName,
+ boolean value) {
+ return saveStringProperty(resource, propertyName, Boolean.toString(value));
+ }
+
+ /**
+ * Loads a boolean property from the persistent storage of a resource.
+ * @param resource The resource from which the boolean value is loaded.
+ * @param propertyName the name of the property. The id of the plug-in is added to this string.
+ * @param defaultValue The default value to return if the property was not found.
+ * @return the property value or the default value if the property was not found.
+ */
+ public static boolean loadBooleanProperty(IResource resource, String propertyName,
+ boolean defaultValue) {
+ String value = loadStringProperty(resource, propertyName);
+ if (value != null) {
+ return Boolean.parseBoolean(value);
+ }
+
+ return defaultValue;
+ }
+
+ public static Boolean loadBooleanProperty(IResource resource, String propertyName) {
+ String value = loadStringProperty(resource, propertyName);
+ if (value != null) {
+ return Boolean.valueOf(value);
+ }
+
+ return null;
+ }
+
+ /**
+ * Saves the path of a resource into the persistent storage of a resource.
+ * @param resource The resource into which the resource path is saved.
+ * @param propertyName the name of the property. The id of the plug-in is added to this string.
+ * @param value The resource to save. It's its path that is actually stored. If null, an
+ * empty string is stored.
+ * @return true if the save succeeded
+ */
+ public static boolean saveResourceProperty(IResource resource, String propertyName,
+ IResource value) {
+ if (value != null) {
+ IPath iPath = value.getFullPath();
+ return saveStringProperty(resource, propertyName, iPath.toString());
+ }
+
+ return saveStringProperty(resource, propertyName, ""); //$NON-NLS-1$
+ }
+
+ /**
+ * Loads the path of a resource from the persistent storage of a resource, and returns the
+ * corresponding IResource object.
+ * @param resource The resource from which the resource path is loaded.
+ * @param propertyName the name of the property. The id of the plug-in is added to this string.
+ * @return The corresponding IResource object (or children interface) or null
+ */
+ public static IResource loadResourceProperty(IResource resource, String propertyName) {
+ String value = loadStringProperty(resource, propertyName);
+
+ if (value != null && value.length() > 0) {
+ return ResourcesPlugin.getWorkspace().getRoot().findMember(new Path(value));
+ }
+
+ return null;
+ }
+
+ /**
+ * Returns the list of referenced project that are opened and Java projects.
+ * @param project
+ * @return a new list object containing the opened referenced java project.
+ * @throws CoreException
+ */
+ public static List<IProject> getReferencedProjects(IProject project) throws CoreException {
+ IProject[] projects = project.getReferencedProjects();
+
+ ArrayList<IProject> list = new ArrayList<IProject>();
+
+ for (IProject p : projects) {
+ if (p.isOpen() && p.hasNature(JavaCore.NATURE_ID)) {
+ list.add(p);
+ }
+ }
+
+ return list;
+ }
+
+
+ /**
+ * Checks a Java project compiler level option against a list of supported versions.
+ * @param optionValue the Compiler level option.
+ * @return true if the option value is supported.
+ */
+ private static boolean checkCompliance(@NonNull IJavaProject project, String optionValue) {
+ for (String s : AdtConstants.COMPILER_COMPLIANCE) {
+ if (s != null && s.equals(optionValue)) {
+ return true;
+ }
+ }
+
+ if (JavaCore.VERSION_1_7.equals(optionValue)) {
+ // Requires API 19 and buildTools 19
+ Sdk currentSdk = Sdk.getCurrent();
+ if (currentSdk != null) {
+ IProject p = project.getProject();
+ IAndroidTarget target = currentSdk.getTarget(p);
+ if (target == null || target.getVersion().getApiLevel() < 19) {
+ return false;
+ }
+
+ ProjectState projectState = Sdk.getProjectState(p);
+ if (projectState != null) {
+ BuildToolInfo buildToolInfo = projectState.getBuildToolInfo();
+ if (buildToolInfo == null) {
+ buildToolInfo = currentSdk.getLatestBuildTool();
+ }
+ if (buildToolInfo == null || buildToolInfo.getRevision().getMajor() < 19) {
+ return false;
+ }
+ }
+
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * Returns the apk filename for the given project
+ * @param project The project.
+ * @param config An optional config name. Can be null.
+ */
+ public static String getApkFilename(IProject project, String config) {
+ if (config != null) {
+ return project.getName() + "-" + config + SdkConstants.DOT_ANDROID_PACKAGE; //$NON-NLS-1$
+ }
+
+ return project.getName() + SdkConstants.DOT_ANDROID_PACKAGE;
+ }
+
+ /**
+ * Find the list of projects on which this JavaProject is dependent on at the compilation level.
+ *
+ * @param javaProject Java project that we are looking for the dependencies.
+ * @return A list of Java projects for which javaProject depend on.
+ * @throws JavaModelException
+ */
+ public static List<IJavaProject> getAndroidProjectDependencies(IJavaProject javaProject)
+ throws JavaModelException {
+ String[] requiredProjectNames = javaProject.getRequiredProjectNames();
+
+ // Go from java project name to JavaProject name
+ IJavaModel javaModel = javaProject.getJavaModel();
+
+ // loop through all dependent projects and keep only those that are Android projects
+ List<IJavaProject> projectList = new ArrayList<IJavaProject>(requiredProjectNames.length);
+ for (String javaProjectName : requiredProjectNames) {
+ IJavaProject androidJavaProject = javaModel.getJavaProject(javaProjectName);
+
+ //Verify that the project has also the Android Nature
+ try {
+ if (!androidJavaProject.getProject().hasNature(AdtConstants.NATURE_DEFAULT)) {
+ continue;
+ }
+ } catch (CoreException e) {
+ continue;
+ }
+
+ projectList.add(androidJavaProject);
+ }
+
+ return projectList;
+ }
+
+ /**
+ * Returns the android package file as an IFile object for the specified
+ * project.
+ * @param project The project
+ * @return The android package as an IFile object or null if not found.
+ */
+ public static IFile getApplicationPackage(IProject project) {
+ // get the output folder
+ IFolder outputLocation = BaseProjectHelper.getAndroidOutputFolder(project);
+
+ if (outputLocation == null) {
+ AdtPlugin.printErrorToConsole(project,
+ "Failed to get the output location of the project. Check build path properties"
+ );
+ return null;
+ }
+
+
+ // get the package path
+ String packageName = project.getName() + SdkConstants.DOT_ANDROID_PACKAGE;
+ IResource r = outputLocation.findMember(packageName);
+
+ // check the package is present
+ if (r instanceof IFile && r.exists()) {
+ return (IFile)r;
+ }
+
+ String msg = String.format("Could not find %1$s!", packageName);
+ AdtPlugin.printErrorToConsole(project, msg);
+
+ return null;
+ }
+
+ /**
+ * Returns an {@link IFile} object representing the manifest for the given project.
+ *
+ * @param project The project containing the manifest file.
+ * @return An IFile object pointing to the manifest or null if the manifest
+ * is missing.
+ */
+ public static IFile getManifest(IProject project) {
+ IResource r = project.findMember(AdtConstants.WS_SEP
+ + SdkConstants.FN_ANDROID_MANIFEST_XML);
+
+ if (r == null || r.exists() == false || (r instanceof IFile) == false) {
+ return null;
+ }
+ return (IFile) r;
+ }
+
+ /**
+ * Does a full release build of the application, including the libraries. Do not build the
+ * package.
+ *
+ * @param project The project to be built.
+ * @param monitor A eclipse runtime progress monitor to be updated by the builders.
+ * @throws CoreException
+ */
+ @SuppressWarnings("unchecked")
+ public static void compileInReleaseMode(IProject project, IProgressMonitor monitor)
+ throws CoreException {
+ compileInReleaseMode(project, true /*includeDependencies*/, monitor);
+ }
+
+ /**
+ * Does a full release build of the application, including the libraries. Do not build the
+ * package.
+ *
+ * @param project The project to be built.
+ * @param monitor A eclipse runtime progress monitor to be updated by the builders.
+ * @throws CoreException
+ */
+ @SuppressWarnings("unchecked")
+ private static void compileInReleaseMode(IProject project, boolean includeDependencies,
+ IProgressMonitor monitor)
+ throws CoreException {
+
+ if (includeDependencies) {
+ ProjectState projectState = Sdk.getProjectState(project);
+
+ // this gives us all the library projects, direct and indirect dependencies,
+ // so no need to run this method recursively.
+ List<IProject> libraries = projectState.getFullLibraryProjects();
+
+ // build dependencies in reverse order to prevent libraries being rebuilt
+ // due to refresh of other libraries (they would be compiled in the wrong mode).
+ for (int i = libraries.size() - 1 ; i >= 0 ; i--) {
+ IProject lib = libraries.get(i);
+ compileInReleaseMode(lib, false /*includeDependencies*/, monitor);
+
+ // force refresh of the dependency.
+ lib.refreshLocal(IResource.DEPTH_INFINITE, monitor);
+ }
+ }
+
+ // do a full build on all the builders to guarantee that the builders are called.
+ // (Eclipse does an optimization where builders are not called if there aren't any
+ // deltas).
+
+ ICommand[] commands = project.getDescription().getBuildSpec();
+ for (ICommand command : commands) {
+ String name = command.getBuilderName();
+ if (PreCompilerBuilder.ID.equals(name)) {
+ Map newArgs = new HashMap();
+ newArgs.put(PreCompilerBuilder.RELEASE_REQUESTED, "");
+ if (command.getArguments() != null) {
+ newArgs.putAll(command.getArguments());
+ }
+
+ project.build(IncrementalProjectBuilder.FULL_BUILD,
+ PreCompilerBuilder.ID, newArgs, monitor);
+ } else if (PostCompilerBuilder.ID.equals(name)) {
+ if (includeDependencies == false) {
+ // this is a library, we need to build it!
+ project.build(IncrementalProjectBuilder.FULL_BUILD, name,
+ command.getArguments(), monitor);
+ }
+ } else {
+
+ project.build(IncrementalProjectBuilder.FULL_BUILD, name,
+ command.getArguments(), monitor);
+ }
+ }
+ }
+
+ /**
+ * Force building the project and all its dependencies.
+ *
+ * @param project the project to build
+ * @param kind the build kind
+ * @param monitor
+ * @throws CoreException
+ */
+ public static void buildWithDeps(IProject project, int kind, IProgressMonitor monitor)
+ throws CoreException {
+ // Get list of projects that we depend on
+ ProjectState projectState = Sdk.getProjectState(project);
+
+ // this gives us all the library projects, direct and indirect dependencies,
+ // so no need to run this method recursively.
+ List<IProject> libraries = projectState.getFullLibraryProjects();
+
+ // build dependencies in reverse order to prevent libraries being rebuilt
+ // due to refresh of other libraries (they would be compiled in the wrong mode).
+ for (int i = libraries.size() - 1 ; i >= 0 ; i--) {
+ IProject lib = libraries.get(i);
+ lib.build(kind, monitor);
+ lib.refreshLocal(IResource.DEPTH_INFINITE, monitor);
+ }
+
+ project.build(kind, monitor);
+ }
+
+
+ /**
+ * Build project incrementally, including making the final packaging even if it is disabled
+ * by default.
+ *
+ * @param project The project to be built.
+ * @param monitor A eclipse runtime progress monitor to be updated by the builders.
+ * @throws CoreException
+ */
+ public static void doFullIncrementalDebugBuild(IProject project, IProgressMonitor monitor)
+ throws CoreException {
+ // Get list of projects that we depend on
+ List<IJavaProject> androidProjectList = new ArrayList<IJavaProject>();
+ try {
+ androidProjectList = getAndroidProjectDependencies(
+ BaseProjectHelper.getJavaProject(project));
+ } catch (JavaModelException e) {
+ AdtPlugin.printErrorToConsole(project, e);
+ }
+ // Recursively build dependencies
+ for (IJavaProject dependency : androidProjectList) {
+ doFullIncrementalDebugBuild(dependency.getProject(), monitor);
+ }
+
+ // Do an incremental build to pick up all the deltas
+ project.build(IncrementalProjectBuilder.INCREMENTAL_BUILD, monitor);
+
+ // If the preferences indicate not to use post compiler optimization
+ // then the incremental build will have done everything necessary, otherwise,
+ // we have to run the final builder manually (if requested).
+ if (AdtPrefs.getPrefs().getBuildSkipPostCompileOnFileSave()) {
+ // Create the map to pass to the PostC builder
+ Map<String, String> args = new TreeMap<String, String>();
+ args.put(PostCompilerBuilder.POST_C_REQUESTED, ""); //$NON-NLS-1$
+
+ // call the post compiler manually, forcing FULL_BUILD otherwise Eclipse won't
+ // call the builder since the delta is empty.
+ project.build(IncrementalProjectBuilder.FULL_BUILD,
+ PostCompilerBuilder.ID, args, monitor);
+ }
+
+ // because the post compiler builder does a delayed refresh due to
+ // library not picking the refresh up if it's done during the build,
+ // we want to force a refresh here as this call is generally asking for
+ // a build to use the apk right after the call.
+ project.refreshLocal(IResource.DEPTH_INFINITE, monitor);
+ }
+}