package jane.tool;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintStream;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.zip.Deflater;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;
import java.util.zip.ZipOutputStream;
import jane.core.Util;
/**
* 对比jar1和jar2两个文件,生成一个新的jar文件,其中包含jar2中有但jar1中没有,以及jar2中有jar1里不同内容但文件路径名相同的文件
* 一般用于给出原版jar和新版jar,生成新版的补丁jar,空目录会被忽略
* 也可用于zip格式
*/
public final class DiffJars
{
private final MessageDigest md5;
public DiffJars() throws NoSuchAlgorithmException
{
md5 = MessageDigest.getInstance("MD5");
}
public byte[] getMd5(byte[] data, int pos, int len)
{
md5.reset();
md5.update(data, pos, len);
return md5.digest();
}
public static void ensurePath(ZipOutputStream zos, HashSet<String> pathes, String path) throws IOException
{
int p = path.lastIndexOf('/');
if(p < 0 || pathes.contains(path.substring(0, p + 1)))
return;
for(p = 0;;)
{
p = path.indexOf('/', p);
if(p < 0) break;
String subPath = path.substring(0, p + 1);
if(!pathes.contains(subPath))
{
pathes.add(subPath);
zos.putNextEntry(new ZipEntry(subPath));
zos.closeEntry();
}
}
}
public int diffJars(InputStream isJar1, InputStream isJar2, OutputStream osJar, PrintStream osLog) throws IOException
{
int count = 0;
HashMap<String, byte[]> jar1Md5s = new HashMap<>();
HashSet<String> pathes = new HashSet<>();
byte[] buf = new byte[0x10000];
try(ZipInputStream zis = new ZipInputStream(new BufferedInputStream(isJar1)))
{
for(ZipEntry ze; (ze = zis.getNextEntry()) != null;)
{
if(ze.isDirectory()) continue;
int len = (int)ze.getSize();
if(len < 0) continue;
if(len > buf.length)
buf = new byte[len];
Util.readStream(zis, ze.getName(), buf, len);
jar1Md5s.put(ze.getName(), getMd5(buf, 0, len));
}
}
try(ZipInputStream zis = new ZipInputStream(new BufferedInputStream(isJar2)))
{
try(ZipOutputStream zos = new ZipOutputStream(new BufferedOutputStream(osJar)))
{
zos.setMethod(ZipOutputStream.DEFLATED);
zos.setLevel(Deflater.BEST_COMPRESSION);
for(ZipEntry ze; (ze = zis.getNextEntry()) != null;)
{
if(ze.isDirectory()) continue;
int len = (int)ze.getSize();
if(len > buf.length)
buf = new byte[len];
String name = ze.getName();
Util.readStream(zis, name, buf, len);
if(Arrays.equals(getMd5(buf, 0, len), jar1Md5s.get(name)))
continue;
if(osLog != null)
osLog.println(name);
ensurePath(zos, pathes, name);
zos.putNextEntry(new ZipEntry(name));
zos.write(buf, 0, len);
zos.closeEntry();
++count;
}
}
}
return count;
}
@SuppressWarnings("resource")
public static void main(String[] args) throws Exception
{
if(args.length < 3)
{
System.err.println("USAGE: java -cp jane-core.jar jane.tool.DiffJars <file1.jar> <file2.jar> <diff.jar>");
return;
}
System.out.println(String.format("%s -> %s = %s ... ", args[0], args[1], args[2]));
int count = new DiffJars().diffJars(new FileInputStream(args[0]), new FileInputStream(args[1]), new FileOutputStream(args[2]), System.out);
System.out.println(String.format("done! (%d files)", count));
}
}