KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > eclipse > jdt > internal > ui > jarpackager > JarFileExportOperation


1 /*******************************************************************************
2  * Copyright (c) 2000, 2007 IBM Corporation and others.
3  * All rights reserved. This program and the accompanying materials
4  * are made available under the terms of the Eclipse Public License v1.0
5  * which accompanies this distribution, and is available at
6  * http://www.eclipse.org/legal/epl-v10.html
7  *
8  * Contributors:
9  * IBM Corporation - initial API and implementation
10  * Matt Chapman, mpchapman@gmail.com - 89977 Make JDT .java agnostic
11  *******************************************************************************/

12 package org.eclipse.jdt.internal.ui.jarpackager;
13
14 import java.io.ByteArrayInputStream JavaDoc;
15 import java.io.ByteArrayOutputStream JavaDoc;
16 import java.io.File JavaDoc;
17 import java.io.IOException JavaDoc;
18 import java.io.InputStream JavaDoc;
19 import java.lang.reflect.InvocationTargetException JavaDoc;
20 import java.net.URI JavaDoc;
21 import java.util.ArrayList JavaDoc;
22 import java.util.Arrays JavaDoc;
23 import java.util.Collections JavaDoc;
24 import java.util.HashMap JavaDoc;
25 import java.util.HashSet JavaDoc;
26 import java.util.Iterator JavaDoc;
27 import java.util.List JavaDoc;
28 import java.util.Map JavaDoc;
29 import java.util.Set JavaDoc;
30 import java.util.jar.Manifest JavaDoc;
31 import java.util.zip.ZipException JavaDoc;
32
33 import org.eclipse.core.filesystem.EFS;
34 import org.eclipse.core.runtime.CoreException;
35 import org.eclipse.core.runtime.IPath;
36 import org.eclipse.core.runtime.IProgressMonitor;
37 import org.eclipse.core.runtime.IStatus;
38 import org.eclipse.core.runtime.MultiStatus;
39 import org.eclipse.core.runtime.Status;
40 import org.eclipse.core.runtime.SubProgressMonitor;
41
42 import org.eclipse.core.resources.IContainer;
43 import org.eclipse.core.resources.IFile;
44 import org.eclipse.core.resources.IFolder;
45 import org.eclipse.core.resources.IMarker;
46 import org.eclipse.core.resources.IProject;
47 import org.eclipse.core.resources.IResource;
48 import org.eclipse.core.resources.IncrementalProjectBuilder;
49 import org.eclipse.core.resources.ResourcesPlugin;
50
51 import org.eclipse.swt.widgets.Shell;
52
53 import org.eclipse.jface.operation.ModalContext;
54
55 import org.eclipse.ui.actions.WorkspaceModifyOperation;
56
57 import org.eclipse.jdt.core.IClasspathEntry;
58 import org.eclipse.jdt.core.IJavaElement;
59 import org.eclipse.jdt.core.IJavaModelMarker;
60 import org.eclipse.jdt.core.IJavaProject;
61 import org.eclipse.jdt.core.IPackageFragment;
62 import org.eclipse.jdt.core.IPackageFragmentRoot;
63 import org.eclipse.jdt.core.IRegion;
64 import org.eclipse.jdt.core.ITypeRoot;
65 import org.eclipse.jdt.core.JavaCore;
66 import org.eclipse.jdt.core.JavaModelException;
67 import org.eclipse.jdt.core.ToolFactory;
68 import org.eclipse.jdt.core.util.IClassFileReader;
69 import org.eclipse.jdt.core.util.ISourceAttribute;
70
71 import org.eclipse.jdt.internal.corext.util.JavaModelUtil;
72 import org.eclipse.jdt.internal.corext.util.Messages;
73 import org.eclipse.jdt.internal.corext.util.Resources;
74
75 import org.eclipse.jdt.ui.StandardJavaElementContentProvider;
76 import org.eclipse.jdt.ui.jarpackager.IJarDescriptionWriter;
77 import org.eclipse.jdt.ui.jarpackager.IJarExportRunnable;
78 import org.eclipse.jdt.ui.jarpackager.JarPackageData;
79 import org.eclipse.jdt.ui.jarpackager.JarWriter3;
80
81 import org.eclipse.jdt.internal.ui.IJavaStatusConstants;
82 import org.eclipse.jdt.internal.ui.JavaPlugin;
83 import org.eclipse.jdt.internal.ui.refactoring.RefactoringSaveHelper;
84 import org.eclipse.jdt.internal.ui.util.BusyIndicatorRunnableContext;
85
86 /**
87  * Operation for exporting a resource and its children to a new JAR file.
88  */

