diff options
author | The Android Open Source Project <initial-contribution@android.com> | 2008-10-21 07:00:00 -0700 |
---|---|---|
committer | The Android Open Source Project <initial-contribution@android.com> | 2008-10-21 07:00:00 -0700 |
commit | fdb2704414a9ed92394ada0d1395e4db86889465 (patch) | |
tree | 9b591a4a50054274a197f02b3ccb51313681879f /nio_char/src | |
download | libcore-fdb2704414a9ed92394ada0d1395e4db86889465.zip libcore-fdb2704414a9ed92394ada0d1395e4db86889465.tar.gz libcore-fdb2704414a9ed92394ada0d1395e4db86889465.tar.bz2 |
Initial Contribution
Diffstat (limited to 'nio_char/src')
64 files changed, 10500 insertions, 0 deletions
diff --git a/nio_char/src/main/java/java/nio/charset/CharacterCodingException.java b/nio_char/src/main/java/java/nio/charset/CharacterCodingException.java new file mode 100644 index 0000000..7a9773f --- /dev/null +++ b/nio_char/src/main/java/java/nio/charset/CharacterCodingException.java @@ -0,0 +1,41 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package java.nio.charset; + +import java.io.IOException; + +/** + * + * Type of exception thrown when an encoding or decoding error occurs. + * + */ +public class CharacterCodingException extends IOException { + + /* + * This constant is used during deserialization to check the J2SE version + * which created the serialized object. + */ + private static final long serialVersionUID = 8421532232154627783L; + + /** + * Default constructor. + */ + public CharacterCodingException() { + super(); + } +} diff --git a/nio_char/src/main/java/java/nio/charset/Charset.java b/nio_char/src/main/java/java/nio/charset/Charset.java new file mode 100644 index 0000000..4274b26 --- /dev/null +++ b/nio_char/src/main/java/java/nio/charset/Charset.java @@ -0,0 +1,873 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package java.nio.charset; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.net.URL; +import java.nio.ByteBuffer; +import java.nio.CharBuffer; +import java.nio.charset.spi.CharsetProvider; +import java.security.AccessController; +import java.security.PrivilegedAction; +import java.util.Collections; +import java.util.Comparator; +import java.util.Enumeration; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.Locale; +import java.util.Set; +import java.util.SortedMap; +import java.util.TreeMap; + +import com.ibm.icu4jni.charset.CharsetProviderICU; + +/** + * A charset defines a mapping between a Unicode character sequence and a byte + * sequence. It facilitate the encoding from a Unicode character sequence into a + * byte sequence, and the decoding from a byte sequence into a Unicode character + * sequence. + * <p> + * A charset has a canonical name, which are usually in uppercase. Typically it + * also has one or more aliases. The name string can only consist of the + * following characters: '0' - '9', 'A' - 'Z', 'a' - 'z', '.', ':'. '-' and '_'. + * The first character of the name must be a digit or a letter. + * </p> + * <p> + * The following charsets should be supported by any java platforms: US-ASCII, + * ISO-8859-1, UTF-8, UTF-16BE, UTF-16LE, UTF-16. + * </p> + * <p> + * Additional charsets can be made available by configuring one or more charset + * providers through provider configuration files. Such files are always named + * as "java.nio.charset.spi.CharsetProvider" and located in the + * "META-INF/services" sub folder of one or more classpaths. The files should be + * encoded in "UTF-8". Each line of their content specifies the class name of a + * charset provider which extends <code>java.nio.spi.CharsetProvider</code>. + * A line should ends with '\r', '\n' or '\r\n'. Leading and trailing + * whitespaces are trimmed. Blank lines, and lines (after trimmed) starting with + * "#" which are regarded as comments, are both ignored. Duplicates of already + * appeared names are also ignored. Both the configuration files and the + * provider classes will be loaded using the thread context class loader. + * </p> + * <p> + * This class is thread-safe. + * </p> + * + * @see java.nio.charset.spi.CharsetProvider + * + */ +public abstract class Charset implements Comparable<Charset> { + + /* + * -------------------------------------------------------------------- + * Constants + * -------------------------------------------------------------------- + */ + + /* + * the name of configuration files where charset provider class names can be + * specified. + */ + private static final String PROVIDER_CONFIGURATION_FILE_NAME = "META-INF/services/java.nio.charset.spi.CharsetProvider"; //$NON-NLS-1$ + + /* + * the encoding of configuration files + */ + private static final String PROVIDER_CONFIGURATION_FILE_ENCODING = "UTF-8"; //$NON-NLS-1$ + + /* + * the comment string used in configuration files + */ + private static final String PROVIDER_CONFIGURATION_FILE_COMMENT = "#"; //$NON-NLS-1$ + + private static ClassLoader systemClassLoader; + + /* + * -------------------------------------------------------------------- + * Class variables + * -------------------------------------------------------------------- + */ + + // built in provider instance, assuming thread-safe + private static CharsetProviderICU _builtInProvider = null; + + // cached built in charsets + private static TreeMap<String, Charset> _builtInCharsets = null; + + /* + * -------------------------------------------------------------------- + * Instance variables + * -------------------------------------------------------------------- + */ + + private final String canonicalName; + + // the aliases set + private final HashSet<String> aliasesSet; + + // cached Charset table + private static HashMap<String, Charset> cachedCharsetTable = new HashMap<String, Charset>(); + + // cached CharsetDecoder table + private static HashMap<String, CharsetDecoder> cachedCharsetDecoderTable = new HashMap<String, CharsetDecoder>(); + + // cached CharsetEncoder table + private static HashMap<String, CharsetEncoder> cachedCharsetEncoderTable = new HashMap<String, CharsetEncoder>(); + + /* + * ------------------------------------------------------------------- + * Global initialization + * ------------------------------------------------------------------- + */ + static { + /* + * create built-in charset provider even if no privilege to access + * charset provider. + */ + _builtInProvider = AccessController + .doPrivileged(new PrivilegedAction<CharsetProviderICU>() { + public CharsetProviderICU run() { + return new CharsetProviderICU(); + } + }); + } + + /* + * ------------------------------------------------------------------- + * Constructors + * ------------------------------------------------------------------- + */ + + /** + * Constructs a <code>Charset</code> object. Duplicated aliases are + * ignored. + * + * @param canonicalName + * the canonical name of the charset + * @param aliases + * an array containing all aliases of the charset + * @throws IllegalCharsetNameException + * on an illegal value being supplied for either + * <code>canonicalName</code> or for any element of + * <code>aliases</code>. + * + */ + protected Charset(String canonicalName, String[] aliases) + throws IllegalCharsetNameException { + // throw IllegalArgumentException if name is null + if (null == canonicalName) { + throw new NullPointerException(); + } + // check whether the given canonical name is legal + checkCharsetName(canonicalName); + this.canonicalName = canonicalName; + // check each alias and put into a set + this.aliasesSet = new HashSet<String>(); + if (null != aliases) { + for (int i = 0; i < aliases.length; i++) { + checkCharsetName(aliases[i]); + this.aliasesSet.add(aliases[i]); + } + } + } + + /* + * ------------------------------------------------------------------- + * Methods + * ------------------------------------------------------------------- + */ + + /* + * Checks whether a character is a special character that can be used in + * charset names, other than letters and digits. + */ + private static boolean isSpecial(char c) { + return ('-' == c || '.' == c || ':' == c || '_' == c); + } + + /* + * Checks whether a character is a letter (ascii) which are defined in Java + * Spec. + */ + private static boolean isLetter(char c) { + return ('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z'); + } + + /* + * Checks whether a character is a digit (ascii) which are defined in Java + * Spec. + */ + private static boolean isDigit(char c) { + return ('0' <= c && c <= '9'); + } + + /* + * Checks whether a given string is a legal charset name. The argument name + * should not be null. + */ + private static void checkCharsetName(String name) { + // An empty string is illegal charset name + if (name.length() == 0) { + throw new IllegalCharsetNameException(name); + } + // The first character must be a letter or a digit + // This is related to HARMONY-68 (won't fix) + // char first = name.charAt(0); + // if (!isLetter(first) && !isDigit(first)) { + // throw new IllegalCharsetNameException(name); + // } + // Check the remaining characters + int length = name.length(); + for (int i = 0; i < length; i++) { + char c = name.charAt(i); + if (!isLetter(c) && !isDigit(c) && !isSpecial(c)) { + throw new IllegalCharsetNameException(name); + } + } + } + + /* + * Use privileged code to get the context class loader. + */ + private static ClassLoader getContextClassLoader() { + final Thread t = Thread.currentThread(); + return AccessController + .doPrivileged(new PrivilegedAction<ClassLoader>() { + public ClassLoader run() { + return t.getContextClassLoader(); + } + }); + } + + /* + * Use privileged code to get the system class loader. + */ + private static void getSystemClassLoader() { + if (null == systemClassLoader) { + systemClassLoader = AccessController + .doPrivileged(new PrivilegedAction<ClassLoader>() { + public ClassLoader run() { + return ClassLoader.getSystemClassLoader(); + } + }); + } + } + + /* + * Add the charsets supported by the given provider to the map. + */ + private static void addCharsets(CharsetProvider cp, + TreeMap<String, Charset> charsets) { + Iterator<Charset> it = cp.charsets(); + while (it.hasNext()) { + Charset cs = it.next(); + // Only new charsets will be added + if (!charsets.containsKey(cs.name())) { + charsets.put(cs.name(), cs); + } + } + } + + /* + * Trim comment string, and then trim white spaces. + */ + private static String trimClassName(String name) { + String trimedName = name; + int index = name.indexOf(PROVIDER_CONFIGURATION_FILE_COMMENT); + // Trim comments + if (index != -1) { + trimedName = name.substring(0, index); + } + return trimedName.trim(); + } + + /* + * Read a configuration file and add the charsets supported by the providers + * specified by this configuration file to the map. + */ + private static void loadConfiguredCharsets(URL configFile, + ClassLoader contextClassLoader, TreeMap<String, Charset> charsets) { + BufferedReader reader = null; + try { + InputStream is = configFile.openStream(); + // Read each line for charset provider class names + // BEGIN android-modified + reader = new BufferedReader(new InputStreamReader(is, + PROVIDER_CONFIGURATION_FILE_ENCODING), 8192); + // END android-modified + String providerClassName = reader.readLine(); + while (null != providerClassName) { + providerClassName = trimClassName(providerClassName); + // Skip comments and blank lines + if (providerClassName.length() > 0) { // Non empty string + // Load the charset provider + Object cp = null; + try { + Class<?> c = Class.forName(providerClassName, true, + contextClassLoader); + cp = c.newInstance(); + } catch (Exception ex) { + // try to use system classloader when context + // classloader failed to load config file. + try { + getSystemClassLoader(); + Class<?> c = Class.forName(providerClassName, true, + systemClassLoader); + cp = c.newInstance(); + } catch (Exception e) { + throw new Error(e.getMessage(), e); + } + } + // Put the charsets supported by this provider into the map + addCharsets((CharsetProvider) cp, charsets); + } + // Read the next line of the config file + providerClassName = reader.readLine(); + } + } catch (IOException ex) { + // Can't read this configuration file, ignore + } finally { + try { + if (null != reader) { + reader.close(); + } + } catch (IOException ex) { + // Ignore closing exception + } + } + } + + /** + * Gets a map of all available charsets supported by the runtime. + * <p> + * The returned map contains mappings from canonical names to corresponding + * instances of <code>Charset</code>. The canonical names can be + * considered as case-insensitive. + * </p> + * + * @return an unmodifiable map of all available charsets supported by the + * runtime + */ + @SuppressWarnings("unchecked") + public static SortedMap<String, Charset> availableCharsets() { + // Initialize the built-in charsets map cache if necessary + if (null == _builtInCharsets) { + synchronized (Charset.class) { + if (null == _builtInCharsets) { + _builtInCharsets = new TreeMap<String, Charset>( + IgnoreCaseComparator.getInstance()); + _builtInProvider.putCharsets(_builtInCharsets); + } + } + } + + // Add built-in charsets + TreeMap<String, Charset> charsets = (TreeMap<String, Charset>) _builtInCharsets + .clone(); + + // Collect all charsets provided by charset providers + ClassLoader contextClassLoader = getContextClassLoader(); + Enumeration<URL> e = null; + try { + if (null != contextClassLoader) { + e = contextClassLoader + .getResources(PROVIDER_CONFIGURATION_FILE_NAME); + } else { + getSystemClassLoader(); + e = systemClassLoader + .getResources(PROVIDER_CONFIGURATION_FILE_NAME); + } + // Examine each configuration file + while (e.hasMoreElements()) { + loadConfiguredCharsets(e.nextElement(), contextClassLoader, + charsets); + } + } catch (IOException ex) { + // Unexpected ClassLoader exception, ignore + } + return Collections.unmodifiableSortedMap(charsets); + } + + /* + * Read a configuration file and try to find the desired charset among those + * which are supported by the providers specified in this configuration + * file. + */ + private static Charset searchConfiguredCharsets(String charsetName, + ClassLoader contextClassLoader, URL configFile) { + BufferedReader reader = null; + try { + InputStream is = configFile.openStream(); + // Read each line for charset provider class names + // BEGIN android-modified + reader = new BufferedReader(new InputStreamReader(is, + PROVIDER_CONFIGURATION_FILE_ENCODING), 8192); + // END android-modified + String providerClassName = reader.readLine(); + while (null != providerClassName) { + providerClassName = trimClassName(providerClassName); + if (providerClassName.length() > 0) { // Non empty string + // Load the charset provider + Object cp = null; + try { + Class<?> c = Class.forName(providerClassName, true, + contextClassLoader); + cp = c.newInstance(); + } catch (Exception ex) { + // try to use system classloader when context + // classloader failed to load config file. + try { + getSystemClassLoader(); + Class<?> c = Class.forName(providerClassName, true, + systemClassLoader); + cp = c.newInstance(); + } catch (SecurityException e) { + throw e; + } catch (Exception e) { + throw new Error(e.getMessage(), e); + } + } + // Try to get the desired charset from this provider + Charset cs = ((CharsetProvider) cp) + .charsetForName(charsetName); + if (null != cs) { + return cs; + } + } + // Read the next line of the config file + providerClassName = reader.readLine(); + } + return null; + } catch (IOException ex) { + // Can't read this configuration file + return null; + } finally { + try { + if (null != reader) { + reader.close(); + } + } catch (IOException ex) { + // Ignore closing exception + } + } + } + + /* + * Gets a <code> Charset </code> instance for the specified charset name. If + * the charset is not supported, returns null instead of throwing an + * exception. + */ + private static Charset forNameInternal(String charsetName) + throws IllegalCharsetNameException { + if (null == charsetName) { + throw new IllegalArgumentException(); + } + checkCharsetName(charsetName); + synchronized (Charset.class) { + // Try to get Charset from cachedCharsetTable + Charset cs = getCachedCharset(charsetName); + if (null != cs) { + return cs; + } + // Try built-in charsets + cs = _builtInProvider.charsetForName(charsetName); + if (null != cs) { + cacheCharset(cs); + return cs; + } + + // Collect all charsets provided by charset providers + ClassLoader contextClassLoader = getContextClassLoader(); + Enumeration<URL> e = null; + try { + if (null != contextClassLoader) { + e = contextClassLoader + .getResources(PROVIDER_CONFIGURATION_FILE_NAME); + } else { + getSystemClassLoader(); + e = systemClassLoader + .getResources(PROVIDER_CONFIGURATION_FILE_NAME); + } + // Examine each configuration file + while (e.hasMoreElements()) { + cs = searchConfiguredCharsets(charsetName, + contextClassLoader, e.nextElement()); + if (null != cs) { + cacheCharset(cs); + return cs; + } + } + } catch (IOException ex) { + // Unexpected ClassLoader exception, ignore + } + } + return null; + } + + /* + * save charset into cachedCharsetTable + */ + private static void cacheCharset(Charset cs) { + cachedCharsetTable.put(cs.name(), cs); + Set<String> aliasesSet = cs.aliases(); + if (null != aliasesSet) { + Iterator<String> iter = aliasesSet.iterator(); + while (iter.hasNext()) { + String alias = iter.next(); + cachedCharsetTable.put(alias, cs); + } + } + } + + /* + * get cached charset reference by name + */ + private static Charset getCachedCharset(String name) { + return cachedCharsetTable.get(name); + } + + /** + * Gets a <code>Charset</code> instance for the specified charset name. + * + * @param charsetName + * the name of the charset + * @return a <code>Charset</code> instance for the specified charset name + * @throws IllegalCharsetNameException + * If the specified charset name is illegal. + * @throws UnsupportedCharsetException + * If the desired charset is not supported by this runtime. + */ + public static Charset forName(String charsetName) + throws IllegalCharsetNameException, UnsupportedCharsetException { + Charset c = forNameInternal(charsetName); + if (null == c) { + throw new UnsupportedCharsetException(charsetName); + } + return c; + } + + /** + * Determines whether the specified charset is supported by this runtime. + * + * @param charsetName + * the name of the charset + * @return true if the specified charset is supported, otherwise false + * @throws IllegalCharsetNameException + * If the specified charset name is illegal. + */ + public static boolean isSupported(String charsetName) + throws IllegalCharsetNameException { + Charset cs = forNameInternal(charsetName); + return (null != cs); + } + + /** + * Determines whether this charset is a super set of the given charset. + * + * @param charset + * a given charset + * @return true if this charset is a super set of the given charset, + * otherwise false + */ + public abstract boolean contains(Charset charset); + + /** + * Gets a new instance of encoder for this charset. + * + * @return a new instance of encoder for this charset + */ + public abstract CharsetEncoder newEncoder(); + + /** + * Gets a new instance of decoder for this charset. + * + * @return a new instance of decoder for this charset + */ + public abstract CharsetDecoder newDecoder(); + + /** + * Gets the canonical name of this charset. + * + * @return this charset's name in canonical form. + */ + public final String name() { + return this.canonicalName; + } + + /** + * Gets the set of this charset's aliases. + * + * @return an unmodifiable set of this charset's aliases + */ + public final Set<String> aliases() { + return Collections.unmodifiableSet(this.aliasesSet); + } + + /** + * Gets the name of this charset for the default locale. + * + * @return the name of this charset for the default locale + */ + public String displayName() { + return this.canonicalName; + } + + /** + * Gets the name of this charset for the specified locale. + * + * @param l + * a certain locale + * @return the name of this charset for the specified locale + */ + public String displayName(Locale l) { + return this.canonicalName; + } + + /** + * Returns whether this charset is known to be registered in the IANA + * Charset Registry. + * + * @return true if the charset is known to be registered, otherwise returns + * false. + */ + public final boolean isRegistered() { + return !canonicalName.startsWith("x-") //$NON-NLS-1$ + && !canonicalName.startsWith("X-"); //$NON-NLS-1$ + } + + /** + * Returns true if this charset supports encoding, otherwise false. + * + * @return true + */ + public boolean canEncode() { + return true; + } + + /** + * Encodes the content of the give character buffer and outputs to a byte + * buffer that is to be returned. + * <p> + * The default action in case of encoding errors is + * <code>CodingErrorAction.REPLACE</code>. + * </p> + * + * @param buffer + * the character buffer containing the content to be encoded + * @return the result of the encoding + */ + synchronized public final ByteBuffer encode(CharBuffer buffer) { + CharsetEncoder e = getCachedCharsetEncoder(canonicalName); + try { + synchronized (e) { + return e.encode(buffer); + } + } catch (CharacterCodingException ex) { + throw new Error(ex.getMessage(), ex); + } + } + + /* + * get cached CharsetEncoder by canonical name + */ + private CharsetEncoder getCachedCharsetEncoder(String name) { + synchronized (cachedCharsetEncoderTable) { + CharsetEncoder e = cachedCharsetEncoderTable + .get(name); + if (null == e) { + e = this.newEncoder(); + e.onMalformedInput(CodingErrorAction.REPLACE); + e.onUnmappableCharacter(CodingErrorAction.REPLACE); + cachedCharsetEncoderTable.put(name, e); + } + return e; + } + } + + /** + * Encodes a string and outputs to a byte buffer that is to be returned. + * <p> + * The default action in case of encoding errors is + * <code>CodingErrorAction.REPLACE</code>. + * </p> + * + * @param s + * the string to be encoded + * @return the result of the encoding + */ + public final ByteBuffer encode(String s) { + return encode(CharBuffer.wrap(s)); + } + + /** + * Decodes the content of the give byte buffer and outputs to a character + * buffer that is to be returned. + * <p> + * The default action in case of decoding errors is + * <code>CodingErrorAction.REPLACE</code>. + * </p> + * + * @param buffer + * the byte buffer containing the content to be decoded + * @return a character buffer containing the output of the decoding + */ + public final CharBuffer decode(ByteBuffer buffer) { + CharsetDecoder d = getCachedCharsetDecoder(canonicalName); + try { + synchronized (d) { + return d.decode(buffer); + } + } catch (CharacterCodingException ex) { + throw new Error(ex.getMessage(), ex); + } + } + + /* + * get cached CharsetDecoder by canonical name + */ + private CharsetDecoder getCachedCharsetDecoder(String name) { + synchronized (cachedCharsetDecoderTable) { + CharsetDecoder d = cachedCharsetDecoderTable + .get(name); + if (null == d) { + d = this.newDecoder(); + d.onMalformedInput(CodingErrorAction.REPLACE); + d.onUnmappableCharacter(CodingErrorAction.REPLACE); + cachedCharsetDecoderTable.put(name, d); + } + return d; + } + } + + /* + * ------------------------------------------------------------------- + * Methods implementing parent interface Comparable + * ------------------------------------------------------------------- + */ + + /** + * Compares this charset with the given charset. + * + * @param charset + * the given object to be compared with + * @return a negative integer if less than the given object, a positive + * integer if larger than it, or 0 if equal to it + */ + public final int compareTo(Charset charset) { + return this.canonicalName.compareToIgnoreCase(charset.canonicalName); + } + + /* + * ------------------------------------------------------------------- + * Methods overriding parent class Object + * ------------------------------------------------------------------- + */ + + /** + * Determines whether this charset equals to the given object. They are + * considered to be equal if they have the same canonical name. + * + * @param obj + * the given object to be compared with + * @return true if they have the same canonical name, otherwise false + */ + @Override + public final boolean equals(Object obj) { + if (obj instanceof Charset) { + Charset that = (Charset) obj; + return this.canonicalName.equals(that.canonicalName); + } + return false; + } + + /** + * Gets the hash code of this charset. + * + * @return the hash code of this charset + */ + @Override + public final int hashCode() { + return this.canonicalName.hashCode(); + } + + /** + * Gets a string representation of this charset. Usually this contains the + * canonical name of the charset. + * + * @return a string representation of this charset + */ + @Override + public final String toString() { + return "Charset[" + this.canonicalName + "]"; //$NON-NLS-1$//$NON-NLS-2$ + } + + /** + * Gets the system default charset from jvm. + * + * @return the default charset + */ + public static Charset defaultCharset() { + Charset defaultCharset = null; + String encoding = AccessController + .doPrivileged(new PrivilegedAction<String>() { + public String run() { + return System.getProperty("file.encoding"); //$NON-NLS-1$ + } + }); + try { + defaultCharset = Charset.forName(encoding); + } catch (UnsupportedCharsetException e) { + defaultCharset = Charset.forName("UTF-8"); //$NON-NLS-1$ + } + return defaultCharset; + } + + /** + * A comparator that ignores case. + */ + static class IgnoreCaseComparator implements Comparator<String> { + + // the singleton + private static Comparator<String> c = new IgnoreCaseComparator(); + + /* + * Default constructor. + */ + private IgnoreCaseComparator() { + // no action + } + + /* + * Gets a single instance. + */ + public static Comparator<String> getInstance() { + return c; + } + + /* + * Compares two strings ignoring case. + */ + public int compare(String s1, String s2) { + return s1.compareToIgnoreCase(s2); + } + } +} diff --git a/nio_char/src/main/java/java/nio/charset/CharsetDecoder.java b/nio_char/src/main/java/java/nio/charset/CharsetDecoder.java new file mode 100644 index 0000000..5c1be6e --- /dev/null +++ b/nio_char/src/main/java/java/nio/charset/CharsetDecoder.java @@ -0,0 +1,749 @@ +/* Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package java.nio.charset; + +import java.nio.BufferOverflowException; +import java.nio.BufferUnderflowException; +import java.nio.ByteBuffer; +import java.nio.CharBuffer; + +import org.apache.harmony.niochar.internal.nls.Messages; + +/** + * An converter that can convert bytes sequence in some charset to 16-bit + * Unicode character sequence. + * <p> + * The input byte sequence is wrapped by {@link java.nio.ByteBuffer ByteBuffer} + * and the output character sequence is {@link java.nio.CharBuffer CharBuffer}. + * A decoder instance should be used in following sequence, which is referred to + * as a decoding operation: + * <ol> + * <li>Invoking the {@link #reset() reset} method to reset the decoder if the + * decoder has been used;</li> + * <li>Invoking the {@link #decode(ByteBuffer, CharBuffer, boolean) decode} + * method until the additional input is not needed, the <code>endOfInput</code> + * parameter must be set to false, the input buffer must be filled and the + * output buffer must be flushed between invocations;</li> + * <li>Invoking the {@link #decode(ByteBuffer, CharBuffer, boolean) decode} + * method last time, and the the <code>endOfInput</code> parameter must be set + * to true</li> + * <li>Invoking the {@link #flush(CharBuffer) flush} method to flush the + * output.</li> + * </ol> + * </p> + * <p> + * The {@link #decode(ByteBuffer, CharBuffer, boolean) decode} method will + * convert as many bytes as possible, and the process won't stop except the + * input bytes has been run out of, the output buffer has been filled or some + * error has happened. A {@link CoderResult CoderResult} instance will be + * returned to indicate the stop reason, and the invoker can identify the result + * and choose further action, which can include filling the input buffer, + * flushing the output buffer, recovering from error and trying again. + * </p> + * <p> + * There are two common decoding errors. One is named as malformed and it is + * returned when the input byte sequence is illegal for current specific + * charset, the other is named as unmappable character and it is returned when a + * problem occurs mapping a legal input byte sequence to its Unicode character + * equivalent. + * </p> + * <p> + * The two errors can be handled in three ways, the default one is to report the + * error to the invoker by a {@link CoderResult CoderResult} instance, and the + * alternatives are to ignore it or to replace the erroneous input with the + * replacement string. The replacement string is "\uFFFD" by default and can be + * changed by invoking {@link #replaceWith(String) replaceWith} method. The + * invoker of this decoder can choose one way by specifying a + * {@link CodingErrorAction CodingErrorAction} instance for each error type via + * {@link #onMalformedInput(CodingErrorAction) onMalformedInput} method and + * {@link #onUnmappableCharacter(CodingErrorAction) onUnmappableCharacter} + * method. + * </p> + * <p> + * This class is abstract class and encapsulate many common operations of + * decoding process for all charsets. Decoder for specific charset should extend + * this class and need only implement + * {@link #decodeLoop(ByteBuffer, CharBuffer) decodeLoop} method for basic + * decoding loop. If a subclass maintains internal state, it should override the + * {@link #implFlush(CharBuffer) implFlush} method and + * {@link #implReset() implReset} method in addition. + * </p> + * <p> + * This class is not thread-safe. + * </p> + * + * @see java.nio.charset.Charset + * @see java.nio.charset.CharsetEncoder + */ +public abstract class CharsetDecoder { + /* + * --------------------------------------- Consts + * --------------------------------------- + */ + /* + * internal status consts + */ + private static final int INIT = 0; + + private static final int ONGOING = 1; + + private static final int END = 2; + + private static final int FLUSH = 3; + + /* + * --------------------------------------- Instance variables + * --------------------------------------- + */ + // average number of chars for one byte + private float averChars; + + // maximum number of chars for one byte + private float maxChars; + + // charset for this decoder + private Charset cs; + + // specify the action if malformed input error encountered + private CodingErrorAction malformAction; + + // specify the action if unmappable character error encountered + private CodingErrorAction unmapAction; + + // the replacement string + private String replace; + + // the current status + private int status; + + /* + * --------------------------------------- Constructor + * --------------------------------------- + */ + /** + * Construct a new <code>CharsetDecoder</code> using given + * <code>Charset</code>, average number and maximum number of characters + * created by this decoder for one input byte, and the default replacement + * string "\uFFFD". + * + * @param charset + * this decoder's <code>Charset</code>, which create this + * decoder + * @param averageCharsPerByte + * average number of characters created by this decoder for one + * input byte, must be positive + * @param maxCharsPerByte + * maximum number of characters created by this decoder for one + * input byte, must be positive + * @throws IllegalArgumentException + * if <code>averageCharsPerByte</code> or + * <code>maxCharsPerByte</code> is negative + */ + protected CharsetDecoder(Charset charset, float averageCharsPerByte, + float maxCharsPerByte) { + if (averageCharsPerByte <= 0 || maxCharsPerByte <= 0) { + // niochar.00=Characters number for one byte must be positive. + throw new IllegalArgumentException(Messages.getString("niochar.00")); //$NON-NLS-1$ + } + if (averageCharsPerByte > maxCharsPerByte) { + // niochar.01=averageCharsPerByte is greater than maxCharsPerByte + throw new IllegalArgumentException(Messages.getString("niochar.01")); //$NON-NLS-1$ + } + averChars = averageCharsPerByte; + maxChars = maxCharsPerByte; + cs = charset; + status = INIT; + malformAction = CodingErrorAction.REPORT; + unmapAction = CodingErrorAction.REPORT; + replace = "\ufffd"; //$NON-NLS-1$ + } + + /* + * --------------------------------------- Methods + * --------------------------------------- + */ + /** + * get the average number of characters created by this decoder for single + * input byte + * + * @return the average number of characters created by this decoder for + * single input byte + */ + public final float averageCharsPerByte() { + return averChars; + } + + /** + * Get the <code>Charset</code> which creates this decoder. + * + * @return the <code>Charset</code> which creates this decoder + */ + public final Charset charset() { + return cs; + } + + /** + * This is a facade method for decoding operation. + * <p> + * This method decodes the remaining byte sequence of the given byte buffer + * into a new character buffer. This method performs a complete decoding + * operation, resets at first, then decodes, and flushes at last. + * </p> + * <p> + * This method should not be invoked if another decode operation is ongoing. + * </p> + * + * @param in + * the input buffer + * @return a new <code>CharBuffer</code> containing the the characters + * produced by this decoding operation. The buffer's limit will be + * the position of last character in buffer, and the position will + * be zero + * @throws IllegalStateException + * if another decoding operation is ongoing + * @throws MalformedInputException + * if illegal input byte sequence for this charset encountered, + * and the action for malformed error is + * {@link CodingErrorAction#REPORT CodingErrorAction.REPORT} + * @throws UnmappableCharacterException + * if legal but unmappable input byte sequence for this charset + * encountered, and the action for unmappable character error is + * {@link CodingErrorAction#REPORT CodingErrorAction.REPORT}. + * Unmappable means the byte sequence at the input buffer's + * current position cannot be mapped to a Unicode character + * sequence. + * @throws CharacterCodingException + * if other exception happened during the decode operation + */ + public final CharBuffer decode(ByteBuffer in) + throws CharacterCodingException { + reset(); + int length = (int) (in.remaining() * averChars); + CharBuffer output = CharBuffer.allocate(length); + CoderResult result = null; + while (true) { + result = decode(in, output, false); + checkCoderResult(result); + if (result.isUnderflow()) { + break; + } else if (result.isOverflow()) { + output = allocateMore(output); + } + } + result = decode(in, output, true); + checkCoderResult(result); + + while (true) { + result = flush(output); + checkCoderResult(result); + if (result.isOverflow()) { + output = allocateMore(output); + } else { + break; + } + } + + output.flip(); + status = FLUSH; + return output; + } + + /* + * checks the result whether it needs to throw CharacterCodingException. + */ + private void checkCoderResult(CoderResult result) + throws CharacterCodingException { + if (result.isMalformed() && malformAction == CodingErrorAction.REPORT) { + throw new MalformedInputException(result.length()); + } else if (result.isUnmappable() + && unmapAction == CodingErrorAction.REPORT) { + throw new UnmappableCharacterException(result.length()); + } + } + + /* + * original output is full and doesn't have remaining. allocate more space + * to new CharBuffer and return it, the contents in the given buffer will be + * copied into the new buffer. + */ + private CharBuffer allocateMore(CharBuffer output) { + if (output.capacity() == 0) { + return CharBuffer.allocate(1); + } + CharBuffer result = CharBuffer.allocate(output.capacity() * 2); + output.flip(); + result.put(output); + return result; + } + + /** + * Decodes bytes starting at the current position of the given input buffer, + * and writes the equivalent character sequence into the given output buffer + * from its current position. + * <p> + * The buffers' position will be changed with the reading and writing + * operation, but their limits and marks will be kept intact. + * </p> + * <p> + * A <code>CoderResult</code> instance will be returned according to + * following rules: + * <ul> + * <li>{@link CoderResult#OVERFLOW CoderResult.OVERFLOW} indicates that + * even though not all of the input has been processed, the buffer the + * output is being written to has reached its capacity. In the event of this + * code being returned this method should be called once more with an + * <code>out</code> argument that has not already been filled.</li> + * <li>{@link CoderResult#UNDERFLOW CoderResult.UNDERFLOW} indicates that + * as many bytes as possible in the input buffer have been decoded. If there + * is no further input and no remaining bytes in the input buffer then this + * operation may be regarded as complete. Otherwise, this method should be + * called once more with additional input.</li> + * <li>A {@link CoderResult#malformedForLength(int) malformed input} result + * indicates that some malformed input error encountered, and the erroneous + * bytes start at the input buffer's position and their number can be got by + * result's {@link CoderResult#length() length}. This kind of result can be + * returned only if the malformed action is + * {@link CodingErrorAction#REPORT CodingErrorAction.REPORT}. </li> + * <li>A {@link CoderResult#unmappableForLength(int) unmappable character} + * result indicates that some unmappable character error encountered, and + * the erroneous bytes start at the input buffer's position and their number + * can be got by result's {@link CoderResult#length() length}. This kind of + * result can be returned only if the unmappable character action is + * {@link CodingErrorAction#REPORT CodingErrorAction.REPORT}. </li> + * </ul> + * </p> + * <p> + * The <code>endOfInput</code> parameter indicates that if the invoker can + * provider further input. This parameter is true if and only if the bytes + * in current input buffer are all inputs for this decoding operation. Note + * that it is common and won't cause error that the invoker sets false and + * then finds no more input available; while it may cause error that the + * invoker always sets true in several consecutive invocations so that any + * remaining input will be treated as malformed input. + * </p> + * <p> + * This method invokes + * {@link #decodeLoop(ByteBuffer, CharBuffer) decodeLoop} method to + * implement basic decode logic for specific charset. + * </p> + * + * @param in + * the input buffer + * @param out + * the output buffer + * @param endOfInput + * true if all the input characters have been provided + * @return a <code>CoderResult</code> instance which indicates the reason + * of termination + * @throws IllegalStateException + * if decoding has started or no more input is needed in this + * decoding progress. + * @throws CoderMalfunctionError + * if the {@link #decodeLoop(ByteBuffer, CharBuffer) decodeLoop} + * method threw an <code>BufferUnderflowException</code> or + * <code>BufferOverflowException</code> + */ + public final CoderResult decode(ByteBuffer in, CharBuffer out, + boolean endOfInput) { + /* + * status check + */ + if ((status == FLUSH) || (!endOfInput && status == END)) { + throw new IllegalStateException(); + } + + CoderResult result = null; + + // begin to decode + while (true) { + CodingErrorAction action = null; + try { + result = decodeLoop(in, out); + } catch (BufferOverflowException ex) { + // unexpected exception + throw new CoderMalfunctionError(ex); + } catch (BufferUnderflowException ex) { + // unexpected exception + throw new CoderMalfunctionError(ex); + } + + /* + * result handling + */ + if (result.isUnderflow()) { + int remaining = in.remaining(); + status = endOfInput ? END : ONGOING; + if (endOfInput && remaining > 0) { + result = CoderResult.malformedForLength(remaining); + in.position(in.position() + result.length()); + } else { + return result; + } + } + if (result.isOverflow()) { + return result; + } + // set coding error handle action + action = malformAction; + if (result.isUnmappable()) { + action = unmapAction; + } + // If the action is IGNORE or REPLACE, we should continue decoding. + if (action == CodingErrorAction.REPLACE) { + if (out.remaining() < replace.length()) { + return CoderResult.OVERFLOW; + } + out.put(replace); + } else { + if (action != CodingErrorAction.IGNORE) + return result; + } + if (!result.isMalformed()) { + in.position(in.position() + result.length()); + } + } + } + + /** + * Decode bytes into characters. This method is called by + * {@link #decode(ByteBuffer, CharBuffer, boolean) decode} method. + * + * This method will implement the essential decoding operation, and it won't + * stop decoding until either all the input bytes are read, the output + * buffer is filled, or some exception encountered. And then it will return + * a <code>CoderResult</code> object indicating the result of current + * decoding operation. The rules to construct the <code>CoderResult</code> + * is same as the {@link #decode(ByteBuffer, CharBuffer, boolean) decode}. + * When exception encountered in the decoding operation, most implementation + * of this method will return a relevant result object to + * {@link #decode(ByteBuffer, CharBuffer, boolean) decode} method, and some + * performance optimized implementation may handle the exception and + * implement the error action itself. + * + * The buffers are scanned from their current positions, and their positions + * will be modified accordingly, while their marks and limits will be + * intact. At most {@link ByteBuffer#remaining() in.remaining()} characters + * will be read, and {@link CharBuffer#remaining() out.remaining()} bytes + * will be written. + * + * Note that some implementation may pre-scan the input buffer and return + * <code>CoderResult.UNDERFLOW</code> until it receives sufficient input. + * + * @param in + * the input buffer + * @param out + * the output buffer + * @return a <code>CoderResult</code> instance indicating the result + */ + protected abstract CoderResult decodeLoop(ByteBuffer in, CharBuffer out); + + /** + * Get the charset detected by this decoder, this method is optional. + * <p> + * If implementing an auto-detecting charset, then this decoder returns the + * detected charset from this method when it is available. The returned + * charset will be the same for the rest of the decode operation. + * </p> + * <p> + * If insufficient bytes have been read to determine the charset, + * <code>IllegalStateException</code> will be thrown. + * </p> + * <p> + * The default implementation always throws + * <code>UnsupportedOperationException</code>, so it should be overridden + * by subclass if needed. + * </p> + * + * @return the charset detected by this decoder, or null if it is not yet + * determined + * @throws UnsupportedOperationException + * if this decoder does not implement an auto-detecting charset + * @throws IllegalStateException + * if insufficient bytes have been read to determine the charset + */ + public Charset detectedCharset() { + throw new UnsupportedOperationException(); + } + + /** + * Flush this decoder. + * + * This method will call {@link #implFlush(CharBuffer) implFlush}. Some + * decoders may need to write some characters to the output buffer when they + * have read all input bytes, subclasses can overridden + * {@link #implFlush(CharBuffer) implFlush} to perform writing action. + * + * The maximum number of written bytes won't larger than + * {@link CharBuffer#remaining() out.remaining()}. If some decoder want to + * write more bytes than output buffer's remaining spaces, then + * <code>CoderResult.OVERFLOW</code> will be returned, and this method + * must be called again with a character buffer that has more spaces. + * Otherwise this method will return <code>CoderResult.UNDERFLOW</code>, + * which means one decoding process has been completed successfully. + * + * During the flush, the output buffer's position will be changed + * accordingly, while its mark and limit will be intact. + * + * @param out + * the given output buffer + * @return <code>CoderResult.UNDERFLOW</code> or + * <code>CoderResult.OVERFLOW</code> + * @throws IllegalStateException + * if this decoder hasn't read all input bytes during one + * decoding process, which means neither after calling + * {@link #decode(ByteBuffer) decode(ByteBuffer)} nor after + * calling {@link #decode(ByteBuffer, CharBuffer, boolean) + * decode(ByteBuffer, CharBuffer, boolean)} with true value for + * the last boolean parameter + */ + public final CoderResult flush(CharBuffer out) { + if (status != END && status != INIT) { + throw new IllegalStateException(); + } + CoderResult result = implFlush(out); + if (result == CoderResult.UNDERFLOW) { + status = FLUSH; + } + return result; + } + + /** + * Flush this decoder. Default implementation does nothing and always return + * <code>CoderResult.UNDERFLOW</code>, and this method can be overridden + * if needed. + * + * @param out + * the output buffer + * @return <code>CoderResult.UNDERFLOW</code> or + * <code>CoderResult.OVERFLOW</code> + */ + protected CoderResult implFlush(CharBuffer out) { + return CoderResult.UNDERFLOW; + } + + /** + * Notify that this decoder's <code>CodingErrorAction</code> specified for + * malformed input error has been changed. Default implementation does + * nothing, and this method can be overridden if needed. + * + * @param newAction + * The new action + */ + protected void implOnMalformedInput(CodingErrorAction newAction) { + // default implementation is empty + } + + /** + * Notify that this decoder's <code>CodingErrorAction</code> specified for + * unmappable character error has been changed. Default implementation does + * nothing, and this method can be overridden if needed. + * + * @param newAction + * The new action + */ + protected void implOnUnmappableCharacter(CodingErrorAction newAction) { + // default implementation is empty + } + + /** + * Notify that this decoder's replacement has been changed. Default + * implementation does nothing, and this method can be overridden if needed. + * + * @param newReplacement + * the new replacement string + */ + protected void implReplaceWith(String newReplacement) { + // default implementation is empty + } + + /** + * Reset this decoder's charset related state. Default implementation does + * nothing, and this method can be overridden if needed. + */ + protected void implReset() { + // default implementation is empty + } + + /** + * Get if this decoder implements an auto-detecting charset. + * + * @return <code>true</code> if this decoder implements an auto-detecting + * charset + */ + public boolean isAutoDetecting() { + return false; + } + + /** + * Get if this decoder has detected a charset, this method is optional. + * <p> + * If this decoder implements an auto-detecting charset, then this method + * may start to return true during decoding operation to indicate that a + * charset has been detected in the input bytes and that the charset can be + * retrieved by invoking {@link #detectedCharset() detectedCharset} method. + * </p> + * <p> + * Note that a decoder that implements an auto-detecting charset may still + * succeed in decoding a portion of the given input even when it is unable + * to detect the charset. For this reason users should be aware that a + * <code>false</code> return value does not indicate that no decoding took + * place. + * </p> + * <p> + * The default implementation always throws an + * <code>UnsupportedOperationException</code>; it should be overridden by + * subclass if needed. + * </p> + * + * @return <code>true</code> this decoder has detected a charset + * @throws UnsupportedOperationException + * if this decoder doesn't implement an auto-detecting charset + */ + public boolean isCharsetDetected() { + throw new UnsupportedOperationException(); + } + + /** + * Gets this decoder's <code>CodingErrorAction</code> when malformed input + * occurred during decoding process. + * + * @return this decoder's <code>CodingErrorAction</code> when malformed + * input occurred during decoding process. + */ + public CodingErrorAction malformedInputAction() { + return malformAction; + } + + /** + * Get the maximum number of characters which can be created by this decoder + * for one input byte, must be positive + * + * @return the maximum number of characters which can be created by this + * decoder for one input byte, must be positive + */ + public final float maxCharsPerByte() { + return maxChars; + } + + /** + * Set this decoder's action on malformed input error. + * + * This method will call the + * {@link #implOnMalformedInput(CodingErrorAction) implOnMalformedInput} + * method with the given new action as argument. + * + * @param newAction + * the new action on malformed input error + * @return this decoder + * @throws IllegalArgumentException + * if the given newAction is null + */ + public final CharsetDecoder onMalformedInput(CodingErrorAction newAction) { + if (null == newAction) { + throw new IllegalArgumentException(); + } + malformAction = newAction; + implOnMalformedInput(newAction); + return this; + } + + /** + * Set this decoder's action on unmappable character error. + * + * This method will call the + * {@link #implOnUnmappableCharacter(CodingErrorAction) implOnUnmappableCharacter} + * method with the given new action as argument. + * + * @param newAction + * the new action on unmappable character error + * @return this decoder + * @throws IllegalArgumentException + * if the given newAction is null + */ + public final CharsetDecoder onUnmappableCharacter( + CodingErrorAction newAction) { + if (null == newAction) { + throw new IllegalArgumentException(); + } + unmapAction = newAction; + implOnUnmappableCharacter(newAction); + return this; + } + + /** + * Get the replacement string, which is never null or empty + * + * @return the replacement string, cannot be null or empty + */ + public final String replacement() { + return replace; + } + + /** + * Set new replacement value. + * + * This method first checks the given replacement's validity, then changes + * the replacement value, and at last calls + * {@link #implReplaceWith(String) implReplaceWith} method with the given + * new replacement as argument. + * + * @param newReplacement + * the replacement string, cannot be null or empty + * @return this decoder + * @throws IllegalArgumentException + * if the given replacement cannot satisfy the requirement + * mentioned above + */ + public final CharsetDecoder replaceWith(String newReplacement) { + if (null == newReplacement || newReplacement.length() == 0) { + // niochar.06=Replacement string cannot be null or empty. + throw new IllegalArgumentException(Messages.getString("niochar.06")); //$NON-NLS-1$ + } + if (newReplacement.length() > maxChars) { + // niochar.07=Replacement string's length cannot be larger than max + // characters per byte. + throw new IllegalArgumentException(Messages.getString("niochar.07")); //$NON-NLS-1$ + } + replace = newReplacement; + implReplaceWith(newReplacement); + return this; + } + + /** + * Reset this decoder. This method will reset internal status, and then call + * <code>implReset()</code> to reset any status related to specific + * charset. + * + * @return this decoder + */ + public final CharsetDecoder reset() { + status = INIT; + implReset(); + return this; + } + + /** + * Gets this decoder's <code>CodingErrorAction</code> when unmappable + * character occurred during decoding process. + * + * @return this decoder's <code>CodingErrorAction</code> when unmappable + * character occurred during decoding process. + */ + public CodingErrorAction unmappableCharacterAction() { + return unmapAction; + } +} diff --git a/nio_char/src/main/java/java/nio/charset/CharsetEncoder.java b/nio_char/src/main/java/java/nio/charset/CharsetEncoder.java new file mode 100644 index 0000000..0cb3fdc --- /dev/null +++ b/nio_char/src/main/java/java/nio/charset/CharsetEncoder.java @@ -0,0 +1,811 @@ +/* Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package java.nio.charset; + +import java.nio.BufferOverflowException; +import java.nio.BufferUnderflowException; +import java.nio.ByteBuffer; +import java.nio.CharBuffer; + +import org.apache.harmony.niochar.internal.nls.Messages; + +/** + * An converter that can convert 16-bit Unicode character sequence to byte + * sequence in some charset . + * <p> + * The input character sequence is wrapped by + * {@link java.nio.CharBuffer CharBuffer} and the output character sequence is + * {@link java.nio.ByteBuffer ByteBuffer}. A encoder instance should be used in + * following sequence, which is referred to as a encoding operation: + * <ol> + * <li>Invoking the {@link #reset() reset} method to reset the encoder if the + * encoder has been used;</li> + * <li>Invoking the {@link #encode(CharBuffer, ByteBuffer, boolean) encode} + * method until the additional input is not needed, the <code>endOfInput</code> + * parameter must be set to false, the input buffer must be filled and the + * output buffer must be flushed between invocations;</li> + * <li>Invoking the {@link #encode(CharBuffer, ByteBuffer, boolean) encode} + * method last time, and the the <code>endOfInput</code> parameter must be set + * to true</li> + * <li>Invoking the {@link #flush(ByteBuffer) flush} method to flush the + * output.</li> + * </ol> + * </p> + * <p> + * The {@link #encode(CharBuffer, ByteBuffer, boolean) encode} method will + * convert as many characters as possible, and the process won't stop except the + * input characters has been run out of, the output buffer has been filled or + * some error has happened. A {@link CoderResult CoderResult} instance will be + * returned to indicate the stop reason, and the invoker can identify the result + * and choose further action, which can include filling the input buffer, + * flushing the output buffer, recovering from error and trying again. + * </p> + * <p> + * There are two common encoding errors. One is named as malformed and it is + * returned when the input content is illegal 16-bit Unicode character sequence, + * the other is named as unmappable character and occurs when there is a problem + * mapping the input to a valid byte sequence in the specific charset. + * </p> + * <p> + * The two errors can be handled in three ways, the default one is to report the + * error to the invoker by a {@link CoderResult CoderResult} instance, and the + * alternatives are to ignore it or to replace the erroneous input with the + * replacement byte array. The replacement byte array is {(byte)'?'} by default + * and can be changed by invoking {@link #replaceWith(byte[]) replaceWith} + * method. The invoker of this encoder can choose one way by specifying a + * {@link CodingErrorAction CodingErrorAction} instance for each error type via + * {@link #onMalformedInput(CodingErrorAction) onMalformedInput} method and + * {@link #onUnmappableCharacter(CodingErrorAction) onUnmappableCharacter} + * method. + * </p> + * <p> + * This class is abstract class and encapsulate many common operations of + * encoding process for all charsets. encoder for specific charset should extend + * this class and need only implement + * {@link #encodeLoop(CharBuffer, ByteBuffer) encodeLoop} method for basic + * encoding loop. If a subclass maintains internal state, it should override the + * {@link #implFlush(ByteBuffer) implFlush} method and + * {@link #implReset() implReset} method in addition. + * </p> + * <p> + * This class is not thread-safe. + * </p> + * + * @see java.nio.charset.Charset + * @see java.nio.charset.CharsetDecoder + */ +public abstract class CharsetEncoder { + /* + * --------------------------------------- Consts + * --------------------------------------- + */ + /* + * internal status consts + */ + private static final int INIT = 0; + + private static final int ONGOING = 1; + + private static final int END = 2; + + private static final int FLUSH = 3; + + /* + * --------------------------------------- Instance variables + * --------------------------------------- + */ + // the Charset which creates this encoder + private Charset cs; + + // average bytes per character created by this encoder + private float averBytes; + + // maximum bytes per character can be created by this encoder + private float maxBytes; + + // replacement byte array + private byte[] replace; + + // internal status + private int status; + + // action for malformed input + private CodingErrorAction malformAction; + + // action for unmapped char input + private CodingErrorAction unmapAction; + + // decoder instance for this encoder's charset, used for replacement value + // checking + private CharsetDecoder decoder; + + /* + * --------------------------------------- Constructors + * --------------------------------------- + */ + + /** + * Construct a new <code>CharsetEncoder</code> using given + * <code>Charset</code>, average number and maximum number of bytes + * created by this encoder for one input character. + * + * @param cs + * this encoder's <code>Charset</code>, which create this + * encoder + * @param averageBytesPerChar + * average number of bytes created by this encoder for one input + * character, must be positive + * @param maxBytesPerChar + * maximum number of bytes which can be created by this encoder + * for one input character, must be positive + * @throws IllegalArgumentException + * if <code>maxBytesPerChar</code> or + * <code>averageBytePerChar</code> is negative + */ + protected CharsetEncoder(Charset cs, float averageBytesPerChar, + float maxBytesPerChar) { + this(cs, averageBytesPerChar, maxBytesPerChar, + new byte[] { (byte) '?' }); + } + + /** + * Construct a new <code>CharsetEncoder</code> using given + * <code>Charset</code>, replace byte array, average number and maximum + * number of bytes created by this encoder for one input character. + * + * @param cs + * the this encoder's <code>Charset</code>, which create this + * encoder + * @param averageBytesPerChar + * average number of bytes created by this encoder for single + * input character, must be positive + * @param maxBytesPerChar + * maximum number of bytes which can be created by this encoder + * for single input character, must be positive + * @param replacement + * the replacement byte array, cannot be null or empty, its + * length cannot larger than <code>maxBytesPerChar</code>, and + * must be legal replacement, which can be justified by + * {@link #isLegalReplacement(byte[]) isLegalReplacement} + * @throws IllegalArgumentException + * if any parameters are invalid + */ + protected CharsetEncoder(Charset cs, float averageBytesPerChar, + float maxBytesPerChar, byte[] replacement) { + if (averageBytesPerChar <= 0 || maxBytesPerChar <= 0) { + // niochar.02=Bytes number for one character must be positive. + throw new IllegalArgumentException(Messages.getString("niochar.02")); //$NON-NLS-1$ + } + if (averageBytesPerChar > maxBytesPerChar) { + // niochar.03=averageBytesPerChar is greater than maxBytesPerChar. + throw new IllegalArgumentException(Messages.getString("niochar.03")); //$NON-NLS-1$ + } + this.cs = cs; + averBytes = averageBytesPerChar; + maxBytes = maxBytesPerChar; + status = INIT; + malformAction = CodingErrorAction.REPORT; + unmapAction = CodingErrorAction.REPORT; + replaceWith(replacement); + } + + /* + * --------------------------------------- Methods + * --------------------------------------- + */ + /** + * get the average number of bytes created by this encoder for single input + * character + * + * @return the average number of bytes created by this encoder for single + * input character + */ + public final float averageBytesPerChar() { + return averBytes; + } + + /** + * Check if given character can be encoded by this encoder. + * + * Note that this method can change the internal status of this encoder, so + * it should not be called when another encode process is ongoing, otherwise + * it will throw <code>IllegalStateException</code>. + * + * This method can be overridden for performance improvement. + * + * @param c + * the given encoder + * @return true if given character can be encoded by this encoder + * @throws IllegalStateException + * if another encode process is ongoing so that current internal + * status is neither RESET or FLUSH + */ + public boolean canEncode(char c) { + return implCanEncode(CharBuffer.wrap(new char[] { c })); + } + + // implementation of canEncode + private boolean implCanEncode(CharBuffer cb) { + if (status == FLUSH) { + status = INIT; + } + if (status != INIT) { + // niochar.0B=Another encoding process is ongoing\! + throw new IllegalStateException(Messages.getString("niochar.0B")); //$NON-NLS-1$ + } + CodingErrorAction malformBak = malformAction; + CodingErrorAction unmapBak = unmapAction; + onMalformedInput(CodingErrorAction.REPORT); + onUnmappableCharacter(CodingErrorAction.REPORT); + boolean result = true; + try { + this.encode(cb); + } catch (CharacterCodingException e) { + result = false; + } + onMalformedInput(malformBak); + onUnmappableCharacter(unmapBak); + reset(); + return result; + } + + /** + * Check if given <code>CharSequence</code> can be encoded by this + * encoder. + * + * Note that this method can change the internal status of this encoder, so + * it should not be called when another encode process is ongoing, otherwise + * it will throw <code>IllegalStateException</code>. + * + * This method can be overridden for performance improvement. + * + * @param sequence + * the given <code>CharSequence</code> + * @return true if given <code>CharSequence</code> can be encoded by this + * encoder + * @throws IllegalStateException + * if current internal status is neither RESET or FLUSH + */ + public boolean canEncode(CharSequence sequence) { + CharBuffer cb; + if (sequence instanceof CharBuffer) { + cb = ((CharBuffer) sequence).duplicate(); + } else { + cb = CharBuffer.wrap(sequence); + } + return implCanEncode(cb); + } + + /** + * Get the <code>Charset</code> which creates this encoder. + * + * @return the <code>Charset</code> which creates this encoder + */ + public final Charset charset() { + return cs; + } + + /** + * This is a facade method for encoding operation. + * <p> + * This method encodes the remaining character sequence of the given + * character buffer into a new byte buffer. This method performs a complete + * encoding operation, resets at first, then encodes, and flushes at last. + * </p> + * <p> + * This method should not be invoked if another encode operation is ongoing. + * </p> + * + * @param in + * the input buffer + * @return a new <code>ByteBuffer</code> containing the the bytes produced + * by this encoding operation. The buffer's limit will be the + * position of last byte in buffer, and the position will be zero + * @throws IllegalStateException + * if another encoding operation is ongoing + * @throws MalformedInputException + * if illegal input character sequence for this charset + * encountered, and the action for malformed error is + * {@link CodingErrorAction#REPORT CodingErrorAction.REPORT} + * @throws UnmappableCharacterException + * if legal but unmappable input character sequence for this + * charset encountered, and the action for unmappable character + * error is + * {@link CodingErrorAction#REPORT CodingErrorAction.REPORT}. + * Unmappable means the Unicode character sequence at the input + * buffer's current position cannot be mapped to a equivalent + * byte sequence. + * @throws CharacterCodingException + * if other exception happened during the encode operation + */ + public final ByteBuffer encode(CharBuffer in) + throws CharacterCodingException { + if (in.remaining() == 0) { + return ByteBuffer.allocate(0); + } + reset(); + int length = (int) (in.remaining() * averBytes); + ByteBuffer output = ByteBuffer.allocate(length); + CoderResult result = null; + while (true) { + result = encode(in, output, false); + checkCoderResult(result); + if (result.isUnderflow()) { + break; + } else if (result.isOverflow()) { + output = allocateMore(output); + } + } + result = encode(in, output, true); + checkCoderResult(result); + + while (true) { + result = flush(output); + checkCoderResult(result); + if (result.isOverflow()) { + output = allocateMore(output); + } else { + break; + } + } + + output.flip(); + if (result.isMalformed()) { + throw new MalformedInputException(result.length()); + } else if (result.isUnmappable()) { + throw new UnmappableCharacterException(result.length()); + } + status = FLUSH; + return output; + } + + /* + * checks the result whether it needs to throw CharacterCodingException. + */ + private void checkCoderResult(CoderResult result) + throws CharacterCodingException { + if (result.isMalformed() && malformAction == CodingErrorAction.REPORT) { + throw new MalformedInputException(result.length()); + } else if (result.isUnmappable() + && unmapAction == CodingErrorAction.REPORT) { + throw new UnmappableCharacterException(result.length()); + } + } + + // allocate more spaces to the given ByteBuffer + private ByteBuffer allocateMore(ByteBuffer output) { + if (output.capacity() == 0) { + return ByteBuffer.allocate(1); + } + ByteBuffer result = ByteBuffer.allocate(output.capacity() * 2); + output.flip(); + result.put(output); + return result; + } + + /** + * Encodes characters starting at the current position of the given input + * buffer, and writes the equivalent byte sequence into the given output + * buffer from its current position. + * <p> + * The buffers' position will be changed with the reading and writing + * operation, but their limits and marks will be kept intact. + * </p> + * <p> + * A <code>CoderResult</code> instance will be returned according to + * following rules: + * <ul> + * <li>A {@link CoderResult#malformedForLength(int) malformed input} result + * indicates that some malformed input error encountered, and the erroneous + * characters start at the input buffer's position and their number can be + * got by result's {@link CoderResult#length() length}. This kind of result + * can be returned only if the malformed action is + * {@link CodingErrorAction#REPORT CodingErrorAction.REPORT}. </li> + * <li>{@link CoderResult#UNDERFLOW CoderResult.UNDERFLOW} indicates that + * as many characters as possible in the input buffer has been encoded. If + * there is no further input and no characters left in the input buffer then + * this task is complete. If this is not the case then the client should + * call this method again supplying some more input characters.</li> + * <li>{@link CoderResult#OVERFLOW CoderResult.OVERFLOW} indicates that the + * output buffer has been filled, while there are still some characters + * remaining in the input buffer. This method should be invoked again with a + * non-full output buffer </li> + * <li>A {@link CoderResult#unmappableForLength(int) unmappable character} + * result indicates that some unmappable character error was encountered, + * and the erroneous characters start at the input buffer's position and + * their number can be got by result's {@link CoderResult#length() length}. + * This kind of result can be returned only on + * {@link CodingErrorAction#REPORT CodingErrorAction.REPORT}. </li> + * </ul> + * </p> + * <p> + * The <code>endOfInput</code> parameter indicates that if the invoker can + * provider further input. This parameter is true if and only if the + * characters in current input buffer are all inputs for this encoding + * operation. Note that it is common and won't cause error that the invoker + * sets false and then finds no more input available; while it may cause + * error that the invoker always sets true in several consecutive + * invocations so that any remaining input will be treated as malformed + * input. + * </p> + * <p> + * This method invokes + * {@link #encodeLoop(CharBuffer, ByteBuffer) encodeLoop} method to + * implement basic encode logic for specific charset. + * </p> + * + * @param in + * the input buffer + * @param out + * the output buffer + * @param endOfInput + * true if all the input characters have been provided + * @return a <code>CoderResult</code> instance indicating the result + * @throws IllegalStateException + * if the encoding operation has already started or no more + * input needed in this encoding progress. + * @throws CoderMalfunctionError + * If the {@link #encodeLoop(CharBuffer, ByteBuffer) encodeLoop} + * method threw an <code>BufferUnderflowException</code> or + * <code>BufferUnderflowException</code> + */ + public final CoderResult encode(CharBuffer in, ByteBuffer out, + boolean endOfInput) { + if ((status == FLUSH) || (!endOfInput && status == END)) { + throw new IllegalStateException(); + } + + CoderResult result; + while (true) { + try { + result = encodeLoop(in, out); + } catch (BufferOverflowException e) { + throw new CoderMalfunctionError(e); + } catch (BufferUnderflowException e) { + throw new CoderMalfunctionError(e); + } + if (result.isUnderflow()) { + int remaining = in.remaining(); + status = endOfInput ? END : ONGOING; + if (endOfInput && remaining > 0) { + result = CoderResult.malformedForLength(remaining); + } else { + return result; + } + } + if (result.isOverflow()) { + status = endOfInput ? END : ONGOING; + return result; + } + CodingErrorAction action = malformAction; + if (result.isUnmappable()) { + action = unmapAction; + } + // If the action is IGNORE or REPLACE, we should continue + // encoding. + if (action == CodingErrorAction.REPLACE) { + if (out.remaining() < replace.length) { + return CoderResult.OVERFLOW; + } + out.put(replace); + } else { + if (action != CodingErrorAction.IGNORE) { + return result; + } + } + in.position(in.position() + result.length()); + } + } + + /** + * Encode characters into bytes. This method is called by + * {@link #encode(CharBuffer, ByteBuffer, boolean) encode}. + * + * This method will implement the essential encoding operation, and it won't + * stop encoding until either all the input characters are read, the output + * buffer is filled, or some exception encountered. And then it will return + * a <code>CoderResult</code> object indicating the result of current + * encoding operation. The rules to construct the <code>CoderResult</code> + * is same as the {@link #encode(CharBuffer, ByteBuffer, boolean) encode}. + * When exception encountered in the encoding operation, most implementation + * of this method will return a relevant result object to + * {@link #encode(CharBuffer, ByteBuffer, boolean) encode} method, and some + * performance optimized implementation may handle the exception and + * implement the error action itself. + * + * The buffers are scanned from their current positions, and their positions + * will be modified accordingly, while their marks and limits will be + * intact. At most {@link CharBuffer#remaining() in.remaining()} characters + * will be read, and {@link ByteBuffer#remaining() out.remaining()} bytes + * will be written. + * + * Note that some implementation may pre-scan the input buffer and return + * <code>CoderResult.UNDERFLOW</code> until it receives sufficient input. + * + * @param in + * the input buffer + * @param out + * the output buffer + * @return a <code>CoderResult</code> instance indicating the result + */ + protected abstract CoderResult encodeLoop(CharBuffer in, ByteBuffer out); + + /** + * Flush this encoder. + * + * This method will call {@link #implFlush(ByteBuffer) implFlush}. Some + * encoders may need to write some bytes to the output buffer when they have + * read all input characters, subclasses can overridden + * {@link #implFlush(ByteBuffer) implFlush} to perform writing action. + * + * The maximum number of written bytes won't larger than + * {@link ByteBuffer#remaining() out.remaining()}. If some encoder want to + * write more bytes than output buffer's remaining spaces, then + * <code>CoderResult.OVERFLOW</code> will be returned, and this method + * must be called again with a byte buffer has more spaces. Otherwise this + * method will return <code>CoderResult.UNDERFLOW</code>, which means one + * encoding process has been completed successfully. + * + * During the flush, the output buffer's position will be changed + * accordingly, while its mark and limit will be intact. + * + * @param out + * the given output buffer + * @return <code>CoderResult.UNDERFLOW</code> or + * <code>CoderResult.OVERFLOW</code> + * @throws IllegalStateException + * if this encoder hasn't read all input characters during one + * encoding process, which means neither after calling + * {@link #encode(CharBuffer) encode(CharBuffer)} nor after + * calling {@link #encode(CharBuffer, ByteBuffer, boolean) + * encode(CharBuffer, ByteBuffer, boolean)} with true value for + * the last boolean parameter + */ + public final CoderResult flush(ByteBuffer out) { + if (status != END && status != INIT) { + throw new IllegalStateException(); + } + CoderResult result = implFlush(out); + if (result == CoderResult.UNDERFLOW) { + status = FLUSH; + } + return result; + } + + /** + * Flush this encoder. Default implementation does nothing and always return + * <code>CoderResult.UNDERFLOW</code>, and this method can be overridden + * if needed. + * + * @param out + * the output buffer + * @return <code>CoderResult.UNDERFLOW</code> or + * <code>CoderResult.OVERFLOW</code> + */ + protected CoderResult implFlush(ByteBuffer out) { + return CoderResult.UNDERFLOW; + } + + /** + * Notify that this encoder's <code>CodingErrorAction</code> specified for + * malformed input error has been changed. Default implementation does + * nothing, and this method can be overridden if needed. + * + * @param newAction + * The new action + */ + protected void implOnMalformedInput(CodingErrorAction newAction) { + // default implementation is empty + } + + /** + * Notify that this encoder's <code>CodingErrorAction</code> specified for + * unmappable character error has been changed. Default implementation does + * nothing, and this method can be overridden if needed. + * + * @param newAction + * The new action + */ + protected void implOnUnmappableCharacter(CodingErrorAction newAction) { + // default implementation is empty + } + + /** + * Notify that this encoder's replacement has been changed. Default + * implementation does nothing, and this method can be overridden if needed. + * + * @param newReplacement + * the new replacement string + */ + protected void implReplaceWith(byte[] newReplacement) { + // default implementation is empty + } + + /** + * Reset this encoder's charset related state. Default implementation does + * nothing, and this method can be overridden if needed. + */ + protected void implReset() { + // default implementation is empty + } + + /** + * Check if the given argument is legal as this encoder's replacement byte + * array. + * + * The given byte array is legal if and only if it can be decode into + * sixteen bits Unicode characters. + * + * This method can be overridden for performance improvement. + * + * @param repl + * the given byte array to be checked + * @return true if the the given argument is legal as this encoder's + * replacement byte array. + */ + public boolean isLegalReplacement(byte[] repl) { + if (decoder == null) { + decoder = cs.newDecoder(); + } + + CodingErrorAction malform = decoder.malformedInputAction(); + CodingErrorAction unmap = decoder.unmappableCharacterAction(); + decoder.onMalformedInput(CodingErrorAction.REPORT); + decoder.onUnmappableCharacter(CodingErrorAction.REPORT); + ByteBuffer in = ByteBuffer.wrap(repl); + CharBuffer out = CharBuffer.allocate((int) (repl.length * decoder + .maxCharsPerByte())); + CoderResult result = decoder.decode(in, out, true); + decoder.onMalformedInput(malform); + decoder.onUnmappableCharacter(unmap); + return !result.isError(); + } + + /** + * Gets this encoder's <code>CodingErrorAction</code> when malformed input + * occurred during encoding process. + * + * @return this encoder's <code>CodingErrorAction</code> when malformed + * input occurred during encoding process. + */ + public CodingErrorAction malformedInputAction() { + return malformAction; + } + + /** + * Get the maximum number of bytes which can be created by this encoder for + * one input character, must be positive + * + * @return the maximum number of bytes which can be created by this encoder + * for one input character, must be positive + */ + public final float maxBytesPerChar() { + return maxBytes; + } + + /** + * Set this encoder's action on malformed input error. + * + * This method will call the + * {@link #implOnMalformedInput(CodingErrorAction) implOnMalformedInput} + * method with the given new action as argument. + * + * @param newAction + * the new action on malformed input error + * @return this encoder + * @throws IllegalArgumentException + * if the given newAction is null + */ + public final CharsetEncoder onMalformedInput(CodingErrorAction newAction) { + if (null == newAction) { + // niochar.0C=Action on malformed input error cannot be null\! + throw new IllegalArgumentException(Messages.getString("niochar.0C")); //$NON-NLS-1$ + } + malformAction = newAction; + implOnMalformedInput(newAction); + return this; + } + + /** + * Set this encoder's action on unmappable character error. + * + * This method will call the + * {@link #implOnUnmappableCharacter(CodingErrorAction) implOnUnmappableCharacter} + * method with the given new action as argument. + * + * @param newAction + * the new action on unmappable character error + * @return this encoder + * @throws IllegalArgumentException + * if the given newAction is null + */ + public final CharsetEncoder onUnmappableCharacter( + CodingErrorAction newAction) { + if (null == newAction) { + // niochar.0D=Action on unmappable character error cannot be null\! + throw new IllegalArgumentException(Messages.getString("niochar.0D")); //$NON-NLS-1$ + } + unmapAction = newAction; + implOnUnmappableCharacter(newAction); + return this; + } + + /** + * Get the replacement byte array, which is never null or empty, and it is + * legal + * + * @return the replacement byte array, cannot be null or empty, and it is + * legal + */ + public final byte[] replacement() { + return replace; + } + + /** + * Set new replacement value. + * + * This method first checks the given replacement's validity, then changes + * the replacement value, and at last calls + * {@link #implReplaceWith(byte[]) implReplaceWith} method with the given + * new replacement as argument. + * + * @param replacement + * the replacement byte array, cannot be null or empty, its + * length cannot larger than <code>maxBytesPerChar</code>, and + * must be legal replacement, which can be justified by + * <code>isLegalReplacement(byte[] repl)</code> + * @return this encoder + * @throws IllegalArgumentException + * if the given replacement cannot satisfy the requirement + * mentioned above + */ + public final CharsetEncoder replaceWith(byte[] replacement) { + if (null == replacement || 0 == replacement.length + || maxBytes < replacement.length + || !isLegalReplacement(replacement)) { + // niochar.0E=Replacement is illegal + throw new IllegalArgumentException(Messages.getString("niochar.0E")); //$NON-NLS-1$ + } + replace = replacement; + implReplaceWith(replacement); + return this; + } + + /** + * Reset this encoder. This method will reset internal status, and then call + * <code>implReset()</code> to reset any status related to specific + * charset. + * + * @return this encoder + */ + public final CharsetEncoder reset() { + status = INIT; + implReset(); + return this; + } + + /** + * Gets this encoder's <code>CodingErrorAction</code> when unmappable + * character occurred during encoding process. + * + * @return this encoder's <code>CodingErrorAction</code> when unmappable + * character occurred during encoding process. + */ + public CodingErrorAction unmappableCharacterAction() { + return unmapAction; + } +} diff --git a/nio_char/src/main/java/java/nio/charset/CoderMalfunctionError.java b/nio_char/src/main/java/java/nio/charset/CoderMalfunctionError.java new file mode 100644 index 0000000..c2400cb --- /dev/null +++ b/nio_char/src/main/java/java/nio/charset/CoderMalfunctionError.java @@ -0,0 +1,40 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package java.nio.charset; + +/** + * Errors thrown when the encoder/decoder is malfunctioning. + */ +public class CoderMalfunctionError extends Error { + + /* + * This constant is used during deserialization to check the J2SE version + * which created the serialized object. + */ + private static final long serialVersionUID = -1151412348057794301L; + + /** + * Constructs an instance of this error. + * + * @param ex + * the original exception thrown by the encoder/decoder + */ + public CoderMalfunctionError(Exception ex) { + super(ex); + } +} diff --git a/nio_char/src/main/java/java/nio/charset/CoderResult.java b/nio_char/src/main/java/java/nio/charset/CoderResult.java new file mode 100644 index 0000000..c222394 --- /dev/null +++ b/nio_char/src/main/java/java/nio/charset/CoderResult.java @@ -0,0 +1,297 @@ +/* Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package java.nio.charset; + +import java.nio.BufferOverflowException; +import java.nio.BufferUnderflowException; +import java.util.WeakHashMap; + +import org.apache.harmony.niochar.internal.nls.Messages; + +/** + * Used to indicate the result of encoding/decoding. There are four types of + * results: + * <ol> + * <li>UNDERFLOW indicates all input has been processed, or more input is + * required. It is represented by the unique object + * <code>CoderResult.UNDERFLOW</code>. + * <li>OVERFLOW indicates insufficient output buffer. It is represented by the + * unique object <code>CoderResult.OVERFLOW</code>. + * <li>A malformed-input error indicates an unrecognizable sequence of input + * units has been encountered. Get an instance of this type of result by calling + * <code>CoderResult.malformedForLength(int)</code> with the length of the + * malformed-input. + * <li>An unmappable-character error indicates a sequence of input units can + * not be mapped to the output charset. Get an instance of this type of result + * by calling <code>CoderResult.unmappableForLength(int)</code> with the input + * sequence size indicating the identity of the unmappable character. + * </ol> + * + */ +public class CoderResult { + + // indicating underflow error type + private static final int TYPE_UNDERFLOW = 1; + + // indicating overflow error type + private static final int TYPE_OVERFLOW = 2; + + // indicating malformed-input error type + private static final int TYPE_MALFORMED_INPUT = 3; + + // indicating unmappable character error type + private static final int TYPE_UNMAPPABLE_CHAR = 4; + + /** + * Result object indicating that there is insufficient data in the + * encoding/decoding buffer or that additional data is required. + */ + public static final CoderResult UNDERFLOW = new CoderResult(TYPE_UNDERFLOW, + 0); + + /** + * Result object used to signify that the out buffer does not have enough + * space available in it to store the result of the encoding/decoding. + */ + public static final CoderResult OVERFLOW = new CoderResult(TYPE_OVERFLOW, 0); + + /* + * Stores unique result objects for each malformed-input error of a certain + * length + */ + private static WeakHashMap<Integer, CoderResult> _malformedErrors = new WeakHashMap<Integer, CoderResult>(); + + /* + * Stores unique result objects for each unmappable-character error of a + * certain length + */ + private static WeakHashMap<Integer, CoderResult> _unmappableErrors = new WeakHashMap<Integer, CoderResult>(); + + // the type this result + private final int type; + + // the length of the erroneous input + private final int length; + + /** + * Construct a <code>CoderResult</code> object with its text description. + * + * @param type + * the type of this result + * @param length + * the length of the erroneous input + */ + private CoderResult(int type, int length) { + super(); + this.type = type; + this.length = length; + } + + /** + * Gets a <code>CoderResult</code> object indicating a malformed-input + * error. + * + * @param length + * the length of the malformed-input + * @return a <code>CoderResult</code> object indicating a malformed-input + * error + * @throws IllegalArgumentException + * If <code>length</code> is non-positive. + */ + public static synchronized CoderResult malformedForLength(int length) + throws IllegalArgumentException { + if (length > 0) { + Integer key = Integer.valueOf(length); + synchronized (_malformedErrors) { + CoderResult r = _malformedErrors.get(key); + if (null == r) { + r = new CoderResult(TYPE_MALFORMED_INPUT, length); + _malformedErrors.put(key, r); + } + return r; + } + } + // niochar.08=The length must be positive: {0}. + throw new IllegalArgumentException(Messages.getString( + "niochar.08", length)); //$NON-NLS-1$ + } + + /** + * Gets a <code>CoderResult</code> object indicating an unmappable + * character error. + * + * @param length + * the length of the input unit sequence denoting the unmappable + * character + * @return a <code>CoderResult</code> object indicating an unmappable + * character error + * @throws IllegalArgumentException + * If <code>length</code> is non-positive. + */ + public static synchronized CoderResult unmappableForLength(int length) + throws IllegalArgumentException { + if (length > 0) { + Integer key = Integer.valueOf(length); + synchronized (_unmappableErrors) { + CoderResult r = _unmappableErrors.get(key); + if (null == r) { + r = new CoderResult(TYPE_UNMAPPABLE_CHAR, length); + _unmappableErrors.put(key, r); + } + return r; + } + } + // niochar.08=The length must be positive: {0}. + throw new IllegalArgumentException(Messages.getString( + "niochar.08", length)); //$NON-NLS-1$ + } + + /** + * Returns true if this result is an underflow condition. + * + * @return true if an underflow, otherwise false + */ + public boolean isUnderflow() { + return this.type == TYPE_UNDERFLOW; + } + + /** + * Returns true if this result represents a malformed-input error or an + * unmappable-character error. + * + * @return true if a malformed-input error or an unmappable-character error, + * otherwise false + */ + public boolean isError() { + return this.type == TYPE_MALFORMED_INPUT + || this.type == TYPE_UNMAPPABLE_CHAR; + } + + /** + * Returns true if this result represents a malformed-input error. + * + * @return true if a malformed-input error, otherwise false + */ + public boolean isMalformed() { + return this.type == TYPE_MALFORMED_INPUT; + } + + /** + * Returns true if this result is an overflow condition. + * + * @return true if an overflow, otherwise false + */ + public boolean isOverflow() { + return this.type == TYPE_OVERFLOW; + } + + /** + * Returns true if this result represents an unmappable-character error. + * + * @return true if an unmappable-character error, otherwise false + */ + public boolean isUnmappable() { + return this.type == TYPE_UNMAPPABLE_CHAR; + } + + /** + * Gets the length of the erroneous input. The length is only meaningful to + * a malformed-input error or an unmappble character error. + * + * @return the length, as an integer, of this object's erroneous input + * @throws UnsupportedOperationException + * If this result is an overflow or underflow. + */ + public int length() throws UnsupportedOperationException { + if (this.type == TYPE_MALFORMED_INPUT + || this.type == TYPE_UNMAPPABLE_CHAR) { + return this.length; + } + // niochar.09=The length of the erroneous input is only meaningful to + // a malformed-input error or an unmappble character error + throw new UnsupportedOperationException(Messages + .getString("niochar.09")); //$NON-NLS-1$ + } + + /** + * Throws an exception corresponding to this coder result. + * + * @throws BufferUnderflowException + * If an underflow. + * @throws BufferOverflowException + * If an overflow. + * @throws UnmappableCharacterException + * If an unmappable-character error. + * @throws MalformedInputException + * If a malformed-input error. + * @throws CharacterCodingException + * The default exception. + */ + public void throwException() throws BufferUnderflowException, + BufferOverflowException, UnmappableCharacterException, + MalformedInputException, CharacterCodingException { + switch (this.type) { + case TYPE_UNDERFLOW: + throw new BufferUnderflowException(); + case TYPE_OVERFLOW: + throw new BufferOverflowException(); + case TYPE_UNMAPPABLE_CHAR: + throw new UnmappableCharacterException(this.length); + case TYPE_MALFORMED_INPUT: + throw new MalformedInputException(this.length); + default: + throw new CharacterCodingException(); + } + } + + /* + * ------------------------------------------------------------------- + * Methods overriding parent class Object + * ------------------------------------------------------------------- + */ + + /** + * Returns a text description of this result. + * + * @return a text description of this result + */ + public String toString() { + String dsc = null; + switch (this.type) { + case TYPE_UNDERFLOW: + dsc = "UNDERFLOW error"; //$NON-NLS-1$ + break; + case TYPE_OVERFLOW: + dsc = "OVERFLOW error"; //$NON-NLS-1$ + break; + case TYPE_UNMAPPABLE_CHAR: + dsc = "Unmappable-character error with erroneous input length " //$NON-NLS-1$ + + this.length; + break; + case TYPE_MALFORMED_INPUT: + dsc = "Malformed-input error with erroneous input length " //$NON-NLS-1$ + + this.length; + break; + default: + dsc = ""; //$NON-NLS-1$ + break; + } + return "CoderResult[" + dsc + "]"; //$NON-NLS-1$ //$NON-NLS-2$ + + } + +} diff --git a/nio_char/src/main/java/java/nio/charset/CodingErrorAction.java b/nio_char/src/main/java/java/nio/charset/CodingErrorAction.java new file mode 100644 index 0000000..e258c71 --- /dev/null +++ b/nio_char/src/main/java/java/nio/charset/CodingErrorAction.java @@ -0,0 +1,64 @@ +/* Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package java.nio.charset; + +/** + * Used to indicate what kind of actions to take in case of encoding/decoding + * errors. Currently three actions are defined, namely, IGNORE, REPLACE and + * REPORT. + */ +public class CodingErrorAction { + + /** + * Indicating the action to ignore any errors. + */ + public static final CodingErrorAction IGNORE = new CodingErrorAction( + "IGNORE"); //$NON-NLS-1$ + + /** + * Indicating the action to fill in the output with a replacement character + * when malformed input or an unmappable character is encountered. + */ + public static final CodingErrorAction REPLACE = new CodingErrorAction( + "REPLACE"); //$NON-NLS-1$ + + /** + * Indicating the action to report the encountered error in an appropriate + * manner, for example, throw an exception or return an informative result. + */ + public static final CodingErrorAction REPORT = new CodingErrorAction( + "REPORT"); //$NON-NLS-1$ + + // The name of this action + private String action; + + /* + * Can't instantiate outside. + */ + private CodingErrorAction(String action) { + this.action = action; + } + + /** + * Returns a text description of this action indication.. + * + * @return a text description of this action indication. + */ + public String toString() { + return "Action: " + this.action; //$NON-NLS-1$ + } +} diff --git a/nio_char/src/main/java/java/nio/charset/IllegalCharsetNameException.java b/nio_char/src/main/java/java/nio/charset/IllegalCharsetNameException.java new file mode 100644 index 0000000..fae1269 --- /dev/null +++ b/nio_char/src/main/java/java/nio/charset/IllegalCharsetNameException.java @@ -0,0 +1,56 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package java.nio.charset; + +import org.apache.harmony.niochar.internal.nls.Messages; + +/** + * Thrown when an illegal charset name is encountered. + */ +public class IllegalCharsetNameException extends IllegalArgumentException { + + /* + * This constant is used during deserialization to check the J2SE version + * which created the serialized object. + */ + private static final long serialVersionUID = 1457525358470002989L; + + // The illegal charset name + private String charsetName; + + /** + * Constructs an instance of this exception with the supplied charset name. + * + * @param charset + * the encountered illegal charset name + */ + public IllegalCharsetNameException(String charset) { + // niochar.0F=The illegal charset name is "{0}". + super(Messages.getString("niochar.0F", charset)); //$NON-NLS-1$ + this.charsetName = charset; + } + + /** + * Gets the encountered illegal charset name. + * + * @return the encountered illegal charset name + */ + public String getCharsetName() { + return this.charsetName; + } +} diff --git a/nio_char/src/main/java/java/nio/charset/MalformedInputException.java b/nio_char/src/main/java/java/nio/charset/MalformedInputException.java new file mode 100644 index 0000000..17b0005 --- /dev/null +++ b/nio_char/src/main/java/java/nio/charset/MalformedInputException.java @@ -0,0 +1,65 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package java.nio.charset; + +import org.apache.harmony.niochar.internal.nls.Messages; + +/** + * Thrown when a malformed input is encountered, for example, a byte sequence is + * illegal for the given charset. + */ +public class MalformedInputException extends CharacterCodingException { + + /* + * This constant is used during deserialization to check the J2SE version + * which created the serialized object. + */ + private static final long serialVersionUID = -3438823399834806194L; + + // the length of the malformed input + private int inputLength; + + /** + * Constructs an instance of this exception. + * + * @param length + * the length of the malformed input + */ + public MalformedInputException(int length) { + this.inputLength = length; + } + + /** + * Gets the length of the malformed input. + * + * @return the length of the malformed input + */ + public int getInputLength() { + return this.inputLength; + } + + /** + * Gets a message describing this exception. + * + * @return a message describing this exception + */ + public String getMessage() { + // niochar.05=Malformed input length is {0}. + return Messages.getString("niochar.05", this.inputLength); //$NON-NLS-1$ + } +} diff --git a/nio_char/src/main/java/java/nio/charset/UnmappableCharacterException.java b/nio_char/src/main/java/java/nio/charset/UnmappableCharacterException.java new file mode 100644 index 0000000..1c66c52 --- /dev/null +++ b/nio_char/src/main/java/java/nio/charset/UnmappableCharacterException.java @@ -0,0 +1,64 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package java.nio.charset; + +import org.apache.harmony.niochar.internal.nls.Messages; + +/** + * Thrown when an unmappable character for the given charset is encountered. + */ +public class UnmappableCharacterException extends CharacterCodingException { + + /* + * This constant is used during deserialization to check the J2SE version + * which created the serialized object. + */ + private static final long serialVersionUID = -7026962371537706123L; + + // The length of the unmappable character + private int inputLength; + + /** + * Constructs an instance of this exception. + * + * @param length + * the length of the unmappable character + */ + public UnmappableCharacterException(int length) { + this.inputLength = length; + } + + /** + * Gets the length of the unmappable character. + * + * @return the length of the unmappable character + */ + public int getInputLength() { + return this.inputLength; + } + + /** + * Gets a message describing this exception. + * + * @return a message describing this exception + */ + public String getMessage() { + // niochar.0A=The unmappable character length is {0}. + return Messages.getString("niochar.0A", this.inputLength); //$NON-NLS-1$ + } +} diff --git a/nio_char/src/main/java/java/nio/charset/UnsupportedCharsetException.java b/nio_char/src/main/java/java/nio/charset/UnsupportedCharsetException.java new file mode 100644 index 0000000..1b55397 --- /dev/null +++ b/nio_char/src/main/java/java/nio/charset/UnsupportedCharsetException.java @@ -0,0 +1,56 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package java.nio.charset; + +import org.apache.harmony.niochar.internal.nls.Messages; + +/** + * Thrown when an unsupported charset name is encountered. + */ +public class UnsupportedCharsetException extends IllegalArgumentException { + + /* + * This constant is used during deserialization to check the J2SE version + * which created the serialized object. + */ + private static final long serialVersionUID = 1490765524727386367L; + + // the unsupported charset name + private String charsetName; + + /** + * Constructs an instance of this exception with the supplied charset name. + * + * @param charset + * the encountered unsupported charset name + */ + public UnsupportedCharsetException(String charset) { + // niochar.04=The unsupported charset name is "{0}". + super(Messages.getString("niochar.04", charset)); //$NON-NLS-1$ + this.charsetName = charset; + } + + /** + * Gets the encountered unsupported charset name. + * + * @return the encountered unsupported charset name + */ + public String getCharsetName() { + return this.charsetName; + } +} diff --git a/nio_char/src/main/java/java/nio/charset/package.html b/nio_char/src/main/java/java/nio/charset/package.html new file mode 100644 index 0000000..104fd50 --- /dev/null +++ b/nio_char/src/main/java/java/nio/charset/package.html @@ -0,0 +1,14 @@ +<html> + <body> + <p> + This package allows translating between bytes and different + character sets. + </p> + <p> + An encoder translates characters into bytes and a decoder can + translate a byte stream into characters. With a charset you can create a + de-/encoder pair that can be used to translate a byte stream. With the + service provider package it is possible to use your own charsets. + </p> + </body> +</html> diff --git a/nio_char/src/main/java/java/nio/charset/spi/CharsetProvider.java b/nio_char/src/main/java/java/nio/charset/spi/CharsetProvider.java new file mode 100644 index 0000000..8a470d7 --- /dev/null +++ b/nio_char/src/main/java/java/nio/charset/spi/CharsetProvider.java @@ -0,0 +1,61 @@ +/* Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package java.nio.charset.spi; + +import java.nio.charset.Charset; +import java.util.Iterator; + +/** + * The service provider class for character sets. + */ +public abstract class CharsetProvider { + + // The permission required to construct a new provider. + private static final RuntimePermission CONSTRUCT_PERM = new RuntimePermission( + "charsetProvider"); //$NON-NLS-1$ + + /** + * Constructor for subclassing with concrete types. + * + * @throws SecurityException + * if there is a security manager installed that does not permit + * the runtime permission labeled "charsetProvider". + */ + protected CharsetProvider() { + SecurityManager securityManager = System.getSecurityManager(); + if (securityManager != null) + securityManager.checkPermission(CONSTRUCT_PERM); + } + + /** + * Returns an iterator over all the available charsets. + * + * @return the iterator. + */ + public abstract Iterator<Charset> charsets(); + + /** + * Returns the named charset. + * <p> + * If the charset is unavailable the method returns <code>null</code>. + * + * @param charsetName + * the canonical or alias name of a character set. + * @return the charset, or <code>null</code> if unavailable. + */ + public abstract Charset charsetForName(String charsetName); +} diff --git a/nio_char/src/main/java/java/nio/charset/spi/package.html b/nio_char/src/main/java/java/nio/charset/spi/package.html new file mode 100644 index 0000000..a68c6d3 --- /dev/null +++ b/nio_char/src/main/java/java/nio/charset/spi/package.html @@ -0,0 +1,7 @@ +<html> + <body> + <p> + Service-provider classe for nio charset. + </p> + </body> +</html> diff --git a/nio_char/src/main/java/org/apache/harmony/niochar/internal/nls/Messages.java b/nio_char/src/main/java/org/apache/harmony/niochar/internal/nls/Messages.java new file mode 100644 index 0000000..3368864 --- /dev/null +++ b/nio_char/src/main/java/org/apache/harmony/niochar/internal/nls/Messages.java @@ -0,0 +1,125 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * THE FILE HAS BEEN AUTOGENERATED BY MSGTOOL TOOL. + * All changes made to this file manually will be overwritten + * if this tool runs again. Better make changes in the template file. + */ + +package org.apache.harmony.niochar.internal.nls; + +import org.apache.harmony.luni.util.MsgHelp; + +/** + * This class retrieves strings from a resource bundle and returns them, + * formatting them with MessageFormat when required. + * <p> + * It is used by the system classes to provide national language support, by + * looking up messages in the <code> + * org.apache.harmony.niochar.internal.nls.messages + * </code> + * resource bundle. Note that if this file is not available, or an invalid key + * is looked up, or resource bundle support is not available, the key itself + * will be returned as the associated message. This means that the <em>KEY</em> + * should a reasonable human-readable (english) string. + * + */ +public class Messages { + + private static final String sResource = + "org.apache.harmony.niochar.internal.nls.messages"; //$NON-NLS-1$ + + /** + * Retrieves a message which has no arguments. + * + * @param msg + * String the key to look up. + * @return String the message for that key in the system message bundle. + */ + static public String getString(String msg) { + return MsgHelp.getString(sResource, msg); + } + + /** + * Retrieves a message which takes 1 argument. + * + * @param msg + * String the key to look up. + * @param arg + * Object the object to insert in the formatted output. + * @return String the message for that key in the system message bundle. + */ + static public String getString(String msg, Object arg) { + return getString(msg, new Object[] { arg }); + } + + /** + * Retrieves a message which takes 1 integer argument. + * + * @param msg + * String the key to look up. + * @param arg + * int the integer to insert in the formatted output. + * @return String the message for that key in the system message bundle. + */ + static public String getString(String msg, int arg) { + return getString(msg, new Object[] { Integer.toString(arg) }); + } + + /** + * Retrieves a message which takes 1 character argument. + * + * @param msg + * String the key to look up. + * @param arg + * char the character to insert in the formatted output. + * @return String the message for that key in the system message bundle. + */ + static public String getString(String msg, char arg) { + return getString(msg, new Object[] { String.valueOf(arg) }); + } + + /** + * Retrieves a message which takes 2 arguments. + * + * @param msg + * String the key to look up. + * @param arg1 + * Object an object to insert in the formatted output. + * @param arg2 + * Object another object to insert in the formatted output. + * @return String the message for that key in the system message bundle. + */ + static public String getString(String msg, Object arg1, Object arg2) { + return getString(msg, new Object[] { arg1, arg2 }); + } + + /** + * Retrieves a message which takes several arguments. + * + * @param msg + * String the key to look up. + * @param args + * Object[] the objects to insert in the formatted output. + * @return String the message for that key in the system message bundle. + */ + static public String getString(String msg, Object[] args) { + return MsgHelp.getString(sResource, msg, args); + } +} + diff --git a/nio_char/src/main/java/org/apache/harmony/niochar/internal/nls/messages.properties b/nio_char/src/main/java/org/apache/harmony/niochar/internal/nls/messages.properties new file mode 100644 index 0000000..6737e2b --- /dev/null +++ b/nio_char/src/main/java/org/apache/harmony/niochar/internal/nls/messages.properties @@ -0,0 +1,32 @@ +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# messages for EN locale +niochar.00=Characters number for one byte must be positive. +niochar.01=averageCharsPerByte is greater than maxCharsPerByte +niochar.02=Bytes number for one character must be positive. +niochar.03=averageBytesPerChar is greater than maxBytesPerChar. +niochar.04=The unsupported charset name is "{0}". +niochar.05=Malformed input length is {0}. +niochar.06=Replacement string cannot be null or empty. +niochar.07=Replacement string's length cannot be larger than max characters per byte. +niochar.08=The length must be positive: {0}. +niochar.09=The length of the erroneous input is only meaningful to a malformed-input error or an unmappble character error +niochar.0A=The unmappable character length is {0}. +niochar.0B=Another encoding process is ongoing\! +niochar.0C=Action on malformed input error cannot be null\! +niochar.0D=Action on unmappable character error cannot be null\! +niochar.0E=Replacement is illegal +niochar.0F=The illegal charset name is "{0}". diff --git a/nio_char/src/test/java/org/apache/harmony/nio_char/tests/java/nio/charset/ASCIICharsetEncoderTest.java b/nio_char/src/test/java/org/apache/harmony/nio_char/tests/java/nio/charset/ASCIICharsetEncoderTest.java new file mode 100644 index 0000000..01385f2 --- /dev/null +++ b/nio_char/src/test/java/org/apache/harmony/nio_char/tests/java/nio/charset/ASCIICharsetEncoderTest.java @@ -0,0 +1,451 @@ +/* Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.harmony.nio_char.tests.java.nio.charset; + +import java.nio.ByteBuffer; +import java.nio.CharBuffer; +import java.nio.charset.CharacterCodingException; +import java.nio.charset.Charset; +import java.nio.charset.CharsetEncoder; +import java.nio.charset.CoderResult; +import java.nio.charset.CodingErrorAction; +import java.nio.charset.MalformedInputException; +import java.nio.charset.UnmappableCharacterException; + +import junit.framework.TestCase; + +public class ASCIICharsetEncoderTest extends TestCase { + + // charset for ascii + private static final Charset cs = Charset.forName("ascii"); + private static final CharsetEncoder encoder = cs.newEncoder(); + private static final int MAXCODEPOINT = 0x7F; + /* + * @see CharsetEncoderTest#setUp() + */ + protected void setUp() throws Exception { + } + + /* + * @see CharsetEncoderTest#tearDown() + */ + protected void tearDown() throws Exception { + } + + public void testCanEncodeCharSequence() { + // normal case for ascCS + assertTrue(encoder.canEncode("\u0077")); + assertFalse(encoder.canEncode("\uc2a3")); + assertFalse(encoder.canEncode("\ud800\udc00")); + try { + encoder.canEncode(null); + } catch (NullPointerException e) { + } + assertTrue(encoder.canEncode("")); + } + + public void testCanEncodeSurrogate () { + assertFalse(encoder.canEncode('\ud800')); + assertFalse(encoder.canEncode("\udc00")); + } + + public void testCanEncodechar() throws CharacterCodingException { + assertTrue(encoder.canEncode('\u0077')); + assertFalse(encoder.canEncode('\uc2a3')); + } + + public void testSpecificDefaultValue() { + assertEquals(1.0, encoder.averageBytesPerChar(), 0.0); + assertEquals(1.0, encoder.maxBytesPerChar(), 0.0); + } + + public void testMultiStepEncode() throws CharacterCodingException { + encoder.onMalformedInput(CodingErrorAction.REPORT); + encoder.onUnmappableCharacter(CodingErrorAction.REPORT); + try { + encoder.encode(CharBuffer.wrap("\ud800\udc00")); + fail("should unmappable"); + } catch (UnmappableCharacterException e) { + } + encoder.reset(); + ByteBuffer out = ByteBuffer.allocate(10); + assertTrue(encoder.encode(CharBuffer.wrap("\ud800"), out, true) + .isMalformed()); + encoder.flush(out); + encoder.reset(); + out = ByteBuffer.allocate(10); + assertSame(CoderResult.UNDERFLOW, encoder.encode(CharBuffer + .wrap("\ud800"), out, false)); + assertTrue(encoder.encode(CharBuffer.wrap("\udc00"), out, true) + .isMalformed()); + } + + public void testEncodeMapping() throws CharacterCodingException { + encoder.reset(); + + for (int i =0; i <= MAXCODEPOINT; i++) { + char[] chars = Character.toChars(i); + CharBuffer cb = CharBuffer.wrap(chars); + ByteBuffer bb = encoder.encode(cb); + assertEquals(i, bb.get(0)); + } + + CharBuffer cb = CharBuffer.wrap("\u0080"); + try { + encoder.encode(cb); + } catch (UnmappableCharacterException e) { + //expected + } + + cb = CharBuffer.wrap("\ud800"); + try { + encoder.encode(cb); + } catch (MalformedInputException e) { + //expected + } + + ByteBuffer bb = ByteBuffer.allocate(0x10); + cb = CharBuffer.wrap("A"); + encoder.reset(); + encoder.encode(cb, bb, false); + try { + encoder.encode(cb); + } catch (IllegalStateException e) { + //expected + } + } + + public void testInternalState() { + CharBuffer in = CharBuffer.wrap("A"); + ByteBuffer out = ByteBuffer.allocate(0x10); + + //normal encoding process + encoder.reset(); + encoder.encode(in, out, false); + in = CharBuffer.wrap("B"); + encoder.encode(in, out, true); + encoder.flush(out); + } + + //reset could be called at any time + public void testInternalState_Reset() { + CharsetEncoder newEncoder = cs.newEncoder(); + //Init - > reset + newEncoder.reset(); + + //reset - > reset + newEncoder.reset(); + + //encoding - >reset + { + CharBuffer in = CharBuffer.wrap("A"); + ByteBuffer out = ByteBuffer.allocate(0x10); + newEncoder.encode(in, out, false); + newEncoder.reset(); + } + + //encoding end -> reset + { + CharBuffer in = CharBuffer.wrap("A"); + ByteBuffer out = ByteBuffer.allocate(0x10); + newEncoder.encode(in, out, true); + newEncoder.reset(); + } + //flused -> reset + { + CharBuffer in = CharBuffer.wrap("A"); + ByteBuffer out = ByteBuffer.allocate(0x10); + newEncoder.encode(in, out, true); + newEncoder.flush(out); + newEncoder.reset(); + } + } + + public void testInternalState_Encoding() { + CharsetEncoder newEncoder = cs.newEncoder(); + //Init - > encoding + { + CharBuffer in = CharBuffer.wrap("A"); + ByteBuffer out = ByteBuffer.allocate(0x10); + newEncoder.encode(in, out, false); + } + + //reset - > encoding + { + CharBuffer in = CharBuffer.wrap("A"); + ByteBuffer out = ByteBuffer.allocate(0x10); + newEncoder.reset(); + newEncoder.encode(in, out, false); + } + //reset - > encoding - > encoding + { + newEncoder.reset(); + CharBuffer in = CharBuffer.wrap("A"); + ByteBuffer out = ByteBuffer.allocate(0x10); + newEncoder.encode(in, out, false); + in = CharBuffer.wrap("BC"); + newEncoder.encode(in, out, false); + } + + //encoding_end - > encoding + { + newEncoder.reset(); + CharBuffer in = CharBuffer.wrap("A"); + ByteBuffer out = ByteBuffer.allocate(0x10); + newEncoder.encode(in, out, true); + in = CharBuffer.wrap("BC"); + try { + newEncoder.encode(in, out, false); + fail("Should throw IllegalStateException"); + } catch (IllegalStateException e) { + //expected + } + } + //flushed - > encoding + { + newEncoder.reset(); + CharBuffer in = CharBuffer.wrap("A"); + ByteBuffer out = ByteBuffer.allocate(0x10); + newEncoder.encode(in, out, true); + newEncoder.flush(out); + in = CharBuffer.wrap("BC"); + try { + newEncoder.encode(in, out, false); + fail("Should throw IllegalStateException"); + } catch (IllegalStateException e) { + //expected + } + } + } + + public void testInternalState_Encoding_END() { + CharsetEncoder newEncoder = cs.newEncoder(); + + //Init - >encoding_end + { + CharBuffer in = CharBuffer.wrap("A"); + ByteBuffer out = ByteBuffer.allocate(0x10); + newEncoder.encode(in, out, true); + } + + //Reset -> encoding_end + { + CharBuffer in = CharBuffer.wrap("A"); + ByteBuffer out = ByteBuffer.allocate(0x10); + newEncoder.reset(); + newEncoder.encode(in, out, true); + } + + //encoding -> encoding_end + { + newEncoder.reset(); + CharBuffer in = CharBuffer.wrap("A"); + ByteBuffer out = ByteBuffer.allocate(0x10); + newEncoder.encode(in, out, false); + in = CharBuffer.wrap("BC"); + newEncoder.encode(in, out, true); + } + + //Reset -> encoding_end + { + newEncoder.reset(); + CharBuffer in = CharBuffer.wrap("A"); + ByteBuffer out = ByteBuffer.allocate(0x10); + newEncoder.encode(in, out, true); + in = CharBuffer.wrap("BC"); + newEncoder.encode(in, out, true); + } + + //Flushed -> encoding_end + { + newEncoder.reset(); + CharBuffer in = CharBuffer.wrap("A"); + ByteBuffer out = ByteBuffer.allocate(0x10); + newEncoder.encode(in, out, true); + newEncoder.flush(out); + in = CharBuffer.wrap("BC"); + try { + newEncoder.encode(in, out, true); + fail("Should throw IllegalStateException"); + } catch (IllegalStateException e) { + //expected + } + } + } + + public void testInternalState_Flushed() { + CharsetEncoder newEncoder = cs.newEncoder(); + + //init -> flushed + { + ByteBuffer out = ByteBuffer.allocate(0x10); + newEncoder.flush(out); + } + + //reset - > flushed + { + newEncoder.reset(); + CharBuffer in = CharBuffer.wrap("A"); + ByteBuffer out = ByteBuffer.allocate(0x10); + newEncoder.encode(in, out, true); + newEncoder.reset(); + newEncoder.flush(out); + } + + //encoding - > flushed + { + newEncoder.reset(); + CharBuffer in = CharBuffer.wrap("A"); + ByteBuffer out = ByteBuffer.allocate(0x10); + newEncoder.encode(in, out, false); + try { + + newEncoder.flush(out); + fail("Should throw IllegalStateException"); + } catch (IllegalStateException e) { + // expected + } + } + + //encoding_end -> flushed + { + newEncoder.reset(); + CharBuffer in = CharBuffer.wrap("A"); + ByteBuffer out = ByteBuffer.allocate(0x10); + newEncoder.encode(in, out, true); + newEncoder.flush(out); + } + + //flushd - > flushed + { + newEncoder.reset(); + CharBuffer in = CharBuffer.wrap("A"); + ByteBuffer out = ByteBuffer.allocate(0x10); + newEncoder.encode(in, out, true); + newEncoder.flush(out); + try { + newEncoder.flush(out); + fail("Should throw IllegalStateException"); + } catch (IllegalStateException e) { + // expected + } + } + } + + public void testInternalState_Encode() throws CharacterCodingException { + CharsetEncoder newEncoder = cs.newEncoder(); + //Init - > encode + { + CharBuffer in = CharBuffer.wrap("A"); + newEncoder.encode(in); + } + + //Reset - > encode + { + newEncoder.reset(); + CharBuffer in = CharBuffer.wrap("A"); + newEncoder.encode(in); + } + + //Encoding -> encode + { + newEncoder.reset(); + CharBuffer in = CharBuffer.wrap("A"); + ByteBuffer out = ByteBuffer.allocate(0x10); + newEncoder.encode(in, out, false); + in = CharBuffer.wrap("BC"); + newEncoder.encode(in); + } + + //Encoding_end -> encode + { + newEncoder.reset(); + CharBuffer in = CharBuffer.wrap("A"); + ByteBuffer out = ByteBuffer.allocate(0x10); + newEncoder.encode(in, out, true); + in = CharBuffer.wrap("BC"); + newEncoder.encode(in); + } + + //Flushed -> reset + { + newEncoder.reset(); + CharBuffer in = CharBuffer.wrap("A"); + ByteBuffer out = ByteBuffer.allocate(0x10); + newEncoder.encode(in, out, true); + in = CharBuffer.wrap("BC"); + newEncoder.flush(out); + out = newEncoder.encode(in); + } + } + + public void testInternalState_from_Encode() throws CharacterCodingException { + CharsetEncoder newEncoder = cs.newEncoder(); + + //Encode -> Reset + { + CharBuffer in = CharBuffer.wrap("A"); + newEncoder.encode(in); + newEncoder.reset(); + } + + // Encode -> encoding + { + CharBuffer in = CharBuffer.wrap("A"); + newEncoder.encode(in); + ByteBuffer out = ByteBuffer.allocate(0x10); + try { + newEncoder.encode(in, out, false); + fail("Should throw IllegalStateException"); + } catch (IllegalStateException e) { + // expected + } + } + + //Encode -> Encoding_end + { + CharBuffer in = CharBuffer.wrap("A"); + newEncoder.encode(in); + ByteBuffer out = ByteBuffer.allocate(0x10); + try { + newEncoder.encode(in, out, true); + fail("Should throw IllegalStateException"); + } catch (IllegalStateException e) { + // expected + } + } + //Encode -> Flushed + { + CharBuffer in = CharBuffer.wrap("A"); + ByteBuffer out = newEncoder.encode(in); + try { + newEncoder.flush(out); + fail("Should throw IllegalStateException"); + } catch (IllegalStateException e) { + // expected + } + } + + //Encode - > encode + { + CharBuffer in = CharBuffer.wrap("A"); + newEncoder.encode(in); + in = CharBuffer.wrap("BC"); + newEncoder.encode(in); + } + } +} diff --git a/nio_char/src/test/java/org/apache/harmony/nio_char/tests/java/nio/charset/AllTests.java b/nio_char/src/test/java/org/apache/harmony/nio_char/tests/java/nio/charset/AllTests.java new file mode 100644 index 0000000..8dd485c --- /dev/null +++ b/nio_char/src/test/java/org/apache/harmony/nio_char/tests/java/nio/charset/AllTests.java @@ -0,0 +1,44 @@ +/* Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.harmony.nio_char.tests.java.nio.charset; + +import junit.framework.Test; +import junit.framework.TestSuite; + +public class AllTests { + + public static void main(String[] args) { + junit.textui.TestRunner.run(AllTests.suite()); + } + + public static Test suite() { + TestSuite suite = new TestSuite( + "Test for org.apache.harmony.nio_char.tests.java.nio.charset"); + //$JUnit-BEGIN$ + suite.addTestSuite(CharacterCodingExceptionTest.class); + suite.addTestSuite(CharsetEncoderTest.class); + suite.addTestSuite(CharsetTest.class); + suite.addTestSuite(CharsetDecoderTest.class); + suite.addTestSuite(MalformedInputExceptionTest.class); + suite.addTestSuite(IllegalCharsetNameExceptionTest.class); + suite.addTestSuite(UnmappableCharacterExceptionTest.class); + suite.addTestSuite(UnsupportedCharsetExceptionTest.class); + suite.addTestSuite(CoderMalfunctionErrorTest.class); + //$JUnit-END$ + return suite; + } +} diff --git a/nio_char/src/test/java/org/apache/harmony/nio_char/tests/java/nio/charset/CharacterCodingExceptionTest.java b/nio_char/src/test/java/org/apache/harmony/nio_char/tests/java/nio/charset/CharacterCodingExceptionTest.java new file mode 100644 index 0000000..f1bab63 --- /dev/null +++ b/nio_char/src/test/java/org/apache/harmony/nio_char/tests/java/nio/charset/CharacterCodingExceptionTest.java @@ -0,0 +1,53 @@ +/* Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.harmony.nio_char.tests.java.nio.charset; + +import java.io.IOException; +import java.nio.charset.CharacterCodingException; + +import junit.framework.TestCase; + +import org.apache.harmony.testframework.serialization.SerializationTest; + +/** + * Test CharacterCodingException + */ +public class CharacterCodingExceptionTest extends TestCase { + + public void testConstructor() { + CharacterCodingException ex = new CharacterCodingException(); + assertTrue(ex instanceof IOException); + assertNull(ex.getCause()); + assertNull(ex.getMessage()); + } + + /** + * @tests serialization/deserialization compatibility. + */ + public void testSerializationSelf() throws Exception { + + SerializationTest.verifySelf(new CharacterCodingException()); + } + + /** + * @tests serialization/deserialization compatibility with RI. + */ + public void testSerializationCompatibility() throws Exception { + SerializationTest.verifyGolden(this, new CharacterCodingException()); + + } +} diff --git a/nio_char/src/test/java/org/apache/harmony/nio_char/tests/java/nio/charset/CharsetDecoderTest.java b/nio_char/src/test/java/org/apache/harmony/nio_char/tests/java/nio/charset/CharsetDecoderTest.java new file mode 100644 index 0000000..8617669 --- /dev/null +++ b/nio_char/src/test/java/org/apache/harmony/nio_char/tests/java/nio/charset/CharsetDecoderTest.java @@ -0,0 +1,231 @@ +/* Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.harmony.nio_char.tests.java.nio.charset; + +import java.nio.BufferOverflowException; +import java.nio.ByteBuffer; +import java.nio.CharBuffer; +import java.nio.charset.CharacterCodingException; +import java.nio.charset.Charset; +import java.nio.charset.CharsetDecoder; +import java.nio.charset.CharsetEncoder; +import java.nio.charset.CoderMalfunctionError; +import java.nio.charset.CoderResult; +import java.nio.charset.CodingErrorAction; +import java.nio.charset.MalformedInputException; + +import junit.framework.TestCase; + +public class CharsetDecoderTest extends TestCase { + + /** + * @tests java.nio.charset.CharsetDecoder.CharsetDecoder(Charset, float, + * float) + */ + public void test_ConstructorLjava_nio_charset_CharsetFF() { + // Regression for HARMONY-142 + try { + Charset cs = Charset.forName("UTF-8"); //$NON-NLS-1$ + new MockCharsetDecoderForHarmony142(cs, 1.1f, 1); + fail("Assert 0: Should throw IllegalArgumentException."); //$NON-NLS-1$ + } catch (IllegalArgumentException e) { + // expected + } + } + + /* + * MockCharsetDecoderForHarmony142: for constructor test + */ + static class MockCharsetDecoderForHarmony142 extends CharsetDecoder { + protected MockCharsetDecoderForHarmony142(Charset cs, + float averageBytesPerChar, float maxBytesPerChar) { + super(cs, averageBytesPerChar, maxBytesPerChar); + } + + protected CoderResult decodeLoop(ByteBuffer in, CharBuffer out) { + return null; + } + } + + /** + * @tests java.nio.charset.CharsetDecoder#decode(java.nio.ByteBuffer) + */ + public void test_decode() throws CharacterCodingException { + // Regression for HARMONY-33 +// ByteBuffer bb = ByteBuffer.allocate(1); +// bb.put(0, (byte) 77); +// CharsetDecoder decoder = Charset.forName("UTF-16").newDecoder(); +// decoder.onMalformedInput(CodingErrorAction.REPLACE); +// decoder.onUnmappableCharacter(CodingErrorAction.REPLACE); +// decoder.decode(bb); + + // Regression for HARMONY-67 +// byte[] b = new byte[] { (byte) 1 }; +// ByteBuffer buf = ByteBuffer.wrap(b); +// CharBuffer charbuf = Charset.forName("UTF-16").decode(buf); +// assertEquals("Assert 0: charset UTF-16", 1, charbuf.length()); +// +// charbuf = Charset.forName("UTF-16BE").decode(buf); +// assertEquals("Assert 1: charset UTF-16BE", 0, charbuf.length()); +// +// charbuf = Charset.forName("UTF-16LE").decode(buf); +// assertEquals("Assert 2: charset UTF16LE", 0, charbuf.length()); + + // Regression for HARMONY-99 + CharsetDecoder decoder2 = Charset.forName("UTF-16").newDecoder(); + decoder2.onMalformedInput(CodingErrorAction.REPORT); + decoder2.onUnmappableCharacter(CodingErrorAction.REPORT); + ByteBuffer in = ByteBuffer.wrap(new byte[] { 109, 97, 109 }); + try { + decoder2.decode(in); + fail("Assert 3: MalformedInputException should have thrown"); + } catch (MalformedInputException e) { + //expected + } + } + + /* + * Test malfunction decode(ByteBuffer) + */ + public void test_decodeLjava_nio_ByteBuffer() throws Exception { + MockMalfunctionCharset cs1 = new MockMalfunctionCharset( + "Harmony-124-1", null); //$NON-NLS-1$ + try { + cs1.newDecoder().onMalformedInput(CodingErrorAction.REPLACE) + .onUnmappableCharacter(CodingErrorAction.REPLACE).decode( + ByteBuffer.wrap(new byte[] { 0x00, 0x11 })); + fail("Assert 0: should throw CoderMalfunctionError"); // NON-NLS-1$ + } catch (CoderMalfunctionError e) { + // expected + } + + MockMalfunctionCharset cs2 = new MockMalfunctionCharset( + "Harmony-124-2", null); //$NON-NLS-1$ + try { + cs2.decode(ByteBuffer.wrap(new byte[] { 0x00, 0x11 })); + fail("Assert 1: Charset.decode should throw CoderMalfunctionError"); // NON-NLS-1 + } catch (CoderMalfunctionError e) { + // expected + } + } + + /* + * Mock charset class with malfunction decode & encode. + */ + static final class MockMalfunctionCharset extends Charset { + + public MockMalfunctionCharset(String canonicalName, String[] aliases) { + super(canonicalName, aliases); + } + + public boolean contains(Charset cs) { + return false; + } + + public CharsetDecoder newDecoder() { + return new MockMalfunctionDecoder(this); + } + + public CharsetEncoder newEncoder() { + return new MockMalfunctionEncoder(this); + } + } + + /* + * Mock decoder. decodeLoop always throws unexpected exception. + */ + static class MockMalfunctionDecoder extends java.nio.charset.CharsetDecoder { + + public MockMalfunctionDecoder(Charset cs) { + super(cs, 1, 10); + } + + protected CoderResult decodeLoop(ByteBuffer in, CharBuffer out) { + throw new BufferOverflowException(); + } + } + + /* + * Mock encoder. encodeLoop always throws unexpected exception. + */ + static class MockMalfunctionEncoder extends java.nio.charset.CharsetEncoder { + + public MockMalfunctionEncoder(Charset cs) { + super(cs, 1, 3, new byte[] { (byte) '?' }); + } + + protected CoderResult encodeLoop(CharBuffer in, ByteBuffer out) { + throw new BufferOverflowException(); + } + } + + /* + * Test the method decode(ByteBuffer) . + */ + public void testDecodeLjava_nio_ByteBuffer_ReplaceOverflow() + throws Exception { + String replaceString = "a"; + Charset cs = Charset.forName("UTF-8"); + MockMalformedDecoder decoder = new MockMalformedDecoder(cs); + decoder.onMalformedInput(CodingErrorAction.REPLACE); + decoder.replaceWith(replaceString); + CharBuffer out = CharBuffer.allocate(1); + // MockMalformedDecoder treats the second byte '0x38' as malformed, + // but "out" doesn't have enough space for replace string. + ByteBuffer in = ByteBuffer.wrap(new byte[] { 0x45, 0x38, 0x45, 0x45 }); + CoderResult result = decoder.decode(in, out, false); + assertTrue(result.isOverflow()); + + // allocate enough space for "out" + out = CharBuffer.allocate(10); + // replace string should be put into "out" firstly, + // and then decode "in". + result = decoder.decode(in, out, true); + out.flip(); + assertTrue(result.isUnderflow()); + assertEquals("bb", out.toString()); + } + + /* + * Mock decoder. It treats byte whose value is less than "0x40" as + * malformed. + */ + static class MockMalformedDecoder extends java.nio.charset.CharsetDecoder { + + public MockMalformedDecoder(Charset cs) { + super(cs, 1, 10); + } + + /* + * It treats byte whose value is less than "0x40" as malformed. + * Otherwise, it's decoded as 'b'. + */ + protected CoderResult decodeLoop(ByteBuffer in, CharBuffer out) { + while (in.hasRemaining()) { + byte b = in.get(); + if (b < 0x40) { + return CoderResult.malformedForLength(1); + } + if (!out.hasRemaining()) { + return CoderResult.OVERFLOW; + } + out.put((char) 'b'); + } + return CoderResult.UNDERFLOW; + } + } +} diff --git a/nio_char/src/test/java/org/apache/harmony/nio_char/tests/java/nio/charset/CharsetEncoderTest.java b/nio_char/src/test/java/org/apache/harmony/nio_char/tests/java/nio/charset/CharsetEncoderTest.java new file mode 100644 index 0000000..fce2a1a --- /dev/null +++ b/nio_char/src/test/java/org/apache/harmony/nio_char/tests/java/nio/charset/CharsetEncoderTest.java @@ -0,0 +1,169 @@ +/* Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.harmony.nio_char.tests.java.nio.charset; + +import java.nio.BufferOverflowException; +import java.nio.ByteBuffer; +import java.nio.CharBuffer; +import java.nio.charset.Charset; +import java.nio.charset.CharsetDecoder; +import java.nio.charset.CharsetEncoder; +import java.nio.charset.CoderMalfunctionError; +import java.nio.charset.CoderResult; + +import junit.framework.TestCase; + +public class CharsetEncoderTest extends TestCase { + + /** + * @tests java.nio.charset.CharsetEncoder.CharsetEncoder( + * java.nio.charset.Charset, float, float) + */ + public void test_ConstructorLjava_nio_charset_CharsetFF() { + // Regression for HARMONY-141 + try { + Charset cs = Charset.forName("UTF-8"); //$NON-NLS-1$ + new MockCharsetEncoderForHarmony141(cs, 1.1f, 1); + fail("Assert 0: Should throw IllegalArgumentException."); //$NON-NLS-1$ + } catch (IllegalArgumentException e) { + // expected + } + + try { + Charset cs = Charset.forName("ISO8859-1"); //$NON-NLS-1$ + new MockCharsetEncoderForHarmony141(cs, 1.1f, 1, + new byte[] { 0x1a }); + fail("Assert 1: Should throw IllegalArgumentException."); //$NON-NLS-1$ + } catch (IllegalArgumentException e) { + // expected + } + } + + /** + * @tests java.nio.charset.CharsetEncoder.CharsetEncoder( + * java.nio.charset.Charset, float, float) + */ + public void test_ConstructorLjava_nio_charset_CharsetNull() { + // Regression for HARMONY-491 + CharsetEncoder ech = new MockCharsetEncoderForHarmony491(null, 1, 1); + assertNull(ech.charset()); + } + + /** + * Helper for constructor tests + */ + + public static class MockCharsetEncoderForHarmony141 extends CharsetEncoder { + + protected MockCharsetEncoderForHarmony141(Charset cs, + float averageBytesPerChar, float maxBytesPerChar) { + super(cs, averageBytesPerChar, maxBytesPerChar); + } + + public MockCharsetEncoderForHarmony141(Charset cs, + float averageBytesPerChar, float maxBytesPerChar, + byte[] replacement) { + super(cs, averageBytesPerChar, maxBytesPerChar, replacement); + } + + protected CoderResult encodeLoop(CharBuffer in, ByteBuffer out) { + return null; + } + } + + public static class MockCharsetEncoderForHarmony491 extends CharsetEncoder { + + public MockCharsetEncoderForHarmony491(Charset arg0, float arg1, + float arg2) { + super(arg0, arg1, arg2); + } + + protected CoderResult encodeLoop(CharBuffer arg0, ByteBuffer arg1) { + return null; + } + + public boolean isLegalReplacement(byte[] arg0) { + return true; + } + } + + /* + * Test malfunction encode(CharBuffer) + */ + public void test_EncodeLjava_nio_CharBuffer() throws Exception { + MockMalfunctionCharset cs = new MockMalfunctionCharset("mock", null); + try { + cs.encode(CharBuffer.wrap("AB")); + fail("should throw CoderMalfunctionError");// NON-NLS-1$ + } catch (CoderMalfunctionError e) { + // expected + } + } + + /* + * Mock charset class with malfunction decode & encode. + */ + static final class MockMalfunctionCharset extends Charset { + + public MockMalfunctionCharset(String canonicalName, String[] aliases) { + super(canonicalName, aliases); + } + + public boolean contains(Charset cs) { + return false; + } + + public CharsetDecoder newDecoder() { + return Charset.forName("UTF-8").newDecoder(); + } + + public CharsetEncoder newEncoder() { + return new MockMalfunctionEncoder(this); + } + } + + /* + * Mock encoder. encodeLoop always throws unexpected exception. + */ + static class MockMalfunctionEncoder extends java.nio.charset.CharsetEncoder { + + public MockMalfunctionEncoder(Charset cs) { + super(cs, 1, 3, new byte[] { (byte) '?' }); + } + + protected CoderResult encodeLoop(CharBuffer in, ByteBuffer out) { + throw new BufferOverflowException(); + } + } + + /* + * Test reserve bytes encode(CharBuffer,ByteBuffer,boolean) + */ + public void test_EncodeLjava_nio_CharBufferLjava_nio_ByteBufferB() { + CharsetEncoder encoder = Charset.forName("utf-8").newEncoder(); + CharBuffer in1 = CharBuffer.wrap("\ud800"); + CharBuffer in2 = CharBuffer.wrap("\udc00"); + ByteBuffer out = ByteBuffer.allocate(4); + encoder.reset(); + CoderResult result = encoder.encode(in1, out, false); + assertEquals(4, out.remaining()); + assertTrue(result.isUnderflow()); + result = encoder.encode(in2, out, true); + assertEquals(4, out.remaining()); + assertTrue(result.isMalformed()); + } +} diff --git a/nio_char/src/test/java/org/apache/harmony/nio_char/tests/java/nio/charset/CharsetTest.java b/nio_char/src/test/java/org/apache/harmony/nio_char/tests/java/nio/charset/CharsetTest.java new file mode 100644 index 0000000..0c46cd9 --- /dev/null +++ b/nio_char/src/test/java/org/apache/harmony/nio_char/tests/java/nio/charset/CharsetTest.java @@ -0,0 +1,292 @@ +/* Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.harmony.nio_char.tests.java.nio.charset; + +import java.nio.ByteBuffer; +import java.nio.CharBuffer; +import java.nio.charset.Charset; +import java.nio.charset.CharsetDecoder; +import java.nio.charset.CharsetEncoder; +import java.nio.charset.CoderResult; +import java.nio.charset.IllegalCharsetNameException; +import java.util.HashSet; +import java.util.Iterator; +import java.util.Properties; +import java.util.Set; + +import junit.framework.TestCase; + +public class CharsetTest extends TestCase { + + // Will contain names of charsets registered with IANA + Set knownRegisteredCharsets = new HashSet(); + + // Will contain names of charsets not known to be registered with IANA + Set unknownRegisteredCharsets = new HashSet(); + + /** + * JUnit set-up method + */ + public void setUp() { + // Populate the known charset vars + Set names = Charset.availableCharsets().keySet(); + for (Iterator nameItr = names.iterator(); nameItr.hasNext();) { + String name = (String) nameItr.next(); + if (name.toLowerCase().startsWith("x-")) + unknownRegisteredCharsets.add(name); + else + knownRegisteredCharsets.add(name); + } + } + + /** + * @tests java.nio.charset.Charset#isRegistered() + */ + public void test_isRegistered() { + // Regression for HARMONY-45 + for (Iterator nameItr = knownRegisteredCharsets.iterator(); nameItr.hasNext();) { + String name = (String) nameItr.next(); + assertTrue("Assert 0: isRegistered() failed for " + name, + Charset.forName(name).isRegistered()); + } + for (Iterator nameItr = unknownRegisteredCharsets.iterator(); nameItr.hasNext();) { + String name = (String) nameItr.next(); + assertFalse("Assert 0: isRegistered() failed for " + name, + Charset.forName(name).isRegistered()); + } + } + + /** + * @tests java.nio.charset.Charset#isSupported(String) + */ + public void testIsSupported_EmptyString() { + // Regression for HARMONY-113 + try { + Charset.isSupported(""); + fail("Assert 0: Should throw IllegalCharsetNameException"); + } catch (IllegalCharsetNameException e) { + // Expected + } + } + + /** + * @tests java.nio.charset.Charset#defaultCharset() + */ + public void test_defaultCharset() { + String charsetName = null; + String defaultCharsetName = null; + String oldDefaultEncoding = System.getProperty("file.encoding"); + try { + // Normal behavior + charsetName = "UTF-8"; //$NON-NLS-1$ + System.setProperty("file.encoding", charsetName);//$NON-NLS-1$ + defaultCharsetName = Charset.defaultCharset().name(); + assertEquals(charsetName, defaultCharsetName); + + charsetName = "ISO-8859-1"; //$NON-NLS-1$ + System.setProperty("file.encoding", charsetName);//$NON-NLS-1$ + defaultCharsetName = Charset.defaultCharset().name(); + assertEquals(charsetName, defaultCharsetName); + + // Unsupported behavior + charsetName = "IMPOSSIBLE-8"; //$NON-NLS-1$ + System.setProperty("file.encoding", charsetName);//$NON-NLS-1$ + defaultCharsetName = Charset.defaultCharset().name(); + assertEquals("UTF-8", defaultCharsetName); + + // Null behavior + try { + Properties currentProps = System.getProperties(); + currentProps.remove("file.encoding");//$NON-NLS-1$ + Charset.defaultCharset().name(); + fail("Should throw illegal IllegalArgumentException");//$NON-NLS-1$ + } catch (IllegalArgumentException e) { + // expected + } + + // IllegalCharsetName behavior + try { + charsetName = "IMP~~OSSIBLE-8"; //$NON-NLS-1$ + System.setProperty("file.encoding", charsetName);//$NON-NLS-1$ + Charset.defaultCharset().name(); + fail("Should throw IllegalCharsetNameException");//$NON-NLS-1$ + } catch (IllegalCharsetNameException e) { + // expected + } + } finally { + System.setProperty("file.encoding", oldDefaultEncoding);//$NON-NLS-1$ + } + } + + /** + * @tests java.nio.charset.Charset#forName(java.lang.String) + */ + public void test_forNameLjava_lang_String() { + /* + * invoke forName two times with the same canonical name, it + * should return the same reference. + */ + Charset cs1 = Charset.forName("UTF-8"); + Charset cs2 = Charset.forName("UTF-8"); + assertSame(cs1, cs2); + + /* + * test forName: invoke forName two times for the same Charset using + * canonical name and alias, it should return the same reference. + */ + Charset cs3 = Charset.forName("ASCII"); + Charset cs4 = Charset.forName("US-ASCII"); + assertSame(cs3, cs4); + } + + /* + * test cached decoder + */ + public void test_DecodeLjava_nio_ByteBuffer() throws Exception{ + MockCharsetForDecoder cs1 = new MockCharsetForDecoder("CachedCharset",null); + MockCharsetForDecoder cs2 = new MockCharsetForDecoder("CachedCharset",null); + ByteBuffer in = ByteBuffer.wrap(new byte[]{0x00}); + cs1.decode(in); + in.flip(); + cs2.decode(in); + in.flip(); + } + /* + * Mock Charset for cached decoder test + */ + static class MockCharsetForDecoder extends Charset{ + + public MockCharsetForDecoder(String canonicalName, String[] aliases){ + super(canonicalName, aliases); + } + + public boolean contains(Charset charset) { + return false; + } + + public CharsetEncoder newEncoder() { + return null; + } + + public CharsetDecoder newDecoder() { + return new MockCachedDecoder(this); + } + + + } + /* + * Mock decoder. Only one caller is permitted. + */ + static class MockCachedDecoder extends CharsetDecoder { + static MockCachedDecoder caller = null; + + public MockCachedDecoder(Charset cs) { + super(cs, 1, 10); + } + + /* + * Only one caller is permitted. + * If there's another caller, throw RuntimeException. + */ + protected CoderResult decodeLoop(ByteBuffer in, CharBuffer out) { + if(null == caller){ + caller = this; + }else{ + if(caller != this){ + // Another instance + fail("should use the same instance"); + } + } + return CoderResult.UNDERFLOW; + } + } + + /* + * test cached encoder + */ + public void test_EncodeLjava_nio_CharBuffer() throws Exception { + MockCharsetForEncoder cs1 = new MockCharsetForEncoder("CachedCharset", null); + MockCharsetForEncoder cs2 = new MockCharsetForEncoder("CachedCharset", null); + CharBuffer in = CharBuffer.wrap("A"); + cs1.encode(in); + in.flip(); + cs2.encode(in); + } + + /* + * Mock Charset for cached encoder test + */ + static class MockCharsetForEncoder extends Charset { + + public MockCharsetForEncoder(String canonicalName, String[] aliases) { + super(canonicalName, aliases); + } + + public boolean contains(Charset charset) { + return false; + } + + public CharsetDecoder newDecoder() { + return new MockDecoderForEncoder(this); + } + + public CharsetEncoder newEncoder() { + return new MockCachedEncoder(this); + } + } + + /* + * Mock encoder. Only one caller is permitted. + */ + static class MockCachedEncoder extends CharsetEncoder { + static MockCachedEncoder caller = null; + + public MockCachedEncoder(Charset cs) { + super(cs, 1, 10); + } + + /* + * Only one caller is permitted. + */ + protected CoderResult encodeLoop(CharBuffer in, ByteBuffer out) { + if (null == caller) { + caller = this; + } else { + if (caller != this) { + // Another instance + fail("should use the same instance"); + } + } + return CoderResult.UNDERFLOW; + } + } + + /* + * Mock decoder for MockCachedEncoder. + */ + static class MockDecoderForEncoder extends CharsetDecoder { + public MockDecoderForEncoder(Charset cs) { + super(cs, 1, 10); + } + + protected CoderResult decodeLoop(ByteBuffer in, CharBuffer out) { + in.position(in.limit()); + return CoderResult.UNDERFLOW; + } + } + +} diff --git a/nio_char/src/test/java/org/apache/harmony/nio_char/tests/java/nio/charset/CoderMalfunctionErrorTest.java b/nio_char/src/test/java/org/apache/harmony/nio_char/tests/java/nio/charset/CoderMalfunctionErrorTest.java new file mode 100644 index 0000000..dc9f269 --- /dev/null +++ b/nio_char/src/test/java/org/apache/harmony/nio_char/tests/java/nio/charset/CoderMalfunctionErrorTest.java @@ -0,0 +1,62 @@ +/* Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.harmony.nio_char.tests.java.nio.charset; + +import java.nio.charset.CoderMalfunctionError; + +import junit.framework.TestCase; + +import org.apache.harmony.testframework.serialization.SerializationTest; + +/** + * Test java.nio.CoderMalfunctionError. + */ +public class CoderMalfunctionErrorTest extends TestCase { + + /* + * Test constructor with normal param. + */ + public void testConstructor_Normal() { + Exception ex = new Exception(); + CoderMalfunctionError e = new CoderMalfunctionError(ex); + assertSame(ex, e.getCause()); + } + + /* + * Test constructor with null param. + */ + public void testConstructor_Null() { + CoderMalfunctionError e = new CoderMalfunctionError(null); + assertNull(e.getCause()); + } + + /** + * @tests serialization/deserialization compatibility. + */ + public void testSerializationSelf() throws Exception { + + SerializationTest.verifySelf(new CoderMalfunctionError(null)); + } + + /** + * @tests serialization/deserialization compatibility with RI. + */ + public void testSerializationCompatibility() throws Exception { + SerializationTest.verifyGolden(this, new CoderMalfunctionError(null)); + + } +} diff --git a/nio_char/src/test/java/org/apache/harmony/nio_char/tests/java/nio/charset/IllegalCharsetNameExceptionTest.java b/nio_char/src/test/java/org/apache/harmony/nio_char/tests/java/nio/charset/IllegalCharsetNameExceptionTest.java new file mode 100644 index 0000000..914eea2 --- /dev/null +++ b/nio_char/src/test/java/org/apache/harmony/nio_char/tests/java/nio/charset/IllegalCharsetNameExceptionTest.java @@ -0,0 +1,94 @@ +/* Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.harmony.nio_char.tests.java.nio.charset; + +import java.io.Serializable; +import java.nio.charset.IllegalCharsetNameException; + +import junit.framework.TestCase; + +import org.apache.harmony.testframework.serialization.SerializationTest; +import org.apache.harmony.testframework.serialization.SerializationTest.SerializableAssert; + +/** + * Test class IllegalCharsetNameException. + */ +public class IllegalCharsetNameExceptionTest extends TestCase { + + public void testConstructor() { + IllegalCharsetNameException ex = new IllegalCharsetNameException( + "impossible"); + assertTrue(ex instanceof IllegalArgumentException); + assertNull(ex.getCause()); + assertEquals(ex.getCharsetName(), "impossible"); + assertTrue(ex.getMessage().indexOf("impossible") != -1); + + ex = new IllegalCharsetNameException("ascii"); + assertNull(ex.getCause()); + assertEquals(ex.getCharsetName(), "ascii"); + assertTrue(ex.getMessage().indexOf("ascii") != -1); + + ex = new IllegalCharsetNameException(""); + assertNull(ex.getCause()); + assertEquals(ex.getCharsetName(), ""); + ex.getMessage(); + + ex = new IllegalCharsetNameException(null); + assertNull(ex.getCause()); + assertNull(ex.getCharsetName()); + assertTrue(ex.getMessage().indexOf("null") != -1); + + } + + // comparator for IllegalCharsetNameException objects + private static final SerializableAssert COMPARATOR = new SerializableAssert() { + public void assertDeserialized(Serializable initial, + Serializable deserialized) { + + // FIXME?: getMessage() returns more helpful string but + // this leads to incompatible message in serial form + // + // do common checks for all throwable objects + // SerializationTest.THROWABLE_COMPARATOR.assertDeserialized(initial, + // deserialized); + + IllegalCharsetNameException initEx = (IllegalCharsetNameException) initial; + IllegalCharsetNameException desrEx = (IllegalCharsetNameException) deserialized; + + assertEquals("CharsetName", initEx.getCharsetName(), desrEx + .getCharsetName()); + } + }; + + /** + * @tests serialization/deserialization compatibility. + */ + public void testSerializationSelf() throws Exception { + + SerializationTest.verifySelf(new IllegalCharsetNameException( + "charsetName"), COMPARATOR); + } + + /** + * @tests serialization/deserialization compatibility with RI. + */ + public void testSerializationCompatibility() throws Exception { + + SerializationTest.verifyGolden(this, new IllegalCharsetNameException( + "charsetName"), COMPARATOR); + } +} diff --git a/nio_char/src/test/java/org/apache/harmony/nio_char/tests/java/nio/charset/MalformedInputExceptionTest.java b/nio_char/src/test/java/org/apache/harmony/nio_char/tests/java/nio/charset/MalformedInputExceptionTest.java new file mode 100644 index 0000000..40ab9c7 --- /dev/null +++ b/nio_char/src/test/java/org/apache/harmony/nio_char/tests/java/nio/charset/MalformedInputExceptionTest.java @@ -0,0 +1,85 @@ +/* Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.harmony.nio_char.tests.java.nio.charset; + +import java.io.Serializable; +import java.nio.charset.CharacterCodingException; +import java.nio.charset.MalformedInputException; + +import junit.framework.TestCase; + +import org.apache.harmony.testframework.serialization.SerializationTest; +import org.apache.harmony.testframework.serialization.SerializationTest.SerializableAssert; + +/** + * Test class MalformedInputException. + */ +public class MalformedInputExceptionTest extends TestCase { + + public void testConstructor() { + MalformedInputException ex = new MalformedInputException(3); + assertTrue(ex instanceof CharacterCodingException); + assertNull(ex.getCause()); + assertEquals(ex.getInputLength(), 3); + assertTrue(ex.getMessage().indexOf("3") != -1); + + ex = new MalformedInputException(-3); + assertNull(ex.getCause()); + assertEquals(ex.getInputLength(), -3); + assertTrue(ex.getMessage().indexOf("-3") != -1); + + ex = new MalformedInputException(0); + assertNull(ex.getCause()); + assertEquals(ex.getInputLength(), 0); + assertTrue(ex.getMessage().indexOf("0") != -1); + } + + // comparator for MalformedInputException objects + private static final SerializableAssert COMPARATOR = new SerializableAssert() { + public void assertDeserialized(Serializable initial, + Serializable deserialized) { + + // do common checks for all throwable objects + SerializationTest.THROWABLE_COMPARATOR.assertDeserialized(initial, + deserialized); + + MalformedInputException initEx = (MalformedInputException) initial; + MalformedInputException desrEx = (MalformedInputException) deserialized; + + assertEquals("InputLength", initEx.getInputLength(), desrEx + .getInputLength()); + } + }; + + /** + * @tests serialization/deserialization compatibility. + */ + public void testSerializationSelf() throws Exception { + + SerializationTest.verifySelf(new MalformedInputException(11), + COMPARATOR); + } + + /** + * @tests serialization/deserialization compatibility with RI. + */ + public void testSerializationCompatibility() throws Exception { + + SerializationTest.verifyGolden(this, new MalformedInputException(11), + COMPARATOR); + } +} diff --git a/nio_char/src/test/java/org/apache/harmony/nio_char/tests/java/nio/charset/UnmappableCharacterExceptionTest.java b/nio_char/src/test/java/org/apache/harmony/nio_char/tests/java/nio/charset/UnmappableCharacterExceptionTest.java new file mode 100644 index 0000000..0abefbe --- /dev/null +++ b/nio_char/src/test/java/org/apache/harmony/nio_char/tests/java/nio/charset/UnmappableCharacterExceptionTest.java @@ -0,0 +1,86 @@ +/* Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.harmony.nio_char.tests.java.nio.charset; + +import java.io.Serializable; +import java.nio.charset.CharacterCodingException; +import java.nio.charset.UnmappableCharacterException; + +import junit.framework.TestCase; + +import org.apache.harmony.testframework.serialization.SerializationTest; +import org.apache.harmony.testframework.serialization.SerializationTest.SerializableAssert; + +/** + * Test class UnmappableCharacterException. + */ +public class UnmappableCharacterExceptionTest extends TestCase { + + public void testConstructor() { + UnmappableCharacterException ex = new UnmappableCharacterException(3); + assertTrue(ex instanceof CharacterCodingException); + assertNull(ex.getCause()); + assertEquals(ex.getInputLength(), 3); + assertTrue(ex.getMessage().indexOf("3") != -1); + + ex = new UnmappableCharacterException(-3); + assertNull(ex.getCause()); + assertEquals(ex.getInputLength(), -3); + assertTrue(ex.getMessage().indexOf("-3") != -1); + + ex = new UnmappableCharacterException(0); + assertNull(ex.getCause()); + assertEquals(ex.getInputLength(), 0); + assertTrue(ex.getMessage().indexOf("0") != -1); + + } + + // comparator for UnmappableCharacterException objects + private static final SerializableAssert COMPARATOR = new SerializableAssert() { + public void assertDeserialized(Serializable initial, + Serializable deserialized) { + + // do common checks for all throwable objects + SerializationTest.THROWABLE_COMPARATOR.assertDeserialized(initial, + deserialized); + + UnmappableCharacterException initEx = (UnmappableCharacterException) initial; + UnmappableCharacterException desrEx = (UnmappableCharacterException) deserialized; + + assertEquals("InputLength", initEx.getInputLength(), desrEx + .getInputLength()); + } + }; + + /** + * @tests serialization/deserialization compatibility. + */ + public void testSerializationSelf() throws Exception { + + SerializationTest.verifySelf(new UnmappableCharacterException(11), + COMPARATOR); + } + + /** + * @tests serialization/deserialization compatibility with RI. + */ + public void testSerializationCompatibility() throws Exception { + + SerializationTest.verifyGolden(this, new UnmappableCharacterException( + 11), COMPARATOR); + } +} diff --git a/nio_char/src/test/java/org/apache/harmony/nio_char/tests/java/nio/charset/UnsupportedCharsetExceptionTest.java b/nio_char/src/test/java/org/apache/harmony/nio_char/tests/java/nio/charset/UnsupportedCharsetExceptionTest.java new file mode 100644 index 0000000..dd17848 --- /dev/null +++ b/nio_char/src/test/java/org/apache/harmony/nio_char/tests/java/nio/charset/UnsupportedCharsetExceptionTest.java @@ -0,0 +1,93 @@ +/* Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.harmony.nio_char.tests.java.nio.charset; + +import java.io.Serializable; +import java.nio.charset.UnsupportedCharsetException; + +import junit.framework.TestCase; + +import org.apache.harmony.testframework.serialization.SerializationTest; +import org.apache.harmony.testframework.serialization.SerializationTest.SerializableAssert; + +/** + * Test class UnsupportedCharsetException. + */ +public class UnsupportedCharsetExceptionTest extends TestCase { + + public void testConstructor() { + UnsupportedCharsetException ex = new UnsupportedCharsetException( + "impossible"); + assertTrue(ex instanceof IllegalArgumentException); + assertNull(ex.getCause()); + assertEquals(ex.getCharsetName(), "impossible"); + assertTrue(ex.getMessage().indexOf("impossible") != -1); + + ex = new UnsupportedCharsetException("ascii"); + assertNull(ex.getCause()); + assertEquals(ex.getCharsetName(), "ascii"); + assertTrue(ex.getMessage().indexOf("ascii") != -1); + + ex = new UnsupportedCharsetException(""); + assertNull(ex.getCause()); + assertEquals(ex.getCharsetName(), ""); + ex.getMessage(); + + ex = new UnsupportedCharsetException(null); + assertNull(ex.getCause()); + assertNull(ex.getCharsetName()); + assertTrue(ex.getMessage().indexOf("null") != -1); + } + + // comparator for UnsupportedCharsetException objects + private static final SerializableAssert COMPARATOR = new SerializableAssert() { + public void assertDeserialized(Serializable initial, + Serializable deserialized) { + + // FIXME?: getMessage() returns more helpful string but + // this leads to incompatible message in serial form + // + // do common checks for all throwable objects + // SerializationTest.THROWABLE_COMPARATOR.assertDeserialized(initial, + // deserialized); + + UnsupportedCharsetException initEx = (UnsupportedCharsetException) initial; + UnsupportedCharsetException desrEx = (UnsupportedCharsetException) deserialized; + + assertEquals("CharsetName", initEx.getCharsetName(), desrEx + .getCharsetName()); + } + }; + + /** + * @tests serialization/deserialization compatibility. + */ + public void testSerializationSelf() throws Exception { + + SerializationTest.verifySelf(new UnsupportedCharsetException( + "charsetName"), COMPARATOR); + } + + /** + * @tests serialization/deserialization compatibility with RI. + */ + public void testSerializationCompatibility() throws Exception { + + SerializationTest.verifyGolden(this, new UnsupportedCharsetException( + "charsetName"), COMPARATOR); + } +} diff --git a/nio_char/src/test/java/org/apache/harmony/nio_char/tests/java/nio/charset/spi/AllTests.java b/nio_char/src/test/java/org/apache/harmony/nio_char/tests/java/nio/charset/spi/AllTests.java new file mode 100644 index 0000000..bf9546a --- /dev/null +++ b/nio_char/src/test/java/org/apache/harmony/nio_char/tests/java/nio/charset/spi/AllTests.java @@ -0,0 +1,36 @@ +/* Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.harmony.nio_char.tests.java.nio.charset.spi; + +import junit.framework.Test; +import junit.framework.TestSuite; + +public class AllTests { + + public static void main(String[] args) { + junit.textui.TestRunner.run(AllTests.suite()); + } + + public static Test suite() { + TestSuite suite = new TestSuite( + "Suite of tests for the java.nio.charset.spi package."); + // $JUnit-BEGIN$ + suite.addTestSuite(CharsetProviderTest.class); + // $JUnit-END$ + return suite; + } +} diff --git a/nio_char/src/test/java/org/apache/harmony/nio_char/tests/java/nio/charset/spi/CharsetProviderTest.java b/nio_char/src/test/java/org/apache/harmony/nio_char/tests/java/nio/charset/spi/CharsetProviderTest.java new file mode 100644 index 0000000..7a90b56 --- /dev/null +++ b/nio_char/src/test/java/org/apache/harmony/nio_char/tests/java/nio/charset/spi/CharsetProviderTest.java @@ -0,0 +1,94 @@ +/* Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.harmony.nio_char.tests.java.nio.charset.spi; + +import java.nio.charset.Charset; +import java.nio.charset.spi.CharsetProvider; +import java.security.Permission; +import java.util.Iterator; + +import junit.framework.TestCase; + +/** + * Test class java.nio.charset.spi.CharsetProvider. + */ +public class CharsetProviderTest extends TestCase { + + /* + * Test the security check in the constructor. + */ + public void testConstructor() { + // with sufficient privilege + new MockCharsetProvider(); + + SecurityManager oldMan = System.getSecurityManager(); + System.setSecurityManager(new MockSecurityManager()); + // set a normal value + try { + new MockCharsetProvider(); + fail("Should throw SecurityException!"); + } catch (SecurityException e) { + // expected + } finally { + System.setSecurityManager(oldMan); + } + } + + /* + * Test the signature. + */ + static class MockCharsetProvider extends CharsetProvider { + + public Charset charsetForName(String charsetName) { + return null; + } + + public Iterator charsets() { + return null; + } + } + + /* + * Used to grant all permissions except charset provider access. + */ + public static class MockSecurityManager extends SecurityManager { + + public MockSecurityManager() { + } + + public void checkPermission(Permission perm) { + // grant all permissions except logging control + if (perm instanceof RuntimePermission) { + RuntimePermission rp = (RuntimePermission) perm; + if (rp.getName().equals("charsetProvider")) { + throw new SecurityException(); + } + } + } + + public void checkPermission(Permission perm, Object context) { + // grant all permissions except logging control + if (perm instanceof RuntimePermission) { + RuntimePermission rp = (RuntimePermission) perm; + if (rp.getName().equals("charsetProvider")) { + throw new SecurityException(); + } + } + } + } + +} diff --git a/nio_char/src/test/java/tests/api/java/nio/charset/ASCCharsetDecoderTest.java b/nio_char/src/test/java/tests/api/java/nio/charset/ASCCharsetDecoderTest.java new file mode 100644 index 0000000..5dcf956 --- /dev/null +++ b/nio_char/src/test/java/tests/api/java/nio/charset/ASCCharsetDecoderTest.java @@ -0,0 +1,72 @@ +/* Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package tests.api.java.nio.charset; + +import java.io.UnsupportedEncodingException; +import java.nio.ByteBuffer; +import java.nio.charset.Charset; + +public class ASCCharsetDecoderTest extends CharsetDecoderTest { + + protected void setUp() throws Exception { + cs = Charset.forName("ascii"); + super.setUp(); + } + + /* + * @see CharsetDecoderTest#tearDown() + */ + protected void tearDown() throws Exception { + super.tearDown(); + } + + // FIXME: give up this tests + // public void testDefaultCharsPerByte(){ + // // assertEquals(1, decoder.averageCharsPerByte()); + // // assertEquals(1, decoder.maxCharsPerByte()); + // assertEquals(decoder.averageCharsPerByte(), 1, 0.001); + // assertEquals(decoder.maxCharsPerByte(), 2, 0.001); + // } + + ByteBuffer getUnmappedByteBuffer() throws UnsupportedEncodingException { + // FIXME: different here + return null; + // ByteBuffer buffer = ByteBuffer.allocate(8); + // buffer.put((byte)-1); + // buffer.put(unibytes); + // buffer.flip(); + // return buffer; + + } + + ByteBuffer getMalformByteBuffer() throws UnsupportedEncodingException { + // FIXME: different here + ByteBuffer buffer = ByteBuffer.allocate(8); + buffer.put((byte) -1); + buffer.put(unibytes); + buffer.flip(); + return buffer; + + // TODO: how malform? + // return null; + } + + ByteBuffer getExceptionByteArray() throws UnsupportedEncodingException { + return null; + } + +} diff --git a/nio_char/src/test/java/tests/api/java/nio/charset/ASCCharsetTest.java b/nio_char/src/test/java/tests/api/java/nio/charset/ASCCharsetTest.java new file mode 100644 index 0000000..d907e93 --- /dev/null +++ b/nio_char/src/test/java/tests/api/java/nio/charset/ASCCharsetTest.java @@ -0,0 +1,59 @@ +/* Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package tests.api.java.nio.charset; + +/** + * Test charset US-ASCII. + */ +public class ASCCharsetTest extends AbstractCharsetTestCase { + + /** + * Constructor. + * + */ + public ASCCharsetTest(String arg0) { + super(arg0, "US-ASCII", new String[] { "ISO646-US", "ASCII", "cp367", + "ascii7", "ANSI_X3.4-1986", "iso-ir-6", "us", "646", + "iso_646.irv:1983", "csASCII", "ANSI_X3.4-1968", + "ISO_646.irv:1991" }, true, true); // "ibm-367" + } + + /* + * (non-Javadoc) + * + * @see tests.api.java.nio.charset.ConcreteCharsetTest#testEncode_Normal() + */ + public void testEncode_Normal() { + String input = "ab\u5D14\u654F"; + byte[] output = new byte[] { 97, 98, + this.testingCharset.newEncoder().replacement()[0], + this.testingCharset.newEncoder().replacement()[0] }; + internalTestEncode(input, output); + } + + /* + * (non-Javadoc) + * + * @see tests.api.java.nio.charset.ConcreteCharsetTest#testDecode_Normal() + */ + public void testDecode_Normal() { + byte[] input = new byte[] { 97, 98, 63, 63 }; + char[] output = "ab??".toCharArray(); + internalTestDecode(input, output); + } + +} diff --git a/nio_char/src/test/java/tests/api/java/nio/charset/AbstractCharsetTestCase.java b/nio_char/src/test/java/tests/api/java/nio/charset/AbstractCharsetTestCase.java new file mode 100644 index 0000000..9a9b45c --- /dev/null +++ b/nio_char/src/test/java/tests/api/java/nio/charset/AbstractCharsetTestCase.java @@ -0,0 +1,158 @@ +package tests.api.java.nio.charset; + +import java.nio.ByteBuffer; +import java.nio.CharBuffer; +import java.nio.charset.Charset; + +import junit.framework.TestCase; + +/** + * Super class for concrete charset test suites. + */ +public abstract class AbstractCharsetTestCase extends TestCase { + + // the canonical name of this charset + protected final String canonicalName; + + // the aliases set + protected final String[] aliases; + + // canEncode + protected final boolean canEncode; + + // isRegistered + protected final boolean isRegistered; + + // charset instance + protected Charset testingCharset; + + /* + * Initialize the field "testingCharset" here. + * + * @see TestCase#setUp() + */ + protected void setUp() throws Exception { + super.setUp(); + this.testingCharset = Charset.forName(this.canonicalName); + } + + /* + * @see TestCase#tearDown() + */ + protected void tearDown() throws Exception { + super.tearDown(); + } + + /** + * Constructor for ConcreteCharsetTest. + * + */ + public AbstractCharsetTestCase(String arg0, String canonicalName, + String[] aliases, boolean canEncode, boolean isRegistered) { + super(arg0); + this.canonicalName = canonicalName; + this.canEncode = canEncode; + this.isRegistered = isRegistered; + this.aliases = aliases; + } + + /* + * Test canEncode. + */ + public void testCanEncode() { + assertEquals(this.canEncode, this.testingCharset.canEncode()); + } + + /* + * Test isRegistered. + */ + public void testIsRegistered() { + assertEquals(this.isRegistered, this.testingCharset.isRegistered()); + } + + /* + * Test name. + */ + public void testName() { + assertEquals(this.canonicalName, this.testingCharset.name()); + // assertEquals(this.canonicalName, this.testingCharset.displayName()); + // assertEquals(this.canonicalName, + // this.testingCharset.displayName(null)); + } + + /* + * Test aliases. + */ + public void testAliases() { + for (int i = 0; i < this.aliases.length; i++) { + Charset c = Charset.forName(this.aliases[i]); + assertEquals(this.canonicalName, c.name()); + // TODO + // assertTrue(this.testingCharset.aliases().contains(this.aliases[i])); + } + } + + /* + * Test the method encode(String) with null. + */ + public void testEncode_String_Null() { + try { + this.testingCharset.encode((String) null); + fail("Should throw NullPointerException!"); + } catch (NullPointerException e) { + // expected + } + } + + /* + * Test the method encode(CharBuffer) with null. + */ + public void testEncode_CharBuffer_Null() { + try { + this.testingCharset.encode((CharBuffer) null); + fail("Should throw NullPointerException!"); + } catch (NullPointerException e) { + // expected + } + } + + /* + * Test encoding. + */ + protected void internalTestEncode(String input, byte[] output) { + ByteBuffer bb = this.testingCharset.encode(input); + int i = 0; + bb.rewind(); + while (bb.hasRemaining() && i < output.length) { + assertEquals(output[i], bb.get()); + i++; + } + assertFalse(bb.hasRemaining()); + assertEquals(output.length, i); + } + + /* + * Test encoding. + */ + public abstract void testEncode_Normal(); + + /* + * Test decoding. + */ + protected void internalTestDecode(byte[] input, char[] output) { + CharBuffer chb = this.testingCharset.decode(ByteBuffer.wrap(input)); + int i = 0; + chb.rewind(); + while (chb.hasRemaining() && i < output.length) { + assertEquals(output[i], chb.get()); + i++; + } + assertFalse(chb.hasRemaining()); + assertEquals(output.length, i); + } + + /* + * Test decoding. + */ + public abstract void testDecode_Normal(); +} diff --git a/nio_char/src/test/java/tests/api/java/nio/charset/AllTests.java b/nio_char/src/test/java/tests/api/java/nio/charset/AllTests.java new file mode 100644 index 0000000..e718651 --- /dev/null +++ b/nio_char/src/test/java/tests/api/java/nio/charset/AllTests.java @@ -0,0 +1,62 @@ +/* Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package tests.api.java.nio.charset; + +import org.apache.harmony.nio_char.tests.java.nio.charset.ASCIICharsetEncoderTest; + +import junit.framework.Test; +import junit.framework.TestSuite; + +/** + * Test suite for java.nio.charset package. + */ +public class AllTests { + + public static Test suite() { + TestSuite suite = new TestSuite("Test for tests.api.java.nio.charset"); + // $JUnit-BEGIN$ + suite.addTestSuite(CodingErrorActionTest.class); + suite.addTestSuite(CoderResultTest.class); + suite.addTestSuite(CharsetTest.class); + suite.addTestSuite(ASCCharsetTest.class); + suite.addTestSuite(ISOCharsetTest.class); + suite.addTestSuite(UTF8CharsetTest.class); + suite.addTestSuite(UTF16CharsetTest.class); + suite.addTestSuite(UTF16BECharsetTest.class); + suite.addTestSuite(UTF16LECharsetTest.class); + suite.addTestSuite(CharsetEncoderTest.class); + suite.addTestSuite(ISOCharsetEncoderTest.class); + suite.addTestSuite(UTFCharsetEncoderTest.class); + // GBCharset not supported + // suite.addTestSuite(GBCharsetEncoderTest.class); + suite.addTestSuite(ASCIICharsetEncoderTest.class); + suite.addTestSuite(UTF16CharsetEncoderTest.class); + suite.addTestSuite(UTF16LECharsetEncoderTest.class); + suite.addTestSuite(UTF16BECharsetEncoderTest.class); + suite.addTestSuite(CharsetDecoderTest.class); + suite.addTestSuite(ISOCharsetDecoderTest.class); + suite.addTestSuite(UTFCharsetDecoderTest.class); + // GBCharset not supported + // suite.addTestSuite(GBCharsetDecoderTest.class); + suite.addTestSuite(ASCCharsetDecoderTest.class); + suite.addTestSuite(UTF16CharsetDecoderTest.class); + suite.addTestSuite(UTF16LECharsetDecoderTest.class); + suite.addTestSuite(UTF16BECharsetDecoderTest.class); + // $JUnit-END$ + return suite; + } +} diff --git a/nio_char/src/test/java/tests/api/java/nio/charset/CharsetDecoderTest.java b/nio_char/src/test/java/tests/api/java/nio/charset/CharsetDecoderTest.java new file mode 100644 index 0000000..0d067b0 --- /dev/null +++ b/nio_char/src/test/java/tests/api/java/nio/charset/CharsetDecoderTest.java @@ -0,0 +1,784 @@ +/* Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package tests.api.java.nio.charset; + +import java.io.UnsupportedEncodingException; +import java.nio.ByteBuffer; +import java.nio.CharBuffer; +import java.nio.charset.CharacterCodingException; +import java.nio.charset.Charset; +import java.nio.charset.CharsetDecoder; +import java.nio.charset.CoderResult; +import java.nio.charset.CodingErrorAction; +import java.nio.charset.MalformedInputException; +import java.nio.charset.UnmappableCharacterException; + +import junit.framework.TestCase; + +/** + * API unit test for java.nio.CharsetDecoder + */ +public class CharsetDecoderTest extends TestCase { + + static final String unistr = " buffer";// \u8000\u8001\u00a5\u3000\r\n"; + + byte[] unibytes = new byte[] { 32, 98, 117, 102, 102, 101, 114 }; + + protected static final int MAX_BYTES = 3; + + protected static final double AVER_BYTES = 0.5; + + // default charset + private static final Charset MOCKCS = new CharsetEncoderTest.MockCharset( + "mock", new String[0]); + + Charset cs = MOCKCS; + + // default decoder + protected static CharsetDecoder decoder; + + String bom = ""; + + protected void setUp() throws Exception { + super.setUp(); + decoder = cs.newDecoder(); + } + + protected void tearDown() throws Exception { + super.tearDown(); + } + + // FIXME: give up this tests + // /* + // * test default value + // */ + // public void testDefaultCharsPerByte() { + // assertTrue(decoder.averageCharsPerByte() == AVER_BYTES); + // assertTrue(decoder.maxCharsPerByte() == MAX_BYTES); + // } + + public void testDefaultValues() { + assertSame(cs, decoder.charset()); + try { + decoder.detectedCharset(); + fail("should unsupported"); + } catch (UnsupportedOperationException e) { + } + try { + assertTrue(decoder.isCharsetDetected()); + fail("should unsupported"); + } catch (UnsupportedOperationException e) { + } + assertFalse(decoder.isAutoDetecting()); + assertSame(CodingErrorAction.REPORT, decoder.malformedInputAction()); + assertSame(CodingErrorAction.REPORT, decoder + .unmappableCharacterAction()); + assertEquals(decoder.replacement(), "\ufffd"); + } + + /* + * test constructor + */ + public void testCharsetDecoder() { + // default value + decoder = new MockCharsetDecoder(cs, (float) AVER_BYTES, MAX_BYTES); + + // normal case + CharsetDecoder ec = new MockCharsetDecoder(cs, 1, MAX_BYTES); + assertSame(ec.charset(), cs); + assertEquals(1.0, ec.averageCharsPerByte(), 0.0); + assertTrue(ec.maxCharsPerByte() == MAX_BYTES); + + /* + * ------------------------ Exceptional cases ------------------------- + */ + // Normal case: null charset + ec = new MockCharsetDecoder(null, 1, MAX_BYTES); + assertNull(ec.charset()); + assertEquals(1.0, ec.averageCharsPerByte(), 0.0); + assertTrue(ec.maxCharsPerByte() == MAX_BYTES); + + ec = new MockCharsetDecoder(new CharsetEncoderTest.MockCharset("mock", + new String[0]), 1, MAX_BYTES); + + // Commented out since the comment is wrong since MAX_BYTES > 1 + // // OK: average length less than max length + // ec = new MockCharsetDecoder(cs, MAX_BYTES, 1); + // assertTrue(ec.averageCharsPerByte() == MAX_BYTES); + // assertTrue(ec.maxCharsPerByte() == 1); + + // Illegal Argument: zero length + try { + ec = new MockCharsetDecoder(cs, 0, MAX_BYTES); + fail("should throw IllegalArgumentException"); + } catch (IllegalArgumentException e) { + } + try { + ec = new MockCharsetDecoder(cs, 1, 0); + fail("should throw IllegalArgumentException"); + } catch (IllegalArgumentException e) { + } + + // Illegal Argument: negative length + try { + ec = new MockCharsetDecoder(cs, -1, MAX_BYTES); + fail("should throw IllegalArgumentException"); + } catch (IllegalArgumentException e) { + } + try { + ec = new MockCharsetDecoder(cs, 1, -1); + fail("should throw IllegalArgumentException"); + } catch (IllegalArgumentException e) { + } + } + + /* + * test onMalformedInput + */ + public void testOnMalformedInput() { + assertSame(CodingErrorAction.REPORT, decoder.malformedInputAction()); + try { + decoder.onMalformedInput(null); + fail("should throw null pointer exception"); + } catch (IllegalArgumentException e) { + } + decoder.onMalformedInput(CodingErrorAction.IGNORE); + assertSame(CodingErrorAction.IGNORE, decoder.malformedInputAction()); + } + + /* + * test unmappableCharacter + */ + public void testOnUnmappableCharacter() { + assertSame(CodingErrorAction.REPORT, decoder + .unmappableCharacterAction()); + try { + decoder.onUnmappableCharacter(null); + fail("should throw null pointer exception"); + } catch (IllegalArgumentException e) { + } + decoder.onUnmappableCharacter(CodingErrorAction.IGNORE); + assertSame(CodingErrorAction.IGNORE, decoder + .unmappableCharacterAction()); + } + + /* + * test replaceWith + */ + public void testReplaceWith() { + try { + decoder.replaceWith(null); + fail("should throw null pointer exception"); + } catch (IllegalArgumentException e) { + } + try { + decoder.replaceWith(""); + fail("should throw null pointer exception"); + } catch (IllegalArgumentException e) { + } + try { + decoder.replaceWith("testReplaceWith"); + fail("should throw illegal argument exception"); + } catch (IllegalArgumentException e) { + } + + decoder.replaceWith("a"); + assertSame("a", decoder.replacement()); + } + + /* + * Class under test for CharBuffer decode(ByteBuffer) + */ + public void testDecodeByteBuffer() throws CharacterCodingException { + implTestDecodeByteBuffer(); + } + + void implTestDecodeByteBuffer() throws CharacterCodingException { + // Null pointer + try { + decoder.decode(null); + fail("should throw null pointer exception"); + } catch (NullPointerException e) { + } + + // empty input buffer + CharBuffer out = decoder.decode(ByteBuffer.allocate(0)); + assertCharBufferValue(out, ""); + + // normal case + ByteBuffer in = ByteBuffer.wrap(getUnibytes()); + out = decoder.decode(in); + assertEquals(out.position(), 0); + assertEquals(out.limit(), unistr.length()); + assertEquals(out.remaining(), unistr.length()); + assertEquals(new String(out.array(), 0, out.limit()), unistr); + } + + public void testDecodeByteBufferException() + throws CharacterCodingException, UnsupportedEncodingException { + CharBuffer out; + ByteBuffer in; + String replaceStr = decoder.replacement() + " buffer"; + + // MalformedException: + decoder.onMalformedInput(CodingErrorAction.REPORT); + decoder.onUnmappableCharacter(CodingErrorAction.REPORT); + in = getMalformByteBuffer(); + if (in != null) { + try { + CharBuffer buffer = decoder.decode(in); + assertTrue(buffer.remaining() > 0); + fail("should throw MalformedInputException"); + } catch (MalformedInputException e) { + } + + decoder.reset(); + in.rewind(); + decoder.onMalformedInput(CodingErrorAction.IGNORE); + out = decoder.decode(in); + assertCharBufferValue(out, " buffer"); + + decoder.reset(); + in.rewind(); + decoder.onMalformedInput(CodingErrorAction.REPLACE); + out = decoder.decode(in); + assertCharBufferValue(out, replaceStr); + } + + // Unmapped Exception: + decoder.onMalformedInput(CodingErrorAction.REPORT); + decoder.onUnmappableCharacter(CodingErrorAction.REPORT); + in = getUnmappedByteBuffer(); + if (in != null) { + try { + decoder.decode(in); + fail("should throw UnmappableCharacterException"); + } catch (UnmappableCharacterException e) { + } + + decoder.reset(); + in.rewind(); + decoder.onUnmappableCharacter(CodingErrorAction.IGNORE); + out = decoder.decode(in); + assertCharBufferValue(out, " buffer"); + + decoder.reset(); + in.rewind(); + decoder.onUnmappableCharacter(CodingErrorAction.REPLACE); + out = decoder.decode(in); + assertCharBufferValue(out, replaceStr); + } + + // RuntimeException + try { + decoder.decode(getExceptionByteArray()); + fail("should throw runtime exception"); + } catch (RuntimeException e) { + } + } + + /* + * Class under test for CoderResult decode(ByteBuffer, CharBuffer, boolean) + */ + public void testDecodeByteBufferCharBufferboolean() { + implTestDecodeByteBufferCharBufferboolean(); + } + + void implTestDecodeByteBufferCharBufferboolean() { + byte[] gb = getUnibytes(); + ByteBuffer in = ByteBuffer.wrap(gb); + CharBuffer out = CharBuffer.allocate(100); + + // Null pointer + try { + decoder.decode(null, out, true); + fail("should throw null pointer exception"); + } catch (NullPointerException e) { + } + try { + decoder.decode(in, null, true); + fail("should throw null pointer exception"); + } catch (NullPointerException e) { + } + + // normal case, one complete operation + decoder.reset(); + in.rewind(); + out.rewind(); + assertSame(CoderResult.UNDERFLOW, decoder.decode(in, out, true)); + assertEquals(out.limit(), 100); + assertEquals(out.position(), unistr.length()); + assertEquals(out.remaining(), 100 - unistr.length()); + assertEquals(out.capacity(), 100); + assertCharBufferValue(out, unistr); + decoder.flush(out); + + // normal case, one complete operation, but call twice, first time set + // endOfInput to false + decoder.reset(); + in.rewind(); + out.clear(); + assertSame(CoderResult.UNDERFLOW, decoder.decode(in, out, false)); + assertEquals(out.limit(), 100); + assertEquals(out.position(), unistr.length()); + assertEquals(out.remaining(), 100 - unistr.length()); + assertEquals(out.capacity(), 100); + assertCharBufferValue(out, unistr); + + decoder.reset(); + in.rewind(); + out.clear(); + assertSame(CoderResult.UNDERFLOW, decoder.decode(in, out, false)); + in = ByteBuffer.wrap(unibytes); + assertSame(CoderResult.UNDERFLOW, decoder.decode(in, out, false)); + in.rewind(); + assertSame(CoderResult.UNDERFLOW, decoder.decode(in, out, true)); + assertEquals(out.limit(), 100); + assertTrue(out.position() > 0); + assertEquals(out.remaining(), out.capacity() - out.position()); + assertEquals(out.capacity(), 100); + assertCharBufferValue(out, unistr + unistr + unistr); + + // overflow + out = CharBuffer.allocate(4); + decoder.reset(); + in = ByteBuffer.wrap(getUnibytes()); + out.rewind(); + assertSame(CoderResult.OVERFLOW, decoder.decode(in, out, false)); + + assertEquals(new String(out.array()), unistr.substring(0, 4)); + out = CharBuffer.allocate(100); + assertSame(CoderResult.UNDERFLOW, decoder.decode(in, out, false)); + assertCharBufferValue(out, unistr.substring(4)); + in.rewind(); + out = CharBuffer.allocate(100); + assertSame(CoderResult.UNDERFLOW, decoder.decode(in, out, true)); + assertCharBufferValue(out, bom + unistr); + } + + public void testDecodeCharBufferByteBufferbooleanExceptionTrue() + throws CharacterCodingException, UnsupportedEncodingException { + implTestDecodeCharBufferByteBufferbooleanException(true); + } + + public void testDecodeCharBufferByteBufferbooleanExceptionFalse() + throws CharacterCodingException, UnsupportedEncodingException { + implTestDecodeCharBufferByteBufferbooleanException(false); + } + + void implTestDecodeCharBufferByteBufferbooleanException(boolean endOfInput) + throws CharacterCodingException, UnsupportedEncodingException { + CharBuffer out; + ByteBuffer in; + + // Unmapped Exception: + in = getUnmappedByteBuffer(); + out = CharBuffer.allocate(50); + decoder.onMalformedInput(CodingErrorAction.REPORT); + if (null != in) { + decoder.reset(); + decoder.onUnmappableCharacter(CodingErrorAction.REPORT); + CoderResult result = decoder.decode(in, out, endOfInput); + assertTrue(result.isUnmappable()); + + decoder.reset(); + out.clear(); + in.rewind(); + decoder.onUnmappableCharacter(CodingErrorAction.IGNORE); + assertSame(CoderResult.UNDERFLOW, decoder.decode(in, out, + endOfInput)); + assertCharBufferValue(out, " buffer"); + + decoder.reset(); + out.clear(); + in.rewind(); + decoder.onUnmappableCharacter(CodingErrorAction.REPLACE); + assertSame(CoderResult.UNDERFLOW, decoder.decode(in, out, + endOfInput)); + assertCharBufferValue(out, decoder.replacement() + " buffer"); + } else if (endOfInput) { + // System.err.println("Cannot find unmappable byte array for " + // + cs.name()); + } + + // MalformedException: + in = getMalformByteBuffer(); + out = CharBuffer.allocate(50); + decoder.onUnmappableCharacter(CodingErrorAction.REPORT); + if (null != in) { + decoder.onMalformedInput(CodingErrorAction.REPORT); + CoderResult result = decoder.decode(in, out, endOfInput); + assertTrue(result.isMalformed()); + + decoder.reset(); + out.clear(); + in.rewind(); + decoder.onMalformedInput(CodingErrorAction.IGNORE); + assertSame(CoderResult.UNDERFLOW, decoder.decode(in, out, + endOfInput)); + assertCharBufferValue(out, " buffer"); + + decoder.reset(); + out.clear(); + in.rewind(); + decoder.onMalformedInput(CodingErrorAction.REPLACE); + assertSame(CoderResult.UNDERFLOW, decoder.decode(in, out, + endOfInput)); + assertCharBufferValue(out, decoder.replacement() + " buffer"); + } else if (endOfInput) { + // System.err.println("Cannot find malform byte array for " + // + cs.name()); + } + + // RuntimeException + in = getExceptionByteArray(); + try { + decoder.decode(in, out, endOfInput); + fail("should throw runtime exception"); + } catch (RuntimeException e) { + } + } + + ByteBuffer getExceptionByteArray() throws UnsupportedEncodingException { + // "runtime" + return ByteBuffer + .wrap(new byte[] { 114, 117, 110, 116, 105, 109, 101 }); + } + + ByteBuffer getUnmappedByteBuffer() throws UnsupportedEncodingException { + // "unmap buffer" + byte[] ba = new byte[] { 117, 110, 109, 97, 112, 32, 98, 117, 102, 102, + 101, 114 }; + return ByteBuffer.wrap(ba); + } + + ByteBuffer getMalformByteBuffer() throws UnsupportedEncodingException { + // "malform buffer" + byte[] ba = new byte[] { 109, 97, 108, 102, 111, 114, 109, 32, 98, 117, + 102, 102, 101, 114 }; + return ByteBuffer.wrap(ba); + } + + void assertCharBufferValue(CharBuffer out, String expected) { + if (out.position() != 0) { + out.flip(); + } + assertEquals(new String(out.array(), 0, out.limit()), expected); + } + + /* + * test flush + */ + public void testFlush() throws CharacterCodingException { + CharBuffer out = CharBuffer.allocate(10); + ByteBuffer in = ByteBuffer.wrap(new byte[] { 12, 12 }); + decoder.decode(in, out, true); + assertSame(CoderResult.UNDERFLOW, decoder.flush(out)); + + decoder.reset(); + decoder.decode((ByteBuffer) in.rewind(), (CharBuffer) out.rewind(), + true); + assertSame(CoderResult.UNDERFLOW, decoder + .flush(CharBuffer.allocate(10))); + } + + /* + * ---------------------------------- methods to test illegal state + * ----------------------------------- + */ + // Normal case: just after reset, and it also means reset can be done + // anywhere + public void testResetIllegalState() throws CharacterCodingException { + byte[] gb = getUnibytes(); + decoder.reset(); + decoder.decode(ByteBuffer.wrap(gb)); + decoder.reset(); + decoder.decode(ByteBuffer.wrap(gb), CharBuffer.allocate(3), false); + decoder.reset(); + decoder.decode(ByteBuffer.wrap(gb), CharBuffer.allocate(3), true); + decoder.reset(); + } + + public void testFlushIllegalState() throws CharacterCodingException { + ByteBuffer in = ByteBuffer.wrap(new byte[] { 98, 98 }); + CharBuffer out = CharBuffer.allocate(5); + // Normal case: after decode with endOfInput is true + decoder.reset(); + decoder.decode(in, out, true); + out.rewind(); + CoderResult result = decoder.flush(out); + assertSame(result, CoderResult.UNDERFLOW); + + // Illegal state: flush twice + try { + decoder.flush(out); + fail("should throw IllegalStateException"); + } catch (IllegalStateException e) { + } + + // Illegal state: flush after decode with endOfInput is false + decoder.reset(); + decoder.decode(in, out, false); + try { + decoder.flush(out); + fail("should throw IllegalStateException"); + } catch (IllegalStateException e) { + } + } + + byte[] getUnibytes() { + return unibytes; + } + + // test illegal states for decode facade + public void testDecodeFacadeIllegalState() throws CharacterCodingException { + // decode facade can be execute in anywhere + byte[] gb = getUnibytes(); + ByteBuffer in = ByteBuffer.wrap(gb); + // Normal case: just created + decoder.decode(in); + in.rewind(); + + // Normal case: just after decode facade + decoder.decode(in); + in.rewind(); + + // Normal case: just after decode with that endOfInput is true + decoder.reset(); + decoder.decode(ByteBuffer.wrap(gb), CharBuffer.allocate(30), true); + decoder.decode(in); + in.rewind(); + + // Normal case:just after decode with that endOfInput is false + decoder.reset(); + decoder.decode(ByteBuffer.wrap(gb), CharBuffer.allocate(30), false); + decoder.decode(in); + in.rewind(); + + // Normal case: just after flush + decoder.reset(); + decoder.decode(ByteBuffer.wrap(gb), CharBuffer.allocate(30), true); + decoder.flush(CharBuffer.allocate(10)); + decoder.decode(in); + in.rewind(); + } + + // test illegal states for two decode method with endOfInput is true + public void testDecodeTrueIllegalState() throws CharacterCodingException { + ByteBuffer in = ByteBuffer.wrap(new byte[] { 98, 98 }); + CharBuffer out = CharBuffer.allocate(100); + // Normal case: just created + decoder.decode(in, out, true); + in.rewind(); + out.rewind(); + + // Normal case: just after decode with that endOfInput is true + decoder.reset(); + decoder.decode(in, CharBuffer.allocate(30), true); + in.rewind(); + decoder.decode(in, out, true); + in.rewind(); + out.rewind(); + + // Normal case:just after decode with that endOfInput is false + decoder.reset(); + decoder.decode(in, CharBuffer.allocate(30), false); + in.rewind(); + decoder.decode(in, out, true); + in.rewind(); + out.rewind(); + + // Illegal state: just after flush + decoder.reset(); + decoder.decode(in, CharBuffer.allocate(30), true); + decoder.flush(CharBuffer.allocate(10)); + in.rewind(); + try { + decoder.decode(in, out, true); + fail("should illegal state"); + } catch (IllegalStateException e) { + } + in.rewind(); + out.rewind(); + + } + + // test illegal states for two decode method with endOfInput is false + public void testDecodeFalseIllegalState() throws CharacterCodingException { + ByteBuffer in = ByteBuffer.wrap(new byte[] { 98, 98 }); + CharBuffer out = CharBuffer.allocate(5); + // Normal case: just created + decoder.decode(in, out, false); + in.rewind(); + out.rewind(); + + // Illegal state: just after decode facade + decoder.reset(); + decoder.decode(in); + in.rewind(); + try { + decoder.decode(in, out, false); + fail("should illegal state"); + } catch (IllegalStateException e) { + } + in.rewind(); + out.rewind(); + + // Illegal state: just after decode with that endOfInput is true + decoder.reset(); + decoder.decode(in, CharBuffer.allocate(30), true); + in.rewind(); + try { + decoder.decode(in, out, false); + fail("should illegal state"); + } catch (IllegalStateException e) { + } + in.rewind(); + out.rewind(); + + // Normal case:just after decode with that endOfInput is false + decoder.reset(); + decoder.decode(in, CharBuffer.allocate(30), false); + in.rewind(); + decoder.decode(in, out, false); + in.rewind(); + out.rewind(); + + // Illegal state: just after flush + decoder.reset(); + decoder.decode(in, CharBuffer.allocate(30), true); + in.rewind(); + decoder.flush(CharBuffer.allocate(10)); + try { + decoder.decode(in, out, false); + fail("should illegal state"); + } catch (IllegalStateException e) { + } + } + + /* + * --------------------------------- illegal state test end + * --------------------------------- + */ + + public void testImplFlush() { + decoder = new MockCharsetDecoder(cs, 1, 3); + assertEquals(CoderResult.UNDERFLOW, ((MockCharsetDecoder) decoder) + .pubImplFlush(null)); + } + + public void testImplOnMalformedInput() { + decoder = new MockCharsetDecoder(cs, 1, 3); + assertEquals(CoderResult.UNDERFLOW, ((MockCharsetDecoder) decoder) + .pubImplFlush(null)); + + } + + public void testImplOnUnmappableCharacter() { + decoder = new MockCharsetDecoder(cs, 1, 3); + ((MockCharsetDecoder) decoder).pubImplOnUnmappableCharacter(null); + } + + public void testImplReplaceWith() { + decoder = new MockCharsetDecoder(cs, 1, 3); + ((MockCharsetDecoder) decoder).pubImplReplaceWith(null); + } + + public void testImplReset() { + decoder = new MockCharsetDecoder(cs, 1, 3); + ((MockCharsetDecoder) decoder).pubImplReset(); + } + + /* + * mock decoder + */ + public static class MockCharsetDecoder extends CharsetDecoder { + public MockCharsetDecoder(Charset cs, float ave, float max) { + super(cs, ave, max); + } + + protected CoderResult decodeLoop(ByteBuffer in, CharBuffer out) { + int inPosition = in.position(); + byte[] input = new byte[in.remaining()]; + in.get(input); + String result = new String(input); + if (result.startsWith("malform")) { + // reset the cursor to the error position + in.position(inPosition + "malform".length()); + // set the error length + return CoderResult.malformedForLength("malform".length()); + } else if (result.startsWith("unmap")) { + // reset the cursor to the error position + in.position(inPosition); + // set the error length + return CoderResult.unmappableForLength("unmap".length()); + } else if (result.startsWith("runtime")) { + // reset the cursor to the error position + in.position(0); + // set the error length + throw new RuntimeException("runtime"); + } + int inLeft = input.length; + int outLeft = out.remaining(); + CoderResult r = CoderResult.UNDERFLOW; + int length = inLeft; + if (outLeft < inLeft) { + r = CoderResult.OVERFLOW; + length = outLeft; + in.position(inPosition + outLeft); + } + for (int i = 0; i < length; i++) { + out.put((char) input[i]); + } + return r; + } + + protected CoderResult implFlush(CharBuffer out) { + CoderResult result = super.implFlush(out); + if (out.remaining() >= 5) { + // TODO + // out.put("flush"); + result = CoderResult.UNDERFLOW; + } else { + // out.put("flush", 0, out.remaining()); + result = CoderResult.OVERFLOW; + } + return result; + } + + public CoderResult pubImplFlush(CharBuffer out) { + return super.implFlush(out); + } + + public void pubImplOnMalformedInput(CodingErrorAction newAction) { + super.implOnMalformedInput(newAction); + } + + public void pubImplOnUnmappableCharacter(CodingErrorAction newAction) { + super.implOnUnmappableCharacter(newAction); + } + + public void pubImplReplaceWith(String newReplacement) { + super.implReplaceWith(newReplacement); + } + + public void pubImplReset() { + super.implReset(); + } + + } + +} diff --git a/nio_char/src/test/java/tests/api/java/nio/charset/CharsetEncoderTest.java b/nio_char/src/test/java/tests/api/java/nio/charset/CharsetEncoderTest.java new file mode 100644 index 0000000..3284a57 --- /dev/null +++ b/nio_char/src/test/java/tests/api/java/nio/charset/CharsetEncoderTest.java @@ -0,0 +1,1103 @@ +/* Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package tests.api.java.nio.charset; + +import java.nio.ByteBuffer; +import java.nio.CharBuffer; +import java.nio.charset.CharacterCodingException; +import java.nio.charset.Charset; +import java.nio.charset.CharsetDecoder; +import java.nio.charset.CharsetEncoder; +import java.nio.charset.CoderResult; +import java.nio.charset.CodingErrorAction; +import java.nio.charset.MalformedInputException; +import java.nio.charset.UnmappableCharacterException; +import java.nio.charset.UnsupportedCharsetException; +import java.util.Arrays; + +import junit.framework.TestCase; + +/** + * API unit test for java.nio.charset.CharsetEncoder + */ +public class CharsetEncoderTest extends TestCase { + + static final int MAX_BYTES = 3; + + static final float AVER_BYTES = 0.5f; + + // charset for mock class + private static final Charset MOCKCS = new MockCharset("CharsetEncoderTest_mock", new String[0]); + + Charset cs = MOCKCS; + + // default encoder + CharsetEncoder encoder; + + // default for Charset abstract class + byte[] defaultReplacement = new byte[] { 63 }; + + // specific for Charset implementation subclass + byte[] specifiedReplacement = new byte[] { 26 }; + + static final String unistr = " buffer";// \u8000\u8001\u00a5\u3000\r\n"; + + byte[] unibytes = new byte[] { 32, 98, 117, 102, 102, 101, 114 }; + + byte[] unibytesWithRep = null; + + byte[] surrogate = new byte[0]; + + protected void setUp() throws Exception { + super.setUp(); + encoder = cs.newEncoder(); + if (null == unibytesWithRep) { + byte[] replacement = encoder.replacement(); + unibytesWithRep = new byte[replacement.length + unibytes.length]; + System.arraycopy(replacement, 0, unibytesWithRep, 0, + replacement.length); + System.arraycopy(unibytes, 0, unibytesWithRep, replacement.length, + unibytes.length); + } + } + + /* + * @see TestCase#tearDown() + */ + protected void tearDown() throws Exception { + super.tearDown(); + } + + public void testSpecificDefaultValue() { + assertTrue(encoder.averageBytesPerChar() == AVER_BYTES); + assertTrue(encoder.maxBytesPerChar() == MAX_BYTES); + } + + public void testDefaultValue() { + assertEquals(CodingErrorAction.REPORT, encoder.malformedInputAction()); + assertEquals(CodingErrorAction.REPORT, encoder + .unmappableCharacterAction()); + assertSame(encoder, encoder.onMalformedInput(CodingErrorAction.IGNORE)); + assertSame(encoder, encoder + .onUnmappableCharacter(CodingErrorAction.IGNORE)); + if (encoder instanceof MockCharsetEncoder) { + assertTrue(Arrays.equals(encoder.replacement(), defaultReplacement)); + } else { + assertTrue(Arrays.equals(encoder.replacement(), + specifiedReplacement)); + } + + } + + /* + * Class under test for constructor CharsetEncoder(Charset, float, float) + */ + public void testCharsetEncoderCharsetfloatfloat() { + // default value + encoder = new MockCharsetEncoder(cs, (float) AVER_BYTES, MAX_BYTES); + assertSame(encoder.charset(), cs); + assertTrue(encoder.averageBytesPerChar() == AVER_BYTES); + assertTrue(encoder.maxBytesPerChar() == MAX_BYTES); + assertEquals(CodingErrorAction.REPORT, encoder.malformedInputAction()); + assertEquals(CodingErrorAction.REPORT, encoder + .unmappableCharacterAction()); + assertEquals(new String(encoder.replacement()), new String( + defaultReplacement)); + assertSame(encoder, encoder.onMalformedInput(CodingErrorAction.IGNORE)); + assertSame(encoder, encoder + .onUnmappableCharacter(CodingErrorAction.IGNORE)); + + // normal case + CharsetEncoder ec = new MockCharsetEncoder(cs, 1, MAX_BYTES); + assertSame(ec.charset(), cs); + assertEquals(1.0, ec.averageBytesPerChar(), 0); + assertTrue(ec.maxBytesPerChar() == MAX_BYTES); + + /* + * ------------------------ Exceptional cases ------------------------- + */ + // NullPointerException: null charset + try { + ec = new MockCharsetEncoder(null, 1, MAX_BYTES); + fail("should throw null pointer exception"); + } catch (NullPointerException e) { + } + + ec = new MockCharsetEncoder(new MockCharset("mock", new String[0]), 1, + MAX_BYTES); + + // Commented out since the comment is wrong since MAX_BYTES > 1 + // // OK: average length less than max length + // ec = new MockCharsetEncoder(cs, MAX_BYTES, 1); + // assertTrue(ec.averageBytesPerChar() == MAX_BYTES); + // assertTrue(ec.maxBytesPerChar() == 1); + + // Illegal Argument: zero length + try { + ec = new MockCharsetEncoder(cs, 0, MAX_BYTES); + fail("should throw IllegalArgumentException"); + } catch (IllegalArgumentException e) { + } + try { + ec = new MockCharsetEncoder(cs, 1, 0); + fail("should throw IllegalArgumentException"); + } catch (IllegalArgumentException e) { + } + + // Illegal Argument: negative length + try { + ec = new MockCharsetEncoder(cs, -1, MAX_BYTES); + fail("should throw IllegalArgumentException"); + } catch (IllegalArgumentException e) { + } + try { + ec = new MockCharsetEncoder(cs, 1, -1); + fail("should throw IllegalArgumentException"); + } catch (IllegalArgumentException e) { + } + } + + /* + * Class under test for constructor CharsetEncoder(Charset, float, float, + * byte[]) + */ + public void testCharsetEncoderCharsetfloatfloatbyteArray() { + byte[] ba = getLegalByteArray(); + // normal case + CharsetEncoder ec = new MockCharsetEncoder(cs, 1, MAX_BYTES, ba); + assertSame(ec.charset(), cs); + assertEquals(1.0, ec.averageBytesPerChar(), 0.0); + assertTrue(ec.maxBytesPerChar() == MAX_BYTES); + assertSame(ba, ec.replacement()); + + /* + * ------------------------ Exceptional cases ------------------------- + */ + // NullPointerException: null charset + try { + ec = new MockCharsetEncoder(null, 1, MAX_BYTES, ba); + fail("should throw null pointer exception"); + } catch (NullPointerException e) { + } + + // Illegal Argument: null byte array + try { + ec = new MockCharsetEncoder(cs, 1, MAX_BYTES, null); + fail("should throw IllegalArgumentException"); + } catch (IllegalArgumentException e) { + } + // Illegal Argument: empty byte array + try { + ec = new MockCharsetEncoder(cs, 1, MAX_BYTES, new byte[0]); + fail("should throw IllegalArgumentException"); + } catch (IllegalArgumentException e) { + } + // Illegal Argument: byte array is longer than max length + try { + ec = new MockCharsetEncoder(cs, 1, MAX_BYTES, new byte[] { 1, 2, + MAX_BYTES, 4 }); + fail("should throw IllegalArgumentException"); + } catch (IllegalArgumentException e) { + } + + // Commented out since the comment is wrong since MAX_BYTES > 1 + // This test throws IllegalArgumentException on Harmony and RI + // // OK: average length less than max length + // ec = new MockCharsetEncoder(cs, MAX_BYTES, ba.length, ba); + // assertTrue(ec.averageBytesPerChar() == MAX_BYTES); + // assertTrue(ec.maxBytesPerChar() == ba.length); + + // Illegal Argument: zero length + try { + ec = new MockCharsetEncoder(cs, 0, MAX_BYTES, ba); + fail("should throw IllegalArgumentException"); + } catch (IllegalArgumentException e) { + } + try { + ec = new MockCharsetEncoder(cs, 1, 0, ba); + fail("should throw IllegalArgumentException"); + } catch (IllegalArgumentException e) { + } + + // Illegal Argument: negative length + try { + ec = new MockCharsetEncoder(cs, -1, MAX_BYTES, ba); + fail("should throw IllegalArgumentException"); + } catch (IllegalArgumentException e) { + } + try { + ec = new MockCharsetEncoder(cs, 1, -1, ba); + fail("should throw IllegalArgumentException"); + } catch (IllegalArgumentException e) { + } + } + + /* + * Class under test for boolean canEncode(char) + */ + public void testCanEncodechar() throws CharacterCodingException { + // for non-mapped char + assertTrue(encoder.canEncode('\uc2c0')); + // surrogate char for unicode + // 1st byte: d800-dbff + // 2nd byte: dc00-dfff + assertTrue(encoder.canEncode('\ud800')); + // valid surrogate pair + assertTrue(encoder.canEncode('\udc00')); + } + + /*----------------------------------------- + * Class under test for illegal state case + * methods which can change internal states are two encode, flush, two canEncode, reset + * ----------------------------------------- + */ + + // Normal case: just after reset, and it also means reset can be done + // anywhere + public void testResetIllegalState() throws CharacterCodingException { + assertSame(encoder, encoder.reset()); + encoder.canEncode('\ud901'); + assertSame(encoder, encoder.reset()); + encoder.canEncode("\ud901\udc00"); + assertSame(encoder, encoder.reset()); + encoder.encode(CharBuffer.wrap("aaa")); + assertSame(encoder, encoder.reset()); + encoder.encode(CharBuffer.wrap("aaa"), ByteBuffer.allocate(3), false); + assertSame(encoder, encoder.reset()); + encoder.encode(CharBuffer.wrap("aaa"), ByteBuffer.allocate(3), true); + assertSame(encoder, encoder.reset()); + } + + public void testFlushIllegalState() throws CharacterCodingException { + CharBuffer in = CharBuffer.wrap("aaa"); + ByteBuffer out = ByteBuffer.allocate(5); + + // Normal case: after encode with endOfInput is true + assertSame(encoder, encoder.reset()); + encoder.encode(in, out, true); + out.rewind(); + CoderResult result = encoder.flush(out); + + // Illegal state: flush twice + try { + encoder.flush(out); + fail("should throw IllegalStateException"); + } catch (IllegalStateException e) { + } + + // Illegal state: flush after encode with endOfInput is false + assertSame(encoder, encoder.reset()); + encoder.encode(in, out, false); + try { + encoder.flush(out); + fail("should throw IllegalStateException"); + } catch (IllegalStateException e) { + } + } + + // test illegal states for encode facade + public void testEncodeFacadeIllegalState() throws CharacterCodingException { + // encode facade can be execute in anywhere + CharBuffer in = CharBuffer.wrap("aaa"); + // Normal case: just created + encoder.encode(in); + in.rewind(); + + // Normal case: just after encode facade + encoder.encode(in); + in.rewind(); + + // Normal case: just after canEncode + assertSame(encoder, encoder.reset()); + encoder.canEncode("\ud902\udc00"); + encoder.encode(in); + in.rewind(); + assertSame(encoder, encoder.reset()); + encoder.canEncode('\ud902'); + encoder.encode(in); + in.rewind(); + + // Normal case: just after encode with that endOfInput is true + assertSame(encoder, encoder.reset()); + encoder.encode(CharBuffer.wrap("testCanEncodeIllegalState2"), + ByteBuffer.allocate(30), true); + encoder.encode(in); + in.rewind(); + + // Normal case:just after encode with that endOfInput is false + assertSame(encoder, encoder.reset()); + encoder.encode(CharBuffer.wrap("testCanEncodeIllegalState3"), + ByteBuffer.allocate(30), false); + encoder.encode(in); + in.rewind(); + + // Normal case: just after flush + assertSame(encoder, encoder.reset()); + encoder.encode(CharBuffer.wrap("testCanEncodeIllegalState4"), + ByteBuffer.allocate(30), true); + encoder.flush(ByteBuffer.allocate(10)); + encoder.encode(in); + in.rewind(); + } + + // test illegal states for two encode method with endOfInput is true + public void testEncodeTrueIllegalState() throws CharacterCodingException { + CharBuffer in = CharBuffer.wrap("aaa"); + ByteBuffer out = ByteBuffer.allocate(5); + // Normal case: just created + encoder.encode(in, out, true); + in.rewind(); + out.rewind(); + + in.rewind(); + out.rewind(); + + // Normal case: just after encode with that endOfInput is true + assertSame(encoder, encoder.reset()); + encoder.encode(CharBuffer.wrap("testCanEncodeIllegalState2"), + ByteBuffer.allocate(30), true); + encoder.encode(in, out, true); + in.rewind(); + out.rewind(); + + // Normal case:just after encode with that endOfInput is false + assertSame(encoder, encoder.reset()); + encoder.encode(CharBuffer.wrap("testCanEncodeIllegalState3"), + ByteBuffer.allocate(30), false); + encoder.encode(in, out, true); + in.rewind(); + out.rewind(); + + // Illegal state: just after flush + assertSame(encoder, encoder.reset()); + encoder.encode(CharBuffer.wrap("testCanEncodeIllegalState4"), + ByteBuffer.allocate(30), true); + encoder.flush(ByteBuffer.allocate(10)); + try { + encoder.encode(in, out, true); + fail("should illegal state"); + } catch (IllegalStateException e) { + } + + // Normal case: after canEncode + assertSame(encoder, encoder.reset()); + encoder.canEncode("\ud906\udc00"); + encoder.encode(in, out, true); + in.rewind(); + out.rewind(); + assertSame(encoder, encoder.reset()); + encoder.canEncode('\ud905'); + encoder.encode(in, out, true); + } + + // test illegal states for two encode method with endOfInput is false + public void testEncodeFalseIllegalState() throws CharacterCodingException { + CharBuffer in = CharBuffer.wrap("aaa"); + ByteBuffer out = ByteBuffer.allocate(5); + // Normal case: just created + encoder.encode(in, out, false); + in.rewind(); + out.rewind(); + + // Illegal state: just after encode facade + assertSame(encoder, encoder.reset()); + encoder.encode(CharBuffer.wrap("testCanEncodeIllegalState1")); + try { + encoder.encode(in, out, false); + fail("should illegal state"); + } catch (IllegalStateException e) { + } + + // Illegal state: just after encode with that endOfInput is true + assertSame(encoder, encoder.reset()); + encoder.encode(CharBuffer.wrap("testCanEncodeIllegalState2"), + ByteBuffer.allocate(30), true); + try { + encoder.encode(in, out, false); + fail("should illegal state"); + } catch (IllegalStateException e) { + } + + // Normal case:just after encode with that endOfInput is false + assertSame(encoder, encoder.reset()); + encoder.encode(CharBuffer.wrap("testCanEncodeIllegalState3"), + ByteBuffer.allocate(30), false); + encoder.encode(in, out, false); + in.rewind(); + out.rewind(); + + // Illegal state: just after flush + assertSame(encoder, encoder.reset()); + encoder.encode(CharBuffer.wrap("testCanEncodeIllegalState4"), + ByteBuffer.allocate(30), true); + encoder.flush(ByteBuffer.allocate(10)); + try { + encoder.encode(in, out, false); + fail("should illegal state"); + } catch (IllegalStateException e) { + } + + // Normal case: after canEncode + assertSame(encoder, encoder.reset()); + encoder.canEncode("\ud906\udc00"); + encoder.encode(in, out, false); + in.rewind(); + out.rewind(); + assertSame(encoder, encoder.reset()); + encoder.canEncode('\ud905'); + encoder.encode(in, out, false); + } + + // test illegal states for two canEncode methods + public void testCanEncodeIllegalState() throws CharacterCodingException { + // Normal case: just created + encoder.canEncode("\ud900\udc00"); + encoder.canEncode('\ud900'); + + // Illegal state: just after encode with that endOfInput is true + assertSame(encoder, encoder.reset()); + encoder.encode(CharBuffer.wrap("testCanEncodeIllegalState2"), + ByteBuffer.allocate(30), true); + try { + encoder.canEncode("\ud903\udc00"); + fail("should throw illegal state exception"); + } catch (IllegalStateException e) { + } + + // Illegal state:just after encode with that endOfInput is false + assertSame(encoder, encoder.reset()); + encoder.encode(CharBuffer.wrap("testCanEncodeIllegalState3"), + ByteBuffer.allocate(30), false); + try { + encoder.canEncode("\ud904\udc00"); + fail("should throw illegal state exception"); + } catch (IllegalStateException e) { + } + + // Normal case: just after flush + encoder.encode(CharBuffer.wrap("testCanEncodeIllegalState4"), + ByteBuffer.allocate(30), true); + encoder.flush(ByteBuffer.allocate(10)); + encoder.canEncode("\ud905\udc00"); + encoder.canEncode('\ud906'); + + // Normal case: after reset again + assertSame(encoder, encoder.reset()); + encoder.canEncode("\ud906\udc00"); + encoder.canEncode('\ud905'); + } + + /* + * --------------------------------- illegal state test end + * --------------------------------- + */ + + /* + * Class under test for boolean canEncode(CharSequence) + */ + public void testCanEncodeCharSequence() { + // for non-mapped char + assertTrue(encoder.canEncode("\uc2c0")); + // surrogate char for unicode + // 1st byte: d800-dbff + // 2nd byte: dc00-dfff + assertTrue(encoder.canEncode("\ud800")); + // valid surrogate pair + assertTrue(encoder.canEncode("\ud800\udc00")); + // invalid surrogate pair + assertTrue(encoder.canEncode("\ud800\udb00")); + } + + /* + * Class under test for Charset charset() + */ + public void testCharset() { + try { + encoder = new MockCharsetEncoder(Charset.forName("gbk"), 1, + MAX_BYTES); + // assertSame(encoder.charset(), Charset.forName("gbk")); + } catch (UnsupportedCharsetException e) { + // System.err + // .println("Don't support GBK encoding, ignore current test"); + } + } + + /* + * Class under test for ByteBuffer encode(CharBuffer) + */ + public void testEncodeCharBuffer() throws CharacterCodingException { + // Null pointer + try { + encoder.encode(null); + fail("should throw null pointer exception"); + } catch (NullPointerException e) { + } + + // empty input buffer + ByteBuffer out = encoder.encode(CharBuffer.wrap("")); + assertEquals(out.position(), 0); + assertByteArray(out, new byte[0]); + // assertByteArray(out, surrogate); + + // normal case + out = encoder.encode(CharBuffer.wrap(unistr)); + assertEquals(out.position(), 0); + assertByteArray(out, addSurrogate(unibytes)); + + // Regression test for harmony-3378 + Charset cs = Charset.forName("UTF-8"); + CharsetEncoder encoder = cs.newEncoder(); + encoder.onMalformedInput(CodingErrorAction.REPLACE); + encoder = encoder.replaceWith(new byte[] { (byte) 0xef, (byte) 0xbf, + (byte) 0xbd, }); + CharBuffer in = CharBuffer.wrap("\ud800"); + out = encoder.encode(in); + assertNotNull(out); + } + + private byte[] addSurrogate(byte[] expected) { + if (surrogate.length > 0) { + byte[] temp = new byte[surrogate.length + expected.length]; + System.arraycopy(surrogate, 0, temp, 0, surrogate.length); + System.arraycopy(expected, 0, temp, surrogate.length, + expected.length); + expected = temp; + } + return expected; + } + + /** + * @return + */ + protected byte[] getEmptyByteArray() { + return new byte[0]; + } + + CharBuffer getMalformedCharBuffer() { + return CharBuffer.wrap("malform buffer"); + } + + CharBuffer getUnmapCharBuffer() { + return CharBuffer.wrap("unmap buffer"); + } + + CharBuffer getExceptionCharBuffer() { + return CharBuffer.wrap("runtime buffer"); + } + + public void testEncodeCharBufferException() throws CharacterCodingException { + ByteBuffer out; + CharBuffer in; + // MalformedException: + in = getMalformedCharBuffer(); + encoder.onMalformedInput(CodingErrorAction.REPORT); + encoder.onUnmappableCharacter(CodingErrorAction.REPORT); + if (in != null) { + try { + // regression test for Harmony-1379 + encoder.encode(in); + fail("should throw MalformedInputException"); + } catch (MalformedInputException e) { + } + + encoder.reset(); + in.rewind(); + encoder.onMalformedInput(CodingErrorAction.IGNORE); + out = encoder.encode(in); + assertByteArray(out, addSurrogate(unibytes)); + + encoder.reset(); + in.rewind(); + encoder.onMalformedInput(CodingErrorAction.REPLACE); + out = encoder.encode(in); + assertByteArray(out, addSurrogate(unibytesWithRep)); + } + + // Unmapped Exception: + in = getUnmapCharBuffer(); + encoder.onMalformedInput(CodingErrorAction.REPORT); + encoder.onUnmappableCharacter(CodingErrorAction.REPORT); + if (in != null) { + encoder.reset(); + try { + encoder.encode(in); + fail("should throw UnmappableCharacterException"); + } catch (UnmappableCharacterException e) { + } + + encoder.reset(); + in.rewind(); + encoder.onUnmappableCharacter(CodingErrorAction.IGNORE); + out = encoder.encode(in); + assertByteArray(out, unibytes); + + encoder.reset(); + in.rewind(); + encoder.onUnmappableCharacter(CodingErrorAction.REPLACE); + out = encoder.encode(in); + assertByteArray(out, unibytesWithRep); + } + + // RuntimeException + try { + encoder.encode(getExceptionCharBuffer()); + fail("should throw runtime exception"); + } catch (RuntimeException e) { + } + } + + /* + * utility method, extract given bytebuffer to a string and compare with + * give string + */ + void assertByteArray(ByteBuffer out, byte[] expected) { + out = out.duplicate(); + if (out.position() != 0) { + out.flip(); + } + byte[] ba = new byte[out.limit() - out.position()]; + out.get(ba); + // byte[] ba = out.array(); + assertTrue(Arrays.equals(ba, expected)); + } + + /* + * Class under test for CoderResult encode(CharBuffer, ByteBuffer, boolean) + */ + public void testEncodeCharBufferByteBufferboolean() + throws CharacterCodingException { + ByteBuffer out = ByteBuffer.allocate(200); + CharBuffer in = CharBuffer.wrap(unistr); + // Null pointer + try { + encoder.encode(null, out, true); + fail("should throw null pointer exception"); + } catch (NullPointerException e) { + } + try { + encoder.encode(in, null, true); + fail("should throw null pointer exception"); + } catch (NullPointerException e) { + } + + // normal case, one complete operation + assertSame(encoder, encoder.reset()); + in.rewind(); + out.rewind(); + assertSame(CoderResult.UNDERFLOW, encoder.encode(in, out, true)); + assertEquals(out.limit(), 200); + assertTrue(out.position() > 0); + assertTrue(out.remaining() > 0); + assertEquals(out.capacity(), 200); + assertByteArray(out, addSurrogate(unibytes)); + in.rewind(); + + encoder.flush(out); + + // normal case, one complete operation, but call twice, first time set + // endOfInput to false + assertSame(encoder, encoder.reset()); + in.rewind(); + out = ByteBuffer.allocate(200); + assertSame(CoderResult.UNDERFLOW, encoder.encode(in, out, false)); + assertEquals(out.limit(), 200); + assertTrue(out.position() > 0); + assertTrue(out.remaining() > 0); + assertEquals(out.capacity(), 200); + assertByteArray(out, addSurrogate(unibytes)); + + in.rewind(); + assertSame(CoderResult.UNDERFLOW, encoder.encode(in, out, false)); + in.rewind(); + assertSame(CoderResult.UNDERFLOW, encoder.encode(in, out, true)); + assertEquals(out.limit(), 200); + assertTrue(out.position() > 0); + assertTrue(out.remaining() > 0); + assertEquals(out.capacity(), 200); + + assertByteArray(out, addSurrogate(duplicateByteArray(unibytes, 3))); + + // overflow + out = ByteBuffer.allocate(4); + assertSame(encoder, encoder.reset()); + in.rewind(); + out.rewind(); + assertSame(CoderResult.OVERFLOW, encoder.encode(in, out, true)); + assertEquals(out.limit(), 4); + assertEquals(out.position(), 4); + assertEquals(out.remaining(), 0); + assertEquals(out.capacity(), 4); + ByteBuffer temp = ByteBuffer.allocate(200); + out.flip(); + temp.put(out); + out = temp; + assertSame(CoderResult.UNDERFLOW, encoder.encode(in, out, true)); + assertEquals(out.limit(), 200); + assertTrue(out.position() > 0); + assertTrue(out.remaining() > 0); + assertEquals(out.capacity(), 200); + assertByteArray(out, addSurrogate(unibytes)); + + assertSame(encoder, encoder.reset()); + in.rewind(); + out = ByteBuffer.allocate(4); + assertSame(CoderResult.OVERFLOW, encoder.encode(in, out, false)); + assertEquals(out.limit(), 4); + assertEquals(out.position(), 4); + assertEquals(out.remaining(), 0); + assertEquals(out.capacity(), 4); + temp = ByteBuffer.allocate(200); + out.flip(); + temp.put(out); + out = temp; + assertSame(CoderResult.UNDERFLOW, encoder.encode(in, out, false)); + assertEquals(out.limit(), 200); + assertTrue(out.position() > 0); + assertTrue(out.remaining() > 0); + assertEquals(out.capacity(), 200); + assertByteArray(out, addSurrogate(unibytes)); + } + + void printByteBuffer(ByteBuffer buffer) { + System.out.println("print buffer"); + if (buffer.position() != 0) { + buffer.flip(); + } + byte[] ba = buffer.array(); + for (int i = 0; i < ba.length; i++) { + System.out.println(Integer.toHexString(ba[i])); + } + } + + public void testEncodeCharBufferByteBufferbooleanExceptionFalse() + throws CharacterCodingException { + implTestEncodeCharBufferByteBufferbooleanException(false); + } + + public void testEncodeCharBufferByteBufferbooleanExceptionTrue() + throws CharacterCodingException { + implTestEncodeCharBufferByteBufferbooleanException(true); + } + + private byte[] duplicateByteArray(byte[] ba, int times) { + byte[] result = new byte[ba.length * times]; + for (int i = 0; i < times; i++) { + System.arraycopy(ba, 0, result, i * ba.length, ba.length); + } + return result; + } + + protected void implTestEncodeCharBufferByteBufferbooleanException( + boolean endOfInput) throws CharacterCodingException { + ByteBuffer out = ByteBuffer.allocate(100); + + // MalformedException: + CharBuffer in = getMalformedCharBuffer(); + encoder.onMalformedInput(CodingErrorAction.REPORT); + encoder.onUnmappableCharacter(CodingErrorAction.REPORT); + if (in != null) { + encoder.reset(); + CoderResult r = encoder.encode(in, out, endOfInput); + assertTrue(r.isMalformed()); + + encoder.reset(); + out.clear(); + in.rewind(); + encoder.onMalformedInput(CodingErrorAction.IGNORE); + assertSame(CoderResult.UNDERFLOW, encoder.encode(in, out, + endOfInput)); + assertCodingErrorAction(endOfInput, out, in, unibytes); + + encoder.reset(); + out.clear(); + in.rewind(); + encoder.onMalformedInput(CodingErrorAction.REPLACE); + assertSame(CoderResult.UNDERFLOW, encoder.encode(in, out, + endOfInput)); + assertCodingErrorAction(endOfInput, out, in, unibytesWithRep); + } else { + // System.out.println("Cannot find malformed char buffer for " + // + cs.name()); + } + + // Unmapped Exception: + in = getUnmapCharBuffer(); + encoder.onMalformedInput(CodingErrorAction.REPORT); + encoder.onUnmappableCharacter(CodingErrorAction.REPORT); + if (in != null) { + encoder.reset(); + out.clear(); + assertTrue(encoder.encode(in, out, endOfInput).isUnmappable()); + + encoder.reset(); + out.clear(); + in.rewind(); + encoder.onUnmappableCharacter(CodingErrorAction.IGNORE); + assertSame(CoderResult.UNDERFLOW, encoder.encode(in, out, + endOfInput)); + assertCodingErrorAction(endOfInput, out, in, unibytes); + + encoder.reset(); + out.clear(); + in.rewind(); + encoder.onUnmappableCharacter(CodingErrorAction.REPLACE); + assertSame(CoderResult.UNDERFLOW, encoder.encode(in, out, + endOfInput)); + assertCodingErrorAction(endOfInput, out, in, unibytesWithRep); + } else { + // System.out.println("Cannot find unmapped char buffer for " + // + cs.name()); + } + + // RuntimeException + try { + encoder.encode(getExceptionCharBuffer()); + fail("should throw runtime exception"); + } catch (RuntimeException e) { + } + } + + private void assertCodingErrorAction(boolean endOfInput, ByteBuffer out, + CharBuffer in, byte[] expect) { + if (endOfInput) { + assertByteArray(out, addSurrogate(expect)); + } else { + in.rewind(); + assertSame(CoderResult.UNDERFLOW, encoder.encode(in, out, + endOfInput)); + in.rewind(); + assertSame(CoderResult.UNDERFLOW, encoder.encode(in, out, true)); + assertByteArray(out, addSurrogate(duplicateByteArray(expect, 3))); + } + } + + /* + * Class under test for CoderResult flush(ByteBuffer) + */ + public void testFlush() throws CharacterCodingException { + ByteBuffer out = ByteBuffer.allocate(6); + CharBuffer in = CharBuffer.wrap("aaa"); + assertEquals(in.remaining(), 3); + + // by encode facade, so that internal state will be wrong + encoder.encode(CharBuffer.wrap("testFlush"), ByteBuffer.allocate(20), + true); + assertSame(CoderResult.UNDERFLOW, encoder + .flush(ByteBuffer.allocate(50))); + } + + /* + * test isLegalReplacement(byte[]) + */ + public void testIsLegalReplacement() { + try { + encoder.isLegalReplacement(null); + fail("should throw null pointer exception"); + } catch (NullPointerException e) { + } + assertTrue(encoder.isLegalReplacement(specifiedReplacement)); + + assertTrue(encoder.isLegalReplacement(new byte[200])); + byte[] ba = getIllegalByteArray(); + if (ba != null) { + assertFalse(encoder.isLegalReplacement(ba)); + } + } + + public void testIsLegalReplacementEmptyArray() { + // ISO, ASC, GB, UTF8 encoder will throw exception in RI + // others will pass + // try { + assertTrue(encoder.isLegalReplacement(new byte[0])); + // fail("should throw ArrayIndexOutOfBoundsException"); + // } catch (ArrayIndexOutOfBoundsException e) { + // } + } + + public void testOnMalformedInput() { + assertSame(CodingErrorAction.REPORT, encoder.malformedInputAction()); + try { + encoder.onMalformedInput(null); + fail("should throw null pointer exception"); + } catch (IllegalArgumentException e) { + } + encoder.onMalformedInput(CodingErrorAction.IGNORE); + assertSame(CodingErrorAction.IGNORE, encoder.malformedInputAction()); + } + + public void testOnUnmappableCharacter() { + assertSame(CodingErrorAction.REPORT, encoder + .unmappableCharacterAction()); + try { + encoder.onUnmappableCharacter(null); + fail("should throw null pointer exception"); + } catch (IllegalArgumentException e) { + } + encoder.onUnmappableCharacter(CodingErrorAction.IGNORE); + assertSame(CodingErrorAction.IGNORE, encoder + .unmappableCharacterAction()); + } + + public void testReplacement() { + try { + encoder.replaceWith(null); + fail("should throw null pointer exception"); + } catch (IllegalArgumentException e) { + } + try { + encoder.replaceWith(new byte[0]); + fail("should throw null pointer exception"); + } catch (IllegalArgumentException e) { + } + try { + encoder.replaceWith(new byte[100]); + fail("should throw null pointer exception"); + } catch (IllegalArgumentException e) { + } + + byte[] nr = getLegalByteArray(); + assertSame(encoder, encoder.replaceWith(nr)); + assertSame(nr, encoder.replacement()); + + nr = getIllegalByteArray(); + try { + encoder.replaceWith(new byte[100]); + fail("should throw null pointer exception"); + } catch (IllegalArgumentException e) { + } + } + + protected byte[] getLegalByteArray() { + return new byte[] { 'a' }; + } + + protected byte[] getIllegalByteArray() { + return new byte[155]; + } + + /* + * Mock subclass of CharsetEncoder For protected method test + */ + public static class MockCharsetEncoder extends CharsetEncoder { + + boolean flushed = false; + + public boolean isFlushed() { + boolean result = flushed; + flushed = false; + return result; + } + + public boolean isLegalReplacement(byte[] ba) { + if (ba.length == 155) {// specified magic number, return false + return false; + } + return super.isLegalReplacement(ba); + } + + public MockCharsetEncoder(Charset cs, float aver, float max) { + super(cs, aver, max); + } + + public MockCharsetEncoder(Charset cs, float aver, float max, + byte[] replacement) { + super(cs, aver, max, replacement); + } + + protected CoderResult encodeLoop(CharBuffer in, ByteBuffer out) { + int inPosition = in.position(); + char[] input = new char[in.remaining()]; + in.get(input); + String result = new String(input); + if (result.startsWith("malform")) { + // reset the cursor to the error position + in.position(inPosition); + // in.position(0); + // set the error length + return CoderResult.malformedForLength("malform".length()); + } else if (result.startsWith("unmap")) { + // reset the cursor to the error position + in.position(inPosition); + // in.position(0); + // set the error length + return CoderResult.unmappableForLength("unmap".length()); + } else if (result.startsWith("runtime")) { + // reset the cursor to the error position + in.position(0); + // set the error length + throw new RuntimeException("runtime"); + } + int inLeft = input.length; + int outLeft = out.remaining(); + CoderResult r = CoderResult.UNDERFLOW; + int length = inLeft; + if (outLeft < inLeft) { + r = CoderResult.OVERFLOW; + length = outLeft; + in.position(inPosition + outLeft); + } + for (int i = 0; i < length; i++) { + out.put((byte) input[i]); + } + return r; + } + + protected CoderResult implFlush(ByteBuffer out) { + CoderResult result = super.implFlush(out); + int length = 0; + if (out.remaining() >= 5) { + length = 5; + result = CoderResult.UNDERFLOW; + flushed = true; + // for (int i = 0; i < length; i++) { + // out.put((byte)'f'); + // } + } else { + length = out.remaining(); + result = CoderResult.OVERFLOW; + } + return result; + } + + protected void implReplaceWith(byte[] ba) { + assertSame(ba, replacement()); + } + + } + + /* + * mock charset for test encoder initialization + */ + public static class MockCharset extends Charset { + protected MockCharset(String arg0, String[] arg1) { + super(arg0, arg1); + } + + public boolean contains(Charset arg0) { + return false; + } + + public CharsetDecoder newDecoder() { + return new CharsetDecoderTest.MockCharsetDecoder(this, + (float) AVER_BYTES, MAX_BYTES); + } + + public CharsetEncoder newEncoder() { + return new MockCharsetEncoder(this, (float) AVER_BYTES, MAX_BYTES); + } + } + +} diff --git a/nio_char/src/test/java/tests/api/java/nio/charset/CharsetProviderTest.java b/nio_char/src/test/java/tests/api/java/nio/charset/CharsetProviderTest.java new file mode 100644 index 0000000..74b56a9 --- /dev/null +++ b/nio_char/src/test/java/tests/api/java/nio/charset/CharsetProviderTest.java @@ -0,0 +1,438 @@ +/* Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package tests.api.java.nio.charset; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.OutputStreamWriter; +import java.nio.charset.Charset; +import java.nio.charset.UnsupportedCharsetException; +import java.nio.charset.spi.CharsetProvider; +import java.util.Iterator; +import java.util.Vector; + +import junit.framework.TestCase; +import tests.api.java.nio.charset.CharsetTest.MockCharset; +import tests.api.java.nio.charset.CharsetTest.MockSecurityManager; + +/** + * Test charset providers managed by Charset. + */ +public class CharsetProviderTest extends TestCase { + + // need to be modified, e.g., read from system property + static String PROP_CONFIG_FILE1 = "clear.tests.cp1"; + + static String CONFIG_FILE1 = null; + + + static MockCharset charset1 = new MockCharset("mockCharset00", + new String[] { "mockCharset01", "mockCharset02" }); + + static MockCharset charset2 = new MockCharset("mockCharset10", + new String[] { "mockCharset11", "mockCharset12" }); + + /** + * @param arg0 + */ + public CharsetProviderTest(String arg0) { + super(arg0); + CONFIG_FILE1 = System.getProperty("java.io.tmpdir")+"/bin/test"; + + String sep = System.getProperty("file.separator"); + + if (!CONFIG_FILE1.endsWith(sep)) { + CONFIG_FILE1 += sep; + } + CONFIG_FILE1 += "META-INF" + sep + "services" + sep + + "java.nio.charset.spi.CharsetProvider"; + } + + /* + * Write the string to the config file. + */ + private void setupFile(String path, String content) throws Exception { + String sep = System.getProperty("file.separator"); + int sepIndex = path.lastIndexOf(sep); + File f = new File(path.substring(0, sepIndex)); + f.mkdirs(); + + FileOutputStream fos = new FileOutputStream(path); + OutputStreamWriter writer = new OutputStreamWriter(fos);// , "UTF-8"); + try { + writer.write(content); + } finally { + writer.close(); + } + } + + /* + * Write the string to the config file. + */ + private void cleanupFile(String path) throws Exception { + File f = new File(path); + f.delete(); + } + + /* + * Test the method isSupported(String) with charset supported by some + * providers (multiple). + */ + public void testIsSupported_And_ForName_NormalProvider() throws Exception { + try { + assertFalse(Charset.isSupported("mockCharset10")); + assertFalse(Charset.isSupported("mockCharset11")); + assertFalse(Charset.isSupported("mockCharset12")); + try { + Charset.forName("mockCharset10"); + fail("Should throw UnsupportedCharsetException!"); + } catch (UnsupportedCharsetException e) { + // expected + } + try { + Charset.forName("mockCharset11"); + fail("Should throw UnsupportedCharsetException!"); + } catch (UnsupportedCharsetException e) { + // expected + } + try { + Charset.forName("mockCharset12"); + fail("Should throw UnsupportedCharsetException!"); + } catch (UnsupportedCharsetException e) { + // expected + } + + StringBuffer sb = new StringBuffer(); + sb.append("#comment\r"); + sb.append("\n"); + sb.append("\r\n"); + sb + .append(" \ttests.api.java.nio.charset.CharsetTest$MockCharsetProvider \t\n\r"); + sb + .append(" \ttests.api.java.nio.charset.CharsetTest$MockCharsetProvider \t"); + setupFile(CONFIG_FILE1, sb.toString()); + + sb = new StringBuffer(); + sb.append(" #comment\r"); + sb.append("\n"); + sb.append("\r\n"); + sb + .append(" \ttests.api.java.nio.charset.CharsetProviderTest$MockCharsetProvider \t\n\r"); + setupFile(CONFIG_FILE1, sb.toString()); + + assertTrue(Charset.isSupported("mockCharset10")); + // ignore case problem in mock, intended + assertTrue(Charset.isSupported("MockCharset11")); + assertTrue(Charset.isSupported("MockCharset12")); + assertTrue(Charset.isSupported("MOCKCharset10")); + // intended case problem in mock + assertTrue(Charset.isSupported("MOCKCharset11")); + assertTrue(Charset.isSupported("MOCKCharset12")); + + assertTrue(Charset.forName("mockCharset10") instanceof MockCharset); + assertTrue(Charset.forName("mockCharset11") instanceof MockCharset); + assertTrue(Charset.forName("mockCharset12") instanceof MockCharset); + + assertTrue(Charset.forName("mockCharset10") == charset2); + // intended case problem in mock + Charset.forName("mockCharset11"); + assertTrue(Charset.forName("mockCharset12") == charset2); + } finally { + cleanupFile(CONFIG_FILE1); + } + } + + /* + * Test the method isSupported(String) when the configuration file contains + * a non-existing class name. + */ + public void testIsSupported_NonExistingClass() throws Exception { + try { + StringBuffer sb = new StringBuffer(); + sb.append("impossible\r"); + setupFile(CONFIG_FILE1, sb.toString()); + + Charset.isSupported("impossible"); + fail("Should throw Error!"); + } catch (Error e) { + // expected + } finally { + cleanupFile(CONFIG_FILE1); + } + } + + /* + * Test the method isSupported(String) when the configuration file contains + * a non-CharsetProvider class name. + */ + public void testIsSupported_NotCharsetProviderClass() throws Exception { + try { + StringBuffer sb = new StringBuffer(); + sb.append("java.lang.String\r"); + setupFile(CONFIG_FILE1, sb.toString()); + + Charset.isSupported("impossible"); + fail("Should throw ClassCastException!"); + } catch (ClassCastException e) { + // expected + } finally { + cleanupFile(CONFIG_FILE1); + } + } + + /* + * Test the method isSupported(String) with insufficient privilege to use + * charset provider. + */ + public void testIsSupported_InsufficientPrivilege() throws Exception { + SecurityManager oldMan = System.getSecurityManager(); + System.setSecurityManager(new MockSecurityManager()); + try { + Charset.isSupported("UTF-8"); + + try { + StringBuffer sb = new StringBuffer(); + sb + .append("tests.api.java.nio.charset.CharsetProviderTest$MockCharsetProvider\r"); + setupFile(CONFIG_FILE1, sb.toString()); + + Charset.isSupported("gb180300000"); + fail("Should throw SecurityException!"); + } catch (SecurityException e) { + // expected + } finally { + cleanupFile(CONFIG_FILE1); + } + } finally { + System.setSecurityManager(oldMan); + } + } + + /* + * Test the method forName(String) when the charset provider supports a + * built-in charset. + */ + public void testForName_DuplicateWithBuiltInCharset() throws Exception { + try { + StringBuffer sb = new StringBuffer(); + sb + .append("tests.api.java.nio.charset.CharsetProviderTest$MockCharsetProviderACSII\r"); + setupFile(CONFIG_FILE1, sb.toString()); + + assertFalse(Charset.forName("us-ascii") instanceof MockCharset); + assertFalse(Charset.availableCharsets().get("us-ascii") instanceof MockCharset); + } finally { + cleanupFile(CONFIG_FILE1); + } + } + + /* + * Test the method forName(String) when the configuration file contains a + * non-existing class name. + */ + public void testForName_NonExistingClass() throws Exception { + try { + StringBuffer sb = new StringBuffer(); + sb.append("impossible\r"); + setupFile(CONFIG_FILE1, sb.toString()); + + Charset.forName("impossible"); + fail("Should throw Error!"); + } catch (Error e) { + // expected + } finally { + cleanupFile(CONFIG_FILE1); + } + } + + /* + * Test the method forName(String) when the configuration file contains a + * non-CharsetProvider class name. + */ + public void testForName_NotCharsetProviderClass() throws Exception { + try { + StringBuffer sb = new StringBuffer(); + sb.append("java.lang.String\r"); + setupFile(CONFIG_FILE1, sb.toString()); + + Charset.forName("impossible"); + fail("Should throw ClassCastException!"); + } catch (ClassCastException e) { + // expected + } finally { + cleanupFile(CONFIG_FILE1); + } + } + + /* + * Test the method availableCharsets() with charset supported by some + * providers (multiple). + */ + public void testAvailableCharsets_NormalProvider() throws Exception { + try { + assertFalse(Charset.availableCharsets() + .containsKey("mockCharset10")); + assertFalse(Charset.availableCharsets() + .containsKey("mockCharset11")); + assertFalse(Charset.availableCharsets() + .containsKey("mockCharset12")); + + StringBuffer sb = new StringBuffer(); + sb.append("#comment\r"); + sb.append("\n"); + sb.append("\r\n"); + sb + .append(" \ttests.api.java.nio.charset.CharsetTest$MockCharsetProvider \t\n\r"); + sb + .append(" \ttests.api.java.nio.charset.CharsetTest$MockCharsetProvider \t"); + setupFile(CONFIG_FILE1, sb.toString()); + + sb = new StringBuffer(); + sb.append("#comment\r"); + sb.append("\n"); + sb.append("\r\n"); + sb + .append(" \ttests.api.java.nio.charset.CharsetProviderTest$MockCharsetProvider \t\n\r"); + setupFile(CONFIG_FILE1, sb.toString()); + + assertTrue(Charset.availableCharsets().containsKey("mockCharset00")); + assertTrue(Charset.availableCharsets().containsKey("MOCKCharset00")); + assertTrue(Charset.availableCharsets().get("mockCharset00") instanceof MockCharset); + assertTrue(Charset.availableCharsets().get("MOCKCharset00") instanceof MockCharset); + assertFalse(Charset.availableCharsets() + .containsKey("mockCharset01")); + assertFalse(Charset.availableCharsets() + .containsKey("mockCharset02")); + + assertTrue(Charset.availableCharsets().get("mockCharset10") == charset2); + assertTrue(Charset.availableCharsets().get("MOCKCharset10") == charset2); + assertFalse(Charset.availableCharsets() + .containsKey("mockCharset11")); + assertFalse(Charset.availableCharsets() + .containsKey("mockCharset12")); + + assertTrue(Charset.availableCharsets().containsKey("mockCharset10")); + assertTrue(Charset.availableCharsets().containsKey("MOCKCharset10")); + assertTrue(Charset.availableCharsets().get("mockCharset10") == charset2); + assertFalse(Charset.availableCharsets() + .containsKey("mockCharset11")); + assertFalse(Charset.availableCharsets() + .containsKey("mockCharset12")); + } finally { + cleanupFile(CONFIG_FILE1); + } + } + + /* + * Test the method availableCharsets(String) when the configuration file + * contains a non-existing class name. + */ + public void testAvailableCharsets_NonExistingClass() throws Exception { + try { + StringBuffer sb = new StringBuffer(); + sb.append("impossible\r"); + setupFile(CONFIG_FILE1, sb.toString()); + + Charset.availableCharsets(); + fail("Should throw Error!"); + } catch (Error e) { + // expected + } finally { + cleanupFile(CONFIG_FILE1); + } + } + + /* + * Test the method availableCharsets(String) when the configuration file + * contains a non-CharsetProvider class name. + */ + public void testAvailableCharsets_NotCharsetProviderClass() + throws Exception { + try { + StringBuffer sb = new StringBuffer(); + sb.append("java.lang.String\r"); + setupFile(CONFIG_FILE1, sb.toString()); + + Charset.availableCharsets(); + fail("Should throw ClassCastException!"); + } catch (ClassCastException e) { + // expected + } finally { + cleanupFile(CONFIG_FILE1); + } + } + + /* + * Test the method availableCharsets(String) when the configuration file + * contains an illegal string. + */ + public void testAvailableCharsets_IllegalString() throws Exception { + try { + StringBuffer sb = new StringBuffer(); + sb.append("java String\r"); + setupFile(CONFIG_FILE1, sb.toString()); + + Charset.availableCharsets(); + fail("Should throw Error!"); + } catch (Error e) { + // expected + } finally { + cleanupFile(CONFIG_FILE1); + } + } + + /* + * Mock charset provider. + */ + public static class MockCharsetProvider extends CharsetProvider { + + public Charset charsetForName(String charsetName) { + if ("MockCharset10".equalsIgnoreCase(charsetName) + || "MockCharset11".equalsIgnoreCase(charsetName) + || "MockCharset12".equalsIgnoreCase(charsetName)) { + return charset2; + } + return null; + } + + public Iterator charsets() { + Vector v = new Vector(); + v.add(charset2); + return v.iterator(); + } + } + + /* + * Another mock charset provider providing build-in charset "ascii". + */ + public static class MockCharsetProviderACSII extends CharsetProvider { + + public Charset charsetForName(String charsetName) { + if ("US-ASCII".equalsIgnoreCase(charsetName) + || "ASCII".equalsIgnoreCase(charsetName)) { + return new MockCharset("US-ASCII", new String[] { "ASCII" }); + } + return null; + } + + public Iterator charsets() { + Vector v = new Vector(); + v.add(new MockCharset("US-ASCII", new String[] { "ASCII" })); + return v.iterator(); + } + } + +} diff --git a/nio_char/src/test/java/tests/api/java/nio/charset/CharsetTest.java b/nio_char/src/test/java/tests/api/java/nio/charset/CharsetTest.java new file mode 100644 index 0000000..daf24a5 --- /dev/null +++ b/nio_char/src/test/java/tests/api/java/nio/charset/CharsetTest.java @@ -0,0 +1,869 @@ +/* Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package tests.api.java.nio.charset; + +import java.nio.ByteBuffer; +import java.nio.CharBuffer; +import java.nio.charset.Charset; +import java.nio.charset.CharsetDecoder; +import java.nio.charset.CharsetEncoder; +import java.nio.charset.CoderResult; +import java.nio.charset.IllegalCharsetNameException; +import java.nio.charset.UnsupportedCharsetException; +import java.nio.charset.spi.CharsetProvider; +import java.security.Permission; +import java.util.Iterator; +import java.util.Locale; +import java.util.SortedMap; +import java.util.Vector; + +import junit.framework.TestCase; + +/** + * Test class java.nio.Charset. + */ +public class CharsetTest extends TestCase { + + static MockCharset charset1 = new MockCharset("mockCharset00", + new String[] { "mockCharset01", "mockCharset02" }); + + static MockCharset charset2 = new MockCharset("mockCharset10", + new String[] { "mockCharset11", "mockCharset12" }); + + /* + * @see TestCase#setUp() + */ + protected void setUp() throws Exception { + super.setUp(); + } + + /* + * @see TestCase#tearDown() + */ + protected void tearDown() throws Exception { + super.tearDown(); + } + + /* + * Test the required 6 charsets are supported. + */ + public void testRequiredCharsetSupported() { + assertTrue(Charset.isSupported("US-ASCII")); + assertTrue(Charset.isSupported("ASCII")); + assertTrue(Charset.isSupported("ISO-8859-1")); + assertTrue(Charset.isSupported("ISO8859_1")); + assertTrue(Charset.isSupported("UTF-8")); + assertTrue(Charset.isSupported("UTF8")); + assertTrue(Charset.isSupported("UTF-16")); + assertTrue(Charset.isSupported("UTF-16BE")); + assertTrue(Charset.isSupported("UTF-16LE")); + + Charset c1 = Charset.forName("US-ASCII"); + assertEquals("US-ASCII", Charset.forName("US-ASCII").name()); + assertEquals("US-ASCII", Charset.forName("ASCII").name()); + assertEquals("ISO-8859-1", Charset.forName("ISO-8859-1").name()); + assertEquals("ISO-8859-1", Charset.forName("ISO8859_1").name()); + assertEquals("UTF-8", Charset.forName("UTF-8").name()); + assertEquals("UTF-8", Charset.forName("UTF8").name()); + assertEquals("UTF-16", Charset.forName("UTF-16").name()); + assertEquals("UTF-16BE", Charset.forName("UTF-16BE").name()); + assertEquals("UTF-16LE", Charset.forName("UTF-16LE").name()); + + assertNotSame(Charset.availableCharsets(), Charset.availableCharsets()); + // assertSame(Charset.forName("US-ASCII"), Charset.availableCharsets() + // .get("US-ASCII")); + // assertSame(Charset.forName("US-ASCII"), c1); + assertTrue(Charset.availableCharsets().containsKey("US-ASCII")); + assertTrue(Charset.availableCharsets().containsKey("ISO-8859-1")); + assertTrue(Charset.availableCharsets().containsKey("UTF-8")); + assertTrue(Charset.availableCharsets().containsKey("UTF-16")); + assertTrue(Charset.availableCharsets().containsKey("UTF-16BE")); + assertTrue(Charset.availableCharsets().containsKey("UTF-16LE")); + } + + /* + * Test the method isSupported(String) with null. + */ + public void testIsSupported_Null() { + try { + Charset.isSupported(null); + fail("Should throw IllegalArgumentException!"); + } catch (IllegalArgumentException e) { + // expected + } + } + + /* + * Test the method isSupported(String) with empty string. + * + */ + public void testIsSupported_EmptyString() { + try { + Charset.isSupported(""); + } catch (IllegalArgumentException e) { + // FIXME: Commented out since RI does throw IAE + // fail("Should not throw IllegalArgumentException!"); + } + } + + /* + * Test the method isSupported(String) with a string starting with ".". + * + */ + public void testIsSupported_InvalidInitialCharacter() { + try { + Charset.isSupported(".char"); + } catch (IllegalArgumentException e) { + fail("Should not throw IllegalArgumentException!"); + } + } + + /* + * Test the method isSupported(String) with illegal charset name. + */ + public void testIsSupported_IllegalName() { + try { + Charset.isSupported(" ///#$$"); + fail("Should throw IllegalCharsetNameException!"); + } catch (IllegalCharsetNameException e) { + // expected + } + } + + /* + * Test the method isSupported(String) with not supported charset name. + */ + public void testIsSupported_NotSupported() { + assertFalse(Charset.isSupported("impossible")); + } + + /* + * Test the method forName(String) with null. + */ + public void testForName_Null() { + try { + Charset.forName(null); + fail("Should throw IllegalArgumentException!"); + } catch (IllegalArgumentException e) { + // expected + } + } + + /* + * Test the method forName(String) with empty string. + */ + public void testForName_EmptyString() { + try { + Charset.forName(""); + fail("Should throw IllegalArgumentException!"); + } catch (IllegalArgumentException e) { + // expected + } + } + + /* + * Test the method forName(String) with a string starting with ".". + */ + public void testForName_InvalidInitialCharacter() { + try { + Charset.forName(".char"); + fail("Should throw IllegalArgumentException!"); + } catch (IllegalArgumentException e) { + // expected + } + } + + /* + * Test the method forName(String) with illegal charset name. + */ + public void testForName_IllegalName() { + try { + Charset.forName(" ///#$$"); + fail("Should throw IllegalCharsetNameException!"); + } catch (IllegalCharsetNameException e) { + // expected + } + } + + /* + * Test the method forName(String) with not supported charset name. + */ + public void testForName_NotSupported() { + try { + Charset.forName("impossible"); + fail("Should throw UnsupportedCharsetException!"); + } catch (UnsupportedCharsetException e) { + // expected + } + } + + /* + * Test the constructor with normal parameter values. + */ + public void testConstructor_Normal() { + final String mockName = "mockChar1234567890abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ.:-_"; + MockCharset c = new MockCharset(mockName, new String[] { "mock" }); + assertEquals(mockName, c.name()); + assertEquals(mockName, c.displayName()); + assertEquals(mockName, c.displayName(Locale.getDefault())); + assertEquals("mock", c.aliases().toArray()[0]); + assertEquals(1, c.aliases().toArray().length); + } + + /* + * Test the constructor with empty canonical name. + * + */ + public void testConstructor_EmptyCanonicalName() { + try { + new MockCharset("", new String[0]); + } catch (IllegalCharsetNameException e) { + // FIXME: Commented out since RI does throw IAE + // fail("Should not throw IllegalArgumentException!"); + } + } + + /* + * Test the constructor with illegal canonical name: starting with neither a + * digit nor a letter. + * + */ + public void testConstructor_IllegalCanonicalName_Initial() { + try { + new MockCharset("-123", new String[] { "mock" }); + } catch (IllegalCharsetNameException e) { + fail("Should not throw IllegalArgumentException!"); + } + } + + /* + * Test the constructor with illegal canonical name, illegal character in + * the middle. + */ + public void testConstructor_IllegalCanonicalName_Middle() { + try { + new MockCharset("1%%23", new String[] { "mock" }); + fail("Should throw IllegalCharsetNameException!"); + } catch (IllegalCharsetNameException e) { + // expected + } + try { + new MockCharset("1//23", new String[] { "mock" }); + fail("Should throw IllegalCharsetNameException!"); + } catch (IllegalCharsetNameException e) { + // expected + } + } + + /* + * Test the constructor with null canonical name. + */ + public void testConstructor_NullCanonicalName() { + try { + MockCharset c = new MockCharset(null, new String[] { "mock" }); + fail("Should throw NullPointerException!"); + } catch (NullPointerException e) { + // expected + } + } + + /* + * Test the constructor with null aliases. + */ + public void testConstructor_NullAliases() { + MockCharset c = new MockCharset("mockChar", null); + assertEquals("mockChar", c.name()); + assertEquals("mockChar", c.displayName()); + assertEquals("mockChar", c.displayName(Locale.getDefault())); + assertEquals(0, c.aliases().toArray().length); + } + + /* + * Test the constructor with a null aliases. + */ + public void testConstructor_NullAliase() { + try { + new MockCharset("mockChar", new String[] { "mock", null }); + fail("Should throw NullPointerException!"); + } catch (NullPointerException e) { + // expected + } + } + + /* + * Test the constructor with no aliases. + */ + public void testConstructor_NoAliases() { + MockCharset c = new MockCharset("mockChar", new String[0]); + assertEquals("mockChar", c.name()); + assertEquals("mockChar", c.displayName()); + assertEquals("mockChar", c.displayName(Locale.getDefault())); + assertEquals(0, c.aliases().toArray().length); + } + + /* + * Test the constructor with empty aliases. + * + */ + public void testConstructor_EmptyAliases() { + try { + new MockCharset("mockChar", new String[] { "" }); + } catch (IllegalCharsetNameException e) { + // FIXME: Commented out since RI does throw IAE + // fail("Should not throw IllegalArgumentException!"); + } + } + + /* + * Test the constructor with illegal aliases: starting with neither a digit + * nor a letter. + * + */ + public void testConstructor_IllegalAliases_Initial() { + try { + new MockCharset("mockChar", new String[] { "mock", "-123" }); + } catch (IllegalCharsetNameException e) { + fail("Should not throw IllegalArgumentException!"); + } + } + + /* + * Test the constructor with illegal aliase, illegal character in the + * middle. + */ + public void testConstructor_IllegalAliases_Middle() { + try { + new MockCharset("mockChar", new String[] { "mock", "22##ab" }); + fail("Should throw IllegalCharsetNameException!"); + } catch (IllegalCharsetNameException e) { + // expected + } + try { + new MockCharset("mockChar", new String[] { "mock", "22%%ab" }); + fail("Should throw IllegalCharsetNameException!"); + } catch (IllegalCharsetNameException e) { + // expected + } + } + + /* + * Test the method aliases() with multiple aliases. Most conditions have + * been tested in the testcases for the constructors. + */ + public void testAliases_Multiple() { + final String mockName = "mockChar1234567890abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ.:-_"; + MockCharset c = new MockCharset("mockChar", new String[] { "mock", + mockName, "mock2" }); + assertEquals("mockChar", c.name()); + assertEquals(3, c.aliases().size()); + assertTrue(c.aliases().contains("mock")); + assertTrue(c.aliases().contains(mockName)); + assertTrue(c.aliases().contains("mock2")); + + try { + c.aliases().clear(); + fail("Should throw UnsupportedOperationException!"); + } catch (UnsupportedOperationException e) { + // expected + } + } + + /* + * Test the method aliases() with duplicate aliases, one same with its + * canonical name. + */ + public void testAliases_Duplicate() { + final String mockName = "mockChar1234567890abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ.:-_"; + MockCharset c = new MockCharset("mockChar", new String[] { "mockChar", + "mock", mockName, "mock", "mockChar", "mock", "mock2" }); + assertEquals("mockChar", c.name()); + assertEquals(4, c.aliases().size()); + assertTrue(c.aliases().contains("mockChar")); + assertTrue(c.aliases().contains("mock")); + assertTrue(c.aliases().contains(mockName)); + assertTrue(c.aliases().contains("mock2")); + } + + /* + * Test the method canEncode(). Test the default return value. + */ + public void testCanEncode() { + MockCharset c = new MockCharset("mock", null); + assertTrue(c.canEncode()); + } + + /* + * Test the method isRegistered(). Test the default return value. + */ + public void testIsRegistered() { + MockCharset c = new MockCharset("mock", null); + assertTrue(c.isRegistered()); + } + + /* + * The name() method has been tested by the testcases for the constructor. + */ + public void testName() { + // already covered by testConstructor_XXX series + } + + /* + * The displayName() method have been tested by the testcases for the + * constructor. + */ + public void testDisplayName() { + // already covered by testConstructor_XXX series + } + + /* + * Test displayName(Locale) with null. + */ + public void testDisplayName_Locale_Null() { + MockCharset c = new MockCharset("mock", null); + assertEquals("mock", c.displayName(null)); + } + + /* + * Test the method compareTo(Object) with normal conditions. + */ + public void testCompareTo_Normal() { + MockCharset c1 = new MockCharset("mock", null); + assertEquals(0, c1.compareTo(c1)); + + MockCharset c2 = new MockCharset("Mock", null); + assertEquals(0, c1.compareTo(c2)); + + c2 = new MockCharset("mock2", null); + assertTrue(c1.compareTo(c2) < 0); + assertTrue(c2.compareTo(c1) > 0); + + c2 = new MockCharset("mack", null); + assertTrue(c1.compareTo(c2) > 0); + assertTrue(c2.compareTo(c1) < 0); + + c2 = new MockCharset("m.", null); + assertTrue(c1.compareTo(c2) > 0); + assertTrue(c2.compareTo(c1) < 0); + + c2 = new MockCharset("m:", null); + assertEquals("mock".compareToIgnoreCase("m:"), c1.compareTo(c2)); + assertEquals("m:".compareToIgnoreCase("mock"), c2.compareTo(c1)); + + c2 = new MockCharset("m-", null); + assertTrue(c1.compareTo(c2) > 0); + assertTrue(c2.compareTo(c1) < 0); + + c2 = new MockCharset("m_", null); + assertTrue(c1.compareTo(c2) > 0); + assertTrue(c2.compareTo(c1) < 0); + } + + /* + * Test the method compareTo(Object) with null param. + */ + public void testCompareTo_Null() { + MockCharset c1 = new MockCharset("mock", null); + try { + c1.compareTo(null); + fail("Should throw NullPointerException!"); + } catch (NullPointerException e) { + // expected + } + } + + /* + * Test the method compareTo(Object) with another kind of charset object. + */ + public void testCompareTo_DiffCharsetClass() { + MockCharset c1 = new MockCharset("mock", null); + MockCharset2 c2 = new MockCharset2("Mock", new String[] { "myname" }); + assertEquals(0, c1.compareTo(c2)); + assertEquals(0, c2.compareTo(c1)); + } + + /* + * Test the method equals(Object) with null param. + */ + public void testEquals_Normal() { + MockCharset c1 = new MockCharset("mock", null); + MockCharset2 c2 = new MockCharset2("mock", null); + assertTrue(c1.equals(c2)); + assertTrue(c2.equals(c1)); + + c2 = new MockCharset2("Mock", null); + assertFalse(c1.equals(c2)); + assertFalse(c2.equals(c1)); + } + + /* + * Test the method equals(Object) with normal conditions. + */ + public void testEquals_Null() { + MockCharset c1 = new MockCharset("mock", null); + assertFalse(c1.equals(null)); + } + + /* + * Test the method equals(Object) with another kind of charset object. + */ + public void testEquals_NonCharsetObject() { + MockCharset c1 = new MockCharset("mock", null); + assertFalse(c1.equals("test")); + } + + /* + * Test the method equals(Object) with another kind of charset object. + */ + public void testEquals_DiffCharsetClass() { + MockCharset c1 = new MockCharset("mock", null); + MockCharset2 c2 = new MockCharset2("mock", null); + assertTrue(c1.equals(c2)); + assertTrue(c2.equals(c1)); + } + + /* + * Test the method hashCode(). + */ + public void testHashCode_DiffCharsetClass() { + MockCharset c1 = new MockCharset("mock", null); + assertEquals(c1.hashCode(), "mock".hashCode()); + + final String mockName = "mockChar1234567890abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ.:-_"; + c1 = new MockCharset(mockName, new String[] { "mockChar", "mock", + mockName, "mock", "mockChar", "mock", "mock2" }); + assertEquals(mockName.hashCode(), c1.hashCode()); + } + + /* + * Test the method encode(CharBuffer) under normal condition. + */ + public void testEncode_CharBuffer_Normal() throws Exception { + MockCharset c1 = new MockCharset("testEncode_CharBuffer_Normal_mock", null); + ByteBuffer bb = c1.encode(CharBuffer.wrap("abcdefg")); + assertEquals("abcdefg", new String(bb.array(), "iso8859-1")); + bb = c1.encode(CharBuffer.wrap("")); + assertEquals("", new String(bb.array(), "iso8859-1")); + } + + /* + * Test the method encode(CharBuffer) with an unmappable char. + */ + public void testEncode_CharBuffer_Unmappable() throws Exception { + Charset c1 = Charset.forName("iso8859-1"); + ByteBuffer bb = c1.encode(CharBuffer.wrap("abcd\u5D14efg")); + assertEquals(new String(bb.array(), "iso8859-1"), "abcd" + + new String(c1.newEncoder().replacement(), "iso8859-1") + + "efg"); + } + + /* + * Test the method encode(CharBuffer) with null CharBuffer. + */ + public void testEncode_CharBuffer_NullCharBuffer() { + MockCharset c = new MockCharset("mock", null); + try { + c.encode((CharBuffer) null); + fail("Should throw NullPointerException!"); + } catch (NullPointerException e) { + // expected + } + } + + /* + * Test the method encode(CharBuffer) with null encoder. + */ + public void testEncode_CharBuffer_NullEncoder() { + MockCharset2 c = new MockCharset2("mock2", null); + try { + c.encode(CharBuffer.wrap("hehe")); + fail("Should throw NullPointerException!"); + } catch (NullPointerException e) { + // expected + } + } + + /* + * Test the method encode(String) under normal condition. + */ + public void testEncode_String_Normal() throws Exception { + MockCharset c1 = new MockCharset("testEncode_String_Normal_mock", null); + ByteBuffer bb = c1.encode("abcdefg"); + assertEquals("abcdefg", new String(bb.array(), "iso8859-1")); + bb = c1.encode(""); + assertEquals("", new String(bb.array(), "iso8859-1")); + } + + /* + * Test the method encode(String) with an unmappable char. + */ + public void testEncode_String_Unmappable() throws Exception { + Charset c1 = Charset.forName("iso8859-1"); + ByteBuffer bb = c1.encode("abcd\u5D14efg"); + assertEquals(new String(bb.array(), "iso8859-1"), "abcd" + + new String(c1.newEncoder().replacement(), "iso8859-1") + + "efg"); + } + + /* + * Test the method encode(String) with null CharBuffer. + */ + public void testEncode_String_NullString() { + MockCharset c = new MockCharset("mock", null); + try { + c.encode((String) null); + fail("Should throw NullPointerException!"); + } catch (NullPointerException e) { + // expected + } + } + + /* + * Test the method encode(String) with null encoder. + */ + public void testEncode_String_NullEncoder() { + + MockCharset2 c = new MockCharset2("mock2", null); + try { + c.encode("hehe"); + fail("Should throw NullPointerException!"); + } catch (NullPointerException e) { + // expected + } + } + + /* + * Test the method decode(ByteBuffer) under normal condition. + */ + public void testDecode_Normal() throws Exception { + MockCharset c1 = new MockCharset("mock", null); + CharBuffer cb = c1.decode(ByteBuffer.wrap("abcdefg" + .getBytes("iso8859-1"))); + assertEquals("abcdefg", new String(cb.array())); + cb = c1.decode(ByteBuffer.wrap("".getBytes("iso8859-1"))); + assertEquals("", new String(cb.array())); + } + + /* + * Test the method decode(ByteBuffer) with a malformed input. + */ + public void testDecode_Malformed() throws Exception { + Charset c1 = Charset.forName("iso8859-1"); + CharBuffer cb = c1.decode(ByteBuffer.wrap("abcd\u5D14efg" + .getBytes("iso8859-1"))); + byte[] replacement = c1.newEncoder().replacement(); + assertEquals(new String(cb.array()), "abcd" + new String(replacement) + + "efg"); + } + + /* + * Test the method decode(ByteBuffer) with null CharBuffer. + */ + public void testDecode_NullByteBuffer() { + MockCharset c = new MockCharset("mock", null); + try { + c.decode(null); + fail("Should throw NullPointerException!"); + } catch (NullPointerException e) { + // expected + } + } + + /* + * Test the method decode(ByteBuffer) with null encoder. + */ + public void testDecode_NullDecoder() { + MockCharset2 c = new MockCharset2("mock2", null); + try { + c.decode(ByteBuffer.wrap("hehe".getBytes())); + fail("Should throw NullPointerException!"); + } catch (NullPointerException e) { + // expected + } + } + + /* + * Test the method toString(). + */ + public void testToString() { + MockCharset c1 = new MockCharset("mock", null); + assertTrue(-1 != c1.toString().indexOf("mock")); + } + + /** + * @tests java.nio.charset.Charset#availableCharsets() + */ + public void test_availableCharsets() throws Exception { + // regression test for Harmony-1051 + ClassLoader originalClassLoader = Thread.currentThread() + .getContextClassLoader(); + try { + Thread.currentThread().setContextClassLoader(null); + SortedMap<String, Charset> charsets = Charset.availableCharsets(); + // make sure "mockCharset00" is loaded by MockCharsetProvider + assertTrue(charsets.containsKey("mockCharset00")); + } finally { + Thread.currentThread().setContextClassLoader(originalClassLoader); + } + } + + /** + * @tests java.nio.charset.Charset#availableCharsets() + */ + public void test_forNameLString() throws Exception { + // regression test for Harmony-1051 + ClassLoader originalClassLoader = Thread.currentThread() + .getContextClassLoader(); + try { + Thread.currentThread().setContextClassLoader(null); + // make sure "mockCharset00" is loaded by MockCharsetProvider + assertNotNull(Charset.forName("mockCharset00")); + } finally { + Thread.currentThread().setContextClassLoader(originalClassLoader); + } + } + + /* + * Mock charset class. + */ + static final class MockCharset extends Charset { + + public MockCharset(String canonicalName, String[] aliases) { + super(canonicalName, aliases); + } + + public boolean contains(Charset cs) { + return false; + } + + public CharsetDecoder newDecoder() { + return new MockDecoder(this); + } + + public CharsetEncoder newEncoder() { + return new MockEncoder(this); + } + } + + /* + * Another mock charset class. + */ + static class MockCharset2 extends Charset { + + public MockCharset2(String canonicalName, String[] aliases) { + super(canonicalName, aliases); + } + + public boolean contains(Charset cs) { + return false; + } + + public CharsetDecoder newDecoder() { + return null; + } + + public CharsetEncoder newEncoder() { + return null; + } + } + + /* + * Mock encoder. + */ + static class MockEncoder extends java.nio.charset.CharsetEncoder { + + public MockEncoder(Charset cs) { + super(cs, 1, 3, new byte[] { (byte) '?' }); + } + + protected CoderResult encodeLoop(CharBuffer in, ByteBuffer out) { + while (in.remaining() > 0) { + out.put((byte) in.get()); + // out.put((byte) '!'); + } + return CoderResult.UNDERFLOW; + } + } + + /* + * Mock decoder. + */ + static class MockDecoder extends java.nio.charset.CharsetDecoder { + + public MockDecoder(Charset cs) { + super(cs, 1, 10); + } + + protected CoderResult decodeLoop(ByteBuffer in, CharBuffer out) { + while (in.remaining() > 0) { + out.put((char) in.get()); + } + return CoderResult.UNDERFLOW; + } + } + + /* + * Mock charset provider. + */ + public static class MockCharsetProvider extends CharsetProvider { + + public Charset charsetForName(String charsetName) { + if ("MockCharset00".equalsIgnoreCase(charsetName) + || "MockCharset01".equalsIgnoreCase(charsetName) + || "MockCharset02".equalsIgnoreCase(charsetName)) { + return new MockCharset("mockCharset00", new String[] { + "mockCharset01", "mockCharset02" }); + } + return null; + } + + public Iterator charsets() { + Vector v = new Vector(); + v.add(new MockCharset("mockCharset00", new String[] { + "mockCharset01", "mockCharset02" })); + return v.iterator(); + } + } + + /* + * Used to grant all permissions except charset provider access. + */ + public static class MockSecurityManager extends SecurityManager { + + public MockSecurityManager() { + } + + public void checkPermission(Permission perm) { + // grant all permissions except logging control + if (perm instanceof RuntimePermission) { + RuntimePermission rp = (RuntimePermission) perm; + if (rp.getName().equals("charsetProvider")) { + throw new SecurityException(); + } + } + } + + public void checkPermission(Permission perm, Object context) { + // grant all permissions except logging control + if (perm instanceof RuntimePermission) { + RuntimePermission rp = (RuntimePermission) perm; + if (rp.getName().equals("charsetProvider")) { + throw new SecurityException(); + } + } + } + } +} diff --git a/nio_char/src/test/java/tests/api/java/nio/charset/CoderResultTest.java b/nio_char/src/test/java/tests/api/java/nio/charset/CoderResultTest.java new file mode 100644 index 0000000..d362a57 --- /dev/null +++ b/nio_char/src/test/java/tests/api/java/nio/charset/CoderResultTest.java @@ -0,0 +1,265 @@ +/* Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package tests.api.java.nio.charset; + +import java.nio.BufferOverflowException; +import java.nio.BufferUnderflowException; +import java.nio.charset.CoderResult; +import java.nio.charset.MalformedInputException; +import java.nio.charset.UnmappableCharacterException; + +import junit.framework.TestCase; + +/** + * Test class java.nio.charset.CoderResult. + */ +public class CoderResultTest extends TestCase { + + /* + * @see TestCase#setUp() + */ + protected void setUp() throws Exception { + super.setUp(); + } + + /* + * @see TestCase#tearDown() + */ + protected void tearDown() throws Exception { + super.tearDown(); + } + + /* + * Test the constant OVERFLOW and UNDERFLOW. + */ + public void testConstants() throws Exception { + assertNotSame(CoderResult.OVERFLOW, CoderResult.UNDERFLOW); + + assertNotNull(CoderResult.OVERFLOW); + assertFalse(CoderResult.OVERFLOW.isError()); + assertFalse(CoderResult.OVERFLOW.isMalformed()); + assertFalse(CoderResult.OVERFLOW.isUnderflow()); + assertFalse(CoderResult.OVERFLOW.isUnmappable()); + assertTrue(CoderResult.OVERFLOW.isOverflow()); + assertTrue(CoderResult.OVERFLOW.toString().indexOf("OVERFLOW") != -1); + try { + CoderResult.OVERFLOW.throwException(); + fail("Should throw BufferOverflowException"); + } catch (BufferOverflowException ex) { + // expected + } + try { + CoderResult.OVERFLOW.length(); + fail("Should throw UnsupportedOperationException"); + } catch (UnsupportedOperationException ex) { + // expected + } + + assertNotNull(CoderResult.UNDERFLOW); + assertFalse(CoderResult.UNDERFLOW.isError()); + assertFalse(CoderResult.UNDERFLOW.isMalformed()); + assertTrue(CoderResult.UNDERFLOW.isUnderflow()); + assertFalse(CoderResult.UNDERFLOW.isUnmappable()); + assertFalse(CoderResult.UNDERFLOW.isOverflow()); + assertTrue(CoderResult.UNDERFLOW.toString().indexOf("UNDERFLOW") != -1); + try { + CoderResult.UNDERFLOW.throwException(); + fail("Should throw BufferOverflowException"); + } catch (BufferUnderflowException ex) { + // expected + } + try { + CoderResult.UNDERFLOW.length(); + fail("Should throw UnsupportedOperationException"); + } catch (UnsupportedOperationException ex) { + // expected + } + } + + /** + * Test method isError(). + * + */ + public void testIsError() { + assertFalse(CoderResult.UNDERFLOW.isError()); + assertFalse(CoderResult.OVERFLOW.isError()); + assertTrue(CoderResult.malformedForLength(1).isError()); + assertTrue(CoderResult.unmappableForLength(1).isError()); + } + + /** + * Test method isMalformed(). + * + */ + public void testIsMalformed() { + assertFalse(CoderResult.UNDERFLOW.isMalformed()); + assertFalse(CoderResult.OVERFLOW.isMalformed()); + assertTrue(CoderResult.malformedForLength(1).isMalformed()); + assertFalse(CoderResult.unmappableForLength(1).isMalformed()); + } + + /** + * Test method isMalformed(). + * + */ + public void testIsUnmappable() { + assertFalse(CoderResult.UNDERFLOW.isUnmappable()); + assertFalse(CoderResult.OVERFLOW.isUnmappable()); + assertFalse(CoderResult.malformedForLength(1).isUnmappable()); + assertTrue(CoderResult.unmappableForLength(1).isUnmappable()); + } + + /** + * Test method isOverflow(). + * + */ + public void testIsOverflow() { + assertFalse(CoderResult.UNDERFLOW.isOverflow()); + assertTrue(CoderResult.OVERFLOW.isOverflow()); + assertFalse(CoderResult.malformedForLength(1).isOverflow()); + assertFalse(CoderResult.unmappableForLength(1).isOverflow()); + } + + /** + * Test method isUnderflow(). + * + */ + public void testIsUnderflow() { + assertTrue(CoderResult.UNDERFLOW.isUnderflow()); + assertFalse(CoderResult.OVERFLOW.isUnderflow()); + assertFalse(CoderResult.malformedForLength(1).isUnderflow()); + assertFalse(CoderResult.unmappableForLength(1).isUnderflow()); + } + + /** + * Test method length(). + * + */ + public void testLength() { + try { + CoderResult.UNDERFLOW.length(); + fail("Should throw UnsupportedOperationException"); + } catch (UnsupportedOperationException ex) { + // expected + } + try { + CoderResult.OVERFLOW.length(); + fail("Should throw UnsupportedOperationException"); + } catch (UnsupportedOperationException ex) { + // expected + } + + assertEquals(CoderResult.malformedForLength(1).length(), 1); + assertEquals(CoderResult.unmappableForLength(1).length(), 1); + } + + /** + * Test method malformedForLength(int). + * + */ + public void testMalformedForLength() { + assertNotNull(CoderResult.malformedForLength(Integer.MAX_VALUE)); + assertNotNull(CoderResult.malformedForLength(1)); + assertSame(CoderResult.malformedForLength(1), CoderResult + .malformedForLength(1)); + assertNotSame(CoderResult.malformedForLength(1), CoderResult + .unmappableForLength(1)); + assertNotSame(CoderResult.malformedForLength(2), CoderResult + .malformedForLength(1)); + try { + CoderResult.malformedForLength(-1); + fail("Should throw IllegalArgumentException"); + } catch (IllegalArgumentException ex) { + // expected + } + try { + CoderResult.malformedForLength(0); + fail("Should throw IllegalArgumentException"); + } catch (IllegalArgumentException ex) { + // expected + } + } + + /** + * Test method unmappableForLength(int). + * + */ + public void testUnmappableForLength() { + assertNotNull(CoderResult.unmappableForLength(Integer.MAX_VALUE)); + assertNotNull(CoderResult.unmappableForLength(1)); + assertSame(CoderResult.unmappableForLength(1), CoderResult + .unmappableForLength(1)); + assertNotSame(CoderResult.unmappableForLength(2), CoderResult + .unmappableForLength(1)); + try { + CoderResult.unmappableForLength(-1); + fail("Should throw IllegalArgumentException"); + } catch (IllegalArgumentException ex) { + // expected + } + try { + CoderResult.unmappableForLength(0); + fail("Should throw IllegalArgumentException"); + } catch (IllegalArgumentException ex) { + // expected + } + } + + /** + * Test method throwException(). + * + */ + public void testThrowException() throws Exception { + try { + CoderResult.OVERFLOW.throwException(); + fail("Should throw BufferOverflowException"); + } catch (BufferOverflowException ex) { + // expected + } + try { + CoderResult.UNDERFLOW.throwException(); + fail("Should throw BufferOverflowException"); + } catch (BufferUnderflowException ex) { + // expected + } + try { + CoderResult.malformedForLength(1).throwException(); + fail("Should throw MalformedInputException"); + } catch (MalformedInputException ex) { + assertEquals(ex.getInputLength(), 1); + } + try { + CoderResult.unmappableForLength(1).throwException(); + fail("Should throw UnmappableCharacterException"); + } catch (UnmappableCharacterException ex) { + assertEquals(ex.getInputLength(), 1); + } + } + + /** + * Test method toString(). + * + */ + public void testToString() throws Exception { + assertTrue(CoderResult.OVERFLOW.toString().indexOf("OVERFLOW") != -1); + assertTrue(CoderResult.UNDERFLOW.toString().indexOf("UNDERFLOW") != -1); + assertTrue(CoderResult.malformedForLength(666).toString() + .indexOf("666") != -1); + assertTrue(CoderResult.unmappableForLength(666).toString().indexOf( + "666") != -1); + } +} diff --git a/nio_char/src/test/java/tests/api/java/nio/charset/CodingErrorActionTest.java b/nio_char/src/test/java/tests/api/java/nio/charset/CodingErrorActionTest.java new file mode 100644 index 0000000..1e23aef --- /dev/null +++ b/nio_char/src/test/java/tests/api/java/nio/charset/CodingErrorActionTest.java @@ -0,0 +1,62 @@ +/* Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package tests.api.java.nio.charset; + +import java.nio.charset.CodingErrorAction; + +import junit.framework.TestCase; + +/** + * Test class java.nio.charset.CodingErrorAction + */ +public class CodingErrorActionTest extends TestCase { + + /* + * @see TestCase#setUp() + */ + protected void setUp() throws Exception { + super.setUp(); + } + + /* + * @see TestCase#tearDown() + */ + protected void tearDown() throws Exception { + super.tearDown(); + } + + /* + * Test the constants. + */ + public void testIGNORE() { + assertNotNull(CodingErrorAction.IGNORE); + assertNotNull(CodingErrorAction.REPLACE); + assertNotNull(CodingErrorAction.REPORT); + assertNotSame(CodingErrorAction.IGNORE, CodingErrorAction.REPLACE); + assertNotSame(CodingErrorAction.IGNORE, CodingErrorAction.REPORT); + assertNotSame(CodingErrorAction.REPLACE, CodingErrorAction.REPORT); + } + + /* + * Test the method toString(). + */ + public void testToString() { + assertTrue(CodingErrorAction.IGNORE.toString().indexOf("IGNORE") != -1); + assertTrue(CodingErrorAction.REPLACE.toString().indexOf("REPLACE") != -1); + assertTrue(CodingErrorAction.REPORT.toString().indexOf("REPORT") != -1); + } +} diff --git a/nio_char/src/test/java/tests/api/java/nio/charset/GBCharsetDecoderTest.java b/nio_char/src/test/java/tests/api/java/nio/charset/GBCharsetDecoderTest.java new file mode 100644 index 0000000..855e85b --- /dev/null +++ b/nio_char/src/test/java/tests/api/java/nio/charset/GBCharsetDecoderTest.java @@ -0,0 +1,64 @@ +/* Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package tests.api.java.nio.charset; + +import java.io.UnsupportedEncodingException; +import java.nio.ByteBuffer; +import java.nio.charset.Charset; + +/** + * test gb18030 decoder + */ +public class GBCharsetDecoderTest extends CharsetDecoderTest { + + protected void setUp() throws Exception { + cs = Charset.forName("gb18030"); + super.setUp(); + } + + /* + * @see CharsetDecoderTest#tearDown() + */ + protected void tearDown() throws Exception { + super.tearDown(); + } + + // // FIXME: give up this tests + // public void testDefaultCharsPerByte(){ + // //assertEquals(1, decoder.averageCharsPerByte()); + // //assertEquals(1, decoder.maxCharsPerByte()); + // assertEquals(decoder.averageCharsPerByte(), 0.25, 0.001); + // assertEquals(decoder.maxCharsPerByte(), 2, 0.001); + // } + + ByteBuffer getUnmappedByteBuffer() throws UnsupportedEncodingException { + return null; + } + + ByteBuffer getMalformByteBuffer() throws UnsupportedEncodingException { + ByteBuffer buffer = ByteBuffer.allocate(20); + buffer.put(new byte[] { (byte) 0xd8, 0 }); + buffer.put(unibytes); + buffer.flip(); + return buffer; + } + + ByteBuffer getExceptionByteArray() throws UnsupportedEncodingException { + return null; + } + +} diff --git a/nio_char/src/test/java/tests/api/java/nio/charset/GBCharsetEncoderTest.java b/nio_char/src/test/java/tests/api/java/nio/charset/GBCharsetEncoderTest.java new file mode 100644 index 0000000..6890b0c --- /dev/null +++ b/nio_char/src/test/java/tests/api/java/nio/charset/GBCharsetEncoderTest.java @@ -0,0 +1,95 @@ +/* Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package tests.api.java.nio.charset; + +import java.nio.CharBuffer; +import java.nio.charset.CharacterCodingException; +import java.nio.charset.Charset; + +/** + * test case specific activity of gb18030 charset encoder + */ +public class GBCharsetEncoderTest extends CharsetEncoderTest { + + // charset for gb180303 + private static final Charset CS = Charset.forName("gb18030"); + + /* + * @see CharsetEncoderTest#setUp() + */ + protected void setUp() throws Exception { + cs = CS; + super.setUp(); + } + + /* + * @see CharsetEncoderTest#tearDown() + */ + protected void tearDown() throws Exception { + super.tearDown(); + } + + public void testCanEncodechar() throws CharacterCodingException { + // normal case for utfCS + assertTrue(encoder.canEncode('\u0077')); + assertTrue(encoder.canEncode('\uc2a3')); + + // for non-mapped char + assertTrue(encoder.canEncode('\uc2c0')); + } + + /* + * Class under test for boolean canEncode(CharSequence) + */ + public void testCanEncodeCharSequence() { + assertTrue(encoder.canEncode("")); + // surrogate char + + // valid surrogate pair + assertTrue(encoder.canEncode("\ud800\udc00")); + // invalid surrogate pair + assertFalse(encoder.canEncode("\ud800\udb00")); + assertFalse(encoder.canEncode("\ud800")); + } + + public void testSpecificDefaultValue() { + // FIXME: different here! + assertEquals(4.0, encoder.maxBytesPerChar(), 0.0); + assertEquals(2.5, encoder.averageBytesPerChar(), 0.0); + + // assertTrue(encoder.averageBytesPerChar() == 3); + // assertTrue(encoder.maxBytesPerChar() == 2); + + } + + CharBuffer getMalformedCharBuffer() { + return CharBuffer.wrap("\ud800 buffer"); + } + + CharBuffer getUnmapCharBuffer() { + return null; + } + + CharBuffer getExceptionCharBuffer() { + return null; + } + + protected byte[] getIllegalByteArray() { + return new byte[] { (byte) 0xd8, (byte) 0x00 }; + } + +} diff --git a/nio_char/src/test/java/tests/api/java/nio/charset/ISOCharsetDecoderTest.java b/nio_char/src/test/java/tests/api/java/nio/charset/ISOCharsetDecoderTest.java new file mode 100644 index 0000000..f1104cd --- /dev/null +++ b/nio_char/src/test/java/tests/api/java/nio/charset/ISOCharsetDecoderTest.java @@ -0,0 +1,61 @@ +/* Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package tests.api.java.nio.charset; + +import java.io.UnsupportedEncodingException; +import java.nio.ByteBuffer; +import java.nio.charset.Charset; + +/** + * test ISO-8859-1 decoder + */ +public class ISOCharsetDecoderTest extends CharsetDecoderTest { + + protected void setUp() throws Exception { + cs = Charset.forName("iso-8859-1"); + super.setUp(); + } + + /* + * @see CharsetDecoderTest#tearDown() + */ + protected void tearDown() throws Exception { + super.tearDown(); + } + + // FIXME: give up this tests + // public void testDefaultCharsPerByte(){ + // assertEquals(1, decoder.averageCharsPerByte()); + // assertEquals(decoder.maxCharsPerByte(), 2, 0.001); + // } + + ByteBuffer getUnmappedByteBuffer() throws UnsupportedEncodingException { + // TODO how on map? + return null; + + } + + ByteBuffer getMalformByteBuffer() throws UnsupportedEncodingException { + // TODO how malform + return null; + } + + ByteBuffer getExceptionByteArray() throws UnsupportedEncodingException { + return null; + } + +} diff --git a/nio_char/src/test/java/tests/api/java/nio/charset/ISOCharsetEncoderTest.java b/nio_char/src/test/java/tests/api/java/nio/charset/ISOCharsetEncoderTest.java new file mode 100644 index 0000000..ea25929 --- /dev/null +++ b/nio_char/src/test/java/tests/api/java/nio/charset/ISOCharsetEncoderTest.java @@ -0,0 +1,112 @@ +/* Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package tests.api.java.nio.charset; + +import java.nio.ByteBuffer; +import java.nio.CharBuffer; +import java.nio.charset.CharacterCodingException; +import java.nio.charset.Charset; +import java.nio.charset.CoderResult; +import java.nio.charset.CodingErrorAction; +import java.nio.charset.UnmappableCharacterException; + +/** + * test case specific activity of iso-8859-1 charset encoder + */ +public class ISOCharsetEncoderTest extends CharsetEncoderTest { + + // charset for iso-8859-1 + private static final Charset CS = Charset.forName("iso-8859-1"); + + /* + * @see CharsetEncoderTest#setUp() + */ + protected void setUp() throws Exception { + cs = CS; + super.setUp(); + } + + /* + * @see CharsetEncoderTest#tearDown() + */ + protected void tearDown() throws Exception { + super.tearDown(); + } + + public void testCanEncodeCharSequence() { + // normal case for isoCS + assertTrue(encoder.canEncode("\u0077")); + assertFalse(encoder.canEncode("\uc2a3")); + assertFalse(encoder.canEncode("\ud800\udc00")); + try { + encoder.canEncode(null); + } catch (NullPointerException e) { + } + assertTrue(encoder.canEncode("")); + } + + public void testCanEncodeICUBug() { + assertFalse(encoder.canEncode((char) '\ud800')); + assertFalse(encoder.canEncode((String) "\ud800")); + } + + public void testCanEncodechar() throws CharacterCodingException { + assertTrue(encoder.canEncode('\u0077')); + assertFalse(encoder.canEncode('\uc2a3')); + } + + public void testSpecificDefaultValue() { + assertEquals(1, encoder.averageBytesPerChar(), 0.001); + assertEquals(1, encoder.maxBytesPerChar(), 0.001); + } + + CharBuffer getMalformedCharBuffer() { + return CharBuffer.wrap("\ud800 buffer"); + } + + CharBuffer getUnmapCharBuffer() { + return CharBuffer.wrap("\ud800\udc00 buffer"); + } + + CharBuffer getExceptionCharBuffer() { + return null; + } + + protected byte[] getIllegalByteArray() { + return null; + } + + public void testMultiStepEncode() throws CharacterCodingException { + encoder.onMalformedInput(CodingErrorAction.REPORT); + encoder.onUnmappableCharacter(CodingErrorAction.REPORT); + try { + encoder.encode(CharBuffer.wrap("\ud800\udc00")); + fail("should unmappable"); + } catch (UnmappableCharacterException e) { + } + encoder.reset(); + ByteBuffer out = ByteBuffer.allocate(10); + assertTrue(encoder.encode(CharBuffer.wrap("\ud800"), out, true) + .isMalformed()); + encoder.flush(out); + encoder.reset(); + out = ByteBuffer.allocate(10); + assertSame(CoderResult.UNDERFLOW, encoder.encode(CharBuffer + .wrap("\ud800"), out, false)); + assertTrue(encoder.encode(CharBuffer.wrap("\udc00"), out, true) + .isMalformed()); + } +} diff --git a/nio_char/src/test/java/tests/api/java/nio/charset/ISOCharsetTest.java b/nio_char/src/test/java/tests/api/java/nio/charset/ISOCharsetTest.java new file mode 100644 index 0000000..5c010b2 --- /dev/null +++ b/nio_char/src/test/java/tests/api/java/nio/charset/ISOCharsetTest.java @@ -0,0 +1,58 @@ +/* Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package tests.api.java.nio.charset; + +/** + * Test ISO-8859-1. + */ +public class ISOCharsetTest extends AbstractCharsetTestCase { + + /** + * Constructor. + */ + public ISOCharsetTest(String arg0) { + super(arg0, "ISO-8859-1", new String[] { "iso-ir-100", "8859_1", + "ISO_8859-1", "ISO8859_1", "819", "csISOLatin1", "IBM-819", + "ISO_8859-1:1987", "latin1", "cp819", "ISO8859-1", "IBM819", + "ISO_8859_1", "l1" }, true, true); + } + + /* + * (non-Javadoc) + * + * @see tests.api.java.nio.charset.ConcreteCharsetTest#testEncode_Normal() + */ + public void testEncode_Normal() { + String input = "ab\u5D14\u654F"; + byte[] output = new byte[] { 97, 98, + this.testingCharset.newEncoder().replacement()[0], + this.testingCharset.newEncoder().replacement()[0] }; + internalTestEncode(input, output); + } + + /* + * (non-Javadoc) + * + * @see tests.api.java.nio.charset.ConcreteCharsetTest#testDecode_Normal() + */ + public void testDecode_Normal() { + byte[] input = new byte[] { 97, 98, 63, 63 }; + char[] output = "ab??".toCharArray(); + internalTestDecode(input, output); + } + +} diff --git a/nio_char/src/test/java/tests/api/java/nio/charset/UTF16BECharsetDecoderTest.java b/nio_char/src/test/java/tests/api/java/nio/charset/UTF16BECharsetDecoderTest.java new file mode 100644 index 0000000..9e2b66d --- /dev/null +++ b/nio_char/src/test/java/tests/api/java/nio/charset/UTF16BECharsetDecoderTest.java @@ -0,0 +1,74 @@ +/* Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package tests.api.java.nio.charset; + +import java.io.UnsupportedEncodingException; +import java.nio.ByteBuffer; +import java.nio.charset.Charset; + +/** + * + */ +public class UTF16BECharsetDecoderTest extends CharsetDecoderTest { + + protected void setUp() throws Exception { + cs = Charset.forName("utf-16be"); + unibytes = new byte[] { 0, 32, 0, 98, 0, 117, 0, 102, 0, 102, 0, 101, + 0, 114 }; + super.setUp(); + } + + /* + * @see CharsetDecoderTest#tearDown() + */ + protected void tearDown() throws Exception { + super.tearDown(); + } + + // FIXME: give up this tests + // public void testDefaultCharsPerByte() { + // // assertEquals(1, decoder.averageCharsPerByte()); + // // assertEquals(1, decoder.maxCharsPerByte()); + // assertEquals(decoder.averageCharsPerByte(), 0.5, 0.001); + // assertEquals(decoder.maxCharsPerByte(), 2, 0.001); + // } + + ByteBuffer getUnmappedByteBuffer() throws UnsupportedEncodingException { + // no unmap byte buffer + return null; + } + + ByteBuffer getMalformByteBuffer() throws UnsupportedEncodingException { + // FIXME: different here, RI can parse 0xd8d8 + // ByteBuffer buffer = ByteBuffer.allocate(100); + // buffer.put((byte)0xd8); + // buffer.put((byte)0xd8); + // buffer.put(unibytes); + // buffer.flip(); + // return buffer; + return null; + } + + ByteBuffer getExceptionByteArray() throws UnsupportedEncodingException { + return null; + } + + byte[] getUnibytes() { + return new byte[] { 0, 32, 0, 98, 0, 117, 0, 102, 0, 102, 0, 101, 0, + 114 }; + } +} diff --git a/nio_char/src/test/java/tests/api/java/nio/charset/UTF16BECharsetEncoderTest.java b/nio_char/src/test/java/tests/api/java/nio/charset/UTF16BECharsetEncoderTest.java new file mode 100644 index 0000000..194b3b3 --- /dev/null +++ b/nio_char/src/test/java/tests/api/java/nio/charset/UTF16BECharsetEncoderTest.java @@ -0,0 +1,121 @@ +/* Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package tests.api.java.nio.charset; + +import java.nio.CharBuffer; +import java.nio.charset.CharacterCodingException; +import java.nio.charset.Charset; + +/** + * TODO type def + */ +public class UTF16BECharsetEncoderTest extends CharsetEncoderTest { + + // charset for utf-16be + private static final Charset CS = Charset.forName("utf-16be"); + + /* + * @see CharsetEncoderTest#setUp() + */ + protected void setUp() throws Exception { + cs = CS; + specifiedReplacement = new byte[] { -1, -3 }; + unibytes = new byte[] { 0, 32, 0, 98, 0, 117, 0, 102, 0, 102, 0, 101, + 0, 114 }; + + // unibytesWithRep = new byte[] {(byte)0xff, (byte)0xfd,0, 32, 0, 98, 0, + // 117, 0, 102, 0, 102, 0, 101, 0, 114}; + + super.setUp(); + } + + /* + * @see CharsetEncoderTest#tearDown() + */ + protected void tearDown() throws Exception { + super.tearDown(); + } + + public void testCharsetEncoderCharsetfloatfloat() { + // this constructor is invalid for UTF16LE CharsetEncoder + } + + public void testCanEncodechar() throws CharacterCodingException { + // normal case for utfCS + assertTrue(encoder.canEncode('\u0077')); + assertTrue(encoder.canEncode('\uc2a3')); + + // for non-mapped char + assertTrue(encoder.canEncode('\uc2c0')); + + } + + public void testCanEncodeCharSequence() { + // normal case for utfCS + assertTrue(encoder.canEncode("\u0077")); + assertTrue(encoder.canEncode("\uc2a3")); + assertTrue(encoder.canEncode("")); + + // for non-mapped char + assertTrue(encoder.canEncode("\uc2c0")); + + // surrogate char for unicode + // 1st byte: d800-dbff + // 2nd byte: dc00-dfff + // valid surrogate pair + assertTrue(encoder.canEncode("\ud800\udc00")); + // invalid surrogate pair + assertFalse(encoder.canEncode("\ud800\udb00")); + } + + public void testCanEncodeICUBug() { + assertFalse(encoder.canEncode("\ud800")); + } + + public void testSpecificDefaultValue() { + // ??? TODO NIO adapt to the actually used UTF16BE charset Encoder + // assertEquals(2, encoder.averageBytesPerChar(), 0.001); + assertEquals(2, encoder.maxBytesPerChar(), 0.001); + } + + CharBuffer getMalformedCharBuffer() { + return CharBuffer.wrap("\ud800 buffer"); + } + + CharBuffer getUnmapCharBuffer() { + return null; + } + + CharBuffer getExceptionCharBuffer() { + return null; + } + + public void testIsLegalReplacementEmptyArray() { + assertTrue(encoder.isLegalReplacement(new byte[0])); + } + + protected byte[] getIllegalByteArray() { + // FIXME: different here + // cannot replace by 0xd8d8, but RI can + // return new byte[]{(byte)0xd8, (byte)0xd8}; + return new byte[] { 0 }; + } + + protected byte[] getLegalByteArray() { + return new byte[] { (byte) 0x00, (byte) 0xd8 }; + } +} diff --git a/nio_char/src/test/java/tests/api/java/nio/charset/UTF16BECharsetTest.java b/nio_char/src/test/java/tests/api/java/nio/charset/UTF16BECharsetTest.java new file mode 100644 index 0000000..754afa3 --- /dev/null +++ b/nio_char/src/test/java/tests/api/java/nio/charset/UTF16BECharsetTest.java @@ -0,0 +1,53 @@ +/* Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package tests.api.java.nio.charset; + +/** + * Test UTF-16BE. + */ +public class UTF16BECharsetTest extends AbstractCharsetTestCase { + + /** + * Constructor. + */ + public UTF16BECharsetTest(String arg0) { + super(arg0, "UTF-16BE", new String[] { "X-UTF-16BE", "UTF_16BE" }, + true, true); // "ISO-10646-UCS-2" + } + + /* + * (non-Javadoc) + * + * @see tests.api.java.nio.charset.ConcreteCharsetTest#testEncode_Normal() + */ + public void testEncode_Normal() { + String input = "ab\u5D14\u654F"; + byte[] output = new byte[] { 0, 97, 0, 98, 93, 20, 101, 79 }; + internalTestEncode(input, output); + } + + /* + * (non-Javadoc) + * + * @see tests.api.java.nio.charset.ConcreteCharsetTest#testDecode_Normal() + */ + public void testDecode_Normal() { + byte[] input = new byte[] { 0, 97, 0, 98, 93, 20, 101, 79 }; + char[] output = "ab\u5D14\u654F".toCharArray(); + internalTestDecode(input, output); + } +} diff --git a/nio_char/src/test/java/tests/api/java/nio/charset/UTF16CharsetDecoderTest.java b/nio_char/src/test/java/tests/api/java/nio/charset/UTF16CharsetDecoderTest.java new file mode 100644 index 0000000..fa1d5ec --- /dev/null +++ b/nio_char/src/test/java/tests/api/java/nio/charset/UTF16CharsetDecoderTest.java @@ -0,0 +1,152 @@ +/* Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package tests.api.java.nio.charset; + +import java.io.UnsupportedEncodingException; +import java.nio.ByteBuffer; +import java.nio.CharBuffer; +import java.nio.charset.CharacterCodingException; +import java.nio.charset.Charset; +import java.nio.charset.CoderResult; +import java.nio.charset.CodingErrorAction; + +/** + * + */ +public class UTF16CharsetDecoderTest extends CharsetDecoderTest { + + boolean bigEndian = true; + + protected void setUp() throws Exception { + cs = Charset.forName("utf-16"); + unibytes = new byte[] { 32, 0, 98, 0, 117, 0, 102, 0, 102, 0, 101, 0, + 114, 0 }; + bom = "\ufeff"; + + // unibytes = new byte[] {-1, -2, 0, 32, 0, 98, 0, 117, 0, 102, 0, 102, + // 0, 101, 0, 114}; + super.setUp(); + } + + /* + * @see CharsetDecoderTest#tearDown() + */ + protected void tearDown() throws Exception { + super.tearDown(); + } + + byte[] getUnibytes() { + // FIXME: different here + // if don't specified BOM + // ICU default is LE + // JDK default is BE + + // maybe start with 0xFEFF, which means big endian + // 0xFFFE, which means little endian + if (bigEndian) { + return new byte[] { -1, -2, 32, 0, 98, 0, 117, 0, 102, 0, 102, 0, + 101, 0, 114, 0 }; + } else { + unibytes = new byte[] { 0, 32, 0, 98, 0, 117, 0, 102, 0, 102, 0, + 101, 0, 114 }; + return new byte[] { -2, -1, 0, 32, 0, 98, 0, 117, 0, 102, 0, 102, + 0, 101, 0, 114 }; + } + } + + public void testMultiStepDecode() throws CharacterCodingException { + if (!cs.name().equals("mock")) { + decoder.onMalformedInput(CodingErrorAction.REPORT); + decoder.onUnmappableCharacter(CodingErrorAction.REPORT); + CharBuffer out = CharBuffer.allocate(10); + assertTrue(decoder.decode( + ByteBuffer.wrap(new byte[] { -1, -2, 32, 0, 98 }), out, + true).isMalformed()); + + decoder.flush(out); + decoder.reset(); + out.clear(); + assertSame(CoderResult.UNDERFLOW, decoder.decode(ByteBuffer + .wrap(new byte[] { -1, -2, 32, 0 }), out, false)); + assertTrue(decoder.decode(ByteBuffer.wrap(new byte[] { 98 }), out, + true).isMalformed()); + + decoder.flush(out); + decoder.reset(); + out.clear(); + assertSame(CoderResult.UNDERFLOW, decoder.decode(ByteBuffer + .wrap(new byte[] { -1, -2, 32, 0, 98 }), out, false)); + assertFalse(decoder + .decode(ByteBuffer.wrap(new byte[] {}), out, true) + .isMalformed()); + + decoder.flush(out); + decoder.reset(); + out.clear(); + assertFalse(decoder.decode( + ByteBuffer.wrap(new byte[] { -1, -2, 32, 0, 98, 0 }), out, + true).isError()); + + decoder.flush(out); + decoder.reset(); + out.clear(); + assertSame(CoderResult.UNDERFLOW, decoder.decode(ByteBuffer + .wrap(new byte[] { -1, -2, 32, 0, 98 }), out, false)); + assertTrue(decoder.decode(ByteBuffer.wrap(new byte[] { 0 }), out, + true).isMalformed()); + + } + } + + public void testLittleEndian() throws CharacterCodingException, + UnsupportedEncodingException { + bigEndian = false; + implTestDecodeByteBufferCharBufferboolean(); + decoder.reset(); + implTestDecodeByteBuffer(); + bigEndian = true; + } + + // FIXME: give up this tests + // public void testDefaultCharsPerByte() { + // // assertEquals(1, decoder.averageCharsPerByte()); + // // assertEquals(1, decoder.maxCharsPerByte()); + // assertEquals(decoder.averageCharsPerByte(), 0.5, 0.001); + // assertEquals(decoder.maxCharsPerByte(), 2, 0.001); + // } + + ByteBuffer getUnmappedByteBuffer() throws UnsupportedEncodingException { + return null; + } + + ByteBuffer getMalformByteBuffer() throws UnsupportedEncodingException { + return null; + // FIXME: different here, RI can parse 0xd8d8 + // ByteBuffer buffer = ByteBuffer.allocate(100); + // buffer.put((byte) -1); + // buffer.put((byte) -2); + // buffer.put((byte) 0xdc); + // buffer.put((byte) 0xdc); + // buffer.put(unibytes); + // buffer.flip(); + // return buffer; + } + + ByteBuffer getExceptionByteArray() throws UnsupportedEncodingException { + return null; + } +} diff --git a/nio_char/src/test/java/tests/api/java/nio/charset/UTF16CharsetEncoderTest.java b/nio_char/src/test/java/tests/api/java/nio/charset/UTF16CharsetEncoderTest.java new file mode 100644 index 0000000..bdc505d --- /dev/null +++ b/nio_char/src/test/java/tests/api/java/nio/charset/UTF16CharsetEncoderTest.java @@ -0,0 +1,138 @@ +/* Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package tests.api.java.nio.charset; + +import java.nio.ByteBuffer; +import java.nio.CharBuffer; +import java.nio.charset.CharacterCodingException; +import java.nio.charset.Charset; +import java.nio.charset.CharsetDecoder; + +/** + * TODO type def + */ +public class UTF16CharsetEncoderTest extends CharsetEncoderTest { + + // charset for utf-16 + // charset for utf-16be + private static final Charset CS = Charset.forName("utf-16"); + + private static final CharsetDecoder decoder = CS.newDecoder(); + + /* + * @see CharsetEncoderTest#setUp() + */ + protected void setUp() throws Exception { + cs = CS; + specifiedReplacement = new byte[] { -3, -1 }; + surrogate = new byte[] { -1, -2 }; + unibytes = new byte[] { 32, 0, 98, 0, 117, 0, 102, 0, 102, 0, 101, 0, + 114, 0 }; + unibytesWithRep = new byte[] { -3, -1, 32, 0, 98, 0, 117, 0, 102, 0, + 102, 0, 101, 0, 114, 0 }; + super.setUp(); + } + + /* + * @see CharsetEncoderTest#tearDown() + */ + protected void tearDown() throws Exception { + super.tearDown(); + } + + public void testCharsetEncoderCharsetfloatfloat() { + // this constructor is invalid for UTF16LE CharsetEncoder + } + + public void testCanEncodechar() throws CharacterCodingException { + // normal case for utfCS + assertTrue(encoder.canEncode('\u0077')); + assertTrue(encoder.canEncode('\uc2a3')); + + // for non-mapped char + assertTrue(encoder.canEncode('\uc2c0')); + } + + public void testCanEncodeCharSequence() { + // normal case for utfCS + assertTrue(encoder.canEncode("\u0077")); + assertTrue(encoder.canEncode("\uc2a3")); + assertTrue(encoder.canEncode("")); + + // for non-mapped char + assertTrue(encoder.canEncode("\uc2c0")); + + // surrogate char for unicode + // 1st byte: d800-dbff + // 2nd byte: dc00-dfff + // valid surrogate pair + assertTrue(encoder.canEncode("\ud800\udc00")); + // invalid surrogate pair + assertFalse(encoder.canEncode("\ud800\udb00")); + } + + public void testCanEncodeICUBug() { + assertFalse(encoder.canEncode('\ud800')); + assertFalse(encoder.canEncode("\ud800")); + } + + public void testSpecificDefaultValue() { + assertEquals(encoder.averageBytesPerChar(), 2, 0.001); + // assertEquals(4, encoder.maxBytesPerChar()); + // FIXME: different here! + assertEquals(encoder.maxBytesPerChar(), 2, 0.001); + } + + CharBuffer getMalformedCharBuffer() { + return CharBuffer.wrap("\ud800 buffer"); + } + + CharBuffer getUnmapCharBuffer() { + return null; + } + + CharBuffer getExceptionCharBuffer() { + return null; + } + + public void testIsLegalReplacementEmptyArray() { + assertTrue(encoder.isLegalReplacement(new byte[0])); + } + + protected byte[] getIllegalByteArray() { + return new byte[] { 0x00 }; + } + + protected byte[] getLegalByteArray() { + // FIXME: Different Here! + // return new byte[]{(byte)0xd8, 0x00}; + return new byte[] { (byte) 0x00, (byte) 0xd8 }; + } + + void assertByteArray(ByteBuffer out, byte[] expected) { + out = out.duplicate(); + if (out.position() > 0) { + out.flip(); + } + try { + assertEquals(decoder.decode(out), decoder.decode(ByteBuffer + .wrap(expected))); + } catch (CharacterCodingException e) { + fail(e.toString()); + } + } +} diff --git a/nio_char/src/test/java/tests/api/java/nio/charset/UTF16CharsetTest.java b/nio_char/src/test/java/tests/api/java/nio/charset/UTF16CharsetTest.java new file mode 100644 index 0000000..e77be5e --- /dev/null +++ b/nio_char/src/test/java/tests/api/java/nio/charset/UTF16CharsetTest.java @@ -0,0 +1,51 @@ +/* Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package tests.api.java.nio.charset; + +/** + * Test UTF-16. + */ +public class UTF16CharsetTest extends AbstractCharsetTestCase { + + /** + * Constructor. + */ + public UTF16CharsetTest(String arg0) { + super(arg0, "UTF-16", new String[] { "UTF_16" }, true, true); + } + + /* + * (non-Javadoc) + * + * @see tests.api.java.nio.charset.ConcreteCharsetTest#testEncode_Normal() + */ + public void testEncode_Normal() { + // TODO Auto-generated method stub + + } + + /* + * (non-Javadoc) + * + * @see tests.api.java.nio.charset.ConcreteCharsetTest#testDecode_Normal() + */ + public void testDecode_Normal() { + // TODO Auto-generated method stub + + } + +} diff --git a/nio_char/src/test/java/tests/api/java/nio/charset/UTF16LECharsetDecoderTest.java b/nio_char/src/test/java/tests/api/java/nio/charset/UTF16LECharsetDecoderTest.java new file mode 100644 index 0000000..183fa47 --- /dev/null +++ b/nio_char/src/test/java/tests/api/java/nio/charset/UTF16LECharsetDecoderTest.java @@ -0,0 +1,74 @@ +/* Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package tests.api.java.nio.charset; + +import java.io.UnsupportedEncodingException; +import java.nio.ByteBuffer; +import java.nio.charset.Charset; + +/** + * TODO typedef + */ +public class UTF16LECharsetDecoderTest extends CharsetDecoderTest { + + protected void setUp() throws Exception { + cs = Charset.forName("utf-16le"); + unibytes = new byte[] { 32, 0, 98, 0, 117, 0, 102, 0, 102, 0, 101, 0, + 114, 0 }; + super.setUp(); + } + + /* + * @see CharsetDecoderTest#tearDown() + */ + protected void tearDown() throws Exception { + super.tearDown(); + } + + // // FIXME: give up this tests + // public void testDefaultCharsPerByte(){ + // // assertEquals(1, decoder.averageCharsPerByte()); + // // assertEquals(1, decoder.maxCharsPerByte()); + // assertEquals(decoder.averageCharsPerByte(), 0.5, 0.001); + // assertEquals(decoder.maxCharsPerByte(), 2, 0.001); + // } + + ByteBuffer getUnmappedByteBuffer() throws UnsupportedEncodingException { + // no unmap byte buffer + return null; + } + + ByteBuffer getMalformByteBuffer() throws UnsupportedEncodingException { + // FIXME: different here, JDK can parse 0xd8d8 + // ByteBuffer buffer = ByteBuffer.allocate(100); + // buffer.put((byte)0xd8); + // buffer.put((byte)0xd8); + // buffer.put(unibytes); + // buffer.flip(); + // return buffer; + return null; + } + + ByteBuffer getExceptionByteArray() throws UnsupportedEncodingException { + return null; + } + + byte[] getUnibytes() { + return new byte[] { 32, 0, 98, 0, 117, 0, 102, 0, 102, 0, 101, 0, 114, + 0 }; + } +} diff --git a/nio_char/src/test/java/tests/api/java/nio/charset/UTF16LECharsetEncoderTest.java b/nio_char/src/test/java/tests/api/java/nio/charset/UTF16LECharsetEncoderTest.java new file mode 100644 index 0000000..e669e11 --- /dev/null +++ b/nio_char/src/test/java/tests/api/java/nio/charset/UTF16LECharsetEncoderTest.java @@ -0,0 +1,118 @@ +/* Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package tests.api.java.nio.charset; + +import java.nio.CharBuffer; +import java.nio.charset.CharacterCodingException; +import java.nio.charset.Charset; + +/** + * TODO type def + */ +public class UTF16LECharsetEncoderTest extends CharsetEncoderTest { + + // charset for utf-16le + private static final Charset CS = Charset.forName("utf-16le"); + + /* + * @see CharsetEncoderTest#setUp() + */ + protected void setUp() throws Exception { + cs = CS; + specifiedReplacement = new byte[] { -3, -1 }; + + unibytes = new byte[] { 32, 0, 98, 0, 117, 0, 102, 0, 102, 0, 101, 0, + 114, 0 }; + + // unibytesWithRep = new byte[] {(byte)0xfd, (byte)0xff, 32, 0, 98, 0, + // 117, 0, 102, 0, 102, 0, 101, 0, 114 ,0}; + + super.setUp(); + } + + /* + * @see CharsetEncoderTest#tearDown() + */ + protected void tearDown() throws Exception { + super.tearDown(); + } + + public void testCharsetEncoderCharsetfloatfloat() { + // this constructor is invalid for UTF16LE CharsetEncoder + } + + public void testCanEncodechar() throws CharacterCodingException { + // normal case for utfCS + assertTrue(encoder.canEncode('\u0077')); + assertTrue(encoder.canEncode('\uc2a3')); + + // for non-mapped char + assertTrue(encoder.canEncode('\uc2c0')); + } + + public void testCanEncodeCharSequence() { + // normal case for utfCS + assertTrue(encoder.canEncode("\u0077")); + assertTrue(encoder.canEncode("\uc2a3")); + assertTrue(encoder.canEncode("")); + + // for non-mapped char + assertTrue(encoder.canEncode("\uc2c0")); + + // surrogate char for unicode + // 1st byte: d800-dbff + // 2nd byte: dc00-dfff + // valid surrogate pair + assertTrue(encoder.canEncode("\ud800\udc00")); + // invalid surrogate pair + assertFalse(encoder.canEncode("\ud800\udb00")); + } + + public void testCanEncodeICUBug() { + assertFalse(encoder.canEncode("\ud800")); + } + + public void testSpecificDefaultValue() { + assertEquals(2, encoder.averageBytesPerChar(), 0.001); + assertEquals(2, encoder.maxBytesPerChar(), 0.001); + } + + public void testIsLegalReplacementEmptyArray() { + assertTrue(encoder.isLegalReplacement(new byte[0])); + } + + CharBuffer getMalformedCharBuffer() { + return CharBuffer.wrap("\ud800 buffer"); + } + + CharBuffer getUnmapCharBuffer() { + return null; + } + + CharBuffer getExceptionCharBuffer() { + return null; + } + + protected byte[] getIllegalByteArray() { + return new byte[] { 0x00 }; + } + + protected byte[] getLegalByteArray() { + return new byte[] { (byte) 0xd8, 0x00 }; + } + +} diff --git a/nio_char/src/test/java/tests/api/java/nio/charset/UTF16LECharsetTest.java b/nio_char/src/test/java/tests/api/java/nio/charset/UTF16LECharsetTest.java new file mode 100644 index 0000000..b32ab9b --- /dev/null +++ b/nio_char/src/test/java/tests/api/java/nio/charset/UTF16LECharsetTest.java @@ -0,0 +1,54 @@ +/* Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package tests.api.java.nio.charset; + +/** + * Test UTF-16LE. + */ +public class UTF16LECharsetTest extends AbstractCharsetTestCase { + + /** + * Constructor. + */ + public UTF16LECharsetTest(String arg0) { + super(arg0, "UTF-16LE", new String[] { "UTF_16LE", "X-UTF-16LE" }, + true, true); + } + + /* + * (non-Javadoc) + * + * @see tests.api.java.nio.charset.ConcreteCharsetTest#testEncode_Normal() + */ + public void testEncode_Normal() { + String input = "ab\u5D14\u654F"; + byte[] output = new byte[] { 97, 0, 98, 0, 20, 93, 79, 101 }; + internalTestEncode(input, output); + } + + /* + * (non-Javadoc) + * + * @see tests.api.java.nio.charset.ConcreteCharsetTest#testDecode_Normal() + */ + public void testDecode_Normal() { + byte[] input = new byte[] { 97, 0, 98, 0, 20, 93, 79, 101 }; + char[] output = "ab\u5D14\u654F".toCharArray(); + internalTestDecode(input, output); + } + +} diff --git a/nio_char/src/test/java/tests/api/java/nio/charset/UTF8CharsetTest.java b/nio_char/src/test/java/tests/api/java/nio/charset/UTF8CharsetTest.java new file mode 100644 index 0000000..8e0f57f --- /dev/null +++ b/nio_char/src/test/java/tests/api/java/nio/charset/UTF8CharsetTest.java @@ -0,0 +1,53 @@ +/* Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package tests.api.java.nio.charset; + +/** + * Test UTF-8 charset. + */ +public class UTF8CharsetTest extends AbstractCharsetTestCase { + + /** + * Constructor for UTF8CharsetTest. + * + */ + public UTF8CharsetTest(String arg0) { + super(arg0, "UTF-8", new String[] { "UTF8" }, true, true); + } + + /* + * (non-Javadoc) + * + * @see tests.api.java.nio.charset.ConcreteCharsetTest#testDecode_Normal() + */ + public void testDecode_Normal() { + byte[] input = new byte[] { 97, 98, -27, -76, -108, -26, -107, -113 }; + char[] output = "ab\u5D14\u654F".toCharArray(); + internalTestDecode(input, output); + } + + /* + * (non-Javadoc) + * + * @see tests.api.java.nio.charset.ConcreteCharsetTest#testEncode_Normal() + */ + public void testEncode_Normal() { + String input = "ab\u5D14\u654F"; + byte[] output = new byte[] { 97, 98, -27, -76, -108, -26, -107, -113 }; + internalTestEncode(input, output); + } +} diff --git a/nio_char/src/test/java/tests/api/java/nio/charset/UTFCharsetDecoderTest.java b/nio_char/src/test/java/tests/api/java/nio/charset/UTFCharsetDecoderTest.java new file mode 100644 index 0000000..288cd16 --- /dev/null +++ b/nio_char/src/test/java/tests/api/java/nio/charset/UTFCharsetDecoderTest.java @@ -0,0 +1,63 @@ +/* Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package tests.api.java.nio.charset; + +import java.io.UnsupportedEncodingException; +import java.nio.ByteBuffer; +import java.nio.charset.Charset; + +/** + * test utf-8 decoder + */ +public class UTFCharsetDecoderTest extends CharsetDecoderTest { + + protected void setUp() throws Exception { + cs = Charset.forName("utf-8"); + super.setUp(); + } + + /* + * @see CharsetDecoderTest#tearDown() + */ + protected void tearDown() throws Exception { + super.tearDown(); + } + + // FIXME: give up this tests + // public void testDefaultCharsPerByte(){ + // assertEquals(decoder.averageCharsPerByte(), 0.333, 0.001); + // assertEquals(decoder.maxCharsPerByte(), 2, 0.001); + // // assertEquals(1, decoder.averageCharsPerByte()); + // // assertEquals(1, decoder.maxCharsPerByte()); + // } + + ByteBuffer getUnmappedByteBuffer() throws UnsupportedEncodingException { + return null; + } + + ByteBuffer getMalformByteBuffer() throws UnsupportedEncodingException { + ByteBuffer buffer = ByteBuffer.allocate(20); + buffer.put((byte) 0xd8); + buffer.put(unibytes); + buffer.flip(); + return buffer; + } + + ByteBuffer getExceptionByteArray() throws UnsupportedEncodingException { + return null; + } +} diff --git a/nio_char/src/test/java/tests/api/java/nio/charset/UTFCharsetEncoderTest.java b/nio_char/src/test/java/tests/api/java/nio/charset/UTFCharsetEncoderTest.java new file mode 100644 index 0000000..b4f1e7d --- /dev/null +++ b/nio_char/src/test/java/tests/api/java/nio/charset/UTFCharsetEncoderTest.java @@ -0,0 +1,102 @@ +/* Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package tests.api.java.nio.charset; + +import java.nio.CharBuffer; +import java.nio.charset.CharacterCodingException; +import java.nio.charset.Charset; + +/** + * test case specific activity of utf-8 charset encoder + */ +public class UTFCharsetEncoderTest extends CharsetEncoderTest { + + // charset for UTF-8 + private static final Charset CS = Charset.forName("utf-8"); + + /* + * @see CharsetEncoderTest#setUp() + */ + protected void setUp() throws Exception { + cs = CS; + specifiedReplacement = new byte[] { -17, -65, -67 }; + super.setUp(); + } + + /* + * @see CharsetEncoderTest#tearDown() + */ + protected void tearDown() throws Exception { + super.tearDown(); + } + + public void testCanEncodechar() throws CharacterCodingException { + // normal case for utfCS + assertTrue(encoder.canEncode('\u0077')); + assertTrue(encoder.canEncode('\uc2a3')); + + // for non-mapped char + assertTrue(encoder.canEncode('\uc2c0')); + } + + public void testCanEncodeCharSequence() { + // normal case for utfCS + assertTrue(encoder.canEncode("\u0077")); + assertTrue(encoder.canEncode("\uc2a3")); + assertTrue(encoder.canEncode("")); + + // for non-mapped char + assertTrue(encoder.canEncode("\uc2c0")); + + // surrogate char for unicode + // 1st byte: d800-dbff + // 2nd byte: dc00-dfff + // valid surrogate pair + assertTrue(encoder.canEncode("\ud800\udc00")); + // invalid surrogate pair + assertFalse(encoder.canEncode("\ud800\udb00")); + } + + public void testCanEncodeICUBug() { + assertFalse(encoder.canEncode("\ud800")); + } + + public void testSpecificDefaultValue() { + // assertEquals(1.1, encoder.averageBytesPerChar(), 0.0001); + assertEquals(2, encoder.averageBytesPerChar(), 0.0001); + assertEquals(3, encoder.maxBytesPerChar(), 0); + } + + CharBuffer getMalformedCharBuffer() { + return CharBuffer.wrap("\ud800 buffer"); + } + + CharBuffer getUnmapCharBuffer() { + return null; + } + + CharBuffer getExceptionCharBuffer() { + return null; + } + + protected byte[] getIllegalByteArray() { + return new byte[] { (byte) 0xd8, (byte) 0x00 }; + } + + protected void assertFlushed() { + } +} diff --git a/nio_char/src/test/java/tests/nio_char/AllTests.java b/nio_char/src/test/java/tests/nio_char/AllTests.java new file mode 100644 index 0000000..f466b07 --- /dev/null +++ b/nio_char/src/test/java/tests/nio_char/AllTests.java @@ -0,0 +1,40 @@ +/* Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package tests.nio_char; + +import junit.framework.Test; +import junit.framework.TestSuite; + +/** + * Test suite that includes all tests for the NIO_Char project. + */ +public class AllTests { + + public static void main(String[] args) { + junit.textui.TestRunner.run(AllTests.suite()); + } + + public static Test suite() { + TestSuite suite = new TestSuite("All NIO_Char test suites"); + // $JUnit-BEGIN$ + suite.addTest(org.apache.harmony.nio_char.tests.java.nio.charset.AllTests.suite()); + suite.addTest(org.apache.harmony.nio_char.tests.java.nio.charset.spi.AllTests.suite()); + suite.addTest(tests.api.java.nio.charset.AllTests.suite()); + // $JUnit-END$ + return suite; + } +} diff --git a/nio_char/src/test/resources/jars/charset_provider.jar b/nio_char/src/test/resources/jars/charset_provider.jar Binary files differnew file mode 100644 index 0000000..f1230d9 --- /dev/null +++ b/nio_char/src/test/resources/jars/charset_provider.jar diff --git a/nio_char/src/test/resources/serialization/org/apache/harmony/nio_char/tests/java/nio/charset/CharacterCodingExceptionTest.golden.ser b/nio_char/src/test/resources/serialization/org/apache/harmony/nio_char/tests/java/nio/charset/CharacterCodingExceptionTest.golden.ser Binary files differnew file mode 100644 index 0000000..a95c4e4 --- /dev/null +++ b/nio_char/src/test/resources/serialization/org/apache/harmony/nio_char/tests/java/nio/charset/CharacterCodingExceptionTest.golden.ser diff --git a/nio_char/src/test/resources/serialization/org/apache/harmony/nio_char/tests/java/nio/charset/CoderMalfunctionErrorTest.golden.ser b/nio_char/src/test/resources/serialization/org/apache/harmony/nio_char/tests/java/nio/charset/CoderMalfunctionErrorTest.golden.ser Binary files differnew file mode 100644 index 0000000..6f4a4e3 --- /dev/null +++ b/nio_char/src/test/resources/serialization/org/apache/harmony/nio_char/tests/java/nio/charset/CoderMalfunctionErrorTest.golden.ser diff --git a/nio_char/src/test/resources/serialization/org/apache/harmony/nio_char/tests/java/nio/charset/IllegalCharsetNameExceptionTest.golden.ser b/nio_char/src/test/resources/serialization/org/apache/harmony/nio_char/tests/java/nio/charset/IllegalCharsetNameExceptionTest.golden.ser Binary files differnew file mode 100644 index 0000000..59e4d0b --- /dev/null +++ b/nio_char/src/test/resources/serialization/org/apache/harmony/nio_char/tests/java/nio/charset/IllegalCharsetNameExceptionTest.golden.ser diff --git a/nio_char/src/test/resources/serialization/org/apache/harmony/nio_char/tests/java/nio/charset/MalformedInputExceptionTest.golden.ser b/nio_char/src/test/resources/serialization/org/apache/harmony/nio_char/tests/java/nio/charset/MalformedInputExceptionTest.golden.ser Binary files differnew file mode 100644 index 0000000..e2f0dec --- /dev/null +++ b/nio_char/src/test/resources/serialization/org/apache/harmony/nio_char/tests/java/nio/charset/MalformedInputExceptionTest.golden.ser diff --git a/nio_char/src/test/resources/serialization/org/apache/harmony/nio_char/tests/java/nio/charset/UnmappableCharacterExceptionTest.golden.ser b/nio_char/src/test/resources/serialization/org/apache/harmony/nio_char/tests/java/nio/charset/UnmappableCharacterExceptionTest.golden.ser Binary files differnew file mode 100644 index 0000000..7933fb9 --- /dev/null +++ b/nio_char/src/test/resources/serialization/org/apache/harmony/nio_char/tests/java/nio/charset/UnmappableCharacterExceptionTest.golden.ser diff --git a/nio_char/src/test/resources/serialization/org/apache/harmony/nio_char/tests/java/nio/charset/UnsupportedCharsetExceptionTest.golden.ser b/nio_char/src/test/resources/serialization/org/apache/harmony/nio_char/tests/java/nio/charset/UnsupportedCharsetExceptionTest.golden.ser Binary files differnew file mode 100644 index 0000000..07c63bb --- /dev/null +++ b/nio_char/src/test/resources/serialization/org/apache/harmony/nio_char/tests/java/nio/charset/UnsupportedCharsetExceptionTest.golden.ser |