emul/mini/src/main/java/org/apidesign/bck2brwsr/emul/lang/ManifestInputStream.java
author Jaroslav Tulach <jaroslav.tulach@apidesign.org>
Thu, 07 Feb 2013 12:58:12 +0100
branchemul
changeset 694 0d277415ed02
permissions -rw-r--r--
Rebasing the Inflater support on jzlib which, unlike GNU ClassPath, has correct implementation of Huffman code. Making the implementation more easily testable by turning Inflater and ZipInputStream into pure delegates. Current implementation is going to need proper long support.
jaroslav@672
     1
/*
jaroslav@672
     2
 * Copyright (c) 1997, 2008, Oracle and/or its affiliates. All rights reserved.
jaroslav@672
     3
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
jaroslav@672
     4
 *
jaroslav@672
     5
 * This code is free software; you can redistribute it and/or modify it
jaroslav@672
     6
 * under the terms of the GNU General Public License version 2 only, as
jaroslav@672
     7
 * published by the Free Software Foundation.  Oracle designates this
jaroslav@672
     8
 * particular file as subject to the "Classpath" exception as provided
jaroslav@672
     9
 * by Oracle in the LICENSE file that accompanied this code.
jaroslav@672
    10
 *
jaroslav@672
    11
 * This code is distributed in the hope that it will be useful, but WITHOUT
jaroslav@672
    12
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
jaroslav@672
    13
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
jaroslav@672
    14
 * version 2 for more details (a copy is included in the LICENSE file that
jaroslav@672
    15
 * accompanied this code).
jaroslav@672
    16
 *
jaroslav@672
    17
 * You should have received a copy of the GNU General Public License version
jaroslav@672
    18
 * 2 along with this work; if not, write to the Free Software Foundation,
jaroslav@672
    19
 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
jaroslav@672
    20
 *
jaroslav@672
    21
 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
jaroslav@672
    22
 * or visit www.oracle.com if you need additional information or have any
jaroslav@672
    23
 * questions.
jaroslav@672
    24
 */
jaroslav@672
    25
package org.apidesign.bck2brwsr.emul.lang;
jaroslav@672
    26
jaroslav@672
    27
import java.io.FilterInputStream;
jaroslav@672
    28
import java.io.IOException;
jaroslav@672
    29
import java.io.InputStream;
jaroslav@672
    30
jaroslav@672
    31
/*
jaroslav@672
    32
 * A fast buffered input stream for parsing manifest files.
jaroslav@672
    33
 * 
jaroslav@672
    34
 * Taken from java.util.jar.Manifest.FastInputStream and modified to be
jaroslav@672
    35
 * independent of other Manifest functionality.
jaroslav@672
    36
 */
jaroslav@672
    37
