| package org.apache.turbine.services.mimetype.util; |
| |
| /* ==================================================================== |
| * The Apache Software License, Version 1.1 |
| * |
| * Copyright (c) 2001-2003 The Apache Software Foundation. All rights |
| * reserved. |
| * |
| * Redistribution and use in source and binary forms, with or without |
| * modification, are permitted provided that the following conditions |
| * are met: |
| * |
| * 1. Redistributions of source code must retain the above copyright |
| * notice, this list of conditions and the following disclaimer. |
| * |
| * 2. Redistributions in binary form must reproduce the above copyright |
| * notice, this list of conditions and the following disclaimer in |
| * the documentation and/or other materials provided with the |
| * distribution. |
| * |
| * 3. The end-user documentation included with the redistribution, |
| * if any, must include the following acknowledgment: |
| * "This product includes software developed by the |
| * Apache Software Foundation (http://www.apache.org/)." |
| * Alternately, this acknowledgment may appear in the software itself, |
| * if and wherever such third-party acknowledgments normally appear. |
| * |
| * 4. The names "Apache" and "Apache Software Foundation" and |
| * "Apache Turbine" must not be used to endorse or promote products |
| * derived from this software without prior written permission. For |
| * written permission, please contact apache@apache.org. |
| * |
| * 5. Products derived from this software may not be called "Apache", |
| * "Apache Turbine", nor may "Apache" appear in their name, without |
| * prior written permission of the Apache Software Foundation. |
| * |
| * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED |
| * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES |
| * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE |
| * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR |
| * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
| * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
| * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF |
| * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND |
| * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, |
| * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT |
| * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF |
| * SUCH DAMAGE. |
| * ==================================================================== |
| * |
| * This software consists of voluntary contributions made by many |
| * individuals on behalf of the Apache Software Foundation. For more |
| * information on the Apache Software Foundation, please see |
| * <http://www.apache.org/>. |
| */ |
| |
| import java.io.File; |
| import java.io.FileInputStream; |
| import java.io.IOException; |
| import java.io.InputStream; |
| |
| import java.util.HashMap; |
| import java.util.Hashtable; |
| import java.util.Locale; |
| import java.util.Map; |
| import java.util.Properties; |
| |
| /** |
| * This class maintains a set of mappers defining mappings |
| * between locales and the corresponding charsets. The mappings |
| * are defined as properties between locale and charset names. |
| * The definitions can be listed in property files located in user's |
| * home directory, Java home directory or the current class jar. |
| * In addition, this class maintains static default mappings |
| * and constructors support application specific mappings. |
| * |
| * @deprecated Use the Fulcrum Mimetype component instead. |
| * @author <a href="mailto:ilkka.priha@simsoft.fi">Ilkka Priha</a> |
| * @version $Id$ |
| */ |
| public class CharSetMap |
| { |
| /** |
| * The default charset when nothing else is applicable. |
| */ |
| public static final String DEFAULT_CHARSET = "ISO-8859-1"; |
| |
| /** |
| * The name for charset mapper resources. |
| */ |
| public static final String CHARSET_RESOURCE = "charset.properties"; |
| |
| /** |
| * Priorities of available mappers. |
| */ |
| private static final int MAP_CACHE = 0; |
| private static final int MAP_PROG = 1; |
| private static final int MAP_HOME = 2; |
| private static final int MAP_SYS = 3; |
| private static final int MAP_JAR = 4; |
| private static final int MAP_COM = 5; |
| |
| /** |
| * A common charset mapper for languages. |
| */ |
| private static HashMap commonMapper = new HashMap(); |
| |
| static |
| { |
| commonMapper.put("ar", "ISO-8859-6"); |
| commonMapper.put("be", "ISO-8859-5"); |
| commonMapper.put("bg", "ISO-8859-5"); |
| commonMapper.put("ca", "ISO-8859-1"); |
| commonMapper.put("cs", "ISO-8859-2"); |
| commonMapper.put("da", "ISO-8859-1"); |
| commonMapper.put("de", "ISO-8859-1"); |
| commonMapper.put("el", "ISO-8859-7"); |
| commonMapper.put("en", "ISO-8859-1"); |
| commonMapper.put("es", "ISO-8859-1"); |
| commonMapper.put("et", "ISO-8859-1"); |
| commonMapper.put("fi", "ISO-8859-1"); |
| commonMapper.put("fr", "ISO-8859-1"); |
| commonMapper.put("hr", "ISO-8859-2"); |
| commonMapper.put("hu", "ISO-8859-2"); |
| commonMapper.put("is", "ISO-8859-1"); |
| commonMapper.put("it", "ISO-8859-1"); |
| commonMapper.put("iw", "ISO-8859-8"); |
| commonMapper.put("ja", "Shift_JIS"); |
| commonMapper.put("ko", "EUC-KR"); |
| commonMapper.put("lt", "ISO-8859-2"); |
| commonMapper.put("lv", "ISO-8859-2"); |
| commonMapper.put("mk", "ISO-8859-5"); |
| commonMapper.put("nl", "ISO-8859-1"); |
| commonMapper.put("no", "ISO-8859-1"); |
| commonMapper.put("pl", "ISO-8859-2"); |
| commonMapper.put("pt", "ISO-8859-1"); |
| commonMapper.put("ro", "ISO-8859-2"); |
| commonMapper.put("ru", "ISO-8859-5"); |
| commonMapper.put("sh", "ISO-8859-5"); |
| commonMapper.put("sk", "ISO-8859-2"); |
| commonMapper.put("sl", "ISO-8859-2"); |
| commonMapper.put("sq", "ISO-8859-2"); |
| commonMapper.put("sr", "ISO-8859-5"); |
| commonMapper.put("sv", "ISO-8859-1"); |
| commonMapper.put("tr", "ISO-8859-9"); |
| commonMapper.put("uk", "ISO-8859-5"); |
| commonMapper.put("zh", "GB2312"); |
| commonMapper.put("zh_TW", "Big5"); |
| } |
| |
| /** |
| * An array of available charset mappers. |
| */ |
| private Map mappers[] = new Map[6]; |
| |
| /** |
| * Loads mappings from a stream. |
| * |
| * @param input an input stream. |
| * @return the mappings. |
| * @throws IOException for an incorrect stream. |
| */ |
| protected static Map loadStream(InputStream input) |
| throws IOException |
| { |
| Properties props = new Properties(); |
| props.load(input); |
| return new HashMap(props); |
| } |
| |
| /** |
| * Loads mappings from a file. |
| * |
| * @param file a file. |
| * @return the mappings. |
| * @throws IOException for an incorrect file. |
| */ |
| protected static Map loadFile(File file) |
| throws IOException |
| { |
| return loadStream(new FileInputStream(file)); |
| } |
| |
| /** |
| * Loads mappings from a file path. |
| * |
| * @param path a file path. |
| * @return the mappings. |
| * @throws IOException for an incorrect file. |
| */ |
| protected static Map loadPath(String path) |
| throws IOException |
| { |
| return loadFile(new File(path)); |
| } |
| |
| /** |
| * Loads mappings from a resource. |
| * |
| * @param name a resource name. |
| * @return the mappings. |
| */ |
| protected static Map loadResource(String name) |
| { |
| InputStream input = CharSetMap.class.getResourceAsStream(name); |
| if (input != null) |
| { |
| try |
| { |
| return loadStream(input); |
| } |
| catch (IOException x) |
| { |
| return null; |
| } |
| } |
| else |
| { |
| return null; |
| } |
| } |
| |
| /** |
| * Constructs a new charset map with default mappers. |
| */ |
| public CharSetMap() |
| { |
| String path; |
| try |
| { |
| // Check whether the user directory contains mappings. |
| path = System.getProperty("user.home"); |
| if (path != null) |
| { |
| path = path + File.separator + CHARSET_RESOURCE; |
| mappers[MAP_HOME] = loadPath(path); |
| } |
| } |
| catch (Exception x) |
| { |
| } |
| |
| try |
| { |
| // Check whether the system directory contains mappings. |
| path = System.getProperty("java.home") + |
| File.separator + "lib" + File.separator + CHARSET_RESOURCE; |
| mappers[MAP_SYS] = loadPath(path); |
| } |
| catch (Exception x) |
| { |
| } |
| |
| // Check whether the current class jar contains mappings. |
| mappers[MAP_JAR] = loadResource("/META-INF/" + CHARSET_RESOURCE); |
| |
| // Set the common mapper to have the lowest priority. |
| mappers[MAP_COM] = commonMapper; |
| |
| // Set the cache mapper to have the highest priority. |
| mappers[MAP_CACHE] = new Hashtable(); |
| } |
| |
| /** |
| * Contructs a charset map from properties. |
| * |
| * @param props charset mapping propeties. |
| */ |
| public CharSetMap(Properties props) |
| { |
| this(); |
| mappers[MAP_PROG] = new HashMap(props); |
| } |
| |
| /** |
| * Contructs a charset map read from a stream. |
| * |
| * @param input an input stream. |
| * @throws IOException for an incorrect stream. |
| */ |
| public CharSetMap(InputStream input) |
| throws IOException |
| { |
| this(); |
| mappers[MAP_PROG] = loadStream(input); |
| } |
| |
| /** |
| * Contructs a charset map read from a property file. |
| * |
| * @param file a property file. |
| * @throws IOException for an incorrect property file. |
| */ |
| public CharSetMap(File file) |
| throws IOException |
| { |
| this(); |
| mappers[MAP_PROG] = loadFile(file); |
| } |
| |
| /** |
| * Contructs a charset map read from a property file path. |
| * |
| * @param path a property file path. |
| * @throws IOException for an incorrect property file. |
| */ |
| public CharSetMap(String path) |
| throws IOException |
| { |
| this(); |
| mappers[MAP_PROG] = loadPath(path); |
| } |
| |
| /** |
| * Sets a locale-charset mapping. |
| * |
| * @param key the key for the charset. |
| * @param charset the corresponding charset. |
| */ |
| public synchronized void setCharSet(String key, |
| String charset) |
| { |
| HashMap mapper = (HashMap) mappers[MAP_PROG]; |
| mapper = mapper != null ? |
| (HashMap) mapper.clone() : new HashMap(); |
| mapper.put(key, charset); |
| mappers[MAP_PROG] = mapper; |
| mappers[MAP_CACHE].clear(); |
| } |
| |
| /** |
| * Gets the charset for a locale. First a locale specific charset |
| * is searched for, then a country specific one and lastly a language |
| * specific one. If none is found, the default charset is returned. |
| * |
| * @param locale the locale. |
| * @return the charset. |
| */ |
| public String getCharSet(Locale locale) |
| { |
| // Check the cache first. |
| String key = locale.toString(); |
| if (key.length() == 0) |
| { |
| key = "__" + locale.getVariant(); |
| if (key.length() == 2) |
| { |
| return DEFAULT_CHARSET; |
| } |
| } |
| String charset = searchCharSet(key); |
| if (charset.length() == 0) |
| { |
| // Not found, perform a full search and update the cache. |
| String[] items = new String[3]; |
| items[2] = locale.getVariant(); |
| items[1] = locale.getCountry(); |
| items[0] = locale.getLanguage(); |
| charset = searchCharSet(items); |
| if (charset.length() == 0) |
| { |
| charset = DEFAULT_CHARSET; |
| } |
| mappers[MAP_CACHE].put(key, charset); |
| } |
| return charset; |
| } |
| |
| /** |
| * Gets the charset for a locale with a variant. The search |
| * is performed in the following order: |
| * "lang"_"country"_"variant"="charset", |
| * _"counry"_"variant"="charset", |
| * "lang"__"variant"="charset", |
| * __"variant"="charset", |
| * "lang"_"country"="charset", |
| * _"country"="charset", |
| * "lang"="charset". |
| * If nothing of the above is found, the default charset is returned. |
| * |
| * @param locale the locale. |
| * @param variant a variant field. |
| * @return the charset. |
| */ |
| public String getCharSet(Locale locale, |
| String variant) |
| { |
| // Check the cache first. |
| if ((variant != null) && |
| (variant.length() > 0)) |
| { |
| String key = locale.toString(); |
| if (key.length() == 0) |
| { |
| key = "__" + locale.getVariant(); |
| if (key.length() > 2) |
| { |
| key += '_' + variant; |
| } |
| else |
| { |
| key += variant; |
| } |
| } |
| else if (locale.getCountry().length() == 0) |
| { |
| key += "__" + variant; |
| } |
| else |
| { |
| key += '_' + variant; |
| } |
| String charset = searchCharSet(key); |
| if (charset.length() == 0) |
| { |
| // Not found, perform a full search and update the cache. |
| String[] items = new String[4]; |
| items[3] = variant; |
| items[2] = locale.getVariant(); |
| items[1] = locale.getCountry(); |
| items[0] = locale.getLanguage(); |
| charset = searchCharSet(items); |
| if (charset.length() == 0) |
| { |
| charset = DEFAULT_CHARSET; |
| } |
| mappers[MAP_CACHE].put(key, charset); |
| } |
| return charset; |
| } |
| else |
| { |
| return getCharSet(locale); |
| } |
| } |
| |
| /** |
| * Gets the charset for a specified key. |
| * |
| * @param key the key for the charset. |
| * @return the found charset or the default one. |
| */ |
| public String getCharSet(String key) |
| { |
| String charset = searchCharSet(key); |
| return charset.length() > 0 ? charset : DEFAULT_CHARSET; |
| } |
| |
| /** |
| * Gets the charset for a specified key. |
| * |
| * @param key the key for the charset. |
| * @param def the default charset if none is found. |
| * @return the found charset or the given default. |
| */ |
| public String getCharSet(String key, |
| String def) |
| { |
| String charset = searchCharSet(key); |
| return charset.length() > 0 ? charset : def; |
| } |
| |
| /** |
| * Searches for a charset for a specified locale. |
| * |
| * @param items an array of locale items. |
| * @return the found charset or an empty string. |
| */ |
| private String searchCharSet(String[] items) |
| { |
| String charset; |
| StringBuffer sb = new StringBuffer(); |
| for (int i = items.length; i > 0; i--) |
| { |
| charset = searchCharSet(items, sb, i); |
| if (charset.length() > 0) |
| { |
| return charset; |
| } |
| sb.setLength(0); |
| } |
| return ""; |
| } |
| |
| /** |
| * Searches recursively for a charset for a specified locale. |
| * |
| * @param items an array of locale items. |
| * @param base a buffer of base items. |
| * @param count the number of items to go through. |
| * @return the found charset or an empty string. |
| */ |
| private String searchCharSet(String[] items, |
| StringBuffer base, |
| int count) |
| { |
| if ((--count >= 0) && |
| (items[count] != null) && |
| (items[count].length() > 0)) |
| { |
| String charset; |
| base.insert(0, items[count]); |
| int length = base.length(); |
| for (int i = count; i > 0; i--) |
| { |
| if ((i == count) || |
| (i <= 1)) |
| { |
| base.insert(0, '_'); |
| length++; |
| } |
| charset = searchCharSet(items, base, i); |
| if (charset.length() > 0) |
| { |
| return charset; |
| } |
| base.delete(0, base.length() - length); |
| } |
| return searchCharSet(base.toString()); |
| } |
| else |
| { |
| return ""; |
| } |
| } |
| |
| /** |
| * Searches for a charset for a specified key. |
| * |
| * @param key the key for the charset. |
| * @return the found charset or an empty string. |
| */ |
| private String searchCharSet(String key) |
| { |
| if ((key != null) && |
| (key.length() > 0)) |
| { |
| // Go through mappers. |
| Map mapper; |
| String charset; |
| for (int i = 0; i < mappers.length; i++) |
| { |
| mapper = mappers[i]; |
| if (mapper != null) |
| { |
| charset = (String) mapper.get(key); |
| if (charset != null) |
| { |
| // Update the cache. |
| if (i > MAP_CACHE) |
| { |
| mappers[MAP_CACHE].put(key, charset); |
| } |
| return charset; |
| } |
| } |
| } |
| |
| // Not found, add an empty string to the cache. |
| mappers[MAP_CACHE].put(key, ""); |
| } |
| return ""; |
| } |
| |
| /** |
| * Sets a common locale-charset mapping. |
| * |
| * @param key the key for the charset. |
| * @param charset the corresponding charset. |
| */ |
| protected synchronized void setCommonCharSet(String key, |
| String charset) |
| { |
| HashMap mapper = (HashMap) ((HashMap) mappers[MAP_COM]).clone(); |
| mapper.put(key, charset); |
| mappers[MAP_COM] = mapper; |
| mappers[MAP_CACHE].clear(); |
| } |
| } |