blob: 30dbb0300e3d391a03da870197f08629c6f3020f [file] [log] [blame]
/*
* Copyright (C) 2015 The Android Open Source Project
*
* 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 com.android.build.gradle.internal.transforms;
import com.android.annotations.NonNull;
import com.android.build.gradle.internal.LoggerWrapper;
import com.android.builder.signing.SignedJarBuilder;
import com.android.utils.FileUtils;
import com.android.utils.ILogger;
import com.google.common.io.Closer;
import org.gradle.api.logging.Logging;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.jar.JarEntry;
import java.util.jar.JarOutputStream;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;
/**
* Jar Merger class.
*/
public class JarMerger {
private final byte[] buffer = new byte[8192];
@NonNull
private final ILogger logger = new LoggerWrapper(Logging.getLogger(JarMerger.class));
@NonNull
private final File jarFile;
private Closer closer;
private JarOutputStream jarOutputStream;
private SignedJarBuilder.IZipEntryFilter filter;
public JarMerger(@NonNull File jarFile) throws IOException {
this.jarFile = jarFile;
}
private void init() throws IOException {
if (closer == null) {
FileUtils.mkdirs(jarFile.getParentFile());
closer = Closer.create();
FileOutputStream fos = closer.register(new FileOutputStream(jarFile));
jarOutputStream = closer.register(new JarOutputStream(fos));
}
}
/**
* Sets a list of regex to exclude from the jar.
*/
public void setFilter(@NonNull SignedJarBuilder.IZipEntryFilter filter) {
this.filter = filter;
}
public void addFolder(@NonNull File folder) throws IOException {
init();
try {
addFolder(folder, "");
} catch (SignedJarBuilder.IZipEntryFilter.ZipAbortException e) {
throw new IOException(e);
}
}
private void addFolder(@NonNull File folder, @NonNull String path)
throws IOException, SignedJarBuilder.IZipEntryFilter.ZipAbortException {
logger.verbose("addFolder(%1$s, %2$s)", folder, path);
File[] files = folder.listFiles();
if (files != null) {
for (File file : files) {
if (file.isFile()) {
String entryPath = path + file.getName();
if (filter == null || filter.checkEntry(entryPath)) {
logger.verbose("addFolder(%1$s, %2$s): entry %3$s", folder, path, entryPath);
// new entry
jarOutputStream.putNextEntry(new JarEntry(entryPath));
// put the file content
Closer localCloser = Closer.create();
try {
FileInputStream fis = localCloser.register(new FileInputStream(file));
int count;
while ((count = fis.read(buffer)) != -1) {
jarOutputStream.write(buffer, 0, count);
}
} finally {
localCloser.close();
}
// close the entry
jarOutputStream.closeEntry();
}
} else if (file.isDirectory()) {
addFolder(file, path + file.getName() + "/");
}
}
}
}
public void addJar(@NonNull File file) throws IOException {
addJar(file, false);
}
public void addJar(@NonNull File file, boolean removeEntryTimestamp) throws IOException {
logger.verbose("addJar(%1$s)", file);
init();
Closer localCloser = Closer.create();
try {
FileInputStream fis = localCloser.register(new FileInputStream(file));
ZipInputStream zis = localCloser.register(new ZipInputStream(fis));
// loop on the entries of the jar file package and put them in the final jar
ZipEntry entry;
while ((entry = zis.getNextEntry()) != null) {
// do not take directories or anything inside a potential META-INF folder.
if (entry.isDirectory()) {
continue;
}
String name = entry.getName();
if (filter != null && !filter.checkEntry(name)) {
continue;
}
JarEntry newEntry;
// Preserve the STORED method of the input entry.
if (entry.getMethod() == JarEntry.STORED) {
newEntry = new JarEntry(entry);
} else {
// Create a new entry so that the compressed len is recomputed.
newEntry = new JarEntry(name);
}
if (removeEntryTimestamp) {
newEntry.setTime(0);
}
// add the entry to the jar archive
logger.verbose("addJar(%1$s): entry %2$s", file, name);
jarOutputStream.putNextEntry(newEntry);
// read the content of the entry from the input stream, and write it into the archive.
int count;
while ((count = zis.read(buffer)) != -1) {
jarOutputStream.write(buffer, 0, count);
}
// close the entries for this file
jarOutputStream.closeEntry();
zis.closeEntry();
}
} catch (SignedJarBuilder.IZipEntryFilter.ZipAbortException e) {
throw new IOException(e);
} finally {
localCloser.close();
}
}
public void addEntry(@NonNull String path, @NonNull byte[] bytes) throws IOException {
init();
jarOutputStream.putNextEntry(new JarEntry(path));
jarOutputStream.write(bytes);
jarOutputStream.closeEntry();
}
public void close() throws IOException {
if (closer != null) {
closer.close();
}
}
}