• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
LibraryClasspathContainerInitializer()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      */
updateProjects(IJavaProject[] androidProjects)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      */
updateProject(List<ProjectState> projects)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
initialize(IPath containerPath, IJavaProject project)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 
allocateLibraryContainer(IJavaProject javaProject)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 
convertJarsToClasspathEntries(final IProject iProject, Set<File> jarFiles)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 
allocateDependencyContainer(IJavaProject javaProject)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 
allocateContainer(IJavaProject javaProject, List<IClasspathEntry> entries, IPath id, String description)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 
getFile(File root, String value)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      */
getJarListFromLibsFolder(IProject project, Set<File> jarFiles)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      */
processReferencedProjects(IProject project, Set<IProject> projects, Set<File> jarFiles)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      */
getDependencyListFromClasspath(IProject project, Set<IProject> projects, Set<File> jarFiles, boolean includeJarFiles)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      */
processCPE(IClasspathEntry entry, IJavaProject javaProject, IWorkspaceRoot wsRoot, Set<IProject> projects, Set<File> jarFiles, boolean includeJarFiles)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 
CPEFile(String pathname, IClasspathEntry classpathEntry)602         public CPEFile(String pathname, IClasspathEntry classpathEntry) {
603             super(pathname);
604             mClasspathEntry = classpathEntry;
605         }
606 
CPEFile(File file, IClasspathEntry classpathEntry)607         public CPEFile(File file, IClasspathEntry classpathEntry) {
608             super(file.getAbsolutePath());
609             mClasspathEntry = classpathEntry;
610         }
611 
getClasspathEntry()612         public IClasspathEntry getClasspathEntry() {
613             return mClasspathEntry;
614         }
615     }
616 
handleClasspathLibrary(IClasspathEntry e, IWorkspaceRoot wsRoot, Set<File> jarFiles)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