blob: 5d39f30cc49a4292a4af526e697e0d6fb6ee137f [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2000, 2006 IBM Corporation and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* IBM Corporation - initial API and implementation
*******************************************************************************/
package org.eclipse.jdt.ui.jarpackager;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.jar.JarEntry;
import java.util.jar.JarOutputStream;
import java.util.jar.Manifest;
import java.util.zip.CRC32;
import java.util.zip.ZipEntry;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.OperationCanceledException;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.jface.util.Assert;
import org.eclipse.jdt.internal.corext.util.Messages;
import org.eclipse.jdt.internal.ui.JavaPlugin;
import org.eclipse.jdt.internal.ui.jarpackager.JarPackagerMessages;
import org.eclipse.jdt.internal.ui.jarpackager.JarPackagerUtil;
/**
* Creates a JAR file for the given JAR package data.
* <p>
* Clients may subclass.
* </p>
*
* @see org.eclipse.jdt.ui.jarpackager.JarPackageData
* @since 3.1
*
* @deprecated Use JarWriter3 instead which leverages new {@link org.eclipse.core.filesystem.EFS EFS} support
*/
public class JarWriter2 {
private JarOutputStream fJarOutputStream;
private JarPackageData fJarPackage;
private Set fDirectories= new HashSet();
/**
* Creates an instance which is used to create a JAR based
* on the given JarPackage.
*
* @param jarPackage the JAR specification
* @param parent the shell used to display question dialogs,
* or <code>null</code> if "false/no/cancel" is the answer
* and no dialog should be shown
* @throws CoreException to signal any other unusual termination.
* This can also be used to return information
* in the status object.
*/
public JarWriter2(JarPackageData jarPackage, Shell parent) throws CoreException {
Assert.isNotNull(jarPackage, "The JAR specification is null"); //$NON-NLS-1$
fJarPackage= jarPackage;
Assert.isTrue(fJarPackage.isValid(), "The JAR package specification is invalid"); //$NON-NLS-1$
if (!canCreateJar(parent))
throw new OperationCanceledException();
try {
if (fJarPackage.usesManifest() && fJarPackage.areGeneratedFilesExported()) {
Manifest manifest= fJarPackage.getManifestProvider().create(fJarPackage);
fJarOutputStream= new JarOutputStream(new FileOutputStream(fJarPackage.getAbsoluteJarLocation().toOSString()), manifest);
} else
fJarOutputStream= new JarOutputStream(new FileOutputStream(fJarPackage.getAbsoluteJarLocation().toOSString()));
String comment= jarPackage.getComment();
if (comment != null)
fJarOutputStream.setComment(comment);
} catch (IOException ex) {
throw JarPackagerUtil.createCoreException(ex.getLocalizedMessage(), ex);
}
}
/**
* Closes the archive and does all required cleanup.
*
* @throws CoreException to signal any other unusual termination.
* This can also be used to return information
* in the status object.
*/
public void close() throws CoreException {
if (fJarOutputStream != null)
try {
fJarOutputStream.close();
registerInWorkspaceIfNeeded();
} catch (IOException ex) {
throw JarPackagerUtil.createCoreException(ex.getLocalizedMessage(), ex);
}
}
/**
* Writes the passed resource to the current archive.
*
* @param resource the file to be written
* @param destinationPath the path for the file inside the archive
* @throws CoreException to signal any other unusual termination.
* This can also be used to return information
* in the status object.
*/
public void write(IFile resource, IPath destinationPath) throws CoreException {
try {
IPath fileLocation= resource.getLocation();
File file= null;
if (fileLocation != null) {
file= new File(fileLocation.toOSString());
}
if (fJarPackage.areDirectoryEntriesIncluded())
addDirectories(destinationPath, file);
addFile(resource, destinationPath, file);
} catch (IOException ex) {
// Ensure full path is visible
String message= null;
if (ex.getLocalizedMessage() != null)
message= Messages.format(JarPackagerMessages.JarWriter_writeProblemWithMessage, new Object[] {resource.getFullPath(), ex.getLocalizedMessage()});
else
message= Messages.format(JarPackagerMessages.JarWriter_writeProblem, resource.getFullPath());
throw JarPackagerUtil.createCoreException(message, ex);
}
}
/**
* Creates a new JarEntry with the passed path and contents, and writes it
* to the current archive.
*
* @param resource the file to write
* @param path the path inside the archive
* @param correspondingFile the corresponding file in the file system
* or <code>null</code> if it doesn't exist
* @throws IOException if an I/O error has occurred
* @throws CoreException if the resource can-t be accessed
*/
protected void addFile(IFile resource, IPath path, File correspondingFile) throws IOException, CoreException {
JarEntry newEntry= new JarEntry(path.toString().replace(File.separatorChar, '/'));
byte[] readBuffer= new byte[4096];
if (fJarPackage.isCompressed())
newEntry.setMethod(ZipEntry.DEFLATED);
// Entry is filled automatically.
else {
newEntry.setMethod(ZipEntry.STORED);
calculateCrcAndSize(newEntry, resource, readBuffer);
}
long lastModified= correspondingFile != null && correspondingFile.exists() ? correspondingFile.lastModified() : System.currentTimeMillis();
// Set modification time
newEntry.setTime(lastModified);
InputStream contentStream = resource.getContents(false);
try {
fJarOutputStream.putNextEntry(newEntry);
int count;
while ((count= contentStream.read(readBuffer, 0, readBuffer.length)) != -1)
fJarOutputStream.write(readBuffer, 0, count);
} finally {
if (contentStream != null)
contentStream.close();
/*
* Commented out because some JREs throw an NPE if a stream
* is closed twice. This works because
* a) putNextEntry closes the previous entry
* b) closing the stream closes the last entry
*/
// fJarOutputStream.closeEntry();
}
}
/**
* Calculates the CRC and size of the resource and updates the jarEntry.
*
* @param jarEntry the JarEntry to update
* @param resource the file to calculate
* @param readBuffer a shared read buffer to store temporary data
*
* @throws IOException if an I/O error has occurred
* @throws CoreException if the resource can-t be accessed
*/
private void calculateCrcAndSize(JarEntry jarEntry, IFile resource, byte[] readBuffer) throws IOException, CoreException {
InputStream contentStream = resource.getContents(false);
int size = 0;
CRC32 checksumCalculator= new CRC32();
int count;
try {
while ((count= contentStream.read(readBuffer, 0, readBuffer.length)) != -1) {
checksumCalculator.update(readBuffer, 0, count);
size += count;
}
} finally {
if (contentStream != null)
contentStream.close();
}
jarEntry.setSize(size);
jarEntry.setCrc(checksumCalculator.getValue());
}
/**
* Creates the directory entries for the given path and writes it to the
* current archive.
*
* @param destinationPath the path to add
* @param correspondingFile the corresponding file in the file system
* or <code>null</code> if it doesn't exist
* @throws IOException if an I/O error has occurred
*/
protected void addDirectories(IPath destinationPath, File correspondingFile) throws IOException {
String path= destinationPath.toString().replace(File.separatorChar, '/');
int lastSlash= path.lastIndexOf('/');
List directories= new ArrayList(2);
while(lastSlash != -1) {
path= path.substring(0, lastSlash + 1);
if (!fDirectories.add(path))
break;
if (correspondingFile != null)
correspondingFile= correspondingFile.getParentFile();
long timeStamp= correspondingFile != null && correspondingFile.exists()
? correspondingFile.lastModified()
: System.currentTimeMillis();
JarEntry newEntry= new JarEntry(path);
newEntry.setMethod(ZipEntry.STORED);
newEntry.setSize(0);
newEntry.setCrc(0);
newEntry.setTime(timeStamp);
directories.add(newEntry);
lastSlash= path.lastIndexOf('/', lastSlash - 1);
}
for(int i= directories.size() - 1; i >= 0; --i) {
fJarOutputStream.putNextEntry((JarEntry)directories.get(i));
}
}
/**
* Checks if the JAR file can be overwritten.
* If the JAR package setting does not allow to overwrite the JAR
* then a dialog will ask the user again.
*
* @param parent the parent for the dialog,
* or <code>null</code> if no dialog should be presented
* @return <code>true</code> if it is OK to create the JAR
*/
protected boolean canCreateJar(Shell parent) {
File file= fJarPackage.getAbsoluteJarLocation().toFile();
if (file.exists()) {
if (!file.canWrite())
return false;
if (fJarPackage.allowOverwrite())
return true;
return parent != null && JarPackagerUtil.askForOverwritePermission(parent, fJarPackage.getAbsoluteJarLocation().toOSString());
}
// Test if directory exists
String path= file.getAbsolutePath();
int separatorIndex = path.lastIndexOf(File.separator);
if (separatorIndex == -1) // i.e.- default directory, which is fine
return true;
File directory= new File(path.substring(0, separatorIndex));
if (!directory.exists()) {
if (JarPackagerUtil.askToCreateDirectory(parent, directory))
return directory.mkdirs();
else
return false;
}
return true;
}
private void registerInWorkspaceIfNeeded() {
IPath jarPath= fJarPackage.getAbsoluteJarLocation();
IProject[] projects= ResourcesPlugin.getWorkspace().getRoot().getProjects();
for (int i= 0; i < projects.length; i++) {
IProject project= projects[i];
// The Jar is always put into the local file system. So it can only be
// part of a project if the project is local as well. So using getLocation
// is currently save here.
IPath projectLocation= project.getLocation();
if (projectLocation != null && projectLocation.isPrefixOf(jarPath)) {
try {
jarPath= jarPath.removeFirstSegments(projectLocation.segmentCount());
jarPath= jarPath.removeLastSegments(1);
IResource containingFolder= project.findMember(jarPath);
if (containingFolder != null && containingFolder.isAccessible())
containingFolder.refreshLocal(IResource.DEPTH_ONE, null);
} catch (CoreException ex) {
// don't refresh the folder but log the problem
JavaPlugin.log(ex);
}
}
}
}
}