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