• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2007 The Android Open Source Project
3  *
4  * Licensed under the Eclipse Public License, Version 1.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.eclipse.org/org/documents/epl-v10.php
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.ide.eclipse.adt.internal.project;
18 
19 import com.android.ide.eclipse.adt.AdtConstants;
20 import com.android.ide.eclipse.adt.AdtPlugin;
21 import com.android.ide.eclipse.adt.internal.build.builders.PostCompilerBuilder;
22 import com.android.ide.eclipse.adt.internal.preferences.AdtPrefs;
23 import com.android.sdklib.SdkConstants;
24 import com.android.sdklib.xml.ManifestData;
25 import com.android.util.Pair;
26 
27 import org.eclipse.core.resources.IFile;
28 import org.eclipse.core.resources.IFolder;
29 import org.eclipse.core.resources.IMarker;
30 import org.eclipse.core.resources.IProject;
31 import org.eclipse.core.resources.IProjectDescription;
32 import org.eclipse.core.resources.IResource;
33 import org.eclipse.core.resources.IWorkspace;
34 import org.eclipse.core.resources.IncrementalProjectBuilder;
35 import org.eclipse.core.resources.ResourcesPlugin;
36 import org.eclipse.core.runtime.CoreException;
37 import org.eclipse.core.runtime.IPath;
38 import org.eclipse.core.runtime.IProgressMonitor;
39 import org.eclipse.core.runtime.NullProgressMonitor;
40 import org.eclipse.core.runtime.Path;
41 import org.eclipse.core.runtime.QualifiedName;
42 import org.eclipse.jdt.core.IClasspathEntry;
43 import org.eclipse.jdt.core.IJavaModel;
44 import org.eclipse.jdt.core.IJavaProject;
45 import org.eclipse.jdt.core.JavaCore;
46 import org.eclipse.jdt.core.JavaModelException;
47 import org.eclipse.jdt.launching.JavaRuntime;
48 
49 import java.util.ArrayList;
50 import java.util.List;
51 import java.util.Map;
52 import java.util.TreeMap;
53 
54 /**
55  * Utility class to manipulate Project parameters/properties.
56  */
57 public final class ProjectHelper {
58     public final static int COMPILER_COMPLIANCE_OK = 0;
59     public final static int COMPILER_COMPLIANCE_LEVEL = 1;
60     public final static int COMPILER_COMPLIANCE_SOURCE = 2;
61     public final static int COMPILER_COMPLIANCE_CODEGEN_TARGET = 3;
62 
63     /**
64      * Adds the corresponding source folder to the class path entries.
65      * This method does not check whether the entry is already defined in the project.
66      *
67      * @param entries The class path entries to read. A copy will be returned.
68      * @param newEntry The new class path entry to add.
69      * @return A new class path entries array.
70      */
addEntryToClasspath( IClasspathEntry[] entries, IClasspathEntry newEntry)71     public static IClasspathEntry[] addEntryToClasspath(
72             IClasspathEntry[] entries, IClasspathEntry newEntry) {
73         int n = entries.length;
74         IClasspathEntry[] newEntries = new IClasspathEntry[n + 1];
75         System.arraycopy(entries, 0, newEntries, 0, n);
76         newEntries[n] = newEntry;
77         return newEntries;
78     }
79 
80     /**
81      * Adds the corresponding source folder to the project's class path entries.
82      * This method does not check whether the entry is already defined in the project.
83      *
84      * @param javaProject The java project of which path entries to update.
85      * @param newEntry The new class path entry to add.
86      * @throws JavaModelException
87      */
addEntryToClasspath(IJavaProject javaProject, IClasspathEntry newEntry)88     public static void addEntryToClasspath(IJavaProject javaProject, IClasspathEntry newEntry)
89             throws JavaModelException {
90 
91         IClasspathEntry[] entries = javaProject.getRawClasspath();
92         entries = addEntryToClasspath(entries, newEntry);
93         javaProject.setRawClasspath(entries, new NullProgressMonitor());
94     }
95 
96     /**
97      * Checks whether the given class path entry is already defined in the project.
98      *
99      * @param javaProject The java project of which path entries to check.
100      * @param newEntry The parent source folder to remove.
101      * @return True if the class path entry is already defined.
102      * @throws JavaModelException
103      */
isEntryInClasspath(IJavaProject javaProject, IClasspathEntry newEntry)104     public static boolean isEntryInClasspath(IJavaProject javaProject, IClasspathEntry newEntry)
105             throws JavaModelException {
106 
107         IClasspathEntry[] entries = javaProject.getRawClasspath();
108         for (IClasspathEntry entry : entries) {
109             if (entry.equals(newEntry)) {
110                 return true;
111             }
112         }
113         return false;
114     }
115 
116     /**
117      * Remove a classpath entry from the array.
118      * @param entries The class path entries to read. A copy will be returned
119      * @param index The index to remove.
120      * @return A new class path entries array.
121      */
removeEntryFromClasspath( IClasspathEntry[] entries, int index)122     public static IClasspathEntry[] removeEntryFromClasspath(
123             IClasspathEntry[] entries, int index) {
124         int n = entries.length;
125         IClasspathEntry[] newEntries = new IClasspathEntry[n-1];
126 
127         // copy the entries before index
128         System.arraycopy(entries, 0, newEntries, 0, index);
129 
130         // copy the entries after index
131         System.arraycopy(entries, index + 1, newEntries, index,
132                 entries.length - index - 1);
133 
134         return newEntries;
135     }
136 
137     /**
138      * Converts a OS specific path into a path valid for the java doc location
139      * attributes of a project.
140      * @param javaDocOSLocation The OS specific path.
141      * @return a valid path for the java doc location.
142      */
getJavaDocPath(String javaDocOSLocation)143     public static String getJavaDocPath(String javaDocOSLocation) {
144         // first thing we do is convert the \ into /
145         String javaDoc = javaDocOSLocation.replaceAll("\\\\", //$NON-NLS-1$
146                 AdtConstants.WS_SEP);
147 
148         // then we add file: at the beginning for unix path, and file:/ for non
149         // unix path
150         if (javaDoc.startsWith(AdtConstants.WS_SEP)) {
151             return "file:" + javaDoc; //$NON-NLS-1$
152         }
153 
154         return "file:/" + javaDoc; //$NON-NLS-1$
155     }
156 
157     /**
158      * Look for a specific classpath entry by full path and return its index.
159      * @param entries The entry array to search in.
160      * @param entryPath The OS specific path of the entry.
161      * @param entryKind The kind of the entry. Accepted values are 0
162      * (no filter), IClasspathEntry.CPE_LIBRARY, IClasspathEntry.CPE_PROJECT,
163      * IClasspathEntry.CPE_SOURCE, IClasspathEntry.CPE_VARIABLE,
164      * and IClasspathEntry.CPE_CONTAINER
165      * @return the index of the found classpath entry or -1.
166      */
findClasspathEntryByPath(IClasspathEntry[] entries, String entryPath, int entryKind)167     public static int findClasspathEntryByPath(IClasspathEntry[] entries,
168             String entryPath, int entryKind) {
169         for (int i = 0 ; i < entries.length ; i++) {
170             IClasspathEntry entry = entries[i];
171 
172             int kind = entry.getEntryKind();
173 
174             if (kind == entryKind || entryKind == 0) {
175                 // get the path
176                 IPath path = entry.getPath();
177 
178                 String osPathString = path.toOSString();
179                 if (osPathString.equals(entryPath)) {
180                     return i;
181                 }
182             }
183         }
184 
185         // not found, return bad index.
186         return -1;
187     }
188 
189     /**
190      * Look for a specific classpath entry for file name only and return its
191      *  index.
192      * @param entries The entry array to search in.
193      * @param entryName The filename of the entry.
194      * @param entryKind The kind of the entry. Accepted values are 0
195      * (no filter), IClasspathEntry.CPE_LIBRARY, IClasspathEntry.CPE_PROJECT,
196      * IClasspathEntry.CPE_SOURCE, IClasspathEntry.CPE_VARIABLE,
197      * and IClasspathEntry.CPE_CONTAINER
198      * @param startIndex Index where to start the search
199      * @return the index of the found classpath entry or -1.
200      */
findClasspathEntryByName(IClasspathEntry[] entries, String entryName, int entryKind, int startIndex)201     public static int findClasspathEntryByName(IClasspathEntry[] entries,
202             String entryName, int entryKind, int startIndex) {
203         if (startIndex < 0) {
204             startIndex = 0;
205         }
206         for (int i = startIndex ; i < entries.length ; i++) {
207             IClasspathEntry entry = entries[i];
208 
209             int kind = entry.getEntryKind();
210 
211             if (kind == entryKind || entryKind == 0) {
212                 // get the path
213                 IPath path = entry.getPath();
214                 String name = path.segment(path.segmentCount()-1);
215 
216                 if (name.equals(entryName)) {
217                     return i;
218                 }
219             }
220         }
221 
222         // not found, return bad index.
223         return -1;
224     }
225 
226     /**
227      * Fix the project. This checks the SDK location.
228      * @param project The project to fix.
229      * @throws JavaModelException
230      */
fixProject(IProject project)231     public static void fixProject(IProject project) throws JavaModelException {
232         if (AdtPlugin.getOsSdkFolder().length() == 0) {
233             AdtPlugin.printToConsole(project, "Unknown SDK Location, project not fixed.");
234             return;
235         }
236 
237         // get a java project
238         IJavaProject javaProject = JavaCore.create(project);
239         fixProjectClasspathEntries(javaProject);
240     }
241 
242     /**
243      * Fix the project classpath entries. The method ensures that:
244      * <ul>
245      * <li>The project does not reference any old android.zip/android.jar archive.</li>
246      * <li>The project does not use its output folder as a sourc folder.</li>
247      * <li>The project does not reference a desktop JRE</li>
248      * <li>The project references the AndroidClasspathContainer.
249      * </ul>
250      * @param javaProject The project to fix.
251      * @throws JavaModelException
252      */
fixProjectClasspathEntries(IJavaProject javaProject)253     public static void fixProjectClasspathEntries(IJavaProject javaProject)
254             throws JavaModelException {
255 
256         // get the project classpath
257         IClasspathEntry[] entries = javaProject.getRawClasspath();
258         IClasspathEntry[] oldEntries = entries;
259 
260         // check if the JRE is set as library
261         int jreIndex = ProjectHelper.findClasspathEntryByPath(entries, JavaRuntime.JRE_CONTAINER,
262                 IClasspathEntry.CPE_CONTAINER);
263         if (jreIndex != -1) {
264             // the project has a JRE included, we remove it
265             entries = ProjectHelper.removeEntryFromClasspath(entries, jreIndex);
266         }
267 
268         // get the output folder
269         IPath outputFolder = javaProject.getOutputLocation();
270 
271         boolean foundFrameworkContainer = false;
272         boolean foundLibrariesContainer = false;
273 
274         for (int i = 0 ; i < entries.length ;) {
275             // get the entry and kind
276             IClasspathEntry entry = entries[i];
277             int kind = entry.getEntryKind();
278 
279             if (kind == IClasspathEntry.CPE_SOURCE) {
280                 IPath path = entry.getPath();
281 
282                 if (path.equals(outputFolder)) {
283                     entries = ProjectHelper.removeEntryFromClasspath(entries, i);
284 
285                     // continue, to skip the i++;
286                     continue;
287                 }
288             } else if (kind == IClasspathEntry.CPE_CONTAINER) {
289                 String path = entry.getPath().toString();
290                 if (AdtConstants.CONTAINER_FRAMEWORK.equals(path)) {
291                     foundFrameworkContainer = true;
292                 }
293                 if (AdtConstants.CONTAINER_LIBRARIES.equals(path)) {
294                     foundLibrariesContainer = true;
295                 }
296             }
297 
298             i++;
299         }
300 
301         // if the framework container is not there, we add it
302         if (foundFrameworkContainer == false) {
303             // add the android container to the array
304             entries = ProjectHelper.addEntryToClasspath(entries,
305                     JavaCore.newContainerEntry(new Path(AdtConstants.CONTAINER_FRAMEWORK)));
306         }
307 
308         // same thing for the library container
309         if (foundLibrariesContainer == false) {
310             // add the android container to the array
311             entries = ProjectHelper.addEntryToClasspath(entries,
312                     JavaCore.newContainerEntry(new Path(AdtConstants.CONTAINER_LIBRARIES)));
313         }
314 
315         // set the new list of entries to the project
316         if (entries != oldEntries) {
317             javaProject.setRawClasspath(entries, new NullProgressMonitor());
318         }
319 
320         // If needed, check and fix compiler compliance and source compatibility
321         ProjectHelper.checkAndFixCompilerCompliance(javaProject);
322     }
323 
324 
325     /**
326      * Checks the project compiler compliance level is supported.
327      * @param javaProject The project to check
328      * @return A pair with the first integer being an error code, and the second value
329      *   being the invalid value found or null. The error code can be: <ul>
330      * <li><code>COMPILER_COMPLIANCE_OK</code> if the project is properly configured</li>
331      * <li><code>COMPILER_COMPLIANCE_LEVEL</code> for unsupported compiler level</li>
332      * <li><code>COMPILER_COMPLIANCE_SOURCE</code> for unsupported source compatibility</li>
333      * <li><code>COMPILER_COMPLIANCE_CODEGEN_TARGET</code> for unsupported .class format</li>
334      * </ul>
335      */
checkCompilerCompliance(IJavaProject javaProject)336     public static final Pair<Integer, String> checkCompilerCompliance(IJavaProject javaProject) {
337         // get the project compliance level option
338         String compliance = javaProject.getOption(JavaCore.COMPILER_COMPLIANCE, true);
339 
340         // check it against a list of valid compliance level strings.
341         if (checkCompliance(compliance) == false) {
342             // if we didn't find the proper compliance level, we return an error
343             return Pair.of(COMPILER_COMPLIANCE_LEVEL, compliance);
344         }
345 
346         // otherwise we check source compatibility
347         String source = javaProject.getOption(JavaCore.COMPILER_SOURCE, true);
348 
349         // check it against a list of valid compliance level strings.
350         if (checkCompliance(source) == false) {
351             // if we didn't find the proper compliance level, we return an error
352             return Pair.of(COMPILER_COMPLIANCE_SOURCE, source);
353         }
354 
355         // otherwise check codegen level
356         String codeGen = javaProject.getOption(JavaCore.COMPILER_CODEGEN_TARGET_PLATFORM, true);
357 
358         // check it against a list of valid compliance level strings.
359         if (checkCompliance(codeGen) == false) {
360             // if we didn't find the proper compliance level, we return an error
361             return Pair.of(COMPILER_COMPLIANCE_CODEGEN_TARGET, codeGen);
362         }
363 
364         return Pair.of(COMPILER_COMPLIANCE_OK, null);
365     }
366 
367     /**
368      * Checks the project compiler compliance level is supported.
369      * @param project The project to check
370      * @return A pair with the first integer being an error code, and the second value
371      *   being the invalid value found or null. The error code can be: <ul>
372      * <li><code>COMPILER_COMPLIANCE_OK</code> if the project is properly configured</li>
373      * <li><code>COMPILER_COMPLIANCE_LEVEL</code> for unsupported compiler level</li>
374      * <li><code>COMPILER_COMPLIANCE_SOURCE</code> for unsupported source compatibility</li>
375      * <li><code>COMPILER_COMPLIANCE_CODEGEN_TARGET</code> for unsupported .class format</li>
376      * </ul>
377      */
checkCompilerCompliance(IProject project)378     public static final Pair<Integer, String> checkCompilerCompliance(IProject project) {
379         // get the java project from the IProject resource object
380         IJavaProject javaProject = JavaCore.create(project);
381 
382         // check and return the result.
383         return checkCompilerCompliance(javaProject);
384     }
385 
386 
387     /**
388      * Checks, and fixes if needed, the compiler compliance level, and the source compatibility
389      * level
390      * @param project The project to check and fix.
391      */
checkAndFixCompilerCompliance(IProject project)392     public static final void checkAndFixCompilerCompliance(IProject project) {
393         // FIXME This method is never used. Shall we just removed it?
394         // {@link #checkAndFixCompilerCompliance(IJavaProject)} is used instead.
395 
396         // get the java project from the IProject resource object
397         IJavaProject javaProject = JavaCore.create(project);
398 
399         // Now we check the compiler compliance level and make sure it is valid
400         checkAndFixCompilerCompliance(javaProject);
401     }
402 
403     /**
404      * Checks, and fixes if needed, the compiler compliance level, and the source compatibility
405      * level
406      * @param javaProject The Java project to check and fix.
407      */
checkAndFixCompilerCompliance(IJavaProject javaProject)408     public static final void checkAndFixCompilerCompliance(IJavaProject javaProject) {
409         Pair<Integer, String> result = checkCompilerCompliance(javaProject);
410         if (result.getFirst().intValue() != COMPILER_COMPLIANCE_OK) {
411             // setup the preferred compiler compliance level.
412             javaProject.setOption(JavaCore.COMPILER_COMPLIANCE,
413                     AdtConstants.COMPILER_COMPLIANCE_PREFERRED);
414             javaProject.setOption(JavaCore.COMPILER_SOURCE,
415                     AdtConstants.COMPILER_COMPLIANCE_PREFERRED);
416             javaProject.setOption(JavaCore.COMPILER_CODEGEN_TARGET_PLATFORM,
417                     AdtConstants.COMPILER_COMPLIANCE_PREFERRED);
418 
419             // clean the project to make sure we recompile
420             try {
421                 javaProject.getProject().build(IncrementalProjectBuilder.CLEAN_BUILD,
422                         new NullProgressMonitor());
423             } catch (CoreException e) {
424                 AdtPlugin.printErrorToConsole(javaProject.getProject(),
425                         "Project compiler settings changed. Clean your project.");
426             }
427         }
428     }
429 
430     /**
431      * Returns a {@link IProject} by its running application name, as it returned by the AVD.
432      * <p/>
433      * <var>applicationName</var> will in most case be the package declared in the manifest, but
434      * can, in some cases, be a custom process name declared in the manifest, in the
435      * <code>application</code>, <code>activity</code>, <code>receiver</code>, or
436      * <code>service</code> nodes.
437      * @param applicationName The application name.
438      * @return a project or <code>null</code> if no matching project were found.
439      */
findAndroidProjectByAppName(String applicationName)440     public static IProject findAndroidProjectByAppName(String applicationName) {
441         // Get the list of project for the current workspace
442         IWorkspace workspace = ResourcesPlugin.getWorkspace();
443         IProject[] projects = workspace.getRoot().getProjects();
444 
445         // look for a project that matches the packageName of the app
446         // we're trying to debug
447         for (IProject p : projects) {
448             if (p.isOpen()) {
449                 try {
450                     if (p.hasNature(AdtConstants.NATURE_DEFAULT) == false) {
451                         // ignore non android projects
452                         continue;
453                     }
454                 } catch (CoreException e) {
455                     // failed to get the nature? skip project.
456                     continue;
457                 }
458 
459                 // check that there is indeed a manifest file.
460                 IFile manifestFile = getManifest(p);
461                 if (manifestFile == null) {
462                     // no file? skip this project.
463                     continue;
464                 }
465 
466                 ManifestData data = AndroidManifestHelper.parseForData(manifestFile);
467                 if (data == null) {
468                     // skip this project.
469                     continue;
470                 }
471 
472                 String manifestPackage = data.getPackage();
473 
474                 if (manifestPackage != null && manifestPackage.equals(applicationName)) {
475                     // this is the project we were looking for!
476                     return p;
477                 } else {
478                     // if the package and application name don't match,
479                     // we look for other possible process names declared in the manifest.
480                     String[] processes = data.getProcesses();
481                     for (String process : processes) {
482                         if (process.equals(applicationName)) {
483                             return p;
484                         }
485                     }
486                 }
487             }
488         }
489 
490         return null;
491 
492     }
493 
fixProjectNatureOrder(IProject project)494     public static void fixProjectNatureOrder(IProject project) throws CoreException {
495         IProjectDescription description = project.getDescription();
496         String[] natures = description.getNatureIds();
497 
498         // if the android nature is not the first one, we reorder them
499         if (AdtConstants.NATURE_DEFAULT.equals(natures[0]) == false) {
500             // look for the index
501             for (int i = 0 ; i < natures.length ; i++) {
502                 if (AdtConstants.NATURE_DEFAULT.equals(natures[i])) {
503                     // if we try to just reorder the array in one pass, this doesn't do
504                     // anything. I guess JDT check that we are actually adding/removing nature.
505                     // So, first we'll remove the android nature, and then add it back.
506 
507                     // remove the android nature
508                     removeNature(project, AdtConstants.NATURE_DEFAULT);
509 
510                     // now add it back at the first index.
511                     description = project.getDescription();
512                     natures = description.getNatureIds();
513 
514                     String[] newNatures = new String[natures.length + 1];
515 
516                     // first one is android
517                     newNatures[0] = AdtConstants.NATURE_DEFAULT;
518 
519                     // next the rest that was before the android nature
520                     System.arraycopy(natures, 0, newNatures, 1, natures.length);
521 
522                     // set the new natures
523                     description.setNatureIds(newNatures);
524                     project.setDescription(description, null);
525 
526                     // and stop
527                     break;
528                 }
529             }
530         }
531     }
532 
533 
534     /**
535      * Removes a specific nature from a project.
536      * @param project The project to remove the nature from.
537      * @param nature The nature id to remove.
538      * @throws CoreException
539      */
removeNature(IProject project, String nature)540     public static void removeNature(IProject project, String nature) throws CoreException {
541         IProjectDescription description = project.getDescription();
542         String[] natures = description.getNatureIds();
543 
544         // check if the project already has the android nature.
545         for (int i = 0; i < natures.length; ++i) {
546             if (nature.equals(natures[i])) {
547                 String[] newNatures = new String[natures.length - 1];
548                 if (i > 0) {
549                     System.arraycopy(natures, 0, newNatures, 0, i);
550                 }
551                 System.arraycopy(natures, i + 1, newNatures, i, natures.length - i - 1);
552                 description.setNatureIds(newNatures);
553                 project.setDescription(description, null);
554 
555                 return;
556             }
557         }
558 
559     }
560 
561     /**
562      * Returns if the project has error level markers.
563      * @param includeReferencedProjects flag to also test the referenced projects.
564      * @throws CoreException
565      */
hasError(IProject project, boolean includeReferencedProjects)566     public static boolean hasError(IProject project, boolean includeReferencedProjects)
567     throws CoreException {
568         IMarker[] markers = project.findMarkers(IMarker.PROBLEM, true, IResource.DEPTH_INFINITE);
569         if (markers != null && markers.length > 0) {
570             // the project has marker(s). even though they are "problem" we
571             // don't know their severity. so we loop on them and figure if they
572             // are warnings or errors
573             for (IMarker m : markers) {
574                 int s = m.getAttribute(IMarker.SEVERITY, -1);
575                 if (s == IMarker.SEVERITY_ERROR) {
576                     return true;
577                 }
578             }
579         }
580 
581         // test the referenced projects if needed.
582         if (includeReferencedProjects) {
583             List<IProject> projects = getReferencedProjects(project);
584 
585             for (IProject p : projects) {
586                 if (hasError(p, false)) {
587                     return true;
588                 }
589             }
590         }
591 
592         return false;
593     }
594 
595     /**
596      * Saves a String property into the persistent storage of a resource.
597      * @param resource The resource into which the string value is saved.
598      * @param propertyName the name of the property. The id of the plug-in is added to this string.
599      * @param value the value to save
600      * @return true if the save succeeded.
601      */
saveStringProperty(IResource resource, String propertyName, String value)602     public static boolean saveStringProperty(IResource resource, String propertyName,
603             String value) {
604         QualifiedName qname = new QualifiedName(AdtPlugin.PLUGIN_ID, propertyName);
605 
606         try {
607             resource.setPersistentProperty(qname, value);
608         } catch (CoreException e) {
609             return false;
610         }
611 
612         return true;
613     }
614 
615     /**
616      * Loads a String property from the persistent storage of a resource.
617      * @param resource The resource from which the string value is loaded.
618      * @param propertyName the name of the property. The id of the plug-in is added to this string.
619      * @return the property value or null if it was not found.
620      */
loadStringProperty(IResource resource, String propertyName)621     public static String loadStringProperty(IResource resource, String propertyName) {
622         QualifiedName qname = new QualifiedName(AdtPlugin.PLUGIN_ID, propertyName);
623 
624         try {
625             String value = resource.getPersistentProperty(qname);
626             return value;
627         } catch (CoreException e) {
628             return null;
629         }
630     }
631 
632     /**
633      * Saves a property into the persistent storage of a resource.
634      * @param resource The resource into which the boolean value is saved.
635      * @param propertyName the name of the property. The id of the plug-in is added to this string.
636      * @param value the value to save
637      * @return true if the save succeeded.
638      */
saveBooleanProperty(IResource resource, String propertyName, boolean value)639     public static boolean saveBooleanProperty(IResource resource, String propertyName,
640             boolean value) {
641         return saveStringProperty(resource, propertyName, Boolean.toString(value));
642     }
643 
644     /**
645      * Loads a boolean property from the persistent storage of a resource.
646      * @param resource The resource from which the boolean value is loaded.
647      * @param propertyName the name of the property. The id of the plug-in is added to this string.
648      * @param defaultValue The default value to return if the property was not found.
649      * @return the property value or the default value if the property was not found.
650      */
loadBooleanProperty(IResource resource, String propertyName, boolean defaultValue)651     public static boolean loadBooleanProperty(IResource resource, String propertyName,
652             boolean defaultValue) {
653         String value = loadStringProperty(resource, propertyName);
654         if (value != null) {
655             return Boolean.parseBoolean(value);
656         }
657 
658         return defaultValue;
659     }
660 
661     /**
662      * Saves the path of a resource into the persistent storage of a resource.
663      * @param resource The resource into which the resource path is saved.
664      * @param propertyName the name of the property. The id of the plug-in is added to this string.
665      * @param value The resource to save. It's its path that is actually stored. If null, an
666      *      empty string is stored.
667      * @return true if the save succeeded
668      */
saveResourceProperty(IResource resource, String propertyName, IResource value)669     public static boolean saveResourceProperty(IResource resource, String propertyName,
670             IResource value) {
671         if (value != null) {
672             IPath iPath = value.getFullPath();
673             return saveStringProperty(resource, propertyName, iPath.toString());
674         }
675 
676         return saveStringProperty(resource, propertyName, ""); //$NON-NLS-1$
677     }
678 
679     /**
680      * Loads the path of a resource from the persistent storage of a resource, and returns the
681      * corresponding IResource object.
682      * @param resource The resource from which the resource path is loaded.
683      * @param propertyName the name of the property. The id of the plug-in is added to this string.
684      * @return The corresponding IResource object (or children interface) or null
685      */
loadResourceProperty(IResource resource, String propertyName)686     public static IResource loadResourceProperty(IResource resource, String propertyName) {
687         String value = loadStringProperty(resource, propertyName);
688 
689         if (value != null && value.length() > 0) {
690             return ResourcesPlugin.getWorkspace().getRoot().findMember(new Path(value));
691         }
692 
693         return null;
694     }
695 
696     /**
697      * Returns the list of referenced project that are opened and Java projects.
698      * @param project
699      * @return a new list object containing the opened referenced java project.
700      * @throws CoreException
701      */
getReferencedProjects(IProject project)702     public static List<IProject> getReferencedProjects(IProject project) throws CoreException {
703         IProject[] projects = project.getReferencedProjects();
704 
705         ArrayList<IProject> list = new ArrayList<IProject>();
706 
707         for (IProject p : projects) {
708             if (p.isOpen() && p.hasNature(JavaCore.NATURE_ID)) {
709                 list.add(p);
710             }
711         }
712 
713         return list;
714     }
715 
716 
717     /**
718      * Checks a Java project compiler level option against a list of supported versions.
719      * @param optionValue the Compiler level option.
720      * @return true if the option value is supproted.
721      */
checkCompliance(String optionValue)722     private static boolean checkCompliance(String optionValue) {
723         for (String s : AdtConstants.COMPILER_COMPLIANCE) {
724             if (s != null && s.equals(optionValue)) {
725                 return true;
726             }
727         }
728 
729         return false;
730     }
731 
732     /**
733      * Returns the apk filename for the given project
734      * @param project The project.
735      * @param config An optional config name. Can be null.
736      */
getApkFilename(IProject project, String config)737     public static String getApkFilename(IProject project, String config) {
738         if (config != null) {
739             return project.getName() + "-" + config + AdtConstants.DOT_ANDROID_PACKAGE; //$NON-NLS-1$
740         }
741 
742         return project.getName() + AdtConstants.DOT_ANDROID_PACKAGE;
743     }
744 
745     /**
746      * Find the list of projects on which this JavaProject is dependent on at the compilation level.
747      *
748      * @param javaProject Java project that we are looking for the dependencies.
749      * @return A list of Java projects for which javaProject depend on.
750      * @throws JavaModelException
751      */
getAndroidProjectDependencies(IJavaProject javaProject)752     public static List<IJavaProject> getAndroidProjectDependencies(IJavaProject javaProject)
753         throws JavaModelException {
754         String[] requiredProjectNames = javaProject.getRequiredProjectNames();
755 
756         // Go from java project name to JavaProject name
757         IJavaModel javaModel = javaProject.getJavaModel();
758 
759         // loop through all dependent projects and keep only those that are Android projects
760         List<IJavaProject> projectList = new ArrayList<IJavaProject>(requiredProjectNames.length);
761         for (String javaProjectName : requiredProjectNames) {
762             IJavaProject androidJavaProject = javaModel.getJavaProject(javaProjectName);
763 
764             //Verify that the project has also the Android Nature
765             try {
766                 if (!androidJavaProject.getProject().hasNature(AdtConstants.NATURE_DEFAULT)) {
767                     continue;
768                 }
769             } catch (CoreException e) {
770                 continue;
771             }
772 
773             projectList.add(androidJavaProject);
774         }
775 
776         return projectList;
777     }
778 
779     /**
780      * Returns the android package file as an IFile object for the specified
781      * project.
782      * @param project The project
783      * @return The android package as an IFile object or null if not found.
784      */
getApplicationPackage(IProject project)785     public static IFile getApplicationPackage(IProject project) {
786         // get the output folder
787         IFolder outputLocation = BaseProjectHelper.getAndroidOutputFolder(project);
788 
789         if (outputLocation == null) {
790             AdtPlugin.printErrorToConsole(project,
791                     "Failed to get the output location of the project. Check build path properties"
792                     );
793             return null;
794         }
795 
796 
797         // get the package path
798         String packageName = project.getName() + AdtConstants.DOT_ANDROID_PACKAGE;
799         IResource r = outputLocation.findMember(packageName);
800 
801         // check the package is present
802         if (r instanceof IFile && r.exists()) {
803             return (IFile)r;
804         }
805 
806         String msg = String.format("Could not find %1$s!", packageName);
807         AdtPlugin.printErrorToConsole(project, msg);
808 
809         return null;
810     }
811 
812     /**
813      * Returns an {@link IFile} object representing the manifest for the given project.
814      *
815      * @param project The project containing the manifest file.
816      * @return An IFile object pointing to the manifest or null if the manifest
817      *         is missing.
818      */
getManifest(IProject project)819     public static IFile getManifest(IProject project) {
820         IResource r = project.findMember(AdtConstants.WS_SEP
821                 + SdkConstants.FN_ANDROID_MANIFEST_XML);
822 
823         if (r == null || r.exists() == false || (r instanceof IFile) == false) {
824             return null;
825         }
826         return (IFile) r;
827     }
828 
829     /**
830      * Build project incrementally. If fullBuild is not set, then the packaging steps in
831      * the post compiler are skipped. (Though resource deltas are still processed).
832      *
833      * @param project The project to be built.
834      * @param monitor A eclipse runtime progress monitor to be updated by the builders.
835      * @param fullBuild Set whether to
836      * run the packaging (dexing and building apk) steps of the
837      *                  post compiler.
838      * @param buildDeps Set whether to run builders on the dependencies of the project
839      * @throws CoreException
840      */
build(IProject project, IProgressMonitor monitor, boolean fullBuild, boolean buildDeps)841     public static void build(IProject project, IProgressMonitor monitor,
842                              boolean fullBuild, boolean buildDeps)
843                             throws CoreException {
844         // Get list of projects that we depend on
845         List<IJavaProject> androidProjectList = new ArrayList<IJavaProject>();
846         if (buildDeps) {
847             try {
848                 androidProjectList = getAndroidProjectDependencies(
849                                         BaseProjectHelper.getJavaProject(project));
850             } catch (JavaModelException e) {
851                 AdtPlugin.printErrorToConsole(project, e);
852             }
853             // Recursively build dependencies
854             for (IJavaProject dependency : androidProjectList) {
855                 build(dependency.getProject(), monitor, fullBuild, true);
856             }
857         }
858 
859         // Do an incremental build to pick up all the deltas
860         project.build(IncrementalProjectBuilder.INCREMENTAL_BUILD, monitor);
861 
862         // If the preferences indicate not to use post compiler optimization
863         // then the incremental build will have done everything necessary
864         if (fullBuild && AdtPrefs.getPrefs().getBuildSkipPostCompileOnFileSave()) {
865             // Create the map to pass to the PostC builder
866             Map<String, String> args = new TreeMap<String, String>();
867             args.put(PostCompilerBuilder.POST_C_REQUESTED, ""); //$NON-NLS-1$
868             // Get Post Compiler for this project
869             project.build(IncrementalProjectBuilder.FULL_BUILD,
870                           PostCompilerBuilder.ID, args, monitor);
871         }
872     }
873 
874     /**
875      * Build the project incrementally. Post compilation step will not occur.
876      * Projects that this project depends on will not be built.
877      * This is equivalent to calling
878      * <code>build(project, monitor, false, false)</code>
879      *
880      * @param project The project to be built.
881      * @param monitor A eclipse runtime progress monitor to be updated by the builders.
882      * @throws CoreException
883      * @see #build(IProject, IProgressMonitor, boolean)
884      */
build(IProject project, IProgressMonitor monitor)885     public static void build(IProject project, IProgressMonitor monitor)
886                              throws CoreException {
887         // Disable full building by default
888         build(project, monitor, false, false);
889     }
890 }
891