blob: d5f4d16f2bcf6778514e52720a030206d074f244 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2007 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.equinox.internal.p2.artifact.processors.jardelta;
import java.io.*;
import java.util.*;
import java.util.zip.*;
public class DeltaApplier {
private static final String DELETE_SUFFIX = ".delete"; //$NON-NLS-1$
private static final String MANIFEST_ENTRY_NAME = "META-INF/MANIFEST.MF"; //$NON-NLS-1$
private File delta;
private File base;
private File destination;
private ZipFile baseJar;
private ZipFile deltaJar;
private Set baseEntries;
private ZipFile manifestJar;
public DeltaApplier(File base, File delta, File destination) {
this.base = base;
this.delta = delta;
this.destination = destination;
}
public void run() {
try {
if (!openJars())
return;
applyDelta();
writeResult();
} finally {
closeJars();
}
}
private void applyDelta() {
// start out assuming that all the base entries will be moved over.
baseEntries = getEntries(baseJar);
// remove from the base all the entries that appear in the delta
for (Enumeration e = deltaJar.entries(); e.hasMoreElements();) {
ZipEntry entry = ((ZipEntry) e.nextElement());
checkForManifest(entry, deltaJar);
String name = entry.getName();
if (name.endsWith(DELETE_SUFFIX)) {
name = name.substring(0, name.length() - DELETE_SUFFIX.length());
// if the manifest is being deleted, forget anyone who might have a manifest
if (name.equalsIgnoreCase(MANIFEST_ENTRY_NAME))
manifestJar = null;
}
baseEntries.remove(name);
}
}
private void writeResult() {
ZipOutputStream result = null;
try {
try {
result = new ZipOutputStream(new FileOutputStream(destination));
// if the delta includes the manifest, be sure to write it first
if (manifestJar != null)
writeEntry(result, manifestJar.getEntry(MANIFEST_ENTRY_NAME), manifestJar, true);
// write out the things we know are staying from the base JAR
for (Iterator i = baseEntries.iterator(); i.hasNext();) {
ZipEntry entry = baseJar.getEntry((String) i.next());
writeEntry(result, entry, baseJar, false);
}
// write out the changes/additions from the delta.
for (Enumeration e = deltaJar.entries(); e.hasMoreElements();) {
ZipEntry entry = (ZipEntry) e.nextElement();
if (!entry.getName().endsWith(DELETE_SUFFIX))
writeEntry(result, entry, deltaJar, false);
}
} finally {
if (result != null)
result.close();
}
} catch (IOException e) {
e.printStackTrace();
return;
}
}
private void writeEntry(ZipOutputStream result, ZipEntry entry, ZipFile sourceJar, boolean manifest) throws IOException {
if (!manifest && entry.getName().equalsIgnoreCase(MANIFEST_ENTRY_NAME))
return;
// add the entry
result.putNextEntry(entry);
try {
// if there is a sourceJar copy over the content for the entry into the result
if (sourceJar != null) {
InputStream contents = sourceJar.getInputStream(entry);
try {
transferStreams(contents, result);
} finally {
contents.close();
}
}
} finally {
result.closeEntry();
}
}
/**
* Transfers all available bytes from the given input stream to the given
* output stream. Does not close either stream.
*
* @param source
* @param destination
* @throws IOException
*/
public static void transferStreams(InputStream source, OutputStream destination) throws IOException {
source = new BufferedInputStream(source);
destination = new BufferedOutputStream(destination);
try {
byte[] buffer = new byte[8192];
while (true) {
int bytesRead = -1;
if ((bytesRead = source.read(buffer)) == -1)
break;
destination.write(buffer, 0, bytesRead);
}
} finally {
destination.flush();
}
}
private boolean openJars() {
try {
baseJar = new ZipFile(base);
deltaJar = new ZipFile(delta);
} catch (IOException e) {
return false;
}
return true;
}
private void closeJars() {
if (baseJar != null)
try {
baseJar.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
if (deltaJar != null)
try {
deltaJar.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
private Set getEntries(ZipFile jar) {
HashSet result = new HashSet(jar.size());
for (Enumeration e = jar.entries(); e.hasMoreElements();) {
ZipEntry entry = (ZipEntry) e.nextElement();
checkForManifest(entry, jar);
result.add(entry.getName());
}
return result;
}
/**
* Check to see if the given entry is the manifest. If so, remember it for use when writing
* the resultant JAR.
* @param entry
* @param jar
*/
private void checkForManifest(ZipEntry entry, ZipFile jar) {
if (entry.getName().equalsIgnoreCase(MANIFEST_ENTRY_NAME))
manifestJar = jar;
}
}