2 * Licensed to the Apache Software Foundation (ASF) under one or more
\r
3 * contributor license agreements. See the NOTICE file distributed with
\r
4 * this work for additional information regarding copyright ownership.
\r
5 * The ASF licenses this file to You under the Apache License, Version 2.0
\r
6 * (the "License"); you may not use this file except in compliance with
\r
7 * the License. You may obtain a copy of the License at
\r
9 * http://www.apache.org/licenses/LICENSE-2.0
\r
11 * Unless required by applicable law or agreed to in writing, software
\r
12 * distributed under the License is distributed on an "AS IS" BASIS,
\r
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
\r
14 * See the License for the specific language governing permissions and
\r
15 * limitations under the License.
\r
19 package org.apache.tools.zip;
\r
21 import java.nio.ByteBuffer;
\r
22 import java.nio.charset.Charset;
\r
23 import java.nio.charset.UnsupportedCharsetException;
\r
24 import java.util.HashMap;
\r
25 import java.util.Map;
\r
28 * Static helper functions for robustly encoding filenames in zip files.
\r
30 @SuppressWarnings({ "unchecked", "rawtypes" })
\r
31 abstract class ZipEncodingHelper {
\r
34 * A class, which holds the high characters of a simple encoding
\r
35 * and lazily instantiates a Simple8BitZipEncoding instance in a
\r
36 * thread-safe manner.
\r
38 private static class SimpleEncodingHolder {
\r
40 private final char [] highChars;
\r
41 private Simple8BitZipEncoding encoding;
\r
44 * Instantiate a simple encoding holder.
\r
46 * @param highChars The characters for byte codes 128 to 255.
\r
48 * @see Simple8BitZipEncoding#Simple8BitZipEncoding(char[])
\r
50 SimpleEncodingHolder(char [] highChars) {
\r
51 this.highChars = highChars;
\r
55 * @return The associated {@link Simple8BitZipEncoding}, which
\r
56 * is instantiated if not done so far.
\r
58 public synchronized Simple8BitZipEncoding getEncoding() {
\r
59 if (this.encoding == null) {
\r
60 this.encoding = new Simple8BitZipEncoding(this.highChars);
\r
62 return this.encoding;
\r
66 private static final Map simpleEncodings;
\r
69 simpleEncodings = new HashMap();
\r
71 char[] cp437_high_chars =
\r
72 new char[] { 0x00c7, 0x00fc, 0x00e9, 0x00e2, 0x00e4, 0x00e0,
\r
73 0x00e5, 0x00e7, 0x00ea, 0x00eb, 0x00e8, 0x00ef,
\r
74 0x00ee, 0x00ec, 0x00c4, 0x00c5, 0x00c9, 0x00e6,
\r
75 0x00c6, 0x00f4, 0x00f6, 0x00f2, 0x00fb, 0x00f9,
\r
76 0x00ff, 0x00d6, 0x00dc, 0x00a2, 0x00a3, 0x00a5,
\r
77 0x20a7, 0x0192, 0x00e1, 0x00ed, 0x00f3, 0x00fa,
\r
78 0x00f1, 0x00d1, 0x00aa, 0x00ba, 0x00bf, 0x2310,
\r
79 0x00ac, 0x00bd, 0x00bc, 0x00a1, 0x00ab, 0x00bb,
\r
80 0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x2561,
\r
81 0x2562, 0x2556, 0x2555, 0x2563, 0x2551, 0x2557,
\r
82 0x255d, 0x255c, 0x255b, 0x2510, 0x2514, 0x2534,
\r
83 0x252c, 0x251c, 0x2500, 0x253c, 0x255e, 0x255f,
\r
84 0x255a, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550,
\r
85 0x256c, 0x2567, 0x2568, 0x2564, 0x2565, 0x2559,
\r
86 0x2558, 0x2552, 0x2553, 0x256b, 0x256a, 0x2518,
\r
87 0x250c, 0x2588, 0x2584, 0x258c, 0x2590, 0x2580,
\r
88 0x03b1, 0x00df, 0x0393, 0x03c0, 0x03a3, 0x03c3,
\r
89 0x00b5, 0x03c4, 0x03a6, 0x0398, 0x03a9, 0x03b4,
\r
90 0x221e, 0x03c6, 0x03b5, 0x2229, 0x2261, 0x00b1,
\r
91 0x2265, 0x2264, 0x2320, 0x2321, 0x00f7, 0x2248,
\r
92 0x00b0, 0x2219, 0x00b7, 0x221a, 0x207f, 0x00b2,
\r
95 SimpleEncodingHolder cp437 = new SimpleEncodingHolder(cp437_high_chars);
\r
97 simpleEncodings.put("CP437",cp437);
\r
98 simpleEncodings.put("Cp437",cp437);
\r
99 simpleEncodings.put("cp437",cp437);
\r
100 simpleEncodings.put("IBM437",cp437);
\r
101 simpleEncodings.put("ibm437",cp437);
\r
103 char[] cp850_high_chars =
\r
104 new char[] { 0x00c7, 0x00fc, 0x00e9, 0x00e2, 0x00e4, 0x00e0,
\r
105 0x00e5, 0x00e7, 0x00ea, 0x00eb, 0x00e8, 0x00ef,
\r
106 0x00ee, 0x00ec, 0x00c4, 0x00c5, 0x00c9, 0x00e6,
\r
107 0x00c6, 0x00f4, 0x00f6, 0x00f2, 0x00fb, 0x00f9,
\r
108 0x00ff, 0x00d6, 0x00dc, 0x00f8, 0x00a3, 0x00d8,
\r
109 0x00d7, 0x0192, 0x00e1, 0x00ed, 0x00f3, 0x00fa,
\r
110 0x00f1, 0x00d1, 0x00aa, 0x00ba, 0x00bf, 0x00ae,
\r
111 0x00ac, 0x00bd, 0x00bc, 0x00a1, 0x00ab, 0x00bb,
\r
112 0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x00c1,
\r
113 0x00c2, 0x00c0, 0x00a9, 0x2563, 0x2551, 0x2557,
\r
114 0x255d, 0x00a2, 0x00a5, 0x2510, 0x2514, 0x2534,
\r
115 0x252c, 0x251c, 0x2500, 0x253c, 0x00e3, 0x00c3,
\r
116 0x255a, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550,
\r
117 0x256c, 0x00a4, 0x00f0, 0x00d0, 0x00ca, 0x00cb,
\r
118 0x00c8, 0x0131, 0x00cd, 0x00ce, 0x00cf, 0x2518,
\r
119 0x250c, 0x2588, 0x2584, 0x00a6, 0x00cc, 0x2580,
\r
120 0x00d3, 0x00df, 0x00d4, 0x00d2, 0x00f5, 0x00d5,
\r
121 0x00b5, 0x00fe, 0x00de, 0x00da, 0x00db, 0x00d9,
\r
122 0x00fd, 0x00dd, 0x00af, 0x00b4, 0x00ad, 0x00b1,
\r
123 0x2017, 0x00be, 0x00b6, 0x00a7, 0x00f7, 0x00b8,
\r
124 0x00b0, 0x00a8, 0x00b7, 0x00b9, 0x00b3, 0x00b2,
\r
127 SimpleEncodingHolder cp850 = new SimpleEncodingHolder(cp850_high_chars);
\r
129 simpleEncodings.put("CP850",cp850);
\r
130 simpleEncodings.put("Cp850",cp850);
\r
131 simpleEncodings.put("cp850",cp850);
\r
132 simpleEncodings.put("IBM850",cp850);
\r
133 simpleEncodings.put("ibm850",cp850);
\r
137 * Grow a byte buffer, so it has a minimal capacity or at least
\r
138 * the double capacity of the original buffer
\r
140 * @param b The original buffer.
\r
141 * @param newCapacity The minimal requested new capacity.
\r
142 * @return A byte buffer <code>r</code> with
\r
143 * <code>r.capacity() = max(b.capacity()*2,newCapacity)</code> and
\r
144 * all the data contained in <code>b</code> copied to the beginning
\r
145 * of <code>r</code>.
\r
148 static ByteBuffer growBuffer(ByteBuffer b, int newCapacity) {
\r
149 b.limit(b.position());
\r
152 int c2 = b.capacity() * 2;
\r
153 ByteBuffer on = ByteBuffer.allocate(c2 < newCapacity ? newCapacity : c2);
\r
161 * The hexadecimal digits <code>0,...,9,A,...,F</code> encoded as
\r
164 private static final byte[] HEX_DIGITS =
\r
166 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x41,
\r
167 0x42, 0x43, 0x44, 0x45, 0x46
\r
171 * Append <code>%Uxxxx</code> to the given byte buffer.
\r
172 * The caller must assure, that <code>bb.remaining()>=6</code>.
\r
174 * @param bb The byte buffer to write to.
\r
175 * @param c The character to write.
\r
177 static void appendSurrogate(ByteBuffer bb, char c) {
\r
179 bb.put((byte) '%');
\r
180 bb.put((byte) 'U');
\r
182 bb.put(HEX_DIGITS[(c >> 12)&0x0f]);
\r
183 bb.put(HEX_DIGITS[(c >> 8)&0x0f]);
\r
184 bb.put(HEX_DIGITS[(c >> 4)&0x0f]);
\r
185 bb.put(HEX_DIGITS[c & 0x0f]);
\r
190 * name of the encoding UTF-8
\r
192 static final String UTF8 = "UTF8";
\r
195 * variant name of the encoding UTF-8 used for comparisions.
\r
197 private static final String UTF_DASH_8 = "utf-8";
\r
200 * name of the encoding UTF-8
\r
202 static final ZipEncoding UTF8_ZIP_ENCODING = new FallbackZipEncoding(UTF8);
\r
205 * Instantiates a zip encoding.
\r
207 * @param name The name of the zip encoding. Specify <code>null</code> for
\r
208 * the platform's default encoding.
\r
209 * @return A zip encoding for the given encoding name.
\r
211 static ZipEncoding getZipEncoding(String name) {
\r
213 // fallback encoding is good enough for utf-8.
\r
214 if (isUTF8(name)) {
\r
215 return UTF8_ZIP_ENCODING;
\r
218 if (name == null) {
\r
219 return new FallbackZipEncoding();
\r
222 SimpleEncodingHolder h =
\r
223 (SimpleEncodingHolder) simpleEncodings.get(name);
\r
226 return h.getEncoding();
\r
231 Charset cs = Charset.forName(name);
\r
232 return new NioZipEncoding(cs);
\r
234 } catch (UnsupportedCharsetException e) {
\r
235 return new FallbackZipEncoding(name);
\r
240 * Whether a given encoding - or the platform's default encoding
\r
241 * if the parameter is null - is UTF-8.
\r
243 static boolean isUTF8(String encoding) {
\r
244 if (encoding == null) {
\r
245 // check platform's default encoding
\r
246 encoding = System.getProperty("file.encoding");
\r
248 return UTF8.equalsIgnoreCase(encoding)
\r
249 || UTF_DASH_8.equalsIgnoreCase(encoding);
\r