Home | History | Annotate | Download | only in zip
      1 /*
      2  * Copyright (c) 2009, 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 
     26 package java.util.zip;
     27 
     28 import java.nio.ByteBuffer;
     29 import java.nio.CharBuffer;
     30 import java.nio.charset.Charset;
     31 import java.nio.charset.StandardCharsets;
     32 import java.nio.charset.CharsetDecoder;
     33 import java.nio.charset.CharsetEncoder;
     34 import java.nio.charset.CoderResult;
     35 import java.nio.charset.CodingErrorAction;
     36 import java.util.Arrays;
     37 import sun.nio.cs.ArrayDecoder;
     38 import sun.nio.cs.ArrayEncoder;
     39 
     40 /**
     41  * Utility class for zipfile name and comment decoding and encoding
     42  */
     43 
     44 final class ZipCoder {
     45 
     46     String toString(byte[] ba, int length) {
     47         CharsetDecoder cd = decoder().reset();
     48         int len = (int)(length * cd.maxCharsPerByte());
     49         char[] ca = new char[len];
     50         if (len == 0)
     51             return new String(ca);
     52         // UTF-8 only for now. Other ArrayDeocder only handles
     53         // CodingErrorAction.REPLACE mode. ZipCoder uses
     54         // REPORT mode.
     55         if (isUTF8 && cd instanceof ArrayDecoder) {
     56             int clen = ((ArrayDecoder)cd).decode(ba, 0, length, ca);
     57             if (clen == -1)    // malformed
     58                 throw new IllegalArgumentException("MALFORMED");
     59             return new String(ca, 0, clen);
     60         }
     61         ByteBuffer bb = ByteBuffer.wrap(ba, 0, length);
     62         CharBuffer cb = CharBuffer.wrap(ca);
     63         CoderResult cr = cd.decode(bb, cb, true);
     64         if (!cr.isUnderflow())
     65             throw new IllegalArgumentException(cr.toString());
     66         cr = cd.flush(cb);
     67         if (!cr.isUnderflow())
     68             throw new IllegalArgumentException(cr.toString());
     69         return new String(ca, 0, cb.position());
     70     }
     71 
     72     String toString(byte[] ba) {
     73         return toString(ba, ba.length);
     74     }
     75 
     76     byte[] getBytes(String s) {
     77         CharsetEncoder ce = encoder().reset();
     78         char[] ca = s.toCharArray();
     79         int len = (int)(ca.length * ce.maxBytesPerChar());
     80         byte[] ba = new byte[len];
     81         if (len == 0)
     82             return ba;
     83         // UTF-8 only for now. Other ArrayDeocder only handles
     84         // CodingErrorAction.REPLACE mode.
     85         if (isUTF8 && ce instanceof ArrayEncoder) {
     86             int blen = ((ArrayEncoder)ce).encode(ca, 0, ca.length, ba);
     87             if (blen == -1)    // malformed
     88                 throw new IllegalArgumentException("MALFORMED");
     89             return Arrays.copyOf(ba, blen);
     90         }
     91         ByteBuffer bb = ByteBuffer.wrap(ba);
     92         CharBuffer cb = CharBuffer.wrap(ca);
     93         CoderResult cr = ce.encode(cb, bb, true);
     94         if (!cr.isUnderflow())
     95             throw new IllegalArgumentException(cr.toString());
     96         cr = ce.flush(bb);
     97         if (!cr.isUnderflow())
     98             throw new IllegalArgumentException(cr.toString());
     99         if (bb.position() == ba.length)  // defensive copy?
    100             return ba;
    101         else
    102             return Arrays.copyOf(ba, bb.position());
    103     }
    104 
    105     // assume invoked only if "this" is not utf8
    106     byte[] getBytesUTF8(String s) {
    107         if (isUTF8)
    108             return getBytes(s);
    109         if (utf8 == null)
    110             utf8 = new ZipCoder(StandardCharsets.UTF_8);
    111         return utf8.getBytes(s);
    112     }
    113 
    114 
    115     String toStringUTF8(byte[] ba, int len) {
    116         if (isUTF8)
    117             return toString(ba, len);
    118         if (utf8 == null)
    119             utf8 = new ZipCoder(StandardCharsets.UTF_8);
    120         return utf8.toString(ba, len);
    121     }
    122 
    123     boolean isUTF8() {
    124         return isUTF8;
    125     }
    126 
    127     private Charset cs;
    128     private CharsetDecoder dec;
    129     private CharsetEncoder enc;
    130     private boolean isUTF8;
    131     private ZipCoder utf8;
    132 
    133     private ZipCoder(Charset cs) {
    134         this.cs = cs;
    135         this.isUTF8 = cs.name().equals(StandardCharsets.UTF_8.name());
    136     }
    137 
    138     static ZipCoder get(Charset charset) {
    139         return new ZipCoder(charset);
    140     }
    141 
    142     private CharsetDecoder decoder() {
    143         if (dec == null) {
    144             dec = cs.newDecoder()
    145               .onMalformedInput(CodingErrorAction.REPORT)
    146               .onUnmappableCharacter(CodingErrorAction.REPORT);
    147         }
    148         return dec;
    149     }
    150 
    151     private CharsetEncoder encoder() {
    152         if (enc == null) {
    153             enc = cs.newEncoder()
    154               .onMalformedInput(CodingErrorAction.REPORT)
    155               .onUnmappableCharacter(CodingErrorAction.REPORT);
    156         }
    157         return enc;
    158     }
    159 }
    160