89 public class JarFileExportOperation extends WorkspaceModifyOperation implements IJarExportRunnable {
90
91     private static class MessageMultiStatus extends MultiStatus {
92         MessageMultiStatus(String JavaDoc pluginId, int code, String JavaDoc message, Throwable JavaDoc exception) {
93             super(pluginId, code, message, exception);
94         }
95         /*
96          * allows to change the message
97          */

98         protected void setMessage(String JavaDoc message) {
99             super.setMessage(message);
100         }
101     }
102
103     private JarWriter3 fJarWriter;
104     private JarPackageData fJarPackage;
105     private JarPackageData[] fJarPackages;
106     private Shell fParentShell;
107     private Map JavaDoc fJavaNameToClassFilesMap;
108     private IContainer fClassFilesMapContainer;
109     private Set JavaDoc fExportedClassContainers;
110     private MessageMultiStatus fStatus;
111     private StandardJavaElementContentProvider fJavaElementContentProvider;
112     private boolean fFilesSaved;
113     
114     /**
115      * Creates an instance of this class.
116      *
117      * @param jarPackage the JAR package specification
118      * @param parent the parent for the dialog,
119      * or <code>null</code> if no dialog should be presented
120      */

121     public JarFileExportOperation(JarPackageData jarPackage, Shell parent) {
122         this(new JarPackageData[] {jarPackage}, parent);
123     }
124
125     /**
126      * Creates an instance of this class.
127      *
128      * @param jarPackages an array with JAR package data objects
129      * @param parent the parent for the dialog,
130      * or <code>null</code> if no dialog should be presented
131      */

132     public JarFileExportOperation(JarPackageData[] jarPackages, Shell parent) {
133         this(parent);
134         fJarPackages= jarPackages;
135     }
136
137     private JarFileExportOperation(Shell parent) {
138         fParentShell= parent;
139         fStatus= new MessageMultiStatus(JavaPlugin.getPluginId(), IStatus.OK, "", null); //$NON-NLS-1$
140
fJavaElementContentProvider= new StandardJavaElementContentProvider();
141     }
142
143     private void addToStatus(CoreException ex) {
144         IStatus status= ex.getStatus();
145         String JavaDoc message= ex.getLocalizedMessage();
146         if (message == null || message.length() < 1) {
147             message= JarPackagerMessages.JarFileExportOperation_coreErrorDuringExport;
148             status= new Status(status.getSeverity(), status.getPlugin(), status.getCode(), message, ex);
149         }
150         fStatus.add(status);
151     }
152
153     /**
154      * Adds a new info to the list with the passed information.
155      * Normally the export operation continues after a warning.
156      * @param message the message
157      * @param error the throwable that caused the warning, or <code>null</code>
158      */

159     protected void addInfo(String JavaDoc message, Throwable JavaDoc error) {
160         fStatus.add(new Status(IStatus.INFO, JavaPlugin.getPluginId(), IJavaStatusConstants.INTERNAL_ERROR, message, error));
161     }
162
163     /**
164      * Adds a new warning to the list with the passed information.
165      * Normally the export operation continues after a warning.
166      * @param message the message
167      * @param error the throwable that caused the warning, or <code>null</code>
168      */

169     private void addWarning(String JavaDoc message, Throwable JavaDoc error) {
170         fStatus.add(new Status(IStatus.WARNING, JavaPlugin.getPluginId(), IJavaStatusConstants.INTERNAL_ERROR, message, error));
171     }
172     
173     /**
174      * Adds a new error to the list with the passed information.
175      * Normally an error terminates the export operation.
176      * @param message the message
177      * @param error the throwable that caused the error, or <code>null</code>
178      */

179     private void addError(String JavaDoc message, Throwable JavaDoc error) {
180         fStatus.add(new Status(IStatus.ERROR, JavaPlugin.getPluginId(), IJavaStatusConstants.INTERNAL_ERROR, message, error));
181     }
182
183     /**
184      * Answers the number of file resources specified by the JAR package.
185      *
186      * @return int
187      */

188     private int countSelectedElements() {
189         Set JavaDoc enclosingJavaProjects= new HashSet JavaDoc(10);
190         int count= 0;
191         
192         int n= fJarPackage.getElements().length;
193         for (int i= 0; i < n; i++) {
194             Object JavaDoc element= fJarPackage.getElements()[i];
195             
196             IJavaProject javaProject= getEnclosingJavaProject(element);
197             if (javaProject != null)
198                 enclosingJavaProjects.add(javaProject);
199             
200             IResource resource= null;
201             if (element instanceof IJavaElement) {
202                 IJavaElement je= (IJavaElement)element;
203                 try {
204                     resource= je.getUnderlyingResource();
205                 } catch (JavaModelException ex) {
206                     continue;
207                 }
208                 
209                 // Should not happen since we only export source files
210
if (resource == null)
211                     continue;
212             }
213             else
214                 resource= (IResource)element;
215             if (resource != null) {
216                 if (resource.getType() == IResource.FILE)
217                     count++;
218                 else
219                     count+= getTotalChildCount((IContainer) resource);
220             }
221         }
222         
223         if (fJarPackage.areOutputFoldersExported()) {
224             if (!fJarPackage.areJavaFilesExported())
225                 count= 0;
226             Iterator JavaDoc iter= enclosingJavaProjects.iterator();
227             while (iter.hasNext()) {
228                 IJavaProject javaProject= (IJavaProject)iter.next();
229                 IContainer[] outputContainers;
230                 try {
231                     outputContainers= getOutputContainers(javaProject);
232                 } catch (CoreException ex) {
233                     addToStatus(ex);
234                     continue;
235                 }
236                 for (int i= 0; i < outputContainers.length; i++)
237                     count += getTotalChildCount(outputContainers[i]);
238
239             }
240         }
241         
242         return count;
243     }
244     
245     private int getTotalChildCount(IContainer container) {
246         IResource[] members;
247         try {
248             members= container.members();
249         } catch (CoreException ex) {
250             return 0;
251         }
252         int count= 0;
253         for (int i= 0; i < members.length; i++) {
254             if (members[i].getType() == IResource.FILE)
255                 count++;
256             else
257                 count += getTotalChildCount((IContainer)members[i]);
258         }
259         return count;
260     }
261
262     /**
263      * Exports the passed resource to the JAR file
264      *
265      * @param element the resource or JavaElement to export
266      * @param progressMonitor the progress monitor
267      * @throws InterruptedException thrown on cancel
268      */

269     private void exportElement(Object JavaDoc element, IProgressMonitor progressMonitor) throws InterruptedException JavaDoc {
270         int leadSegmentsToRemove= 1;
271         IPackageFragmentRoot pkgRoot= null;
272         boolean isInJavaProject= false;
273         IResource resource= null;
274         ITypeRoot typeRootElement= null;
275         IJavaProject jProject= null;
276         if (element instanceof IJavaElement) {
277             isInJavaProject= true;
278             IJavaElement je= (IJavaElement)element;
279             if (!(je instanceof ITypeRoot)) {
280                 exportJavaElement(progressMonitor, je);
281                 return;
282             }
283             typeRootElement= (ITypeRoot) je;
284             jProject= typeRootElement.getJavaProject();
285             pkgRoot= JavaModelUtil.getPackageFragmentRoot(je);
286             resource= typeRootElement.getResource();
287         } else
288             resource= (IResource)element;
289
290         if (!resource.isAccessible()) {
291             addWarning(Messages.format(JarPackagerMessages.JarFileExportOperation_resourceNotFound, resource.getFullPath()), null);
292             return;
293         }
294
295         if (resource.getType() == IResource.FILE) {
296             if (!isInJavaProject) {
297                 // check if it's a Java resource
298
try {
299                     isInJavaProject= resource.getProject().hasNature(JavaCore.NATURE_ID);
300                 } catch (CoreException ex) {
301                     addWarning(Messages.format(JarPackagerMessages.JarFileExportOperation_projectNatureNotDeterminable, resource.getFullPath()), ex);
302                     return;
303                 }
304                 if (isInJavaProject) {
305                     jProject= JavaCore.create(resource.getProject());
306                     try {
307                         IPackageFragment pkgFragment= jProject.findPackageFragment(resource.getFullPath().removeLastSegments(1));
308                         if (pkgFragment != null)
309                             pkgRoot= JavaModelUtil.getPackageFragmentRoot(pkgFragment);
310                         else
311                             pkgRoot= findPackageFragmentRoot(jProject, resource.getFullPath().removeLastSegments(1));
312                     } catch (JavaModelException ex) {
313                         addWarning(Messages.format(JarPackagerMessages.JarFileExportOperation_javaPackageNotDeterminable, resource.getFullPath()), ex);
314                         return;
315                     }
316                 }
317             }
318             
319             if (pkgRoot != null && jProject != null) {
320                 leadSegmentsToRemove= pkgRoot.getPath().segmentCount();
321                 boolean isOnBuildPath;
322                 isOnBuildPath= jProject.isOnClasspath(resource);
323                 if (!isOnBuildPath || (mustUseSourceFolderHierarchy() && !pkgRoot.getElementName().equals(IPackageFragmentRoot.DEFAULT_PACKAGEROOT_PATH)))
324                     leadSegmentsToRemove--;
325             }
326             
327             IPath destinationPath= resource.getFullPath().removeFirstSegments(leadSegmentsToRemove);
328             
329             boolean isInOutputFolder= false;
330             if (isInJavaProject && jProject != null) {
331                 try {
332                     isInOutputFolder= jProject.getOutputLocation().isPrefixOf(resource.getFullPath());
333                 } catch (JavaModelException ex) {
334                     isInOutputFolder= false;
335                 }
336             }
337             if (typeRootElement != null) {
338                 exportClassFiles(progressMonitor, typeRootElement, destinationPath);
339             }
340             
341             exportResource(progressMonitor, pkgRoot, isInJavaProject, resource, destinationPath, isInOutputFolder);
342
343             progressMonitor.worked(1);
344             ModalContext.checkCanceled(progressMonitor);
345
346         } else
347             exportContainer(progressMonitor, (IContainer)resource);
348     }
349
350     private void exportJavaElement(IProgressMonitor progressMonitor, IJavaElement je) throws InterruptedException JavaDoc {
351         if (je.getElementType() == IJavaElement.PACKAGE_FRAGMENT_ROOT && ((IPackageFragmentRoot)je).isArchive())
352             return;
353
354         Object JavaDoc[] children= fJavaElementContentProvider.getChildren(je);
355         for (int i= 0; i < children.length; i++)
356             exportElement(children[i], progressMonitor);
357     }
358
359     private void exportResource(IProgressMonitor progressMonitor, IResource resource, int leadingSegmentsToRemove) throws InterruptedException JavaDoc {
360         if (resource instanceof IContainer) {
361             IContainer container= (IContainer)resource;
362             IResource[] children;
363             try {
364                 children= container.members();
365             } catch (CoreException e) {
366                 // this should never happen because an #isAccessible check is done before #members is invoked
367
addWarning(Messages.format(JarPackagerMessages.JarFileExportOperation_errorDuringExport, container.getFullPath()), e);
368                 return;
369             }
370             for (int i= 0; i < children.length; i++)
371                 exportResource(progressMonitor, children[i], leadingSegmentsToRemove);
372         } else if (resource instanceof IFile) {
373             try {
374                 IPath destinationPath= resource.getFullPath().removeFirstSegments(leadingSegmentsToRemove);
375                 progressMonitor.subTask(Messages.format(JarPackagerMessages.JarFileExportOperation_exporting, destinationPath.toString()));
376                 fJarWriter.write((IFile)resource, destinationPath);
377             } catch (CoreException ex) {
378                 Throwable JavaDoc realEx= ex.getStatus().getException();
379                 if (realEx instanceof ZipException JavaDoc && realEx.getMessage() != null && realEx.getMessage().startsWith("duplicate entry:")) //$NON-NLS-1$
380
addWarning(ex.getMessage(), realEx);
381                 else
382                     addToStatus(ex);
383             } finally {
384                 progressMonitor.worked(1);
385                 ModalContext.checkCanceled(progressMonitor);
386             }
387         }
388     }
389
390     private void exportContainer(IProgressMonitor progressMonitor, IContainer container) throws InterruptedException JavaDoc {
391         if (container.getType() == IResource.FOLDER && isOutputFolder((IFolder)container))
392             return;
393         
394         IResource[] children= null;
395         try {
396             children= container.members();
397         } catch (CoreException exception) {
398             // this should never happen because an #isAccessible check is done before #members is invoked
399
addWarning(Messages.format(JarPackagerMessages.JarFileExportOperation_errorDuringExport, container.getFullPath()), exception);
400         }
401         if (children != null) {
402             for (int i= 0; i < children.length; i++)
403                 exportElement(children[i], progressMonitor);
404         }
405     }
406
407     private IPackageFragmentRoot findPackageFragmentRoot(IJavaProject jProject, IPath path) throws JavaModelException {
408         if (jProject == null || path == null || path.segmentCount() <= 0)
409             return null;
410         IPackageFragmentRoot pkgRoot= jProject.findPackageFragmentRoot(path);
411         if (pkgRoot != null)
412             return pkgRoot;
413         else
414             return findPackageFragmentRoot(jProject, path.removeLastSegments(1));
415     }
416
417     private void exportResource(IProgressMonitor progressMonitor, IPackageFragmentRoot pkgRoot, boolean isInJavaProject, IResource resource, IPath destinationPath, boolean isInOutputFolder) {
418
419         // Handle case where META-INF/MANIFEST.MF is part of the exported files
420
if (fJarPackage.areClassFilesExported() && destinationPath.toString().equals("META-INF/MANIFEST.MF")) {//$NON-NLS-1$
421
if (fJarPackage.isManifestGenerated())
422                 addWarning(Messages.format(JarPackagerMessages.JarFileExportOperation_didNotAddManifestToJar, resource.getFullPath()), null);
423             return;
424         }
425
426         boolean isNonJavaResource= !isInJavaProject || pkgRoot == null;
427         boolean isInClassFolder= false;
428         try {
429             isInClassFolder= pkgRoot != null && !pkgRoot.isArchive() && pkgRoot.getKind() == IPackageFragmentRoot.K_BINARY;
430         } catch (JavaModelException ex) {
431             addWarning(Messages.format(JarPackagerMessages.JarFileExportOperation_cantGetRootKind, resource.getFullPath()), ex);
432         }
433         if ((fJarPackage.areClassFilesExported() &&
434                     ((isNonJavaResource || (pkgRoot != null && !isJavaFile(resource) && !isClassFile(resource)))
435                     || isInClassFolder && isClassFile(resource)))
436             || (fJarPackage.areJavaFilesExported() && (isNonJavaResource || (pkgRoot != null && !isClassFile(resource)) || (isInClassFolder && isClassFile(resource) && !fJarPackage.areClassFilesExported())))) {
437             try {
438                 progressMonitor.subTask(Messages.format(JarPackagerMessages.JarFileExportOperation_exporting, destinationPath.toString()));
439                 fJarWriter.write((IFile) resource, destinationPath);
440             } catch (CoreException ex) {
441                 Throwable JavaDoc realEx= ex.getStatus().getException();
442                 if (realEx instanceof ZipException JavaDoc && realEx.getMessage() != null && realEx.getMessage().startsWith("duplicate entry:")) //$NON-NLS-1$
443
addWarning(ex.getMessage(), realEx);
444                 else
445                     addToStatus(ex);
446             }
447         }
448     }
449
450     private boolean isOutputFolder(IFolder folder) {
451         try {
452             IJavaProject javaProject= JavaCore.create(folder.getProject());
453             IPath outputFolderPath= javaProject.getOutputLocation();
454             return folder.getFullPath().equals(outputFolderPath);
455         } catch (JavaModelException ex) {
456             return false;
457         }
458     }
459
460     private void exportClassFiles(IProgressMonitor progressMonitor, ITypeRoot typeRootElement, IPath destinationPath) {
461         if (fJarPackage.areClassFilesExported()) {
462             try {
463                 if (!typeRootElement.exists())
464                     return;
465
466                 // find corresponding file(s) on classpath and export
467
Iterator JavaDoc iter= filesOnClasspath(typeRootElement, destinationPath, progressMonitor);
468                 IPath baseDestinationPath= destinationPath.removeLastSegments(1);
469                 while (iter.hasNext()) {
470                     IFile file= (IFile)iter.next();
471                     IPath classFilePath= baseDestinationPath.append(file.getName());
472                     progressMonitor.subTask(Messages.format(JarPackagerMessages.JarFileExportOperation_exporting, classFilePath.toString()));
473                     fJarWriter.write(file, classFilePath);
474                 }
475             } catch (CoreException ex) {
476                 addToStatus(ex);
477             }
478         }
479     }
480
481     /**
482      * Exports the resources as specified by the JAR package.
483      * @param progressMonitor the progress monitor
484      * @throws InterruptedException thrown when cancelled
485      */

486     private void exportSelectedElements(IProgressMonitor progressMonitor) throws InterruptedException JavaDoc {
487         fExportedClassContainers= new HashSet JavaDoc(10);
488         Set JavaDoc enclosingJavaProjects= new HashSet JavaDoc(10);
489         int n= fJarPackage.getElements().length;
490         for (int i= 0; i < n; i++) {
491             Object JavaDoc element= fJarPackage.getElements()[i];
492             exportElement(element, progressMonitor);
493             if (fJarPackage.areOutputFoldersExported()) {
494                 IJavaProject javaProject= getEnclosingJavaProject(element);
495                 if (javaProject != null)
496                     enclosingJavaProjects.add(javaProject);
497             }
498         }
499         if (fJarPackage.areOutputFoldersExported())
500             exportOutputFolders(progressMonitor, enclosingJavaProjects);
501     }
502     
503     private IJavaProject getEnclosingJavaProject(Object JavaDoc element) {
504         if (element instanceof IJavaElement) {
505             return ((IJavaElement)element).getJavaProject();
506         } else if (element instanceof IResource) {
507             IProject project= ((IResource)element).getProject();
508             try {
509                 if (project.hasNature(JavaCore.NATURE_ID))
510                     return JavaCore.create(project);
511             } catch (CoreException ex) {
512                 addWarning(Messages.format(JarPackagerMessages.JarFileExportOperation_projectNatureNotDeterminable, project.getFullPath()), ex);
513             }
514         }
515         return null;
516     }
517     
518     private void exportOutputFolders(IProgressMonitor progressMonitor, Set JavaDoc javaProjects) throws InterruptedException JavaDoc {
519         if (javaProjects == null)
520             return;
521         
522         Iterator JavaDoc iter= javaProjects.iterator();
523         while (iter.hasNext()) {
524             IJavaProject javaProject= (IJavaProject)iter.next();
525             IContainer[] outputContainers;
526             try {
527                 outputContainers= getOutputContainers(javaProject);
528             } catch (CoreException ex) {
529                 addToStatus(ex);
530                 continue;
531             }
532             for (int i= 0; i < outputContainers.length; i++)
533                 exportResource(progressMonitor, outputContainers[i], outputContainers[i].getFullPath().segmentCount());
534
535         }
536     }
537     
538     private IContainer[] getOutputContainers(IJavaProject javaProject) throws CoreException {
539         Set JavaDoc outputPaths= new HashSet JavaDoc();
540         boolean includeDefaultOutputPath= false;
541         IPackageFragmentRoot[] roots= javaProject.getPackageFragmentRoots();
542         for (int i= 0; i < roots.length; i++) {
543             if (roots[i] != null) {
544                 IClasspathEntry cpEntry= roots[i].getRawClasspathEntry();
545                 if (cpEntry.getEntryKind() == IClasspathEntry.CPE_SOURCE) {
546                     IPath location= cpEntry.getOutputLocation();
547                     if (location != null)
548                         outputPaths.add(location);
549                     else
550                         includeDefaultOutputPath= true;
551                 }
552             }
553         }
554         
555         if (includeDefaultOutputPath) {
556             // Use default output location
557
outputPaths.add(javaProject.getOutputLocation());
558         }
559         
560         // Convert paths to containers
561
Set JavaDoc outputContainers= new HashSet JavaDoc(outputPaths.size());
562         Iterator JavaDoc iter= outputPaths.iterator();
563         while (iter.hasNext()) {
564             IPath path= (IPath)iter.next();
565             if (javaProject.getProject().getFullPath().equals(path))
566                 outputContainers.add(javaProject.getProject());
567             else {
568                 IFolder outputFolder= createFolderHandle(path);
569                 if (outputFolder == null || !outputFolder.isAccessible()) {
570                     String JavaDoc msg= JarPackagerMessages.JarFileExportOperation_outputContainerNotAccessible;
571                     addToStatus(new CoreException(new Status(IStatus.ERROR, JavaPlugin.getPluginId(), IJavaStatusConstants.INTERNAL_ERROR, msg, null)));
572                 } else
573                     outputContainers.add(outputFolder);
574             }
575         }
576         return (IContainer[])outputContainers.toArray(new IContainer[outputContainers.size()]);
577     }
578     
579     /**
580      * Returns an iterator on a list with files that correspond to the
581      * passed file and that are on the classpath of its project.
582      *
583      * @param typeRootElement the class file or compilation unit to evaluate the class files for
584      * @param pathInJar the path that the file has in the JAR (i.e. project and source folder segments removed)
585      * @param progressMonitor the progressMonitor to use
586      * @return the iterator over the corresponding classpath files for the given file
587      * @throws CoreException
588      */

589     private Iterator JavaDoc filesOnClasspath(ITypeRoot typeRootElement, IPath pathInJar, IProgressMonitor progressMonitor) throws CoreException {
590         IFile file= (IFile) typeRootElement.getResource();
591         IJavaProject javaProject= typeRootElement.getJavaProject();
592         IPackageFragmentRoot pkgRoot= JavaModelUtil.getPackageFragmentRoot(typeRootElement);
593         
594         // Allow JAR Package to provide its own strategy
595
IFile[] classFiles= fJarPackage.findClassfilesFor(file);
596         if (classFiles != null)
597             return Arrays.asList(classFiles).iterator();
598
599         if (!isJavaFile(file))
600             return Collections.EMPTY_LIST.iterator();
601
602         IPath outputPath= null;
603         if (pkgRoot != null) {
604             IClasspathEntry cpEntry= pkgRoot.getRawClasspathEntry();
605             if (cpEntry.getEntryKind() == IClasspathEntry.CPE_SOURCE)
606                 outputPath= cpEntry.getOutputLocation();
607         }
608         if (outputPath == null)
609             // Use default output location
610
outputPath= javaProject.getOutputLocation();
611                 
612         IContainer outputContainer;
613         if (javaProject.getProject().getFullPath().equals(outputPath))
614             outputContainer= javaProject.getProject();
615         else {
616             outputContainer= createFolderHandle(outputPath);
617             if (outputContainer == null || !outputContainer.isAccessible()) {
618                 String JavaDoc msg= JarPackagerMessages.JarFileExportOperation_outputContainerNotAccessible;
619                 throw new CoreException(new Status(IStatus.ERROR, JavaPlugin.getPluginId(), IJavaStatusConstants.INTERNAL_ERROR, msg, null));
620             }
621         }
622
623         // Java CU - search files with .class ending
624
boolean hasErrors= hasCompileErrors(file);
625         boolean hasWarnings= hasCompileWarnings(file);
626         boolean canBeExported= canBeExported(hasErrors, hasWarnings);
627         if (!canBeExported)
628             return Collections.EMPTY_LIST.iterator();
629         reportPossibleCompileProblems(file, hasErrors, hasWarnings, canBeExported);
630         IContainer classContainer= outputContainer;
631         if (pathInJar.segmentCount() > 1)
632             classContainer= outputContainer.getFolder(pathInJar.removeLastSegments(1));
633             
634         if (fExportedClassContainers.contains(classContainer))
635             return Collections.EMPTY_LIST.iterator();
636         
637         if (JavaCore.DO_NOT_GENERATE.equals(javaProject.getOption(JavaCore.COMPILER_SOURCE_FILE_ATTR, true))) {
638             IRegion region= JavaCore.newRegion();
639             region.add(typeRootElement);
640             IResource[] generatedResources= JavaCore.getGeneratedResources(region, false);
641             if (generatedResources.length > 0)
642                 return Arrays.asList(generatedResources).iterator();
643             // if no results, give the old code a last chance
644
}
645         if (fClassFilesMapContainer == null || !fClassFilesMapContainer.equals(classContainer)) {
646             fJavaNameToClassFilesMap= buildJavaToClassMap(classContainer, progressMonitor);
647             if (fJavaNameToClassFilesMap == null) {
648                 // Could not fully build map. fallback is to export whole directory
649
String JavaDoc containerName= classContainer.getFullPath().toString();
650                 String JavaDoc msg= Messages.format(JarPackagerMessages.JarFileExportOperation_missingSourceFileAttributeExportedAll, containerName);
651                 addInfo(msg, null);
652                 fExportedClassContainers.add(classContainer);
653                 return getClassesIn(classContainer);
654             }
655             fClassFilesMapContainer= classContainer;
656         }
657         ArrayList JavaDoc classFileList= (ArrayList JavaDoc)fJavaNameToClassFilesMap.get(file.getName());
658         if (classFileList == null || classFileList.isEmpty()) {
659             String JavaDoc msg= Messages.format(JarPackagerMessages.JarFileExportOperation_classFileOnClasspathNotAccessible, file.getFullPath());
660             throw new CoreException(new Status(IStatus.ERROR, JavaPlugin.getPluginId(), IJavaStatusConstants.INTERNAL_ERROR, msg, null));
661         }
662         return classFileList.iterator();
663     }
664
665     private Iterator JavaDoc getClassesIn(IContainer classContainer) throws CoreException {
666         IResource[] resources= classContainer.members();
667         List JavaDoc files= new ArrayList JavaDoc(resources.length);
668         for (int i= 0; i < resources.length; i++)
669             if (resources[i].getType() == IResource.FILE && isClassFile(resources[i]))
670                 files.add(resources[i]);
671         return files.iterator();
672     }
673
674     /**
675      * Answers whether the given resource is a Java file.
676      * The resource must be a file whose file name ends with ".java",
677      * or an extension defined as Java source.
678      *
679      * @param file the file to test
680      * @return a <code>true<code> if the given resource is a Java file
681      */

682     private boolean isJavaFile(IResource file) {
683         return file != null
684             && file.getType() == IResource.FILE
685             && file.getFileExtension() != null
686             && JavaCore.isJavaLikeFileName(file.getName());
687     }
688
689     /**
690      * Answers whether the given resource is a class file.
691      * The resource must be a file whose file name ends with ".class".
692      *
693      * @param file the file to test
694      * @return a <code>true<code> if the given resource is a class file
695      */

696     private boolean isClassFile(IResource file) {
697         return file != null
698             && file.getType() == IResource.FILE
699             && file.getFileExtension() != null
700             && file.getFileExtension().equalsIgnoreCase("class"); //$NON-NLS-1$
701
}
702
703     /*
704      * Builds and returns a map that has the class files
705      * for each java file in a given directory
706      */

707     private Map JavaDoc buildJavaToClassMap(IContainer container, IProgressMonitor monitor) throws CoreException {
708         if (container == null || !container.isAccessible())
709             return new HashMap JavaDoc(0);
710         /*
711          * XXX: Bug 6584: Need a way to get class files for a java file (or CU)
712          */

713         IClassFileReader cfReader= null;
714         IResource[] members= container.members();
715         Map JavaDoc map= new HashMap JavaDoc(members.length);
716         for (int i= 0; i < members.length; i++) {
717             if (isClassFile(members[i])) {
718                 IFile classFile= (IFile)members[i];
719                 URI JavaDoc location= classFile.getLocationURI();
720                 if (location != null) {
721                     InputStream JavaDoc contents= null;
722                     try {
723                         contents= EFS.getStore(location).openInputStream(EFS.NONE, monitor);
724                         cfReader= ToolFactory.createDefaultClassFileReader(contents, IClassFileReader.CLASSFILE_ATTRIBUTES);
725                     } finally {
726                         try {
727                             if (contents != null)
728                                 contents.close();
729                         } catch (IOException JavaDoc e) {
730                             throw new CoreException(new Status(IStatus.ERROR, JavaPlugin.getPluginId(), IStatus.ERROR,
731                                 Messages.format(JarPackagerMessages.JarFileExportOperation_errorCannotCloseConnection, Resources.getLocationString(classFile)),
732                                 e));
733                         }
734                     }
735                     if (cfReader != null) {
736                         ISourceAttribute sourceAttribute= cfReader.getSourceFileAttribute();
737                         if (sourceAttribute == null) {
738                             /*
739                              * Can't fully build the map because one or more
740                              * class file does not contain the name of its
741                              * source file.
742                              */

743                             addWarning(Messages.format(
744                                 JarPackagerMessages.JarFileExportOperation_classFileWithoutSourceFileAttribute,
745                                 Resources.getLocationString(classFile)), null);
746                             return null;
747                         }
748                         String JavaDoc javaName= new String JavaDoc(sourceAttribute.getSourceFileName());
749                         Object JavaDoc classFiles= map.get(javaName);
750                         if (classFiles == null) {
751                             classFiles= new ArrayList JavaDoc(3);
752                             map.put(javaName, classFiles);
753                         }
754                         ((ArrayList JavaDoc)classFiles).add(classFile);
755                     }
756                 }
757             }
758         }
759         return map;
760     }
761
762     /**
763      * Creates a folder resource handle for the folder with the given workspace path.
764      *
765      * @param folderPath the path of the folder to create a handle for
766      * @return the new folder resource handle
767      */

768     private IFolder createFolderHandle(IPath folderPath) {
769         if (folderPath.isValidPath(folderPath.toString()) && folderPath.segmentCount() >= 2)
770             return JavaPlugin.getWorkspace().getRoot().getFolder(folderPath);
771         else
772             return null;
773     }
774
775     /**
776      * Returns the status of this operation.
777      * The result is a status object containing individual
778      * status objects.
779      *
780      * @return the status of this operation
781      */

782     public IStatus getStatus() {
783         String JavaDoc message= null;
784         switch (fStatus.getSeverity()) {
785             case IStatus.OK:
786                 message= ""; //$NON-NLS-1$
787
break;
788             case IStatus.INFO:
789                 message= JarPackagerMessages.JarFileExportOperation_exportFinishedWithInfo;
790                 break;
791             case IStatus.WARNING:
792                 message= JarPackagerMessages.JarFileExportOperation_exportFinishedWithWarnings;
793                 break;
794             case IStatus.ERROR:
795                 if (fJarPackages.length > 1)
796                     message= JarPackagerMessages.JarFileExportOperation_creationOfSomeJARsFailed;
797                 else
798                     message= JarPackagerMessages.JarFileExportOperation_jarCreationFailed;
799                 break;
800             default:
801                 // defensive code in case new severity is defined
802
message= ""; //$NON-NLS-1$
803
break;
804         }
805         fStatus.setMessage(message);
806         return fStatus;
807     }
808
809     private boolean canBeExported(boolean hasErrors, boolean hasWarnings) throws CoreException {
810         return (!hasErrors && !hasWarnings)
811             || (hasErrors && fJarPackage.areErrorsExported())
812             || (hasWarnings && fJarPackage.exportWarnings());
813     }
814
815     private void reportPossibleCompileProblems(IFile file, boolean hasErrors, boolean hasWarnings, boolean canBeExported) {
816         if (hasErrors) {
817             if (canBeExported)
818                 addWarning(Messages.format(JarPackagerMessages.JarFileExportOperation_exportedWithCompileErrors, file.getFullPath()), null);
819             else
820                 addWarning(Messages.format(JarPackagerMessages.JarFileExportOperation_notExportedDueToCompileErrors, file.getFullPath()), null);
821         }
822         if (hasWarnings) {
823             if (canBeExported)
824                 addWarning(Messages.format(JarPackagerMessages.JarFileExportOperation_exportedWithCompileWarnings, file.getFullPath()), null);
825             else
826                 addWarning(Messages.format(JarPackagerMessages.JarFileExportOperation_notExportedDueToCompileWarnings, file.getFullPath()), null);
827         }
828     }
829
830     /**
831      * Exports the resources as specified by the JAR package.
832      *
833      * @param progressMonitor the progress monitor that displays the progress
834      * @throws InvocationTargetException thrown when an ecxeption occurred
835      * @throws InterruptedException thrown when cancelled
836      * @see #getStatus()
837      */

838     protected void execute(IProgressMonitor progressMonitor) throws InvocationTargetException JavaDoc, InterruptedException JavaDoc {
839         int count= fJarPackages.length;
840         progressMonitor.beginTask("", count); //$NON-NLS-1$
841
try {
842             for (int i= 0; i < count; i++) {
843                 SubProgressMonitor subProgressMonitor= new SubProgressMonitor(progressMonitor, 1, SubProgressMonitor.PREPEND_MAIN_LABEL_TO_SUBTASK);
844                 fJarPackage= fJarPackages[i];
845                 if (fJarPackage != null)
846                     singleRun(subProgressMonitor);
847             }
848         } finally {
849             progressMonitor.done();
850         }
851     }
852
853     private void singleRun(IProgressMonitor progressMonitor) throws InvocationTargetException JavaDoc, InterruptedException JavaDoc {
854         try {
855             if (!preconditionsOK())
856                 throw new InvocationTargetException JavaDoc(null, JarPackagerMessages.JarFileExportOperation_jarCreationFailedSeeDetails);
857             int totalWork= countSelectedElements();
858             if (fJarPackage.areGeneratedFilesExported()
859                 && ((!isAutoBuilding() && fJarPackage.isBuildingIfNeeded())
860                     || (isAutoBuilding() && fFilesSaved))) {
861                 int subMonitorTicks= totalWork/10;
862                 totalWork += subMonitorTicks;
863                 progressMonitor.beginTask("", totalWork); //$NON-NLS-1$
864
SubProgressMonitor subProgressMonitor= new SubProgressMonitor(progressMonitor, subMonitorTicks, SubProgressMonitor.PREPEND_MAIN_LABEL_TO_SUBTASK);
865                 buildProjects(subProgressMonitor);
866             } else
867                 progressMonitor.beginTask("", totalWork); //$NON-NLS-1$
868

869             fJarWriter= fJarPackage.createJarWriter3(fParentShell);
870             exportSelectedElements(progressMonitor);
871             if (getStatus().getSeverity() != IStatus.ERROR) {
872                 progressMonitor.subTask(JarPackagerMessages.JarFileExportOperation_savingFiles);
873                 saveFiles();
874             }
875         } catch (CoreException ex) {
876             addToStatus(ex);
877         } finally {
878             try {
879                 if (fJarWriter != null)
880                     fJarWriter.close();
881             } catch (CoreException ex) {
882                 addToStatus(ex);
883             }
884             progressMonitor.done();
885         }
886     }
887     
888     private boolean preconditionsOK() {
889         if (!fJarPackage.areGeneratedFilesExported() && !fJarPackage.areJavaFilesExported()) {
890             addError(JarPackagerMessages.JarFileExportOperation_noExportTypeChosen, null);
891             return false;
892         }
893         if (fJarPackage.getElements() == null || fJarPackage.getElements().length == 0) {
894             addError(JarPackagerMessages.JarFileExportOperation_noResourcesSelected, null);
895             return false;
896         }
897         if (fJarPackage.getAbsoluteJarLocation() == null) {
898             addError(JarPackagerMessages.JarFileExportOperation_invalidJarLocation, null);
899             return false;
900         }
901         File JavaDoc targetFile= fJarPackage.getAbsoluteJarLocation().toFile();
902         if (targetFile.exists() && !targetFile.canWrite()) {
903             addError(JarPackagerMessages.JarFileExportOperation_jarFileExistsAndNotWritable, null);
904             return false;
905         }
906         if (!fJarPackage.isManifestAccessible()) {
907             addError(JarPackagerMessages.JarFileExportOperation_manifestDoesNotExist, null);
908             return false;
909         }
910         if (!fJarPackage.isMainClassValid(new BusyIndicatorRunnableContext())) {
911             addError(JarPackagerMessages.JarFileExportOperation_invalidMainClass, null);
912             return false;
913         }
914         
915         if (fParentShell != null) {
916             final boolean[] res= { false };
917             fParentShell.getDisplay().syncExec(new Runnable JavaDoc() {
918                 public void run() {
919                     RefactoringSaveHelper refactoringSaveHelper= new RefactoringSaveHelper(RefactoringSaveHelper.SAVE_ALL_ALWAYS_ASK);
920                     res[0]= refactoringSaveHelper.saveEditors(fParentShell);
921                     fFilesSaved= refactoringSaveHelper.hasFilesSaved();
922                 }
923             });
924             if (!res[0]) {
925                 addError(JarPackagerMessages.JarFileExportOperation_fileUnsaved, null);
926                 return false;
927             }
928         }
929
930         return true;
931     }
932
933     private void saveFiles() {
934         // Save the manifest
935
if (fJarPackage.areGeneratedFilesExported() && fJarPackage.isManifestGenerated() && fJarPackage.isManifestSaved()) {
936             try {
937                 saveManifest();
938             } catch (CoreException ex) {
939                 addError(JarPackagerMessages.JarFileExportOperation_errorSavingManifest, ex);
940             } catch (IOException JavaDoc ex) {
941                 addError(JarPackagerMessages.JarFileExportOperation_errorSavingManifest, ex);
942             }
943         }
944         
945         // Save the description
946
if (fJarPackage.isDescriptionSaved()) {
947             try {
948                 saveDescription();
949             } catch (CoreException ex) {
950                 addError(JarPackagerMessages.JarFileExportOperation_errorSavingDescription, ex);
951             } catch (IOException JavaDoc ex) {
952                 addError(JarPackagerMessages.JarFileExportOperation_errorSavingDescription, ex);
953             }
954         }
955     }
956
957     private void saveDescription() throws CoreException, IOException JavaDoc {
958         // Adjust JAR package attributes
959
if (fJarPackage.isManifestReused())
960             fJarPackage.setGenerateManifest(false);
961         ByteArrayOutputStream JavaDoc objectStreamOutput= new ByteArrayOutputStream JavaDoc();
962         IFile descriptionFile= fJarPackage.getDescriptionFile();
963         String JavaDoc encoding= "UTF-8"; //$NON-NLS-1$
964
try {
965             encoding= descriptionFile.getCharset(true);
966         } catch (CoreException exception) {
967             JavaPlugin.log(exception);
968         }
969         IJarDescriptionWriter writer= fJarPackage.createJarDescriptionWriter(objectStreamOutput, encoding);
970         ByteArrayInputStream JavaDoc fileInput= null;
971         try {
972             writer.write(fJarPackage);
973             fileInput= new ByteArrayInputStream JavaDoc(objectStreamOutput.toByteArray());
974             if (descriptionFile.isAccessible()) {
975                 if (fJarPackage.allowOverwrite() || JarPackagerUtil.askForOverwritePermission(fParentShell, descriptionFile.getFullPath().toString()))
976                     descriptionFile.setContents(fileInput, true, true, null);
977             } else
978                 descriptionFile.create(fileInput, true, null);
979         } finally {
980             if (fileInput != null)
981                 fileInput.close();
982             if (writer != null)
983                 writer.close();
984         }
985     }
986
987     private void saveManifest() throws CoreException, IOException JavaDoc {
988         ByteArrayOutputStream JavaDoc manifestOutput= new ByteArrayOutputStream JavaDoc();
989         Manifest JavaDoc manifest= fJarPackage.getManifestProvider().create(fJarPackage);
990         manifest.write(manifestOutput);
991         ByteArrayInputStream JavaDoc fileInput= new ByteArrayInputStream JavaDoc(manifestOutput.toByteArray());
992         IFile manifestFile= fJarPackage.getManifestFile();
993         if (manifestFile.isAccessible()) {
994             if (fJarPackage.allowOverwrite() || JarPackagerUtil.askForOverwritePermission(fParentShell, manifestFile.getFullPath().toString()))
995                 manifestFile.setContents(fileInput, true, true, null);
996         } else
997             manifestFile.create(fileInput, true, null);
998     }
999     
1000    private boolean isAutoBuilding() {
1001        return ResourcesPlugin.getWorkspace().getDescription().isAutoBuilding();
1002    }
1003
1004    private void buildProjects(IProgressMonitor progressMonitor) {
1005        Set JavaDoc builtProjects= new HashSet JavaDoc(10);
1006        Object JavaDoc[] elements= fJarPackage.getElements();
1007        for (int i= 0; i < elements.length; i++) {
1008            IProject project= null;
1009            Object JavaDoc element= elements[i];
1010            if (element instanceof IResource)
1011                project= ((IResource)element).getProject();
1012            else if (element instanceof IJavaElement)
1013                project= ((IJavaElement)element).getJavaProject().getProject();
1014            if (project != null && !builtProjects.contains(project)) {
1015                try {
1016                    project.build(IncrementalProjectBuilder.INCREMENTAL_BUILD, progressMonitor);
1017                } catch (CoreException ex) {
1018                    String JavaDoc message= Messages.format(JarPackagerMessages.JarFileExportOperation_errorDuringProjectBuild, project.getFullPath());
1019                    addError(message, ex);
1020                } finally {
1021                    // don't try to build same project a second time even if it failed
1022
builtProjects.add(project);
1023                }
1024            }
1025        }
1026    }
1027
1028    /**
1029     * Tells whether the given resource (or its children) have compile errors.
1030     * The method acts on the current build state and does not recompile.
1031     *
1032     * @param resource the resource to check for errors
1033     * @return <code>true</code> if the resource (and its children) are error free
1034     * @throws CoreException import org.eclipse.core.runtime.CoreException if there's a marker problem
1035     */

1036    private boolean hasCompileErrors(IResource resource) throws CoreException {
1037        IMarker[] problemMarkers= resource.findMarkers(IJavaModelMarker.JAVA_MODEL_PROBLEM_MARKER, true, IResource.DEPTH_INFINITE);
1038        for (int i= 0; i < problemMarkers.length; i++) {
1039            if (problemMarkers[i].getAttribute(IMarker.SEVERITY, -1) == IMarker.SEVERITY_ERROR)
1040                return true;
1041        }
1042        return false;
1043    }
1044
1045    /**
1046     * Tells whether the given resource (or its children) have compile errors.
1047     * The method acts on the current build state and does not recompile.
1048     *
1049     * @param resource the resource to check for errors
1050     * @return <code>true</code> if the resource (and its children) are error free
1051     * @throws CoreException import org.eclipse.core.runtime.CoreException if there's a marker problem
1052     */

1053    private boolean hasCompileWarnings(IResource resource) throws CoreException {
1054        IMarker[] problemMarkers= resource.findMarkers(IJavaModelMarker.JAVA_MODEL_PROBLEM_MARKER, true, IResource.DEPTH_INFINITE);
1055        for (int i= 0; i < problemMarkers.length; i++) {
1056            if (problemMarkers[i].getAttribute(IMarker.SEVERITY, -1) == IMarker.SEVERITY_WARNING)
1057                return true;
1058        }
1059        return false;
1060    }
1061
1062    private boolean mustUseSourceFolderHierarchy() {
1063        return fJarPackage.useSourceFolderHierarchy() && fJarPackage.areJavaFilesExported() && !fJarPackage.areGeneratedFilesExported();
1064    }
1065}
1066
Popular Tags