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