KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > eclipse > jdt > ui > jarpackager > JarWriter3


1 /*******************************************************************************
2  * Copyright (c) 2000, 2006 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  *******************************************************************************/

11 package org.eclipse.jdt.ui.jarpackager;
12
13 import java.io.BufferedInputStream JavaDoc;
14 import java.io.BufferedOutputStream JavaDoc;
15 import java.io.File JavaDoc;
16 import java.io.FileInputStream JavaDoc;
17 import java.io.FileNotFoundException JavaDoc;
18 import java.io.FileOutputStream JavaDoc;
19 import java.io.IOException JavaDoc;
20 import java.io.InputStream JavaDoc;
21 import java.io.OutputStream JavaDoc;
22 import java.net.URI JavaDoc;
23 import java.util.ArrayList JavaDoc;
24 import java.util.Arrays JavaDoc;
25 import java.util.Comparator JavaDoc;
26 import java.util.HashSet JavaDoc;
27 import java.util.List JavaDoc;
28 import java.util.Set JavaDoc;
29 import java.util.jar.JarEntry JavaDoc;
30 import java.util.jar.JarOutputStream JavaDoc;
31 import java.util.jar.Manifest JavaDoc;
32 import java.util.zip.ZipEntry JavaDoc;
33
34 import org.eclipse.core.filesystem.EFS;
35 import org.eclipse.core.filesystem.IFileInfo;
36
37 import org.eclipse.core.runtime.Assert;
38 import org.eclipse.core.runtime.CoreException;
39 import org.eclipse.core.runtime.IPath;
40 import org.eclipse.core.runtime.IProgressMonitor;
41 import org.eclipse.core.runtime.NullProgressMonitor;
42 import org.eclipse.core.runtime.OperationCanceledException;
43 import org.eclipse.core.runtime.Path;
44
45 import org.eclipse.core.resources.IContainer;
46 import org.eclipse.core.resources.IFile;
47 import org.eclipse.core.resources.IProject;
48 import org.eclipse.core.resources.IResource;
49 import org.eclipse.core.resources.ResourcesPlugin;
50
51 import org.eclipse.swt.widgets.Shell;
52
53
54 import org.eclipse.ltk.core.refactoring.RefactoringCore;
55 import org.eclipse.ltk.core.refactoring.RefactoringDescriptor;
56 import org.eclipse.ltk.core.refactoring.RefactoringDescriptorProxy;
57
58 import org.eclipse.jdt.internal.corext.util.Messages;
59
60 import org.eclipse.jdt.internal.ui.JavaPlugin;
61 import org.eclipse.jdt.internal.ui.jarpackager.JarPackagerMessages;
62 import org.eclipse.jdt.internal.ui.jarpackager.JarPackagerUtil;
63
64 /**
65  * Creates a JAR file for the given JAR package data.
66  * <p>
67  * Clients may subclass.
68  * </p>
69  *
70  * @see org.eclipse.jdt.ui.jarpackager.JarPackageData
71  * @since 3.2
72  */

