Home | History | Annotate | Download | only in project
      1 /*
      2  * Copyright (C) 2011 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 static com.android.ide.eclipse.adt.AdtConstants.CONTAINER_DEPENDENCIES;
     20 
     21 import com.android.SdkConstants;
     22 import com.android.ide.common.sdk.LoadStatus;
     23 import com.android.ide.eclipse.adt.AdtConstants;
     24 import com.android.ide.eclipse.adt.AdtPlugin;
     25 import com.android.ide.eclipse.adt.AndroidPrintStream;
     26 import com.android.ide.eclipse.adt.internal.sdk.ProjectState;
     27 import com.android.ide.eclipse.adt.internal.sdk.Sdk;
     28 import com.android.sdklib.BuildToolInfo;
     29 import com.android.sdklib.build.JarListSanitizer;
     30 import com.android.sdklib.build.JarListSanitizer.DifferentLibException;
     31 import com.android.sdklib.build.JarListSanitizer.Sha1Exception;
     32 import com.android.sdklib.build.RenderScriptProcessor;
     33 
     34 import org.eclipse.core.resources.IFile;
     35 import org.eclipse.core.resources.IFolder;
     36 import org.eclipse.core.resources.IProject;
     37 import org.eclipse.core.resources.IResource;
     38 import org.eclipse.core.resources.IWorkspaceRoot;
     39 import org.eclipse.core.resources.ResourcesPlugin;
     40 import org.eclipse.core.runtime.CoreException;
     41 import org.eclipse.core.runtime.IPath;
     42 import org.eclipse.core.runtime.NullProgressMonitor;
     43 import org.eclipse.core.runtime.Path;
     44 import org.eclipse.jdt.core.IAccessRule;
     45 import org.eclipse.jdt.core.IClasspathAttribute;
     46 import org.eclipse.jdt.core.IClasspathContainer;
     47 import org.eclipse.jdt.core.IClasspathEntry;
     48 import org.eclipse.jdt.core.IJavaProject;
     49 import org.eclipse.jdt.core.JavaCore;
     50 import org.eclipse.jdt.core.JavaModelException;
     51 
     52 import java.io.File;
     53 import java.io.FileInputStream;
     54 import java.io.FileNotFoundException;
     55 import java.io.IOException;
     56 import java.io.InputStream;
     57 import java.net.MalformedURLException;
     58 import java.util.ArrayList;
     59 import java.util.HashSet;
     60 import java.util.List;
     61 import java.util.Properties;
     62 import java.util.Set;
     63 
     64 public class LibraryClasspathContainerInitializer extends BaseClasspathContainerInitializer {
     65 
     66     private final static String ATTR_SRC = "src"; //$NON-NLS-1$
     67     private final static String ATTR_DOC = "doc"; //$NON-NLS-1$
     68     private final static String DOT_PROPERTIES = ".properties"; //$NON-NLS-1$
     69 
     70     public LibraryClasspathContainerInitializer() {
     71     }
     72 
     73     /**
     74      * Updates the {@link IJavaProject} objects with new library.
     75      * @param androidProjects the projects to update.
     76      * @return <code>true</code> if success, <code>false</code> otherwise.
     77      */
     78     public static boolean updateProjects(IJavaProject[] androidProjects) {
     79         try {
     80             // Allocate a new AndroidClasspathContainer, and associate it to the library
     81             // container id for each projects.
     82             int projectCount = androidProjects.length;
     83 
     84             IClasspathContainer[] libraryContainers = new IClasspathContainer[projectCount];
     85             IClasspathContainer[] dependencyContainers = new IClasspathContainer[projectCount];
     86             for (int i = 0 ; i < projectCount; i++) {
     87                 libraryContainers[i] = allocateLibraryContainer(androidProjects[i]);
     88                 dependencyContainers[i] = allocateDependencyContainer(androidProjects[i]);
     89             }
     90 
     91             // give each project their new container in one call.
     92             JavaCore.setClasspathContainer(
     93                     new Path(AdtConstants.CONTAINER_PRIVATE_LIBRARIES),
     94                     androidProjects, libraryContainers, new NullProgressMonitor());
     95 
     96             JavaCore.setClasspathContainer(
     97                     new Path(AdtConstants.CONTAINER_DEPENDENCIES),
     98                     androidProjects, dependencyContainers, new NullProgressMonitor());
     99             return true;
    100         } catch (JavaModelException e) {
    101             return false;
    102         }
    103     }
    104 
    105     /**
    106      * Updates the {@link IJavaProject} objects with new library.
    107      * @param androidProjects the projects to update.
    108      * @return <code>true</code> if success, <code>false</code> otherwise.
    109      */
    110     public static boolean updateProject(List<ProjectState> projects) {
    111         List<IJavaProject> javaProjectList = new ArrayList<IJavaProject>(projects.size());
    112         for (ProjectState p : projects) {
    113             IJavaProject javaProject = JavaCore.create(p.getProject());
    114             if (javaProject != null) {
    115                 javaProjectList.add(javaProject);
    116             }
    117         }
    118 
    119         IJavaProject[] javaProjects = javaProjectList.toArray(
    120                 new IJavaProject[javaProjectList.size()]);
    121 
    122         return updateProjects(javaProjects);
    123     }
    124 
    125     @Override
    126     public void initialize(IPath containerPath, IJavaProject project) throws CoreException {
    127         if (AdtConstants.CONTAINER_PRIVATE_LIBRARIES.equals(containerPath.toString())) {
    128             IClasspathContainer libraries = allocateLibraryContainer(project);
    129             if (libraries != null) {
    130                 JavaCore.setClasspathContainer(new Path(AdtConstants.CONTAINER_PRIVATE_LIBRARIES),
    131                         new IJavaProject[] { project },
    132                         new IClasspathContainer[] { libraries },
    133                         new NullProgressMonitor());
    134             }
    135 
    136         } else if(AdtConstants.CONTAINER_DEPENDENCIES.equals(containerPath.toString())) {
    137             IClasspathContainer dependencies = allocateDependencyContainer(project);
    138             if (dependencies != null) {
    139                 JavaCore.setClasspathContainer(new Path(AdtConstants.CONTAINER_DEPENDENCIES),
    140                         new IJavaProject[] { project },
    141                         new IClasspathContainer[] { dependencies },
    142                         new NullProgressMonitor());
    143             }
    144         }
    145     }
    146 
    147     private static IClasspathContainer allocateLibraryContainer(IJavaProject javaProject) {
    148         final IProject iProject = javaProject.getProject();
    149 
    150         // check if the project has a valid target.
    151         ProjectState state = Sdk.getProjectState(iProject);
    152         if (state == null) {
    153             // getProjectState should already have logged an error. Just bail out.
    154             return null;
    155         }
    156 
    157         /*
    158          * At this point we're going to gather a list of all that need to go in the
    159          * dependency container.
    160          * - Library project outputs (direct and indirect)
    161          * - Java project output (those can be indirectly referenced through library projects
    162          *   or other other Java projects)
    163          * - Jar files:
    164          *    + inside this project's libs/
    165          *    + inside the library projects' libs/
    166          *    + inside the referenced Java projects' classpath
    167          */
    168         List<IClasspathEntry> entries = new ArrayList<IClasspathEntry>();
    169 
    170         // list of java project dependencies and jar files that will be built while
    171         // going through the library projects.
    172         Set<File> jarFiles = new HashSet<File>();
    173         Set<IProject> refProjects = new HashSet<IProject>();
    174 
    175         // process all the libraries
    176 
    177         List<IProject> libProjects = state.getFullLibraryProjects();
    178         for (IProject libProject : libProjects) {
    179             // process all of the library project's dependencies
    180             getDependencyListFromClasspath(libProject, refProjects, jarFiles, true);
    181         }
    182 
    183         // now process this projects' referenced projects only.
    184         processReferencedProjects(iProject, refProjects, jarFiles);
    185 
    186         // and the content of its libs folder
    187         getJarListFromLibsFolder(iProject, jarFiles);
    188 
    189         // now add a classpath entry for each Java project (this is a set so dups are already
    190         // removed)
    191         for (IProject p : refProjects) {
    192             entries.add(JavaCore.newProjectEntry(p.getFullPath(), true /*isExported*/));
    193         }
    194 
    195         entries.addAll(convertJarsToClasspathEntries(iProject, jarFiles));
    196 
    197         return allocateContainer(javaProject, entries, new Path(AdtConstants.CONTAINER_PRIVATE_LIBRARIES),
    198                 "Android Private Libraries");
    199     }
    200 
    201     private static List<IClasspathEntry> convertJarsToClasspathEntries(final IProject iProject,
    202             Set<File> jarFiles) {
    203         List<IClasspathEntry> entries = new ArrayList<IClasspathEntry>(jarFiles.size());
    204 
    205         // and process the jar files list, but first sanitize it to remove dups.
    206         JarListSanitizer sanitizer = new JarListSanitizer(
    207                 iProject.getFolder(SdkConstants.FD_OUTPUT).getLocation().toFile(),
    208                 new AndroidPrintStream(iProject, null /*prefix*/,
    209                         AdtPlugin.getOutStream()));
    210 
    211         String errorMessage = null;
    212 
    213         try {
    214             List<File> sanitizedList = sanitizer.sanitize(jarFiles);
    215 
    216             for (File jarFile : sanitizedList) {
    217                 if (jarFile instanceof CPEFile) {
    218                     CPEFile cpeFile = (CPEFile) jarFile;
    219                     IClasspathEntry e = cpeFile.getClasspathEntry();
    220 
    221                     entries.add(JavaCore.newLibraryEntry(
    222                             e.getPath(),
    223                             e.getSourceAttachmentPath(),
    224                             e.getSourceAttachmentRootPath(),
    225                             e.getAccessRules(),
    226                             e.getExtraAttributes(),
    227                             true /*isExported*/));
    228                 } else {
    229                     String jarPath = jarFile.getAbsolutePath();
    230 
    231                     IPath sourceAttachmentPath = null;
    232                     IClasspathAttribute javaDocAttribute = null;
    233 
    234                     File jarProperties = new File(jarPath + DOT_PROPERTIES);
    235                     if (jarProperties.isFile()) {
    236                         Properties p = new Properties();
    237                         InputStream is = null;
    238                         try {
    239                             p.load(is = new FileInputStream(jarProperties));
    240 
    241                             String value = p.getProperty(ATTR_SRC);
    242                             if (value != null) {
    243                                 File srcPath = getFile(jarFile, value);
    244 
    245                                 if (srcPath.exists()) {
    246                                     sourceAttachmentPath = new Path(srcPath.getAbsolutePath());
    247                                 }
    248                             }
    249 
    250                             value = p.getProperty(ATTR_DOC);
    251                             if (value != null) {
    252                                 File docPath = getFile(jarFile, value);
    253                                 if (docPath.exists()) {
    254                                     try {
    255                                         javaDocAttribute = JavaCore.newClasspathAttribute(
    256                                                 IClasspathAttribute.JAVADOC_LOCATION_ATTRIBUTE_NAME,
    257                                                 docPath.toURI().toURL().toString());
    258                                     } catch (MalformedURLException e) {
    259                                         AdtPlugin.log(e, "Failed to process 'doc' attribute for %s",
    260                                                 jarProperties.getAbsolutePath());
    261                                     }
    262                                 }
    263                             }
    264 
    265                         } catch (FileNotFoundException e) {
    266                             // shouldn't happen since we check upfront
    267                         } catch (IOException e) {
    268                             AdtPlugin.log(e, "Failed to read %s", jarProperties.getAbsolutePath());
    269                         } finally {
    270                             if (is != null) {
    271                                 try {
    272                                     is.close();
    273                                 } catch (IOException e) {
    274                                     // ignore
    275                                 }
    276                             }
    277                         }
    278                     }
    279 
    280                     if (javaDocAttribute != null) {
    281                         entries.add(JavaCore.newLibraryEntry(new Path(jarPath),
    282                                 sourceAttachmentPath, null /*sourceAttachmentRootPath*/,
    283                                 new IAccessRule[0],
    284                                 new IClasspathAttribute[] { javaDocAttribute },
    285                                 true /*isExported*/));
    286                     } else {
    287                         entries.add(JavaCore.newLibraryEntry(new Path(jarPath),
    288                                 sourceAttachmentPath, null /*sourceAttachmentRootPath*/,
    289                                 true /*isExported*/));
    290                     }
    291                 }
    292             }
    293         } catch (DifferentLibException e) {
    294             errorMessage = e.getMessage();
    295             AdtPlugin.printErrorToConsole(iProject, (Object[]) e.getDetails());
    296         } catch (Sha1Exception e) {
    297             errorMessage = e.getMessage();
    298         }
    299 
    300         processError(iProject, errorMessage, AdtConstants.MARKER_DEPENDENCY,
    301                 true /*outputToConsole*/);
    302 
    303         return entries;
    304     }
    305 
    306     private static IClasspathContainer allocateDependencyContainer(IJavaProject javaProject) {
    307         final IProject iProject = javaProject.getProject();
    308         final List<IClasspathEntry> entries = new ArrayList<IClasspathEntry>();
    309         final Set<File> jarFiles = new HashSet<File>();
    310         final IWorkspaceRoot workspaceRoot = ResourcesPlugin.getWorkspace().getRoot();
    311 
    312         AdtPlugin plugin = AdtPlugin.getDefault();
    313         if (plugin == null) { // This is totally weird, but I've seen it happen!
    314             return null;
    315         }
    316 
    317         synchronized (Sdk.getLock()) {
    318             boolean sdkIsLoaded = plugin.getSdkLoadStatus() == LoadStatus.LOADED;
    319 
    320             // check if the project has a valid target.
    321             final ProjectState state = Sdk.getProjectState(iProject);
    322             if (state == null) {
    323                 // getProjectState should already have logged an error. Just bail out.
    324                 return null;
    325             }
    326 
    327             // annotations support for older version of android
    328             if (state.getTarget() != null && state.getTarget().getVersion().getApiLevel() <= 15) {
    329                 File annotationsJar = new File(Sdk.getCurrent().getSdkOsLocation(),
    330                         SdkConstants.FD_TOOLS + File.separator + SdkConstants.FD_SUPPORT +
    331                         File.separator + SdkConstants.FN_ANNOTATIONS_JAR);
    332 
    333                 jarFiles.add(annotationsJar);
    334             }
    335 
    336             if (state.getRenderScriptSupportMode()) {
    337                 if (!sdkIsLoaded) {
    338                     return null;
    339                 }
    340                 BuildToolInfo buildToolInfo = state.getBuildToolInfo();
    341                 if (buildToolInfo == null) {
    342                     buildToolInfo = Sdk.getCurrent().getLatestBuildTool();
    343 
    344                     if (buildToolInfo == null) {
    345                         return null;
    346                     }
    347                 }
    348 
    349                 File renderScriptSupportJar = RenderScriptProcessor.getSupportJar(
    350                         buildToolInfo.getLocation().getAbsolutePath());
    351 
    352                 jarFiles.add(renderScriptSupportJar);
    353             }
    354 
    355             // process all the libraries
    356 
    357             List<IProject> libProjects = state.getFullLibraryProjects();
    358             for (IProject libProject : libProjects) {
    359                 // get the project output
    360                 IFolder outputFolder = BaseProjectHelper.getAndroidOutputFolder(libProject);
    361 
    362                 if (outputFolder != null) { // can happen when closing/deleting a library)
    363                     IFile jarIFile = outputFolder.getFile(libProject.getName().toLowerCase() +
    364                            SdkConstants.DOT_JAR);
    365 
    366                     // get the source folder for the library project
    367                     List<IPath> srcs = BaseProjectHelper.getSourceClasspaths(libProject);
    368                     // find the first non-derived source folder.
    369                     IPath sourceFolder = null;
    370                     for (IPath src : srcs) {
    371                         IFolder srcFolder = workspaceRoot.getFolder(src);
    372                         if (srcFolder.isDerived() == false) {
    373                             sourceFolder = src;
    374                             break;
    375                         }
    376                     }
    377 
    378                     // we can directly add a CPE for this jar as there's no risk of a duplicate.
    379                     IClasspathEntry entry = JavaCore.newLibraryEntry(
    380                             jarIFile.getLocation(),
    381                             sourceFolder, // source attachment path
    382                             null,         // default source attachment root path.
    383                             true /*isExported*/);
    384 
    385                     entries.add(entry);
    386                 }
    387             }
    388 
    389             entries.addAll(convertJarsToClasspathEntries(iProject, jarFiles));
    390 
    391             return allocateContainer(javaProject, entries, new Path(CONTAINER_DEPENDENCIES),
    392                     "Android Dependencies");
    393         }
    394     }
    395 
    396     private static IClasspathContainer allocateContainer(IJavaProject javaProject,
    397             List<IClasspathEntry> entries, IPath id, String description) {
    398 
    399         if (AdtPlugin.getDefault() == null) { // This is totally weird, but I've seen it happen!
    400             return null;
    401         }
    402 
    403         // First check that the project has a library-type container.
    404         try {
    405             IClasspathEntry[] rawClasspath = javaProject.getRawClasspath();
    406             final IClasspathEntry[] oldRawClasspath = rawClasspath;
    407 
    408             boolean foundContainer = false;
    409             for (IClasspathEntry entry : rawClasspath) {
    410                 // get the entry and kind
    411                 final int kind = entry.getEntryKind();
    412 
    413                 if (kind == IClasspathEntry.CPE_CONTAINER) {
    414                     String path = entry.getPath().toString();
    415                     String idString = id.toString();
    416                     if (idString.equals(path)) {
    417                         foundContainer = true;
    418                         break;
    419                     }
    420                 }
    421             }
    422 
    423             // if there isn't any, add it.
    424             if (foundContainer == false) {
    425                 // add the android container to the array
    426                 rawClasspath = ProjectHelper.addEntryToClasspath(rawClasspath,
    427                         JavaCore.newContainerEntry(id, true /*isExported*/));
    428             }
    429 
    430             // set the new list of entries to the project
    431             if (rawClasspath != oldRawClasspath) {
    432                 javaProject.setRawClasspath(rawClasspath, new NullProgressMonitor());
    433             }
    434         } catch (JavaModelException e) {
    435             // This really shouldn't happen, but if it does, simply return null (the calling
    436             // method will fails as well)
    437             return null;
    438         }
    439 
    440         return new AndroidClasspathContainer(
    441                 entries.toArray(new IClasspathEntry[entries.size()]),
    442                 id,
    443                 description,
    444                 IClasspathContainer.K_APPLICATION);
    445     }
    446 
    447     private static File getFile(File root, String value) {
    448         File file = new File(value);
    449         if (file.isAbsolute() == false) {
    450             file = new File(root.getParentFile(), value);
    451         }
    452 
    453         return file;
    454     }
    455 
    456     /**
    457      * Finds all the jar files inside a project's libs folder.
    458      * @param project
    459      * @param jarFiles
    460      */
    461     private static void getJarListFromLibsFolder(IProject project, Set<File> jarFiles) {
    462         IFolder libsFolder = project.getFolder(SdkConstants.FD_NATIVE_LIBS);
    463         if (libsFolder.exists()) {
    464             try {
    465                 IResource[] members = libsFolder.members();
    466                 for (IResource member : members) {
    467                     if (member.getType() == IResource.FILE &&
    468                             SdkConstants.EXT_JAR.equalsIgnoreCase(member.getFileExtension())) {
    469                         IPath location = member.getLocation();
    470                         if (location != null) {
    471                             jarFiles.add(location.toFile());
    472                         }
    473                     }
    474                 }
    475             } catch (CoreException e) {
    476                 // can't get the list? ignore this folder.
    477             }
    478         }
    479     }
    480 
    481     /**
    482      * Process reference projects from the main projects to add indirect dependencies coming
    483      * from Java project.
    484      * @param project the main project
    485      * @param projects the project list to add to
    486      * @param jarFiles the jar list to add to.
    487      */
    488     private static void processReferencedProjects(IProject project,
    489             Set<IProject> projects, Set<File> jarFiles) {
    490         try {
    491             IProject[] refs = project.getReferencedProjects();
    492             for (IProject p : refs) {
    493                 // ignore if it's an Android project, or if it's not a Java
    494                 // Project
    495                 if (p.hasNature(JavaCore.NATURE_ID)
    496                         && p.hasNature(AdtConstants.NATURE_DEFAULT) == false) {
    497 
    498                     // process this project's dependencies
    499                     getDependencyListFromClasspath(p, projects, jarFiles, true /*includeJarFiles*/);
    500                 }
    501             }
    502         } catch (CoreException e) {
    503             // can't get the referenced projects? ignore
    504         }
    505     }
    506 
    507     /**
    508      * Finds all the dependencies of a given project and add them to a project list and
    509      * a jar list.
    510      * Only classpath entries that are exported are added, and only Java project (not Android
    511      * project) are added.
    512      *
    513      * @param project the project to query
    514      * @param projects the referenced project list to add to
    515      * @param jarFiles the jar list to add to
    516      * @param includeJarFiles whether to include jar files or just projects. This is useful when
    517      *           calling on an Android project (value should be <code>false</code>)
    518      */
    519     private static void getDependencyListFromClasspath(IProject project, Set<IProject> projects,
    520             Set<File> jarFiles, boolean includeJarFiles) {
    521         IJavaProject javaProject = JavaCore.create(project);
    522         IWorkspaceRoot wsRoot = ResourcesPlugin.getWorkspace().getRoot();
    523 
    524         // we could use IJavaProject.getResolvedClasspath directly, but we actually
    525         // want to see the containers themselves.
    526         IClasspathEntry[] classpaths = javaProject.readRawClasspath();
    527         if (classpaths != null) {
    528             for (IClasspathEntry e : classpaths) {
    529                 // ignore entries that are not exported
    530                 if (!e.getPath().toString().equals(CONTAINER_DEPENDENCIES) && e.isExported()) {
    531                     processCPE(e, javaProject, wsRoot, projects, jarFiles, includeJarFiles);
    532                 }
    533             }
    534         }
    535     }
    536 
    537     /**
    538      * Processes a {@link IClasspathEntry} and add it to one of the list if applicable.
    539      * @param entry the entry to process
    540      * @param javaProject the {@link IJavaProject} from which this entry came.
    541      * @param wsRoot the {@link IWorkspaceRoot}
    542      * @param projects the project list to add to
    543      * @param jarFiles the jar list to add to
    544      * @param includeJarFiles whether to include jar files or just projects. This is useful when
    545      *           calling on an Android project (value should be <code>false</code>)
    546      */
    547     private static void processCPE(IClasspathEntry entry, IJavaProject javaProject,
    548             IWorkspaceRoot wsRoot,
    549             Set<IProject> projects, Set<File> jarFiles, boolean includeJarFiles) {
    550 
    551         // if this is a classpath variable reference, we resolve it.
    552         if (entry.getEntryKind() == IClasspathEntry.CPE_VARIABLE) {
    553             entry = JavaCore.getResolvedClasspathEntry(entry);
    554         }
    555 
    556         if (entry.getEntryKind() == IClasspathEntry.CPE_PROJECT) {
    557             IProject refProject = wsRoot.getProject(entry.getPath().lastSegment());
    558             try {
    559                 // ignore if it's an Android project, or if it's not a Java Project
    560                 if (refProject.hasNature(JavaCore.NATURE_ID) &&
    561                         refProject.hasNature(AdtConstants.NATURE_DEFAULT) == false) {
    562                     // add this project to the list
    563                     projects.add(refProject);
    564 
    565                     // also get the dependency from this project.
    566                     getDependencyListFromClasspath(refProject, projects, jarFiles,
    567                             true /*includeJarFiles*/);
    568                 }
    569             } catch (CoreException exception) {
    570                 // can't query the project nature? ignore
    571             }
    572         } else if (entry.getEntryKind() == IClasspathEntry.CPE_LIBRARY) {
    573             if (includeJarFiles) {
    574                 handleClasspathLibrary(entry, wsRoot, jarFiles);
    575             }
    576         } else if (entry.getEntryKind() == IClasspathEntry.CPE_CONTAINER) {
    577             // get the container and its content
    578             try {
    579                 IClasspathContainer container = JavaCore.getClasspathContainer(
    580                         entry.getPath(), javaProject);
    581                 // ignore the system and default_system types as they represent
    582                 // libraries that are part of the runtime.
    583                 if (container != null &&
    584                         container.getKind() == IClasspathContainer.K_APPLICATION) {
    585                     IClasspathEntry[] entries = container.getClasspathEntries();
    586                     for (IClasspathEntry cpe : entries) {
    587                         processCPE(cpe, javaProject, wsRoot, projects, jarFiles, includeJarFiles);
    588                     }
    589                 }
    590             } catch (JavaModelException jme) {
    591                 // can't resolve the container? ignore it.
    592                 AdtPlugin.log(jme, "Failed to resolve ClasspathContainer: %s", entry.getPath());
    593             }
    594         }
    595     }
    596 
    597     private static final class CPEFile extends File {
    598         private static final long serialVersionUID = 1L;
    599 
    600         private final IClasspathEntry mClasspathEntry;
    601 
    602         public CPEFile(String pathname, IClasspathEntry classpathEntry) {
    603             super(pathname);
    604             mClasspathEntry = classpathEntry;
    605         }
    606 
    607         public CPEFile(File file, IClasspathEntry classpathEntry) {
    608             super(file.getAbsolutePath());
    609             mClasspathEntry = classpathEntry;
    610         }
    611 
    612         public IClasspathEntry getClasspathEntry() {
    613             return mClasspathEntry;
    614         }
    615     }
    616 
    617     private static void handleClasspathLibrary(IClasspathEntry e, IWorkspaceRoot wsRoot,
    618             Set<File> jarFiles) {
    619         // get the IPath
    620         IPath path = e.getPath();
    621 
    622         IResource resource = wsRoot.findMember(path);
    623 
    624         if (SdkConstants.EXT_JAR.equalsIgnoreCase(path.getFileExtension())) {
    625             // case of a jar file (which could be relative to the workspace or a full path)
    626             if (resource != null && resource.exists() &&
    627                     resource.getType() == IResource.FILE) {
    628                 jarFiles.add(new CPEFile(resource.getLocation().toFile(), e));
    629             } else {
    630                 // if the jar path doesn't match a workspace resource,
    631                 // then we get an OSString and check if this links to a valid file.
    632                 String osFullPath = path.toOSString();
    633 
    634                 File f = new CPEFile(osFullPath, e);
    635                 if (f.isFile()) {
    636                     jarFiles.add(f);
    637                 }
    638             }
    639         }
    640     }
    641 }
    642