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