| // Copyright 2015 The Bazel Authors. All rights reserved. |
| // |
| // 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.google.devtools.build.android.ziputils; |
| |
| import static com.google.devtools.build.android.ziputils.DataDescriptor.EXTLEN; |
| import static com.google.devtools.build.android.ziputils.DataDescriptor.EXTSIZ; |
| import static com.google.devtools.build.android.ziputils.DirectoryEntry.CENFLG; |
| import static com.google.devtools.build.android.ziputils.DirectoryEntry.CENLEN; |
| import static com.google.devtools.build.android.ziputils.DirectoryEntry.CENOFF; |
| import static com.google.devtools.build.android.ziputils.DirectoryEntry.CENSIZ; |
| import static com.google.devtools.build.android.ziputils.DirectoryEntry.CENTIM; |
| import static com.google.devtools.build.android.ziputils.LocalFileHeader.LOCFLG; |
| import static com.google.devtools.build.android.ziputils.LocalFileHeader.LOCLEN; |
| import static com.google.devtools.build.android.ziputils.LocalFileHeader.LOCSIZ; |
| import static com.google.devtools.build.android.ziputils.LocalFileHeader.LOCTIM; |
| |
| import java.io.IOException; |
| import java.nio.ByteBuffer; |
| import java.nio.charset.Charset; |
| import java.util.ArrayList; |
| import java.util.List; |
| |
| /** |
| * Zip file builder for testing, For now it only supports building |
| * valid zip files. |
| */ |
| class ZipFileBuilder { |
| private final List<FileInfo> input; |
| private static final Charset CHARSET = Charset.forName("UTF-8"); |
| private static final byte[] EMPTY = {}; |
| |
| public ZipFileBuilder() { |
| input = new ArrayList<>(); |
| } |
| |
| public ZipFileBuilder add(String filename, String content) { |
| input.add(new FileInfo(filename, content.getBytes(Charset.defaultCharset()))); |
| return this; |
| } |
| |
| public ZipFileBuilder add(FileInfo fileInfo) { |
| input.add(fileInfo); |
| return this; |
| } |
| |
| public void create(String filename) throws IOException { |
| ZipOut out = new ZipOut(FileSystem.fileSystem().getOutputChannel(filename, false), filename); |
| for (FileInfo info : input) { |
| int compressed = info.compressedSize(); |
| int uncompressed = info.uncompressedSize(); |
| int dirCompressed = info.dirCompressedSize(); |
| int dirUncompressed = info.dirUncompressedSize(); |
| short flags = info.flags(); |
| DirectoryEntry entry = DirectoryEntry.allocate(info.name, info.extra, info.comment); |
| out.nextEntry(entry) |
| .set(CENOFF, out.fileOffset()) |
| .set(CENFLG, flags) |
| .set(CENTIM, info.date) |
| .set(CENLEN, dirUncompressed) |
| .set(CENSIZ, dirCompressed); |
| LocalFileHeader header = LocalFileHeader.allocate(info.name, null) |
| .set(LOCFLG, flags) |
| .set(LOCTIM, info.date) |
| .set(LOCLEN, uncompressed) |
| .set(LOCSIZ, compressed); |
| out.write(header); |
| out.write(ByteBuffer.wrap(info.data)); |
| if (flags != 0) { |
| DataDescriptor desc = DataDescriptor.allocate() |
| .set(EXTLEN, dirUncompressed) |
| .set(EXTSIZ, dirCompressed); |
| out.write(desc); |
| } |
| } |
| out.close(); |
| } |
| |
| public static class FileInfo { |
| private final String name; |
| private final short method; |
| private final int date; |
| private final int uncompressed; |
| private final byte[] data; |
| private final byte[] extra; |
| private final String comment; |
| boolean maskSize; |
| |
| static final short STORED = 0; |
| static final short DEFLATED = 8; |
| |
| public FileInfo(String filename, String content) { |
| this(filename, DosTime.EPOCH.time, STORED, 0, |
| (content == null ? EMPTY : content.getBytes(CHARSET)), null, null); |
| } |
| |
| public FileInfo(String filename, byte[] data) { |
| this(filename, DosTime.EPOCH.time, STORED, 0, data, null, null); |
| } |
| |
| public FileInfo(String filename, byte[] data, int uncompressed) { |
| this(filename, DosTime.EPOCH.time, DEFLATED, uncompressed, data, null, null); |
| } |
| |
| public FileInfo(String filename, int dosTime, String content) { |
| this(filename, dosTime, STORED, 0, |
| (content == null ? EMPTY : content.getBytes(CHARSET)), null, null); |
| } |
| |
| public FileInfo(String filename, int dosTime, byte[] data) { |
| this(filename, dosTime, STORED, 0, data, null, null); |
| } |
| |
| public FileInfo(String filename, int dosTime, byte[] data, int uncompressed) { |
| this(filename, dosTime, DEFLATED, uncompressed, data, null, null); |
| } |
| |
| public FileInfo(String filename, int dosTime, short method, int uncompressed, |
| byte[] content, byte[] extra, String comment) { |
| this.name = filename; |
| this.date = dosTime; |
| this.method = method; |
| this.uncompressed = uncompressed; |
| this.data = content; |
| this.extra = extra; |
| this.comment = comment; |
| maskSize = false; |
| } |
| |
| public void setMaskSize(boolean ignore) { |
| maskSize = ignore; |
| } |
| |
| int compressedSize() { |
| return method != 0 && !maskSize ? data.length : 0; |
| } |
| |
| int uncompressedSize() { |
| return method == 0 ? data.length : maskSize ? 0 : uncompressed; |
| } |
| |
| int dirCompressedSize() { |
| return method == 0 ? 0 : data.length; |
| } |
| |
| int dirUncompressedSize() { |
| return method == 0 ? data.length : uncompressed; |
| } |
| |
| short flags() { |
| return method != 0 && uncompressed == 0 ? LocalFileHeader.SIZE_MASKED_FLAG : 0; |
| } |
| } |
| } |