1 package org.apache.turbine.services.mimetype.util;
2
3 /* ====================================================================
4 * The Apache Software License, Version 1.1
5 *
6 * Copyright (c) 2001 The Apache Software Foundation. All rights
7 * reserved.
8 *
9 * Redistribution and use in source and binary forms, with or without
10 * modification, are permitted provided that the following conditions
11 * are met:
12 *
13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 *
16 * 2. Redistributions in binary form must reproduce the above copyright
17 * notice, this list of conditions and the following disclaimer in
18 * the documentation and/or other materials provided with the
19 * distribution.
20 *
21 * 3. The end-user documentation included with the redistribution,
22 * if any, must include the following acknowledgment:
23 * "This product includes software developed by the
24 * Apache Software Foundation (http://www.apache.org/)."
25 * Alternately, this acknowledgment may appear in the software itself,
26 * if and wherever such third-party acknowledgments normally appear.
27 *
28 * 4. The names "Apache" and "Apache Software Foundation" and
29 * "Apache Turbine" must not be used to endorse or promote products
30 * derived from this software without prior written permission. For
31 * written permission, please contact apache@apache.org.
32 *
33 * 5. Products derived from this software may not be called "Apache",
34 * "Apache Turbine", nor may "Apache" appear in their name, without
35 * prior written permission of the Apache Software Foundation.
36 *
37 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
38 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
39 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
40 * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
41 * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
42 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
43 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
44 * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
45 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
46 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
47 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
48 * SUCH DAMAGE.
49 * ====================================================================
50 *
51 * This software consists of voluntary contributions made by many
52 * individuals on behalf of the Apache Software Foundation. For more
53 * information on the Apache Software Foundation, please see
54 * <http://www.apache.org/>.
55 */
56
57 import java.util.Locale;
58 import java.util.Map;
59 import java.util.HashMap;
60 import java.util.Hashtable;
61 import java.util.Properties;
62 import java.io.File;
63 import java.io.InputStream;
64 import java.io.FileInputStream;
65 import java.io.IOException;
66
67 /***
68 * This class maintains a set of mappers defining mappings
69 * between locales and the corresponding charsets. The mappings
70 * are defined as properties between locale and charset names.
71 * The definitions can be listed in property files located in user's
72 * home directory, Java home directory or the current class jar.
73 * In addition, this class maintains static default mappings
74 * and constructors support application specific mappings.
75 *
76 * @author <a href="mailto:ilkka.priha@simsoft.fi">Ilkka Priha</a>
77 * @version $Id: CharSetMap.java,v 1.1.1.1 2001/08/16 05:09:07 jvanzyl Exp $
78 */
79 public class CharSetMap
80 {
81 /***
82 * The default charset when nothing else is applicable.
83 */
84 public static final String DEFAULT_CHARSET = "ISO-8859-1";
85
86 /***
87 * The name for charset mapper resources.
88 */
89 public static final String CHARSET_RESOURCE = "charset.properties";
90
91 /***
92 * Priorities of available mappers.
93 */
94 private static final int MAP_CACHE = 0;
95 private static final int MAP_PROG = 1;
96 private static final int MAP_HOME = 2;
97 private static final int MAP_SYS = 3;
98 private static final int MAP_JAR = 4;
99 private static final int MAP_COM = 5;
100
101 /***
102 * A common charset mapper for languages.
103 */
104 private static HashMap commonMapper = new HashMap();
105 static
106 {
107 commonMapper.put("ar","ISO-8859-6");
108 commonMapper.put("be","ISO-8859-5");
109 commonMapper.put("bg","ISO-8859-5");
110 commonMapper.put("ca","ISO-8859-1");
111 commonMapper.put("cs","ISO-8859-2");
112 commonMapper.put("da","ISO-8859-1");
113 commonMapper.put("de","ISO-8859-1");
114 commonMapper.put("el","ISO-8859-7");
115 commonMapper.put("en","ISO-8859-1");
116 commonMapper.put("es","ISO-8859-1");
117 commonMapper.put("et","ISO-8859-1");
118 commonMapper.put("fi","ISO-8859-1");
119 commonMapper.put("fr","ISO-8859-1");
120 commonMapper.put("hr","ISO-8859-2");
121 commonMapper.put("hu","ISO-8859-2");
122 commonMapper.put("is","ISO-8859-1");
123 commonMapper.put("it","ISO-8859-1");
124 commonMapper.put("iw","ISO-8859-8");
125 commonMapper.put("ja","Shift_JIS");
126 commonMapper.put("ko","EUC-KR");
127 commonMapper.put("lt","ISO-8859-2");
128 commonMapper.put("lv","ISO-8859-2");
129 commonMapper.put("mk","ISO-8859-5");
130 commonMapper.put("nl","ISO-8859-1");
131 commonMapper.put("no","ISO-8859-1");
132 commonMapper.put("pl","ISO-8859-2");
133 commonMapper.put("pt","ISO-8859-1");
134 commonMapper.put("ro","ISO-8859-2");
135 commonMapper.put("ru","ISO-8859-5");
136 commonMapper.put("sh","ISO-8859-5");
137 commonMapper.put("sk","ISO-8859-2");
138 commonMapper.put("sl","ISO-8859-2");
139 commonMapper.put("sq","ISO-8859-2");
140 commonMapper.put("sr","ISO-8859-5");
141 commonMapper.put("sv","ISO-8859-1");
142 commonMapper.put("tr","ISO-8859-9");
143 commonMapper.put("uk","ISO-8859-5");
144 commonMapper.put("zh","GB2312");
145 commonMapper.put("zh_TW","Big5");
146 }
147
148 /***
149 * An array of available charset mappers.
150 */
151 private Map mappers[] = new Map[6];
152
153 /***
154 * Loads mappings from a stream.
155 *
156 * @param input an input stream.
157 * @return the mappings.
158 * @throws IOException for an incorrect stream.
159 */
160 protected static Map loadStream(InputStream input)
161 throws IOException
162 {
163 Properties props = new Properties();
164 props.load(input);
165 return new HashMap(props);
166 }
167
168 /***
169 * Loads mappings from a file.
170 *
171 * @param file a file.
172 * @return the mappings.
173 * @throws IOException for an incorrect file.
174 */
175 protected static Map loadFile(File file)
176 throws IOException
177 {
178 return loadStream(new FileInputStream(file));
179 }
180
181 /***
182 * Loads mappings from a file path.
183 *
184 * @param path a file path.
185 * @return the mappings.
186 * @throws IOException for an incorrect file.
187 */
188 protected static Map loadPath(String path)
189 throws IOException
190 {
191 return loadFile(new File(path));
192 }
193
194 /***
195 * Loads mappings from a resource.
196 *
197 * @param name a resource name.
198 * @return the mappings.
199 */
200 protected static Map loadResource(String name)
201 {
202 InputStream input = CharSetMap.class.getResourceAsStream(name);
203 if (input != null)
204 {
205 try
206 {
207 return loadStream(input);
208 }
209 catch (IOException x)
210 {
211 return null;
212 }
213 }
214 else
215 {
216 return null;
217 }
218 }
219
220 /***
221 * Constructs a new charset map with default mappers.
222 */
223 public CharSetMap()
224 {
225 String path;
226 try
227 {
228 // Check whether the user directory contains mappings.
229 path = System.getProperty("user.home");
230 if (path != null)
231 {
232 path = path + File.separator + CHARSET_RESOURCE;
233 mappers[MAP_HOME] = loadPath(path);
234 }
235 }
236 catch (Exception x)
237 {
238 }
239
240 try
241 {
242 // Check whether the system directory contains mappings.
243 path = System.getProperty("java.home") +
244 File.separator + "lib" + File.separator + CHARSET_RESOURCE;
245 mappers[MAP_SYS] = loadPath(path);
246 }
247 catch (Exception x)
248 {
249 }
250
251 // Check whether the current class jar contains mappings.
252 mappers[MAP_JAR] = loadResource("/META-INF/" + CHARSET_RESOURCE);
253
254 // Set the common mapper to have the lowest priority.
255 mappers[MAP_COM] = commonMapper;
256
257 // Set the cache mapper to have the highest priority.
258 mappers[MAP_CACHE] = new Hashtable();
259 }
260
261 /***
262 * Contructs a charset map from properties.
263 *
264 * @param props charset mapping propeties.
265 */
266 public CharSetMap(Properties props)
267 {
268 this();
269 mappers[MAP_PROG] = new HashMap(props);
270 }
271
272 /***
273 * Contructs a charset map read from a stream.
274 *
275 * @param input an input stream.
276 * @throws IOException for an incorrect stream.
277 */
278 public CharSetMap(InputStream input)
279 throws IOException
280 {
281 this();
282 mappers[MAP_PROG] = loadStream(input);
283 }
284
285 /***
286 * Contructs a charset map read from a property file.
287 *
288 * @param file a property file.
289 * @throws IOException for an incorrect property file.
290 */
291 public CharSetMap(File file)
292 throws IOException
293 {
294 this();
295 mappers[MAP_PROG] = loadFile(file);
296 }
297
298 /***
299 * Contructs a charset map read from a property file path.
300 *
301 * @param path a property file path.
302 * @throws IOException for an incorrect property file.
303 */
304 public CharSetMap(String path)
305 throws IOException
306 {
307 this();
308 mappers[MAP_PROG] = loadPath(path);
309 }
310
311 /***
312 * Sets a locale-charset mapping.
313 *
314 * @param key the key for the charset.
315 * @param charset the corresponding charset.
316 */
317 public synchronized void setCharSet(String key,
318 String charset)
319 {
320 HashMap mapper = (HashMap) mappers[MAP_PROG];
321 mapper = mapper != null ?
322 (HashMap) mapper.clone() : new HashMap();
323 mapper.put(key,charset);
324 mappers[MAP_PROG] = mapper;
325 mappers[MAP_CACHE].clear();
326 }
327
328 /***
329 * Gets the charset for a locale. First a locale specific charset
330 * is searched for, then a country specific one and lastly a language
331 * specific one. If none is found, the default charset is returned.
332 *
333 * @param locale the locale.
334 * @return the charset.
335 */
336 public String getCharSet(Locale locale)
337 {
338 // Check the cache first.
339 String key = locale.toString();
340 if (key.length() == 0)
341 {
342 key = "__" + locale.getVariant();
343 if (key.length() == 2)
344 {
345 return DEFAULT_CHARSET;
346 }
347 }
348 String charset = searchCharSet(key);
349 if (charset.length() == 0)
350 {
351 // Not found, perform a full search and update the cache.
352 String[] items = new String[3];
353 items[2] = locale.getVariant();
354 items[1] = locale.getCountry();
355 items[0] = locale.getLanguage();
356 charset = searchCharSet(items);
357 if (charset.length() == 0)
358 {
359 charset = DEFAULT_CHARSET;
360 }
361 mappers[MAP_CACHE].put(key,charset);
362 }
363 return charset;
364 }
365
366 /***
367 * Gets the charset for a locale with a variant. The search
368 * is performed in the following order:
369 * "lang"_"country"_"variant"="charset",
370 * _"counry"_"variant"="charset",
371 * "lang"__"variant"="charset",
372 * __"variant"="charset",
373 * "lang"_"country"="charset",
374 * _"country"="charset",
375 * "lang"="charset".
376 * If nothing of the above is found, the default charset is returned.
377 *
378 * @param locale the locale.
379 * @param variant a variant field.
380 * @return the charset.
381 */
382 public String getCharSet(Locale locale,
383 String variant)
384 {
385 // Check the cache first.
386 if ((variant != null) &&
387 (variant.length() > 0))
388 {
389 String key = locale.toString();
390 if (key.length() == 0)
391 {
392 key = "__" + locale.getVariant();
393 if (key.length() > 2)
394 {
395 key += '_' + variant;
396 }
397 else
398 {
399 key += variant;
400 }
401 }
402 else if (locale.getCountry().length() == 0)
403 {
404 key += "__" + variant;
405 }
406 else
407 {
408 key += '_' + variant;
409 }
410 String charset = searchCharSet(key);
411 if (charset.length() == 0)
412 {
413 // Not found, perform a full search and update the cache.
414 String[] items = new String[4];
415 items[3] = variant;
416 items[2] = locale.getVariant();
417 items[1] = locale.getCountry();
418 items[0] = locale.getLanguage();
419 charset = searchCharSet(items);
420 if (charset.length() == 0)
421 {
422 charset = DEFAULT_CHARSET;
423 }
424 mappers[MAP_CACHE].put(key,charset);
425 }
426 return charset;
427 }
428 else
429 {
430 return getCharSet(locale);
431 }
432 }
433
434 /***
435 * Gets the charset for a specified key.
436 *
437 * @param key the key for the charset.
438 * @return the found charset or the default one.
439 */
440 public String getCharSet(String key)
441 {
442 String charset = searchCharSet(key);
443 return charset.length() > 0 ? charset : DEFAULT_CHARSET;
444 }
445
446 /***
447 * Gets the charset for a specified key.
448 *
449 * @param key the key for the charset.
450 * @param def the default charset if none is found.
451 * @return the found charset or the given default.
452 */
453 public String getCharSet(String key,
454 String def)
455 {
456 String charset = searchCharSet(key);
457 return charset.length() > 0 ? charset : def;
458 }
459
460 /***
461 * Searches for a charset for a specified locale.
462 *
463 * @param items an array of locale items.
464 * @return the found charset or an empty string.
465 */
466 private String searchCharSet(String[] items)
467 {
468 String charset;
469 StringBuffer sb = new StringBuffer();
470 for (int i = items.length; i > 0; i--)
471 {
472 charset = searchCharSet(items,sb,i);
473 if (charset.length() > 0)
474 {
475 return charset;
476 }
477 sb.setLength(0);
478 }
479 return "";
480 }
481
482 /***
483 * Searches recursively for a charset for a specified locale.
484 *
485 * @param items an array of locale items.
486 * @param base a buffer of base items.
487 * @param count the number of items to go through.
488 * @return the found charset or an empty string.
489 */
490 private String searchCharSet(String[] items,
491 StringBuffer base,
492 int count)
493 {
494 if ((--count >= 0) &&
495 (items[count] != null) &&
496 (items[count].length() > 0))
497 {
498 String charset;
499 base.insert(0,items[count]);
500 int length = base.length();
501 for (int i = count; i > 0; i--)
502 {
503 if ((i == count) ||
504 (i <= 1))
505 {
506 base.insert(0,'_');
507 length++;
508 }
509 charset = searchCharSet(items,base,i);
510 if (charset.length() > 0)
511 {
512 return charset;
513 }
514 base.delete(0,base.length() - length);
515 }
516 return searchCharSet(base.toString());
517 }
518 else
519 {
520 return "";
521 }
522 }
523
524 /***
525 * Searches for a charset for a specified key.
526 *
527 * @param key the key for the charset.
528 * @return the found charset or an empty string.
529 */
530 private String searchCharSet(String key)
531 {
532 if ((key != null) &&
533 (key.length() > 0))
534 {
535 // Go through mappers.
536 Map mapper;
537 String charset;
538 for (int i = 0; i < mappers.length; i++)
539 {
540 mapper = mappers[i];
541 if (mapper != null)
542 {
543 charset = (String) mapper.get(key);
544 if (charset != null)
545 {
546 // Update the cache.
547 if (i > MAP_CACHE)
548 {
549 mappers[MAP_CACHE].put(key,charset);
550 }
551 return charset;
552 }
553 }
554 }
555
556 // Not found, add an empty string to the cache.
557 mappers[MAP_CACHE].put(key,"");
558 }
559 return "";
560 }
561
562 /***
563 * Sets a common locale-charset mapping.
564 *
565 * @param key the key for the charset.
566 * @param charset the corresponding charset.
567 */
568 protected synchronized void setCommonCharSet(String key,
569 String charset)
570 {
571 HashMap mapper = (HashMap) ((HashMap) mappers[MAP_COM]).clone();
572 mapper.put(key,charset);
573 mappers[MAP_COM] = mapper;
574 mappers[MAP_CACHE].clear();
575 }
576 }
This page was automatically generated by Maven