/*
* Copyright (C) 2012 Sony Mobile Communications AB
*
* This file is part of ApkAnalyser.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package util;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Enumeration;
import java.util.List;
import java.util.jar.JarFile;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
import java.util.zip.ZipOutputStream;
public abstract class JarFileModifier
{
static SimpleDateFormat m_dateFormat = new SimpleDateFormat("yyyyMMdd_HHmmss");
ZipFile m_origJar;
String m_destJarName;
public JarFileModifier(ZipFile originalJar)
{
this(originalJar, null);
}
public JarFileModifier(JarFile originalJar)
{
this(originalJar, null);
}
public JarFileModifier(ZipFile originalJar, String destinationJar)
{
if (destinationJar == null || destinationJar.trim().length() == 0)
{
String name = originalJar.getName();
if (name.toLowerCase().endsWith(".apk"))
{
name = name.substring(0, name.length() - 4);
}
String dateString = m_dateFormat.format(new Date());
destinationJar = name + "_" + dateString + ".ap_";
}
m_origJar = originalJar;
m_destJarName = destinationJar;
}
public JarFileModifier(JarFile originalJar, String destinationJar)
{
if (destinationJar == null || destinationJar.trim().length() == 0)
{
String name = originalJar.getName();
if (name.toLowerCase().endsWith(".jar"))
{
name = name.substring(0, name.length() - 4);
}
String dateString = m_dateFormat.format(new Date());
destinationJar = name + "_" + dateString + ".jar";
}
m_origJar = originalJar;
m_destJarName = destinationJar;
}
/**
* Implement to return a list of Strings denoting what entries should be excluded in
* modified jar file
* @return List of Strings with exclude names, or null of no excluded entries
*/
public abstract List<String> excludeEntries();
/**
* Implement to return a list of Strings denoting names of new entries which should
* be included modified jar file. <code>getNewEntry(theName, false)</code> will then
* be called to get the content of the new entry.
* @return List of Strings with new names, or null of no new entries
*/
public abstract List<String> newEntries();
/**
* Implement this to return a inputstream to data to a modified or a new entry.
* @param entryName The name of the new or modified entry
* @param modified True if requested entry is modified, false if requested entry is new.
* @return inputstream to new or modified entry.
*/
public abstract InputStream getNewEntry(String entryName, boolean modified);
public File createModifiedJar() throws IOException {
File f = new File(m_destJarName);
if (f.exists()) {
f.delete();
}
ZipOutputStream zout = null;
FileOutputStream fos = null;
InputStream is = null;
try
{
fos = new FileOutputStream(m_destJarName);
zout = new ZipOutputStream(fos);
zout.setLevel(9);
// Fill in all new entries
List<String> newEntries = newEntries();
for (int i = 0; newEntries != null && i < newEntries.size(); i++) {
String newEntryName = newEntries.get(i);
is = getNewEntry(newEntryName, false);
if (is != null) {
zout.putNextEntry(new ZipEntry(newEntryName));
transfer(is, zout);
zout.closeEntry();
is.close();
}
}
List<String> excludeEntries = excludeEntries();
// Fill in existing and modified, exclude specified
Enumeration<? extends ZipEntry> entries = m_origJar.entries();
while (entries.hasMoreElements()) {
ZipEntry entry = entries.nextElement();
String entryName = entry.getName();
// Exclude entry
if (excludeEntries != null && excludeEntries.contains(entryName)) {
continue;
}
// Try getting a modified entry
is = getNewEntry(entryName, true);
// Otherwise, get original entry
if (is == null) {
is = m_origJar.getInputStream(entry);
}
zout.putNextEntry(entry.getMethod() == ZipEntry.STORED ? new ZipEntry(entry) : new ZipEntry(entryName));
transfer(is, zout);
zout.closeEntry();
is.close();
}
} catch (Throwable t) {
t.printStackTrace();
} finally {
if (is != null) {
try {
is.close();
} catch (Throwable ignore) {
}
}
if (zout != null) {
try {
zout.closeEntry();
} catch (Throwable ignore) {
}
zout.close();
}
if (fos != null) {
try {
fos.close();
} catch (Throwable ignore) {
}
}
}
return f;
}
void transfer(InputStream is, OutputStream os) throws IOException
{
int len;
byte[] buffer = new byte[1024];
synchronized (buffer)
{
while ((len = is.read(buffer)) > 0)
{
os.write(buffer, 0, len);
}
os.flush();
}
}
}