001/*
002 *  Licensed to the Apache Software Foundation (ASF) under one or more
003 *  contributor license agreements.  See the NOTICE file distributed with
004 *  this work for additional information regarding copyright ownership.
005 *  The ASF licenses this file to You under the Apache License, Version 2.0
006 *  (the "License"); you may not use this file except in compliance with
007 *  the License.  You may obtain a copy of the License at
008 *
009 *     http://www.apache.org/licenses/LICENSE-2.0
010 *
011 *  Unless required by applicable law or agreed to in writing, software
012 *  distributed under the License is distributed on an "AS IS" BASIS,
013 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 *  See the License for the specific language governing permissions and
015 *  limitations under the License.
016 */
017package org.apache.commons.compress.harmony.unpack200;
018
019import java.io.IOException;
020import java.io.InputStream;
021
022import org.apache.commons.compress.harmony.pack200.Codec;
023import org.apache.commons.compress.harmony.pack200.Pack200Exception;
024import org.apache.commons.compress.utils.IOUtils;
025
026/**
027 * Parses the file band headers (not including the actual bits themselves). At the end of this parse call, the input stream will be positioned at the start of
028 * the file_bits themselves, and there will be Sum(file_size) bits remaining in the stream with BYTE1 compression. A decent implementation will probably just
029 * stream the bytes out to the reconstituted Jar rather than caching them.
030 */
031public class FileBands extends BandSet {
032
033    private byte[][] fileBits;
034
035    private int[] fileModtime;
036
037    private String[] fileName;
038
039    private int[] fileOptions;
040
041    private long[] fileSize;
042
043    private final String[] cpUTF8;
044
045    private InputStream in;
046
047    /**
048     * @param segment TODO
049     */
050    public FileBands(final Segment segment) {
051        super(segment);
052        this.cpUTF8 = segment.getCpBands().getCpUTF8();
053    }
054
055    public byte[][] getFileBits() {
056        return fileBits;
057    }
058
059    public int[] getFileModtime() {
060        return fileModtime;
061    }
062
063    public String[] getFileName() {
064        return fileName;
065    }
066
067    public int[] getFileOptions() {
068        return fileOptions;
069    }
070
071    public long[] getFileSize() {
072        return fileSize;
073    }
074
075    // TODO: stream the file bits directly somehow
076    public void processFileBits() throws IOException {
077        // now read in the bytes
078        final int numberOfFiles = header.getNumberOfFiles();
079        fileBits = new byte[numberOfFiles][];
080        for (int i = 0; i < numberOfFiles; i++) {
081            final int size = (int) fileSize[i];
082            fileBits[i] = IOUtils.readRange(in, size);
083            final int read = fileBits[i].length;
084            if (size != 0 && read < size) {
085                throw new IOException("Expected to read " + size + " bytes but read " + read);
086            }
087        }
088    }
089
090    /*
091     * (non-Javadoc)
092     *
093     * @see org.apache.commons.compress.harmony.unpack200.BandSet#unpack(java.io.InputStream)
094     */
095    @Override
096    public void read(final InputStream in) throws IOException, Pack200Exception {
097        final int numberOfFiles = header.getNumberOfFiles();
098        final SegmentOptions options = header.getOptions();
099
100        fileName = parseReferences("file_name", in, Codec.UNSIGNED5, numberOfFiles, cpUTF8);
101        fileSize = parseFlags("file_size", in, numberOfFiles, Codec.UNSIGNED5, options.hasFileSizeHi());
102        if (options.hasFileModtime()) {
103            fileModtime = decodeBandInt("file_modtime", in, Codec.DELTA5, numberOfFiles);
104        } else {
105            fileModtime = new int[numberOfFiles];
106        }
107        if (options.hasFileOptions()) {
108            fileOptions = decodeBandInt("file_options", in, Codec.UNSIGNED5, numberOfFiles);
109        } else {
110            fileOptions = new int[numberOfFiles];
111        }
112        this.in = in; // store for use by processFileBits(), which is called
113        // later
114    }
115
116    @Override
117    public void unpack() {
118
119    }
120
121}