1 /*
   2  * Copyright (c) 2009, 2011, Oracle and/or its affiliates. All rights reserved.
   3  *
   4  * Redistribution and use in source and binary forms, with or without
   5  * modification, are permitted provided that the following conditions
   6  * are met:
   7  *
   8  *   - Redistributions of source code must retain the above copyright
   9  *     notice, this list of conditions and the following disclaimer.
  10  *
  11  *   - Redistributions in binary form must reproduce the above copyright
  12  *     notice, this list of conditions and the following disclaimer in the
  13  *     documentation and/or other materials provided with the distribution.
  14  *
  15  *   - Neither the name of Oracle nor the names of its
  16  *     contributors may be used to endorse or promote products derived
  17  *     from this software without specific prior written permission.
  18  *
  19  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
  20  * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
  21  * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
  22  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR
  23  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
  24  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
  25  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
  26  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
  27  * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
  28  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
  29  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  30  */
  31 
  32 /*
  33  * This source code is provided to illustrate the usage of a given feature
  34  * or technique and has been deliberately simplified. Additional steps
  35  * required for a production-quality application, such as security checks,
  36  * input validation and proper error handling, might not be present in
  37  * this sample code.
  38  */
  39 
  40 
  41 package com.sun.nio.zipfs;
  42 
  43 import java.nio.ByteBuffer;
  44 import java.nio.CharBuffer;
  45 import java.nio.charset.Charset;
  46 import java.nio.charset.CharsetDecoder;
  47 import java.nio.charset.CharsetEncoder;
  48 import java.nio.charset.CoderResult;
  49 import java.nio.charset.CodingErrorAction;
  50 import java.util.Arrays;
  51 
  52 /**
  53  * Utility class for zipfile name and comment decoding and encoding
  54  *
  55  * @author  Xueming Shen
  56  */
  57 
  58 final class ZipCoder {
  59 
  60     String toString(byte[] ba, int length) {
  61         CharsetDecoder cd = decoder().reset();
  62         int len = (int)(length * cd.maxCharsPerByte());
  63         char[] ca = new char[len];
  64         if (len == 0)
  65             return new String(ca);
  66         ByteBuffer bb = ByteBuffer.wrap(ba, 0, length);
  67         CharBuffer cb = CharBuffer.wrap(ca);
  68         CoderResult cr = cd.decode(bb, cb, true);
  69         if (!cr.isUnderflow())
  70             throw new IllegalArgumentException(cr.toString());
  71         cr = cd.flush(cb);
  72         if (!cr.isUnderflow())
  73             throw new IllegalArgumentException(cr.toString());
  74         return new String(ca, 0, cb.position());
  75     }
  76 
  77     String toString(byte[] ba) {
  78         return toString(ba, ba.length);
  79     }
  80 
  81     byte[] getBytes(String s) {
  82         CharsetEncoder ce = encoder().reset();
  83         char[] ca = s.toCharArray();
  84         int len = (int)(ca.length * ce.maxBytesPerChar());
  85         byte[] ba = new byte[len];
  86         if (len == 0)
  87             return ba;
  88         ByteBuffer bb = ByteBuffer.wrap(ba);
  89         CharBuffer cb = CharBuffer.wrap(ca);
  90         CoderResult cr = ce.encode(cb, bb, true);
  91         if (!cr.isUnderflow())
  92             throw new IllegalArgumentException(cr.toString());
  93         cr = ce.flush(bb);
  94         if (!cr.isUnderflow())
  95             throw new IllegalArgumentException(cr.toString());
  96         if (bb.position() == ba.length)  // defensive copy?
  97             return ba;
  98         else
  99             return Arrays.copyOf(ba, bb.position());
 100     }
 101 
 102     // assume invoked only if "this" is not utf8
 103     byte[] getBytesUTF8(String s) {
 104         if (isutf8)
 105             return getBytes(s);
 106         if (utf8 == null)
 107             utf8 = new ZipCoder(Charset.forName("UTF-8"));
 108         return utf8.getBytes(s);
 109     }
 110 
 111     String toStringUTF8(byte[] ba, int len) {
 112         if (isutf8)
 113             return toString(ba, len);
 114         if (utf8 == null)
 115             utf8 = new ZipCoder(Charset.forName("UTF-8"));
 116         return utf8.toString(ba, len);
 117     }
 118 
 119     boolean isUTF8() {
 120         return isutf8;
 121     }
 122 
 123     private Charset cs;
 124     private boolean isutf8;
 125     private ZipCoder utf8;
 126 
 127     private ZipCoder(Charset cs) {
 128         this.cs = cs;
 129         this.isutf8 = cs.name().equals("UTF-8");
 130     }
 131 
 132     static ZipCoder get(Charset charset) {
 133         return new ZipCoder(charset);
 134     }
 135 
 136     static ZipCoder get(String csn) {
 137         try {
 138             return new ZipCoder(Charset.forName(csn));
 139         } catch (Throwable t) {
 140             t.printStackTrace();
 141         }
 142         return new ZipCoder(Charset.defaultCharset());
 143     }
 144 
 145     private final ThreadLocal<CharsetDecoder> decTL = new ThreadLocal<>();
 146     private final ThreadLocal<CharsetEncoder> encTL = new ThreadLocal<>();
 147 
 148     private CharsetDecoder decoder() {
 149         CharsetDecoder dec = decTL.get();
 150         if (dec == null) {
 151             dec = cs.newDecoder()
 152               .onMalformedInput(CodingErrorAction.REPORT)
 153               .onUnmappableCharacter(CodingErrorAction.REPORT);
 154             decTL.set(dec);
 155         }
 156         return dec;
 157     }
 158 
 159     private CharsetEncoder encoder() {
 160         CharsetEncoder enc = encTL.get();
 161         if (enc == null) {
 162             enc = cs.newEncoder()
 163               .onMalformedInput(CodingErrorAction.REPORT)
 164               .onUnmappableCharacter(CodingErrorAction.REPORT);
 165             encTL.set(enc);
 166         }
 167         return enc;
 168     }
 169 }