73 public class JarWriter3 {
74
75     private Set JavaDoc fDirectories= new HashSet JavaDoc();
76
77     private JarOutputStream JavaDoc fJarOutputStream;
78
79     private JarPackageData fJarPackage;
80
81     /**
82      * Creates an instance which is used to create a JAR based
83      * on the given JarPackage.
84      *
85      * @param jarPackage the JAR specification
86      * @param parent the shell used to display question dialogs,
87      * or <code>null</code> if "false/no/cancel" is the answer
88      * and no dialog should be shown
89      * @throws CoreException to signal any other unusual termination.
90      * This can also be used to return information
91      * in the status object.
92      */

93     public JarWriter3(JarPackageData jarPackage, Shell parent) throws CoreException {
94         Assert.isNotNull(jarPackage, "The JAR specification is null"); //$NON-NLS-1$
95
fJarPackage= jarPackage;
96         Assert.isTrue(fJarPackage.isValid(), "The JAR package specification is invalid"); //$NON-NLS-1$
97
if (!canCreateJar(parent))
98             throw new OperationCanceledException();
99
100         try {
101             if (fJarPackage.usesManifest() && fJarPackage.areGeneratedFilesExported()) {
102                 Manifest JavaDoc manifest= fJarPackage.getManifestProvider().create(fJarPackage);
103                 fJarOutputStream= new JarOutputStream JavaDoc(new FileOutputStream JavaDoc(fJarPackage.getAbsoluteJarLocation().toOSString()), manifest);
104             } else
105                 fJarOutputStream= new JarOutputStream JavaDoc(new FileOutputStream JavaDoc(fJarPackage.getAbsoluteJarLocation().toOSString()));
106             String JavaDoc comment= jarPackage.getComment();
107             if (comment != null)
108                 fJarOutputStream.setComment(comment);
109             if (fJarPackage.isRefactoringAware()) {
110                 Assert.isTrue(fJarPackage.areDirectoryEntriesIncluded());
111                 final IPath metaPath= new Path(JarPackagerUtil.getMetaEntry());
112                 addDirectories(metaPath);
113                 addHistory(fJarPackage, new Path(JarPackagerUtil.getRefactoringsEntry()), new NullProgressMonitor());
114             }
115         } catch (IOException JavaDoc exception) {
116             throw JarPackagerUtil.createCoreException(exception.getLocalizedMessage(), exception);
117         }
118     }
119     
120     /**
121      * Creates the directory entries for the given path and writes it to the
122      * current archive.
123      *
124      * @param destinationPath
125      * the path to add
126      *
127      * @throws IOException
128      * if an I/O error has occurred
129      */

130     protected void addDirectories(IPath destinationPath) throws IOException JavaDoc {
131         String JavaDoc path= destinationPath.toString().replace(File.separatorChar, '/');
132         int lastSlash= path.lastIndexOf('/');
133         List JavaDoc directories= new ArrayList JavaDoc(2);
134         while (lastSlash != -1) {
135             path= path.substring(0, lastSlash + 1);
136             if (!fDirectories.add(path))
137                 break;
138
139             JarEntry JavaDoc newEntry= new JarEntry JavaDoc(path);
140             newEntry.setMethod(ZipEntry.STORED);
141             newEntry.setSize(0);
142             newEntry.setCrc(0);
143             newEntry.setTime(System.currentTimeMillis());
144             directories.add(newEntry);
145
146             lastSlash= path.lastIndexOf('/', lastSlash - 1);
147         }
148
149         for (int i= directories.size() - 1; i >= 0; --i) {
150             fJarOutputStream.putNextEntry((JarEntry JavaDoc) directories.get(i));
151         }
152     }
153     
154     /**
155      * Creates the directory entries for the given path and writes it to the
156      * current archive.
157      *
158      * @param resource
159      * the resource for which the parent directories are to be added
160      * @param destinationPath
161      * the path to add
162      *
163      * @throws IOException
164      * if an I/O error has occurred
165      */

166     protected void addDirectories(IResource resource, IPath destinationPath) throws IOException JavaDoc, CoreException {
167         IContainer parent= null;
168         String JavaDoc path= destinationPath.toString().replace(File.separatorChar, '/');
169         int lastSlash= path.lastIndexOf('/');
170         List JavaDoc directories= new ArrayList JavaDoc(2);
171         while (lastSlash != -1) {
172             path= path.substring(0, lastSlash + 1);
173             if (!fDirectories.add(path))
174                 break;
175
176             parent= resource.getParent();
177             long timeStamp= System.currentTimeMillis();
178             URI JavaDoc location= parent.getLocationURI();
179             if (location != null) {
180                 IFileInfo info= EFS.getStore(location).fetchInfo();
181                 if (info.exists())
182                     timeStamp= info.getLastModified();
183             }
184
185             JarEntry JavaDoc newEntry= new JarEntry JavaDoc(path);
186             newEntry.setMethod(ZipEntry.STORED);
187             newEntry.setSize(0);
188             newEntry.setCrc(0);
189             newEntry.setTime(timeStamp);
190             directories.add(newEntry);
191
192             lastSlash= path.lastIndexOf('/', lastSlash - 1);
193         }
194
195         for (int i= directories.size() - 1; i >= 0; --i) {
196             fJarOutputStream.putNextEntry((JarEntry JavaDoc) directories.get(i));
197         }
198     }
199     
200     /**
201      * Creates a new JarEntry with the passed path and contents, and writes it
202      * to the current archive.
203      *
204      * @param resource the file to write
205      * @param path the path inside the archive
206      *
207      * @throws IOException if an I/O error has occurred
208      * @throws CoreException if the resource can-t be accessed
209      */

210     protected void addFile(IFile resource, IPath path) throws IOException JavaDoc, CoreException {
211         JarEntry JavaDoc newEntry= new JarEntry JavaDoc(path.toString().replace(File.separatorChar, '/'));
212         byte[] readBuffer= new byte[4096];
213
214         if (fJarPackage.isCompressed())
215             newEntry.setMethod(ZipEntry.DEFLATED);
216             // Entry is filled automatically.
217
else {
218             newEntry.setMethod(ZipEntry.STORED);
219             JarPackagerUtil.calculateCrcAndSize(newEntry, resource.getContents(false), readBuffer);
220         }
221
222         long lastModified= System.currentTimeMillis();
223         URI JavaDoc locationURI= resource.getLocationURI();
224         if (locationURI != null) {
225             IFileInfo info= EFS.getStore(locationURI).fetchInfo();
226             if (info.exists())
227                 lastModified= info.getLastModified();
228         }
229         
230         // Set modification time
231
newEntry.setTime(lastModified);
232
233         InputStream JavaDoc contentStream = resource.getContents(false);
234
235         try {
236             fJarOutputStream.putNextEntry(newEntry);
237             int count;
238             while ((count= contentStream.read(readBuffer, 0, readBuffer.length)) != -1)
239                 fJarOutputStream.write(readBuffer, 0, count);
240         } finally {
241             if (contentStream != null)
242                 contentStream.close();
243
244             /*
245              * Commented out because some JREs throw an NPE if a stream
246              * is closed twice. This works because
247              * a) putNextEntry closes the previous entry
248              * b) closing the stream closes the last entry
249              */

250             // fJarOutputStream.closeEntry();
251
}
252     }
253
254     /**
255      * Creates a new JAR file entry containing the refactoring history.
256      *
257      * @param data
258      * the jar package data
259      * @param path
260      * the path of the refactoring history file within the archive
261      * @param monitor
262      * the progress monitor to use
263      * @throws IOException
264      * if no temp file could be written
265      * @throws CoreException
266      * if an error occurs while transforming the refactorings
267      */

268     private void addHistory(final JarPackageData data, final IPath path, final IProgressMonitor monitor) throws IOException JavaDoc, CoreException {
269         Assert.isNotNull(data);
270         Assert.isNotNull(path);
271         Assert.isNotNull(monitor);
272         final RefactoringDescriptorProxy[] proxies= data.getRefactoringDescriptors();
273         Arrays.sort(proxies, new Comparator JavaDoc() {
274
275             public final int compare(final Object JavaDoc first, final Object JavaDoc second) {
276                 final RefactoringDescriptorProxy predecessor= (RefactoringDescriptorProxy) first;
277                 final RefactoringDescriptorProxy successor= (RefactoringDescriptorProxy) second;
278                 final long delta= predecessor.getTimeStamp() - successor.getTimeStamp();
279                 if (delta > 0)
280                     return 1;
281                 else if (delta < 0)
282                     return -1;
283                 return 0;
284             }
285         });
286         File JavaDoc file= null;
287         OutputStream JavaDoc output= null;
288         try {
289             file= File.createTempFile("history", null); //$NON-NLS-1$
290
output= new BufferedOutputStream JavaDoc(new FileOutputStream JavaDoc(file));
291             try {
292                 RefactoringCore.getHistoryService().writeRefactoringDescriptors(proxies, output, RefactoringDescriptor.NONE, false, monitor);
293                 try {
294                     output.close();
295                     output= null;
296                 } catch (IOException JavaDoc exception) {
297                     // Do nothing
298
}
299                 writeMetaData(data, file, path);
300             } finally {
301                 if (output != null) {
302                     try {
303                         output.close();
304                     } catch (IOException JavaDoc exception) {
305                         // Do nothing
306
}
307                 }
308             }
309         } finally {
310             if (file != null)
311                 file.delete();
312         }
313     }
314
315     /**
316      * Checks if the JAR file can be overwritten.
317      * If the JAR package setting does not allow to overwrite the JAR
318      * then a dialog will ask the user again.
319      *
320      * @param parent the parent for the dialog,
321      * or <code>null</code> if no dialog should be presented
322      * @return <code>true</code> if it is OK to create the JAR
323      */

324     protected boolean canCreateJar(Shell parent) {
325         File JavaDoc file= fJarPackage.getAbsoluteJarLocation().toFile();
326         if (file.exists()) {
327             if (!file.canWrite())
328                 return false;
329             if (fJarPackage.allowOverwrite())
330                 return true;
331             return parent != null && JarPackagerUtil.askForOverwritePermission(parent, fJarPackage.getAbsoluteJarLocation().toOSString());
332         }
333                     
334         // Test if directory exists
335
String JavaDoc path= file.getAbsolutePath();
336         int separatorIndex = path.lastIndexOf(File.separator);
337         if (separatorIndex == -1) // i.e.- default directory, which is fine
338
return true;
339         File JavaDoc directory= new File JavaDoc(path.substring(0, separatorIndex));
340         if (!directory.exists()) {
341             if (JarPackagerUtil.askToCreateDirectory(parent, directory))
342                 return directory.mkdirs();
343             else
344                 return false;
345         }
346         return true;
347     }
348
349     /**
350      * Closes the archive and does all required cleanup.
351      *
352      * @throws CoreException
353      * to signal any other unusual termination. This can also be
354      * used to return information in the status object.
355      */

356     public void close() throws CoreException {
357         if (fJarOutputStream != null)
358             try {
359                 fJarOutputStream.close();
360                 registerInWorkspaceIfNeeded();
361             } catch (IOException JavaDoc ex) {
362                 throw JarPackagerUtil.createCoreException(ex.getLocalizedMessage(), ex);
363             }
364     }
365
366     private void registerInWorkspaceIfNeeded() {
367         IPath jarPath= fJarPackage.getAbsoluteJarLocation();
368         IProject[] projects= ResourcesPlugin.getWorkspace().getRoot().getProjects();
369         for (int i= 0; i < projects.length; i++) {
370             IProject project= projects[i];
371             // The Jar is always put into the local file system. So it can only be
372
// part of a project if the project is local as well. So using getLocation
373
// is currently save here.
374
IPath projectLocation= project.getLocation();
375             if (projectLocation != null && projectLocation.isPrefixOf(jarPath)) {
376                 try {
377                     jarPath= jarPath.removeFirstSegments(projectLocation.segmentCount());
378                     jarPath= jarPath.removeLastSegments(1);
379                     IResource containingFolder= project.findMember(jarPath);
380                     if (containingFolder != null && containingFolder.isAccessible())
381                         containingFolder.refreshLocal(IResource.DEPTH_ONE, null);
382                 } catch (CoreException ex) {
383                     // don't refresh the folder but log the problem
384
JavaPlugin.log(ex);
385                 }
386             }
387         }
388     }
389
390     /**
391      * Writes the passed resource to the current archive.
392      *
393      * @param resource
394      * the file to be written
395      * @param destinationPath
396      * the path for the file inside the archive
397      * @throws CoreException
398      * to signal any other unusual termination. This can also be
399      * used to return information in the status object.
400      */

401     public void write(IFile resource, IPath destinationPath) throws CoreException {
402         try {
403             if (fJarPackage.areDirectoryEntriesIncluded())
404                 addDirectories(resource, destinationPath);
405             addFile(resource, destinationPath);
406         } catch (IOException JavaDoc ex) {
407             // Ensure full path is visible
408
String JavaDoc message= null;
409             if (ex.getLocalizedMessage() != null)
410                 message= Messages.format(JarPackagerMessages.JarWriter_writeProblemWithMessage, new Object JavaDoc[] {resource.getFullPath(), ex.getLocalizedMessage()});
411             else
412                 message= Messages.format(JarPackagerMessages.JarWriter_writeProblem, resource.getFullPath());
413             throw JarPackagerUtil.createCoreException(message, ex);
414         }
415     }
416
417     /**
418      * Writes the meta file to the JAR file.
419      *
420      * @param data
421      * the jar package data
422      * @param file
423      * the file containing the meta data
424      * @param path
425      * the path of the meta file within the archive
426      * @throws FileNotFoundException
427      * if the meta file could not be found
428      * @throws IOException
429      * if an input/output error occurs
430      */

431     private void writeMetaData(final JarPackageData data, final File JavaDoc file, final IPath path) throws FileNotFoundException JavaDoc, IOException JavaDoc, CoreException {
432         Assert.isNotNull(data);
433         Assert.isNotNull(file);
434         Assert.isNotNull(path);
435         final JarEntry JavaDoc entry= new JarEntry JavaDoc(path.toString().replace(File.separatorChar, '/'));
436         byte[] buffer= new byte[4096];
437         if (data.isCompressed())
438             entry.setMethod(ZipEntry.DEFLATED);
439         else {
440             entry.setMethod(ZipEntry.STORED);
441             JarPackagerUtil.calculateCrcAndSize(entry, new BufferedInputStream JavaDoc(new FileInputStream JavaDoc(file)), buffer);
442         }
443         entry.setTime(System.currentTimeMillis());
444         final InputStream JavaDoc stream= new BufferedInputStream JavaDoc(new FileInputStream JavaDoc(file));
445         try {
446             fJarOutputStream.putNextEntry(entry);
447             int count;
448             while ((count= stream.read(buffer, 0, buffer.length)) != -1)
449                 fJarOutputStream.write(buffer, 0, count);
450         } finally {
451             try {
452                 stream.close();
453             } catch (IOException JavaDoc exception) {
454                 // Do nothing
455
}
456         }
457     }
458 }
459
Popular Tags