| /* Charset.java -- | |
| Copyright (C) 2002, 2004, 2005 Free Software Foundation, Inc. | |
| This file is part of GNU Classpath. | |
| GNU Classpath is free software; you can redistribute it and/or modify | |
| it under the terms of the GNU General Public License as published by | |
| the Free Software Foundation; either version 2, or (at your option) | |
| any later version. | |
| GNU Classpath is distributed in the hope that it will be useful, but | |
| WITHOUT ANY WARRANTY; without even the implied warranty of | |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
| General Public License for more details. | |
| You should have received a copy of the GNU General Public License | |
| along with GNU Classpath; see the file COPYING. If not, write to the | |
| Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA | |
| 02110-1301 USA. | |
| Linking this library statically or dynamically with other modules is | |
| making a combined work based on this library. Thus, the terms and | |
| conditions of the GNU General Public License cover the whole | |
| combination. | |
| As a special exception, the copyright holders of this library give you | |
| permission to link this library with independent modules to produce an | |
| executable, regardless of the license terms of these independent | |
| modules, and to copy and distribute the resulting executable under | |
| terms of your choice, provided that you also meet, for each linked | |
| independent module, the terms and conditions of the license of that | |
| module. An independent module is a module which is not derived from | |
| or based on this library. If you modify this library, you may extend | |
| this exception to your version of the library, but you are not | |
| obligated to do so. If you do not wish to do so, delete this | |
| exception statement from your version. */ | |
| package java.nio.charset; | |
| import gnu.classpath.ServiceFactory; | |
| import gnu.classpath.SystemProperties; | |
| import gnu.java.nio.charset.Provider; | |
| import gnu.java.nio.charset.iconv.IconvProvider; | |
| import java.nio.ByteBuffer; | |
| import java.nio.CharBuffer; | |
| import java.nio.charset.spi.CharsetProvider; | |
| import java.util.Collections; | |
| import java.util.HashSet; | |
| import java.util.Iterator; | |
| import java.util.LinkedHashSet; | |
| import java.util.Locale; | |
| import java.util.Set; | |
| import java.util.SortedMap; | |
| import java.util.TreeMap; | |
| /** | |
| * @author Jesse Rosenstock | |
| * @since 1.4 | |
| * @status updated to 1.5 | |
| */ | |
| public abstract class Charset implements Comparable | |
| { | |
| private CharsetEncoder cachedEncoder; | |
| private CharsetDecoder cachedDecoder; | |
| /** | |
| * Extra Charset providers. | |
| */ | |
| private static CharsetProvider[] providers; | |
| private final String canonicalName; | |
| private final String[] aliases; | |
| protected Charset (String canonicalName, String[] aliases) | |
| { | |
| checkName (canonicalName); | |
| if (aliases != null) | |
| { | |
| int n = aliases.length; | |
| for (int i = 0; i < n; ++i) | |
| checkName (aliases[i]); | |
| } | |
| cachedEncoder = null; | |
| cachedDecoder = null; | |
| this.canonicalName = canonicalName; | |
| this.aliases = aliases; | |
| } | |
| /** | |
| * @throws IllegalCharsetNameException if the name is illegal | |
| */ | |
| private static void checkName (String name) | |
| { | |
| int n = name.length (); | |
| if (n == 0) | |
| throw new IllegalCharsetNameException (name); | |
| char ch = name.charAt (0); | |
| if (!(('A' <= ch && ch <= 'Z') | |
| || ('a' <= ch && ch <= 'z') | |
| || ('0' <= ch && ch <= '9'))) | |
| throw new IllegalCharsetNameException (name); | |
| for (int i = 1; i < n; ++i) | |
| { | |
| ch = name.charAt (i); | |
| if (!(('A' <= ch && ch <= 'Z') | |
| || ('a' <= ch && ch <= 'z') | |
| || ('0' <= ch && ch <= '9') | |
| || ch == '-' || ch == '.' || ch == ':' || ch == '_')) | |
| throw new IllegalCharsetNameException (name); | |
| } | |
| } | |
| /** | |
| * Returns the system default charset. | |
| * | |
| * This may be set by the user or VM with the file.encoding | |
| * property. | |
| * | |
| * @since 1.5 | |
| */ | |
| public static Charset defaultCharset() | |
| { | |
| String encoding; | |
| try | |
| { | |
| encoding = SystemProperties.getProperty("file.encoding"); | |
| } | |
| catch(SecurityException e) | |
| { | |
| // Use fallback. | |
| encoding = "ISO-8859-1"; | |
| } | |
| catch(IllegalArgumentException e) | |
| { | |
| // Use fallback. | |
| encoding = "ISO-8859-1"; | |
| } | |
| try | |
| { | |
| return forName(encoding); | |
| } | |
| catch(UnsupportedCharsetException e) | |
| { | |
| // Ignore. | |
| } | |
| catch(IllegalCharsetNameException e) | |
| { | |
| // Ignore. | |
| } | |
| catch(IllegalArgumentException e) | |
| { | |
| // Ignore. | |
| } | |
| throw new IllegalStateException("Can't get default charset!"); | |
| } | |
| public static boolean isSupported (String charsetName) | |
| { | |
| return charsetForName (charsetName) != null; | |
| } | |
| /** | |
| * Returns the Charset instance for the charset of the given name. | |
| * | |
| * @param charsetName | |
| * @return the Charset instance for the indicated charset | |
| * @throws UnsupportedCharsetException if this VM does not support | |
| * the charset of the given name. | |
| * @throws IllegalCharsetNameException if the given charset name is | |
| * legal. | |
| * @throws IllegalArgumentException if <code>charsetName</code> is null. | |
| */ | |
| public static Charset forName (String charsetName) | |
| { | |
| // Throws IllegalArgumentException as the JDK does. | |
| if(charsetName == null) | |
| throw new IllegalArgumentException("Charset name must not be null."); | |
| Charset cs = charsetForName (charsetName); | |
| if (cs == null) | |
| throw new UnsupportedCharsetException (charsetName); | |
| return cs; | |
| } | |
| /** | |
| * Retrieves a charset for the given charset name. | |
| * | |
| * @return A charset object for the charset with the specified name, or | |
| * <code>null</code> if no such charset exists. | |
| * | |
| * @throws IllegalCharsetNameException if the name is illegal | |
| */ | |
| private static Charset charsetForName(String charsetName) | |
| { | |
| checkName (charsetName); | |
| // Try the default provider first | |
| // (so we don't need to load external providers unless really necessary) | |
| // if it is an exotic charset try loading the external providers. | |
| Charset cs = provider().charsetForName(charsetName); | |
| if (cs == null) | |
| { | |
| CharsetProvider[] providers = providers2(); | |
| for (int i = 0; i < providers.length; i++) | |
| { | |
| cs = providers[i].charsetForName(charsetName); | |
| if (cs != null) | |
| break; | |
| } | |
| } | |
| return cs; | |
| } | |
| public static SortedMap availableCharsets() | |
| { | |
| TreeMap charsets = new TreeMap(String.CASE_INSENSITIVE_ORDER); | |
| for (Iterator i = provider().charsets(); i.hasNext(); ) | |
| { | |
| Charset cs = (Charset) i.next(); | |
| charsets.put(cs.name(), cs); | |
| } | |
| CharsetProvider[] providers = providers2(); | |
| for (int j = 0; j < providers.length; j++) | |
| { | |
| for (Iterator i = providers[j].charsets(); i.hasNext(); ) | |
| { | |
| Charset cs = (Charset) i.next(); | |
| charsets.put(cs.name(), cs); | |
| } | |
| } | |
| return Collections.unmodifiableSortedMap(charsets); | |
| } | |
| private static CharsetProvider provider() | |
| { | |
| String useIconv = SystemProperties.getProperty | |
| ("gnu.classpath.nio.charset.provider.iconv"); | |
| if (useIconv != null) | |
| return IconvProvider.provider(); | |
| return Provider.provider(); | |
| } | |
| /** | |
| * We need to support multiple providers, reading them from | |
| * java.nio.charset.spi.CharsetProvider in the resource directory | |
| * META-INF/services. This returns the "extra" charset providers. | |
| */ | |
| private static CharsetProvider[] providers2() | |
| { | |
| if (providers == null) | |
| { | |
| try | |
| { | |
| Iterator i = ServiceFactory.lookupProviders(CharsetProvider.class); | |
| LinkedHashSet set = new LinkedHashSet(); | |
| while (i.hasNext()) | |
| set.add(i.next()); | |
| providers = new CharsetProvider[set.size()]; | |
| set.toArray(providers); | |
| } | |
| catch (Exception e) | |
| { | |
| throw new RuntimeException(e); | |
| } | |
| } | |
| return providers; | |
| } | |
| public final String name () | |
| { | |
| return canonicalName; | |
| } | |
| public final Set aliases () | |
| { | |
| if (aliases == null) | |
| return Collections.EMPTY_SET; | |
| // should we cache the aliasSet instead? | |
| int n = aliases.length; | |
| HashSet aliasSet = new HashSet (n); | |
| for (int i = 0; i < n; ++i) | |
| aliasSet.add (aliases[i]); | |
| return Collections.unmodifiableSet (aliasSet); | |
| } | |
| public String displayName () | |
| { | |
| return canonicalName; | |
| } | |
| public String displayName (Locale locale) | |
| { | |
| return canonicalName; | |
| } | |
| public final boolean isRegistered () | |
| { | |
| return (!canonicalName.startsWith ("x-") | |
| && !canonicalName.startsWith ("X-")); | |
| } | |
| public abstract boolean contains (Charset cs); | |
| public abstract CharsetDecoder newDecoder (); | |
| public abstract CharsetEncoder newEncoder (); | |
| public boolean canEncode () | |
| { | |
| return true; | |
| } | |
| // NB: This implementation serializes different threads calling | |
| // Charset.encode(), a potential performance problem. It might | |
| // be better to remove the cache, or use ThreadLocal to cache on | |
| // a per-thread basis. | |
| public final synchronized ByteBuffer encode (CharBuffer cb) | |
| { | |
| try | |
| { | |
| if (cachedEncoder == null) | |
| { | |
| cachedEncoder = newEncoder () | |
| .onMalformedInput (CodingErrorAction.REPLACE) | |
| .onUnmappableCharacter (CodingErrorAction.REPLACE); | |
| } else | |
| cachedEncoder.reset(); | |
| return cachedEncoder.encode (cb); | |
| } | |
| catch (CharacterCodingException e) | |
| { | |
| throw new AssertionError (e); | |
| } | |
| } | |
| public final ByteBuffer encode (String str) | |
| { | |
| return encode (CharBuffer.wrap (str)); | |
| } | |
| // NB: This implementation serializes different threads calling | |
| // Charset.decode(), a potential performance problem. It might | |
| // be better to remove the cache, or use ThreadLocal to cache on | |
| // a per-thread basis. | |
| public final synchronized CharBuffer decode (ByteBuffer bb) | |
| { | |
| try | |
| { | |
| if (cachedDecoder == null) | |
| { | |
| cachedDecoder = newDecoder () | |
| .onMalformedInput (CodingErrorAction.REPLACE) | |
| .onUnmappableCharacter (CodingErrorAction.REPLACE); | |
| } else | |
| cachedDecoder.reset(); | |
| return cachedDecoder.decode (bb); | |
| } | |
| catch (CharacterCodingException e) | |
| { | |
| throw new AssertionError (e); | |
| } | |
| } | |
| public final int compareTo (Object ob) | |
| { | |
| return canonicalName.compareToIgnoreCase (((Charset) ob).canonicalName); | |
| } | |
| public final int hashCode () | |
| { | |
| return canonicalName.hashCode (); | |
| } | |
| public final boolean equals (Object ob) | |
| { | |
| if (ob instanceof Charset) | |
| return canonicalName.equalsIgnoreCase (((Charset) ob).canonicalName); | |
| else | |
| return false; | |
| } | |
| public final String toString () | |
| { | |
| return canonicalName; | |
| } | |
| } |