public abstract class ManifestInputStream extends FilterInputStream {
jaroslav@672
    38
    private byte[] buf;
jaroslav@672
    39
    private int count = 0;
jaroslav@672
    40
    private int pos = 0;
jaroslav@672
    41
jaroslav@672
    42
    protected ManifestInputStream(InputStream in) {
jaroslav@672
    43
        this(in, 8192);
jaroslav@672
    44
    }
jaroslav@672
    45
jaroslav@672
    46
    protected ManifestInputStream(InputStream in, int size) {
jaroslav@672
    47
        super(in);
jaroslav@672
    48
        buf = new byte[size];
jaroslav@672
    49
    }
jaroslav@672
    50
jaroslav@672
    51
    public int read() throws IOException {
jaroslav@672
    52
        if (pos >= count) {
jaroslav@672
    53
            fill();
jaroslav@672
    54
            if (pos >= count) {
jaroslav@672
    55
                return -1;
jaroslav@672
    56
            }
jaroslav@672
    57
        }
jaroslav@672
    58
        return buf[pos++] & 0xff;
jaroslav@672
    59
    }
jaroslav@672
    60
jaroslav@672
    61
    public int read(byte[] b, int off, int len) throws IOException {
jaroslav@672
    62
        int avail = count - pos;
jaroslav@672
    63
        if (avail <= 0) {
jaroslav@672
    64
            if (len >= buf.length) {
jaroslav@672
    65
                return in.read(b, off, len);
jaroslav@672
    66
            }
jaroslav@672
    67
            fill();
jaroslav@672
    68
            avail = count - pos;
jaroslav@672
    69
            if (avail <= 0) {
jaroslav@672
    70
                return -1;
jaroslav@672
    71
            }
jaroslav@672
    72
        }
jaroslav@672
    73
        if (len > avail) {
jaroslav@672
    74
            len = avail;
jaroslav@672
    75
        }
jaroslav@672
    76
        System.arraycopy(buf, pos, b, off, len);
jaroslav@672
    77
        pos += len;
jaroslav@672
    78
        return len;
jaroslav@672
    79
    }
jaroslav@672
    80
jaroslav@672
    81
    /*
jaroslav@672
    82
     * Reads 'len' bytes from the input stream, or until an end-of-line
jaroslav@672
    83
     * is reached. Returns the number of bytes read.
jaroslav@672
    84
     */
jaroslav@672
    85
    public int readLine(byte[] b, int off, int len) throws IOException {
jaroslav@672
    86
        byte[] tbuf = this.buf;
jaroslav@672
    87
        int total = 0;
jaroslav@672
    88
        while (total < len) {
jaroslav@672
    89
            int avail = count - pos;
jaroslav@672
    90
            if (avail <= 0) {
jaroslav@672
    91
                fill();
jaroslav@672
    92
                avail = count - pos;
jaroslav@672
    93
                if (avail <= 0) {
jaroslav@672
    94
                    return -1;
jaroslav@672
    95
                }
jaroslav@672
    96
            }
jaroslav@672
    97
            int n = len - total;
jaroslav@672
    98
            if (n > avail) {
jaroslav@672
    99
                n = avail;
jaroslav@672
   100
            }
jaroslav@672
   101
            int tpos = pos;
jaroslav@672
   102
            int maxpos = tpos + n;
jaroslav@672
   103
            while (tpos < maxpos && tbuf[tpos++] != '\n') {
jaroslav@672
   104
                ;
jaroslav@672
   105
            }
jaroslav@672
   106
            n = tpos - pos;
jaroslav@672
   107
            System.arraycopy(tbuf, pos, b, off, n);
jaroslav@672
   108
            off += n;
jaroslav@672
   109
            total += n;
jaroslav@672
   110
            pos = tpos;
jaroslav@672
   111
            if (tbuf[tpos - 1] == '\n') {
jaroslav@672
   112
                break;
jaroslav@672
   113
            }
jaroslav@672
   114
        }
jaroslav@672
   115
        return total;
jaroslav@672
   116
    }
jaroslav@672
   117
jaroslav@672
   118
    public byte peek() throws IOException {
jaroslav@672
   119
        if (pos == count) {
jaroslav@672
   120
            fill();
jaroslav@672
   121
        }
jaroslav@672
   122
        if (pos == count) {
jaroslav@672
   123
            return -1; // nothing left in buffer
jaroslav@672
   124
        }
jaroslav@672
   125
        return buf[pos];
jaroslav@672
   126
    }
jaroslav@672
   127
jaroslav@672
   128
    public int readLine(byte[] b) throws IOException {
jaroslav@672
   129
        return readLine(b, 0, b.length);
jaroslav@672
   130
    }
jaroslav@672
   131
jaroslav@672
   132
    public long skip(long n) throws IOException {
jaroslav@672
   133
        if (n <= 0) {
jaroslav@672
   134
            return 0;
jaroslav@672
   135
        }
jaroslav@672
   136
        long avail = count - pos;
jaroslav@672
   137
        if (avail <= 0) {
jaroslav@672
   138
            return in.skip(n);
jaroslav@672
   139
        }
jaroslav@672
   140
        if (n > avail) {
jaroslav@672
   141
            n = avail;
jaroslav@672
   142
        }
jaroslav@672
   143
        pos += n;
jaroslav@672
   144
        return n;
jaroslav@672
   145
    }
jaroslav@672
   146
jaroslav@672
   147
    public int available() throws IOException {
jaroslav@672
   148
        return (count - pos) + in.available();
jaroslav@672
   149
    }
jaroslav@672
   150
jaroslav@672
   151
    public void close() throws IOException {
jaroslav@672
   152
        if (in != null) {
jaroslav@672
   153
            in.close();
jaroslav@672
   154
            in = null;
jaroslav@672
   155
            buf = null;
jaroslav@672
   156
        }
jaroslav@672
   157
    }
jaroslav@672
   158
jaroslav@672
   159
    private void fill() throws IOException {
jaroslav@672
   160
        count = pos = 0;
jaroslav@672
   161
        int n = in.read(buf, 0, buf.length);
jaroslav@672
   162
        if (n > 0) {
jaroslav@672
   163
            count = n;
jaroslav@672
   164
        }
jaroslav@672
   165
    }
jaroslav@672
   166
    
jaroslav@672
   167
    protected abstract String putValue(String key, String value);
jaroslav@672
   168
jaroslav@672
   169
    public void readAttributes(byte[] lbuf) throws IOException {
jaroslav@672
   170
        ManifestInputStream is = this;
jaroslav@672
   171
jaroslav@672
   172
        String name = null;
jaroslav@672
   173
        String value = null;
jaroslav@672
   174
        byte[] lastline = null;
jaroslav@672
   175
        int len;
jaroslav@672
   176
        while ((len = is.readLine(lbuf)) != -1) {
jaroslav@672
   177
            boolean lineContinued = false;
jaroslav@672
   178
            if (lbuf[--len] != '\n') {
jaroslav@672
   179
                throw new IOException("line too long");
jaroslav@672
   180
            }
jaroslav@672
   181
            if (len > 0 && lbuf[len - 1] == '\r') {
jaroslav@672
   182
                --len;
jaroslav@672
   183
            }
jaroslav@672
   184
            if (len == 0) {
jaroslav@672
   185
                break;
jaroslav@672
   186
            }
jaroslav@672
   187
            int i = 0;
jaroslav@672
   188
            if (lbuf[0] == ' ') {
jaroslav@672
   189
                if (name == null) {
jaroslav@672
   190
                    throw new IOException("misplaced continuation line");
jaroslav@672
   191
                }
jaroslav@672
   192
                lineContinued = true;
jaroslav@672
   193
                byte[] buf = new byte[lastline.length + len - 1];
jaroslav@672
   194
                System.arraycopy(lastline, 0, buf, 0, lastline.length);
jaroslav@672
   195
                System.arraycopy(lbuf, 1, buf, lastline.length, len - 1);
jaroslav@672
   196
                if (is.peek() == ' ') {
jaroslav@672
   197
                    lastline = buf;
jaroslav@672
   198
                    continue;
jaroslav@672
   199
                }
jaroslav@672
   200
                value = new String(buf, 0, buf.length, "UTF8");
jaroslav@672
   201
                lastline = null;
jaroslav@672
   202
            } else {
jaroslav@672
   203
                while (lbuf[i++] != ':') {
jaroslav@672
   204
                    if (i >= len) {
jaroslav@672
   205
                        throw new IOException("invalid header field");
jaroslav@672
   206
                    }
jaroslav@672
   207
                }
jaroslav@672
   208
                if (lbuf[i++] != ' ') {
jaroslav@672
   209
                    throw new IOException("invalid header field");
jaroslav@672
   210
                }
jaroslav@672
   211
                name = new String(lbuf, 0, 0, i - 2);
jaroslav@672
   212
                if (is.peek() == ' ') {
jaroslav@672
   213
                    lastline = new byte[len - i];
jaroslav@672
   214
                    System.arraycopy(lbuf, i, lastline, 0, len - i);
jaroslav@672
   215
                    continue;
jaroslav@672
   216
                }
jaroslav@672
   217
                value = new String(lbuf, i, len - i, "UTF8");
jaroslav@672
   218
            }
jaroslav@672
   219
            try {
jaroslav@672
   220
                if ((putValue(name, value) != null) && (!lineContinued)) {
jaroslav@672
   221
                    throw new IOException("Duplicate name in Manifest: " + name + ".\n" + "Ensure that the manifest does not " + "have duplicate entries, and\n" + "that blank lines separate " + "individual sections in both your\n" + "manifest and in the META-INF/MANIFEST.MF " + "entry in the jar file.");
jaroslav@672
   222
                }
jaroslav@672
   223
            } catch (IllegalArgumentException e) {
jaroslav@672
   224
                throw new IOException("invalid header field name: " + name);
jaroslav@672
   225
            }
jaroslav@672
   226
        }
jaroslav@672
   227
    }
jaroslav@672
   228
}