diff options
Diffstat (limited to 'icu/src')
38 files changed, 12702 insertions, 0 deletions
diff --git a/icu/src/main/java/com/ibm/icu4jni/charset/CharsetDecoderICU.java b/icu/src/main/java/com/ibm/icu4jni/charset/CharsetDecoderICU.java new file mode 100644 index 0000000..3b9bf86 --- /dev/null +++ b/icu/src/main/java/com/ibm/icu4jni/charset/CharsetDecoderICU.java @@ -0,0 +1,342 @@ +/** +******************************************************************************* +* Copyright (C) 1996-2006, International Business Machines Corporation and * +* others. All Rights Reserved. * +******************************************************************************* +* +******************************************************************************* +*/ + /** + * A JNI interface for ICU converters. + * + * + * @author Ram Viswanadha, IBM + */ +package com.ibm.icu4jni.charset; + +import com.ibm.icu4jni.common.ErrorCode; +// BEGIN android-removed +// import com.ibm.icu4jni.converters.NativeConverter; +// END android-removed + + +import java.nio.CharBuffer; +import java.nio.charset.Charset; +import java.nio.charset.CharsetDecoder; +import java.nio.charset.CoderResult; +import java.nio.charset.CodingErrorAction; +import java.nio.ByteBuffer; + +public final class CharsetDecoderICU extends CharsetDecoder{ + + + private static final int INPUT_OFFSET = 0, + OUTPUT_OFFSET = 1, + INVALID_BYTES = 2, + INPUT_HELD = 3, + LIMIT = 4; + /* data is 3 element array where + * data[INPUT_OFFSET] = on input contains the start of input and on output the number of input chars consumed + * data[OUTPUT_OFFSET] = on input contains the start of output and on output the number of output bytes written + * data[INVALID_CHARS] = number of invalid chars + * data[INPUT_HELD] = number of input chars held in the converter's state + */ + private int[] data = new int[LIMIT]; + + /* handle to the ICU converter that is opened */ + private long converterHandle=0; + + + private byte[] input = null; + private char[] output= null; + + // These instance variables are + // always assigned in the methods + // before being used. This class + // inhrently multithread unsafe + // so we dont have to worry about + // synchronization + private int inEnd; + private int outEnd; + private int ec; + private int onUnmappableInput = NativeConverter.STOP_CALLBACK;; + private int onMalformedInput = NativeConverter.STOP_CALLBACK;; + private int savedInputHeldLen; + + /** + * Constructs a new decoder for the given charset + * @param cs for which the decoder is created + * @param cHandle the address of ICU converter + * @exception RuntimeException + * @stable ICU 2.4 + */ + public CharsetDecoderICU(Charset cs,long cHandle){ + super(cs, + NativeConverter.getAveCharsPerByte(cHandle), + NativeConverter.getMaxCharsPerByte(cHandle) + ); + + char[] sub = replacement().toCharArray(); + ec = NativeConverter.setCallbackDecode(cHandle, + onMalformedInput, + onUnmappableInput, + sub, sub.length); + if(ErrorCode.isFailure(ec)){ + throw ErrorCode.getException(ec); + } + // store the converter handle + converterHandle=cHandle; + + } + + /** + * Sets this decoders replacement string. Substitutes the string in input if an + * umappable or illegal sequence is encountered + * @param newReplacement to replace the error bytes with + * @stable ICU 2.4 + */ + protected void implReplaceWith(String newReplacement) { + if(converterHandle > 0){ + if( newReplacement.length() > NativeConverter.getMaxBytesPerChar(converterHandle)) { + throw new IllegalArgumentException(); + } + ec =NativeConverter.setSubstitutionChars(converterHandle, + newReplacement.toCharArray(), + newReplacement.length() + ); + if(ErrorCode.isFailure(ec)){ + throw ErrorCode.getException(ec); + } + } + } + + /** + * Sets the action to be taken if an illegal sequence is encountered + * @param newAction action to be taken + * @exception IllegalArgumentException + * @stable ICU 2.4 + */ + protected final void implOnMalformedInput(CodingErrorAction newAction) { + if(newAction.equals(CodingErrorAction.IGNORE)){ + onMalformedInput = NativeConverter.SKIP_CALLBACK; + }else if(newAction.equals(CodingErrorAction.REPLACE)){ + onMalformedInput = NativeConverter.SUBSTITUTE_CALLBACK; + }else if(newAction.equals(CodingErrorAction.REPORT)){ + onMalformedInput = NativeConverter.STOP_CALLBACK; + } + char[] sub = replacement().toCharArray(); + //System.out.println(" setting callbacks mfi " + onMalformedInput +" umi " + onUnmappableInput); + ec = NativeConverter.setCallbackDecode(converterHandle, onMalformedInput, onUnmappableInput, sub, sub.length); + if(ErrorCode.isFailure(ec)){ + throw ErrorCode.getException(ec); + } + } + + /** + * Sets the action to be taken if an illegal sequence is encountered + * @param newAction action to be taken + * @exception IllegalArgumentException + * @stable ICU 2.4 + */ + protected final void implOnUnmappableCharacter(CodingErrorAction newAction) { + if(newAction.equals(CodingErrorAction.IGNORE)){ + onUnmappableInput = NativeConverter.SKIP_CALLBACK; + }else if(newAction.equals(CodingErrorAction.REPLACE)){ + onUnmappableInput = NativeConverter.SUBSTITUTE_CALLBACK; + }else if(newAction.equals(CodingErrorAction.REPORT)){ + onUnmappableInput = NativeConverter.STOP_CALLBACK; + } + char[] sub = replacement().toCharArray(); + ec = NativeConverter.setCallbackDecode(converterHandle,onMalformedInput, onUnmappableInput, sub, sub.length); + if(ErrorCode.isFailure(ec)){ + throw ErrorCode.getException(ec); + } + } + + /** + * Flushes any characters saved in the converter's internal buffer and + * resets the converter. + * @param out action to be taken + * @return result of flushing action and completes the decoding all input. + * Returns CoderResult.UNDERFLOW if the action succeeds. + * @stable ICU 2.4 + */ + protected final CoderResult implFlush(CharBuffer out) { + try{ + + data[OUTPUT_OFFSET] = getArray(out); + + ec=NativeConverter.flushByteToChar( + converterHandle, /* Handle to ICU Converter */ + output, /* input array of chars */ + outEnd, /* input index+1 to be written */ + data /* contains data, inOff,outOff */ + ); + + + /* If we don't have room for the output, throw an exception*/ + if (ErrorCode.isFailure(ec)) { + if (ec == ErrorCode.U_BUFFER_OVERFLOW_ERROR) { + return CoderResult.OVERFLOW; + }else if (ec == ErrorCode.U_TRUNCATED_CHAR_FOUND ) {//CSDL: add this truncated character error handling + if(data[INPUT_OFFSET]>0){ + return CoderResult.malformedForLength(data[INPUT_OFFSET]); + } + }else { + ErrorCode.getException(ec); + } + } + return CoderResult.UNDERFLOW; + }finally{ + /* save the flushed data */ + setPosition(out); + implReset(); + } + } + + /** + * Resets the to Unicode mode of converter + * @stable ICU 2.4 + */ + protected void implReset() { + NativeConverter.resetByteToChar(converterHandle); + data[INPUT_OFFSET] = 0; + data[OUTPUT_OFFSET] = 0; + data[INVALID_BYTES] = 0; + data[INPUT_HELD] = 0; + savedInputHeldLen = 0; + output = null; + input = null; + } + + /** + * Decodes one or more bytes. The default behaviour of the converter + * is stop and report if an error in input stream is encountered. + * To set different behaviour use @see CharsetDecoder.onMalformedInput() + * This method allows a buffer by buffer conversion of a data stream. + * The state of the conversion is saved between calls to convert. + * Among other things, this means multibyte input sequences can be + * split between calls. If a call to convert results in an Error, the + * conversion may be continued by calling convert again with suitably + * modified parameters.All conversions should be finished with a call to + * the flush method. + * @param in buffer to decode + * @param out buffer to populate with decoded result + * @return result of decoding action. Returns CoderResult.UNDERFLOW if the decoding + * action succeeds or more input is needed for completing the decoding action. + * @stable ICU 2.4 + */ + protected CoderResult decodeLoop(ByteBuffer in,CharBuffer out){ + + if(!in.hasRemaining()){ + return CoderResult.UNDERFLOW; + } + + data[INPUT_OFFSET] = getArray(in); + data[OUTPUT_OFFSET]= getArray(out); + data[INPUT_HELD] = 0; + + try{ + /* do the conversion */ + ec=NativeConverter.decode( + converterHandle, /* Handle to ICU Converter */ + input, /* input array of bytes */ + inEnd, /* last index+1 to be converted */ + output, /* input array of chars */ + outEnd, /* input index+1 to be written */ + data, /* contains data, inOff,outOff */ + false /* donot flush the data */ + ); + + + /* return an error*/ + if(ec == ErrorCode.U_BUFFER_OVERFLOW_ERROR){ + return CoderResult.OVERFLOW; + }else if(ec==ErrorCode.U_INVALID_CHAR_FOUND){ + return CoderResult.malformedForLength(data[INVALID_BYTES]); + }else if(ec==ErrorCode.U_ILLEGAL_CHAR_FOUND){ + return CoderResult.malformedForLength(data[INVALID_BYTES]); + } + /* decoding action succeded */ + return CoderResult.UNDERFLOW; + }finally{ + setPosition(in); + setPosition(out); + } + } + + /** + * Releases the system resources by cleanly closing ICU converter opened + * @stable ICU 2.4 + */ + protected void finalize()throws Throwable{ + NativeConverter.closeConverter(converterHandle); + super.finalize(); + converterHandle = 0; + } + + //------------------------------------------ + // private utility methods + //------------------------------------------ + + private final int getArray(CharBuffer out){ + if(out.hasArray()){ + output = out.array(); + outEnd = out.limit(); + return out.position(); + }else{ + outEnd = out.remaining(); + if(output==null || (outEnd > output.length)){ + output = new char[outEnd]; + } + //since the new + // buffer start position + // is 0 + return 0; + } + + } + private final int getArray(ByteBuffer in){ + if(in.hasArray()){ + input = in.array(); + inEnd = in.limit(); + return in.position()+savedInputHeldLen;/*exclude the number fo bytes held in previous conversion*/ + }else{ + inEnd = in.remaining(); + if(input==null|| (inEnd > input.length)){ + input = new byte[inEnd]; + } + // save the current position + int pos = in.position(); + in.get(input,0,inEnd); + // reset the position + in.position(pos); + // the start position + // of the new buffer + // is whatever is savedInputLen + return savedInputHeldLen; + } + + } + private final void setPosition(CharBuffer out){ + if(out.hasArray()){ + out.position(out.position() + data[OUTPUT_OFFSET]); + }else{ + out.put(output,0,data[OUTPUT_OFFSET]); + } + } + private final void setPosition(ByteBuffer in){ + + // ok was there input held in the previous invocation of decodeLoop + // that resulted in output in this invocation? + if(data[OUTPUT_OFFSET]>0 && savedInputHeldLen >0){ + int len = in.position() + data[INPUT_OFFSET] + savedInputHeldLen; + in.position(len); + savedInputHeldLen = data[INPUT_HELD]; + }else{ + in.position(in.position() + data[INPUT_OFFSET] + savedInputHeldLen); + savedInputHeldLen = data[INPUT_HELD]; + in.position(in.position() - savedInputHeldLen); + } + } +} diff --git a/icu/src/main/java/com/ibm/icu4jni/charset/CharsetEncoderICU.java b/icu/src/main/java/com/ibm/icu4jni/charset/CharsetEncoderICU.java new file mode 100644 index 0000000..0fdb2c5 --- /dev/null +++ b/icu/src/main/java/com/ibm/icu4jni/charset/CharsetEncoderICU.java @@ -0,0 +1,408 @@ +/** +******************************************************************************* +* Copyright (C) 1996-2006, International Business Machines Corporation and * +* others. All Rights Reserved. * +******************************************************************************* +* +******************************************************************************* +*/ +/** + * A JNI interface for ICU converters. + * + * + * @author Ram Viswanadha, IBM + */ +package com.ibm.icu4jni.charset; + +import java.nio.ByteBuffer; +import java.nio.CharBuffer; +import java.nio.charset.Charset; +import java.nio.charset.CharsetEncoder; +import java.nio.charset.CoderResult; +import java.nio.charset.CodingErrorAction; + +import com.ibm.icu4jni.common.ErrorCode; +// BEGIN android-removed +// import com.ibm.icu4jni.converters.NativeConverter; +// END android-removed + +public final class CharsetEncoderICU extends CharsetEncoder { + + private static final int INPUT_OFFSET = 0, + OUTPUT_OFFSET = 1, + INVALID_CHARS = 2, + INPUT_HELD = 3, + LIMIT = 4; + /* data is 3 element array where + * data[INPUT_OFFSET] = on input contains the start of input and on output the number of input chars consumed + * data[OUTPUT_OFFSET] = on input contains the start of output and on output the number of output bytes written + * data[INVALID_CHARS] = number of invalid chars + * data[INPUT_HELD] = number of input chars held in the converter's state + */ + private int[] data = new int[LIMIT]; + /* handle to the ICU converter that is opened */ + private long converterHandle=0; + + private char[] input = null; + private byte[] output = null; + + // These instance variables are + // always assigned in the methods + // before being used. This class + // inhrently multithread unsafe + // so we dont have to worry about + // synchronization + private int inEnd; + private int outEnd; + private int ec; + private int savedInputHeldLen; + private int onUnmappableInput = NativeConverter.STOP_CALLBACK;; + private int onMalformedInput = NativeConverter.STOP_CALLBACK;; + + /** + * Construcs a new encoder for the given charset + * @param cs for which the decoder is created + * @param cHandle the address of ICU converter + * @param replacement the substitution bytes + * @stable ICU 2.4 + */ + public CharsetEncoderICU(Charset cs, long cHandle, byte[] replacement) { + super( + cs, + (float) NativeConverter.getAveBytesPerChar(cHandle), + (float) NativeConverter.getMaxBytesPerChar(cHandle), + replacement); + byte[] sub = replacement(); + // The default callback action on unmappable input + // or malformed input is to ignore so we set ICU converter + // callback to stop and report the error + ec = NativeConverter.setCallbackEncode( cHandle, + onMalformedInput, + onUnmappableInput, + sub, sub.length); + converterHandle = cHandle; + if (ErrorCode.isFailure(ec)) { + throw ErrorCode.getException(ec); + } + } + + /** + * Sets this encoders replacement string. Substitutes the string in output if an + * umappable or illegal sequence is encountered + * @param newReplacement to replace the error chars with + * @stable ICU 2.4 + */ + protected void implReplaceWith(byte[] newReplacement) { + if (converterHandle != 0) { + if (newReplacement.length + > NativeConverter.getMaxBytesPerChar(converterHandle)) { + throw new IllegalArgumentException("Number of replacement Bytes are greater than max bytes per char"); + } + ec = NativeConverter.setSubstitutionBytes(converterHandle, + newReplacement, + newReplacement.length); + if (ErrorCode.isFailure(ec)) { + throw ErrorCode.getException(ec); + } + } + } + + /** + * Sets the action to be taken if an illegal sequence is encountered + * @param newAction action to be taken + * @exception IllegalArgumentException + * @stable ICU 2.4 + */ + protected void implOnMalformedInput(CodingErrorAction newAction) { + onMalformedInput = NativeConverter.STOP_CALLBACK; + + if (newAction.equals(CodingErrorAction.IGNORE)) { + onMalformedInput = NativeConverter.SKIP_CALLBACK; + } else if (newAction.equals(CodingErrorAction.REPLACE)) { + onMalformedInput = NativeConverter.SUBSTITUTE_CALLBACK; + } + byte[] sub = replacement(); + ec = NativeConverter.setCallbackEncode(converterHandle, onMalformedInput, onUnmappableInput, sub, sub.length); + if (ErrorCode.isFailure(ec)) { + throw ErrorCode.getException(ec); + } + + } + + /** + * Sets the action to be taken if an illegal sequence is encountered + * @param newAction action to be taken + * @exception IllegalArgumentException + * @stable ICU 2.4 + */ + protected void implOnUnmappableCharacter(CodingErrorAction newAction) { + onUnmappableInput = NativeConverter.STOP_CALLBACK; + + if (newAction.equals(CodingErrorAction.IGNORE)) { + onUnmappableInput = NativeConverter.SKIP_CALLBACK; + } else if (newAction.equals(CodingErrorAction.REPLACE)) { + onUnmappableInput = NativeConverter.SUBSTITUTE_CALLBACK; + } + byte[] sub = replacement(); + ec = NativeConverter.setCallbackEncode(converterHandle, onMalformedInput, onUnmappableInput, sub, sub.length); + if (ErrorCode.isFailure(ec)) { + throw ErrorCode.getException(ec); + } + } + + /** + * Flushes any characters saved in the converter's internal buffer and + * resets the converter. + * @param out action to be taken + * @return result of flushing action and completes the decoding all input. + * Returns CoderResult.UNDERFLOW if the action succeeds. + * @stable ICU 2.4 + */ + protected CoderResult implFlush(ByteBuffer out) { + try { + data[OUTPUT_OFFSET] = getArray(out); + ec = NativeConverter.flushCharToByte(converterHandle,/* Handle to ICU Converter */ + output, /* output array of chars */ + outEnd, /* output index+1 to be written */ + data /* contains data, inOff,outOff */ + ); + + /* If we don't have room for the output, throw an exception*/ + if (ErrorCode.isFailure(ec)) { + if (ec == ErrorCode.U_BUFFER_OVERFLOW_ERROR) { + return CoderResult.OVERFLOW; + }else if (ec == ErrorCode.U_TRUNCATED_CHAR_FOUND) {//CSDL: add this truncated character error handling + if(data[INPUT_OFFSET]>0){ + return CoderResult.malformedForLength(data[INPUT_OFFSET]); + } + }else { + ErrorCode.getException(ec); + } + } + return CoderResult.UNDERFLOW; + } finally { + setPosition(out); + implReset(); + } + } + + /** + * Resets the from Unicode mode of converter + * @stable ICU 2.4 + */ + protected void implReset() { + NativeConverter.resetCharToByte(converterHandle); + data[INPUT_OFFSET] = 0; + data[OUTPUT_OFFSET] = 0; + data[INVALID_CHARS] = 0; + data[INPUT_HELD] = 0; + savedInputHeldLen = 0; + } + + /** + * Encodes one or more chars. The default behaviour of the + * converter is stop and report if an error in input stream is encountered. + * To set different behaviour use @see CharsetEncoder.onMalformedInput() + * @param in buffer to decode + * @param out buffer to populate with decoded result + * @return result of decoding action. Returns CoderResult.UNDERFLOW if the decoding + * action succeeds or more input is needed for completing the decoding action. + * @stable ICU 2.4 + */ + protected CoderResult encodeLoop(CharBuffer in, ByteBuffer out) { + + if (!in.hasRemaining()) { + return CoderResult.UNDERFLOW; + } + + data[INPUT_OFFSET] = getArray(in); + data[OUTPUT_OFFSET]= getArray(out); + data[INPUT_HELD] = 0; + // BEGIN android-added + data[INVALID_CHARS] = 0; // Make sure we don't see earlier errors. + // END android added + + try { + /* do the conversion */ + ec = NativeConverter.encode(converterHandle,/* Handle to ICU Converter */ + input, /* input array of bytes */ + inEnd, /* last index+1 to be converted */ + output, /* output array of chars */ + outEnd, /* output index+1 to be written */ + data, /* contains data, inOff,outOff */ + false /* donot flush the data */ + ); + if (ErrorCode.isFailure(ec)) { + /* If we don't have room for the output return error */ + if (ec == ErrorCode.U_BUFFER_OVERFLOW_ERROR) { + return CoderResult.OVERFLOW; + } else if (ec == ErrorCode.U_INVALID_CHAR_FOUND) { + return CoderResult.unmappableForLength(data[INVALID_CHARS]); + } else if (ec == ErrorCode.U_ILLEGAL_CHAR_FOUND) { + // in.position(in.position() - 1); + return CoderResult.malformedForLength(data[INVALID_CHARS]); + } + } + return CoderResult.UNDERFLOW; + } finally { + /* save state */ + setPosition(in); + setPosition(out); + } + } + + /** + * Ascertains if a given Unicode character can + * be converted to the target encoding + * + * @param c the character to be converted + * @return true if a character can be converted + * @stable ICU 2.4 + * + */ + public boolean canEncode(char c) { + return canEncode((int) c); + } + + /** + * Ascertains if a given Unicode code point (32bit value for handling surrogates) + * can be converted to the target encoding. If the caller wants to test if a + * surrogate pair can be converted to target encoding then the + * responsibility of assembling the int value lies with the caller. + * For assembling a code point the caller can use UTF16 class of ICU4J and do something like: + * <pre> + * while(i<mySource.length){ + * if(UTF16.isLeadSurrogate(mySource[i])&& i+1< mySource.length){ + * if(UTF16.isTrailSurrogate(mySource[i+1])){ + * int temp = UTF16.charAt(mySource,i,i+1,0); + * if(!((CharsetEncoderICU) myConv).canEncode(temp)){ + * passed=false; + * } + * i++; + * i++; + * } + * } + * } + * </pre> + * or + * <pre> + * String src = new String(mySource); + * int i,codepoint; + * boolean passed = false; + * while(i<src.length()){ + * codepoint = UTF16.charAt(src,i); + * i+= (codepoint>0xfff)? 2:1; + * if(!(CharsetEncoderICU) myConv).canEncode(codepoint)){ + * passed = false; + * } + * } + * </pre> + * + * @param codepoint Unicode code point as int value + * @return true if a character can be converted + * @obsolete ICU 2.4 + * @deprecated ICU 3.4 + */ + public boolean canEncode(int codepoint) { + return NativeConverter.canEncode(converterHandle, codepoint); + } + + /** + * Releases the system resources by cleanly closing ICU converter opened + * @exception Throwable exception thrown by super class' finalize method + * @stable ICU 2.4 + */ + protected void finalize() throws Throwable { + NativeConverter.closeConverter(converterHandle); + super.finalize(); + converterHandle=0; + } + + //------------------------------------------ + // private utility methods + //------------------------------------------ + private final int getArray(ByteBuffer out) { + if(out.hasArray()){ + output = out.array(); + outEnd = out.limit(); + return out.position(); + }else{ + outEnd = out.remaining(); + if(output==null || (outEnd > output.length)){ + output = new byte[outEnd]; + } + //since the new + // buffer start position + // is 0 + return 0; + } + } + + private final int getArray(CharBuffer in) { + if(in.hasArray()){ + input = in.array(); + inEnd = in.limit(); + return in.position()+savedInputHeldLen;/*exclude the number fo bytes held in previous conversion*/ + }else{ + inEnd = in.remaining(); + if(input==null|| (inEnd > input.length)){ + input = new char[inEnd]; + } + // save the current position + int pos = in.position(); + in.get(input,0,inEnd); + // reset the position + in.position(pos); + // the start position + // of the new buffer + // is whatever is savedInputLen + return savedInputHeldLen; + } + + } + private final void setPosition(ByteBuffer out) { + + if (out.hasArray()) { + // in getArray method we accessed the + // array backing the buffer directly and wrote to + // it, so just just set the position and return. + // This is done to avoid the creation of temp array. + out.position(out.position() + data[OUTPUT_OFFSET] ); + } else { + out.put(output, 0, data[OUTPUT_OFFSET]); + } + } + private final void setPosition(CharBuffer in){ + +// BEGIN android-removed +// // was there input held in the previous invocation of encodeLoop +// // that resulted in output in this invocation? +// if(data[OUTPUT_OFFSET]>0 && savedInputHeldLen>0){ +// int len = in.position() + data[INPUT_OFFSET] + savedInputHeldLen; +// in.position(len); +// savedInputHeldLen = data[INPUT_HELD]; +// }else{ +// in.position(in.position() + data[INPUT_OFFSET] + savedInputHeldLen); +// savedInputHeldLen = data[INPUT_HELD]; +// in.position(in.position() - savedInputHeldLen); +// } +// END android-removed + +// BEGIN android-added + // Slightly rewired original code to make it cleaner. Also + // added a fix for the problem where input charatcers got + // lost when invalid characters were encountered. Not sure + // what happens when data[INVALID_CHARS] is > 1, though, + // since we never saw that happening. + int len = in.position() + data[INPUT_OFFSET] + savedInputHeldLen; + len -= data[INVALID_CHARS]; // Otherwise position becomes wrong. + in.position(len); + savedInputHeldLen = data[INPUT_HELD]; + // was there input held in the previous invocation of encodeLoop + // that resulted in output in this invocation? + if(!(data[OUTPUT_OFFSET]>0 && savedInputHeldLen>0)){ + in.position(in.position() - savedInputHeldLen); + } +// END android-added + } +} diff --git a/icu/src/main/java/com/ibm/icu4jni/charset/CharsetICU.java b/icu/src/main/java/com/ibm/icu4jni/charset/CharsetICU.java new file mode 100644 index 0000000..df6f7dc --- /dev/null +++ b/icu/src/main/java/com/ibm/icu4jni/charset/CharsetICU.java @@ -0,0 +1,129 @@ +/** +******************************************************************************* +* Copyright (C) 1996-2005, International Business Machines Corporation and * +* others. All Rights Reserved. * +******************************************************************************* +* +******************************************************************************* +*/ + +package com.ibm.icu4jni.charset; + +import java.nio.charset.Charset; +import java.nio.charset.CharsetDecoder; +import java.nio.charset.CharsetEncoder; +import java.util.HashMap; +import java.util.Map; + +// BEGIN android-removed +// import com.ibm.icu4jni.common.ErrorCode; +// import com.ibm.icu4jni.converters.NativeConverter; +// END android-removed + + +public final class CharsetICU extends Charset{ + private String icuCanonicalName; + /** + * Constructor to create a the CharsetICU object + * @param canonicalName the canonical name as a string + * @param aliases the alias set as an array of strings + * @stable ICU 2.4 + */ + protected CharsetICU(String canonicalName, String icuCanonName, String[] aliases) { + super(canonicalName,aliases); + icuCanonicalName = icuCanonName; + + } + /** + * Returns a new decoder instance of this charset object + * @return a new decoder object + * @stable ICU 2.4 + */ + public CharsetDecoder newDecoder(){ + // the arrays are locals and not + // instance variables since the + // methods on this class need to + // be thread safe + long converterHandle = NativeConverter.openConverter(icuCanonicalName); + return new CharsetDecoderICU(this,converterHandle); + }; + + // hardCoded list of replacement bytes + private static final Map subByteMap = new HashMap(); + static{ + subByteMap.put("UTF-32",new byte[]{0x00, 0x00, (byte)0xfe, (byte)0xff}); + subByteMap.put("ibm-16684_P110-2003",new byte[]{0x40, 0x40}); // make \u3000 the sub char + subByteMap.put("ibm-971_P100-1995",new byte[]{(byte)0xa1, (byte)0xa1}); // make \u3000 the sub char + } + /** + * Returns a new encoder object of the charset + * @return a new encoder + * @stable ICU 2.4 + */ + public CharsetEncoder newEncoder(){ + // the arrays are locals and not + // instance variables since the + // methods on this class need to + // be thread safe + long converterHandle = NativeConverter.openConverter(icuCanonicalName); + + //According to the contract all converters should have non-empty replacement + byte[] replacement = NativeConverter.getSubstitutionBytes(converterHandle); + + try{ + return new CharsetEncoderICU(this,converterHandle, replacement); + }catch(IllegalArgumentException ex){ + // work around for the non-sensical check in the nio API that + // a substitution character must be mappable while decoding!! + replacement = (byte[])subByteMap.get(icuCanonicalName); + if(replacement==null){ + replacement = new byte[NativeConverter.getMinBytesPerChar(converterHandle)]; + for(int i=0; i<replacement.length; i++){ + replacement[i]= 0x3f; + } + } + NativeConverter.setSubstitutionBytes(converterHandle, replacement, replacement.length); + return new CharsetEncoderICU(this,converterHandle, replacement); + } + } + + /** + * Ascertains if a charset is a sub set of this charset + * @param cs charset to test + * @return true if the given charset is a subset of this charset + * @stable ICU 2.4 + * + * //CSDL: major changes by Jack + */ + public boolean contains(Charset cs){ + if (null == cs) { + return false; + } else if (this.equals(cs)) { + return true; + } + + long converterHandle1 = 0; + long converterHandle2 = 0; + + try { + converterHandle1 = NativeConverter.openConverter(this.name()); + if (converterHandle1 > 0) { + converterHandle2 = NativeConverter.openConverter(cs.name()); + if (converterHandle2 > 0) { + return NativeConverter.contains(converterHandle1, + converterHandle2); + } + } + return false; + } finally { + if (0 != converterHandle1) { + NativeConverter.closeConverter(converterHandle1); + if (0 != converterHandle2) { + NativeConverter.closeConverter(converterHandle2); + } + } + } + } +} + + diff --git a/icu/src/main/java/com/ibm/icu4jni/charset/CharsetProviderICU.java b/icu/src/main/java/com/ibm/icu4jni/charset/CharsetProviderICU.java new file mode 100644 index 0000000..6f63479 --- /dev/null +++ b/icu/src/main/java/com/ibm/icu4jni/charset/CharsetProviderICU.java @@ -0,0 +1,118 @@ +/** +******************************************************************************* +* Copyright (C) 1996-2005, International Business Machines Corporation and * +* others. All Rights Reserved. * +******************************************************************************* +* +******************************************************************************* +*/ + +package com.ibm.icu4jni.charset; + +import java.nio.charset.Charset; +import java.nio.charset.spi.CharsetProvider; +import java.util.*; +import java.util.Iterator; +// BEGIN android-removed +// import com.ibm.icu4jni.converters.NativeConverter; +// END android-removed + +public final class CharsetProviderICU extends CharsetProvider{ + + /** + * Constructs a CharsetProviderICU object + * @stable ICU 2.4 + */ + public CharsetProviderICU(){ + } + + /** + * Constructs a charset for the given charset name + * @param charsetName charset name + * @return charset objet for the given charset name + * @stable ICU 2.4 + */ + public final Charset charsetForName(String charsetName) { + // get the canonical name + String icuCanonicalName = NativeConverter.getICUCanonicalName(charsetName); + + // create the converter object and return it + if(icuCanonicalName==null || icuCanonicalName.length()==0){ + // this would make the Charset API to throw + // unsupported encoding exception + return null; + } + + // BEGIN android-added + try{ + long cn = NativeConverter.openConverter(icuCanonicalName); + NativeConverter.closeConverter(cn); + }catch (RuntimeException re) { + // unsupported encoding. let the charset api throw an + // UnsupportedEncodingException + return null; + } + // END android-added + + return getCharset(icuCanonicalName); + } + private final Charset getCharset(String icuCanonicalName){ + String[] aliases = (String[])NativeConverter.getAliases(icuCanonicalName); + String canonicalName = NativeConverter.getJavaCanonicalName(icuCanonicalName); + return (new CharsetICU(canonicalName,icuCanonicalName, aliases)); + } + /** + * Adds an entry to the given map whose key is the charset's + * canonical name and whose value is the charset itself. + * @param map a map to receive charset objects and names + * @stable ICU 2.4 + */ + public final void putCharsets(Map map) { + // Get the available converter canonical names and aliases + String[] charsets = NativeConverter.getAvailable(); + for(int i=0; i<charsets.length;i++){ + // store the charsets and aliases in a Map + if (!map.containsKey(charsets[i])){ + map.put(charsets[i], charsetForName(charsets[i])); + } + } + } + /** + * Class that implements the iterator for charsets + * @stable ICU 2.4 + */ + protected final class CharsetIterator implements Iterator{ + private String[] names; + private int currentIndex; + protected CharsetIterator(String[] strs){ + names = strs; + currentIndex=0; + } + public boolean hasNext(){ + return (currentIndex< names.length); + } + public Object next(){ + if(currentIndex<names.length){ + return charsetForName(names[currentIndex++]); + }else{ + throw new NoSuchElementException(); + } + } + public void remove() { + throw new UnsupportedOperationException(); + } + } + + + /** + * Returns an iterator for the available charsets + * @return Iterator the charset name iterator + * @stable ICU 2.4 + */ + public final Iterator charsets(){ + String[] charsets = NativeConverter.getAvailable(); + Iterator iter = new CharsetIterator(charsets); + return iter; + } + +} diff --git a/icu/src/main/java/com/ibm/icu4jni/charset/NativeConverter.java b/icu/src/main/java/com/ibm/icu4jni/charset/NativeConverter.java new file mode 100644 index 0000000..2bfb050 --- /dev/null +++ b/icu/src/main/java/com/ibm/icu4jni/charset/NativeConverter.java @@ -0,0 +1,422 @@ +/** +******************************************************************************* +* Copyright (C) 1996-2006, International Business Machines Corporation and * +* others. All Rights Reserved. * +******************************************************************************* +* +******************************************************************************* +*/ + +package com.ibm.icu4jni.charset; + +/** + * Class for accessing the underlying JNI methods + * @internal ICU 2.4 + */ +final class NativeConverter{ + + //Native methods + + /** + * Converts an array of bytes containing characters in an external + * encoding into an array of Unicode characters. This method allows + * a buffer by buffer conversion of a data stream. The state of the + * conversion is saved between calls to convert. Among other things, + * this means multibyte input sequences can be split between calls. + * If a call to convert results in an Error, the conversion may be + * continued by calling convert again with suitably modified parameters. + * All conversions should be finished with a call to the flush method. + * + * @param converterHandle Address of converter object created by C code + * @param input byte array containing text to be converted. + * @param inEnd stop conversion at this offset in input array (exclusive). + * @param output character array to receive conversion result. + * @param outEnd stop writing to output array at this offset (exclusive). + * @param data integer array containing the following data + * data[0] = inputOffset + * data[1] = outputOffset + * @return int error code returned by ICU + * @internal ICU 2.4 + */ + + public static final native int convertByteToChar( long converterHandle, + byte[] input, int inEnd, + char[] output, int outEnd, + int[] data, + boolean flush); + /** + * Converts an array of bytes containing characters in an external + * encoding into an array of Unicode characters. This method allows + * a buffer by buffer conversion of a data stream. The state of the + * conversion is saved between calls to convert. Among other things, + * this means multibyte input sequences can be split between calls. + * If a call to convert results in an Error, the conversion may be + * continued by calling convert again with suitably modified parameters. + * All conversions should be finished with a call to the flush method. + * + * @param converterHandle Address of converter object created by C code + * @param input byte array containing text to be converted. + * @param inEnd stop conversion at this offset in input array (exclusive). + * @param output character array to receive conversion result. + * @param outEnd stop writing to output array at this offset (exclusive). + * @param data integer array containing the following data + * data[0] = inputOffset + * data[1] = outputOffset + * @return int error code returned by ICU + * @internal ICU 2.4 + */ + public static final native int decode( long converterHandle, + byte[] input, int inEnd, + char[] output, int outEnd, + int[] data, + boolean flush); + /** + * Converts an array of Unicode chars containing characters in an + * external encoding into an array of bytes. This method allows + * a buffer by buffer conversion of a data stream. The state of the + * conversion is saved between calls to convert. Among other things, + * this means multibyte input sequences can be split between calls. + * If a call to convert results in an Error, the conversion may be + * continued by calling convert again with suitably modified parameters. + * All conversions should be finished with a call to the flush method. + * + * @param converterHandle Address of converter object created by C code + * @param input char array containing text to be converted. + * @param inEnd stop conversion at this offset in input array (exclusive). + * @param output byte array to receive conversion result. + * @param outEnd stop writing to output array at this offset (exclusive). + * @param data integer array containing the following data + * data[0] = inputOffset + * data[1] = outputOffset + * @return int error code returned by ICU + * @internal ICU 2.4 + */ + public static final native int convertCharToByte(long converterHandle, + char[] input, int inEnd, + byte[] output, int outEnd, + int[] data, + boolean flush); + /** + * Converts an array of Unicode chars containing characters in an + * external encoding into an array of bytes. This method allows + * a buffer by buffer conversion of a data stream. The state of the + * conversion is saved between calls to convert. Among other things, + * this means multibyte input sequences can be split between calls. + * If a call to convert results in an Error, the conversion may be + * continued by calling convert again with suitably modified parameters. + * All conversions should be finished with a call to the flush method. + * + * @param converterHandle Address of converter object created by C code + * @param input char array containing text to be converted. + * @param inEnd stop conversion at this offset in input array (exclusive). + * @param output byte array to receive conversion result. + * @param outEnd stop writing to output array at this offset (exclusive). + * @param data integer array containing the following data + * data[0] = inputOffset + * data[1] = outputOffset + * @return int error code returned by ICU + * @internal ICU 2.4 + */ + public static final native int encode(long converterHandle, + char[] input, int inEnd, + byte[] output, int outEnd, + int[] data, + boolean flush); + /** + * Writes any remaining output to the output buffer and resets the + * converter to its initial state. + * + * @param converterHandle Address of converter object created by C code + * @param output byte array to receive flushed output. + * @param outEnd stop writing to output array at this offset (exclusive). + * @return int error code returned by ICU + * @param data integer array containing the following data + * data[0] = inputOffset + * data[1] = outputOffset + * @internal ICU 2.4 + */ + public static final native int flushCharToByte(long converterHandle, + byte[] output, + int outEnd, + int[] data); + /** + * Writes any remaining output to the output buffer and resets the + * converter to its initial state. + * + * @param converterHandle Address of converter object created by the native code + * @param output char array to receive flushed output. + * @param outEnd stop writing to output array at this offset (exclusive). + * @return int error code returned by ICU + * @param data integer array containing the following data + * data[0] = inputOffset + * data[1] = outputOffset + * @internal ICU 2.4 + */ + public static final native int flushByteToChar(long converterHandle, + char[] output, + int outEnd, + int[] data); + + /** + * Open the converter with the specified encoding + * + * @param converterHandle long array for recieving the adress of converter object + * created by the native code + * @param encoding string representing encoding + * @return int error code returned by ICU + * @internal ICU 2.4 + */ + public static final native long openConverter(String encoding); + /** + * Resets the ByteToChar (toUnicode) state of specified converter + * + * @param converterHandle Address of converter object created by the native code + * @internal ICU 2.4 + */ + public static final native void resetByteToChar(long converterHandle); + + /** + * Resets the CharToByte (fromUnicode) state of specified converter + * + * @param converterHandle Address of converter object created by the native code + * @internal ICU 2.4 + */ + public static final native void resetCharToByte(long converterHandle); + + /** + * Closes the specified converter and releases the resources + * + * @param converterHandle Address of converter object created by the native code + * @internal ICU 2.4 + */ + public static final native void closeConverter(long converterHandle); + + /** + * Sets the substitution Unicode chars of the specified converter used + * by encoder + * @param converterHandle Address of converter object created by the native code + * @param subChars array of chars to used for substitution + * @param length length of the array + * @return int error code returned by ICU + * @internal ICU 2.4 + */ + public static final native int setSubstitutionChars( long converterHandle, + char[] subChars,int length); + /** + * Sets the substitution bytes of the specified converter used by decoder + * + * @param converterHandle Address of converter object created by the native code + * @param subChars array of bytes to used for substitution + * @param length length of the array + * @return int error code returned by ICU + * @internal ICU 2.4 + */ + public static final native int setSubstitutionBytes( long converterHandle, + byte[] subChars,int length); + /** + * Sets the substitution mode of CharToByte(fromUnicode) for the specified converter + * + * @param converterHandle Address of converter object created by the native code + * @param mode to set the true/false + * @return int error code returned by ICU + * @internal ICU 2.4 + */ + public static final native int setSubstitutionModeCharToByte(long converterHandle, + boolean mode); + /** + * Sets the substitution mode of CharToByte(fromUnicode) for the specified converter + * + * @param converterHandle Address of converter object created by the native code + * @param mode to set the true/false + * @return int error code returned by ICU + * @internal ICU 3.6 + */ + public static final native int setSubstitutionModeByteToChar(long converterHandle, + boolean mode); + /** + * Gets the numnber of invalid bytes in the specified converter object + * for the last error that has occured + * + * @param converterHandle Address of converter object created by the native code + * @param length array of int to recieve length of the array + * @return int error code returned by ICU + * @internal ICU 2.4 + */ + public static final native int countInvalidBytes(long converterHandle, int[] length); + + /** + * Gets the numnber of invalid chars in the specified converter object + * for the last error that has occured + * + * @param converterHandle Address of converter object created by the native code + * @param length array of int to recieve length of the array + * @return int error code returned by ICU + * @internal ICU 2.4 + */ + public static final native int countInvalidChars(long converterHandle, int[] length); + + /** + * Gets the number of bytes needed for converting a char + * + * @param converterHandle Address of converter object created by the native code + * @return number of bytes needed + * @internal ICU 2.4 + */ + public static final native int getMaxBytesPerChar(long converterHandle); + + /** + * Gets the number of bytes needed for converting a char + * + * @param converterHandle Address of converter object created by the native code + * @return number of bytes needed + * @internal ICU 3.2 + */ + public static final native int getMinBytesPerChar(long converterHandle); + + /** + * Gets the average numnber of bytes needed for converting a char + * + * @param converterHandle Address of converter object created by the native code + * @return number of bytes needed + * @internal ICU 2.4 + */ + public static final native float getAveBytesPerChar(long converterHandle); + + /** + * Gets the number of chars needed for converting a byte + * + * @param converterHandle Address of converter object created by the native code + * @return number of bytes needed + * @internal ICU 2.4 + */ + public static final native int getMaxCharsPerByte(long converterHandle); + + /** + * Gets the average numnber of chars needed for converting a byte + * + * @param converterHandle Address of converter object created by the native code + * @return number of bytes needed + * @internal ICU 2.4 + */ + public static final native float getAveCharsPerByte(long converterHandle); + + //CSDL: added by Jack + /** + * Determines whether charset1 contains charset2. + */ + public static final native boolean contains(long converterHandle1, long converterHandle2); + + public static final native byte[] getSubstitutionBytes(long converterHandle); + + /** + * Ascertains if a given Unicode code unit can + * be converted to the target encoding + * @param converterHandle Address of converter object created by the native code + * @param codeUnit the character to be converted + * @return true if a character can be converted + * @internal ICU 2.4 + * + */ + public static final native boolean canEncode(long converterHandle,int codeUnit); + + /** + * Ascertains if a given a byte sequence can be converted to Unicode + * @param converterHandle Address of converter object created by the native code + * @param bytes the bytes to be converted + * @return true if a character can be converted + * @internal ICU 2.4 + * + */ + public static final native boolean canDecode(long converterHandle,byte[] bytes); + + /** + * Gets the number of converters installed in the current installation of ICU + * @return int number of converters installed + * @internal ICU 2.4 + */ + public static final native int countAvailable(); + + /** + * Gets the canonical names of available converters + * @return Object[] names as an object array + * @internal ICU 2.4 + */ + public static final native String[] getAvailable(); + + /** + * Gets the number of aliases for a converter name + * @param enc encoding name + * @return number of aliases for the converter + * @internal ICU 2.4 + */ + public static final native int countAliases(String enc); + + /** + * Gets the aliases associated with the converter name + * @param enc converter name + * @return converter names as elements in an object array + * @internal ICU 2.4 + */ + public static final native String[] getAliases(String enc); + + /** + * Gets the canonical name of the converter + * @param enc converter name + * @return canonical name of the converter + * @internal ICU 2.4 + */ + public static final native String getCanonicalName(String enc); + + /** + * Gets the canonical name of the converter as defined by Java + * @param enc converter name + * @return canonical name of the converter + * @internal ICU 3.4 + */ + public static final native String getICUCanonicalName(String enc); + + /** + * Gets the canonical name of the converter as defined by Java + * @param icuCanonicalName converter name + * @return canonical name of the converter + * @internal ICU 3.4 + */ + public static final native String getJavaCanonicalName(String icuCanonicalName); + + /** + * Sets the callback to Unicode for ICU conveter. The default behaviour of ICU callback + * is to call the specified callback function for both illegal and unmapped sequences. + * @param converterHandle Adress of the converter object created by native code + * @param mode call back mode to set. This is either STOP_CALLBACK, SKIP_CALLBACK or SUBSTITUE_CALLBACK + * The converter performs the specified callback when an error occurs + * @param stopOnIllegal If true sets the alerts the converter callback to stop on an illegal sequence + * @return int error code returned by ICU + * @internal ICU 2.4 + */ + public static final native int setCallbackDecode(long converterHandle, int onMalformedInput, int onUnmappableInput, char[] subChars, int length); + + /** + * Sets the callback from Unicode for ICU conveter. The default behaviour of ICU callback + * is to call the specified callback function for both illegal and unmapped sequences. + * @param converterHandle Adress of the converter object created by native code + * @param mode call back mode to set. This is either STOP_CALLBACK, SKIP_CALLBACK or SUBSTITUE_CALLBACK + * The converter performs the specified callback when an error occurs + * @param stopOnIllegal If true sets the alerts the converter callback to stop on an illegal sequence + * @return int error code returned by ICU + * @internal ICU 2.4 + */ + public static final native int setCallbackEncode(long converterHandle, int onMalformedInput, int onUnmappableInput, byte[] subBytes, int length); + + /** + * Returns a thread safe clone of the converter + * @internal ICU 2.4 + */ + public static final native long safeClone(long converterHandle); + + /** @internal ICU 2.4 */ + public static final int STOP_CALLBACK = 0;//CodingErrorAction.REPORT + /** @internal ICU 2.4 */ + public static final int SKIP_CALLBACK = 1;//CodingErrorAction.IGNORE + /** @internal ICU 2.4 */ + public static final int SUBSTITUTE_CALLBACK = 2;//CodingErrorAction.REPLACE +} diff --git a/icu/src/main/java/com/ibm/icu4jni/common/ErrorCode.java b/icu/src/main/java/com/ibm/icu4jni/common/ErrorCode.java new file mode 100644 index 0000000..023f165 --- /dev/null +++ b/icu/src/main/java/com/ibm/icu4jni/common/ErrorCode.java @@ -0,0 +1,205 @@ +/** +****************************************************************************** +* Copyright (C) 1996-2005, International Business Machines Corporation and * +* others. All Rights Reserved. * +****************************************************************************** +* +****************************************************************************** +*/ + +package com.ibm.icu4jni.common; + +/** +* Error exception class mapping ICU error codes of the enum UErrorCode +* @author syn wee quek +* @internal +*/ +public final class ErrorCode extends Exception +{ + + // public methods -------------------------------------------------------- + + /** + * Generic mapping from the error codes to java default exceptions. + * @param error error code + * @return java default exception that maps to the argument error code, + * otherwise if error is not a valid error code, null is returned. + * @stable ICU 2.4 + */ + public static final RuntimeException getException(int error) + { + if (error <= U_ZERO_ERROR && error >= U_ERROR_LIMIT) { + return null; + } + String errorname = ERROR_NAMES_[U_ILLEGAL_ARGUMENT_ERROR]; + switch (error) { + case U_ILLEGAL_ARGUMENT_ERROR : + return new IllegalArgumentException(errorname); + case U_INDEX_OUTOFBOUNDS_ERROR : + return new ArrayIndexOutOfBoundsException(errorname); + case U_BUFFER_OVERFLOW_ERROR : + return new ArrayIndexOutOfBoundsException(errorname); + case U_UNSUPPORTED_ERROR : + return new UnsupportedOperationException(errorname); + default : + return new RuntimeException(errorname); + } + } + + // public static data member --------------------------------------------- + + /** + * Start of information results (semantically successful) + */ + public static final int U_ERROR_INFO_START = -128; + /** + * A resource bundle lookup returned a fallback result (not an error) + */ + public static final int U_USING_FALLBACK_ERROR = -128; + /** + * A resource bundle lookup returned a result from the root locale (not an + * error) + */ + public static final int U_USING_DEFAULT_ERROR = -127; + /** + * A SafeClone operation required allocating memory (informational + * only + */ + public static final int U_SAFECLONE_ALLOCATED_ERROR = -126; + /** + * This must always be the last warning value to indicate the limit for + * UErrorCode warnings (last warning code +1) + */ + public static final int U_ERROR_INFO_LIMIT = -125; + + /** + * No error, no warning + */ + public static final int U_ZERO_ERROR = 0; + /** + * Start of codes indicating failure + */ + public static final int U_ILLEGAL_ARGUMENT_ERROR = 1; + public static final int U_MISSING_RESOURCE_ERROR = 2; + public static final int U_INVALID_FORMAT_ERROR = 3; + public static final int U_FILE_ACCESS_ERROR = 4; + /** + * Indicates a bug in the library code + */ + public static final int U_INTERNAL_PROGRAM_ERROR = 5; + public static final int U_MESSAGE_PARSE_ERROR = 6; + /** + * Memory allocation error + */ + public static final int U_MEMORY_ALLOCATION_ERROR = 7; + public static final int U_INDEX_OUTOFBOUNDS_ERROR = 8; + /** + * Equivalent to Java ParseException + */ + public static final int U_PARSE_ERROR = 9; + /** + * In the Character conversion routines: Invalid character or sequence was + * encountered + */ + public static final int U_INVALID_CHAR_FOUND = 10; + /** + * In the Character conversion routines: More bytes are required to complete + * the conversion successfully + */ + public static final int U_TRUNCATED_CHAR_FOUND = 11; + /** + * In codeset conversion: a sequence that does NOT belong in the codepage has + * been encountered + */ + public static final int U_ILLEGAL_CHAR_FOUND = 12; + /** + * Conversion table file found, but corrupted + */ + public static final int U_INVALID_TABLE_FORMAT = 13; + /** + * Conversion table file not found + */ + public static final int U_INVALID_TABLE_FILE = 14; + /** + * A result would not fit in the supplied buffer + */ + public static final int U_BUFFER_OVERFLOW_ERROR = 15; + /** + * Requested operation not supported in current context + */ + public static final int U_UNSUPPORTED_ERROR = 16; + /** + * an operation is requested over a resource that does not support it + */ + public static final int U_RESOURCE_TYPE_MISMATCH = 17; + /** + * ISO-2022 illlegal escape sequence + */ + public static final int U_ILLEGAL_ESCAPE_SEQUENCE = 18; + /** + * ISO-2022 unsupported escape sequence + */ + public static final int U_UNSUPPORTED_ESCAPE_SEQUENCE = 19; + /** + * No space available for in-buffer expansion for Arabic shaping + */ + public static final int U_NO_SPACE_AVAILABLE = 20; + /** + * This must always be the last value to indicate the limit for UErrorCode + * (last error code +1) + */ + public static final int U_ERROR_LIMIT = 21; + /** + * Load library flag + */ + public static boolean LIBRARY_LOADED = false; + + // private data member ---------------------------------------------------- + + /** + * Array of error code names corresponding to the errorcodes. + * ie ERROR_NAMES_[0] = name of U_ZERO_ERROR + */ + private static final String ERROR_NAMES_[] = { + "U_ZERO_ERROR", "U_ILLEGAL_ARGUMENT_ERROR", + "U_MISSING_RESOURCE_ERROR", "U_INVALID_FORMAT_ERROR", + "U_FILE_ACCESS_ERROR", "U_INTERNAL_PROGRAM_ERROR", + "U_MESSAGE_PARSE_ERROR", "U_MEMORY_ALLOCATION_ERROR", + "U_INDEX_OUTOFBOUNDS_ERROR", "U_PARSE_ERROR", + "U_INVALID_CHAR_FOUND", "U_TRUNCATED_CHAR_FOUND", + "U_ILLEGAL_CHAR_FOUND", "U_INVALID_TABLE_FORMAT", + "U_INVALID_TABLE_FILE", "U_BUFFER_OVERFLOW_ERROR", + "U_UNSUPPORTED_ERROR", "U_RESOURCE_TYPE_MISMATCH", + "U_ILLEGAL_ESCAPE_SEQUENCE", "U_UNSUPPORTED_ESCAPE_SEQUENCE" + }; + /** + * Returns the error name of the input error code + * @param ec int value of the error code + * @return String name of the error code + * @stable ICU 2.4 + */ + public static String getErrorName(int ec){ + return ERROR_NAMES_[ec]; + } + + /** + * Returns true if the input error code denotes success + * @param ec int value of the error code + * @return boolean + * @stable ICU 2.4 + */ + public static boolean isSuccess(int ec){ + return (ec<=U_ZERO_ERROR); + } + + /** + * Returns true if the input error code denotes failure + * @param ec int value of the error code + * @return boolean + * @stable ICU 2.4 + */ + public static boolean isFailure(int ec){ + return (ec>U_ZERO_ERROR); + } +} + diff --git a/icu/src/main/java/com/ibm/icu4jni/lang/UCharacter.java b/icu/src/main/java/com/ibm/icu4jni/lang/UCharacter.java new file mode 100644 index 0000000..7ab1843 --- /dev/null +++ b/icu/src/main/java/com/ibm/icu4jni/lang/UCharacter.java @@ -0,0 +1,295 @@ +/* + * Copyright (C) 2008 The Android Open Source Project + * + * Licensed 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 com.ibm.icu4jni.lang; + +import java.lang.Character.UnicodeBlock; + +public class UCharacter { + + public static int digit(int codePoint, int radix) { + return digitImpl(codePoint, radix); + } + + private static native int digitImpl(int codePoint, int radix); + + public static int getType(int codePoint) { + return getTypeImpl(codePoint); + } + + private static native int getTypeImpl(int codePoint); + + public static byte getDirectionality(int codePoint) { + return getDirectionalityImpl(codePoint); + } + + private static native byte getDirectionalityImpl(int codePoint); + + public static boolean isMirrored(int codePoint) { + return isMirroredImpl(codePoint); + } + + private static native boolean isMirroredImpl(int codePoint); + + public static int getNumericValue(int codePoint) { + return getNumericValueImpl(codePoint); + } + + private static native int getNumericValueImpl(int codePoint); + + public static boolean isDefined(int codePoint) { + return isDefinedValueImpl(codePoint); + } + + private static native boolean isDefinedValueImpl(int codePoint); + + public static boolean isDigit(int codePoint) { + return isDigitImpl(codePoint); + } + + private static native boolean isDigitImpl(int codePoint); + + public static boolean isIdentifierIgnorable(int codePoint) { + return isIdentifierIgnorableImpl(codePoint); + } + + private static native boolean isIdentifierIgnorableImpl(int codePoint); + + public static boolean isLetter(int codePoint) { + return isLetterImpl(codePoint); + } + + private static native boolean isLetterImpl(int codePoint); + + public static boolean isLetterOrDigit(int codePoint) { + return isLetterOrDigitImpl(codePoint); + } + + private static native boolean isLetterOrDigitImpl(int codePoint); + + public static boolean isSpaceChar(int codePoint) { + return isSpaceCharImpl(codePoint); + } + + private static native boolean isSpaceCharImpl(int codePoint); + + public static boolean isTitleCase(int codePoint) { + return isTitleCaseImpl(codePoint); + } + + private static native boolean isTitleCaseImpl(int codePoint); + + public static boolean isUnicodeIdentifierPart(int codePoint) { + return isUnicodeIdentifierPartImpl(codePoint); + } + + private static native boolean isUnicodeIdentifierPartImpl(int codePoint); + + public static boolean isUnicodeIdentifierStart(int codePoint) { + return isUnicodeIdentifierStartImpl(codePoint); + } + + private static native boolean isUnicodeIdentifierStartImpl(int codePoint); + + public static boolean isWhitespace(int codePoint) { + return isWhitespaceImpl(codePoint); + } + + private static native boolean isWhitespaceImpl(int codePoint); + + public static int toLowerCase(int codePoint) { + return toLowerCaseImpl(codePoint); + } + + private static native int toLowerCaseImpl(int codePoint); + + public static int toTitleCase(int codePoint) { + return toTitleCaseImpl(codePoint); + } + + private static native int toTitleCaseImpl(int codePoint); + + public static int toUpperCase(int codePoint) { + return toUpperCaseImpl(codePoint); + } + + private static native int toUpperCaseImpl(int codePoint); + + public static boolean isUpperCase(int codePoint) { + return isUpperCaseImpl(codePoint); + } + + private static native boolean isUpperCaseImpl(int codePoint); + + public static boolean isLowerCase(int codePoint) { + return isLowerCaseImpl(codePoint); + } + + private static native boolean isLowerCaseImpl(int codePoint); + + public static int forName(String blockName) { + if (blockName == null) { + throw new NullPointerException(); + } + return forname(blockName); + } + + private static native int forname(String blockName); + + public static int of(int codePoint) { + return codeblock(codePoint); + } + + private static native int codeblock(int codePoint); + + public static UnicodeBlock[] getBlockTable() { + /** + * The indices of the entries of this table correspond with the value + * of the ICU enum UBlockCode. When updating ICU it's necessary + * to check if there where any changes for the properties + * used by java.lang.Character. + * The enum is defined in common/unicode/uchar.h + */ + UnicodeBlock[] result = new UnicodeBlock[] { null, + UnicodeBlock.BASIC_LATIN, + UnicodeBlock.LATIN_1_SUPPLEMENT, + UnicodeBlock.LATIN_EXTENDED_A, + UnicodeBlock.LATIN_EXTENDED_B, + UnicodeBlock.IPA_EXTENSIONS, + UnicodeBlock.SPACING_MODIFIER_LETTERS, + UnicodeBlock.COMBINING_DIACRITICAL_MARKS, + UnicodeBlock.GREEK, + UnicodeBlock.CYRILLIC, + UnicodeBlock.ARMENIAN, + UnicodeBlock.HEBREW, + UnicodeBlock.ARABIC, + UnicodeBlock.SYRIAC, + UnicodeBlock.THAANA, + UnicodeBlock.DEVANAGARI, + UnicodeBlock.BENGALI, + UnicodeBlock.GURMUKHI, + UnicodeBlock.GUJARATI, + UnicodeBlock.ORIYA, + UnicodeBlock.TAMIL, + UnicodeBlock.TELUGU, + UnicodeBlock.KANNADA, + UnicodeBlock.MALAYALAM, + UnicodeBlock.SINHALA, + UnicodeBlock.THAI, + UnicodeBlock.LAO, + UnicodeBlock.TIBETAN, + UnicodeBlock.MYANMAR, + UnicodeBlock.GEORGIAN, + UnicodeBlock.HANGUL_JAMO, + UnicodeBlock.ETHIOPIC, + UnicodeBlock.CHEROKEE, + UnicodeBlock.UNIFIED_CANADIAN_ABORIGINAL_SYLLABICS, + UnicodeBlock.OGHAM, + UnicodeBlock.RUNIC, + UnicodeBlock.KHMER, + UnicodeBlock.MONGOLIAN, + UnicodeBlock.LATIN_EXTENDED_ADDITIONAL, + UnicodeBlock.GREEK_EXTENDED, + UnicodeBlock.GENERAL_PUNCTUATION, + UnicodeBlock.SUPERSCRIPTS_AND_SUBSCRIPTS, + UnicodeBlock.CURRENCY_SYMBOLS, + UnicodeBlock.COMBINING_MARKS_FOR_SYMBOLS, + UnicodeBlock.LETTERLIKE_SYMBOLS, + UnicodeBlock.NUMBER_FORMS, + UnicodeBlock.ARROWS, + UnicodeBlock.MATHEMATICAL_OPERATORS, + UnicodeBlock.MISCELLANEOUS_TECHNICAL, + UnicodeBlock.CONTROL_PICTURES, + UnicodeBlock.OPTICAL_CHARACTER_RECOGNITION, + UnicodeBlock.ENCLOSED_ALPHANUMERICS, + UnicodeBlock.BOX_DRAWING, + UnicodeBlock.BLOCK_ELEMENTS, + UnicodeBlock.GEOMETRIC_SHAPES, + UnicodeBlock.MISCELLANEOUS_SYMBOLS, + UnicodeBlock.DINGBATS, + UnicodeBlock.BRAILLE_PATTERNS, + UnicodeBlock.CJK_RADICALS_SUPPLEMENT, + UnicodeBlock.KANGXI_RADICALS, + UnicodeBlock.IDEOGRAPHIC_DESCRIPTION_CHARACTERS, + UnicodeBlock.CJK_SYMBOLS_AND_PUNCTUATION, + UnicodeBlock.HIRAGANA, + UnicodeBlock.KATAKANA, + UnicodeBlock.BOPOMOFO, + UnicodeBlock.HANGUL_COMPATIBILITY_JAMO, + UnicodeBlock.KANBUN, + UnicodeBlock.BOPOMOFO_EXTENDED, + UnicodeBlock.ENCLOSED_CJK_LETTERS_AND_MONTHS, + UnicodeBlock.CJK_COMPATIBILITY, + UnicodeBlock.CJK_UNIFIED_IDEOGRAPHS_EXTENSION_A, + UnicodeBlock.CJK_UNIFIED_IDEOGRAPHS, + UnicodeBlock.YI_SYLLABLES, + UnicodeBlock.YI_RADICALS, + UnicodeBlock.HANGUL_SYLLABLES, + UnicodeBlock.HIGH_SURROGATES, + UnicodeBlock.HIGH_PRIVATE_USE_SURROGATES, + UnicodeBlock.LOW_SURROGATES, + UnicodeBlock.PRIVATE_USE_AREA, + UnicodeBlock.CJK_COMPATIBILITY_IDEOGRAPHS, + UnicodeBlock.ALPHABETIC_PRESENTATION_FORMS, + UnicodeBlock.ARABIC_PRESENTATION_FORMS_A, + UnicodeBlock.COMBINING_HALF_MARKS, + UnicodeBlock.CJK_COMPATIBILITY_FORMS, + UnicodeBlock.SMALL_FORM_VARIANTS, + UnicodeBlock.ARABIC_PRESENTATION_FORMS_B, + UnicodeBlock.SPECIALS, + UnicodeBlock.HALFWIDTH_AND_FULLWIDTH_FORMS, + UnicodeBlock.OLD_ITALIC, + UnicodeBlock.GOTHIC, + UnicodeBlock.DESERET, + UnicodeBlock.BYZANTINE_MUSICAL_SYMBOLS, + UnicodeBlock.MUSICAL_SYMBOLS, + UnicodeBlock.MATHEMATICAL_ALPHANUMERIC_SYMBOLS, + UnicodeBlock.CJK_UNIFIED_IDEOGRAPHS_EXTENSION_B, + UnicodeBlock.CJK_COMPATIBILITY_IDEOGRAPHS_SUPPLEMENT, + UnicodeBlock.TAGS, + UnicodeBlock.CYRILLIC_SUPPLEMENTARY, + UnicodeBlock.TAGALOG, + UnicodeBlock.HANUNOO, + UnicodeBlock.BUHID, + UnicodeBlock.TAGBANWA, + UnicodeBlock.MISCELLANEOUS_MATHEMATICAL_SYMBOLS_A, + UnicodeBlock.SUPPLEMENTAL_ARROWS_A, + UnicodeBlock.SUPPLEMENTAL_ARROWS_B, + UnicodeBlock.MISCELLANEOUS_MATHEMATICAL_SYMBOLS_B, + UnicodeBlock.SUPPLEMENTAL_MATHEMATICAL_OPERATORS, + UnicodeBlock.KATAKANA_PHONETIC_EXTENSIONS, + UnicodeBlock.VARIATION_SELECTORS, + UnicodeBlock.SUPPLEMENTARY_PRIVATE_USE_AREA_A, + UnicodeBlock.SUPPLEMENTARY_PRIVATE_USE_AREA_B, + UnicodeBlock.LIMBU, + UnicodeBlock.TAI_LE, + UnicodeBlock.KHMER_SYMBOLS, + UnicodeBlock.PHONETIC_EXTENSIONS, + UnicodeBlock.MISCELLANEOUS_SYMBOLS_AND_ARROWS, + UnicodeBlock.YIJING_HEXAGRAM_SYMBOLS, + UnicodeBlock.LINEAR_B_SYLLABARY, + UnicodeBlock.LINEAR_B_IDEOGRAMS, + UnicodeBlock.AEGEAN_NUMBERS, + UnicodeBlock.UGARITIC, + UnicodeBlock.SHAVIAN, + UnicodeBlock.OSMANYA, + UnicodeBlock.CYPRIOT_SYLLABARY, + UnicodeBlock.TAI_XUAN_JING_SYMBOLS, + UnicodeBlock.VARIATION_SELECTORS_SUPPLEMENT + }; + return result; + } +} diff --git a/icu/src/main/java/com/ibm/icu4jni/math/BigDecimal.java b/icu/src/main/java/com/ibm/icu4jni/math/BigDecimal.java new file mode 100644 index 0000000..4460b19 --- /dev/null +++ b/icu/src/main/java/com/ibm/icu4jni/math/BigDecimal.java @@ -0,0 +1,34 @@ +package com.ibm.icu4jni.math; + +public class BigDecimal extends Number { + + @Override + public double doubleValue() { + // TODO Auto-generated method stub + return 0; + } + + @Override + public float floatValue() { + // TODO Auto-generated method stub + return 0; + } + + @Override + public int intValue() { + // TODO Auto-generated method stub + return 0; + } + + @Override + public long longValue() { + // TODO Auto-generated method stub + return 0; + } + + @Override + public String toString() { + return ""; + } + +} diff --git a/icu/src/main/java/com/ibm/icu4jni/regex/NativeRegEx.java b/icu/src/main/java/com/ibm/icu4jni/regex/NativeRegEx.java new file mode 100644 index 0000000..bdfff5b --- /dev/null +++ b/icu/src/main/java/com/ibm/icu4jni/regex/NativeRegEx.java @@ -0,0 +1,130 @@ +/* + * Copyright (C) 2008 The Android Open Source Project + * + * Licensed 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 com.ibm.icu4jni.regex; + +public class NativeRegEx { + + /** + * Opens (compiles) an ICU regular expression. + */ + public static native int open(String pattern, int flags); + + /** + * Makes a copy of a compiled regular expression. + */ + public static native int clone(int regex); + + /** + * Closes the regular expression, recovering all resources (memory) it was + * holding. + */ + public static native void close(int regex); + + /** + * Sets the subject text string upon which the regular expression will look + * for matches. + */ + public static native void setText(int regex, String text); + + /** + * Attempts to match the input string, beginning at startIndex, against the + * pattern. + */ + public static native boolean matches(int regex, int startIndex); + + /** + * Attempts to match the input string, starting from the specified index, + * against the pattern. + */ + public static native boolean lookingAt(int regex, int startIndex); + + /** + * Finds the first matching substring of the input string that matches the + * pattern. + */ + public static native boolean find(int regex, int startIndex); + + /** + * Finds the first matching substring of the input string that matches the + * pattern. + */ + public static native boolean findNext(int regex); + + /** + * Gets the number of capturing groups in this regular expression's pattern. + */ + public static native int groupCount(int regex); + + /** + * Gets all the group information for the current match of the pattern. + */ + public static native void startEnd(int regex, int[] startEnd); + + /** + * Sets the region of the input to be considered during matching. + */ + public static native void setRegion(int regex, int start, int end); + + /** + * Queries the start of the region of the input to be considered during + * matching. + */ + public static native int regionStart(int regex); + + /** + * Queries the end of the region of the input to be considered during + * matching. + */ + public static native int regionEnd(int regex); + + /** + * Controls the transparency of the region bounds. + */ + public static native void useTransparentBounds(int regex, boolean value); + + /** + * Queries the transparency of the region bounds. + */ + public static native boolean hasTransparentBounds(int regex); + + /** + * Controls the anchoring property of the region bounds. + */ + public static native void useAnchoringBounds(int regex, boolean value); + + /** + * Queries the anchoring property of the region bounds. + */ + public static native boolean hasAnchoringBounds(int regex); + + /** + * Queries whether we hit the end of the input during the last match. + */ + public static native boolean hitEnd(int regex); + + /** + * Queries whether more input might change a current match, but wouldn't + * destroy it. + */ + public static native boolean requireEnd(int regex); + + /** + * Resets the matcher, cause a current match to be lost, and sets the + * position at which a subsequent findNext() would start. + */ + public static native void reset(int regex, int position); +} diff --git a/icu/src/main/java/com/ibm/icu4jni/text/BreakIterator.java b/icu/src/main/java/com/ibm/icu4jni/text/BreakIterator.java new file mode 100644 index 0000000..c8f5372 --- /dev/null +++ b/icu/src/main/java/com/ibm/icu4jni/text/BreakIterator.java @@ -0,0 +1,133 @@ +/* + * Copyright (C) 2008 The Android Open Source Project + * + * Licensed 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 com.ibm.icu4jni.text; + +import java.text.CharacterIterator; +import java.text.StringCharacterIterator; +import java.util.Locale; + +public abstract class BreakIterator implements Cloneable +{ + protected static int BI_CHAR_INSTANCE = 1; + protected static int BI_WORD_INSTANCE = 2; + protected static int BI_LINE_INSTANCE = 3; + protected static int BI_SENT_INSTANCE = 4; + + protected int type = 0; + + public static Locale[] getAvailableLocales() { + + String[] locales = NativeBreakIterator.getAvailableLocalesImpl(); + + Locale[] result = new Locale[locales.length]; + + String locale; + + int index, index2; + + for(int i = 0; i < locales.length; i++) { + locale = locales[i]; + + index = locale.indexOf('_'); + index2 = locale.lastIndexOf('_'); + + if(index == -1) { + result[i] = new Locale(locales[i]); + } else if(index > 0 && index == index2) { + result[i] = new Locale( + locale.substring(0,index), + locale.substring(index+1)); + } else if(index > 0 && index2 > index) { + result[i] = new Locale( + locale.substring(0,index), + locale.substring(index+1,index2), + locale.substring(index2+1)); + } + } + + return result; + } + + public static BreakIterator getCharacterInstance() { + int iter = NativeBreakIterator.getCharacterInstanceImpl(""); + return new RuleBasedBreakIterator(iter, BI_CHAR_INSTANCE); + } + + public static BreakIterator getCharacterInstance(Locale where) { + int iter = NativeBreakIterator.getCharacterInstanceImpl(where.toString()); + return new RuleBasedBreakIterator(iter, BI_CHAR_INSTANCE); + } + + public static BreakIterator getLineInstance() { + int iter = NativeBreakIterator.getLineInstanceImpl(""); + return new RuleBasedBreakIterator(iter, BI_LINE_INSTANCE); + } + + public static BreakIterator getLineInstance(Locale where) { + int iter = NativeBreakIterator.getLineInstanceImpl(where.toString()); + return new RuleBasedBreakIterator(iter, BI_LINE_INSTANCE); + } + + public static BreakIterator getSentenceInstance() { + int iter = NativeBreakIterator.getSentenceInstanceImpl(""); + return new RuleBasedBreakIterator(iter, BI_SENT_INSTANCE); + } + + public static BreakIterator getSentenceInstance(Locale where) { + int iter = NativeBreakIterator.getSentenceInstanceImpl(where.toString()); + return new RuleBasedBreakIterator(iter, BI_SENT_INSTANCE); + } + + public static BreakIterator getWordInstance() { + int iter = NativeBreakIterator.getWordInstanceImpl(""); + return new RuleBasedBreakIterator(iter, BI_WORD_INSTANCE); + } + + public static BreakIterator getWordInstance(Locale where) { + int iter = NativeBreakIterator.getWordInstanceImpl(where.toString()); + return new RuleBasedBreakIterator(iter, BI_WORD_INSTANCE); + } + + public void setText(String newText) { + setText(new StringCharacterIterator(newText)); + } + + public abstract boolean isBoundary(int offset); + + public abstract int preceding(int offset); + + public abstract Object clone(); + + public abstract int current(); + + public abstract int first(); + + public abstract int following(int offset); + + public abstract CharacterIterator getText(); + + public abstract int last(); + + public abstract int next(int n); + + public abstract int next(); + + public abstract int previous(); + + public abstract void setText(CharacterIterator newText); + +} diff --git a/icu/src/main/java/com/ibm/icu4jni/text/CollationAttribute.java b/icu/src/main/java/com/ibm/icu4jni/text/CollationAttribute.java new file mode 100644 index 0000000..b1c6107 --- /dev/null +++ b/icu/src/main/java/com/ibm/icu4jni/text/CollationAttribute.java @@ -0,0 +1,214 @@ +/** +******************************************************************************* +* Copyright (C) 1996-2005, International Business Machines Corporation and * +* others. All Rights Reserved. * +******************************************************************************* +* +******************************************************************************* +*/ + +package com.ibm.icu4jni.text; + +/** +* Interface for storing ICU collation equivalent enum values. +* Constants with the prefix VALUE corresponds to ICU's UColAttributeValues, +* the rest corresponds to UColAttribute. +* @author syn wee quek +* @stable ICU 2.4 +*/ + +public final class CollationAttribute +{ + // Collation strength constants ---------------------------------- + /** + * Default value, accepted by most attributes + * @stable ICU 2.4 + */ + public static final int VALUE_DEFAULT = -1; + /** + * Primary collation strength + * @stable ICU 2.4 + */ + public static final int VALUE_PRIMARY = 0; + /** + * Secondary collation strength + * @stable ICU 2.4 + */ + public static final int VALUE_SECONDARY = 1; + /** + * Tertiary collation strength + * @stable ICU 2.4 + */ + public static final int VALUE_TERTIARY = 2; + /** + * Default collation strength + * @stable ICU 2.4 + */ + public static final int VALUE_DEFAULT_STRENGTH = VALUE_TERTIARY; + /** + * Quaternary collation strength + * @stable ICU 2.4 + */ + public static final int VALUE_QUATERNARY = 3; + /** + * Identical collation strength + * @stable ICU 2.4 + */ + public static final int VALUE_IDENTICAL = 15; + + /** + * Turn the feature off - works for FRENCH_COLLATION, CASE_LEVEL, + * HIRAGANA_QUATERNARY_MODE and DECOMPOSITION_MODE + * @stable ICU 2.4 + */ + public static final int VALUE_OFF = 16; + /** @stable ICU 2.4 */ + public static final int VALUE_ON = 17; + + /** + * ALTERNATE_HANDLING mode constants + * @stable ICU 2.4 + */ + public static final int VALUE_SHIFTED = 20; + /** @stable ICU 2.4 */ + public static final int VALUE_NON_IGNORABLE = 21; + + /** + * CASE_FIRST mode constants + * @stable ICU 2.4 + */ + public static final int VALUE_LOWER_FIRST = 24; + /** @stable ICU 2.4 */ + public static final int VALUE_UPPER_FIRST = 25; + + /** + * NORMALIZATION_MODE mode constants + * @deprecated ICU 2.4. Users advised to use VALUE_ON instead. + */ + public static final int VALUE_ON_WITHOUT_HANGUL = 28; + + /** + * Number of attribute value constants + * @stable ICU 2.4 + */ + public static final int VALUE_ATTRIBUTE_VALUE_COUNT = 29; + + // Collation attribute constants ----------------------------------- + + /** + * Attribute for direction of secondary weights + * @stable ICU 2.4 + */ + public static final int FRENCH_COLLATION = 0; + /** + * Attribute for handling variable elements + * @stable ICU 2.4 + */ + public static final int ALTERNATE_HANDLING = 1; + /** + * Who goes first, lower case or uppercase. + * @stable ICU 2.4 + */ + public static final int CASE_FIRST = 2; + /** + * Do we have an extra case level + * @stable ICU 2.4 + */ + public static final int CASE_LEVEL = 3; + /** + * Attribute for normalization + * @stable ICU 2.4 + */ + public static final int NORMALIZATION_MODE = 4; + /** + * Attribute for strength + * @stable ICU 2.4 + */ + public static final int STRENGTH = 5; + /** + * Attribute count + * @stable ICU 2.4 + */ + public static final int ATTRIBUTE_COUNT = 6; + + // package methods -------------------------------------------------- + + /** + * Checks if argument is a valid collation strength + * @param strength potential collation strength + * @return true if strength is a valid collation strength, false otherwise + */ + static boolean checkStrength(int strength) + { + if (strength < VALUE_PRIMARY || + (strength > VALUE_QUATERNARY && strength != VALUE_IDENTICAL)) + return false; + return true; + } + + /** + * Checks if argument is a valid collation type + * @param type collation type to be checked + * @return true if type is a valid collation type, false otherwise + */ + static boolean checkType(int type) + { + if (type < FRENCH_COLLATION || type > STRENGTH) + return false; + return true; + } + + /** + * Checks if argument is a valid normalization type + * @param type normalization type to be checked + * @return true if type is a valid normalization type, false otherwise + */ + static boolean checkNormalization(int type) + { + if (type != VALUE_ON && type != VALUE_OFF + && type != VALUE_ON_WITHOUT_HANGUL) { + return false; + } + return true; + } + + /** + * Checks if attribute type and corresponding attribute value is valid + * @param type attribute type + * @param value attribute value + * @return true if the pair is valid, false otherwise + */ + static boolean checkAttribute(int type, int value) + { + if (value == VALUE_DEFAULT) { + return true; + } + + switch (type) + { + case FRENCH_COLLATION : + if (value >= VALUE_OFF && value <= VALUE_ON) + return true; + break; + case ALTERNATE_HANDLING : + if (value >= VALUE_SHIFTED && + value <= VALUE_NON_IGNORABLE) + return true; + break; + case CASE_FIRST : + if (value >= VALUE_LOWER_FIRST && + value <= VALUE_UPPER_FIRST) + return true; + break; + case CASE_LEVEL : + return (value == VALUE_ON || + value <= VALUE_OFF); + case NORMALIZATION_MODE : + return (value == VALUE_OFF || value == VALUE_ON || + value == VALUE_ON_WITHOUT_HANGUL); + case STRENGTH : + checkStrength(value); + } + return false; + } +} diff --git a/icu/src/main/java/com/ibm/icu4jni/text/CollationElementIterator.java b/icu/src/main/java/com/ibm/icu4jni/text/CollationElementIterator.java new file mode 100644 index 0000000..0f5bae4 --- /dev/null +++ b/icu/src/main/java/com/ibm/icu4jni/text/CollationElementIterator.java @@ -0,0 +1,230 @@ +/** +******************************************************************************* +* Copyright (C) 1996-2005, International Business Machines Corporation and * +* others. All Rights Reserved. * +******************************************************************************* +* +******************************************************************************* +*/ + +package com.ibm.icu4jni.text; + +import java.text.CharacterIterator; + + +/** +* Collation element iterator JNI wrapper. +* Iterates over the collation elements of a data string. +* The iterator supports both forward and backwards full iteration, ie if +* backwards iteration is performed in the midst of a forward iteration, the +* result is undefined. +* To perform a backwards iteration in the midst of a forward iteration, +* reset() has to be called. +* This will shift the position to either the start or the last character in the +* data string depending on whether next() is called or previous(). +* <pre> +* RuleBasedCollator coll = Collator.getInstance(); +* CollationElementIterator iterator = coll.getCollationElementIterator("abc"); +* int ce = 0; +* while (ce != CollationElementIterator.NULLORDER) { +* ce = iterator.next(); +* } +* iterator.reset(); +* while (ce != CollationElementIterator.NULLORDER) { +* ce = iterator.previous(); +* } +* </pre> +* @author syn wee quek +* @stable ICU 2.4 +*/ + +public final class CollationElementIterator +{ + // public data member ------------------------------------------- + + /** + * @stable ICU 2.4 + */ + public static final int NULLORDER = 0xFFFFFFFF; + + // public methods ----------------------------------------------- + + /** + * Reset the collation elements to their initial state. + * This will move the 'cursor' to the beginning of the text. + * @stable ICU 2.4 + */ + public void reset() + { + NativeCollation.reset(m_collelemiterator_); + } + + /** + * Get the ordering priority of the next collation element in the text. + * A single character may contain more than one collation element. + * @return next collation elements ordering, or NULLORDER if the end of the + * text is reached. + * @stable ICU 2.4 + */ + public int next() + { + return NativeCollation.next(m_collelemiterator_); + } + + /** + * Get the ordering priority of the previous collation element in the text. + * A single character may contain more than one collation element. + * @return previous collation element ordering, or NULLORDER if the end of + * the text is reached. + * @stable ICU 2.4 + */ + public int previous() + { + return NativeCollation.previous(m_collelemiterator_); + } + + /** + * Get the maximum length of any expansion sequences that end with the + * specified comparison order. + * @param order collation order returned by previous or next. + * @return maximum size of the expansion sequences ending with the collation + * element or 1 if collation element does not occur at the end of + * any expansion sequence + * @stable ICU 2.4 + */ + public int getMaxExpansion(int order) + { + return NativeCollation.getMaxExpansion(m_collelemiterator_, order); + } + + /** + * Set the text containing the collation elements. + * @param source text containing the collation elements. + * @stable ICU 2.4 + */ + public void setText(String source) + { + NativeCollation.setText(m_collelemiterator_, source); + } + + // BEGIN android-added + public void setText(CharacterIterator source) + { + NativeCollation.setText(m_collelemiterator_, source.toString()); + } + // END android-added + + /** + * Get the offset of the current source character. + * This is an offset into the text of the character containing the current + * collation elements. + * @return offset of the current source character. + * @stable ICU 2.4 + */ + public int getOffset() + { + return NativeCollation.getOffset(m_collelemiterator_); + } + + /** + * Set the offset of the current source character. + * This is an offset into the text of the character to be processed. + * @param offset The desired character offset. + * @stable ICU 2.4 + */ + public void setOffset(int offset) + { + NativeCollation.setOffset(m_collelemiterator_, offset); + } + + /** + * Gets the primary order of a collation order. + * @param order the collation order + * @return the primary order of a collation order. + * @stable ICU 2.4 + */ + public static int primaryOrder(int order) + { + return ((order & PRIMARY_ORDER_MASK_) >> PRIMARY_ORDER_SHIFT_) & + UNSIGNED_16_BIT_MASK_; + } + + /** + * Gets the secondary order of a collation order. + * @param order the collation order + * @return the secondary order of a collation order. + * @stable ICU 2.4 + */ + public static int secondaryOrder(int order) + { + return (order & SECONDARY_ORDER_MASK_) >> SECONDARY_ORDER_SHIFT_; + } + + /** + * Gets the tertiary order of a collation order. + * @param order the collation order + * @return the tertiary order of a collation order. + * @stable ICU 2.4 + */ + public static int tertiaryOrder(int order) + { + return order & TERTIARY_ORDER_MASK_; + } + + // protected constructor ---------------------------------------- + + /** + * CollationElementIteratorJNI constructor. + * The only caller of this class should be + * RuleBasedCollator.getCollationElementIterator(). + * @param collelemiteratoraddress address of C collationelementiterator + */ + CollationElementIterator(int collelemiteratoraddress) + { + m_collelemiterator_ = collelemiteratoraddress; + } + + // protected methods -------------------------------------------- + + /** + * Garbage collection. + * Close C collator and reclaim memory. + * @stable ICU 2.4 + */ + protected void finalize() + { + NativeCollation.closeElements(m_collelemiterator_); + } + + // private data members ----------------------------------------- + + /** + * C collator + */ + private int m_collelemiterator_; + + /** + * ICU constant primary order mask for collation elements + */ + private static final int PRIMARY_ORDER_MASK_ = 0xffff0000; + /** + * ICU constant secondary order mask for collation elements + */ + private static final int SECONDARY_ORDER_MASK_ = 0x0000ff00; + /** + * ICU constant tertiary order mask for collation elements + */ + private static final int TERTIARY_ORDER_MASK_ = 0x000000ff; + /** + * ICU constant primary order shift for collation elements + */ + private static final int PRIMARY_ORDER_SHIFT_ = 16; + /** + * ICU constant secondary order shift for collation elements + */ + private static final int SECONDARY_ORDER_SHIFT_ = 8; + /** + * Unsigned 16 bit mask + */ + private static final int UNSIGNED_16_BIT_MASK_ = 0x0000FFFF; +} diff --git a/icu/src/main/java/com/ibm/icu4jni/text/CollationKey.java b/icu/src/main/java/com/ibm/icu4jni/text/CollationKey.java new file mode 100644 index 0000000..7e8a000 --- /dev/null +++ b/icu/src/main/java/com/ibm/icu4jni/text/CollationKey.java @@ -0,0 +1,186 @@ +/** +******************************************************************************* +* Copyright (C) 1996-2005, International Business Machines Corporation and * +* others. All Rights Reserved. * +******************************************************************************* +* +* +******************************************************************************* +*/ + +package com.ibm.icu4jni.text; + +/** +* Collation key wrapper, containing the byte array sort key. +* @author syn wee quek +* @stable ICU 2.4 +*/ + +public final class CollationKey implements Comparable +{ + // public methods ----------------------------------------------- + + /** + * Bitwise comparison for the collation keys + * @param target CollationKey to be compared + * @return comparison result from Collator, RESULT_LESS, RESULT_EQUAL, + * RESULT_GREATER + * @stable ICU 2.4 + */ + public int compareTo(CollationKey target) + { + byte tgtbytes[] = target.m_bytes_; + + if (m_bytes_ == null || m_bytes_.length == 0) { + if (tgtbytes == null || tgtbytes.length == 0) { + return Collator.RESULT_EQUAL; + } + return Collator.RESULT_LESS; + } + else { + if (tgtbytes == null || tgtbytes.length == 0) { + return Collator.RESULT_GREATER; + } + } + + int count = m_bytes_.length; + if (tgtbytes.length < count) { + count = tgtbytes.length; + } + + int s, + t; + for (int i = 0; i < count; i ++) + { + // unable to use Arrays.equals + s = m_bytes_[i] & UNSIGNED_BYTE_MASK_; + t = tgtbytes[i] & UNSIGNED_BYTE_MASK_; + if (s < t) { + return Collator.RESULT_LESS; + } + if (s > t) { + return Collator.RESULT_GREATER; + } + } + + if (m_bytes_.length < target.m_bytes_.length) { + return Collator.RESULT_LESS; + } + + if (m_bytes_.length > target.m_bytes_.length) { + return Collator.RESULT_GREATER; + } + + return Collator.RESULT_EQUAL; + } + + /** + * Bitwise comparison for the collation keys. + * Argument is casted to CollationKey + * @param target CollationKey to be compared + * @return comparison result from Collator, RESULT_LESS, RESULT_EQUAL, + * RESULT_GREATER + * @stable ICU 2.4 + */ + public int compareTo(Object target) + { + return compareTo((CollationKey)target); + } + + /** + * Checks if target object is equal to this object. + * Target is first casted to CollationKey and bitwise compared. + * @param target comparison object + * @return true if both objects are equal, false otherwise + * @stable ICU 2.4 + */ + public boolean equals(Object target) + { + if (this == target) { + return true; + } + + // checks getClass here since CollationKey is final not subclassable + if (target == null || target.getClass() != getClass()) { + return false; + } + + return compareTo((CollationKey)target) == Collator.RESULT_EQUAL; + } + + /** + * Creates a hash code for this CollationKey. + * Compute the hash by iterating sparsely over about 32 (up to 63) bytes + * spaced evenly through the string. For each byte, multiply the previous + * hash value by a prime number and add the new byte in, like a linear + * congruential random number generator, producing a pseudorandom + * deterministic value well distributed over the output range. + * @return hash value of collation key. Hash value is never 0. + * @stable ICU 2.4 + */ + public int hashCode() + { + if (m_hash_ == 0) + { + if (m_bytes_ != null || m_bytes_.length != 0) + { + int len = m_bytes_.length; + int inc = ((len - 32) / 32) + 1; + for (int i = 0; i < len;) + { + m_hash_ = (m_hash_ * 37) + m_bytes_[i]; + i += inc; + } + } + if (m_hash_ == 0) + m_hash_ = 1; + } + return m_hash_; + } + + /** + * Create the value of the Collation key in term of bytes + * @return value of Collation key in bytes + * @stable ICU 2.4 + */ + public byte[] toByteArray() + { + if (m_bytes_ == null || m_bytes_.length == 0) + return null; + + return (byte[])m_bytes_.clone(); + } + + // package constructors ---------------------------------------------- + + /** + * Default constructor, for use by the Collator and its subclasses. + */ + CollationKey() + { + m_hash_ = 0; + } + + /** + * Constructor, for use only by the Collator and its subclasses. + */ + CollationKey(byte[] bytes) + { + m_bytes_ = bytes; + m_hash_ = 0; + } + + // private data members ----------------------------------------------- + + private byte m_bytes_[]; + + /** + * Mask value to retrieve a single unsigned byte + */ + private static final int UNSIGNED_BYTE_MASK_ = 0x00FF; + + /** + * Cached hash value + */ + private int m_hash_; +} diff --git a/icu/src/main/java/com/ibm/icu4jni/text/Collator.java b/icu/src/main/java/com/ibm/icu4jni/text/Collator.java new file mode 100644 index 0000000..4a7e1bf --- /dev/null +++ b/icu/src/main/java/com/ibm/icu4jni/text/Collator.java @@ -0,0 +1,431 @@ +/** +******************************************************************************* +* Copyright (C) 1996-2005, International Business Machines Corporation and * +* others. All Rights Reserved. * +******************************************************************************* +* +* +******************************************************************************* +*/ + +package com.ibm.icu4jni.text; + +import java.util.Locale; +import com.ibm.icu4jni.text.RuleBasedCollator; + +/** +* Abstract class handling locale specific collation via JNI and ICU. +* Subclasses implement specific collation strategies. One subclass, +* com.ibm.icu4jni.text.RuleBasedCollator, is currently provided and is +* applicable to a wide set of languages. Other subclasses may be created to +* handle more specialized needs. +* You can use the static factory method, getInstance(), to obtain the +* appropriate Collator object for a given locale. +* +* <pre> +* // Compare two strings in the default locale +* Collator myCollator = Collator.getInstance(); +* if (myCollator.compare("abc", "ABC") < 0) { +* System.out.println("abc is less than ABC"); +* } +* else { +* System.out.println("abc is greater than or equal to ABC"); +* } +* </pre> +* +* You can set a Collator's strength property to determine the level of +* difference considered significant in comparisons. +* Five strengths in CollationAttribute are provided: VALUE_PRIMARY, +* VALUE_SECONDARY, VALUE_TERTIARY, VALUE_QUARTENARY and VALUE_IDENTICAL. +* The exact assignment of strengths to language features is locale dependant. +* For example, in Czech, "e" and "f" are considered primary differences, while +* "e" and "?" latin small letter e with circumflex are secondary differences, +* "e" and "E" are tertiary differences and "e" and "e" are identical. +* +* <p> +* The following shows how both case and accents could be ignored for US +* English. +* <pre> +* //Get the Collator for US English and set its strength to PRIMARY +* Collator usCollator = Collator.getInstance(Locale.US); +* usCollator.setStrength(Collator.PRIMARY); +* if (usCollator.compare("abc", "ABC") == 0) { +* System.out.println("Strings are equivalent"); +* } +* </pre> +* For comparing Strings exactly once, the compare method provides the best +* performance. +* When sorting a list of Strings however, it is generally necessary to compare +* each String multiple times. +* In this case, com.ibm.icu4jni.text.CollationKey provide better performance. +* The CollationKey class converts a String to a series of bits that can be +* compared bitwise against other CollationKeys. +* A CollationKey is created by a Collator object for a given String. +* Note: CollationKeys from different Collators can not be compared. +* </p> +* +* Considerations : +* 1) ErrorCode not returned to user throw exceptions instead +* 2) Similar API to java.text.Collator +* @author syn wee quek +* @stable ICU 2.4 +*/ + +public abstract class Collator implements Cloneable +{ + // public data members --------------------------------------------------- + + /** + * Strongest collator strength value. Typically used to denote differences + * between base characters. See class documentation for more explanation. + * @see #setStrength + * @see #getStrength + * @stable ICU 2.4 + */ + public final static int PRIMARY = CollationAttribute.VALUE_PRIMARY; + + /** + * Second level collator strength value. + * Accents in the characters are considered secondary differences. + * Other differences between letters can also be considered secondary + * differences, depending on the language. + * See class documentation for more explanation. + * @see #setStrength + * @see #getStrength + * @stable ICU 2.4 + */ + public final static int SECONDARY = CollationAttribute.VALUE_SECONDARY; + + /** + * Third level collator strength value. + * Upper and lower case differences in characters are distinguished at this + * strength level. In addition, a variant of a letter differs from the base + * form on the tertiary level. + * See class documentation for more explanation. + * @see #setStrength + * @see #getStrength + * @stable ICU 2.4 + */ + public final static int TERTIARY = CollationAttribute.VALUE_TERTIARY; + + /** + * Fourth level collator strength value. + * When punctuation is ignored + * <a href="http://www-124.ibm.com/icu/userguide/Collate_Concepts.html#Ignoring_Punctuation"> + * (see Ignoring Punctuations in the user guide)</a> at PRIMARY to TERTIARY + * strength, an additional strength level can + * be used to distinguish words with and without punctuation. + * See class documentation for more explanation. + * @see #setStrength + * @see #getStrength + * @stable ICU 2.4 + */ + public final static int QUATERNARY = CollationAttribute.VALUE_QUATERNARY; + + /** + * <p> + * Smallest Collator strength value. When all other strengths are equal, + * the IDENTICAL strength is used as a tiebreaker. The Unicode code point + * values of the NFD form of each string are compared, just in case there + * is no difference. + * See class documentation for more explanation. + * </p> + * <p> + * Note this value is different from JDK's + * </p> + * @stable ICU 2.4 + */ + public final static int IDENTICAL = CollationAttribute.VALUE_IDENTICAL; + + /** + * <p>Decomposition mode value. With NO_DECOMPOSITION set, Strings + * will not be decomposed for collation. This is the default + * decomposition setting unless otherwise specified by the locale + * used to create the Collator.</p> + * + * <p><strong>Note</strong> this value is different from the JDK's.</p> + * @see #CANONICAL_DECOMPOSITION + * @see #getDecomposition + * @see #setDecomposition + * @stable ICU 2.4 + */ + public final static int NO_DECOMPOSITION = CollationAttribute.VALUE_OFF; + + /** + * <p>Decomposition mode value. With CANONICAL_DECOMPOSITION set, + * characters that are canonical variants according to the Unicode standard + * will be decomposed for collation.</p> + * + * <p>CANONICAL_DECOMPOSITION corresponds to Normalization Form D as + * described in <a href="http://www.unicode.org/unicode/reports/tr15/"> + * Unicode Technical Report #15</a>. + * </p> + * @see #NO_DECOMPOSITION + * @see #getDecomposition + * @see #setDecomposition + * @stable ICU 2.4 + */ + public final static int CANONICAL_DECOMPOSITION + = CollationAttribute.VALUE_ON; + + // Collation result constants ----------------------------------- + // corresponds to ICU's UCollationResult enum balues + /** + * string a == string b + * @stable ICU 2.4 + */ + public static final int RESULT_EQUAL = 0; + /** + * string a > string b + * @stable ICU 2.4 + */ + public static final int RESULT_GREATER = 1; + /** + * string a < string b + * @stable ICU 2.4 + */ + public static final int RESULT_LESS = -1; + /** + * accepted by most attributes + * @stable ICU 2.4 + */ + public static final int RESULT_DEFAULT = -1; + + // public methods ----------------------------------------------- + + /** + * Factory method to create an appropriate Collator which uses the default + * locale collation rules. + * Current implementation createInstance() returns a RuleBasedCollator(Locale) + * instance. The RuleBasedCollator will be created in the following order, + * <ul> + * <li> Data from argument locale resource bundle if found, otherwise + * <li> Data from parent locale resource bundle of arguemtn locale if found, + * otherwise + * <li> Data from built-in default collation rules if found, other + * <li> null is returned + * </ul> + * @return an instance of Collator + * @stable ICU 2.4 + */ + public static Collator getInstance() + { + return getInstance(null); + } + + /** + * Factory method to create an appropriate Collator which uses the argument + * locale collation rules.<br> + * Current implementation createInstance() returns a RuleBasedCollator(Locale) + * instance. The RuleBasedCollator will be created in the following order, + * <ul> + * <li> Data from argument locale resource bundle if found, otherwise + * <li> Data from parent locale resource bundle of arguemtn locale if found, + * otherwise + * <li> Data from built-in default collation rules if found, other + * <li> null is returned + * </ul> + * @param locale to be used for collation + * @return an instance of Collator + * @stable ICU 2.4 + */ + public static Collator getInstance(Locale locale) + { + RuleBasedCollator result = new RuleBasedCollator(locale); + return result; + } + + /** + * Locale dependent equality check for the argument strings. + * @param source string + * @param target string + * @return true if source is equivalent to target, false otherwise + * @stable ICU 2.4 + */ + public boolean equals(String source, String target) + { + return (compare(source, target) == RESULT_EQUAL); + } + + /** + * Checks if argument object is equals to this object. + * @param target object + * @return true if source is equivalent to target, false otherwise + * @stable ICU 2.4 + */ + public abstract boolean equals(Object target); + + /** + * Makes a copy of the current object. + * @return a copy of this object + * @stable ICU 2.4 + */ + public abstract Object clone() throws CloneNotSupportedException; + + /** + * The comparison function compares the character data stored in two + * different strings. Returns information about whether a string is less + * than, greater than or equal to another string. + * <p>Example of use: + * <pre> + * . Collator myCollation = Collator.getInstance(Locale::US); + * . myCollation.setStrength(CollationAttribute.VALUE_PRIMARY); + * . // result would be CollationAttribute.VALUE_EQUAL + * . // ("abc" == "ABC") + * . // (no primary difference between "abc" and "ABC") + * . int result = myCollation.compare("abc", "ABC",3); + * . myCollation.setStrength(CollationAttribute.VALUE_TERTIARY); + * . // result would be Collation.LESS (abc" <<< "ABC") + * . // (with tertiary difference between "abc" and "ABC") + * . int result = myCollation.compare("abc", "ABC",3); + * </pre> + * @param source source string. + * @param target target string. + * @return result of the comparison, Collator.RESULT_EQUAL, + * Collator.RESULT_GREATER or Collator.RESULT_LESS + * @stable ICU 2.4 + */ + public abstract int compare(String source, String target); + + /** + * Get the decomposition mode of this Collator. + * @return the decomposition mode + * @see #CANONICAL_DECOMPOSITION + * @see #NO_DECOMPOSITION + * @stable ICU 2.4 + */ + public abstract int getDecomposition(); + + /** + * Set the normalization mode used int this object + * The normalization mode influences how strings are compared. + * @param mode desired normalization mode + * @see #CANONICAL_DECOMPOSITION + * @see #NO_DECOMPOSITION + * @stable ICU 2.4 + */ + public abstract void setDecomposition(int mode); + + /** + * Determines the minimum strength that will be use in comparison or + * transformation. + * <p> + * E.g. with strength == SECONDARY, the tertiary difference is ignored + * </p> + * <p> + * E.g. with strength == PRIMARY, the secondary and tertiary difference + * are ignored. + * </p> + * @return the current comparison level. + * @see #PRIMARY + * @see #SECONDARY + * @see #TERTIARY + * @see #QUATERNARY + * @see #IDENTICAL + * @stable ICU 2.4 + */ + public abstract int getStrength(); + + /** + * Gets the attribute to be used in comparison or transformation. + * @param type the attribute to be set from CollationAttribute + * @return value attribute value from CollationAttribute + * @stable ICU 2.4 + */ + public abstract int getAttribute(int type); + + /** + * Sets the minimum strength to be used in comparison or transformation. + * <p>Example of use: + * <pre> + * . Collator myCollation = Collator.createInstance(Locale::US); + * . myCollation.setStrength(PRIMARY); + * . // result will be "abc" == "ABC" + * . // tertiary differences will be ignored + * . int result = myCollation->compare("abc", "ABC"); + * </pre> + * @param strength the new comparison level. + * @see #PRIMARY + * @see #SECONDARY + * @see #TERTIARY + * @see #QUATERNARY + * @see #IDENTICAL + * @stable ICU 2.4 + */ + public abstract void setStrength(int strength); + + /** + * Sets the attribute to be used in comparison or transformation. + * <p>Example of use: + * <pre> + * . Collator myCollation = Collator.createInstance(Locale::US); + * . myCollation.setAttribute(CollationAttribute.CASE_LEVEL, + * . CollationAttribute.VALUE_ON); + * . int result = myCollation->compare("\\u30C3\\u30CF", + * . "\\u30C4\\u30CF"); + * . // result will be Collator.RESULT_LESS. + * </pre> + * @param type the attribute to be set from CollationAttribute + * @param value attribute value from CollationAttribute + * @stable ICU 2.4 + */ + public abstract void setAttribute(int type, int value); + + /** + * Get the sort key as an CollationKey object from the argument string. + * To retrieve sort key in terms of byte arrays, use the method as below<br> + * <code> + * Collator collator = Collator.getInstance(); + * CollationKey collationkey = collator.getCollationKey("string"); + * byte[] array = collationkey.toByteArray(); + * </code><br> + * Byte array result are zero-terminated and can be compared using + * java.util.Arrays.equals(); + * @param source string to be processed. + * @return the sort key + * @stable ICU 2.4 + */ + public abstract CollationKey getCollationKey(String source); + + /** + * Returns a hash of this collation object + * @return hash of this collation object + * @stable ICU 2.4 + */ + public abstract int hashCode(); + + // BEGIN android-added + public static Locale[] getAvailableLocales() { + + String[] locales = NativeCollation.getAvailableLocalesImpl(); + + Locale[] result = new Locale[locales.length]; + + String locale; + + int index, index2; + + for(int i = 0; i < locales.length; i++) { + locale = locales[i]; + + index = locale.indexOf('_'); + index2 = locale.lastIndexOf('_'); + + if(index == -1) { + result[i] = new Locale(locales[i]); + } else if(index == 2 && index == index2) { + result[i] = new Locale( + locale.substring(0,2), + locale.substring(3,5)); + } else if(index == 2 && index2 > index) { + result[i] = new Locale( + locale.substring(0,index), + locale.substring(index + 1,index2), + locale.substring(index2 + 1)); + } + } + + return result; + } + // END android-added +} diff --git a/icu/src/main/java/com/ibm/icu4jni/text/DecimalFormat.java b/icu/src/main/java/com/ibm/icu4jni/text/DecimalFormat.java new file mode 100644 index 0000000..5bee1fd --- /dev/null +++ b/icu/src/main/java/com/ibm/icu4jni/text/DecimalFormat.java @@ -0,0 +1,568 @@ +/* + * Copyright (C) 2008 The Android Open Source Project + * + * Licensed 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 com.ibm.icu4jni.text; + +import com.ibm.icu4jni.text.NativeDecimalFormat.UNumberFormatAttribute; +import com.ibm.icu4jni.text.NativeDecimalFormat.UNumberFormatTextAttribute; + +import java.math.BigDecimal; +import java.math.BigInteger; +import java.text.AttributedCharacterIterator; +import java.text.AttributedString; +import java.text.FieldPosition; +import java.text.Format; +import java.text.NumberFormat; +import java.text.ParsePosition; +import java.util.Currency; +import java.util.Locale; + +public class DecimalFormat extends NumberFormat { + + private int addr; + + private DecimalFormatSymbols symbols; + + // fix to be icu4j conform (harmony wants this field to exist) + // for serialization of java.text.DecimalFormat + @SuppressWarnings("unused") + private boolean useExponentialNotation = false; + @SuppressWarnings("unused") + private byte minExponentDigits = 0; + + private boolean negPrefNull; + private boolean negSuffNull; + private boolean posPrefNull; + private boolean posSuffNull; + + public DecimalFormat(String pattern, DecimalFormatSymbols icuSymbols) { + this.addr = icuSymbols.getAddr(); + this.symbols = icuSymbols; + applyPattern(pattern); + } + + @Override + public int hashCode() { + return super.hashCode() * 37 + this.getPositivePrefix().hashCode(); + } + + @Override + public Object clone() { + String pat = this.toPattern(); + Locale loc = this.symbols.getLocale(); + DecimalFormatSymbols sym = (DecimalFormatSymbols) this.symbols.clone(); + DecimalFormat newdf = new DecimalFormat(pat, sym); + newdf.setMaximumIntegerDigits(this.getMaximumIntegerDigits()); + newdf.setMaximumFractionDigits(this.getMaximumFractionDigits()); + newdf.setMinimumIntegerDigits(this.getMinimumIntegerDigits()); + newdf.setMinimumFractionDigits(this.getMinimumFractionDigits()); + newdf.setGroupingUsed(this.isGroupingUsed()); + newdf.setGroupingSize(this.getGroupingSize()); + return newdf; + } + + @Override + public boolean equals(Object object) { + if (object == this) { + return true; + } + if (!(object instanceof DecimalFormat)) { + return false; + } + DecimalFormat obj = (DecimalFormat) object; + + if(obj.addr == this.addr) { + return true; + } + + boolean result = super.equals(object); + + + result &= obj.toPattern().equals(this.toPattern()); + result &= obj.isDecimalSeparatorAlwaysShown() == this.isDecimalSeparatorAlwaysShown(); + result &= obj.getGroupingSize() == this.getGroupingSize(); + result &= obj.getMultiplier() == this.getMultiplier(); + result &= obj.getNegativePrefix().equals(this.getNegativePrefix()); + result &= obj.getNegativeSuffix().equals(this.getNegativeSuffix()); + result &= obj.getPositivePrefix().equals(this.getPositivePrefix()); + result &= obj.getPositiveSuffix().equals(this.getPositiveSuffix()); + result &= obj.getMaximumIntegerDigits() == this.getMaximumIntegerDigits(); + result &= obj.getMaximumFractionDigits() == this.getMaximumFractionDigits(); + result &= obj.getMinimumIntegerDigits() == this.getMinimumIntegerDigits(); + result &= obj.getMinimumFractionDigits() == this.getMinimumFractionDigits(); + result &= obj.isGroupingUsed() == this.isGroupingUsed(); + Currency objCurr = obj.getCurrency(); + Currency thisCurr = this.getCurrency(); + if(objCurr != null) { + result &= objCurr.getCurrencyCode().equals(thisCurr.getCurrencyCode()); + result &= objCurr.getSymbol().equals(thisCurr.getSymbol()); + result &= objCurr.getDefaultFractionDigits() == thisCurr.getDefaultFractionDigits(); + } else { + result &= thisCurr == null; + } + result &= obj.getDecimalFormatSymbols().equals(this.getDecimalFormatSymbols()); + + return result; + } + + @Override + public StringBuffer format(Object value, StringBuffer buffer, FieldPosition field) { + + if(!(value instanceof Number)) { + throw new IllegalArgumentException(); + } + if(buffer == null || field == null) { + throw new NullPointerException(); + } + + String fieldType = getFieldType(field.getFieldAttribute()); + + Number number = (Number) value; + + if(number instanceof BigInteger) { + BigInteger valBigInteger = (BigInteger) number; + String result = NativeDecimalFormat.format(this.addr, + valBigInteger.toString(10), field, fieldType, null, 0); + return buffer.append(result); + } else if(number instanceof BigDecimal) { + BigDecimal valBigDecimal = (BigDecimal) number; + StringBuilder val = new StringBuilder(); + val.append(valBigDecimal.unscaledValue().toString(10)); + int scale = valBigDecimal.scale(); + scale = makeScalePositive(scale, val); + String result = NativeDecimalFormat.format(this.addr, + val.toString(), field, fieldType, null, scale); + return buffer.append(result); + } else { + double dv = number.doubleValue(); + long lv = number.longValue(); + if (dv == lv) { + String result = NativeDecimalFormat.format(this.addr, lv, field, + fieldType, null); + return buffer.append(result); + } + String result = NativeDecimalFormat.format(this.addr, dv, field, + fieldType, null); + return buffer.append(result); + } + } + + @Override + public StringBuffer format(long value, StringBuffer buffer, FieldPosition field) { + + if(buffer == null || field == null) { + throw new NullPointerException(); + } + + String fieldType = getFieldType(field.getFieldAttribute()); + + String result = NativeDecimalFormat.format(this.addr, value, field, + fieldType, null); + + buffer.append(result.toCharArray(), 0, result.length()); + + return buffer; + } + + @Override + public StringBuffer format(double value, StringBuffer buffer, FieldPosition field) { + + if(buffer == null || field == null) { + throw new NullPointerException(); + } + + String fieldType = getFieldType(field.getFieldAttribute()); + + String result = NativeDecimalFormat.format(this.addr, value, field, + fieldType, null); + + buffer.append(result.toCharArray(), 0, result.length()); + + return buffer; + } + + public void applyLocalizedPattern(String pattern) { + if (pattern == null) { + throw new NullPointerException("pattern was null"); + } + try { + NativeDecimalFormat.applyPatternImpl(this.addr, false, pattern); + } catch(RuntimeException re) { + throw new IllegalArgumentException( + "applying localized pattern failed for pattern: " + pattern, re); + } + } + + public void applyPattern(String pattern) { + if (pattern == null) { + throw new NullPointerException("pattern was null"); + } + try { + NativeDecimalFormat.applyPatternImpl(this.addr, false, pattern); + } catch(RuntimeException re) { + throw new IllegalArgumentException( + "applying pattern failed for pattern: " + pattern, re); + } + } + + @Override + public AttributedCharacterIterator formatToCharacterIterator(Object object) { + if (!(object instanceof Number)) { + throw new IllegalArgumentException(); + } + Number number = (Number) object; + String text = null; + StringBuffer attributes = new StringBuffer(); + + if(number instanceof BigInteger) { + BigInteger valBigInteger = (BigInteger) number; + text = NativeDecimalFormat.format(this.addr, + valBigInteger.toString(10), null, null, attributes, 0); + } else if(number instanceof BigDecimal) { + BigDecimal valBigDecimal = (BigDecimal) number; + StringBuilder val = new StringBuilder(); + val.append(valBigDecimal.unscaledValue().toString(10)); + int scale = valBigDecimal.scale(); + scale = makeScalePositive(scale, val); + text = NativeDecimalFormat.format(this.addr, val.toString(), null, + null, attributes, scale); + } else { + double dv = number.doubleValue(); + long lv = number.longValue(); + if (dv == lv) { + text = NativeDecimalFormat.format(this.addr, lv, null, + null, attributes); + } else { + text = NativeDecimalFormat.format(this.addr, dv, null, + null, attributes); + } + } + + AttributedString as = new AttributedString(text.toString()); + + String[] attrs = attributes.toString().split(";"); + // add NumberFormat field attributes to the AttributedString + int size = attrs.length / 3; + if(size * 3 != attrs.length) { + return as.getIterator(); + } + for (int i = 0; i < size; i++) { + Format.Field attribute = getField(attrs[3*i]); + as.addAttribute(attribute, attribute, Integer.parseInt(attrs[3*i+1]), + Integer.parseInt(attrs[3*i+2])); + } + + // return the CharacterIterator from AttributedString + return as.getIterator(); + } + + private int makeScalePositive(int scale, StringBuilder val) { + if (scale < 0) { + scale = -scale; + for (int i = scale; i > 0; i--) { + val.append('0'); + } + scale = 0; + } + return scale; + } + + public String toLocalizedPattern() { + return NativeDecimalFormat.toPatternImpl(this.addr, true); + } + + public String toPattern() { + return NativeDecimalFormat.toPatternImpl(this.addr, false); + } + + @Override + public Number parse(String string, ParsePosition position) { + return NativeDecimalFormat.parse(addr, string, position); + } + + // start getter and setter + + @Override + public int getMaximumFractionDigits() { + return NativeDecimalFormat.getAttribute(this .addr, + UNumberFormatAttribute.UNUM_MAX_FRACTION_DIGITS.ordinal()); + } + + @Override + public int getMaximumIntegerDigits() { + return NativeDecimalFormat.getAttribute(this .addr, + UNumberFormatAttribute.UNUM_MAX_INTEGER_DIGITS.ordinal()); + } + + @Override + public int getMinimumFractionDigits() { + return NativeDecimalFormat.getAttribute(this .addr, + UNumberFormatAttribute.UNUM_MIN_FRACTION_DIGITS.ordinal()); + } + + @Override + public int getMinimumIntegerDigits() { + return NativeDecimalFormat.getAttribute(this .addr, + UNumberFormatAttribute.UNUM_MIN_INTEGER_DIGITS.ordinal()); + } + + @Override + public Currency getCurrency() { + return this.symbols.getCurrency(); + } + + public int getGroupingSize() { + return NativeDecimalFormat.getAttribute(this.addr, + UNumberFormatAttribute.UNUM_GROUPING_SIZE.ordinal()); + } + + public int getMultiplier() { + return NativeDecimalFormat.getAttribute(this.addr, + UNumberFormatAttribute.UNUM_MULTIPLIER.ordinal()); + } + + public String getNegativePrefix() { + if (negPrefNull) { + return null; + } + return NativeDecimalFormat.getTextAttribute(this.addr, + UNumberFormatTextAttribute.UNUM_NEGATIVE_PREFIX.ordinal()); + } + + public String getNegativeSuffix() { + if (negSuffNull) { + return null; + } + return NativeDecimalFormat.getTextAttribute(this.addr, + UNumberFormatTextAttribute.UNUM_NEGATIVE_SUFFIX.ordinal()); + } + + public String getPositivePrefix() { + if (posPrefNull) { + return null; + } + return NativeDecimalFormat.getTextAttribute(this.addr, + UNumberFormatTextAttribute.UNUM_POSITIVE_PREFIX.ordinal()); + } + + public String getPositiveSuffix() { + if (posSuffNull) { + return null; + } + return NativeDecimalFormat.getTextAttribute(this.addr, + UNumberFormatTextAttribute.UNUM_POSITIVE_SUFFIX.ordinal()); + } + + public boolean isDecimalSeparatorAlwaysShown() { + return NativeDecimalFormat.getAttribute(this.addr, + UNumberFormatAttribute.UNUM_DECIMAL_ALWAYS_SHOWN.ordinal()) != 0; + } + + @Override + public boolean isParseIntegerOnly() { + return NativeDecimalFormat.getAttribute(this.addr, + UNumberFormatAttribute.UNUM_PARSE_INT_ONLY.ordinal()) != 0; + } + + @Override + public boolean isGroupingUsed() { + return NativeDecimalFormat.getAttribute(this.addr, + UNumberFormatAttribute.UNUM_GROUPING_USED.ordinal()) != 0; + } + + public DecimalFormatSymbols getDecimalFormatSymbols() { + return this.symbols; + } + + public void setDecimalFormatSymbols(DecimalFormatSymbols icuSymbols) { + this.symbols = icuSymbols; + } + + public void setDecimalSeparatorAlwaysShown(boolean value) { + int i = value ? -1 : 0; + NativeDecimalFormat.setAttribute(this.addr, + UNumberFormatAttribute.UNUM_DECIMAL_ALWAYS_SHOWN.ordinal(), i); + } + + @Override + public void setCurrency(Currency currency) { + this.symbols.setCurrency(currency); + } + + public void setGroupingSize(int value) { + NativeDecimalFormat.setAttribute(this.addr, + UNumberFormatAttribute.UNUM_GROUPING_SIZE.ordinal(), value); + } + + @Override + public void setGroupingUsed(boolean value) { + int i = value ? -1 : 0; + NativeDecimalFormat.setAttribute(this.addr, + UNumberFormatAttribute.UNUM_GROUPING_USED.ordinal(), i); + } + + @Override + public void setMaximumFractionDigits(int value) { + NativeDecimalFormat.setAttribute(this.addr, + UNumberFormatAttribute.UNUM_MAX_FRACTION_DIGITS.ordinal(), value); + } + + @Override + public void setMaximumIntegerDigits(int value) { + NativeDecimalFormat.setAttribute(this.addr, + UNumberFormatAttribute.UNUM_MAX_INTEGER_DIGITS.ordinal(), value); + } + + @Override + public void setMinimumFractionDigits(int value) { + NativeDecimalFormat.setAttribute(this.addr, + UNumberFormatAttribute.UNUM_MIN_FRACTION_DIGITS.ordinal(), value); + } + + @Override + public void setMinimumIntegerDigits(int value) { + NativeDecimalFormat.setAttribute(this.addr, + UNumberFormatAttribute.UNUM_MIN_INTEGER_DIGITS.ordinal(), value); + } + + public void setMultiplier(int value) { + NativeDecimalFormat.setAttribute(this.addr, + UNumberFormatAttribute.UNUM_MULTIPLIER.ordinal(), value); + } + + public void setNegativePrefix(String value) { + negPrefNull = value == null; + if (!negPrefNull) { + NativeDecimalFormat.setTextAttribute(this.addr, + UNumberFormatTextAttribute.UNUM_NEGATIVE_PREFIX.ordinal(), + value); + } + } + + public void setNegativeSuffix(String value) { + negSuffNull = value == null; + if (!negSuffNull) { + NativeDecimalFormat.setTextAttribute(this.addr, + UNumberFormatTextAttribute.UNUM_NEGATIVE_SUFFIX.ordinal(), + value); + } + } + + public void setPositivePrefix(String value) { + posPrefNull = value == null; + if (!posPrefNull) { + NativeDecimalFormat.setTextAttribute(this.addr, + UNumberFormatTextAttribute.UNUM_POSITIVE_PREFIX.ordinal(), + value); + } + } + + public void setPositiveSuffix(String value) { + posSuffNull = value == null; + if (!posSuffNull) { + NativeDecimalFormat.setTextAttribute(this.addr, + UNumberFormatTextAttribute.UNUM_POSITIVE_SUFFIX.ordinal(), + value); + } + } + + @Override + public void setParseIntegerOnly(boolean value) { + int i = value ? -1 : 0; + NativeDecimalFormat.setAttribute(this.addr, + UNumberFormatAttribute.UNUM_PARSE_INT_ONLY.ordinal(), i); + } + + static protected String getFieldType(Format.Field field) { + if(field == null) { + return null; + } + if(field.equals(NumberFormat.Field.SIGN)) { + return "sign"; + } + if(field.equals(NumberFormat.Field.INTEGER)) { + return "integer"; + } + if(field.equals(NumberFormat.Field.FRACTION)) { + return "fraction"; + } + if(field.equals(NumberFormat.Field.EXPONENT)) { + return "exponent"; + } + if(field.equals(NumberFormat.Field.EXPONENT_SIGN)) { + return "exponent_sign"; + } + if(field.equals(NumberFormat.Field.EXPONENT_SYMBOL)) { + return "exponent_symbol"; + } + if(field.equals(NumberFormat.Field.CURRENCY)) { + return "currency"; + } + if(field.equals(NumberFormat.Field.GROUPING_SEPARATOR)) { + return "grouping_separator"; + } + if(field.equals(NumberFormat.Field.DECIMAL_SEPARATOR)) { + return "decimal_separator"; + } + if(field.equals(NumberFormat.Field.PERCENT)) { + return "percent"; + } + if(field.equals(NumberFormat.Field.PERMILLE)) { + return "permille"; + } + return null; + } + + protected Format.Field getField(String type) { + if(type.equals("")) { + return null; + } + if(type.equals("sign")) { + return NumberFormat.Field.SIGN; + } + if(type.equals("integer")) { + return NumberFormat.Field.INTEGER; + } + if(type.equals("fraction")) { + return NumberFormat.Field.FRACTION; + } + if(type.equals("exponent")) { + return NumberFormat.Field.EXPONENT; + } + if(type.equals("exponent_sign")) { + return NumberFormat.Field.EXPONENT_SIGN; + } + if(type.equals("exponent_symbol")) { + return NumberFormat.Field.EXPONENT_SYMBOL; + } + if(type.equals("currency")) { + return NumberFormat.Field.CURRENCY; + } + if(type.equals("grouping_separator")) { + return NumberFormat.Field.GROUPING_SEPARATOR; + } + if(type.equals("decimal_separator")) { + return NumberFormat.Field.DECIMAL_SEPARATOR; + } + if(type.equals("percent")) { + return NumberFormat.Field.PERCENT; + } + if(type.equals("permille")) { + return NumberFormat.Field.PERMILLE; + } + return null; + } +} diff --git a/icu/src/main/java/com/ibm/icu4jni/text/DecimalFormatSymbols.java b/icu/src/main/java/com/ibm/icu4jni/text/DecimalFormatSymbols.java new file mode 100644 index 0000000..98463e4 --- /dev/null +++ b/icu/src/main/java/com/ibm/icu4jni/text/DecimalFormatSymbols.java @@ -0,0 +1,295 @@ +/* + * Copyright (C) 2008 The Android Open Source Project + * + * Licensed 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 com.ibm.icu4jni.text; + +import com.ibm.icu4jni.text.NativeDecimalFormat.UNumberFormatSymbol; + +import java.security.AccessController; +import java.security.PrivilegedAction; +import java.util.Currency; +import java.util.Locale; +import java.util.ResourceBundle; + +public class DecimalFormatSymbols { + + private int addr; + + private Locale loc; + + private DecimalFormatSymbols(int addr, Locale loc) { + this.addr = addr; + this.loc = loc; + } + + public DecimalFormatSymbols(Locale locale) { + this.loc = locale; + ResourceBundle bundle = AccessController. + doPrivileged(new PrivilegedAction<ResourceBundle>() { + public ResourceBundle run() { + return ResourceBundle.getBundle( + "org.apache.harmony.luni.internal.locale.Locale", loc); //$NON-NLS-1$ + } + }); + String pattern = bundle.getString("Number"); + this.addr = NativeDecimalFormat.openDecimalFormatImpl( + locale.toString(), pattern); + String currSymbol = bundle.getString("CurrencySymbol"); + String intCurrSymbol = bundle.getString("IntCurrencySymbol"); + NativeDecimalFormat.setSymbol(this.addr, + UNumberFormatSymbol.UNUM_CURRENCY_SYMBOL.ordinal(), currSymbol); + NativeDecimalFormat.setSymbol(this.addr, + UNumberFormatSymbol.UNUM_INTL_CURRENCY_SYMBOL.ordinal(), + intCurrSymbol); + } + + @Override + public boolean equals(Object object) { + if(object == null) { + return false; + } + if(!(object instanceof DecimalFormatSymbols)) { + return false; + } + + DecimalFormatSymbols sym = (DecimalFormatSymbols) object; + + if(sym.addr == this.addr) { + return true; + } + + boolean result = true; + + Currency objCurr = sym.getCurrency(); + Currency thisCurr = this.getCurrency(); + if(objCurr != null) { + result &= objCurr.getCurrencyCode().equals(thisCurr.getCurrencyCode()); + result &= objCurr.getSymbol().equals(thisCurr.getSymbol()); + result &= objCurr.getDefaultFractionDigits() == thisCurr.getDefaultFractionDigits(); + } else { + result &= thisCurr == null; + } + result &= sym.getCurrencySymbol().equals(this.getCurrencySymbol()); + result &= sym.getDecimalSeparator() == this.getDecimalSeparator(); + result &= sym.getDigit() == this.getDigit(); + result &= sym.getGroupingSeparator() == this.getGroupingSeparator(); + result &= sym.getInfinity().equals(this.getInfinity()); + result &= sym.getInternationalCurrencySymbol().equals( + this.getInternationalCurrencySymbol()); + result &= sym.getMinusSign() == this.getMinusSign(); + result &= sym.getMonetaryDecimalSeparator() == + this.getMonetaryDecimalSeparator(); + result &= sym.getNaN().equals(this.getNaN()); + result &= sym.getPatternSeparator() == this.getPatternSeparator(); + result &= sym.getPercent() == this.getPercent(); + result &= sym.getPerMill() == this.getPerMill(); + result &= sym.getZeroDigit() == this.getZeroDigit(); + + return result; + } + + @Override + public Object clone() { + int addr = NativeDecimalFormat.cloneImpl(this.addr); + Locale loc = (Locale) this.loc.clone(); + return new DecimalFormatSymbols(addr, loc); + } + + public void setCurrency(Currency currency) { + NativeDecimalFormat.setSymbol(this.addr, + UNumberFormatSymbol.UNUM_CURRENCY_SYMBOL.ordinal(), + currency.getSymbol()); + NativeDecimalFormat.setSymbol(this.addr, + UNumberFormatSymbol.UNUM_INTL_CURRENCY_SYMBOL.ordinal(), + currency.getCurrencyCode()); + } + + public void setCurrencySymbol(String symbol) { + NativeDecimalFormat.setSymbol(this.addr, + UNumberFormatSymbol.UNUM_CURRENCY_SYMBOL.ordinal(), + symbol); + } + + public void setDecimalSeparator(char symbol) { + NativeDecimalFormat.setSymbol(this.addr, + UNumberFormatSymbol.UNUM_DECIMAL_SEPARATOR_SYMBOL.ordinal(), + "" + symbol); + } + + public void setDigit(char symbol) { + NativeDecimalFormat.setSymbol(this.addr, + UNumberFormatSymbol.UNUM_DIGIT_SYMBOL.ordinal(), + "" + symbol); + } + + public void setGroupingSeparator(char symbol) { + NativeDecimalFormat.setSymbol(this.addr, + UNumberFormatSymbol.UNUM_GROUPING_SEPARATOR_SYMBOL.ordinal(), + "" + symbol); + NativeDecimalFormat.setSymbol(this.addr, + UNumberFormatSymbol.UNUM_MONETARY_GROUPING_SEPARATOR_SYMBOL.ordinal(), + "" + symbol); + } + + public void setInfinity(String symbol) { + NativeDecimalFormat.setSymbol(this.addr, + UNumberFormatSymbol.UNUM_INFINITY_SYMBOL.ordinal(), + symbol); + } + + public void setInternationalCurrencySymbol(String symbol) { + NativeDecimalFormat.setSymbol(this.addr, + UNumberFormatSymbol.UNUM_INTL_CURRENCY_SYMBOL.ordinal(), + symbol); + } + + public void setMinusSign(char symbol) { + NativeDecimalFormat.setSymbol(this.addr, + UNumberFormatSymbol.UNUM_MINUS_SIGN_SYMBOL.ordinal(), + "" + symbol); + } + + public void setMonetaryDecimalSeparator(char symbol) { + NativeDecimalFormat.setSymbol(this.addr, + UNumberFormatSymbol.UNUM_MONETARY_SEPARATOR_SYMBOL.ordinal(), + "" + symbol); + } + + public void setNaN(String symbol) { + NativeDecimalFormat.setSymbol(this.addr, + UNumberFormatSymbol.UNUM_NAN_SYMBOL.ordinal(), + "" + symbol); + } + + public void setPatternSeparator(char symbol) { + NativeDecimalFormat.setSymbol(this.addr, + UNumberFormatSymbol.UNUM_PATTERN_SEPARATOR_SYMBOL.ordinal(), + "" + symbol); + } + + public void setPercent(char symbol) { + NativeDecimalFormat.setSymbol(this.addr, + UNumberFormatSymbol.UNUM_PERCENT_SYMBOL.ordinal(), + "" + symbol); + } + + public void setPerMill(char symbol) { + NativeDecimalFormat.setSymbol(this.addr, + UNumberFormatSymbol.UNUM_PERMILL_SYMBOL.ordinal(), + "" + symbol); + } + + public void setZeroDigit(char symbol) { + NativeDecimalFormat.setSymbol(this.addr, + UNumberFormatSymbol.UNUM_ZERO_DIGIT_SYMBOL.ordinal(), + "" + symbol); + } + + public Currency getCurrency() { + String curr = NativeDecimalFormat.getSymbol(this.addr, + UNumberFormatSymbol.UNUM_INTL_CURRENCY_SYMBOL.ordinal()); + if(curr.equals("") || curr.equals("\u00a4\u00a4")) { + return null; + } + return Currency.getInstance(curr); + } + + public String getCurrencySymbol() { + return NativeDecimalFormat.getSymbol(this.addr, + UNumberFormatSymbol.UNUM_CURRENCY_SYMBOL.ordinal()); + } + + public char getDecimalSeparator() { + return NativeDecimalFormat.getSymbol(this.addr, + UNumberFormatSymbol.UNUM_DECIMAL_SEPARATOR_SYMBOL.ordinal()) + .charAt(0); + } + + public char getDigit() { + return NativeDecimalFormat.getSymbol(this.addr, + UNumberFormatSymbol.UNUM_DIGIT_SYMBOL.ordinal()) + .charAt(0); + } + + public char getGroupingSeparator() { + return NativeDecimalFormat.getSymbol(this.addr, + UNumberFormatSymbol.UNUM_GROUPING_SEPARATOR_SYMBOL.ordinal()) + .charAt(0); + } + + public String getInfinity() { + return NativeDecimalFormat.getSymbol(this.addr, + UNumberFormatSymbol.UNUM_INFINITY_SYMBOL.ordinal()); + } + + public String getInternationalCurrencySymbol() { + return NativeDecimalFormat.getSymbol(this.addr, + UNumberFormatSymbol.UNUM_INTL_CURRENCY_SYMBOL.ordinal()); + } + + public char getMinusSign() { + return NativeDecimalFormat.getSymbol(this.addr, + UNumberFormatSymbol.UNUM_MINUS_SIGN_SYMBOL.ordinal()) + .charAt(0); + } + + public char getMonetaryDecimalSeparator() { + return NativeDecimalFormat.getSymbol(this.addr, + UNumberFormatSymbol.UNUM_MONETARY_SEPARATOR_SYMBOL.ordinal()) + .charAt(0); + } + + public String getNaN() { + return NativeDecimalFormat.getSymbol(this.addr, + UNumberFormatSymbol.UNUM_NAN_SYMBOL.ordinal()); + } + + public char getPatternSeparator() { + return NativeDecimalFormat.getSymbol(this.addr, + UNumberFormatSymbol.UNUM_PATTERN_SEPARATOR_SYMBOL.ordinal()) + .charAt(0); + } + + public char getPercent() { + return NativeDecimalFormat.getSymbol(this.addr, + UNumberFormatSymbol.UNUM_PERCENT_SYMBOL.ordinal()) + .charAt(0); + } + + public char getPerMill() { + return NativeDecimalFormat.getSymbol(this.addr, + UNumberFormatSymbol.UNUM_PERMILL_SYMBOL.ordinal()) + .charAt(0); + } + + public char getZeroDigit() { + return NativeDecimalFormat.getSymbol(this.addr, + UNumberFormatSymbol.UNUM_ZERO_DIGIT_SYMBOL.ordinal()) + .charAt(0); + } + + int getAddr() { + return this.addr; + } + + Locale getLocale() { + return this.loc; + } + + protected void finalize() { + NativeDecimalFormat.closeDecimalFormatImpl(this.addr); + } +} diff --git a/icu/src/main/java/com/ibm/icu4jni/text/NativeBreakIterator.java b/icu/src/main/java/com/ibm/icu4jni/text/NativeBreakIterator.java new file mode 100644 index 0000000..25249c7 --- /dev/null +++ b/icu/src/main/java/com/ibm/icu4jni/text/NativeBreakIterator.java @@ -0,0 +1,68 @@ +/* + * Copyright (C) 2008 The Android Open Source Project + * + * Licensed 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 com.ibm.icu4jni.text; + +final class NativeBreakIterator +{ + public NativeBreakIterator() { + + } + + static String[] getAvailableLocalesImpl() { + int count = getAvailableLocalesCountImpl(); + String[] result = new String[count]; + + for(int i = 0; i < count; i++) { + result[i] = getAvailableLocalesImpl(i); + } + return result; + } + + private static native String getAvailableLocalesImpl(int i); + + private static native int getAvailableLocalesCountImpl(); + + static native int getCharacterInstanceImpl(String locale); + + static native int getWordInstanceImpl(String locale); + + static native int getLineInstanceImpl(String locale); + + static native int getSentenceInstanceImpl(String locale); + + static native void closeBreakIteratorImpl(int biaddress); + + static native void setTextImpl(int biaddress, String text); + + static native int cloneImpl(int biaddress); + + static native int precedingImpl(int biaddress, int offset); + + static native boolean isBoundaryImpl(int biaddress, int offset); + + static native int nextImpl(int biaddress, int n); + + static native int previousImpl(int biaddress); + + static native int currentImpl(int biaddress); + + static native int firstImpl(int biaddress); + + static native int followingImpl(int biaddress, int offset); + + static native int lastImpl(int biaddress); +} diff --git a/icu/src/main/java/com/ibm/icu4jni/text/NativeCollation.java b/icu/src/main/java/com/ibm/icu4jni/text/NativeCollation.java new file mode 100644 index 0000000..2242b69 --- /dev/null +++ b/icu/src/main/java/com/ibm/icu4jni/text/NativeCollation.java @@ -0,0 +1,263 @@ +/** +******************************************************************************* +* Copyright (C) 1996-2005, International Business Machines Corporation and * +* others. All Rights Reserved. * +******************************************************************************* +* +* +******************************************************************************* +*/ + +package com.ibm.icu4jni.text; + +/** +* Package static class for declaring all native methods for collation use. +* @author syn wee quek +* @internal ICU 2.4 +*/ + +final class NativeCollation +{ + // collator methods --------------------------------------------- + + public NativeCollation() { + + } + + /** + * Method to create a new C Collator using the default locale rules. + * @return new c collator + * @internal ICU 2.4 + */ + static native int openCollator(); + + /** + * Method to create a new C Collator using the argument locale rules. + * @param locale locale name + * @return new c collator + * @internal ICU 2.4 + */ + static native int openCollator(String locale); + + /** + * Method to create a new C Collator using the argument rules. + * @param rules , set of collation rules + * @param normalizationmode default normalization mode + * @param collationstrength default collation strength + * @return new c collator + * @internal ICU 2.4 + */ + static native int openCollatorFromRules(String rules, + int normalizationmode, + int collationstrength); + + /** + * Close a C collator + * Once closed, a UCollatorOld should not be used. + * @param collatoraddress The UCollatorOld to close + * @internal ICU 2.4 + */ + static native void closeCollator(int collatoraddress); + + /** + * Compare two strings. + * The strings will be compared using the normalization mode and options + * specified in openCollator or openCollatorFromRules + * @param collatoraddress address of the c collator + * @param source The source string. + * @param target The target string. + * @return result of the comparison, Collation.EQUAL, + * Collation.GREATER or Collation.LESS + * @internal ICU 2.4 + */ + static native int compare(int collatoraddress, String source, + String target); + + /** + * Get the normalization mode for this object. + * The normalization mode influences how strings are compared. + * @param collatoraddress + * @return normalization mode; one of the values from Normalization + * @internal ICU 2.4 + */ + static native int getNormalization(int collatoraddress); + + /** + * Set the normalization mode used int this object + * The normalization mode influences how strings are compared. + * @param collatoraddress the address of the C collator + * @param normalizationmode desired normalization mode; one of the values + * from Normalization + * @internal ICU 2.4 + */ + static native void setNormalization(int collatoraddress, + int normalizationmode); + + /** + * Get the collation rules from a UCollator. + * The rules will follow the rule syntax. + * @param collatoraddress the address of the C collator + * @return collation rules. + * @internal ICU 2.4 + */ + static native String getRules(int collatoraddress); + + /** + * Get a sort key for the argument string + * Sort keys may be compared using java.util.Arrays.equals + * @param collatoraddress address of the C collator + * @param source string for key to be generated + * @return sort key + * @internal ICU 2.4 + */ + static native byte[] getSortKey(int collatoraddress, String source); + + /** + * Gets the version information for collation. + * @param collatoraddress address of the C collator + * @return version information + * @internal ICU 2.4 + */ + // private native String getVersion(int collatoraddress); + + /** + * Universal attribute setter. + * @param collatoraddress address of the C collator + * @param type type of attribute to be set + * @param value attribute value + * @exception RuntimeException when error occurs while setting attribute value + * @internal ICU 2.4 + */ + static native void setAttribute(int collatoraddress, int type, int value); + + /** + * Universal attribute getter + * @param collatoraddress address of the C collator + * @param type type of attribute to be set + * @return attribute value + * @exception RuntimeException thrown when error occurs while getting attribute value + * @internal ICU 2.4 + */ + static native int getAttribute(int collatoraddress, int type); + + /** + * Thread safe cloning operation + * @param collatoraddress address of C collator to be cloned + * @return address of the new clone + * @exception RuntimeException thrown when error occurs while cloning + * @internal ICU 2.4 + */ + static native int safeClone(int collatoraddress); + + /** + * Create a CollationElementIterator object that will iterator over the + * elements in a string, using the collation rules defined in this + * RuleBasedCollator + * @param collatoraddress address of C collator + * @param source string to iterate over + * @return address of C collationelementiterator + * @internal ICU 2.4 + */ + static native int getCollationElementIterator(int collatoraddress, + String source); + + /** + * Returns a hash of this collation object + * @param collatoraddress address of C collator + * @return hash of this collation object + * @internal ICU 2.4 + */ + static native int hashCode(int collatoraddress); + + + // collationelementiterator methods ------------------------------------- + + /** + * Close a C collation element iterator. + * @param address of C collation element iterator to close. + * @internal ICU 2.4 + */ + static native void closeElements(int address); + + /** + * Reset the collation elements to their initial state. + * This will move the 'cursor' to the beginning of the text. + * @param address of C collation element iterator to reset. + * @internal ICU 2.4 + */ + static native void reset(int address); + + /** + * Get the ordering priority of the next collation element in the text. + * A single character may contain more than one collation element. + * @param address if C collation elements containing the text. + * @return next collation elements ordering, or NULLORDER if the end of the + * text is reached. + * @internal ICU 2.4 + */ + static native int next(int address); + + /** + * Get the ordering priority of the previous collation element in the text. + * A single character may contain more than one collation element. + * @param address of the C collation element iterator containing the text. + * @return previous collation element ordering, or NULLORDER if the end of + * the text is reached. + * @internal ICU 2.4 + */ + static native int previous(int address); + + /** + * Get the maximum length of any expansion sequences that end with the + * specified comparison order. + * @param address of the C collation element iterator containing the text. + * @param order collation order returned by previous or next. + * @return maximum length of any expansion sequences ending with the + * specified order. + * @internal ICU 2.4 + */ + static native int getMaxExpansion(int address, int order); + + /** + * Set the text containing the collation elements. + * @param address of the C collation element iterator to be set + * @param source text containing the collation elements. + * @internal ICU 2.4 + */ + static native void setText(int address, String source); + + /** + * Get the offset of the current source character. + * This is an offset into the text of the character containing the current + * collation elements. + * @param address of the C collation elements iterator to query. + * @return offset of the current source character. + * @internal ICU 2.4 + */ + static native int getOffset(int address); + + /** + * Set the offset of the current source character. + * This is an offset into the text of the character to be processed. + * @param address of the C collation element iterator to set. + * @param offset The desired character offset. + * @internal ICU 2.4 + */ + static native void setOffset(int address, int offset); + + // BEGIN android-added + static String[] getAvailableLocalesImpl() { + int count = getAvailableLocalesCountImpl(); + String[] result = new String[count]; + + for(int i = 0; i < count; i++) { + result[i] = getAvailableLocalesImpl(i); + } + return result; + } + + private static native String getAvailableLocalesImpl(int i); + + private static native int getAvailableLocalesCountImpl(); + // END android-added +} diff --git a/icu/src/main/java/com/ibm/icu4jni/text/NativeDecimalFormat.java b/icu/src/main/java/com/ibm/icu4jni/text/NativeDecimalFormat.java new file mode 100644 index 0000000..39f7307 --- /dev/null +++ b/icu/src/main/java/com/ibm/icu4jni/text/NativeDecimalFormat.java @@ -0,0 +1,109 @@ +/* + * Copyright (C) 2008 The Android Open Source Project + * + * Licensed 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 com.ibm.icu4jni.text; + +import java.text.FieldPosition; +import java.text.ParsePosition; + +final class NativeDecimalFormat { + + enum UNumberFormatSymbol { + UNUM_DECIMAL_SEPARATOR_SYMBOL, + UNUM_GROUPING_SEPARATOR_SYMBOL, + UNUM_PATTERN_SEPARATOR_SYMBOL, + UNUM_PERCENT_SYMBOL, + UNUM_ZERO_DIGIT_SYMBOL, + UNUM_DIGIT_SYMBOL, + UNUM_MINUS_SIGN_SYMBOL, + UNUM_PLUS_SIGN_SYMBOL, + UNUM_CURRENCY_SYMBOL, + UNUM_INTL_CURRENCY_SYMBOL, + UNUM_MONETARY_SEPARATOR_SYMBOL, + UNUM_EXPONENTIAL_SYMBOL, + UNUM_PERMILL_SYMBOL, + UNUM_PAD_ESCAPE_SYMBOL, + UNUM_INFINITY_SYMBOL, + UNUM_NAN_SYMBOL, + UNUM_SIGNIFICANT_DIGIT_SYMBOL, + UNUM_MONETARY_GROUPING_SEPARATOR_SYMBOL, + UNUM_FORMAT_SYMBOL_COUNT + } + + enum UNumberFormatAttribute { + UNUM_PARSE_INT_ONLY, + UNUM_GROUPING_USED, + UNUM_DECIMAL_ALWAYS_SHOWN, + UNUM_MAX_INTEGER_DIGITS, + UNUM_MIN_INTEGER_DIGITS, + UNUM_INTEGER_DIGITS, + UNUM_MAX_FRACTION_DIGITS, + UNUM_MIN_FRACTION_DIGITS, + UNUM_FRACTION_DIGITS, + UNUM_MULTIPLIER, + UNUM_GROUPING_SIZE, + UNUM_ROUNDING_MODE, + UNUM_ROUNDING_INCREMENT, + UNUM_FORMAT_WIDTH, + UNUM_PADDING_POSITION, + UNUM_SECONDARY_GROUPING_SIZE, + UNUM_SIGNIFICANT_DIGITS_USED, + UNUM_MIN_SIGNIFICANT_DIGITS, + UNUM_MAX_SIGNIFICANT_DIGITS, + UNUM_LENIENT_PARSE + } + + enum UNumberFormatTextAttribute { + UNUM_POSITIVE_PREFIX, + UNUM_POSITIVE_SUFFIX, + UNUM_NEGATIVE_PREFIX, + UNUM_NEGATIVE_SUFFIX, + UNUM_PADDING_CHARACTER, + UNUM_CURRENCY_CODE, + UNUM_DEFAULT_RULESET, + UNUM_PUBLIC_RULESETS + } + + static native int openDecimalFormatImpl(String locale, String pattern); + + static native void closeDecimalFormatImpl(int addr); + + static native int cloneImpl(int addr); + + static native void setSymbol(int addr, int symbol, String str); + + static native String getSymbol(int addr, int symbol); + + static native void setAttribute(int addr, int symbol, int i); + + static native int getAttribute(int addr, int symbol); + + static native void setTextAttribute(int addr, int symbol, String str); + + static native String getTextAttribute(int addr, int symbol); + + static native void applyPatternImpl(int addr, boolean localized, String pattern); + + static native String toPatternImpl(int addr, boolean localized); + + static native String format(int addr, long value, FieldPosition position, String fieldType, StringBuffer attributes); + + static native String format(int addr, double value, FieldPosition position, String fieldType, StringBuffer attributes); + + static native String format(int addr, String value, FieldPosition position, String fieldType, StringBuffer attributes, int scale); + + static native Number parse(int addr, String string, ParsePosition position); +} diff --git a/icu/src/main/java/com/ibm/icu4jni/text/RuleBasedBreakIterator.java b/icu/src/main/java/com/ibm/icu4jni/text/RuleBasedBreakIterator.java new file mode 100644 index 0000000..4d38f2b --- /dev/null +++ b/icu/src/main/java/com/ibm/icu4jni/text/RuleBasedBreakIterator.java @@ -0,0 +1,133 @@ +/* + * Copyright (C) 2008 The Android Open Source Project + * + * Licensed 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 com.ibm.icu4jni.text; + +import java.text.CharacterIterator; +import java.text.StringCharacterIterator; + +public class RuleBasedBreakIterator extends BreakIterator { + + private CharacterIterator charIter; + + private int addr; + + RuleBasedBreakIterator(int iterAddr, int type) { + this.addr = iterAddr; + this.type = type; + this.charIter = new StringCharacterIterator(""); + } + + @Override + public Object clone() { + int cloneAddr = NativeBreakIterator.cloneImpl(this.addr); + RuleBasedBreakIterator rbbi = + new RuleBasedBreakIterator(cloneAddr, this.type); + + rbbi.charIter = this.charIter; + + return rbbi; + } + + @Override + public boolean equals(Object object) { + if(object == null) { + return false; + } + + if(!(object instanceof RuleBasedBreakIterator)) { + return false; + } + + CharacterIterator iter = ((RuleBasedBreakIterator) object).charIter; + + boolean result = this.type == ((RuleBasedBreakIterator) object).type; + + return result && iter.equals(this.charIter); + } + + @Override + public int current() { + return NativeBreakIterator.currentImpl(this.addr); + } + + @Override + public int first() { + return NativeBreakIterator.firstImpl(this.addr); + } + + @Override + public int following(int offset) { + return NativeBreakIterator.followingImpl(this.addr, offset); + } + + @Override + public CharacterIterator getText() { + int newLoc = NativeBreakIterator.currentImpl(this.addr); + this.charIter.setIndex(newLoc); + return this.charIter; + } + + @Override + public int last() { + return NativeBreakIterator.lastImpl(this.addr); + } + + @Override + public int next(int n) { + return NativeBreakIterator.nextImpl(this.addr, n); + } + + @Override + public int next() { + return NativeBreakIterator.nextImpl(this.addr, 1); + } + + @Override + public int previous() { + return NativeBreakIterator.previousImpl(this.addr); + } + + @Override + public void setText(CharacterIterator newText) { + this.charIter = newText; + + StringBuilder sb = new StringBuilder(); + + char c = newText.first(); + while(c != CharacterIterator.DONE) { + sb.append(c); + c = newText.next(); + } + + NativeBreakIterator.setTextImpl(this.addr, sb.toString()); + } + + protected void finalize() { + NativeBreakIterator.closeBreakIteratorImpl(this.addr); + } + + @Override + public boolean isBoundary(int offset) { + return NativeBreakIterator.isBoundaryImpl(this.addr, offset); + } + + @Override + public int preceding(int offset) { + return NativeBreakIterator.precedingImpl(this.addr, offset); + } + +} diff --git a/icu/src/main/java/com/ibm/icu4jni/text/RuleBasedCollator.java b/icu/src/main/java/com/ibm/icu4jni/text/RuleBasedCollator.java new file mode 100644 index 0000000..c0aca3b --- /dev/null +++ b/icu/src/main/java/com/ibm/icu4jni/text/RuleBasedCollator.java @@ -0,0 +1,719 @@ +/** +******************************************************************************* +* Copyright (C) 1996-2005, International Business Machines Corporation and * +* others. All Rights Reserved. * +******************************************************************************* +* +* +******************************************************************************* +*/ + +package com.ibm.icu4jni.text; + +import java.util.Locale; +import java.text.CharacterIterator; +import java.text.ParseException; +import com.ibm.icu4jni.common.ErrorCode; + +/** +* Concrete implementation class for Collation. +* <p> +* The collation table is composed of a list of collation rules, where each +* rule is of three forms: +* <pre> +* < modifier > +* < relation > < text-argument > +* < reset > < text-argument > +* </pre> +* <p> +* <code>RuleBasedCollator</code> has the following restrictions for efficiency +* (other subclasses may be used for more complex languages) : +* <ol> +* <li> If a French secondary ordering is specified it applies to the whole +* collator object. +* <li> All non-mentioned Unicode characters are at the end of the collation +* order. +* <li> If a character is not located in the RuleBasedCollator, the default +* Unicode Collation Algorithm (UCA) rulebased table is automatically +* searched as a backup. +* </ol> +* +* The following demonstrates how to create your own collation rules: +* <UL Type=disc> +* <LI><strong>Text-Argument</strong>: A text-argument is any sequence of +* characters, excluding special characters (that is, common whitespace +* characters [0009-000D, 0020] and rule syntax characters [0021-002F, +* 003A-0040, 005B-0060, 007B-007E]). If those characters are desired, +* you can put them in single quotes (e.g. ampersand => '&'). Note that +* unquoted white space characters are ignored; e.g. <code>b c</code> is +* treated as <code>bc</code>. +* <LI><strong>Modifier</strong>: There is a single modifier which is used +* to specify that all accents (secondary differences) are backwards. +* <p>'@' : Indicates that accents are sorted backwards, as in French. +* <LI><strong>Relation</strong>: The relations are the following: +* <UL Type=square> +* <LI>'<' : Greater, as a letter difference (primary) +* <LI>';' : Greater, as an accent difference (secondary) +* <LI>',' : Greater, as a case difference (tertiary) +* <LI>'=' : Equal +* </UL> +* <LI><strong>Reset</strong>: There is a single reset which is used +* primarily for contractions and expansions, but which can also be used +* to add a modification at the end of a set of rules. +* <p>'&' : Indicates that the next rule follows the position to where +* the reset text-argument would be sorted. +* </UL> +* +* <p> +* This sounds more complicated than it is in practice. For example, the +* following are equivalent ways of expressing the same thing: +* <blockquote> +* <pre> +* a < b < c +* a < b & b < c +* a < c & a < b +* </pre> +* </blockquote> +* Notice that the order is important, as the subsequent item goes immediately +* after the text-argument. The following are not equivalent: +* <blockquote> +* <pre> +* a < b & a < c +* a < c & a < b +* </pre> +* </blockquote> +* Either the text-argument must already be present in the sequence, or some +* initial substring of the text-argument must be present. (e.g. "a < b & ae < +* e" is valid since "a" is present in the sequence before "ae" is reset). In +* this latter case, "ae" is not entered and treated as a single character; +* instead, "e" is sorted as if it were expanded to two characters: "a" +* followed by an "e". This difference appears in natural languages: in +* traditional Spanish "ch" is treated as though it contracts to a single +* character (expressed as "c < ch < d"), while in traditional German a-umlaut +* is treated as though it expanded to two characters (expressed as "a,A < b,B +* ... & ae;? & AE;?"). [? and ? are, of course, the escape sequences for +* a-umlaut.] +* <p> +* <strong>Ignorable Characters</strong> +* <p> +* For ignorable characters, the first rule must start with a relation (the +* examples we have used above are really fragments; "a < b" really should be +* "< a < b"). If, however, the first relation is not "<", then all the all +* text-arguments up to the first "<" are ignorable. For example, ", - < a < b" +* makes "-" an ignorable character, as we saw earlier in the word +* "black-birds". In the samples for different languages, you see that most +* accents are ignorable. +* +* <p><strong>Normalization and Accents</strong> +* <p> +* <code>RuleBasedCollator</code> automatically processes its rule table to +* include both pre-composed and combining-character versions of accented +* characters. Even if the provided rule string contains only base characters +* and separate combining accent characters, the pre-composed accented +* characters matching all canonical combinations of characters from the rule +* string will be entered in the table. +* <p> +* This allows you to use a RuleBasedCollator to compare accented strings even +* when the collator is set to NO_DECOMPOSITION. However, if the strings to be +* collated contain combining sequences that may not be in canonical order, you +* should set the collator to CANONICAL_DECOMPOSITION to enable sorting of +* combining sequences. +* For more information, see +* <A HREF="http://www.aw.com/devpress">The Unicode Standard, Version 3.0</A>.) +* +* <p><strong>Errors</strong> +* <p> +* The following are errors: +* <UL Type=disc> +* <LI>A text-argument contains unquoted punctuation symbols +* (e.g. "a < b-c < d"). +* <LI>A relation or reset character not followed by a text-argument +* (e.g. "a < , b"). +* <LI>A reset where the text-argument (or an initial substring of the +* text-argument) is not already in the sequence or allocated in the +* default UCA table. +* (e.g. "a < b & e < f") +* </UL> +* If you produce one of these errors, a <code>RuleBasedCollator</code> throws +* a <code>ParseException</code>. +* +* <p><strong>Examples</strong> +* <p>Simple: "< a < b < c < d" +* <p>Norwegian: "< a,A< b,B< c,C< d,D< e,E< f,F< g,G< h,H< i,I< j,J +* < k,K< l,L< m,M< n,N< o,O< p,P< q,Q< r,R< s,S< t,T +* < u,U< v,V< w,W< x,X< y,Y< z,Z +* < ?=a?,?=A? +* ;aa,AA< ?,?< ?,?" +* +* <p> +* Normally, to create a rule-based Collator object, you will use +* <code>Collator</code>'s factory method <code>getInstance</code>. +* However, to create a rule-based Collator object with specialized rules +* tailored to your needs, you construct the <code>RuleBasedCollator</code> +* with the rules contained in a <code>String</code> object. For example: +* <blockquote> +* <pre> +* String Simple = "< a < b < c < d"; +* RuleBasedCollator mySimple = new RuleBasedCollator(Simple); +* </pre> +* </blockquote> +* Or: +* <blockquote> +* <pre> +* String Norwegian = "< a,A< b,B< c,C< d,D< e,E< f,F< g,G< h,H< i,I< j,J" + +* "< k,K< l,L< m,M< n,N< o,O< p,P< q,Q< r,R< s,S< t,T" + +* "< u,U< v,V< w,W< x,X< y,Y< z,Z" + +* "< ?=a?,?=A?" + +* ";aa,AA< ?,?< ?,?"; +* RuleBasedCollator myNorwegian = new RuleBasedCollator(Norwegian); +* </pre> +* </blockquote> +* +* <p> +* Combining <code>Collator</code>s is as simple as concatenating strings. +* Here's an example that combines two <code>Collator</code>s from two +* different locales: +* <blockquote> +* <pre> +* // Create an en_US Collator object +* RuleBasedCollator en_USCollator = (RuleBasedCollator) +* Collator.getInstance(new Locale("en", "US", "")); +* // Create a da_DK Collator object +* RuleBasedCollator da_DKCollator = (RuleBasedCollator) +* Collator.getInstance(new Locale("da", "DK", "")); +* // Combine the two +* // First, get the collation rules from en_USCollator +* String en_USRules = en_USCollator.getRules(); +* // Second, get the collation rules from da_DKCollator +* String da_DKRules = da_DKCollator.getRules(); +* RuleBasedCollator newCollator = +* new RuleBasedCollator(en_USRules + da_DKRules); +* // newCollator has the combined rules +* </pre> +* </blockquote> +* +* <p> +* Another more interesting example would be to make changes on an existing +* table to create a new <code>Collator</code> object. For example, add +* "& C < ch, cH, Ch, CH" to the <code>en_USCollator</code> object to create +* your own: +* <blockquote> +* <pre> +* // Create a new Collator object with additional rules +* String addRules = "& C < ch, cH, Ch, CH"; +* RuleBasedCollator myCollator = +* new RuleBasedCollator(en_USCollator + addRules); +* // myCollator contains the new rules +* </pre> +* </blockquote> +* +* <p> +* The following example demonstrates how to change the order of +* non-spacing accents, +* <blockquote> +* <pre> +* // old rule +* String oldRules = "=?;?;?" // main accents Diaeresis 00A8, Macron 00AF +* // Acute 00BF +* + "< a , A ; ae, AE ; ? , ?" +* + "< b , B < c, C < e, E & C < d, D"; +* // change the order of accent characters +* String addOn = "& ?;?;?;"; // Acute 00BF, Macron 00AF, Diaeresis 00A8 +* RuleBasedCollator myCollator = new RuleBasedCollator(oldRules + addOn); +* </pre> +* </blockquote> +* +* <p> +* The last example shows how to put new primary ordering in before the +* default setting. For example, in Japanese <code>Collator</code>, you +* can either sort English characters before or after Japanese characters, +* <blockquote> +* <pre> +* // get en_US Collator rules +* RuleBasedCollator en_USCollator = +* (RuleBasedCollator)Collator.getInstance(Locale.US); +* // add a few Japanese character to sort before English characters +* // suppose the last character before the first base letter 'a' in +* // the English collation rule is ? +* String jaString = "& \\u30A2 , \\u30FC < \\u30C8"; +* RuleBasedCollator myJapaneseCollator = new +* RuleBasedCollator(en_USCollator.getRules() + jaString); +* </pre> +* </blockquote> +* <P> +* @author syn wee quek +* @stable ICU 2.4 +*/ + +public final class RuleBasedCollator extends Collator +{ + // public constructors ------------------------------------------ + + /** + * RuleBasedCollator constructor. This takes the table rules and builds a + * collation table out of them. Please see RuleBasedCollator class + * description for more details on the collation rule syntax. + * @param rules the collation rules to build the collation table from. + * @exception ParseException thrown if rules are empty or a Runtime error + * if collator can not be created. + * @stable ICU 2.4 + */ + public RuleBasedCollator(String rules) throws ParseException + { + // BEGIN android-changed + if (rules == null) { + throw new NullPointerException(); + } + // if (rules.length() == 0) + // throw new ParseException("Build rules empty.", 0); + // END android-changed + m_collator_ = NativeCollation.openCollatorFromRules(rules, + CollationAttribute.VALUE_OFF, + CollationAttribute.VALUE_DEFAULT_STRENGTH); + } + + /** + * RuleBasedCollator constructor. This takes the table rules and builds a + * collation table out of them. Please see RuleBasedCollator class + * description for more details on the collation rule syntax. + * @param rules the collation rules to build the collation table from. + * @param strength collation strength + * @exception ParseException thrown if rules are empty or a Runtime error + * if collator can not be created. + * @see #PRIMARY + * @see #SECONDARY + * @see #TERTIARY + * @see #QUATERNARY + * @see #IDENTICAL + * @stable ICU 2.4 + */ + public RuleBasedCollator(String rules, int strength) throws ParseException + { + // BEGIN android-changed + if (rules == null) { + throw new NullPointerException(); + } + // if (rules.length() == 0) + // throw new ParseException("Build rules empty.", 0); + // END android-changed + if (!CollationAttribute.checkStrength(strength)) + throw ErrorCode.getException(ErrorCode.U_ILLEGAL_ARGUMENT_ERROR); + + m_collator_ = NativeCollation.openCollatorFromRules(rules, + CollationAttribute.VALUE_OFF, + strength); + } + + /** + * RuleBasedCollator constructor. This takes the table rules and builds a + * collation table out of them. Please see RuleBasedCollator class + * description for more details on the collation rule syntax. + * <p>Note API change starting from release 2.4. Prior to release 2.4, the + * normalizationmode argument values are from the class + * com.ibm.icu4jni.text.Normalization. In 2.4, + * the valid normalizationmode arguments for this API are + * CollationAttribute.VALUE_ON and CollationAttribute.VALUE_OFF. + * </p> + * @param rules the collation rules to build the collation table from. + * @param strength collation strength + * @param normalizationmode normalization mode + * @exception IllegalArgumentException thrown when constructor error occurs + * @see #PRIMARY + * @see #SECONDARY + * @see #TERTIARY + * @see #QUATERNARY + * @see #IDENTICAL + * @see #CANONICAL_DECOMPOSITION + * @see #NO_DECOMPOSITION + * @stable ICU 2.4 + */ + public RuleBasedCollator(String rules, int normalizationmode, int strength) + { + // BEGIN android-added + if (rules == null) { + throw new NullPointerException(); + } + // END android-added + if (!CollationAttribute.checkStrength(strength) || + !CollationAttribute.checkNormalization(normalizationmode)) { + throw ErrorCode.getException(ErrorCode.U_ILLEGAL_ARGUMENT_ERROR); + } + + m_collator_ = NativeCollation.openCollatorFromRules(rules, + normalizationmode, strength); + } + + // public methods ----------------------------------------------- + + /** + * Makes a complete copy of the current object. + * @return a copy of this object if data clone is a success, otherwise null + * @stable ICU 2.4 + */ + public Object clone() + { + RuleBasedCollator result = null; + int collatoraddress = NativeCollation.safeClone(m_collator_); + result = new RuleBasedCollator(collatoraddress); + return (Collator)result; + } + + /** + * The comparison function compares the character data stored in two + * different strings. Returns information about whether a string is less + * than, greater than or equal to another string. + * <p>Example of use: + * <br> + * <code> + * Collator myCollation = Collator.createInstance(Locale::US); + * myCollation.setStrength(CollationAttribute.VALUE_PRIMARY); + * // result would be Collator.RESULT_EQUAL ("abc" == "ABC") + * // (no primary difference between "abc" and "ABC") + * int result = myCollation.compare("abc", "ABC",3); + * myCollation.setStrength(CollationAttribute.VALUE_TERTIARY); + * // result would be Collation::LESS (abc" <<< "ABC") + * // (with tertiary difference between "abc" and "ABC") + * int result = myCollation.compare("abc", "ABC",3); + * </code> + * @param source The source string. + * @param target The target string. + * @return result of the comparison, Collator.RESULT_EQUAL, + * Collator.RESULT_GREATER or Collator.RESULT_LESS + * @stable ICU 2.4 + */ + public int compare(String source, String target) + { + return NativeCollation.compare(m_collator_, source, target); + } + + /** + * Get the normalization mode for this object. + * The normalization mode influences how strings are compared. + * @see #CANONICAL_DECOMPOSITION + * @see #NO_DECOMPOSITION + * @stable ICU 2.4 + */ + public int getDecomposition() + { + return NativeCollation.getNormalization(m_collator_); + } + + /** + * <p>Sets the decomposition mode of the Collator object on or off. + * If the decomposition mode is set to on, string would be decomposed into + * NFD format where necessary before sorting.</p> + * </p> + * @param decompositionmode the new decomposition mode + * @see #CANONICAL_DECOMPOSITION + * @see #NO_DECOMPOSITION + * @stable ICU 2.4 + */ + public void setDecomposition(int decompositionmode) + { + if (!CollationAttribute.checkNormalization(decompositionmode)) + throw ErrorCode.getException(ErrorCode.U_ILLEGAL_ARGUMENT_ERROR); + NativeCollation.setAttribute(m_collator_, + CollationAttribute.NORMALIZATION_MODE, + decompositionmode); + } + + /** + * Determines the minimum strength that will be use in comparison or + * transformation. + * <p> + * E.g. with strength == CollationAttribute.VALUE_SECONDARY, the tertiary difference + * is ignored + * </p> + * <p> + * E.g. with strength == PRIMARY, the secondary and tertiary difference are + * ignored. + * </p> + * @return the current comparison level. + * @see #PRIMARY + * @see #SECONDARY + * @see #TERTIARY + * @see #QUATERNARY + * @see #IDENTICAL + * @stable ICU 2.4 + */ + public int getStrength() + { + return NativeCollation.getAttribute(m_collator_, + CollationAttribute.STRENGTH); + } + + /** + * Sets the minimum strength to be used in comparison or transformation. + * <p>Example of use: + * <br> + * <code> + * Collator myCollation = Collator.createInstance(Locale::US); + * myCollation.setStrength(PRIMARY); + * // result will be "abc" == "ABC" + * // tertiary differences will be ignored + * int result = myCollation->compare("abc", "ABC"); + * </code> + * @param strength the new comparison level. + * @exception IllegalArgumentException when argument does not belong to any collation strength + * mode or error occurs while setting data. + * @see #PRIMARY + * @see #SECONDARY + * @see #TERTIARY + * @see #QUATERNARY + * @see #IDENTICAL + * @stable ICU 2.4 + */ + public void setStrength(int strength) + { + if (!CollationAttribute.checkStrength(strength)) + throw ErrorCode.getException(ErrorCode.U_ILLEGAL_ARGUMENT_ERROR); + NativeCollation.setAttribute(m_collator_, CollationAttribute.STRENGTH, + strength); + } + + /** + * Sets the attribute to be used in comparison or transformation. + * <p>Example of use: + * <br> + * <code> + * Collator myCollation = Collator.createInstance(Locale::US); + * myCollation.setAttribute(CollationAttribute.CASE_LEVEL, + * CollationAttribute.VALUE_ON); + * int result = myCollation->compare("\\u30C3\\u30CF", + * "\\u30C4\\u30CF"); + * // result will be Collator.RESULT_LESS. + * </code> + * @param type the attribute to be set from CollationAttribute + * @param value attribute value from CollationAttribute + * @stable ICU 2.4 + */ + public void setAttribute(int type, int value) + { + if (!CollationAttribute.checkAttribute(type, value)) + throw ErrorCode.getException(ErrorCode.U_ILLEGAL_ARGUMENT_ERROR); + NativeCollation.setAttribute(m_collator_, type, value); + } + + /** + * Gets the attribute to be used in comparison or transformation. + * @param type the attribute to be set from CollationAttribute + * @return value attribute value from CollationAttribute + * @stable ICU 2.4 + */ + public int getAttribute(int type) + { + if (!CollationAttribute.checkType(type)) + throw ErrorCode.getException(ErrorCode.U_ILLEGAL_ARGUMENT_ERROR); + return NativeCollation.getAttribute(m_collator_, type); + } + + /** + * Get the sort key as an CollationKey object from the argument string. + * To retrieve sort key in terms of byte arrays, use the method as below<br> + * <br> + * <code> + * Collator collator = Collator.getInstance(); + * byte[] array = collator.getSortKey(source); + * </code><br> + * Byte array result are zero-terminated and can be compared using + * java.util.Arrays.equals(); + * @param source string to be processed. + * @return the sort key + * @stable ICU 2.4 + */ + public CollationKey getCollationKey(String source) + { + // BEGIN android-removed + // return new CollationKey(NativeCollation.getSortKey(m_collator_, source)); + // END android-removed + // BEGIN android-added + if(source == null) { + return null; + } + byte[] key = NativeCollation.getSortKey(m_collator_, source); + if(key == null) { + return null; + } + return new CollationKey(key); + // END android-added + } + + /** + * Get a sort key for the argument string + * Sort keys may be compared using java.util.Arrays.equals + * @param source string for key to be generated + * @return sort key + * @stable ICU 2.4 + */ + public byte[] getSortKey(String source) + { + return NativeCollation.getSortKey(m_collator_, source); + } + + /** + * Get the collation rules of this Collation object + * The rules will follow the rule syntax. + * @return collation rules. + * @stable ICU 2.4 + */ + public String getRules() + { + return NativeCollation.getRules(m_collator_); + } + + /** + * Create a CollationElementIterator object that will iterator over the + * elements in a string, using the collation rules defined in this + * RuleBasedCollator + * @param source string to iterate over + * @return address of C collationelement + * @exception IllegalArgumentException thrown when error occurs + * @stable ICU 2.4 + */ + public CollationElementIterator getCollationElementIterator(String source) + { + CollationElementIterator result = new CollationElementIterator( + NativeCollation.getCollationElementIterator(m_collator_, source)); + // result.setOwnCollationElementIterator(true); + return result; + } + + // BEGIN android-added + /** + * Create a CollationElementIterator object that will iterator over the + * elements in a string, using the collation rules defined in this + * RuleBasedCollator + * @param source string to iterate over + * @return address of C collationelement + * @exception IllegalArgumentException thrown when error occurs + * @stable ICU 2.4 + */ + public CollationElementIterator getCollationElementIterator( + CharacterIterator source) + { + CollationElementIterator result = new CollationElementIterator( + NativeCollation.getCollationElementIterator(m_collator_, + source.toString())); + // result.setOwnCollationElementIterator(true); + return result; + } + // END android-added + + /** + * Returns a hash of this collation object + * Note this method is not complete, it only returns 0 at the moment. + * @return hash of this collation object + * @stable ICU 2.4 + */ + public int hashCode() + { + // since rules do not change once it is created, we can cache the hash + if (m_hashcode_ == 0) { + m_hashcode_ = NativeCollation.hashCode(m_collator_); + if (m_hashcode_ == 0) + m_hashcode_ = 1; + } + return m_hashcode_; + } + + /** + * Checks if argument object is equals to this object. + * @param target object + * @return true if source is equivalent to target, false otherwise + * @stable ICU 2.4 + */ + public boolean equals(Object target) + { + if (this == target) + return true; + if (target == null) + return false; + if (getClass() != target.getClass()) + return false; + + RuleBasedCollator tgtcoll = (RuleBasedCollator)target; + return getRules().equals(tgtcoll.getRules()) && + getStrength() == tgtcoll.getStrength() && + getDecomposition() == tgtcoll.getDecomposition(); + } + + // package constructor ---------------------------------------- + + /** + * RuleBasedCollator default constructor. This constructor takes the default + * locale. The only caller of this class should be Collator.getInstance(). + * Current implementation createInstance() returns a RuleBasedCollator(Locale) + * instance. The RuleBasedCollator will be created in the following order, + * <ul> + * <li> Data from argument locale resource bundle if found, otherwise + * <li> Data from parent locale resource bundle of arguemtn locale if found, + * otherwise + * <li> Data from built-in default collation rules if found, other + * <li> null is returned + * </ul> + */ + RuleBasedCollator() + { + m_collator_ = NativeCollation.openCollator(); + } + + /** + * RuleBasedCollator constructor. This constructor takes a locale. The + * only caller of this class should be Collator.createInstance(). + * Current implementation createInstance() returns a RuleBasedCollator(Locale) + * instance. The RuleBasedCollator will be created in the following order, + * <ul> + * <li> Data from argument locale resource bundle if found, otherwise + * <li> Data from parent locale resource bundle of arguemtn locale if found, + * otherwise + * <li> Data from built-in default collation rules if found, other + * <li> null is returned + * </ul> + * @param locale locale used + */ + RuleBasedCollator(Locale locale) + { + if (locale == null) { + m_collator_ = NativeCollation.openCollator(); + } + else { + m_collator_ = NativeCollation.openCollator(locale.toString()); + } + } + + // protected methods -------------------------------------------- + + /** + * Garbage collection. + * Close C collator and reclaim memory. + */ + protected void finalize() + { + NativeCollation.closeCollator(m_collator_); + } + + // private data members ----------------------------------------- + + /** + * C collator + */ + private int m_collator_; + + /** + * Hash code for rules + */ + private int m_hashcode_ = 0; + + // private constructor ----------------------------------------- + + /** + * Private use constructor. + * Does not create any instance of the C collator. Accepts argument as the + * C collator for new instance. + * @param collatoraddress address of C collator + */ + private RuleBasedCollator(int collatoraddress) + { + m_collator_ = collatoraddress; + } +} diff --git a/icu/src/main/java/com/ibm/icu4jni/text/RuleBasedNumberFormat.java b/icu/src/main/java/com/ibm/icu4jni/text/RuleBasedNumberFormat.java new file mode 100644 index 0000000..3c865d8 --- /dev/null +++ b/icu/src/main/java/com/ibm/icu4jni/text/RuleBasedNumberFormat.java @@ -0,0 +1,258 @@ +/* + * Copyright (C) 2008 The Android Open Source Project + * + * Licensed 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 com.ibm.icu4jni.text; + +import java.text.FieldPosition; +import java.text.Format; +import java.text.NumberFormat; +import java.text.ParsePosition; +import java.util.Locale; + +public class RuleBasedNumberFormat extends NumberFormat { + + /** + * Enum of predefined RBNF types. + */ + public enum RBNFType { + /** + * This creates a spellout instance of RBNF. + * It formats numbers into textual representation: + * 15 -> 'fifteen' or 15.15 -> 'fifteen point one five' + * and it can parse words into numbers: 'twenty' -> 20 + */ + SPELLOUT(0), + /** + * This creates an ordinal instance of RBNF. + * It formats numbers into an ordinal text representation: + * 15 -> '15th' and by parsing it also works in the other direction. + */ + ORDINAL(1), + /** + * This creates instance of RBNF that allows to format numbers into time + * values: 15 -> '15 sec.' and by parsing it also works in the other + * direction. + */ + DURATION(2); + + int type; + + RBNFType(int t) { + type = t; + } + + int getType() { + return type; + } + } + + @Override + protected void finalize(){ + close(); + } + + private int addr = 0; + + /** + * Open a new rule based number format of selected type for the + * default location + * + * @param type the type of rule based number format + */ + public void open(RBNFType type) { + this.addr = openRBNFImpl(type.getType(), + Locale.getDefault().toString()); + } + + /** + * Open a new rule based number format of selected type for the + * given location + * + * @param type the type of rule based number format + * @param locale the locale to use for this rule based number format + */ + public void open(RBNFType type, Locale locale) { + String loc = locale.toString(); + if (loc == null) { + throw new NullPointerException(); + } + this.addr = openRBNFImpl(type.getType(), loc); + } + + private static native int openRBNFImpl(int type, String loc); + + /** + * Open a new rule based number format for the + * default location. The rule passed to the method has to be of the form + * described in the ibm icu documentation for RuleBasedNumberFormat. + * + * @param rule the rule for the rule based number format + */ + public void open(String rule) { + if (rule == null) { + throw new NullPointerException(); + } + this.addr = openRBNFImpl(rule, Locale.getDefault().toString()); + } + + /** + * Open a new rule based number format for the + * given location. The rule passed to the method has to be of the form + * described in the ibm icu documentation for RuleBasedNumberFormat. + * + * @param rule the rule for the rule based number format + * @param locale the locale to use for this rule based number format + */ + public void open(String rule, Locale locale) { + String loc = locale.toString(); + if (loc == null || rule == null) { + throw new NullPointerException(); + } + this.addr = openRBNFImpl(rule, locale.toString()); + } + + private static native int openRBNFImpl(String rule, String loc); + + /** + * close a RuleBasedNumberFormat + */ + public void close() { + if(this.addr != 0) { + closeRBNFImpl(this.addr); + this.addr = 0; + } + } + + private static native void closeRBNFImpl(int addr); + + @Override + public StringBuffer format(long value, StringBuffer buffer, FieldPosition field) { + + if(buffer == null) { + throw new NullPointerException(); + } + + String fieldType = null; + + if(field != null) { + fieldType = getFieldType(field.getFieldAttribute()); + } + + String result = formatRBNFImpl(this.addr, value, field, + fieldType, null); + + buffer.append(result.toCharArray(), 0, result.length()); + + return buffer; + } + + private static native String formatRBNFImpl(int addr, long value, + FieldPosition field, String fieldType, StringBuffer buffer); + + @Override + public StringBuffer format(double value, StringBuffer buffer, FieldPosition field) { + + if(buffer == null) { + throw new NullPointerException(); + } + + String fieldType = null; + + if(field != null) { + fieldType = getFieldType(field.getFieldAttribute()); + } + + String result = formatRBNFImpl(this.addr, value, field, + fieldType, null); + + buffer.append(result.toCharArray(), 0, result.length()); + + return buffer; + } + + private static native String formatRBNFImpl(int addr, double value, + FieldPosition field, String fieldType, StringBuffer buffer); + + @Override + public Number parse(String string, ParsePosition position) { + if (string == null || position == null) { + throw new NullPointerException(); + } + return parseRBNFImpl(this.addr, string, position, false); + } + + /** + * This method has the same functionality + * as {@link #parse(String, ParsePosition)} + * But it uses lenient parsing. This means it also accepts strings that + * differ from the correct writing (e.g. case or umlaut differences). + * + * @param string the string to parse + * @param position the ParsePosition, updated on return with the index + * following the parsed text, or on error the index is unchanged and + * the error index is set to the index where the error occurred + * @return the Number resulting from the parse, or null if there is an error + */ + public Number parseLenient(String string, ParsePosition position) { + if (string == null || position == null) { + throw new NullPointerException(); + } + return parseRBNFImpl(this.addr, string, position, true); + } + + static native Number parseRBNFImpl(int addr, String string, ParsePosition position, boolean lenient); + + + static private String getFieldType(Format.Field field) { + if(field == null) { + return null; + } + if(field.equals(NumberFormat.Field.SIGN)) { + return "sign"; + } + if(field.equals(NumberFormat.Field.INTEGER)) { + return "integer"; + } + if(field.equals(NumberFormat.Field.FRACTION)) { + return "fraction"; + } + if(field.equals(NumberFormat.Field.EXPONENT)) { + return "exponent"; + } + if(field.equals(NumberFormat.Field.EXPONENT_SIGN)) { + return "exponent_sign"; + } + if(field.equals(NumberFormat.Field.EXPONENT_SYMBOL)) { + return "exponent_symbol"; + } + if(field.equals(NumberFormat.Field.CURRENCY)) { + return "currency"; + } + if(field.equals(NumberFormat.Field.GROUPING_SEPARATOR)) { + return "grouping_separator"; + } + if(field.equals(NumberFormat.Field.DECIMAL_SEPARATOR)) { + return "decimal_separator"; + } + if(field.equals(NumberFormat.Field.PERCENT)) { + return "percent"; + } + if(field.equals(NumberFormat.Field.PERMILLE)) { + return "permille"; + } + return null; + } +} diff --git a/icu/src/main/java/com/ibm/icu4jni/util/Resources.java b/icu/src/main/java/com/ibm/icu4jni/util/Resources.java new file mode 100644 index 0000000..85530ac --- /dev/null +++ b/icu/src/main/java/com/ibm/icu4jni/util/Resources.java @@ -0,0 +1,533 @@ +/* + * Copyright (C) 2008 The Android Open Source Project + * + * Licensed 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 com.ibm.icu4jni.util; + +import java.util.Enumeration; +import java.util.ListResourceBundle; +import java.util.MissingResourceException; +import java.util.ResourceBundle; +import java.util.TimeZone; +import java.util.logging.Logger; + +/** + * Helper class that delivers ResourceBundle instances expected by Harmony, but + * with the data taken from ICU's database. This approach has a couple of + * advantages: + * <ol> + * <li> We have less classes in the overall system, since we use different + * instances for different ResourceBundles. + * <li> We don't have these classes that consists of monstrous static arrays + * with anymore. + * <li> We have control over which values we load at which time or even cache + * for later use. + * <li> There is only one central place left in the system where I18N data needs + * to be configured, namely ICU. + * </ol> + * Since we're mimicking the original Harmony ResourceBundle structures, most of + * the Harmony code can stay the same. We basically just need to change the + * ResourceBundle instantiation. Only the special case of the Locale bundles + * needs some more tweaking, since we don't want to keep several hundred + * timezone names in memory. + */ +public class Resources { + + /** + * Cache for ISO language names. + */ + private static String[] isoLanguages = null; + + /** + * Cache for ISO country names. + */ + private static String[] isoCountries = null; + + /** + * Available locales cache. + */ + private static String[] availableLocales = null; + + /** + * Available timezones cache. + */ + private static String[] availableTimezones = null; + + /** + * Creates ResourceBundle instance and fills it with ICU data. + * + * @param bundleName The name of the requested Harmony resource bundle, + * excluding the package name. + * @param locale The locale to use for the resources. A null value denotes + * the default locale as configured in Java. + * @return The new ResourceBundle, or null, if no ResourceBundle was + * created. + */ + public static ResourceBundle getInstance(String bundleName, String locale) { + if (locale == null) { + locale = java.util.Locale.getDefault().toString(); + } + + if (bundleName.startsWith("Locale")) { + return new Locale(locale); + } else if (bundleName.startsWith("Country")) { + return new Country(locale); + } else if (bundleName.startsWith("Currency")) { + return new Currency(locale); + } else if (bundleName.startsWith("Language")) { + return new Language(locale); + } else if (bundleName.startsWith("Variant")) { + return new Variant(locale); + } else if (bundleName.equals("ISO3Countries")) { + return new ISO3Countries(); + } else if (bundleName.equals("ISO3Languages")) { + return new ISO3Languages(); + } else if (bundleName.equals("ISO4CurrenciesToDigits")) { + return new ISO4CurrenciesToDigits(); + } else if (bundleName.equals("ISO4Currencies")) { + return new ISO4Currencies(); + } + + return null; + } + + /** + * Returns an array of ISO language names (two-letter codes), fetched either + * from ICU's database or from our memory cache. + * + * @return The array. + */ + public static String[] getISOLanguages() { + if (isoLanguages == null) { + isoLanguages = getISOLanguagesNative(); + } + + return isoLanguages; + } + + /** + * Returns an array of ISO country names (two-letter codes), fetched either + * from ICU's database or from our memory cache. + * + * @return The array. + */ + public static String[] getISOCountries() { + if (isoCountries == null) { + isoCountries = getISOCountriesNative(); + } + + return isoCountries; + } + + /** + * Returns an array of names of locales that are available in the system, + * fetched either from ICU's database or from our memory cache. + * + * @return The array. + */ + public static String[] getAvailableLocales() { + if (availableLocales == null) { + availableLocales = getAvailableLocalesNative(); + } + + return availableLocales; + } + + /** + * Returns an array of names of timezones that are available in the system, + * fetched either from the TimeZone class or from our memory cache. + * + * @return The array. + */ + public static String[] getKnownTimezones() { + // TODO Drop the Linux ZoneInfo stuff in favor of ICU. + if (availableTimezones == null) { + availableTimezones = TimeZone.getAvailableIDs(); + } + + return availableTimezones; + } + + /** + * Returns the display name for the given time zone using the given locale. + * + * @param id The time zone ID, for example "Europe/Berlin" + * @param isDST Indicates whether daylight savings is in use + * @param style The style, 0 for long, 1 for short + * @param locale The locale name, for example "en_US". + * @return The desired display name + */ + public static String getDisplayTimeZone(String id, boolean isDST, int style, String locale) { + return getDisplayTimeZoneNative(id, isDST, style, locale); + } + + /** + * Gets the name of the default locale. + */ + private static String getDefaultLocaleName() { + return java.util.Locale.getDefault().toString(); + } + + /** + * Name of default locale at the time this class was initialized. + */ + private static final String initialLocale = getDefaultLocaleName(); + + /** + * Names of time zones for the default locale. + */ + private static String[][] defaultTimezoneNames = null; + + /** + * Creates array of time zone names for the given locale. This method takes + * about 2s to run on a 400mhz ARM11. + */ + private static String[][] createTimeZoneNamesFor(String locale) { + long start = System.currentTimeMillis(); + + /* + * The following code is optimized for fast native response (the time a + * method call can be in native code is limited). It prepares an empty + * array to keep native code from having to create new Java objects. It + * also fill in the time zone IDs to speed things up a bit. There's one + * array for each time zone name type. (standard/long, standard/short, + * daylight/long, daylight/short) The native method that fetches these + * strings is faster if it can do all entries of one type, before having + * to change to the next type. That's why the array passed down to + * native has 5 entries, each providing space for all time zone names of + * one type. Likely this access to the fields is much faster in the + * native code because there's less array access overhead. + */ + String[][] arrayToFill = new String[5][]; + arrayToFill[0] = getKnownTimezones(); + arrayToFill[1] = new String[availableTimezones.length]; + arrayToFill[2] = new String[availableTimezones.length]; + arrayToFill[3] = new String[availableTimezones.length]; + arrayToFill[4] = new String[availableTimezones.length]; + + /* + * Fill in the zone names in native. + */ + getTimeZonesNative(arrayToFill, locale); + + /* + * Finally we need to reorder the entries so we get the expected result. + */ + String[][] result = new String[availableTimezones.length][5]; + for (int i = 0; i < availableTimezones.length; i++) { + result[i][0] = arrayToFill[0][i]; + result[i][1] = arrayToFill[1][i]; + result[i][2] = arrayToFill[2][i]; + result[i][3] = arrayToFill[3][i]; + result[i][4] = arrayToFill[4][i]; + } + + Logger.getLogger(Resources.class.getSimpleName()).info( + "Loaded time zone names for " + locale + " in " + + (System.currentTimeMillis() - start) + "ms."); + + return result; + } + + /** + * Returns the display names for all given timezones using the given locale. + * + * @return An array of time zone strings. Each row represents one time zone. + * The first columns holds the ID of the time zone, for example + * "Europe/Berlin". The other columns then hold for each row the + * four time zone names with and without daylight savings and in + * long and short format. It's exactly the array layout required by + * the TomeZone class. + */ + public static String[][] getDisplayTimeZones(String locale) { + // Note: Defer loading DefaultTimeZones as long as possible. + + String defaultLocaleName = getDefaultLocaleName(); + if (locale == null) { + locale = defaultLocaleName; + } + + // If locale == default and the default locale hasn't changed since + // DefaultTimeZones loaded, return the cached names. + // TODO: We should force a reboot if the default locale changes. + if (defaultLocaleName.equals(locale) + && initialLocale.equals(defaultLocaleName)) { + if (defaultTimezoneNames == null) { + defaultTimezoneNames = createTimeZoneNamesFor(locale); + } + return defaultTimezoneNames; + } + + return createTimeZoneNamesFor(locale); + } + + // --- Specialized ResourceBundle subclasses ------------------------------ + + /** + * Internal ResourceBundle mimicking the Harmony "ISO3Countries" bundle. + * Keys are the two-letter ISO country codes. Values are the three-letter + * ISO country abbreviations. An example entry is "US"->"USA". + */ + private static final class ISO3Countries extends ResourceBundle { + + @Override + public Enumeration<String> getKeys() { + // Won't get used + throw new UnsupportedOperationException(); + } + + @Override + protected Object handleGetObject(String key) { + return getISO3CountryNative(key); + } + + } + + /** + * Internal ResourceBundle mimicking the Harmony "ISO3Languages" bundle. + * Keys are the two-letter ISO language codes. Values are the three-letter + * ISO language abbreviations. An example entry is "EN"->"ENG". + */ + private static final class ISO3Languages extends ResourceBundle { + + @Override + public Enumeration<String> getKeys() { + // Won't get used + throw new UnsupportedOperationException(); + } + + @Override + protected Object handleGetObject(String key) { + return getISO3LanguageNative(key); + } + + } + + /** + * Internal ResourceBundle mimicking the Harmony "ISO4Currencies" bundle. + * Keys are the two-letter ISO language codes. Values are the three-letter + * ISO currency abbreviations. An example entry is "US"->"USD". + */ + private static final class ISO4Currencies extends ResourceBundle { + + @Override + public Enumeration<String> getKeys() { + // Won't get used + throw new UnsupportedOperationException(); + } + + @Override + protected Object handleGetObject(String key) { + return getCurrencyCodeNative(key); + } + + } + + /** + * Internal ResourceBundle mimicking the Harmony "ISO4CurrenciesToDigits" + * bundle. Keys are the three-letter ISO currency codes. Values are strings + * containing the number of fraction digits to use for the currency. An + * example entry is "USD"->"2". + */ + private static final class ISO4CurrenciesToDigits extends ResourceBundle { + + @Override + public Enumeration<String> getKeys() { + // Won't get used + throw new UnsupportedOperationException(); + } + + @Override + protected Object handleGetObject(String key) { + // In some places the triple-x code is used as the fall back + // currency. The harmony package returned -1 for this requested + // currency. + if ("XXX".equals(key)) { + return "-1"; + } + int res = getFractionDigitsNative(key); + if(res < 0) { + throw new MissingResourceException("couldn't find resource.", + ISO4CurrenciesToDigits.class.getName(), key); + } + return "" + res; + } + + } + + /** + * Internal ResourceBundle mimicking the Harmony "Country_*" bundles. Keys + * are the two-letter ISO country codes. Values are the printable country + * names in terms of the specified locale. An example entry is "US"->"United + * States". + */ + private static final class Country extends ResourceBundle { + private String locale; + + public Country(String locale) { + super(); + this.locale = locale; + } + + @Override + public Enumeration<String> getKeys() { + // Won't get used + throw new UnsupportedOperationException(); + } + + @Override + protected Object handleGetObject(String key) { + return getDisplayCountryNative(key, locale); + } + + } + + /** + * Internal ResourceBundle mimicking the Harmony "Currency_*" bundles. Keys + * are the three-letter ISO currency codes. Values are the printable + * currency names in terms of the specified locale. An example entry is + * "USD"->"$" (for inside the US) and "USD->"US$" (for outside the US). + */ + private static final class Currency extends ResourceBundle { + + private String locale; + + public Currency(String locale) { + super(); + this.locale = locale; + } + + @Override + public Enumeration<String> getKeys() { + // Won't get used + throw new UnsupportedOperationException(); + } + + @Override + protected Object handleGetObject(String key) { + return getCurrencySymbolNative(locale, key); + } + + } + + /** + * Internal ResourceBundle mimicking the Harmony "Language_*" bundles. Keys + * are the two-letter ISO language codes. Values are the printable language + * names in terms of the specified locale. An example entry is + * "en"->"English". + */ + private static final class Language extends ResourceBundle { + private String locale; + + public Language(String locale) { + super(); + this.locale = locale; + } + + @Override + public Enumeration<String> getKeys() { + // Won't get used + throw new UnsupportedOperationException(); + } + + @Override + protected Object handleGetObject(String key) { + return getDisplayLanguageNative(key, locale); + } + + } + + /** + * Internal ResourceBundle mimicking the Harmony "Variant_*" bundles. Keys + * are a fixed set of variants codes known to Harmony. Values are the + * printable variant names in terms of the specified locale. An example + * entry is "EURO"->"Euro". + */ + private static final class Variant extends ResourceBundle { + + private String locale; + + public Variant(String locale) { + super(); + this.locale = locale; + } + + @Override + public Enumeration<String> getKeys() { + // Won't get used + throw new UnsupportedOperationException(); + } + + @Override + protected Object handleGetObject(String key) { + return getDisplayVariantNative(key, locale); + } + + } + + /** + * Internal ResourceBundle mimicking the Harmony "Locale_*" bundles. This is + * clearly the most complex case, because the content covers a wide range of + * data items, with values even being arrays in some cases. Note we are + * cheating with the "timezones" entry, since we normally don't want to + * waste our precious RAM on several thousand of these Strings. + */ + private static final class Locale extends ListResourceBundle { + + private String locale; + + public Locale(String locale) { + super(); + this.locale = locale; + } + + @Override + protected Object[][] getContents() { + return getContentImpl(locale, false); + } + + } + + // --- Native methods accessing ICU's database ---------------------------- + + private static native int getFractionDigitsNative(String currencyCode); + + private static native String getCurrencyCodeNative(String locale); + + private static native String getCurrencySymbolNative(String locale, String currencyCode); + + private static native String getDisplayCountryNative(String countryCode, String locale); + + private static native String getDisplayLanguageNative(String languageCode, String locale); + + private static native String getDisplayVariantNative(String variantCode, String locale); + + private static native String getISO3CountryNative(String locale); + + private static native String getISO3LanguageNative(String locale); + + private static native String[] getAvailableLocalesNative(); + + private static native String[] getISOLanguagesNative(); + + private static native String[] getISOCountriesNative(); + + private static native void getTimeZonesNative(String[][] arrayToFill, String locale); + + private static native String getDisplayTimeZoneNative(String id, boolean isDST, int style, + String locale); + + private static native Object[][] getContentImpl(String locale, boolean needsTimeZones); +} diff --git a/icu/src/main/native/BidiWrapperInterface.c b/icu/src/main/native/BidiWrapperInterface.c new file mode 100644 index 0000000..2c6b3cd --- /dev/null +++ b/icu/src/main/native/BidiWrapperInterface.c @@ -0,0 +1,253 @@ +/* + * 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. + */ + +#include <stdlib.h> +#include <unicode/ubidi.h> +#include <string.h> +#include "BidiWrapperInterface.h" + +typedef struct { + UBiDi *pBiDi; + void *embeddingLevels; +} BiDiData; + +void check_fail (JNIEnv * env, int err); + +JNIEXPORT jlong JNICALL Java_org_apache_harmony_text_BidiWrapper_ubidi_1open + (JNIEnv * env, jclass clazz) +{ + BiDiData *data = (BiDiData *)malloc(sizeof(BiDiData)); + (*data).pBiDi = ubidi_open (); + (*data).embeddingLevels = NULL; + return (jlong) (data); +} + +JNIEXPORT void JNICALL Java_org_apache_harmony_text_BidiWrapper_ubidi_1close + (JNIEnv * env, jclass clazz, jlong pBiDi) +{ + BiDiData *data = (BiDiData *)pBiDi; + + ubidi_close ((*data).pBiDi); + + if ((*data).embeddingLevels != NULL) + free((*data).embeddingLevels); + free(data); +} + +JNIEXPORT void JNICALL Java_org_apache_harmony_text_BidiWrapper_ubidi_1setPara + (JNIEnv * env, jclass clazz, jlong pBiDi, jcharArray text, jint length, + jbyte paraLevel, jbyteArray embeddingLevels) +{ + UErrorCode err = 0; + jchar *_text = NULL; + BiDiData *data = (BiDiData *)pBiDi; + /* Remembering old embedding levels */ + void *embLvls = (*data).embeddingLevels; + + _text = (*env)->GetCharArrayElements (env, text, NULL); + + if (embeddingLevels != NULL) + { + jbyte *el = (*env)->GetByteArrayElements (env, embeddingLevels, NULL); + (*data).embeddingLevels = malloc(length); + memcpy(((*data).embeddingLevels), el, length); + (*env)->ReleaseByteArrayElements (env, embeddingLevels, el, 0); + } else + { + (*data).embeddingLevels = NULL; + } + + ubidi_setPara ((*data).pBiDi, _text, length, paraLevel, + ((*data).embeddingLevels), &err); + check_fail (env, err); + + /* Freeing old embedding levels */ + if (embLvls != NULL) { + free(embLvls); + } + + (*env)->ReleaseCharArrayElements (env, text, _text, 0); +} + +JNIEXPORT jlong JNICALL Java_org_apache_harmony_text_BidiWrapper_ubidi_1setLine + (JNIEnv * env, jclass clazz, jlong pBiDi, jint start, jint limit) +{ + UErrorCode err = 0; + BiDiData *data = (BiDiData *)pBiDi; + BiDiData *lineData = (BiDiData *) malloc(sizeof(BiDiData)); + (*lineData).embeddingLevels = NULL; + + (*lineData).pBiDi = ubidi_openSized (limit - start, 0, &err); + check_fail (env, err); + + ubidi_setLine ((*data).pBiDi, start, limit, (*lineData).pBiDi, + &err); + check_fail (env, err); + + return (jlong) lineData; +} + +JNIEXPORT jint JNICALL Java_org_apache_harmony_text_BidiWrapper_ubidi_1getDirection + (JNIEnv * env, jclass clazz, jlong pBiDi) +{ + BiDiData *data = (BiDiData *)pBiDi; + return ubidi_getDirection ((*data).pBiDi); +} + +JNIEXPORT jint JNICALL Java_org_apache_harmony_text_BidiWrapper_ubidi_1getLength + (JNIEnv * env, jclass clazz, jlong pBiDi) +{ + BiDiData *data = (BiDiData *)pBiDi; + return ubidi_getLength ((*data).pBiDi); +} + +JNIEXPORT jbyte JNICALL Java_org_apache_harmony_text_BidiWrapper_ubidi_1getParaLevel + (JNIEnv * env, jclass clazz, jlong pBiDi) +{ + BiDiData *data = (BiDiData *)pBiDi; + return ubidi_getParaLevel ((*data).pBiDi); +} + +JNIEXPORT jbyteArray JNICALL Java_org_apache_harmony_text_BidiWrapper_ubidi_1getLevels + (JNIEnv * env, jclass clazz, jlong pBiDi) +{ + UErrorCode err = 0; + const UBiDiLevel *levels = NULL; + jbyteArray result = NULL; + int len = 0; + BiDiData *data = (BiDiData *)pBiDi; + + levels = ubidi_getLevels ((*data).pBiDi, &err); + check_fail (env, err); + + len = ubidi_getLength ((*data).pBiDi); + result = (*env)->NewByteArray (env, len); + (*env)->SetByteArrayRegion (env, result, 0, len, (jbyte *) levels); + + return result; +} + +JNIEXPORT jint JNICALL Java_org_apache_harmony_text_BidiWrapper_ubidi_1countRuns + (JNIEnv * env, jclass clazz, jlong pBiDi) +{ + UErrorCode err = 0; + BiDiData *data = (BiDiData *)pBiDi; + + int count = ubidi_countRuns ((*data).pBiDi, &err); + check_fail (env, err); + + return count; +} + +JNIEXPORT jobjectArray JNICALL Java_org_apache_harmony_text_BidiWrapper_ubidi_1getRuns + (JNIEnv * env, jclass clz, jlong pBiDi) +{ + int runCount = 0; + int start = 0; + int limit = 0; + int i = 0; + UBiDiLevel level = 0; + jclass run_clazz = 0; + jmethodID initID = 0; + jobject run = 0; + jobjectArray runs; + UErrorCode err = 0; + BiDiData *data = (BiDiData *)pBiDi; + + run_clazz = (*env)->FindClass (env, "org/apache/harmony/text/BidiRun"); + initID = (*env)->GetMethodID (env, run_clazz, "<init>", "(III)V"); + + runCount = ubidi_countRuns ((*data).pBiDi, &err); + check_fail (env, err); + + runs = (*env)->NewObjectArray(env, runCount,run_clazz, NULL); + for (i = 0; i < runCount; i++) { + ubidi_getLogicalRun((*data).pBiDi, start, &limit, &level); + run = (*env)->NewObject (env, run_clazz, initID, start, limit, level); + (*env)->SetObjectArrayElement(env, runs, i, run); + start = limit; + } + return runs; +} + +void +check_fail (JNIEnv * env, int err) +{ + char message[] = "ICU Internal Error: "; + + if (U_FAILURE (err)) + { + sprintf (message, "ICU Internal Error: %d", err); + jniThrowException(env, "java/lang/RuntimeException", + message); + } +} + +JNIEXPORT jintArray JNICALL Java_org_apache_harmony_text_BidiWrapper_ubidi_1reorderVisual + (JNIEnv * env, jclass clazz, jbyteArray levels, jint length) +{ + UBiDiLevel *local_levels = 0; + int *local_indexMap = 0; + jintArray result = 0; + + local_indexMap = (int *) malloc(sizeof (int) * length); + local_levels = (*env)->GetByteArrayElements (env, levels, NULL); + + ubidi_reorderVisual (local_levels, length, local_indexMap); + + result = (*env)->NewIntArray (env, length); + (*env)->SetIntArrayRegion (env, result, 0, length, (jint *) local_indexMap); + + free(local_indexMap); + (*env)->ReleaseByteArrayElements (env, levels, local_levels, 0); + + return result; +} + +/* + * JNI registration + */ +static JNINativeMethod gMethods[] = { + /* NAME , SIGNATURE , FUNCPTR */ + { "ubidi_open" , "()J" , + Java_org_apache_harmony_text_BidiWrapper_ubidi_1open }, + { "ubidi_close" , "(J)V" , + Java_org_apache_harmony_text_BidiWrapper_ubidi_1close }, + { "ubidi_setPara" , "(J[CIB[B)V" , + Java_org_apache_harmony_text_BidiWrapper_ubidi_1setPara }, + { "ubidi_setLine" , "(JII)J" , + Java_org_apache_harmony_text_BidiWrapper_ubidi_1setLine }, + { "ubidi_getDirection" , "(J)I" , + Java_org_apache_harmony_text_BidiWrapper_ubidi_1getDirection }, + { "ubidi_getLength" , "(J)I" , + Java_org_apache_harmony_text_BidiWrapper_ubidi_1getLength }, + { "ubidi_getParaLevel" , "(J)B" , + Java_org_apache_harmony_text_BidiWrapper_ubidi_1getParaLevel }, + { "ubidi_getLevels" , "(J)[B" , + Java_org_apache_harmony_text_BidiWrapper_ubidi_1getLevels }, + { "ubidi_countRuns" , "(J)I" , + Java_org_apache_harmony_text_BidiWrapper_ubidi_1countRuns }, + { "ubidi_getRuns" , "(J)[Lorg/apache/harmony/text/BidiRun;", + Java_org_apache_harmony_text_BidiWrapper_ubidi_1getRuns }, + { "ubidi_reorderVisual", "([BI)[I" , + Java_org_apache_harmony_text_BidiWrapper_ubidi_1reorderVisual }, +}; +int register_org_apache_harmony_text_BidiWrapper(JNIEnv *env) +{ + return jniRegisterNativeMethods(env, "org/apache/harmony/text/BidiWrapper", + gMethods, NELEM(gMethods)); +} diff --git a/icu/src/main/native/BidiWrapperInterface.h b/icu/src/main/native/BidiWrapperInterface.h new file mode 100644 index 0000000..c73597e --- /dev/null +++ b/icu/src/main/native/BidiWrapperInterface.h @@ -0,0 +1,132 @@ +/* + * 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. + */ + +#include <JNIHelp.h> +/* Header for class org_apache_harmony_text_BidiWrapper */ + +#if !defined(_Included_org_apache_harmony_text_BidiWrapper) +#define _Included_org_apache_harmony_text_BidiWrapper +#if defined(__cplusplus) +extern "C" +{ +#endif +#undef org_apache_harmony_text_BidiWrapper_UBIDI_DEFAULT_LTR +#define org_apache_harmony_text_BidiWrapper_UBIDI_DEFAULT_LTR 254L +#undef org_apache_harmony_text_BidiWrapper_UBIDI_DEFAULT_RTL +#define org_apache_harmony_text_BidiWrapper_UBIDI_DEFAULT_RTL 255L +#undef org_apache_harmony_text_BidiWrapper_UBIDI_MAX_EXPLICIT_LEVEL +#define org_apache_harmony_text_BidiWrapper_UBIDI_MAX_EXPLICIT_LEVEL 61L +#undef org_apache_harmony_text_BidiWrapper_UBIDI_LEVEL_OVERRIDE +#define org_apache_harmony_text_BidiWrapper_UBIDI_LEVEL_OVERRIDE 128L +#undef org_apache_harmony_text_BidiWrapper_UBIDI_KEEP_BASE_COMBINING +#define org_apache_harmony_text_BidiWrapper_UBIDI_KEEP_BASE_COMBINING 1L +#undef org_apache_harmony_text_BidiWrapper_UBIDI_DO_MIRRORING +#define org_apache_harmony_text_BidiWrapper_UBIDI_DO_MIRRORING 2L +#undef org_apache_harmony_text_BidiWrapper_UBIDI_INSERT_LRM_FOR_NUMERIC +#define org_apache_harmony_text_BidiWrapper_UBIDI_INSERT_LRM_FOR_NUMERIC 4L +#undef org_apache_harmony_text_BidiWrapper_UBIDI_REMOVE_BIDI_CONTROLS +#define org_apache_harmony_text_BidiWrapper_UBIDI_REMOVE_BIDI_CONTROLS 8L +#undef org_apache_harmony_text_BidiWrapper_UBIDI_OUTPUT_REVERSE +#define org_apache_harmony_text_BidiWrapper_UBIDI_OUTPUT_REVERSE 16L +#undef org_apache_harmony_text_BidiWrapper_UBiDiDirection_UBIDI_LTR +#define org_apache_harmony_text_BidiWrapper_UBiDiDirection_UBIDI_LTR 0L +#undef org_apache_harmony_text_BidiWrapper_UBiDiDirection_UBIDI_RTL +#define org_apache_harmony_text_BidiWrapper_UBiDiDirection_UBIDI_RTL 1L +#undef org_apache_harmony_text_BidiWrapper_UBiDiDirection_UBIDI_MIXED +#define org_apache_harmony_text_BidiWrapper_UBiDiDirection_UBIDI_MIXED 2L +/* + * Class: org_apache_harmony_text_BidiWrapper + * Method: ubidi_open + * Signature: ()J + */ + JNIEXPORT jlong JNICALL Java_org_apache_harmony_text_BidiWrapper_ubidi_1open + (JNIEnv *, jclass); +/* + * Class: org_apache_harmony_text_BidiWrapper + * Method: ubidi_close + * Signature: (J)V + */ + JNIEXPORT void JNICALL Java_org_apache_harmony_text_BidiWrapper_ubidi_1close + (JNIEnv *, jclass, jlong); +/* + * Class: org_apache_harmony_text_BidiWrapper + * Method: ubidi_setPara + * Signature: (J[CIB[B)V + */ + JNIEXPORT void JNICALL Java_org_apache_harmony_text_BidiWrapper_ubidi_1setPara + (JNIEnv *, jclass, jlong, jcharArray, jint, jbyte, jbyteArray); +/* + * Class: org_apache_harmony_text_BidiWrapper + * Method: ubidi_setLine + * Signature: (JII)J + */ + JNIEXPORT jlong JNICALL Java_org_apache_harmony_text_BidiWrapper_ubidi_1setLine + (JNIEnv *, jclass, jlong, jint, jint); +/* + * Class: org_apache_harmony_text_BidiWrapper + * Method: ubidi_getDirection + * Signature: (J)I + */ + JNIEXPORT jint JNICALL Java_org_apache_harmony_text_BidiWrapper_ubidi_1getDirection + (JNIEnv *, jclass, jlong); +/* + * Class: org_apache_harmony_text_BidiWrapper + * Method: ubidi_getLength + * Signature: (J)I + */ + JNIEXPORT jint JNICALL Java_org_apache_harmony_text_BidiWrapper_ubidi_1getLength + (JNIEnv *, jclass, jlong); +/* + * Class: org_apache_harmony_text_BidiWrapper + * Method: ubidi_getParaLevel + * Signature: (J)B + */ + JNIEXPORT jbyte JNICALL Java_org_apache_harmony_text_BidiWrapper_ubidi_1getParaLevel + (JNIEnv *, jclass, jlong); +/* + * Class: org_apache_harmony_text_BidiWrapper + * Method: ubidi_getLevels + * Signature: (J)[B + */ + JNIEXPORT jbyteArray JNICALL Java_org_apache_harmony_text_BidiWrapper_ubidi_1getLevels + (JNIEnv *, jclass, jlong); +/* + * Class: org_apache_harmony_text_BidiWrapper + * Method: ubidi_countRuns + * Signature: (J)I + */ + JNIEXPORT jint JNICALL Java_org_apache_harmony_text_BidiWrapper_ubidi_1countRuns + (JNIEnv *, jclass, jlong); +/* + * Class: org_apache_harmony_text_BidiWrapper + * Method: ubidi_getRuns + * Signature: (J)[Lorg/apache/harmony/text/BidiRun; + */ +JNIEXPORT jobjectArray JNICALL Java_org_apache_harmony_text_BidiWrapper_ubidi_1getRuns + (JNIEnv *, jclass, jlong); +/* + * Class: org_apache_harmony_text_BidiWrapper + * Method: ubidi_reorderVisual + * Signature: ([BI)[I + */ + JNIEXPORT jintArray JNICALL + Java_org_apache_harmony_text_BidiWrapper_ubidi_1reorderVisual (JNIEnv *, jclass, + jbyteArray, jint); +#if defined(__cplusplus) +} +#endif +#endif diff --git a/icu/src/main/native/BreakIteratorInterface.c b/icu/src/main/native/BreakIteratorInterface.c new file mode 100644 index 0000000..021ace1 --- /dev/null +++ b/icu/src/main/native/BreakIteratorInterface.c @@ -0,0 +1,264 @@ +/* + * Copyright 2006 The Android Open Source Project + * + * Internal native functions. All of the functions defined here make + * direct use of VM functions or data structures, so they can't be written + * with JNI and shouldn't really be in a shared library. + * + * All functions here either complete quickly or are used to enter a wait + * state, so we don't set the thread status to THREAD_NATIVE when executing + * these methods. This means that the GC will wait for these functions + * to finish. DO NOT perform long operations or blocking I/O in here. + * + * In some cases we're following the division of labor defined by GNU + * ClassPath, e.g. java.lang.Thread has "Thread" and "VMThread", with + * the VM-specific behavior isolated in VMThread. + */ + +#include "JNIHelp.h" +#include "AndroidSystemNatives.h" +#include "ErrorCode.h" +#include "unicode/ubrk.h" +#include "unicode/putil.h" +#include <stdlib.h> + +static jstring getAvailableLocalesImpl(JNIEnv *env, jclass clazz, jint index) { + + const char * locale = ubrk_getAvailable(index); + + return (*env)->NewStringUTF(env, locale); + +} + +static jint getAvailableLocalesCountImpl(JNIEnv *env, jclass clazz) { + return ubrk_countAvailable(); +} + +static jint getCharacterInstanceImpl(JNIEnv *env, jclass clazz, jstring locale) { + + UErrorCode status = U_ZERO_ERROR; + + const char *localeChars = (*env)->GetStringUTFChars(env, locale, 0); + + UBreakIterator *iter = ubrk_open(UBRK_CHARACTER, localeChars, NULL, 0, &status); + + (*env)->ReleaseStringUTFChars(env, locale, localeChars); + + if ( icu4jni_error(env, status) != FALSE) { + return 0; + } + + return (long) iter; +} + +static jint getLineInstanceImpl(JNIEnv *env, jclass clazz, jstring locale) { + + UErrorCode status = U_ZERO_ERROR; + + const char *localeChars = (*env)->GetStringUTFChars(env, locale, 0); + + enum UBreakIteratorType type = UBRK_LINE; + + UBreakIterator *iter = ubrk_open(type, localeChars, NULL, 0, &status); + + (*env)->ReleaseStringUTFChars(env, locale, localeChars); + + if ( icu4jni_error(env, status) != FALSE) { + return 0; + } + + return (long) iter; +} + +static jint getSentenceInstanceImpl(JNIEnv *env, jclass clazz, jstring locale) { + + UErrorCode status = U_ZERO_ERROR; + + const char *localeChars = (*env)->GetStringUTFChars(env, locale, 0); + + enum UBreakIteratorType type = UBRK_SENTENCE; + + UBreakIterator *iter = ubrk_open(type, localeChars, NULL, 0, &status); + + (*env)->ReleaseStringUTFChars(env, locale, localeChars); + + if ( icu4jni_error(env, status) != FALSE) { + return 0; + } + + return (long) iter; +} + +static jint getWordInstanceImpl(JNIEnv *env, jclass clazz, jstring locale) { + + UErrorCode status = U_ZERO_ERROR; + + const char *localeChars = (*env)->GetStringUTFChars(env, locale, 0); + + enum UBreakIteratorType type = UBRK_WORD; + + UBreakIterator *iter = ubrk_open(type, localeChars, NULL, 0, &status); + + (*env)->ReleaseStringUTFChars(env, locale, localeChars); + + if ( icu4jni_error(env, status) != FALSE) { + return 0; + } + + return (long) iter; +} + +static void closeBreakIteratorImpl(JNIEnv *env, jclass clazz, jint address) { + + UBreakIterator *bi = (UBreakIterator *)(long)address; + + ubrk_close(bi); +} + +static jint cloneImpl(JNIEnv *env, jclass clazz, jint address) { + + UErrorCode status = U_ZERO_ERROR; + + UBreakIterator *bi = (UBreakIterator *)(long)address; + + jint buffersize = U_BRK_SAFECLONE_BUFFERSIZE; + + UBreakIterator *iter = ubrk_safeClone(bi, NULL, &buffersize, &status); + + if (icu4jni_error(env, status) != FALSE) { + return 0; + } + + return (long) iter; +} + +static void setTextImpl(JNIEnv *env, jclass clazz, jint address, jstring text) { + + UErrorCode status = U_ZERO_ERROR; + + UBreakIterator *bi = (UBreakIterator *)(long)address; + + const UChar *strUChars = (*env)->GetStringChars(env, text, NULL); + int strLen = (*env)->GetStringLength(env, text); + + ubrk_setText(bi, strUChars, strLen, &status); + + (*env)->ReleaseStringChars(env, text, strUChars); + + icu4jni_error(env, status); +} + +static jboolean isBoundaryImpl(JNIEnv *env, jclass clazz, jint address, jint offset) { + + UBreakIterator *bi = (UBreakIterator *)(long)address; + + return ubrk_isBoundary(bi, offset); +} + +static jint nextImpl(JNIEnv *env, jclass clazz, jint address, jint n) { + + UBreakIterator *bi = (UBreakIterator *)(long)address; + + if(n < 0) { + while(n++ < -1) { + ubrk_previous(bi); + } + return ubrk_previous(bi); + } else if(n == 0) { + return ubrk_current(bi); + } else { + while(n-- > 1) { + ubrk_next(bi); + } + return ubrk_next(bi); + } + + return -1; +} + +static jint precedingImpl(JNIEnv *env, jclass clazz, jint address, jint offset) { + + UBreakIterator *bi = (UBreakIterator *)(long)address; + + return ubrk_preceding(bi, offset); +} + +static jint firstImpl(JNIEnv *env, jclass clazz, jint address) { + + UBreakIterator *bi = (UBreakIterator *)(long)address; + + return ubrk_first(bi); +} + +static jint followingImpl(JNIEnv *env, jclass clazz, jint address, jint offset) { + + UBreakIterator *bi = (UBreakIterator *)(long)address; + + return ubrk_following(bi, offset); +} + +static jint currentImpl(JNIEnv *env, jclass clazz, jint address) { + + UBreakIterator *bi = (UBreakIterator *)(long)address; + + return ubrk_current(bi); +} + +static jint previousImpl(JNIEnv *env, jclass clazz, jint address) { + + UBreakIterator *bi = (UBreakIterator *)(long)address; + + return ubrk_previous(bi); +} + +static jint lastImpl(JNIEnv *env, jclass clazz, jint address) { + + UBreakIterator *bi = (UBreakIterator *)(long)address; + + return ubrk_last(bi); +} + +/* + * JNI registration + */ +static JNINativeMethod gMethods[] = { + /* name, signature, funcPtr */ + { "getAvailableLocalesImpl", "(I)Ljava/lang/String;", + (void*) getAvailableLocalesImpl }, + { "getAvailableLocalesCountImpl", "()I", + (void*) getAvailableLocalesCountImpl }, + { "getCharacterInstanceImpl", "(Ljava/lang/String;)I", + (void*) getCharacterInstanceImpl }, + { "getLineInstanceImpl", "(Ljava/lang/String;)I", + (void*) getLineInstanceImpl }, + { "getSentenceInstanceImpl", "(Ljava/lang/String;)I", + (void*) getSentenceInstanceImpl }, + { "getWordInstanceImpl", "(Ljava/lang/String;)I", + (void*) getWordInstanceImpl }, + { "closeBreakIteratorImpl", "(I)V", + (void*) closeBreakIteratorImpl }, + { "cloneImpl", "(I)I", + (void*) cloneImpl }, + { "setTextImpl", "(ILjava/lang/String;)V", + (void*) setTextImpl }, + { "isBoundaryImpl", "(II)Z", + (void*) isBoundaryImpl }, + { "nextImpl", "(II)I", + (void*) nextImpl }, + { "precedingImpl", "(II)I", + (void*) precedingImpl }, + { "firstImpl", "(I)I", + (void*) firstImpl }, + { "lastImpl", "(I)I", + (void*) lastImpl }, + { "currentImpl", "(I)I", + (void*) currentImpl }, + { "followingImpl", "(II)I", + (void*) followingImpl }, + { "previousImpl", "(I)I", + (void*) previousImpl }, +}; +int register_com_ibm_icu4jni_text_NativeBreakIterator(JNIEnv* env) { + return jniRegisterNativeMethods(env, "com/ibm/icu4jni/text/NativeBreakIterator", + gMethods, NELEM(gMethods)); +} diff --git a/icu/src/main/native/CharacterInterface.c b/icu/src/main/native/CharacterInterface.c new file mode 100644 index 0000000..70e9f29 --- /dev/null +++ b/icu/src/main/native/CharacterInterface.c @@ -0,0 +1,194 @@ +/* + * Copyright 2006 The Android Open Source Project + * + * Internal native functions. All of the functions defined here make + * direct use of VM functions or data structures, so they can't be written + * with JNI and shouldn't really be in a shared library. + * + * All functions here either complete quickly or are used to enter a wait + * state, so we don't set the thread status to THREAD_NATIVE when executing + * these methods. This means that the GC will wait for these functions + * to finish. DO NOT perform long operations or blocking I/O in here. + * + * In some cases we're following the division of labor defined by GNU + * ClassPath, e.g. java.lang.Thread has "Thread" and "VMThread", with + * the VM-specific behavior isolated in VMThread. + */ + +#include "JNIHelp.h" +#include "AndroidSystemNatives.h" +#include "unicode/uchar.h" +#include <stdlib.h> +#include <math.h> + +static jint digitImpl(JNIEnv *env, jclass clazz, jint codePoint, jint radix) { + return u_digit(codePoint, radix); +} + +static jint getTypeImpl(JNIEnv *env, jclass clazz, jint codePoint) { + return u_charType(codePoint); +} + +static jbyte getDirectionalityImpl(JNIEnv *env, jclass clazz, jint codePoint) { + return u_charDirection (codePoint); +} + +static jboolean isMirroredImpl(JNIEnv *env, jclass clazz, jint codePoint) { + return u_isMirrored (codePoint); +} + +static jint getNumericValueImpl(JNIEnv *env, jclass clazz, jint codePoint){ + // The letters A-Z in their uppercase ('\u0041' through '\u005A'), + // lowercase ('\u0061' through '\u007A'), + // and full width variant ('\uFF21' through '\uFF3A' + // and '\uFF41' through '\uFF5A') forms + // have numeric values from 10 through 35. This is independent of the + // Unicode specification, which does not assign numeric values to these + // char values. + if (codePoint >= 0x41 && codePoint <= 0x5A) { + return codePoint - 0x37; + } + if (codePoint >= 0x61 && codePoint <= 0x7A) { + return codePoint - 0x57; + } + if (codePoint >= 0xFF21 && codePoint <= 0xFF3A) { + return codePoint - 0xFF17; + } + if (codePoint >= 0xFF41 && codePoint <= 0xFF5A) { + return codePoint - 0xFF37; + } + + double result = u_getNumericValue(codePoint); + + if (result == U_NO_NUMERIC_VALUE) { + return -1; + } else if (result < 0 || floor(result + 0.5) != result) { + return -2; + } + + return result; +} + +static jboolean isDefinedValueImpl(JNIEnv *env, jclass clazz, jint codePoint) { + return u_isdefined(codePoint); +} + +static jboolean isDigitImpl(JNIEnv *env, jclass clazz, jint codePoint) { + return u_isdigit(codePoint); +} + +static jboolean isIdentifierIgnorableImpl(JNIEnv *env, jclass clazz, + jint codePoint) { + + // Java also returns TRUE for U+0085 Next Line (it omits U+0085 from whitespace ISO controls) + if(codePoint == 0x0085) { + return JNI_TRUE; + } + + return u_isIDIgnorable(codePoint); +} + +static jboolean isLetterImpl(JNIEnv *env, jclass clazz, jint codePoint) { + return u_isalpha(codePoint); +} + +static jboolean isLetterOrDigitImpl(JNIEnv *env, jclass clazz, jint codePoint) { + return u_isalnum(codePoint); +} + +static jboolean isSpaceCharImpl(JNIEnv *env, jclass clazz, jint codePoint) { + return u_isJavaSpaceChar(codePoint); +} + +static jboolean isTitleCaseImpl(JNIEnv *env, jclass clazz, jint codePoint) { + return u_istitle(codePoint); +} + +static jboolean isUnicodeIdentifierPartImpl(JNIEnv *env, jclass clazz, + jint codePoint) { + return u_isIDPart(codePoint); +} + +static jboolean isUnicodeIdentifierStartImpl(JNIEnv *env, jclass clazz, + jint codePoint) { + return u_isIDStart(codePoint); +} + +static jboolean isWhitespaceImpl(JNIEnv *env, jclass clazz, jint codePoint) { + + // Java omits U+0085 + if(codePoint == 0x0085) { + return JNI_FALSE; + } + + return u_isWhitespace(codePoint); +} + +static jint toLowerCaseImpl(JNIEnv *env, jclass clazz, jint codePoint) { + return u_tolower(codePoint); +} + +static jint toTitleCaseImpl(JNIEnv *env, jclass clazz, jint codePoint) { + return u_totitle(codePoint); +} + +static jint toUpperCaseImpl(JNIEnv *env, jclass clazz, jint codePoint) { + return u_toupper(codePoint); +} + +static jboolean isUpperCaseImpl(JNIEnv *env, jclass clazz, jint codePoint) { + return u_isupper(codePoint); +} + +static jboolean isLowerCaseImpl(JNIEnv *env, jclass clazz, jint codePoint) { + return u_islower(codePoint); +} + +static int forName(JNIEnv *env, jclass clazz, jstring blockName) { + const char *bName = (*env)->GetStringUTFChars(env, blockName, NULL); + int result = u_getPropertyValueEnum(UCHAR_BLOCK, bName); + (*env)->ReleaseStringUTFChars(env, blockName, bName); + return result; +} + +static int codeBlock(JNIEnv *env, jclass clazz, jint codePoint) { + return ublock_getCode(codePoint); +} + +/* + * JNI registration + */ +static JNINativeMethod gMethods[] = { + /* name, signature, funcPtr */ + { "digitImpl", "(II)I", (void*) digitImpl }, + { "getTypeImpl", "(I)I", (void*) getTypeImpl }, + { "getDirectionalityImpl", "(I)B", (void*) getDirectionalityImpl }, + { "isMirroredImpl", "(I)Z", (void*) isMirroredImpl }, + { "getNumericValueImpl", "(I)I", (void*) getNumericValueImpl }, + { "isDefinedValueImpl", "(I)Z", (void*) isDefinedValueImpl }, + { "isDigitImpl", "(I)Z", (void*) isDigitImpl }, + { "isIdentifierIgnorableImpl", "(I)Z", (void*) isIdentifierIgnorableImpl }, + { "isLetterImpl", "(I)Z", (void*) isLetterImpl }, + { "isLetterOrDigitImpl", "(I)Z", (void*) isLetterOrDigitImpl }, + { "isSpaceCharImpl", "(I)Z", (void*) isSpaceCharImpl }, + { "isTitleCaseImpl", "(I)Z", (void*) isTitleCaseImpl }, + { "isUnicodeIdentifierPartImpl", "(I)Z", + (void*) isUnicodeIdentifierPartImpl }, + { "isUnicodeIdentifierStartImpl", "(I)Z", + (void*) isUnicodeIdentifierStartImpl }, + { "isWhitespaceImpl", "(I)Z", (void*) isWhitespaceImpl }, + { "toLowerCaseImpl", "(I)I", (void*) toLowerCaseImpl }, + { "toTitleCaseImpl", "(I)I", (void*) toTitleCaseImpl }, + { "toUpperCaseImpl", "(I)I", (void*) toUpperCaseImpl }, + { "isUpperCaseImpl", "(I)Z", (void*) isUpperCaseImpl }, + { "isLowerCaseImpl", "(I)Z", (void*) isLowerCaseImpl }, + { "forname", "(Ljava/lang/String;)I", (void*) forName }, + { "codeblock", "(I)I", (void*) codeBlock } +}; + +int register_com_ibm_icu4jni_lang_UCharacter(JNIEnv *env) { + return jniRegisterNativeMethods(env, "com/ibm/icu4jni/lang/UCharacter", + gMethods, NELEM(gMethods)); +} + + diff --git a/icu/src/main/native/CollationInterface.c b/icu/src/main/native/CollationInterface.c new file mode 100644 index 0000000..86246ac --- /dev/null +++ b/icu/src/main/native/CollationInterface.c @@ -0,0 +1,589 @@ +/** +******************************************************************************* +* Copyright (C) 1996-2005, International Business Machines Corporation and * +* others. All Rights Reserved. * +******************************************************************************* +* +******************************************************************************* +*/ + +#include "JNIHelp.h" +#include "AndroidSystemNatives.h" +#include "ErrorCode.h" +#include "unicode/ucol.h" +#include "unicode/ucoleitr.h" +#include "ucol_imp.h" + + +/** +* Closing a C UCollator with the argument locale rules. +* Note determining if a collator currently exist for the caller is to be handled +* by the caller. Hence if the caller has a existing collator, it is his +* responsibility to delete first before calling this method. +* @param env JNI environment +* @param obj RuleBasedCollatorJNI object +* @param address of the C UCollator +*/ +static void closeCollator(JNIEnv *env, jclass obj, + jint address) { + + UCollator *collator = (UCollator *)(int)address; + ucol_close(collator); +} + + +/** +* Close a C collation element iterator. +* @param env JNI environment +* @param obj RuleBasedCollatorJNI object +* @param address of C collation element iterator to close. +*/ +static void closeElements(JNIEnv *env, jclass obj, + jint address) { + + UCollationElements *iterator = (UCollationElements *)(int)address; + ucol_closeElements(iterator); +} + +/** +* Compare two strings. +* The strings will be compared using the normalization mode and options +* specified in openCollator or openCollatorFromRules +* @param env JNI environment +* @param obj RuleBasedCollatorJNI object +* @param address address of the c collator +* @param source The source string. +* @param target The target string. +* @return result of the comparison, UCOL_EQUAL, UCOL_GREATER or UCOL_LESS +*/ +static jint compare(JNIEnv *env, jclass obj, jint address, + jstring source, jstring target) { + + const UCollator *collator = (const UCollator *)(int)address; + jint result = -2; + if(collator){ + jsize srclength = (*env)->GetStringLength(env, source); + const UChar *srcstr = (const UChar *)(*env)->GetStringCritical(env,source,0); + if(srcstr){ + jsize tgtlength = (*env)->GetStringLength(env, target); + const UChar *tgtstr = (const UChar *)(*env)->GetStringCritical(env,target,0); + if(tgtstr){ + result = ucol_strcoll(collator, srcstr, srclength, tgtstr, tgtlength); + (*env)->ReleaseStringCritical(env, source, srcstr); + (*env)->ReleaseStringCritical(env, target, tgtstr); + return result; + }else{ + icu4jni_error(env,U_ILLEGAL_ARGUMENT_ERROR); + } + }else{ + icu4jni_error(env,U_ILLEGAL_ARGUMENT_ERROR); + } + }else{ + icu4jni_error(env,U_ILLEGAL_ARGUMENT_ERROR); + } + return result; +} + +/** +* Universal attribute getter +* @param env JNI environment +* @param obj RuleBasedCollatorJNI object +* @param address address of the C collator +* @param type type of attribute to be set +* @return attribute value +* @exception thrown when error occurs while getting attribute value +*/ +static jint getAttribute(JNIEnv *env, jclass obj, jint address, + jint type) { + + const UCollator *collator = (const UCollator *)(int)address; + UErrorCode status = U_ZERO_ERROR; + if(collator){ + jint result = (jint)ucol_getAttribute(collator, (UColAttribute)type, + &status); + if (icu4jni_error(env, status) != FALSE){ + return (jint)UCOL_DEFAULT; + } + return result; + }else{ + icu4jni_error(env,U_ILLEGAL_ARGUMENT_ERROR); + } + return (jint)UCOL_DEFAULT; +} + +/** +* Create a CollationElementIterator object that will iterator over the elements +* in a string, using the collation rules defined in this RuleBasedCollatorJNI +* @param env JNI environment +* @param obj RuleBasedCollatorJNI object +* @param address address of C collator +* @param source string to iterate over +* @return address of C collationelement +*/ +static jint getCollationElementIterator(JNIEnv *env, + jclass obj, jint address, jstring source) { + + UErrorCode status = U_ZERO_ERROR; + UCollator *collator = (UCollator *)(int)address; + jint result=0; + if(collator){ + jsize srclength = (*env)->GetStringLength(env, source); + const UChar *srcstr = (const UChar *)(*env)->GetStringCritical(env,source,0); + if(srcstr){ + result = (jint)(ucol_openElements(collator, srcstr, srclength, &status)); + + (*env)->ReleaseStringCritical(env, source, srcstr); + icu4jni_error(env, status); + }else{ + icu4jni_error(env, U_ILLEGAL_ARGUMENT_ERROR); + } + }else{ + icu4jni_error(env, U_ILLEGAL_ARGUMENT_ERROR); + } + return result; +} + +/** +* Get the maximum length of any expansion sequences that end with the specified +* comparison order. +* @param env JNI environment +* @param obj RuleBasedCollatorJNI object +* @param address of the C collation element iterator containing the text. +* @param order collation order returned by previous or next. +* @return maximum length of any expansion sequences ending with the specified +* order or 1 if collation order does not occur at the end of any +* expansion sequence. +*/ +static jint getMaxExpansion(JNIEnv *env, jclass obj, + jint address, jint order) { + + UCollationElements *iterator = (UCollationElements *)(int)address; + return ucol_getMaxExpansion(iterator, order); +} + +/** +* Get the normalization mode for this object. +* The normalization mode influences how strings are compared. +* @param env JNI environment +* @param obj RuleBasedCollatorJNI object +* @param address of C collator +* @return normalization mode; one of the values from NormalizerEnum +*/ +static jint getNormalization(JNIEnv *env, jclass obj, + jint address) { + + UErrorCode status = U_ZERO_ERROR; + const UCollator *collator = (const UCollator *)(int)address; + if(U_FAILURE(status)){ + icu4jni_error(env, status); + } + return (jint)ucol_getAttribute(collator,UCOL_NORMALIZATION_MODE,&status); + +} + +/** +* Set the normalization mode for this object. +* The normalization mode influences how strings are compared. +* @param env JNI environment +* @param obj RuleBasedCollatorJNI object +* @param address of C collator +* @param mode the normalization mode +*/ +static void setNormalization(JNIEnv *env, jclass obj, jint address, + jint mode) { + + UErrorCode status = U_ZERO_ERROR; + const UCollator *collator = (const UCollator *)(int)address; + if(U_FAILURE(status)){ + icu4jni_error(env, status); + } + ucol_setAttribute(collator,UCOL_NORMALIZATION_MODE,mode,&status); +} + + +/** +* Get the offset of the current source character. +* This is an offset into the text of the character containing the current +* collation elements. +* @param env JNI environment +* @param obj RuleBasedCollatorJNI object +* @param addresss of the C collation elements iterator to query. +* @return offset of the current source character. +*/ +static jint getOffset(JNIEnv *env, jclass obj, jint address) { + + UCollationElements *iterator = (UCollationElements *)(int)address; + return ucol_getOffset(iterator); +} + +/** +* Get the collation rules from a UCollator. +* The rules will follow the rule syntax. +* @param env JNI environment +* @param obj RuleBasedCollatorJNI object +* @param address the address of the C collator +* @return collation rules. +*/ +static jstring getRules(JNIEnv *env, jclass obj, + jint address) { + + const UCollator *collator = (const UCollator *)(int)address; + int32_t length=0; + const UChar *rules = ucol_getRules(collator, &length); + return (*env)->NewString(env, rules, length); +} + +/** +* Get a sort key for the argument string +* Sort keys may be compared using java.util.Arrays.equals +* @param env JNI environment +* @param obj RuleBasedCollatorJNI object +* @param address address of the C collator +* @param source string for key to be generated +* @return sort key +*/ +static jbyteArray getSortKey(JNIEnv *env, jclass obj, + jint address, jstring source) { + + const UCollator *collator = (const UCollator *)(int)address; + jbyteArray result; + if(collator && source){ + // BEGIN android-added + if(!source) { + return NULL; + } + // END android-added + jsize srclength = (*env)->GetStringLength(env, source); + const UChar *srcstr = (const UChar *)(*env)->GetStringCritical(env,source, 0); + if(srcstr){ +// BEGIN android-changed + uint8_t bytearray[UCOL_MAX_BUFFER * 2]; + uint8_t *largerbytearray = NULL; + uint8_t *usedbytearray = bytearray; + + jint bytearraysize = ucol_getSortKey(collator, srcstr, srclength, bytearray, + sizeof(bytearray) - 1); + + if (bytearraysize > sizeof(bytearray) - 1) { + // didn't fit, try again with a larger buffer. + largerbytearray = malloc(bytearraysize + 1); + usedbytearray = largerbytearray; + bytearraysize = ucol_getSortKey(collator, srcstr, srclength, largerbytearray, + bytearraysize); + } + + (*env)->ReleaseStringCritical(env, source, srcstr); + + if (bytearraysize == 0) { + free(largerbytearray); + return NULL; + } + + /* no problem converting uint8_t to int8_t, gives back the correct value + * tried and tested + */ + result = (*env)->NewByteArray(env, bytearraysize); + (*env)->SetByteArrayRegion(env, result, 0, bytearraysize, usedbytearray); + free(largerbytearray); +// END android-changed + }else{ + icu4jni_error(env,U_ILLEGAL_ARGUMENT_ERROR); + } + }else{ + icu4jni_error(env,U_ILLEGAL_ARGUMENT_ERROR); + } + return result; +} + +/** +* Returns a hash of this collation object +* Note this method is not complete, it only returns 0 at the moment. +* @param env JNI environment +* @param obj RuleBasedCollatorJNI object +* @param address address of C collator +* @return hash of this collation object +*/ +static jint hashCode(JNIEnv *env, jclass obj, jint address) { + + UCollator *collator = (UCollator *)(int)address; + int32_t length=0; + const UChar *rules = ucol_getRules(collator, &length); + /* temporary commented out + * return uhash_hashUCharsN(rules, length); + */ + return 0; +} + +/** +* Get the ordering priority of the next collation element in the text. +* A single character may contain more than one collation element. +* @param env JNI environment +* @param obj RuleBasedCollatorJNI object +* @param address if C collation elements containing the text. +* @return next collation elements ordering, otherwise returns NULLORDER if an +* error has occured or if the end of string has been reached +*/ +static jint next(JNIEnv *env, jclass obj, jint address) { + UCollationElements *iterator = (UCollationElements *)(int)address; + UErrorCode status = U_ZERO_ERROR; + jint result = ucol_next(iterator, &status); + + icu4jni_error(env, status); + return result; +} + +/** +* Opening a new C UCollator with the default locale. +* Note determining if a collator currently exist for the caller is to be handled +* by the caller. Hence if the caller has a existing collator, it is his +* responsibility to delete first before calling this method. +* @param env JNI environment +* @param obj RuleBasedCollatorJNI object +* @return address of the new C UCollator +* @exception thrown if creation of the UCollator fails +*/ +static jint openCollator__(JNIEnv *env, jclass obj) { + jint result; + UErrorCode status = U_ZERO_ERROR; + + result = (jint)ucol_open(NULL, &status); + if ( icu4jni_error(env, status) != FALSE) + return 0; + + return result; +} + + +/** +* Opening a new C UCollator with the argument locale rules. +* Note determining if a collator currently exist for the caller is to be handled +* by the caller. Hence if the caller has a existing collator, it is his +* responsibility to delete first before calling this method. +* @param env JNI environment +* @param obj RuleBasedCollatorJNI object +* @param locale name +* @return address of the new C UCollator +* @exception thrown if creation of the UCollator fails +*/ +static jint openCollator__Ljava_lang_String_2(JNIEnv *env, + jclass obj, jstring locale) { + + /* this will be null terminated */ + const char *localestr = (*env)->GetStringUTFChars(env, locale, 0); + jint result=0; + UErrorCode status = U_ZERO_ERROR; + + if(localestr){ + result = (jint)ucol_open(localestr, &status); + (*env)->ReleaseStringUTFChars(env, locale, localestr); + icu4jni_error(env, status); + }else{ + icu4jni_error(env,U_ILLEGAL_ARGUMENT_ERROR); + } + return result; +} + +/** +* Opening a new C UCollator with the argument locale rules. +* Note determining if a collator currently exist for the caller is to be +* handled by the caller. Hence if the caller has a existing collator, it is his +* responsibility to delete first before calling this method. +* @param env JNI environment +* @param obj RuleBasedCollatorJNI object +* @param rules set of collation rules +* @param normalizationmode normalization mode +* @param strength collation strength +* @return address of the new C UCollator +* @exception thrown if creation of the UCollator fails +*/ +static jint openCollatorFromRules(JNIEnv *env, jclass obj, + jstring rules, jint normalizationmode, jint strength) { + + jsize ruleslength = (*env)->GetStringLength(env, rules); + const UChar *rulestr = (const UChar *)(*env)->GetStringCritical(env,rules, 0); + UErrorCode status = U_ZERO_ERROR; + jint result = 0; + if(rulestr){ + result = (jint)ucol_openRules(rulestr, ruleslength, + (UColAttributeValue)normalizationmode, + (UCollationStrength)strength, NULL, &status); + + (*env)->ReleaseStringCritical(env, rules, rulestr); + icu4jni_error(env, status); + }else{ + icu4jni_error(env,U_ILLEGAL_ARGUMENT_ERROR); + } + + return result; +} + +/** +* Get the ordering priority of the previous collation element in the text. +* A single character may contain more than one collation element. +* @param env JNI environment +* @param obj RuleBasedCollatorJNI object +* @param address of the C collation element iterator containing the text. +* @return previous collation element ordering, otherwise returns NULLORDER if +* an error has occured or if the start of string has been reached +* @exception thrown when retrieval of previous collation element fails. +*/ +static jint previous(JNIEnv *env, jclass obj, jint address) { + + UCollationElements *iterator = (UCollationElements *)(int)address; + UErrorCode status = U_ZERO_ERROR; + jint result = ucol_previous(iterator, &status); + + icu4jni_error(env, status); + return result; +} + + +/** +* Reset the collation elements to their initial state. +* This will move the 'cursor' to the beginning of the text. +* @param env JNI environment +* @param obj RuleBasedCollatorJNI object +* @param address of C collation element iterator to reset. +*/ +static void reset(JNIEnv *env, jclass obj, jint address) { + + UCollationElements *iterator = (UCollationElements *)(int)address; + ucol_reset(iterator); +} + +/** +* Thread safe cloning operation +* @param env JNI environment +* @param obj RuleBasedCollatorJNI object +* @param address address of C collator to be cloned +* @return address of the new clone +* @exception thrown when error occurs while cloning +*/ +static jint safeClone(JNIEnv *env, jclass obj, jint address) { + + const UCollator *collator = (const UCollator *)(int)address; + UErrorCode status = U_ZERO_ERROR; + jint result; + jint buffersize = U_COL_SAFECLONE_BUFFERSIZE; + + result = (jint)ucol_safeClone(collator, NULL, &buffersize, &status); + + if ( icu4jni_error(env, status) != FALSE) { + return 0; + } + + return result; +} + +/** +* Universal attribute setter. +* @param env JNI environment +* @param obj RuleBasedCollatorJNI object +* @param address address of the C collator +* @param type type of attribute to be set +* @param value attribute value +* @exception thrown when error occurs while setting attribute value +*/ +static void setAttribute(JNIEnv *env, jclass obj, jint address, + jint type, jint value) { + + UCollator *collator = (UCollator *)(int)address; + UErrorCode status = U_ZERO_ERROR; + ucol_setAttribute(collator, (UColAttribute)type, (UColAttributeValue)value, + &status); + icu4jni_error(env, status); +} + +/** +* Set the offset of the current source character. +* This is an offset into the text of the character to be processed. +* @param env JNI environment +* @param obj RuleBasedCollatorJNI object +* @param address of the C collation element iterator to set. +* @param offset The desired character offset. +* @exception thrown when offset setting fails +*/ +static void setOffset(JNIEnv *env, jclass obj, jint address, + jint offset) { + + UCollationElements *iterator = (UCollationElements *)(int)address; + UErrorCode status = U_ZERO_ERROR; + + ucol_setOffset(iterator, offset, &status); + icu4jni_error(env, status); +} + +/** +* Set the text containing the collation elements. +* @param env JNI environment +* @param obj RuleBasedCollatorJNI object +* @param address of the C collation element iterator to be set +* @param source text containing the collation elements. +* @exception thrown when error occurs while setting offset +*/ +static void setText(JNIEnv *env, jclass obj, jint address, + jstring source) { + + UCollationElements *iterator = (UCollationElements *)(int)address; + UErrorCode status = U_ZERO_ERROR; + int strlength = (*env)->GetStringLength(env, source); + const UChar *str = (const UChar *)(*env)->GetStringCritical(env, source, 0); + + ucol_setText(iterator, str, strlength, &status); + (*env)->ReleaseStringCritical(env, source, str); + + icu4jni_error(env, status); +} + +// BEGIN android-added +static jstring getAvailableLocalesImpl(JNIEnv *env, jclass clazz, jint index) { + + const char * locale = ucol_getAvailable(index); + + return (*env)->NewStringUTF(env, locale); + +} + +static jint getAvailableLocalesCountImpl(JNIEnv *env, jclass clazz) { + return ucol_countAvailable(); +} +// END android-added + +/* + * JNI registratio + */ +static JNINativeMethod gMethods[] = { + /* name, signature, funcPtr */ + // BEGIN android-added + { "getAvailableLocalesImpl", "(I)Ljava/lang/String;", (void*) getAvailableLocalesImpl }, + { "getAvailableLocalesCountImpl", "()I", (void*) getAvailableLocalesCountImpl }, + // END android-added + { "openCollator", "()I", (void*) openCollator__ }, + { "openCollator", "(Ljava/lang/String;)I", (void*) openCollator__Ljava_lang_String_2 }, + { "openCollatorFromRules", "(Ljava/lang/String;II)I", (void*) openCollatorFromRules }, + { "closeCollator", "(I)V", (void*) closeCollator }, + { "compare", "(ILjava/lang/String;Ljava/lang/String;)I", (void*) compare }, + { "getNormalization", "(I)I", (void*) getNormalization }, + { "setNormalization", "(II)V", (void*) setNormalization }, + { "getRules", "(I)Ljava/lang/String;", (void*) getRules }, + { "getSortKey", "(ILjava/lang/String;)[B", (void*) getSortKey }, + { "setAttribute", "(III)V", (void*) setAttribute }, + { "getAttribute", "(II)I", (void*) getAttribute }, + { "safeClone", "(I)I", (void*) safeClone }, + { "getCollationElementIterator", "(ILjava/lang/String;)I", (void*) getCollationElementIterator }, + { "hashCode", "(I)I", (void*) hashCode }, + { "closeElements", "(I)V", (void*) closeElements }, + { "reset", "(I)V", (void*) reset }, + { "next", "(I)I", (void*) next }, + { "previous", "(I)I", (void*) previous }, + { "getMaxExpansion", "(II)I", (void*) getMaxExpansion }, + { "setText", "(ILjava/lang/String;)V", (void*) setText }, + { "getOffset", "(I)I", (void*) getOffset }, + { "setOffset", "(II)V", (void*) setOffset } +}; + +int register_com_ibm_icu4jni_text_NativeCollator(JNIEnv *_env) { + return jniRegisterNativeMethods(_env, "com/ibm/icu4jni/text/NativeCollation", + gMethods, NELEM(gMethods)); +} + diff --git a/icu/src/main/native/CollationInterface.h b/icu/src/main/native/CollationInterface.h new file mode 100644 index 0000000..bdb4b67 --- /dev/null +++ b/icu/src/main/native/CollationInterface.h @@ -0,0 +1,214 @@ +/** +******************************************************************************* +* Copyright (C) 1996-2005, International Business Machines Corporation and * +* others. All Rights Reserved. * +******************************************************************************* +* +******************************************************************************* +*/ +/* DO NOT EDIT THIS FILE - it is machine generated */ +#include <jni.h> +/* Header for class CollationInterface */ + +#ifndef _Included_com_ibm_icu4jni_text_NativeCollation +#define _Included_com_ibm_icu4jni_text_NativeCollation +#ifdef __cplusplus +extern "C" { +#endif +/* + * Class: com_ibm_icu4jni_text_NativeCollation + * Method: closeCollator + * Signature: (J)V + */ +JNIEXPORT void JNICALL Java_com_ibm_icu4jni_text_NativeCollation_closeCollator + (JNIEnv *, jclass, jlong); + +/* + * Class: com_ibm_icu4jni_text_NativeCollation + * Method: closeElements + * Signature: (J)V + */ +JNIEXPORT void JNICALL Java_com_ibm_icu4jni_text_NativeCollation_closeElements + (JNIEnv *, jclass, jlong); + +/* + * Class: com_ibm_icu4jni_text_NativeCollation + * Method: compare + * Signature: (JLjava/lang/String;Ljava/lang/String;)I + */ +JNIEXPORT jint JNICALL Java_com_ibm_icu4jni_text_NativeCollation_compare + (JNIEnv *, jclass, jlong, jstring, jstring); + +/* + * Class: com_ibm_icu4jni_text_NativeCollation + * Method: getAttribute + * Signature: (JI)I + */ +JNIEXPORT jint JNICALL Java_com_ibm_icu4jni_text_NativeCollation_getAttribute + (JNIEnv *, jclass, jlong, jint); + +/* + * Class: com_ibm_icu4jni_text_NativeCollation + * Method: getCollationElementIterator + * Signature: (JLjava/lang/String;)J + */ +JNIEXPORT jlong JNICALL Java_com_ibm_icu4jni_text_NativeCollation_getCollationElementIterator + (JNIEnv *, jclass, jlong, jstring); + +/* + * Class: com_ibm_icu4jni_text_NativeCollation + * Method: getMaxExpansion + * Signature: (JI)I + */ +JNIEXPORT jint JNICALL Java_com_ibm_icu4jni_text_NativeCollation_getMaxExpansion + (JNIEnv *, jclass, jlong, jint); + +/* + * Class: com_ibm_icu4jni_text_NativeCollation + * Method: getNormalization + * Signature: (J)I + */ +JNIEXPORT jint JNICALL Java_com_ibm_icu4jni_text_NativeCollation_getNormalization + (JNIEnv *, jclass, jlong); + +/* + * Class: com_ibm_icu4jni_text_NativeCollation + * Method: getOffset + * Signature: (J)I + */ +JNIEXPORT jint JNICALL Java_com_ibm_icu4jni_text_NativeCollation_getOffset + (JNIEnv *, jclass, jlong); + +/* + * Class: com_ibm_icu4jni_text_NativeCollation + * Method: getRules + * Signature: (J)Ljava/lang/String; + */ +JNIEXPORT jstring JNICALL Java_com_ibm_icu4jni_text_NativeCollation_getRules + (JNIEnv *, jclass, jlong); + +/* + * Class: com_ibm_icu4jni_text_NativeCollation + * Method: getSortKey + * Signature: (JLjava/lang/String;)[B + */ +JNIEXPORT jbyteArray JNICALL Java_com_ibm_icu4jni_text_NativeCollation_getSortKey + (JNIEnv *, jclass, jlong, jstring); + +/* + * Class: com_ibm_icu4jni_text_NativeCollation + * Method: hashCode + * Signature: (J)I + */ +JNIEXPORT jint JNICALL Java_com_ibm_icu4jni_text_NativeCollation_hashCode + (JNIEnv *, jclass, jlong); + +/* + * Class: com_ibm_icu4jni_text_NativeCollation + * Method: next + * Signature: (J)I + */ +JNIEXPORT jint JNICALL Java_com_ibm_icu4jni_text_NativeCollation_next + (JNIEnv *, jclass, jlong); + +/* + * Class: com_ibm_icu4jni_text_NativeCollation + * Method: openCollator + * Signature: ()J + */ +JNIEXPORT jlong JNICALL Java_com_ibm_icu4jni_text_NativeCollation_openCollator__ + (JNIEnv *, jclass); + +/* + * Class: com_ibm_icu4jni_text_NativeCollation + * Method: openCollator + * Signature: (Ljava/lang/String;)J + */ +JNIEXPORT jlong JNICALL Java_com_ibm_icu4jni_text_NativeCollation_openCollator__Ljava_lang_String_2 + (JNIEnv *, jclass, jstring); + + +/* + * Class: com_ibm_icu4jni_text_NativeCollation + * Method: openCollatorFromRules + * Signature: (Ljava/lang/String;II)J + */ +JNIEXPORT jlong JNICALL Java_com_ibm_icu4jni_text_NativeCollation_openCollatorFromRules + (JNIEnv *, jclass, jstring, jint, jint); + +/* + * Class: com_ibm_icu4jni_text_NativeCollation + * Method: previous + * Signature: (J)I + */ +JNIEXPORT jint JNICALL Java_com_ibm_icu4jni_text_NativeCollation_previous + (JNIEnv *, jclass, jlong); + +/* + * Class: com_ibm_icu4jni_text_NativeCollation + * Method: primaryOrder + * Signature: (I)I + */ +JNIEXPORT jint JNICALL Java_com_ibm_icu4jni_text_NativeCollation_primaryOrder + (JNIEnv *, jclass, jint); + +/* + * Class: com_ibm_icu4jni_text_NativeCollation + * Method: reset + * Signature: (J)V + */ +JNIEXPORT void JNICALL Java_com_ibm_icu4jni_text_NativeCollation_reset + (JNIEnv *, jclass, jlong); + +/* + * Class: com_ibm_icu4jni_text_NativeCollation + * Method: safeClone + * Signature: (J)J + */ +JNIEXPORT jlong JNICALL Java_com_ibm_icu4jni_text_NativeCollation_safeClone + (JNIEnv *, jclass, jlong); + +/* + * Class: com_ibm_icu4jni_text_NativeCollation + * Method: secondaryOrder + * Signature: (I)I + */ +JNIEXPORT jint JNICALL Java_com_ibm_icu4jni_text_NativeCollation_secondaryOrder + (JNIEnv *, jclass, jint); + +/* + * Class: com_ibm_icu4jni_text_NativeCollation + * Method: setAttribute + * Signature: (JII)V + */ +JNIEXPORT void JNICALL Java_com_ibm_icu4jni_text_NativeCollation_setAttribute + (JNIEnv *, jclass, jlong, jint, jint); + +/* + * Class: com_ibm_icu4jni_text_NativeCollation + * Method: setOffset + * Signature: (JI)V + */ +JNIEXPORT void JNICALL Java_com_ibm_icu4jni_text_NativeCollation_setOffset + (JNIEnv *, jclass, jlong, jint); + +/* + * Class: com_ibm_icu4jni_text_NativeCollation + * Method: setText + * Signature: (JLjava/lang/String;)V + */ +JNIEXPORT void JNICALL Java_com_ibm_icu4jni_text_NativeCollation_setText + (JNIEnv *, jclass, jlong, jstring); + +/* + * Class: com_ibm_icu4jni_text_NativeCollation + * Method: tertiaryOrder + * Signature: (I)I + */ +JNIEXPORT jint JNICALL Java_com_ibm_icu4jni_text_NativeCollation_tertiaryOrder + (JNIEnv *, jclass, jint); + +#ifdef __cplusplus +} +#endif +#endif diff --git a/icu/src/main/native/ConverterInterface.c b/icu/src/main/native/ConverterInterface.c new file mode 100644 index 0000000..d7f5c9b --- /dev/null +++ b/icu/src/main/native/ConverterInterface.c @@ -0,0 +1,1378 @@ +/** +******************************************************************************* +* Copyright (C) 1996-2006, International Business Machines Corporation and * +* others. All Rights Reserved. * +******************************************************************************* +* +* +******************************************************************************* +*/ +/* + * @(#) icujniinterface.c 1.2 00/10/11 + * + * (C) Copyright IBM Corp. 2000 - All Rights Reserved + * A JNI wrapper to ICU native converter Interface + * @author: Ram Viswanadha + */ + +#include "ConverterInterface.h" +#include "JNIHelp.h" +#include "AndroidSystemNatives.h" +#include "unicode/utypes.h" /* Basic ICU data types */ +#include "unicode/ucnv.h" /* C Converter API */ +#include "unicode/ustring.h" /* some more string functions*/ +#include "unicode/ucnv_cb.h" /* for callback functions */ +#include "unicode/uset.h" /* for contains function */ +#include "ErrorCode.h" +#include <stdlib.h> +#include <string.h> + +// BEGIN android-removed +// #define UTF_16BE "UTF-16BE" +// #define UTF_16 "UTF-16" +// END android-removed + +/* Prototype of callback for substituting user settable sub chars */ +void JNI_TO_U_CALLBACK_SUBSTITUTE + (const void *,UConverterToUnicodeArgs *,const char* ,int32_t ,UConverterCallbackReason ,UErrorCode * ); + +/** + * Opens the ICU converter + * @param env environment handle for JNI + * @param jClass handle for the class + * @param handle buffer to recieve ICU's converter address + * @param converterName name of the ICU converter + */ +static jlong openConverter (JNIEnv *env, jclass jClass, jstring converterName) { + + UConverter* conv=NULL; + UErrorCode errorCode = U_ZERO_ERROR; + + const char* cnvName= (const char*) (*env)->GetStringUTFChars(env, converterName,NULL); + if(cnvName) { + int count = (*env)->GetStringUTFLength(env,converterName); + + conv = ucnv_open(cnvName,&errorCode); + } + (*env)->ReleaseStringUTFChars(env, converterName,cnvName); + + if (icu4jni_error(env, errorCode) != FALSE) { + return 0; + } + + return (jlong) conv; +} + +/** + * Closes the ICU converter + * @param env environment handle for JNI + * @param jClass handle for the class + * @param handle address of ICU converter + */ +static void closeConverter (JNIEnv *env, jclass jClass, jlong handle) { + + UConverter* cnv = (UConverter*)(long)handle; + if(cnv) { + // BEGIN android-added + // Free up any contexts created in setCallback[Encode|Decode]() + UConverterToUCallback toAction; + UConverterFromUCallback fromAction; + void * context1 = NULL; + void * context2 = NULL; + ucnv_getToUCallBack(cnv, &toAction, &context1); + ucnv_getFromUCallBack(cnv, &fromAction, &context2); + // END android-added + ucnv_close(cnv); + // BEGIN android-added + if (context1 != NULL) { + free(context1); + } + if (context2 != NULL) { + free(context2); + } + // END android-added + } +} + +/** + * Sets the substution mode for from Unicode conversion. Currently only + * two modes are supported: substitute or report + * @param env environment handle for JNI + * @param jClass handle for the class + * @param handle address of ICU converter + * @param mode the mode to set + */ +static jint setSubstitutionModeCharToByte (JNIEnv *env, jclass jClass, jlong handle, jboolean mode) { + + UConverter* conv = (UConverter*)(long)handle; + UErrorCode errorCode =U_ZERO_ERROR; + + if(conv) { + + UConverterFromUCallback fromUOldAction ; + void* fromUOldContext; + void* fromUNewContext=NULL; + if(mode) { + + ucnv_setFromUCallBack(conv, + UCNV_FROM_U_CALLBACK_SUBSTITUTE, + fromUNewContext, + &fromUOldAction, + (const void**)&fromUOldContext, + &errorCode); + + } + else{ + + ucnv_setFromUCallBack(conv, + UCNV_FROM_U_CALLBACK_STOP, + fromUNewContext, + &fromUOldAction, + (const void**)&fromUOldContext, + &errorCode); + + } + return errorCode; + } + errorCode = U_ILLEGAL_ARGUMENT_ERROR; + return errorCode; +} +/** + * Sets the substution mode for to Unicode conversion. Currently only + * two modes are supported: substitute or report + * @param env environment handle for JNI + * @param jClass handle for the class + * @param handle address of ICU converter + * @param mode the mode to set + */ +static jint setSubstitutionModeByteToChar (JNIEnv *env, jclass jClass, jlong handle, jboolean mode) { + + UConverter* conv = (UConverter*)handle; + UErrorCode errorCode =U_ZERO_ERROR; + + if(conv) { + + UConverterToUCallback toUOldAction ; + void* toUOldContext; + void* toUNewContext=NULL; + if(mode) { + + ucnv_setToUCallBack(conv, + UCNV_TO_U_CALLBACK_SUBSTITUTE, + toUNewContext, + &toUOldAction, + (const void**)&toUOldContext, + &errorCode); + + } + else{ + + ucnv_setToUCallBack(conv, + UCNV_TO_U_CALLBACK_STOP, + toUNewContext, + &toUOldAction, + (const void**)&toUOldContext, + &errorCode); + + } + return errorCode; + } + errorCode = U_ILLEGAL_ARGUMENT_ERROR; + return errorCode; +} +/** + * Converts a buffer of Unicode code units to target encoding + * @param env environment handle for JNI + * @param jClass handle for the class + * @param handle address of ICU converter + * @param source buffer of Unicode chars to convert + * @param sourceEnd limit of the source buffer + * @param target buffer to recieve the converted bytes + * @param targetEnd the limit of the target buffer + * @param data buffer to recieve state of the current conversion + * @param flush boolean that specifies end of source input + */ +static jint convertCharToByte(JNIEnv *env, jclass jClass, jlong handle, jcharArray source, jint sourceEnd, jbyteArray target, jint targetEnd, jintArray data, jboolean flush) { + + + UErrorCode errorCode =U_ZERO_ERROR; + UConverter* cnv = (UConverter*)handle; + if(cnv) { + jint* myData = (jint*) (*env)->GetPrimitiveArrayCritical(env,data,NULL); + if(myData) { + jint* sourceOffset = &myData[0]; + jint* targetOffset = &myData[1]; + const jchar* uSource =(jchar*) (*env)->GetPrimitiveArrayCritical(env,source, NULL); + if(uSource) { + jbyte* uTarget=(jbyte*) (*env)->GetPrimitiveArrayCritical(env,target,NULL); + if(uTarget) { + const jchar* mySource = uSource+ *sourceOffset; + const UChar* mySourceLimit= uSource+sourceEnd; + char* cTarget=uTarget+ *targetOffset; + const char* cTargetLimit=uTarget+targetEnd; + + ucnv_fromUnicode( cnv , &cTarget, cTargetLimit,&mySource, + mySourceLimit,NULL,(UBool) flush, &errorCode); + + *sourceOffset = (jint) (mySource - uSource)-*sourceOffset; + *targetOffset = (jint) ((jbyte*)cTarget - uTarget)- *targetOffset; + if(U_FAILURE(errorCode)) { + (*env)->ReleasePrimitiveArrayCritical(env,target,uTarget,0); + (*env)->ReleasePrimitiveArrayCritical(env,source,(jchar*)uSource,0); + (*env)->ReleasePrimitiveArrayCritical(env,data,(jint*)myData,0); + return errorCode; + } + }else{ + errorCode = U_ILLEGAL_ARGUMENT_ERROR; + } + (*env)->ReleasePrimitiveArrayCritical(env,target,uTarget,0); + }else{ + errorCode = U_ILLEGAL_ARGUMENT_ERROR; + } + (*env)->ReleasePrimitiveArrayCritical(env,source,(jchar*)uSource,0); + }else{ + errorCode = U_ILLEGAL_ARGUMENT_ERROR; + } + (*env)->ReleasePrimitiveArrayCritical(env,data,(jint*)myData,0); + return errorCode; + } + errorCode = U_ILLEGAL_ARGUMENT_ERROR; + return errorCode; +} + +static jint encode(JNIEnv *env, jclass jClass, jlong handle, jcharArray source, jint sourceEnd, jbyteArray target, jint targetEnd, jintArray data, jboolean flush) { + + UErrorCode ec = convertCharToByte(env,jClass,handle,source,sourceEnd, target,targetEnd,data,flush); + UConverter* cnv = (UConverter*)handle; + jint* myData = (jint*) (*env)->GetPrimitiveArrayCritical(env,data,NULL); + + if(cnv && myData) { + + UErrorCode errorCode = U_ZERO_ERROR; + myData[3] = ucnv_fromUCountPending(cnv, &errorCode); + + if(ec == U_ILLEGAL_CHAR_FOUND || ec == U_INVALID_CHAR_FOUND) { + int8_t count =32; + UChar invalidUChars[32]; + ucnv_getInvalidUChars(cnv,invalidUChars,&count,&errorCode); + + if(U_SUCCESS(errorCode)) { + myData[2] = count; + } + } + } + (*env)->ReleasePrimitiveArrayCritical(env,data,(jint*)myData,0); + return ec; +} + +/** + * Converts a buffer of encoded bytes to Unicode code units + * @param env environment handle for JNI + * @param jClass handle for the class + * @param handle address of ICU converter + * @param source buffer of Unicode chars to convert + * @param sourceEnd limit of the source buffer + * @param target buffer to recieve the converted bytes + * @param targetEnd the limit of the target buffer + * @param data buffer to recieve state of the current conversion + * @param flush boolean that specifies end of source input + */ +static jint convertByteToChar(JNIEnv *env, jclass jClass, jlong handle, jbyteArray source, jint sourceEnd, jcharArray target, jint targetEnd, jintArray data, jboolean flush) { + + UErrorCode errorCode =U_ZERO_ERROR; + UConverter* cnv = (UConverter*)handle; + if(cnv) { + jint* myData = (jint*) (*env)->GetPrimitiveArrayCritical(env,data,NULL); + if(myData) { + jint* sourceOffset = &myData[0]; + jint* targetOffset = &myData[1]; + + const jbyte* uSource =(jbyte*) (*env)->GetPrimitiveArrayCritical(env,source, NULL); + if(uSource) { + jchar* uTarget=(jchar*) (*env)->GetPrimitiveArrayCritical(env,target,NULL); + if(uTarget) { + const jbyte* mySource = uSource+ *sourceOffset; + const char* mySourceLimit= uSource+sourceEnd; + UChar* cTarget=uTarget+ *targetOffset; + const UChar* cTargetLimit=uTarget+targetEnd; + + ucnv_toUnicode( cnv , &cTarget, cTargetLimit,(const char**)&mySource, + mySourceLimit,NULL,(UBool) flush, &errorCode); + + *sourceOffset = mySource - uSource - *sourceOffset ; + *targetOffset = cTarget - uTarget - *targetOffset; + if(U_FAILURE(errorCode)) { + (*env)->ReleasePrimitiveArrayCritical(env,target,uTarget,0); + (*env)->ReleasePrimitiveArrayCritical(env,source,(jchar*)uSource,0); + (*env)->ReleasePrimitiveArrayCritical(env,data,(jint*)myData,0); + return errorCode; + } + }else{ + errorCode = U_ILLEGAL_ARGUMENT_ERROR; + } + (*env)->ReleasePrimitiveArrayCritical(env,target,uTarget,0); + }else{ + errorCode = U_ILLEGAL_ARGUMENT_ERROR; + } + (*env)->ReleasePrimitiveArrayCritical(env,source,(jchar*)uSource,0); + }else{ + errorCode = U_ILLEGAL_ARGUMENT_ERROR; + } + (*env)->ReleasePrimitiveArrayCritical(env,data,(jint*)myData,0); + return errorCode; + } + errorCode = U_ILLEGAL_ARGUMENT_ERROR; + return errorCode; +} + +static jint decode(JNIEnv *env, jclass jClass, jlong handle, jbyteArray source, jint sourceEnd, jcharArray target, jint targetEnd, jintArray data, jboolean flush) { + + jint ec = convertByteToChar(env, jClass,handle,source,sourceEnd, target,targetEnd,data,flush); + + jint* myData = (jint*) (*env)->GetPrimitiveArrayCritical(env,data,NULL); + UConverter* cnv = (UConverter*)handle; + + if(myData && cnv) { + UErrorCode errorCode = U_ZERO_ERROR; + myData[3] = ucnv_toUCountPending(cnv, &errorCode); + + if(ec == U_ILLEGAL_CHAR_FOUND || ec == U_INVALID_CHAR_FOUND ) { + char invalidChars[32] = {'\0'}; + int8_t len = 32; + ucnv_getInvalidChars(cnv,invalidChars,&len,&errorCode); + + if(U_SUCCESS(errorCode)) { + myData[2] = len; + } + } + } + (*env)->ReleasePrimitiveArrayCritical(env,data,(jint*)myData,0); + return ec; +} +static void resetByteToChar(JNIEnv *env, jclass jClass, jlong handle) { + + UConverter* cnv = (UConverter*)handle; + if(cnv) { + ucnv_resetToUnicode(cnv); + } +} + +static void resetCharToByte(JNIEnv *env, jclass jClass, jlong handle) { + + UConverter* cnv = (UConverter*)handle; + if(cnv) { + ucnv_resetFromUnicode(cnv); + } + +} + +static jint countInvalidBytes (JNIEnv *env, jclass jClass, jlong handle, jintArray length) { + + UConverter* cnv = (UConverter*)handle; + UErrorCode errorCode = U_ZERO_ERROR; + if(cnv) { + char invalidChars[32]; + + jint* len = (jint*) (*env)->GetPrimitiveArrayCritical(env,length, NULL); + if(len) { + ucnv_getInvalidChars(cnv,invalidChars,(int8_t*)len,&errorCode); + } + (*env)->ReleasePrimitiveArrayCritical(env,length,(jint*)len,0); + return errorCode; + } + errorCode = U_ILLEGAL_ARGUMENT_ERROR; + return errorCode; + +} + + +static jint countInvalidChars(JNIEnv *env, jclass jClass, jlong handle, jintArray length) { + + UErrorCode errorCode =U_ZERO_ERROR; + UConverter* cnv = (UConverter*)handle; + UChar invalidUChars[32]; + if(cnv) { + jint* len = (jint*) (*env)->GetPrimitiveArrayCritical(env,length, NULL); + if(len) { + ucnv_getInvalidUChars(cnv,invalidUChars,(int8_t*)len,&errorCode); + } + (*env)->ReleasePrimitiveArrayCritical(env,length,(jint*)len,0); + return errorCode; + } + errorCode = U_ILLEGAL_ARGUMENT_ERROR; + return errorCode; + +} + +static jint getMaxBytesPerChar(JNIEnv *env, jclass jClass, jlong handle) { + + UConverter* cnv = (UConverter*)handle; + if(cnv) { + return (jint)ucnv_getMaxCharSize(cnv); + } + return -1; +} + +static jint getMinBytesPerChar(JNIEnv *env, jclass jClass, jlong handle) { + + UConverter* cnv = (UConverter*)handle; + if(cnv) { + return (jint)ucnv_getMinCharSize(cnv); + } + return -1; +} +static jfloat getAveBytesPerChar(JNIEnv *env, jclass jClass, jlong handle) { + + UConverter* cnv = (UConverter*)handle; + if(cnv) { + jfloat max = (jfloat)ucnv_getMaxCharSize(cnv); + jfloat min = (jfloat)ucnv_getMinCharSize(cnv); + return (jfloat) ( (max+min)/2 ); + } + return -1; +} +static jint flushByteToChar(JNIEnv *env, jclass jClass,jlong handle, jcharArray target, jint targetEnd, jintArray data) { + + UErrorCode errorCode =U_ZERO_ERROR; + UConverter* cnv = (UConverter*)handle; + if(cnv) { + jbyte source ='\0'; + jint* myData = (jint*) (*env)->GetPrimitiveArrayCritical(env,data,NULL); + if(myData) { + jint* targetOffset = &myData[1]; + jchar* uTarget=(jchar*) (*env)->GetPrimitiveArrayCritical(env,target,NULL); + if(uTarget) { + const jbyte* mySource =&source; + const char* mySourceLimit=&source; + UChar* cTarget=uTarget+ *targetOffset; + const UChar* cTargetLimit=uTarget+targetEnd; + + ucnv_toUnicode( cnv , &cTarget, cTargetLimit,(const char**)&mySource, + mySourceLimit,NULL,TRUE, &errorCode); + + + *targetOffset = (jint) ((jchar*)cTarget - uTarget)- *targetOffset; + if(U_FAILURE(errorCode)) { + (*env)->ReleasePrimitiveArrayCritical(env,target,uTarget,0); + (*env)->ReleasePrimitiveArrayCritical(env,data,(jint*)myData,0); + return errorCode; + } + }else{ + errorCode = U_ILLEGAL_ARGUMENT_ERROR; + } + (*env)->ReleasePrimitiveArrayCritical(env,target,uTarget,0); + + }else{ + errorCode = U_ILLEGAL_ARGUMENT_ERROR; + } + (*env)->ReleasePrimitiveArrayCritical(env,data,(jint*)myData,0); + return errorCode; + } + errorCode = U_ILLEGAL_ARGUMENT_ERROR; + return errorCode; +} + +static jint flushCharToByte (JNIEnv *env, jclass jClass, jlong handle, jbyteArray target, jint targetEnd, jintArray data) { + + UErrorCode errorCode =U_ZERO_ERROR; + UConverter* cnv = (UConverter*)handle; + jchar source = '\0'; + if(cnv) { + jint* myData = (jint*) (*env)->GetPrimitiveArrayCritical(env,data,NULL); + if(myData) { + jint* targetOffset = &myData[1]; + jbyte* uTarget=(jbyte*) (*env)->GetPrimitiveArrayCritical(env,target,NULL); + if(uTarget) { + const jchar* mySource = &source; + const UChar* mySourceLimit= &source; + char* cTarget=uTarget+ *targetOffset; + const char* cTargetLimit=uTarget+targetEnd; + + ucnv_fromUnicode( cnv , &cTarget, cTargetLimit,&mySource, + mySourceLimit,NULL,TRUE, &errorCode); + + + *targetOffset = (jint) ((jbyte*)cTarget - uTarget)- *targetOffset; + if(U_FAILURE(errorCode)) { + (*env)->ReleasePrimitiveArrayCritical(env,target,uTarget,0); + + (*env)->ReleasePrimitiveArrayCritical(env,data,(jint*)myData,0); + return errorCode; + } + }else{ + errorCode = U_ILLEGAL_ARGUMENT_ERROR; + } + (*env)->ReleasePrimitiveArrayCritical(env,target,uTarget,0); + }else{ + errorCode = U_ILLEGAL_ARGUMENT_ERROR; + } + (*env)->ReleasePrimitiveArrayCritical(env,data,(jint*)myData,0); + return errorCode; + } + errorCode = U_ILLEGAL_ARGUMENT_ERROR; + return errorCode; +} + +void toChars(const UChar* us, char* cs, int32_t length) { + UChar u; + while(length>0) { + u=*us++; + *cs++=(char)u; + --length; + } +} +static jint setSubstitutionBytes(JNIEnv *env, jclass jClass, jlong handle, jbyteArray subChars, jint length) { + + UConverter* cnv = (UConverter*) handle; + UErrorCode errorCode = U_ZERO_ERROR; + if(cnv) { + jbyte* u_subChars = (*env)->GetPrimitiveArrayCritical(env,subChars,NULL); + if(u_subChars) { + char* mySubChars= (char*)malloc(sizeof(char)*length); + toChars((UChar*)u_subChars,&mySubChars[0],length); + ucnv_setSubstChars(cnv,mySubChars, (char)length,&errorCode); + if(U_FAILURE(errorCode)) { + (*env)->ReleasePrimitiveArrayCritical(env,subChars,mySubChars,0); + return errorCode; + } + free(mySubChars); + } + else{ + errorCode = U_ILLEGAL_ARGUMENT_ERROR; + } + (*env)->ReleasePrimitiveArrayCritical(env,subChars,u_subChars,0); + return errorCode; + } + errorCode = U_ILLEGAL_ARGUMENT_ERROR; + return errorCode; +} + + +#define VALUE_STRING_LENGTH 32 + +typedef struct{ + int length; + UChar subChars[256]; + UBool stopOnIllegal; +}SubCharStruct; + + +static UErrorCode +setToUCallbackSubs(UConverter* cnv,UChar* subChars, int32_t length,UBool stopOnIllegal ) { + SubCharStruct* substitutionCharS = (SubCharStruct*) malloc(sizeof(SubCharStruct)); + UErrorCode errorCode = U_ZERO_ERROR; + if(substitutionCharS) { + UConverterToUCallback toUOldAction; + void* toUOldContext=NULL; + void* toUNewContext=NULL ; + if(subChars) { + u_strncpy(substitutionCharS->subChars,subChars,length); + }else{ + substitutionCharS->subChars[length++] =0xFFFD; + } + substitutionCharS->subChars[length]=0; + substitutionCharS->length = length; + substitutionCharS->stopOnIllegal = stopOnIllegal; + toUNewContext = substitutionCharS; + + ucnv_setToUCallBack(cnv, + JNI_TO_U_CALLBACK_SUBSTITUTE, + toUNewContext, + &toUOldAction, + (const void**)&toUOldContext, + &errorCode); + + if(toUOldContext) { + SubCharStruct* temp = (SubCharStruct*) toUOldContext; + free(temp); + } + + return errorCode; + } + return U_MEMORY_ALLOCATION_ERROR; +} +static jint setSubstitutionChars(JNIEnv *env, jclass jClass, jlong handle, jcharArray subChars, jint length) { + + UErrorCode errorCode = U_ZERO_ERROR; + UConverter* cnv = (UConverter*) handle; + jchar* u_subChars=NULL; + if(cnv) { + if(subChars) { + int len = (*env)->GetArrayLength(env,subChars); + u_subChars = (*env)->GetPrimitiveArrayCritical(env,subChars,NULL); + if(u_subChars) { + errorCode = setToUCallbackSubs(cnv,u_subChars,len,FALSE); + }else{ + errorCode = U_ILLEGAL_ARGUMENT_ERROR; + } + (*env)->ReleasePrimitiveArrayCritical(env,subChars,u_subChars,0); + return errorCode; + } + } + return U_ILLEGAL_ARGUMENT_ERROR; +} + + +void JNI_TO_U_CALLBACK_SUBSTITUTE( const void *context, UConverterToUnicodeArgs *toArgs, const char* codeUnits, int32_t length, UConverterCallbackReason reason, UErrorCode * err) { + + if(context) { + SubCharStruct* temp = (SubCharStruct*)context; + if( temp) { + if(temp->stopOnIllegal==FALSE) { + if (reason > UCNV_IRREGULAR) { + return; + } + /* reset the error */ + *err = U_ZERO_ERROR; + ucnv_cbToUWriteUChars(toArgs,temp->subChars ,temp->length , 0, err); + }else{ + if(reason != UCNV_UNASSIGNED) { + /* the caller must have set + * the error code accordingly + */ + return; + }else{ + *err = U_ZERO_ERROR; + ucnv_cbToUWriteUChars(toArgs,temp->subChars ,temp->length , 0, err); + return; + } + } + } + } + return; +} + +static jboolean canEncode(JNIEnv *env, jclass jClass, jlong handle, jint codeUnit) { + + UErrorCode errorCode =U_ZERO_ERROR; + UConverter* cnv = (UConverter*)handle; + if(cnv) { + UChar source[3]; + UChar *mySource=source; + const UChar* sourceLimit = (codeUnit<0x010000) ? &source[1] : &source[2]; + char target[5]; + char *myTarget = target; + const char* targetLimit = &target[4]; + int i=0; + UTF_APPEND_CHAR(&source[0],i,2,codeUnit); + + ucnv_fromUnicode(cnv,&myTarget,targetLimit, + (const UChar**)&mySource, + sourceLimit,NULL, TRUE,&errorCode); + + if(U_SUCCESS(errorCode)) { + return (jboolean)TRUE; + } + } + return (jboolean)FALSE; +} + + +static jboolean canDecode(JNIEnv *env, jclass jClass, jlong handle, jbyteArray source) { + + UErrorCode errorCode =U_ZERO_ERROR; + UConverter* cnv = (UConverter*)handle; + if(cnv) { + jint len = (*env)->GetArrayLength(env,source); + jbyte* cSource =(jbyte*) (*env)->GetPrimitiveArrayCritical(env,source, NULL); + if(cSource) { + const jbyte* cSourceLimit = cSource+len; + + /* Assume that we need at most twice the length of source */ + UChar* target = (UChar*) malloc(sizeof(UChar)* (len<<1)); + UChar* targetLimit = target + (len<<1); + if(target) { + ucnv_toUnicode(cnv,&target,targetLimit, + (const char**)&cSource, + cSourceLimit,NULL, TRUE,&errorCode); + + if(U_SUCCESS(errorCode)) { + free(target); + (*env)->ReleasePrimitiveArrayCritical(env,source,cSource,0); + return (jboolean)TRUE; + } + } + free(target); + } + (*env)->ReleasePrimitiveArrayCritical(env,source,cSource,0); + } + return (jboolean)FALSE; +} + +static jint countAvailable(JNIEnv *env, jclass jClass) { + return ucnv_countAvailable(); +} + +int32_t copyString(char* dest, int32_t destCapacity, int32_t startIndex, + const char* src, UErrorCode* status) { + int32_t srcLen = 0, i=0; + if(U_FAILURE(*status)) { + return 0; + } + if(dest == NULL || src == NULL || destCapacity < startIndex) { + *status = U_ILLEGAL_ARGUMENT_ERROR; + return 0; + } + srcLen = strlen(src); + if(srcLen >= destCapacity) { + *status = U_BUFFER_OVERFLOW_ERROR; + return 0; + } + for(i=0; i < srcLen; i++) { + dest[startIndex++] = src[i]; + } + /* null terminate the buffer */ + dest[startIndex] = 0; /* no bounds check already made sure that we have enough room */ + return startIndex; +} + +int32_t getJavaCanonicalName1(const char* icuCanonicalName, + char* canonicalName, int32_t capacity, + UErrorCode* status) { + /* + If a charset listed in the IANA Charset Registry is supported by an implementation + of the Java platform then its canonical name must be the name listed in the registry. + Many charsets are given more than one name in the registry, in which case the registry + identifies one of the names as MIME-preferred. If a charset has more than one registry + name then its canonical name must be the MIME-preferred name and the other names in + the registry must be valid aliases. If a supported charset is not listed in the IANA + registry then its canonical name must begin with one of the strings "X-" or "x-". + */ + int32_t retLen = 0; + const char* cName = NULL; + /* find out the alias with MIME tag */ + if((cName =ucnv_getStandardName(icuCanonicalName, "MIME", status)) != NULL) { + retLen = copyString(canonicalName, capacity, 0, cName, status); + /* find out the alias with IANA tag */ + }else if((cName =ucnv_getStandardName(icuCanonicalName, "IANA", status)) != NULL) { + retLen = copyString(canonicalName, capacity, 0, cName, status); + }else { + /* + check to see if an alias already exists with x- prefix, if yes then + make that the canonical name + */ + int32_t aliasNum = ucnv_countAliases(icuCanonicalName,status); + int32_t i=0; + const char* name; + for(i=0;i<aliasNum;i++) { + name = ucnv_getAlias(icuCanonicalName,(uint16_t)i, status); + if(name != NULL && name[0]=='x' && name[1]=='-') { + retLen = copyString(canonicalName, capacity, 0, name, status); + break; + } + } + /* last resort just append x- to any of the alias and + make it the canonical name */ + if(retLen == 0 && U_SUCCESS(*status)) { + name = ucnv_getStandardName(icuCanonicalName, "UTR22", status); + if(name == NULL && strchr(icuCanonicalName, ',')!= NULL) { + name = ucnv_getAlias(icuCanonicalName, 1, status); + if(*status == U_INDEX_OUTOFBOUNDS_ERROR) { + *status = U_ZERO_ERROR; + } + } + /* if there is no UTR22 canonical name .. then just return itself*/ + if(name == NULL) { + name = icuCanonicalName; + } + if(capacity >= 2) { + strcpy(canonicalName,"x-"); + } + retLen = copyString(canonicalName, capacity, 2, name, status); + } + } + return retLen; +} + +static jobjectArray getAvailable(JNIEnv *env, jclass jClass) { + + jobjectArray ret; + int32_t i = 0; + int32_t num = ucnv_countAvailable(); + UErrorCode error = U_ZERO_ERROR; + const char* name =NULL; + char canonicalName[256]={0}; + ret= (jobjectArray)(*env)->NewObjectArray( env,num, + (*env)->FindClass(env,"java/lang/String"), + (*env)->NewStringUTF(env,"")); + + for(i=0;i<num;i++) { + name = ucnv_getAvailableName(i); + getJavaCanonicalName1(name, canonicalName, 256, &error); +#if DEBUG + if(U_FAILURE(error)) { + printf("An error occurred retrieving index %i. Error: %s. \n", i, u_errorName(error)); + } + + printf("canonical name for %s\n", canonicalName); +#endif + // BEGIN android-changed + jstring canonName = (*env)->NewStringUTF(env,canonicalName); + (*env)->SetObjectArrayElement(env,ret,i,canonName); + (*env)->DeleteLocalRef(env, canonName); + // END android-changed + /*printf("canonical name : %s at %i\n", name,i); */ + canonicalName[0]='\0';/* nul terminate */ + } + return (ret); +} + +static jint countAliases(JNIEnv *env, jclass jClass,jstring enc) { + + UErrorCode error = U_ZERO_ERROR; + jint num =0; + const char* encName = (*env)->GetStringUTFChars(env,enc,NULL); + + if(encName) { + num = ucnv_countAliases(encName,&error); + } + + (*env)->ReleaseStringUTFChars(env,enc,encName); + + return num; +} + + +static jobjectArray getAliases(JNIEnv *env, jclass jClass, jstring enc) { + + jobjectArray ret=NULL; + int32_t aliasNum = 0; + UErrorCode error = U_ZERO_ERROR; + const char* encName = (*env)->GetStringUTFChars(env,enc,NULL); + int i=0; + int j=0; + const char* aliasArray[50]; + // BEGIN android-removed + // int32_t utf16AliasNum = 0; + // END android-removed + + + if(encName) { + const char* myEncName = encName; + aliasNum = ucnv_countAliases(myEncName,&error); + + // BEGIN android-removed + // /* special case for UTF-16. In java UTF-16 is always BE*/ + // if(strcmp(myEncName, UTF_16BE)==0) { + // utf16AliasNum=ucnv_countAliases(UTF_16,&error); + // } + // END android-removed + + if(aliasNum==0 && encName[0] == 0x78 /*x*/ && encName[1]== 0x2d /*-*/) { + myEncName = encName+2; + aliasNum = ucnv_countAliases(myEncName,&error); + } + if(U_SUCCESS(error)) { + for(i=0,j=0;i<aliasNum;i++) { + const char* name = ucnv_getAlias(myEncName,(uint16_t)i,&error); + if(strchr(name,'+')==0 && strchr(name,',')==0) { + aliasArray[j++]= name; + } + } + + // BEGIN android-removed + // if(utf16AliasNum>0) { + // for(i=0;i<utf16AliasNum;i++) { + // const char* name = ucnv_getAlias(UTF_16,(uint16_t)i,&error); + // if(strchr(name,'+')==0 && strchr(name,',')==0) { + // aliasArray[j++]= name; + // } + // } + // } + // END android-removed + + ret = (jobjectArray)(*env)->NewObjectArray(env,j, + (*env)->FindClass(env,"java/lang/String"), + (*env)->NewStringUTF(env,"")); + for(;--j>=0;) { + // BEGIN android-changed + jstring alias = (*env)->NewStringUTF(env, aliasArray[j]); + (*env)->SetObjectArrayElement(env, ret, j, alias); + (*env)->DeleteLocalRef(env, alias); + // END android-changed + } + } + } + (*env)->ReleaseStringUTFChars(env,enc,encName); + + return (ret); +} + +static jstring getCanonicalName(JNIEnv *env, jclass jClass,jstring enc) { + + UErrorCode error = U_ZERO_ERROR; + const char* encName = (*env)->GetStringUTFChars(env,enc,NULL); + const char* canonicalName = ""; + // BEGIN android-changed + jstring ret = NULL; + if(encName) { + canonicalName = ucnv_getAlias(encName,0,&error); + if(canonicalName !=NULL && strstr(canonicalName,",")!=0) { + canonicalName = ucnv_getAlias(canonicalName,1,&error); + } + ret = ((*env)->NewStringUTF(env, canonicalName)); + (*env)->ReleaseStringUTFChars(env,enc,encName); + } + // END android-changed + return ret; +} + +static jstring getICUCanonicalName(JNIEnv *env, jclass jClass, jstring enc) { + + UErrorCode error = U_ZERO_ERROR; + const char* encName = (*env)->GetStringUTFChars(env,enc,NULL); + const char* canonicalName = NULL; + jstring ret = NULL; + if(encName) { + // BEGIN android-removed + // if(strcmp(encName,"UTF-16")==0) { + // ret = ((*env)->NewStringUTF(env,UTF_16BE)); + // }else + // END android-removed + if((canonicalName = ucnv_getCanonicalName(encName, "MIME", &error))!=NULL) { + ret = ((*env)->NewStringUTF(env, canonicalName)); + }else if((canonicalName = ucnv_getCanonicalName(encName, "IANA", &error))!=NULL) { + ret = ((*env)->NewStringUTF(env, canonicalName)); + }else if((canonicalName = ucnv_getCanonicalName(encName, "", &error))!=NULL) { + ret = ((*env)->NewStringUTF(env, canonicalName)); + }else if((canonicalName = ucnv_getAlias(encName, 0, &error)) != NULL) { + /* we have some aliases in the form x-blah .. match those first */ + ret = ((*env)->NewStringUTF(env, canonicalName)); + }else if( ret ==NULL && strstr(encName, "x-") == encName) { + /* check if the converter can be opened with the encName given */ + UConverter* conv = NULL; + error = U_ZERO_ERROR; + conv = ucnv_open(encName+2, &error); + if(conv!=NULL) { + ret = ((*env)->NewStringUTF(env, encName+2)); + }else{ + /* unsupported encoding */ + ret = ((*env)->NewStringUTF(env, "")); + } + ucnv_close(conv); + }else{ + /* unsupported encoding */ + ret = ((*env)->NewStringUTF(env, "")); + } + } + (*env)->ReleaseStringUTFChars(env,enc,encName); + return ret; +} + +static jstring getJavaCanonicalName2(JNIEnv *env, jclass jClass, jstring icuCanonName) { + /* + If a charset listed in the IANA Charset Registry is supported by an implementation + of the Java platform then its canonical name must be the name listed in the registry. + Many charsets are given more than one name in the registry, in which case the registry + identifies one of the names as MIME-preferred. If a charset has more than one registry + name then its canonical name must be the MIME-preferred name and the other names in + the registry must be valid aliases. If a supported charset is not listed in the IANA + registry then its canonical name must begin with one of the strings "X-" or "x-". + */ + UErrorCode error = U_ZERO_ERROR; + const char* icuCanonicalName = (*env)->GetStringUTFChars(env,icuCanonName,NULL); + char cName[UCNV_MAX_CONVERTER_NAME_LENGTH] = {0}; + jstring ret; + if(icuCanonicalName && icuCanonicalName[0] != 0) { + getJavaCanonicalName1(icuCanonicalName, cName, UCNV_MAX_CONVERTER_NAME_LENGTH, &error); + } + ret = ((*env)->NewStringUTF(env, cName)); + (*env)->ReleaseStringUTFChars(env,icuCanonName,icuCanonicalName); + return ret; +} + +#define SUBS_ARRAY_CAPACITY 256 +typedef struct{ + int length; + char subChars[SUBS_ARRAY_CAPACITY]; + UConverterFromUCallback onUnmappableInput; + UConverterFromUCallback onMalformedInput; +}EncoderCallbackContext; + +void CHARSET_ENCODER_CALLBACK(const void *context, + UConverterFromUnicodeArgs *fromArgs, + const UChar* codeUnits, + int32_t length, + UChar32 codePoint, + UConverterCallbackReason reason, + UErrorCode * status) { + if(context) { + EncoderCallbackContext* ctx = (EncoderCallbackContext*)context; + + if(ctx) { + UConverterFromUCallback realCB = NULL; + switch(reason) { + case UCNV_UNASSIGNED: + realCB = ctx->onUnmappableInput; + break; + case UCNV_ILLEGAL:/*malformed input*/ + case UCNV_IRREGULAR:/*malformed input*/ + realCB = ctx->onMalformedInput; + break; + /* + case UCNV_RESET: + ucnv_resetToUnicode(args->converter); + break; + case UCNV_CLOSE: + ucnv_close(args->converter); + break; + case UCNV_CLONE: + ucnv_clone(args->clone); + */ + default: + *status = U_ILLEGAL_ARGUMENT_ERROR; + return; + } + if(realCB==NULL) { + *status = U_INTERNAL_PROGRAM_ERROR; + } + realCB(context, fromArgs, codeUnits, length, codePoint, reason, status); + } + } +} + +void JNI_FROM_U_CALLBACK_SUBSTITUTE_ENCODER(const void *context, + UConverterFromUnicodeArgs *fromArgs, + const UChar* codeUnits, + int32_t length, + UChar32 codePoint, + UConverterCallbackReason reason, + UErrorCode * err) { + if(context) { + EncoderCallbackContext* temp = (EncoderCallbackContext*)context; + *err = U_ZERO_ERROR; + ucnv_cbFromUWriteBytes(fromArgs,temp->subChars ,temp->length , 0, err); + } + return; +} + +UConverterFromUCallback getFromUCallback(int32_t mode) { + switch(mode) { + default: /* falls through */ + case com_ibm_icu4jni_converters_NativeConverter_STOP_CALLBACK: + return UCNV_FROM_U_CALLBACK_STOP; + case com_ibm_icu4jni_converters_NativeConverter_SKIP_CALLBACK: + return UCNV_FROM_U_CALLBACK_SKIP ; + case com_ibm_icu4jni_converters_NativeConverter_SUBSTITUTE_CALLBACK: + return JNI_FROM_U_CALLBACK_SUBSTITUTE_ENCODER; + } +} + +static jint setCallbackEncode(JNIEnv *env, jclass jClass, jlong handle, jint onMalformedInput, jint onUnmappableInput, jbyteArray subChars, jint length) { + + UConverter* conv = (UConverter*)handle; + UErrorCode errorCode =U_ZERO_ERROR; + + if(conv) { + + UConverterFromUCallback fromUOldAction = NULL; + void* fromUOldContext = NULL; + EncoderCallbackContext* fromUNewContext=NULL; + UConverterFromUCallback fromUNewAction=NULL; + jbyte* sub = (jbyte*) (*env)->GetPrimitiveArrayCritical(env,subChars, NULL); + ucnv_getFromUCallBack(conv, &fromUOldAction, &fromUOldContext); + + /* fromUOldContext can only be DecodeCallbackContext since + the converter created is private data for the decoder + and callbacks can only be set via this method! + */ + if(fromUOldContext==NULL) { + fromUNewContext = (EncoderCallbackContext*) malloc(sizeof(EncoderCallbackContext)); + fromUNewAction = CHARSET_ENCODER_CALLBACK; + }else{ + fromUNewContext = fromUOldContext; + fromUNewAction = fromUOldAction; + fromUOldAction = NULL; + fromUOldContext = NULL; + } + fromUNewContext->onMalformedInput = getFromUCallback(onMalformedInput); + fromUNewContext->onUnmappableInput = getFromUCallback(onUnmappableInput); + // BEGIN android-changed + if(sub!=NULL) { + fromUNewContext->length = length; + strncpy(fromUNewContext->subChars, sub, length); + (*env)->ReleasePrimitiveArrayCritical(env,subChars, sub, 0); + }else{ + errorCode = U_ILLEGAL_ARGUMENT_ERROR; + } + // END android-changed + + ucnv_setFromUCallBack(conv, + fromUNewAction, + fromUNewContext, + &fromUOldAction, + (const void**)&fromUOldContext, + &errorCode); + + + return errorCode; + } + return U_ILLEGAL_ARGUMENT_ERROR; +} + +typedef struct{ + int length; + UChar subUChars[256]; + UConverterToUCallback onUnmappableInput; + UConverterToUCallback onMalformedInput; +}DecoderCallbackContext; + +void JNI_TO_U_CALLBACK_SUBSTITUTE_DECODER(const void *context, + UConverterToUnicodeArgs *toArgs, + const char* codeUnits, + int32_t length, + UConverterCallbackReason reason, + UErrorCode * err) { + if(context) { + DecoderCallbackContext* temp = (DecoderCallbackContext*)context; + *err = U_ZERO_ERROR; + ucnv_cbToUWriteUChars(toArgs,temp->subUChars ,temp->length , 0, err); + } + return; +} + +UConverterToUCallback getToUCallback(int32_t mode) { + switch(mode) { + default: /* falls through */ + case com_ibm_icu4jni_converters_NativeConverter_STOP_CALLBACK: + return UCNV_TO_U_CALLBACK_STOP; + case com_ibm_icu4jni_converters_NativeConverter_SKIP_CALLBACK: + return UCNV_TO_U_CALLBACK_SKIP ; + case com_ibm_icu4jni_converters_NativeConverter_SUBSTITUTE_CALLBACK: + return JNI_TO_U_CALLBACK_SUBSTITUTE_DECODER; + } +} + +void CHARSET_DECODER_CALLBACK(const void *context, + UConverterToUnicodeArgs *args, + const char* codeUnits, + int32_t length, + UConverterCallbackReason reason, + UErrorCode *status ) { + + if(context) { + DecoderCallbackContext* ctx = (DecoderCallbackContext*)context; + + if(ctx) { + UConverterToUCallback realCB = NULL; + switch(reason) { + case UCNV_UNASSIGNED: + realCB = ctx->onUnmappableInput; + break; + case UCNV_ILLEGAL:/*malformed input*/ + case UCNV_IRREGULAR:/*malformed input*/ + realCB = ctx->onMalformedInput; + break; + /* + case UCNV_RESET: + ucnv_resetToUnicode(args->converter); + break; + case UCNV_CLOSE: + ucnv_close(args->converter); + break; + case UCNV_CLONE: + ucnv_clone(args->clone); + */ + default: + *status = U_ILLEGAL_ARGUMENT_ERROR; + return; + } + if(realCB==NULL) { + *status = U_INTERNAL_PROGRAM_ERROR; + } + realCB(context, args, codeUnits, length, reason, status); + } + } +} + +static jint setCallbackDecode(JNIEnv *env, jclass jClass, jlong handle, jint onMalformedInput, jint onUnmappableInput, jcharArray subChars, jint length) { + + UConverter* conv = (UConverter*)handle; + UErrorCode errorCode =U_ZERO_ERROR; + if(conv) { + + UConverterToUCallback toUOldAction ; + void* toUOldContext; + DecoderCallbackContext* toUNewContext = NULL; + UConverterToUCallback toUNewAction = NULL; + jchar* sub = (jchar*) (*env)->GetPrimitiveArrayCritical(env,subChars, NULL); + + ucnv_getToUCallBack(conv, &toUOldAction, &toUOldContext); + + /* toUOldContext can only be DecodeCallbackContext since + the converter created is private data for the decoder + and callbacks can only be set via this method! + */ + if(toUOldContext==NULL) { + toUNewContext = (DecoderCallbackContext*) malloc(sizeof(DecoderCallbackContext)); + toUNewAction = CHARSET_DECODER_CALLBACK; + }else{ + toUNewContext = toUOldContext; + toUNewAction = toUOldAction; + toUOldAction = NULL; + toUOldContext = NULL; + } + toUNewContext->onMalformedInput = getToUCallback(onMalformedInput); + toUNewContext->onUnmappableInput = getToUCallback(onUnmappableInput); + // BEGIN android-changed + if(sub!=NULL) { + toUNewContext->length = length; + u_strncpy(toUNewContext->subUChars, sub, length); + (*env)->ReleasePrimitiveArrayCritical(env,subChars, sub, 0); + }else{ + errorCode = U_ILLEGAL_ARGUMENT_ERROR; + } + // END android-changed + ucnv_setToUCallBack(conv, + toUNewAction, + toUNewContext, + &toUOldAction, + (const void**)&toUOldContext, + &errorCode); + + return errorCode; + } + return U_ILLEGAL_ARGUMENT_ERROR; +} + +static jlong safeClone(JNIEnv *env, jclass jClass, jlong src) { + + UErrorCode status = U_ZERO_ERROR; + + jint buffersize = U_CNV_SAFECLONE_BUFFERSIZE; + + UConverter* conv=NULL; + UErrorCode errorCode = U_ZERO_ERROR; + UConverter* source = (UConverter*) src; + + if(source) { + conv = ucnv_safeClone(source, NULL, &buffersize, &errorCode); + } + + if (icu4jni_error(env, errorCode) != FALSE) { + return NULL; + } + + return conv; +} + +static jint getMaxCharsPerByte(JNIEnv *env, jclass jClass, jlong handle) { + /* + * currently we know that max number of chars per byte is 2 + */ + return 2; +} + +static jfloat getAveCharsPerByte(JNIEnv *env, jclass jClass, jlong handle) { + jfloat ret = 0; + ret = (jfloat)( 1/(jfloat)getMaxBytesPerChar(env, jClass, handle)); + return ret; +} + +void toUChars(const char* cs, UChar* us, int32_t length) { + char c; + while(length>0) { + c=*cs++; + *us++=(char)c; + --length; + } +} + +static jbyteArray getSubstitutionBytes(JNIEnv *env, jclass jClass, jlong handle) { + + const UConverter * cnv = (const UConverter *) handle; + UErrorCode status = U_ZERO_ERROR; + char subBytes[10]; + int8_t len =(char)10; + jbyteArray arr; + if(cnv) { + ucnv_getSubstChars(cnv,subBytes,&len,&status); + if(U_SUCCESS(status)) { + arr = ((*env)->NewByteArray(env, len)); + if(arr) { + (*env)->SetByteArrayRegion(env,arr,0,len,(jbyte*)subBytes); + } + return arr; + } + } + return ((*env)->NewByteArray(env, 0)); +} + +static jboolean contains( JNIEnv *env, jclass jClass, jlong handle1, jlong handle2) { + UErrorCode status = U_ZERO_ERROR; + const UConverter * cnv1 = (const UConverter *) handle1; + const UConverter * cnv2 = (const UConverter *) handle2; + USet* set1; + USet* set2; + UBool bRet = 0; + + if(cnv1 != NULL && cnv2 != NULL) { + /* open charset 1 */ + set1 = uset_open(1, 2); + ucnv_getUnicodeSet(cnv1, set1, UCNV_ROUNDTRIP_SET, &status); + + if(U_SUCCESS(status)) { + /* open charset 2 */ + status = U_ZERO_ERROR; + set2 = uset_open(1, 2); + ucnv_getUnicodeSet(cnv2, set2, UCNV_ROUNDTRIP_SET, &status); + + /* contains? */ + if(U_SUCCESS(status)) { + bRet = uset_containsAll(set1, set2); + uset_close(set2); + } + uset_close(set1); + } + } + return bRet; +} + +/* + * JNI registration + */ +static JNINativeMethod gMethods[] = { + /* name, signature, funcPtr */ + { "convertByteToChar", "(J[BI[CI[IZ)I", (void*) convertByteToChar }, + { "decode", "(J[BI[CI[IZ)I", (void*) decode }, + { "convertCharToByte", "(J[CI[BI[IZ)I", (void*) convertCharToByte }, + { "encode", "(J[CI[BI[IZ)I", (void*) encode }, + { "flushCharToByte", "(J[BI[I)I", (void*) flushCharToByte }, + { "flushByteToChar", "(J[CI[I)I", (void*) flushByteToChar }, + { "openConverter", "(Ljava/lang/String;)J", (void*) openConverter }, + { "resetByteToChar", "(J)V", (void*) resetByteToChar }, + { "resetCharToByte", "(J)V", (void*) resetCharToByte }, + { "closeConverter", "(J)V", (void*) closeConverter }, + { "setSubstitutionChars", "(J[CI)I", (void*) setSubstitutionChars }, + { "setSubstitutionBytes", "(J[BI)I", (void*) setSubstitutionBytes }, + { "setSubstitutionModeCharToByte", "(JZ)I", (void*) setSubstitutionModeCharToByte }, + { "setSubstitutionModeByteToChar", "(JZ)I", (void*) setSubstitutionModeByteToChar }, + { "countInvalidBytes", "(J[I)I", (void*) countInvalidBytes }, + { "countInvalidChars", "(J[I)I", (void*) countInvalidChars }, + { "getMaxBytesPerChar", "(J)I", (void*) getMaxBytesPerChar }, + { "getMinBytesPerChar", "(J)I", (void*) getMinBytesPerChar }, + { "getAveBytesPerChar", "(J)F", (void*) getAveBytesPerChar }, + { "getMaxCharsPerByte", "(J)I", (void*) getMaxCharsPerByte }, + { "getAveCharsPerByte", "(J)F", (void*) getAveCharsPerByte }, + { "contains", "(JJ)Z", (void*) contains }, + { "getSubstitutionBytes", "(J)[B", (void*) getSubstitutionBytes }, + { "canEncode", "(JI)Z", (void*) canEncode }, + { "canDecode", "(J[B)Z", (void*) canDecode }, + { "countAvailable", "()I", (void*) countAvailable }, + { "getAvailable", "()[Ljava/lang/String;", (void*) getAvailable }, + { "countAliases", "(Ljava/lang/String;)I", (void*) countAliases }, + { "getAliases", "(Ljava/lang/String;)[Ljava/lang/String;", (void*) getAliases }, + { "getCanonicalName", "(Ljava/lang/String;)Ljava/lang/String;", (void*) getCanonicalName }, + { "getICUCanonicalName", "(Ljava/lang/String;)Ljava/lang/String;", (void*) getICUCanonicalName }, + { "getJavaCanonicalName", "(Ljava/lang/String;)Ljava/lang/String;", (void*) getJavaCanonicalName2 }, + { "setCallbackDecode", "(JII[CI)I", (void*) setCallbackDecode }, + { "setCallbackEncode", "(JII[BI)I", (void*) setCallbackEncode }, + { "safeClone", "(J)J", (void*) safeClone } +}; + +int register_com_ibm_icu4jni_converters_NativeConverter(JNIEnv *_env) { + return jniRegisterNativeMethods(_env, "com/ibm/icu4jni/charset/NativeConverter", + gMethods, NELEM(gMethods)); +} + + diff --git a/icu/src/main/native/ConverterInterface.h b/icu/src/main/native/ConverterInterface.h new file mode 100644 index 0000000..e9dbf6b --- /dev/null +++ b/icu/src/main/native/ConverterInterface.h @@ -0,0 +1,299 @@ +/* DO NOT EDIT THIS FILE - it is machine generated */ +#include <jni.h> +/* Header for class com_ibm_icu4jni_converters_NativeConverter */ + +#ifndef _Included_com_ibm_icu4jni_converters_NativeConverter +#define _Included_com_ibm_icu4jni_converters_NativeConverter +#ifdef __cplusplus +extern "C" { +#endif +#undef com_ibm_icu4jni_converters_NativeConverter_STOP_CALLBACK +#define com_ibm_icu4jni_converters_NativeConverter_STOP_CALLBACK 0L +#undef com_ibm_icu4jni_converters_NativeConverter_SKIP_CALLBACK +#define com_ibm_icu4jni_converters_NativeConverter_SKIP_CALLBACK 1L +#undef com_ibm_icu4jni_converters_NativeConverter_SUBSTITUTE_CALLBACK +#define com_ibm_icu4jni_converters_NativeConverter_SUBSTITUTE_CALLBACK 2L +/* + * Class: com_ibm_icu4jni_converters_NativeConverter + * Method: convertByteToChar + * Signature: (J[BI[CI[IZ)I + */ +JNIEXPORT jint JNICALL Java_com_ibm_icu4jni_converters_NativeConverter_convertByteToChar + (JNIEnv *, jclass, jlong, jbyteArray, jint, jcharArray, jint, jintArray, jboolean); + +/* + * Class: com_ibm_icu4jni_converters_NativeConverter + * Method: decode + * Signature: (J[BI[CI[IZ)I + */ +JNIEXPORT jint JNICALL Java_com_ibm_icu4jni_converters_NativeConverter_decode + (JNIEnv *, jclass, jlong, jbyteArray, jint, jcharArray, jint, jintArray, jboolean); + +/* + * Class: com_ibm_icu4jni_converters_NativeConverter + * Method: convertCharToByte + * Signature: (J[CI[BI[IZ)I + */ +JNIEXPORT jint JNICALL Java_com_ibm_icu4jni_converters_NativeConverter_convertCharToByte + (JNIEnv *, jclass, jlong, jcharArray, jint, jbyteArray, jint, jintArray, jboolean); + +/* + * Class: com_ibm_icu4jni_converters_NativeConverter + * Method: encode + * Signature: (J[CI[BI[IZ)I + */ +JNIEXPORT jint JNICALL Java_com_ibm_icu4jni_converters_NativeConverter_encode + (JNIEnv *, jclass, jlong, jcharArray, jint, jbyteArray, jint, jintArray, jboolean); + +/* + * Class: com_ibm_icu4jni_converters_NativeConverter + * Method: flushCharToByte + * Signature: (J[BI[I)I + */ +JNIEXPORT jint JNICALL Java_com_ibm_icu4jni_converters_NativeConverter_flushCharToByte + (JNIEnv *, jclass, jlong, jbyteArray, jint, jintArray); + +/* + * Class: com_ibm_icu4jni_converters_NativeConverter + * Method: flushByteToChar + * Signature: (J[CI[I)I + */ +JNIEXPORT jint JNICALL Java_com_ibm_icu4jni_converters_NativeConverter_flushByteToChar + (JNIEnv *, jclass, jlong, jcharArray, jint, jintArray); + +/* + * Class: com_ibm_icu4jni_converters_NativeConverter + * Method: openConverter + * Signature: ([JLjava/lang/String;)I + */ +JNIEXPORT jint JNICALL Java_com_ibm_icu4jni_converters_NativeConverter_openConverter + (JNIEnv *, jclass, jlongArray, jstring); + +/* + * Class: com_ibm_icu4jni_converters_NativeConverter + * Method: resetByteToChar + * Signature: (J)V + */ +JNIEXPORT void JNICALL Java_com_ibm_icu4jni_converters_NativeConverter_resetByteToChar + (JNIEnv *, jclass, jlong); + +/* + * Class: com_ibm_icu4jni_converters_NativeConverter + * Method: resetCharToByte + * Signature: (J)V + */ +JNIEXPORT void JNICALL Java_com_ibm_icu4jni_converters_NativeConverter_resetCharToByte + (JNIEnv *, jclass, jlong); + +/* + * Class: com_ibm_icu4jni_converters_NativeConverter + * Method: closeConverter + * Signature: (J)V + */ +JNIEXPORT void JNICALL Java_com_ibm_icu4jni_converters_NativeConverter_closeConverter + (JNIEnv *, jclass, jlong); + +/* + * Class: com_ibm_icu4jni_converters_NativeConverter + * Method: setSubstitutionChars + * Signature: (J[CI)I + */ +JNIEXPORT jint JNICALL Java_com_ibm_icu4jni_converters_NativeConverter_setSubstitutionChars + (JNIEnv *, jclass, jlong, jcharArray, jint); + +/* + * Class: com_ibm_icu4jni_converters_NativeConverter + * Method: setSubstitutionBytes + * Signature: (J[BI)I + */ +JNIEXPORT jint JNICALL Java_com_ibm_icu4jni_converters_NativeConverter_setSubstitutionBytes + (JNIEnv *, jclass, jlong, jbyteArray, jint); + +/* + * Class: com_ibm_icu4jni_converters_NativeConverter + * Method: setSubstitutionModeCharToByte + * Signature: (JZ)I + */ +JNIEXPORT jint JNICALL Java_com_ibm_icu4jni_converters_NativeConverter_setSubstitutionModeCharToByte + (JNIEnv *, jclass, jlong, jboolean); + +/* + * Class: com_ibm_icu4jni_converters_NativeConverter + * Method: setSubstitutionModeByteToChar + * Signature: (JZ)I + */ +JNIEXPORT jint JNICALL Java_com_ibm_icu4jni_converters_NativeConverter_setSubstitutionModeByteToChar + (JNIEnv *, jclass, jlong, jboolean); + +/* + * Class: com_ibm_icu4jni_converters_NativeConverter + * Method: countInvalidBytes + * Signature: (J[I)I + */ +JNIEXPORT jint JNICALL Java_com_ibm_icu4jni_converters_NativeConverter_countInvalidBytes + (JNIEnv *, jclass, jlong, jintArray); + +/* + * Class: com_ibm_icu4jni_converters_NativeConverter + * Method: countInvalidChars + * Signature: (J[I)I + */ +JNIEXPORT jint JNICALL Java_com_ibm_icu4jni_converters_NativeConverter_countInvalidChars + (JNIEnv *, jclass, jlong, jintArray); + +/* + * Class: com_ibm_icu4jni_converters_NativeConverter + * Method: getMaxBytesPerChar + * Signature: (J)I + */ +JNIEXPORT jint JNICALL Java_com_ibm_icu4jni_converters_NativeConverter_getMaxBytesPerChar + (JNIEnv *, jclass, jlong); + +/* + * Class: com_ibm_icu4jni_converters_NativeConverter + * Method: getMinBytesPerChar + * Signature: (J)I + */ +JNIEXPORT jint JNICALL Java_com_ibm_icu4jni_converters_NativeConverter_getMinBytesPerChar + (JNIEnv *, jclass, jlong); + +/* + * Class: com_ibm_icu4jni_converters_NativeConverter + * Method: getAveBytesPerChar + * Signature: (J)F + */ +JNIEXPORT jfloat JNICALL Java_com_ibm_icu4jni_converters_NativeConverter_getAveBytesPerChar + (JNIEnv *, jclass, jlong); + +/* + * Class: com_ibm_icu4jni_converters_NativeConverter + * Method: getMaxCharsPerByte + * Signature: (J)I + */ +JNIEXPORT jint JNICALL Java_com_ibm_icu4jni_converters_NativeConverter_getMaxCharsPerByte + (JNIEnv *, jclass, jlong); + +/* + * Class: com_ibm_icu4jni_converters_NativeConverter + * Method: getAveCharsPerByte + * Signature: (J)F + */ +JNIEXPORT jfloat JNICALL Java_com_ibm_icu4jni_converters_NativeConverter_getAveCharsPerByte + (JNIEnv *, jclass, jlong); + +/* + * Class: com_ibm_icu4jni_converters_NativeConverter + * Method: contains + * Signature: (JJ)Z + */ +JNIEXPORT jboolean JNICALL Java_com_ibm_icu4jni_converters_NativeConverter_contains + (JNIEnv *, jclass, jlong, jlong); + +/* + * Class: com_ibm_icu4jni_converters_NativeConverter + * Method: getSubstitutionBytes + * Signature: (J)[B + */ +JNIEXPORT jbyteArray JNICALL Java_com_ibm_icu4jni_converters_NativeConverter_getSubstitutionBytes + (JNIEnv *, jclass, jlong); + +/* + * Class: com_ibm_icu4jni_converters_NativeConverter + * Method: canEncode + * Signature: (JI)Z + */ +JNIEXPORT jboolean JNICALL Java_com_ibm_icu4jni_converters_NativeConverter_canEncode + (JNIEnv *, jclass, jlong, jint); + +/* + * Class: com_ibm_icu4jni_converters_NativeConverter + * Method: canDecode + * Signature: (J[B)Z + */ +JNIEXPORT jboolean JNICALL Java_com_ibm_icu4jni_converters_NativeConverter_canDecode + (JNIEnv *, jclass, jlong, jbyteArray); + +/* + * Class: com_ibm_icu4jni_converters_NativeConverter + * Method: countAvailable + * Signature: ()I + */ +JNIEXPORT jint JNICALL Java_com_ibm_icu4jni_converters_NativeConverter_countAvailable + (JNIEnv *, jclass); + +/* + * Class: com_ibm_icu4jni_converters_NativeConverter + * Method: getAvailable + * Signature: ()[Ljava/lang/String; + */ +JNIEXPORT jobjectArray JNICALL Java_com_ibm_icu4jni_converters_NativeConverter_getAvailable + (JNIEnv *, jclass); + +/* + * Class: com_ibm_icu4jni_converters_NativeConverter + * Method: countAliases + * Signature: (Ljava/lang/String;)I + */ +JNIEXPORT jint JNICALL Java_com_ibm_icu4jni_converters_NativeConverter_countAliases + (JNIEnv *, jclass, jstring); + +/* + * Class: com_ibm_icu4jni_converters_NativeConverter + * Method: getAliases + * Signature: (Ljava/lang/String;)[Ljava/lang/String; + */ +JNIEXPORT jobjectArray JNICALL Java_com_ibm_icu4jni_converters_NativeConverter_getAliases + (JNIEnv *, jclass, jstring); + +/* + * Class: com_ibm_icu4jni_converters_NativeConverter + * Method: getCanonicalName + * Signature: (Ljava/lang/String;)Ljava/lang/String; + */ +JNIEXPORT jstring JNICALL Java_com_ibm_icu4jni_converters_NativeConverter_getCanonicalName + (JNIEnv *, jclass, jstring); + +/* + * Class: com_ibm_icu4jni_converters_NativeConverter + * Method: getICUCanonicalName + * Signature: (Ljava/lang/String;)Ljava/lang/String; + */ +JNIEXPORT jstring JNICALL Java_com_ibm_icu4jni_converters_NativeConverter_getICUCanonicalName + (JNIEnv *, jclass, jstring); + +/* + * Class: com_ibm_icu4jni_converters_NativeConverter + * Method: getJavaCanonicalName + * Signature: (Ljava/lang/String;)Ljava/lang/String; + */ +JNIEXPORT jstring JNICALL Java_com_ibm_icu4jni_converters_NativeConverter_getJavaCanonicalName + (JNIEnv *, jclass, jstring); + +/* + * Class: com_ibm_icu4jni_converters_NativeConverter + * Method: setCallbackDecode + * Signature: (JII[CI)I + */ +JNIEXPORT jint JNICALL Java_com_ibm_icu4jni_converters_NativeConverter_setCallbackDecode + (JNIEnv *, jclass, jlong, jint, jint, jcharArray, jint); + +/* + * Class: com_ibm_icu4jni_converters_NativeConverter + * Method: setCallbackEncode + * Signature: (JII[BI)I + */ +JNIEXPORT jint JNICALL Java_com_ibm_icu4jni_converters_NativeConverter_setCallbackEncode + (JNIEnv *, jclass, jlong, jint, jint, jbyteArray, jint); + +/* + * Class: com_ibm_icu4jni_converters_NativeConverter + * Method: safeClone + * Signature: (J[J)I + */ +JNIEXPORT jint JNICALL Java_com_ibm_icu4jni_converters_NativeConverter_safeClone + (JNIEnv *, jclass, jlong, jlongArray); + +#ifdef __cplusplus +} +#endif +#endif diff --git a/icu/src/main/native/DecimalFormatInterface.cpp b/icu/src/main/native/DecimalFormatInterface.cpp new file mode 100644 index 0000000..6221826 --- /dev/null +++ b/icu/src/main/native/DecimalFormatInterface.cpp @@ -0,0 +1,851 @@ +/* + * Copyright 2006 The Android Open Source Project + * + * Internal native functions. All of the functions defined here make + * direct use of VM functions or data structures, so they can't be written + * with JNI and shouldn't really be in a shared library. + * + * All functions here either complete quickly or are used to enter a wait + * state, so we don't set the thread status to THREAD_NATIVE when executing + * these methods. This means that the GC will wait for these functions + * to finish. DO NOT perform long operations or blocking I/O in here. + * + * In some cases we're following the division of labor defined by GNU + * ClassPath, e.g. java.lang.Thread has "Thread" and "VMThread", with + * the VM-specific behavior isolated in VMThread. + */ + +#include "JNIHelp.h" +#include "AndroidSystemNatives.h" +#include "unicode/unum.h" +#include "unicode/numfmt.h" +#include "unicode/decimfmt.h" +#include "unicode/fmtable.h" +#include "unicode/ustring.h" +#include "digitlst.h" +#include "ErrorCode.h" +#include <stdlib.h> +#include <string.h> +#include "cutils/log.h" + +#define LOG_TAG "DecimalFormatInterface" + +static UBool icuError(JNIEnv *env, UErrorCode errorcode) +{ + const char *emsg = u_errorName(errorcode); + jclass exception; + + if (U_FAILURE(errorcode)) {// errorcode > U_ZERO_ERROR && errorcode < U_ERROR_LIMIT) { + switch (errorcode) { + case U_ILLEGAL_ARGUMENT_ERROR : + exception = env->FindClass("java/lang/IllegalArgumentException"); + break; + case U_INDEX_OUTOFBOUNDS_ERROR : + case U_BUFFER_OVERFLOW_ERROR : + exception = env->FindClass("java/lang/ArrayIndexOutOfBoundsException"); + break; + case U_UNSUPPORTED_ERROR : + exception = env->FindClass("java/lang/UnsupportedOperationException"); + break; + default : + exception = env->FindClass("java/lang/RuntimeException"); + } + + return (env->ThrowNew(exception, emsg) != 0); + } + return 0; +} + +static jint openDecimalFormatImpl(JNIEnv *env, jclass clazz, jstring locale, + jstring pattern) { + + // the errorcode returned by unum_open + UErrorCode status = U_ZERO_ERROR; + + // prepare the pattern string for the call to unum_open + const UChar *pattChars = env->GetStringChars(pattern, NULL); + int pattLen = env->GetStringLength(pattern); + + // prepare the locale string for the call to unum_open + const char *localeChars = env->GetStringUTFChars(locale, NULL); + + // open a default type number format + UNumberFormat *fmt = unum_open(UNUM_PATTERN_DECIMAL, pattChars, pattLen, + localeChars, NULL, &status); + + // release the allocated strings + env->ReleaseStringChars(pattern, pattChars); + env->ReleaseStringUTFChars(locale, localeChars); + + // check for an error + if ( icuError(env, status) != FALSE) { + return 0; + } + + // return the handle to the number format + return (long) fmt; +} + +static void closeDecimalFormatImpl(JNIEnv *env, jclass clazz, jint addr) { + + // get the pointer to the number format + UNumberFormat *fmt = (UNumberFormat *)(int)addr; + + // close this number format + unum_close(fmt); +} + +static void setSymbol(JNIEnv *env, jclass clazz, jint addr, jint symbol, + jstring text) { + + // the errorcode returned by unum_setSymbol + UErrorCode status = U_ZERO_ERROR; + + // get the pointer to the number format + UNumberFormat *fmt = (UNumberFormat *)(int)addr; + + // prepare the symbol string for the call to unum_setSymbol + const UChar *textChars = env->GetStringChars(text, NULL); + int textLen = env->GetStringLength(text); + + // set the symbol + unum_setSymbol(fmt, (UNumberFormatSymbol) symbol, textChars, textLen, + &status); + + // release previously allocated space + env->ReleaseStringChars(text, textChars); + + // check if an error occured + icuError(env, status); +} + +static jstring getSymbol(JNIEnv *env, jclass clazz, jint addr, jint symbol) { + + uint32_t resultlength, reslenneeded; + + // the errorcode returned by unum_getSymbol + UErrorCode status = U_ZERO_ERROR; + + // get the pointer to the number format + UNumberFormat *fmt = (UNumberFormat *)(int)addr; + + UChar* result = NULL; + resultlength=0; + + // find out how long the result will be + reslenneeded=unum_getSymbol(fmt, (UNumberFormatSymbol) symbol, result, + resultlength, &status); + + result = NULL; + if(status==U_BUFFER_OVERFLOW_ERROR) { + status=U_ZERO_ERROR; + resultlength=reslenneeded+1; + result=(UChar*)malloc(sizeof(UChar) * resultlength); + reslenneeded=unum_getSymbol(fmt, (UNumberFormatSymbol) symbol, result, + resultlength, &status); + } + if (icuError(env, status) != FALSE) { + return NULL; + } + + jstring res = env->NewString(result, reslenneeded); + + free(result); + + return res; +} + +static void setAttribute(JNIEnv *env, jclass clazz, jint addr, jint symbol, + jint value) { + + UNumberFormat *fmt = (UNumberFormat *)(int)addr; + + unum_setAttribute(fmt, (UNumberFormatAttribute) symbol, value); +} + +static jint getAttribute(JNIEnv *env, jclass clazz, jint addr, jint symbol) { + + UNumberFormat *fmt = (UNumberFormat *)(int)addr; + + int res = unum_getAttribute(fmt, (UNumberFormatAttribute) symbol); + + return res; +} + +static void setTextAttribute(JNIEnv *env, jclass clazz, jint addr, jint symbol, + jstring text) { + + // the errorcode returned by unum_setTextAttribute + UErrorCode status = U_ZERO_ERROR; + + // get the pointer to the number format + UNumberFormat *fmt = (UNumberFormat *)(int)addr; + + const UChar *textChars = env->GetStringChars(text, NULL); + int textLen = env->GetStringLength(text); + + unum_setTextAttribute(fmt, (UNumberFormatTextAttribute) symbol, textChars, + textLen, &status); + + env->ReleaseStringChars(text, textChars); + + icuError(env, status); +} + +static jstring getTextAttribute(JNIEnv *env, jclass clazz, jint addr, + jint symbol) { + + uint32_t resultlength, reslenneeded; + + // the errorcode returned by unum_getTextAttribute + UErrorCode status = U_ZERO_ERROR; + + // get the pointer to the number format + UNumberFormat *fmt = (UNumberFormat *)(int)addr; + + UChar* result = NULL; + resultlength=0; + + // find out how long the result will be + reslenneeded=unum_getTextAttribute(fmt, (UNumberFormatTextAttribute) symbol, + result, resultlength, &status); + + result = NULL; + if(status==U_BUFFER_OVERFLOW_ERROR) { + status=U_ZERO_ERROR; + resultlength=reslenneeded+1; + result=(UChar*)malloc(sizeof(UChar) * resultlength); + reslenneeded=unum_getTextAttribute(fmt, + (UNumberFormatTextAttribute) symbol, result, resultlength, + &status); + } + if (icuError(env, status) != FALSE) { + return NULL; + } + + jstring res = env->NewString(result, reslenneeded); + + free(result); + + return res; +} + +static void applyPatternImpl(JNIEnv *env, jclass clazz, jint addr, + jboolean localized, jstring pattern) { + + // the errorcode returned by unum_applyPattern + UErrorCode status = U_ZERO_ERROR; + + // get the pointer to the number format + UNumberFormat *fmt = (UNumberFormat *)(int)addr; + + const UChar *pattChars = env->GetStringChars(pattern, NULL); + int pattLen = env->GetStringLength(pattern); + + unum_applyPattern(fmt, localized, pattChars, pattLen, NULL, &status); + + env->ReleaseStringChars(pattern, pattChars); + + icuError(env, status); +} + +static jstring toPatternImpl(JNIEnv *env, jclass clazz, jint addr, + jboolean localized) { + + uint32_t resultlength, reslenneeded; + + // the errorcode returned by unum_toPattern + UErrorCode status = U_ZERO_ERROR; + + // get the pointer to the number format + UNumberFormat *fmt = (UNumberFormat *)(int)addr; + + UChar* result = NULL; + resultlength=0; + + // find out how long the result will be + reslenneeded=unum_toPattern(fmt, localized, result, resultlength, &status); + + result = NULL; + if(status==U_BUFFER_OVERFLOW_ERROR) { + status=U_ZERO_ERROR; + resultlength=reslenneeded+1; + result=(UChar*)malloc(sizeof(UChar) * resultlength); + reslenneeded=unum_toPattern(fmt, localized, result, resultlength, + &status); + } + if (icuError(env, status) != FALSE) { + return NULL; + } + + jstring res = env->NewString(result, reslenneeded); + + free(result); + + return res; +} + +static jstring formatLong(JNIEnv *env, jclass clazz, jint addr, jlong value, + jobject field, jstring fieldType, jobject attributes) { + + const char * fieldPositionClassName = "java/text/FieldPosition"; + const char * stringBufferClassName = "java/lang/StringBuffer"; + jclass fieldPositionClass = env->FindClass(fieldPositionClassName); + jclass stringBufferClass = env->FindClass(stringBufferClassName); + jmethodID setBeginIndexMethodID = env->GetMethodID(fieldPositionClass, + "setBeginIndex", "(I)V"); + jmethodID setEndIndexMethodID = env->GetMethodID(fieldPositionClass, + "setEndIndex", "(I)V"); + jmethodID appendMethodID = env->GetMethodID(stringBufferClass, + "append", "(Ljava/lang/String;)Ljava/lang/StringBuffer;"); + + const char * fieldName = NULL; + + if(fieldType != NULL) { + fieldName = env->GetStringUTFChars(fieldType, NULL); + } + + uint32_t reslenneeded; + int64_t val = value; + UChar *result = NULL; + + FieldPosition fp; + fp.setField(FieldPosition::DONT_CARE); + + UErrorCode status = U_ZERO_ERROR; + + DecimalFormat::AttrBuffer attrBuffer = NULL; + attrBuffer = (DecimalFormat::AttrBuffer) malloc(sizeof(*attrBuffer)); + attrBuffer->bufferSize = 128; + attrBuffer->buffer = (char *) malloc(129 * sizeof(char)); + attrBuffer->buffer[0] = '\0'; + + DecimalFormat *fmt = (DecimalFormat *)(int)addr; + + UnicodeString *res = new UnicodeString(); + + fmt->format(val, *res, fp, attrBuffer); + + reslenneeded = res->extract(NULL, 0, status); + + if(status==U_BUFFER_OVERFLOW_ERROR) { + status=U_ZERO_ERROR; + + result = (UChar*)malloc(sizeof(UChar) * (reslenneeded + 1)); + + res->extract(result, reslenneeded + 1, status); + } + if (icuError(env, status) != FALSE) { + free(attrBuffer->buffer); + free(attrBuffer); + free(result); + delete(res); + return NULL; + } + + int attrLength = 0; + + attrLength = (strlen(attrBuffer->buffer) + 1 ); + + if(strlen(attrBuffer->buffer) > 0) { + + // check if we want to get all attributes + if(attributes != NULL) { + jstring attrString = env->NewStringUTF(attrBuffer->buffer + 1); // cut off the leading ';' + env->CallObjectMethod(attributes, appendMethodID, attrString); + } + + // check if we want one special attribute returned in the given FieldPos + if(fieldName != NULL && field != NULL) { + const char *delimiter = ";"; + int begin; + int end; + char * resattr; + resattr = strtok(attrBuffer->buffer, delimiter); + + while(resattr != NULL && strcmp(resattr, fieldName) != 0) { + resattr = strtok(NULL, delimiter); + } + + if(resattr != NULL && strcmp(resattr, fieldName) == 0) { + resattr = strtok(NULL, delimiter); + begin = (int) strtol(resattr, NULL, 10); + resattr = strtok(NULL, delimiter); + end = (int) strtol(resattr, NULL, 10); + + env->CallVoidMethod(field, setBeginIndexMethodID, (jint) begin); + env->CallVoidMethod(field, setEndIndexMethodID, (jint) end); + } + } + } + + if(fieldType != NULL) { + env->ReleaseStringUTFChars(fieldType, fieldName); + } + + jstring resulting = env->NewString(result, reslenneeded); + + free(attrBuffer->buffer); + free(attrBuffer); + free(result); + delete(res); + + return resulting; +} + +static jstring formatDouble(JNIEnv *env, jclass clazz, jint addr, jdouble value, + jobject field, jstring fieldType, jobject attributes) { + + const char * fieldPositionClassName = "java/text/FieldPosition"; + const char * stringBufferClassName = "java/lang/StringBuffer"; + jclass fieldPositionClass = env->FindClass(fieldPositionClassName); + jclass stringBufferClass = env->FindClass(stringBufferClassName); + jmethodID setBeginIndexMethodID = env->GetMethodID(fieldPositionClass, + "setBeginIndex", "(I)V"); + jmethodID setEndIndexMethodID = env->GetMethodID(fieldPositionClass, + "setEndIndex", "(I)V"); + jmethodID appendMethodID = env->GetMethodID(stringBufferClass, + "append", "(Ljava/lang/String;)Ljava/lang/StringBuffer;"); + + const char * fieldName = NULL; + + if(fieldType != NULL) { + fieldName = env->GetStringUTFChars(fieldType, NULL); + } + + uint32_t reslenneeded; + double val = value; + UChar *result = NULL; + + FieldPosition fp; + fp.setField(FieldPosition::DONT_CARE); + + UErrorCode status = U_ZERO_ERROR; + + DecimalFormat::AttrBuffer attrBuffer = NULL; + attrBuffer = (DecimalFormat::AttrBuffer) malloc(sizeof(*attrBuffer)); + attrBuffer->bufferSize = 128; + attrBuffer->buffer = (char *) malloc(129 * sizeof(char)); + attrBuffer->buffer[0] = '\0'; + + DecimalFormat *fmt = (DecimalFormat *)(int)addr; + + UnicodeString *res = new UnicodeString(); + + fmt->format(val, *res, fp, attrBuffer); + + reslenneeded = res->extract(NULL, 0, status); + + if(status==U_BUFFER_OVERFLOW_ERROR) { + status=U_ZERO_ERROR; + + result = (UChar*)malloc(sizeof(UChar) * (reslenneeded + 1)); + + res->extract(result, reslenneeded + 1, status); + + } + if (icuError(env, status) != FALSE) { + free(attrBuffer->buffer); + free(attrBuffer); + free(result); + delete(res); + return NULL; + } + + int attrLength = 0; + + attrLength = (strlen(attrBuffer->buffer) + 1 ); + + if(strlen(attrBuffer->buffer) > 0) { + + // check if we want to get all attributes + if(attributes != NULL) { + jstring attrString = env->NewStringUTF(attrBuffer->buffer + 1); // cut off the leading ';' + env->CallObjectMethod(attributes, appendMethodID, attrString); + } + + // check if we want one special attribute returned in the given FieldPos + if(fieldName != NULL && field != NULL) { + const char *delimiter = ";"; + int begin; + int end; + char * resattr; + resattr = strtok(attrBuffer->buffer, delimiter); + + while(resattr != NULL && strcmp(resattr, fieldName) != 0) { + resattr = strtok(NULL, delimiter); + } + + if(resattr != NULL && strcmp(resattr, fieldName) == 0) { + resattr = strtok(NULL, delimiter); + begin = (int) strtol(resattr, NULL, 10); + resattr = strtok(NULL, delimiter); + end = (int) strtol(resattr, NULL, 10); + + env->CallVoidMethod(field, setBeginIndexMethodID, (jint) begin); + env->CallVoidMethod(field, setEndIndexMethodID, (jint) end); + } + } + } + + if(fieldType != NULL) { + env->ReleaseStringUTFChars(fieldType, fieldName); + } + + jstring resulting = env->NewString(result, reslenneeded); + + free(attrBuffer->buffer); + free(attrBuffer); + free(result); + delete(res); + + return resulting; +} + +static jstring formatDigitList(JNIEnv *env, jclass clazz, jint addr, jstring value, + jobject field, jstring fieldType, jobject attributes, jint scale) { + + // const char * valueUTF = env->GetStringUTFChars(value, NULL); + // LOGI("ENTER formatDigitList: %s, scale: %d", valueUTF, scale); + // env->ReleaseStringUTFChars(value, valueUTF); + + if (scale < 0) { + icuError(env, U_ILLEGAL_ARGUMENT_ERROR); + return NULL; + } + + const char * fieldName = NULL; + if(fieldType != NULL) { + fieldName = env->GetStringUTFChars(fieldType, NULL); + } + + uint32_t reslenneeded; + + // prepare digit list + + const char *valueChars = env->GetStringUTFChars(value, NULL); + + bool isInteger = (scale == 0); + bool isPositive = (*valueChars != '-'); + + // skip the '-' if the number is negative + const char *digits = (isPositive ? valueChars : valueChars + 1); + int length = strlen(digits); + + // The length of our digit list buffer must be the actual string length + 3, + // because ICU will append some additional characters at the head and at the + // tail of the string, in order to keep strtod() happy: + // + // - The sign "+" or "-" is appended at the head + // - The exponent "e" and the "\0" terminator is appended at the tail + // + // In retrospect, the changes to ICU's DigitList that were necessary for + // big numbers look a bit hacky. It would make sense to rework all this + // once ICU 4.x has been integrated into Android. Ideally, big number + // support would make it into ICU itself, so we don't need our private + // fix anymore. + DigitList digitList(length + 3); + digitList.fCount = length; + strcpy(digitList.fDigits, digits); + env->ReleaseStringUTFChars(value, valueChars); + + digitList.fDecimalAt = digitList.fCount - scale; + digitList.fIsPositive = isPositive; + digitList.fRoundingMode = DecimalFormat::kRoundHalfUp; + + UChar *result = NULL; + + FieldPosition fp; + fp.setField(FieldPosition::DONT_CARE); + fp.setBeginIndex(0); + fp.setEndIndex(0); + + UErrorCode status = U_ZERO_ERROR; + + DecimalFormat::AttributeBuffer *attrBuffer = NULL; + attrBuffer = (DecimalFormat::AttributeBuffer *) calloc(sizeof(DecimalFormat::AttributeBuffer), 1); + attrBuffer->bufferSize = 128; + attrBuffer->buffer = (char *) calloc(129 * sizeof(char), 1); + + DecimalFormat *fmt = (DecimalFormat *)(int)addr; + + UnicodeString res; + + fmt->subformat(res, fp, attrBuffer, digitList, isInteger); + + reslenneeded = res.extract(NULL, 0, status); + + if(status==U_BUFFER_OVERFLOW_ERROR) { + status=U_ZERO_ERROR; + + result = (UChar*)malloc(sizeof(UChar) * (reslenneeded + 1)); + + res.extract(result, reslenneeded + 1, status); + + if (icuError(env, status) != FALSE) { + if(fieldType != NULL) { + env->ReleaseStringUTFChars(fieldType, fieldName); + } + free(result); + free(attrBuffer->buffer); + free(attrBuffer); + return NULL; + } + + } else { + if(fieldType != NULL) { + env->ReleaseStringUTFChars(fieldType, fieldName); + } + free(attrBuffer->buffer); + free(attrBuffer); + return NULL; + } + + int attrLength = (strlen(attrBuffer->buffer) + 1 ); + + if(attrLength > 1) { + + // check if we want to get all attributes + if(attributes != NULL) { + // prepare the classes and method ids + const char * stringBufferClassName = "java/lang/StringBuffer"; + jclass stringBufferClass = env->FindClass(stringBufferClassName); + jmethodID appendMethodID = env->GetMethodID(stringBufferClass, + "append", "(Ljava/lang/String;)Ljava/lang/StringBuffer;"); + + jstring attrString = env->NewStringUTF(attrBuffer->buffer + 1); // cut off the leading ';' + env->CallObjectMethod(attributes, appendMethodID, attrString); + } + + // check if we want one special attribute returned in the given FieldPos + if(fieldName != NULL && field != NULL) { + const char *delimiter = ";"; + int begin; + int end; + char * resattr; + resattr = strtok(attrBuffer->buffer, delimiter); + + while(resattr != NULL && strcmp(resattr, fieldName) != 0) { + resattr = strtok(NULL, delimiter); + } + + if(resattr != NULL && strcmp(resattr, fieldName) == 0) { + + // prepare the classes and method ids + const char * fieldPositionClassName = + "java/text/FieldPosition"; + jclass fieldPositionClass = env->FindClass( + fieldPositionClassName); + jmethodID setBeginIndexMethodID = env->GetMethodID( + fieldPositionClass, "setBeginIndex", "(I)V"); + jmethodID setEndIndexMethodID = env->GetMethodID( + fieldPositionClass, "setEndIndex", "(I)V"); + + + resattr = strtok(NULL, delimiter); + begin = (int) strtol(resattr, NULL, 10); + resattr = strtok(NULL, delimiter); + end = (int) strtol(resattr, NULL, 10); + + env->CallVoidMethod(field, setBeginIndexMethodID, (jint) begin); + env->CallVoidMethod(field, setEndIndexMethodID, (jint) end); + } + } + } + + if(fieldType != NULL) { + env->ReleaseStringUTFChars(fieldType, fieldName); + } + + jstring resulting = env->NewString(result, reslenneeded); + + free(attrBuffer->buffer); + free(attrBuffer); + free(result); + // const char * resultUTF = env->GetStringUTFChars(resulting, NULL); + // LOGI("RETURN formatDigitList: %s", resultUTF); + // env->ReleaseStringUTFChars(resulting, resultUTF); + + return resulting; +} + +static jobject parse(JNIEnv *env, jclass clazz, jint addr, jstring text, + jobject position) { + + const char * textUTF = env->GetStringUTFChars(text, NULL); + env->ReleaseStringUTFChars(text, textUTF); + + const char * parsePositionClassName = "java/text/ParsePosition"; + const char * longClassName = "java/lang/Long"; + const char * doubleClassName = "java/lang/Double"; + const char * bigDecimalClassName = "java/math/BigDecimal"; + const char * bigIntegerClassName = "java/math/BigInteger"; + + UErrorCode status = U_ZERO_ERROR; + + UNumberFormat *fmt = (UNumberFormat *)(int)addr; + + jchar *str = (UChar *)env->GetStringChars(text, NULL); + int strlength = env->GetStringLength(text); + + jclass parsePositionClass = env->FindClass(parsePositionClassName); + jclass longClass = env->FindClass(longClassName); + jclass doubleClass = env->FindClass(doubleClassName); + jclass bigDecimalClass = env->FindClass(bigDecimalClassName); + jclass bigIntegerClass = env->FindClass(bigIntegerClassName); + + jmethodID getIndexMethodID = env->GetMethodID(parsePositionClass, + "getIndex", "()I"); + jmethodID setIndexMethodID = env->GetMethodID(parsePositionClass, + "setIndex", "(I)V"); + jmethodID setErrorIndexMethodID = env->GetMethodID(parsePositionClass, + "setErrorIndex", "(I)V"); + + jmethodID longInitMethodID = env->GetMethodID(longClass, "<init>", "(J)V"); + jmethodID dblInitMethodID = env->GetMethodID(doubleClass, "<init>", "(D)V"); + jmethodID bigDecimalInitMethodID = env->GetMethodID(bigDecimalClass, "<init>", "(Ljava/math/BigInteger;I)V"); + jmethodID bigIntegerInitMethodID = env->GetMethodID(bigIntegerClass, "<init>", "(Ljava/lang/String;)V"); + jmethodID doubleValueMethodID = env->GetMethodID(bigDecimalClass, "doubleValue", "()D"); + + bool resultAssigned; + int parsePos = env->CallIntMethod(position, getIndexMethodID, NULL); + + // make sure the ParsePosition is valid. Actually icu4c would parse a number + // correctly even if the parsePosition is set to -1, but since the RI fails + // for that case we have to fail too + if(parsePos < 0 || parsePos > strlength) { + return NULL; + } + + Formattable res; + + const UnicodeString src((UChar*)str, strlength, strlength); + ParsePosition pp; + + pp.setIndex(parsePos); + + DigitList digits; + + ((const DecimalFormat*)fmt)->parse(src, resultAssigned, res, pp, FALSE, digits); + + env->ReleaseStringChars(text, str); + + if(pp.getErrorIndex() == -1) { + parsePos = pp.getIndex(); + } else { + env->CallVoidMethod(position, setErrorIndexMethodID, + (jint) pp.getErrorIndex()); + return NULL; + } + + Formattable::Type numType; + numType = res.getType(); + UErrorCode fmtStatus; + + double resultDouble; + long resultLong; + int64_t resultInt64; + UnicodeString resultString; + jstring resultStr; + int resLength; + const char * resultUTF; + jobject resultObject1, resultObject2; + jdouble doubleTest; + jchar * result; + + if (resultAssigned) + { + switch(numType) { + case Formattable::kDouble: + resultDouble = res.getDouble(); + env->CallVoidMethod(position, setIndexMethodID, (jint) parsePos); + return env->NewObject(doubleClass, dblInitMethodID, + (jdouble) resultDouble); + case Formattable::kLong: + resultLong = res.getLong(); + env->CallVoidMethod(position, setIndexMethodID, (jint) parsePos); + return env->NewObject(longClass, longInitMethodID, + (jlong) resultLong); + case Formattable::kInt64: + resultInt64 = res.getInt64(); + env->CallVoidMethod(position, setIndexMethodID, (jint) parsePos); + return env->NewObject(longClass, longInitMethodID, + (jlong) resultInt64); + default: + return NULL; + } + } + else + { + int scale = digits.fCount - digits.fDecimalAt; + // ATTENTION: Abuse of Implementation Knowlegde! + digits.fDigits[digits.fCount] = 0; + if (digits.fIsPositive) { + resultStr = env->NewStringUTF(digits.fDigits); + } else { + if (digits.fCount == 0) { + env->CallVoidMethod(position, setIndexMethodID, (jint) parsePos); + return env->NewObject(doubleClass, dblInitMethodID, (jdouble)-0); + } else { + // ATTENTION: Abuse of Implementation Knowlegde! + *(digits.fDigits - 1) = '-'; + resultStr = env->NewStringUTF(digits.fDigits - 1); + } + } + + env->CallVoidMethod(position, setIndexMethodID, (jint) parsePos); + + resultObject1 = env->NewObject(bigIntegerClass, bigIntegerInitMethodID, resultStr); + resultObject2 = env->NewObject(bigDecimalClass, bigDecimalInitMethodID, resultObject1, scale); + return resultObject2; + } +} + +static jint cloneImpl(JNIEnv *env, jclass clazz, jint addr) { + + UErrorCode status = U_ZERO_ERROR; + + UNumberFormat *fmt = (UNumberFormat *)(int)addr; + + UNumberFormat *result = unum_clone(fmt, &status); + + if(icuError(env, status) != FALSE) { + return 0; + } + + return (long) result; + +} + +static JNINativeMethod gMethods[] = { + /* name, signature, funcPtr */ + {"openDecimalFormatImpl", "(Ljava/lang/String;Ljava/lang/String;)I", + (void*) openDecimalFormatImpl}, + {"closeDecimalFormatImpl", "(I)V", (void*) closeDecimalFormatImpl}, + {"setSymbol", "(IILjava/lang/String;)V", (void*) setSymbol}, + {"getSymbol", "(II)Ljava/lang/String;", (void*) getSymbol}, + {"setAttribute", "(III)V", (void*) setAttribute}, + {"getAttribute", "(II)I", (void*) getAttribute}, + {"setTextAttribute", "(IILjava/lang/String;)V", (void*) setTextAttribute}, + {"getTextAttribute", "(II)Ljava/lang/String;", (void*) getTextAttribute}, + {"applyPatternImpl", "(IZLjava/lang/String;)V", (void*) applyPatternImpl}, + {"toPatternImpl", "(IZ)Ljava/lang/String;", (void*) toPatternImpl}, + {"format", + "(IJLjava/text/FieldPosition;Ljava/lang/String;Ljava/lang/StringBuffer;)Ljava/lang/String;", + (void*) formatLong}, + {"format", + "(IDLjava/text/FieldPosition;Ljava/lang/String;Ljava/lang/StringBuffer;)Ljava/lang/String;", + (void*) formatDouble}, + {"format", + "(ILjava/lang/String;Ljava/text/FieldPosition;Ljava/lang/String;Ljava/lang/StringBuffer;I)Ljava/lang/String;", + (void*) formatDigitList}, + {"parse", + "(ILjava/lang/String;Ljava/text/ParsePosition;)Ljava/lang/Number;", + (void*) parse}, + {"cloneImpl", "(I)I", (void*) cloneImpl} +}; +int register_com_ibm_icu4jni_text_NativeDecimalFormat(JNIEnv* env) { + return jniRegisterNativeMethods(env, + "com/ibm/icu4jni/text/NativeDecimalFormat", gMethods, + NELEM(gMethods)); +} diff --git a/icu/src/main/native/ErrorCode.c b/icu/src/main/native/ErrorCode.c new file mode 100644 index 0000000..b3e43cd --- /dev/null +++ b/icu/src/main/native/ErrorCode.c @@ -0,0 +1,57 @@ +/** +******************************************************************************* +* Copyright (C) 1996-2005, International Business Machines Corporation and * +* others. All Rights Reserved. * +******************************************************************************* +* +******************************************************************************* +*/ + +#include "ErrorCode.h" + +/* private data members ----------------------------------------------------*/ + +/** +* Name of the java runtime exception classes +*/ +#define ILLEGALARGUMENTEXCEPTION_ "java/lang/IllegalArgumentException" +#define ARRAYINDEXOUTOFBOUNDSEXCEPTION_ "java/lang/ArrayIndexOutOfBoundsException" +#define UNSUPPORTEDOPERATIONEXCEPTION_ "java/lang/UnsupportedOperationException" +#define RUNTIMEEXCEPTION_ "java/lang/RuntimeException" + +/* public methods ---------------------------------------------------------*/ + +/** +* Checks if an error has occured. +* Throws a generic Java RuntimeException if an error has occured. +* @param env JNI environment variable +* @param errorcode code to determine if it is an erro +* @return 0 if errorcode is not an error, 1 if errorcode is an error, but the +* creation of the exception to be thrown fails +* @exception thrown if errorcode represents an error +*/ +UBool icu4jni_error(JNIEnv *env, UErrorCode errorcode) +{ + const char *emsg = u_errorName(errorcode); + jclass exception; + + if (errorcode > U_ZERO_ERROR && errorcode < U_ERROR_LIMIT) { + switch (errorcode) { + case U_ILLEGAL_ARGUMENT_ERROR : + exception = (*env)->FindClass(env, ILLEGALARGUMENTEXCEPTION_); + break; + case U_INDEX_OUTOFBOUNDS_ERROR : + case U_BUFFER_OVERFLOW_ERROR : + exception = (*env)->FindClass(env, ARRAYINDEXOUTOFBOUNDSEXCEPTION_); + break; + case U_UNSUPPORTED_ERROR : + exception = (*env)->FindClass(env, UNSUPPORTEDOPERATIONEXCEPTION_); + break; + default : + exception = (*env)->FindClass(env, RUNTIMEEXCEPTION_); + } + + return ((*env)->ThrowNew(env, exception, emsg) != 0); + } + return 0; +} diff --git a/icu/src/main/native/ErrorCode.h b/icu/src/main/native/ErrorCode.h new file mode 100644 index 0000000..a5bbfc6 --- /dev/null +++ b/icu/src/main/native/ErrorCode.h @@ -0,0 +1,27 @@ +/** +******************************************************************************* +* Copyright (C) 1996-2005, International Business Machines Corporation and * +* others. All Rights Reserved. * +******************************************************************************* +******************************************************************************* +*/ + +#ifndef ERRORCODE_H +#define ERRORCODE_H + +#include <jni.h> +#include "unicode/utypes.h" +#include "unicode/putil.h" + +/** +* Checks if an error has occured. +* Throws a generic Java RuntimeException if an error has occured. +* @param env JNI environment variable +* @param errorcode code to determine if it is an erro +* @return 0 if errorcode is not an error, 1 if errorcode is an error, but the +* creation of the exception to be thrown fails +* @exception thrown if errorcode represents an error +*/ +UBool icu4jni_error(JNIEnv *env, UErrorCode errorcode); + +#endif diff --git a/icu/src/main/native/RBNFInterface.cpp b/icu/src/main/native/RBNFInterface.cpp new file mode 100644 index 0000000..d9bf460 --- /dev/null +++ b/icu/src/main/native/RBNFInterface.cpp @@ -0,0 +1,382 @@ +/* + * Copyright (C) 2008 The Android Open Source Project + * + * Licensed 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. + */ + +#include "JNIHelp.h" +#include "AndroidSystemNatives.h" +#include "unicode/numfmt.h" +#include "unicode/rbnf.h" +#include "unicode/fmtable.h" +#include "unicode/ustring.h" +#include "unicode/locid.h" +#include "ErrorCode.h" +#include <stdlib.h> +#include <string.h> + +static UBool icuError(JNIEnv *env, UErrorCode errorcode) +{ + const char *emsg = u_errorName(errorcode); + jclass exception; + + if (errorcode > U_ZERO_ERROR && errorcode < U_ERROR_LIMIT) { + switch (errorcode) { + case U_ILLEGAL_ARGUMENT_ERROR : + exception = env->FindClass("java/lang/IllegalArgumentException"); + break; + case U_INDEX_OUTOFBOUNDS_ERROR : + case U_BUFFER_OVERFLOW_ERROR : + exception = env->FindClass("java/lang/ArrayIndexOutOfBoundsException"); + break; + case U_UNSUPPORTED_ERROR : + exception = env->FindClass("java/lang/UnsupportedOperationException"); + break; + default : + exception = env->FindClass("java/lang/RuntimeException"); + } + + return (env->ThrowNew(exception, emsg) != 0); + } + return 0; +} + +static jint openRBNFImpl1(JNIEnv* env, jclass clazz, + jint type, jstring locale) { + + // LOGI("ENTER openRBNFImpl1"); + + // the errorcode returned by unum_open + UErrorCode status = U_ZERO_ERROR; + + // prepare the locale string for the call to unum_open + const char *localeChars = env->GetStringUTFChars(locale, NULL); + + URBNFRuleSetTag style; + if(type == 0) { + style = URBNF_SPELLOUT; + } else if(type == 1) { + style = URBNF_ORDINAL; + } else if(type == 2) { + style = URBNF_DURATION; + } else if(type == 3) { + style = URBNF_COUNT; + } else { + icuError(env, U_ILLEGAL_ARGUMENT_ERROR); + } + + Locale loc = Locale::createFromName(localeChars); + + // open a default type number format + RuleBasedNumberFormat *fmt = new RuleBasedNumberFormat(style, loc, status); + + // release the allocated strings + env->ReleaseStringUTFChars(locale, localeChars); + + // check for an error + if ( icuError(env, status) != FALSE) { + return 0; + } + + // return the handle to the number format + return (long) fmt; + +} + +static jint openRBNFImpl2(JNIEnv* env, jclass clazz, + jstring rule, jstring locale) { + + // LOGI("ENTER openRBNFImpl2"); + + // the errorcode returned by unum_open + UErrorCode status = U_ZERO_ERROR; + + // prepare the pattern string for the call to unum_open + const UChar *ruleChars = env->GetStringChars(rule, NULL); + int ruleLen = env->GetStringLength(rule); + + // prepare the locale string for the call to unum_open + const char *localeChars = env->GetStringUTFChars(locale, NULL); + + // open a rule based number format + UNumberFormat *fmt = unum_open(UNUM_PATTERN_RULEBASED, ruleChars, ruleLen, + localeChars, NULL, &status); + + // release the allocated strings + env->ReleaseStringChars(rule, ruleChars); + env->ReleaseStringUTFChars(locale, localeChars); + + // check for an error + if ( icuError(env, status) != FALSE) { + return 0; + } + + // return the handle to the number format + return (long) fmt; + +} + +static void closeRBNFImpl(JNIEnv *env, jclass clazz, jint addr) { + + // LOGI("ENTER closeRBNFImpl"); + + // get the pointer to the number format + RuleBasedNumberFormat *fmt = (RuleBasedNumberFormat *)(int)addr; + + // close this number format + delete fmt; +} + +static jstring formatLongRBNFImpl(JNIEnv *env, jclass clazz, jint addr, jlong value, + jobject field, jstring fieldType, jobject attributes) { + + // LOGI("ENTER formatLongRBNFImpl"); + + const char * fieldPositionClassName = "java/text/FieldPosition"; + const char * stringBufferClassName = "java/lang/StringBuffer"; + jclass fieldPositionClass = env->FindClass(fieldPositionClassName); + jclass stringBufferClass = env->FindClass(stringBufferClassName); + jmethodID setBeginIndexMethodID = env->GetMethodID(fieldPositionClass, + "setBeginIndex", "(I)V"); + jmethodID setEndIndexMethodID = env->GetMethodID(fieldPositionClass, + "setEndIndex", "(I)V"); + jmethodID appendMethodID = env->GetMethodID(stringBufferClass, + "append", "(Ljava/lang/String;)Ljava/lang/StringBuffer;"); + + const char * fieldName = NULL; + + if(fieldType != NULL) { + fieldName = env->GetStringUTFChars(fieldType, NULL); + } + + uint32_t reslenneeded; + int64_t val = value; + UChar *result = NULL; + + FieldPosition fp; + fp.setField(FieldPosition::DONT_CARE); + + UErrorCode status = U_ZERO_ERROR; + + RuleBasedNumberFormat *fmt = (RuleBasedNumberFormat *)(int)addr; + + UnicodeString res; + + fmt->format(val, res, fp); + + reslenneeded = res.extract(NULL, 0, status); + + if(status==U_BUFFER_OVERFLOW_ERROR) { + status=U_ZERO_ERROR; + + result = (UChar*)malloc(sizeof(UChar) * (reslenneeded + 1)); + + res.extract(result, reslenneeded + 1, status); + } + if (icuError(env, status) != FALSE) { + free(result); + return NULL; + } + + if(fieldType != NULL) { + env->ReleaseStringUTFChars(fieldType, fieldName); + } + + jstring resulting = env->NewString(result, reslenneeded); + + free(result); + + return resulting; +} + +static jstring formatDoubleRBNFImpl(JNIEnv *env, jclass clazz, jint addr, jdouble value, + jobject field, jstring fieldType, jobject attributes) { + + // LOGI("ENTER formatDoubleRBNFImpl"); + + const char * fieldPositionClassName = "java/text/FieldPosition"; + const char * stringBufferClassName = "java/lang/StringBuffer"; + jclass fieldPositionClass = env->FindClass(fieldPositionClassName); + jclass stringBufferClass = env->FindClass(stringBufferClassName); + jmethodID setBeginIndexMethodID = env->GetMethodID(fieldPositionClass, + "setBeginIndex", "(I)V"); + jmethodID setEndIndexMethodID = env->GetMethodID(fieldPositionClass, + "setEndIndex", "(I)V"); + jmethodID appendMethodID = env->GetMethodID(stringBufferClass, + "append", "(Ljava/lang/String;)Ljava/lang/StringBuffer;"); + + const char * fieldName = NULL; + + if(fieldType != NULL) { + fieldName = env->GetStringUTFChars(fieldType, NULL); + } + + uint32_t reslenneeded; + double val = value; + UChar *result = NULL; + + FieldPosition fp; + fp.setField(FieldPosition::DONT_CARE); + + UErrorCode status = U_ZERO_ERROR; + + RuleBasedNumberFormat *fmt = (RuleBasedNumberFormat *)(int)addr; + + UnicodeString res; + + fmt->format(val, res, fp); + + reslenneeded = res.extract(NULL, 0, status); + + if(status==U_BUFFER_OVERFLOW_ERROR) { + status=U_ZERO_ERROR; + + result = (UChar*)malloc(sizeof(UChar) * (reslenneeded + 1)); + + res.extract(result, reslenneeded + 1, status); + } + if (icuError(env, status) != FALSE) { + free(result); + return NULL; + } + + if(fieldType != NULL) { + env->ReleaseStringUTFChars(fieldType, fieldName); + } + + jstring resulting = env->NewString(result, reslenneeded); + + free(result); + + return resulting; +} + +static jobject parseRBNFImpl(JNIEnv *env, jclass clazz, jint addr, jstring text, + jobject position, jboolean lenient) { + + // LOGI("ENTER parseRBNFImpl"); + + const char * parsePositionClassName = "java/text/ParsePosition"; + const char * longClassName = "java/lang/Long"; + const char * doubleClassName = "java/lang/Double"; + + + UErrorCode status = U_ZERO_ERROR; + + UNumberFormat *fmt = (UNumberFormat *)(int)addr; + + jchar *str = (UChar *)env->GetStringChars(text, NULL); + int strlength = env->GetStringLength(text); + + jclass parsePositionClass = env->FindClass(parsePositionClassName); + jclass longClass = env->FindClass(longClassName); + jclass doubleClass = env->FindClass(doubleClassName); + + jmethodID getIndexMethodID = env->GetMethodID(parsePositionClass, + "getIndex", "()I"); + jmethodID setIndexMethodID = env->GetMethodID(parsePositionClass, + "setIndex", "(I)V"); + jmethodID setErrorIndexMethodID = env->GetMethodID(parsePositionClass, + "setErrorIndex", "(I)V"); + + jmethodID longInitMethodID = env->GetMethodID(longClass, "<init>", "(J)V"); + jmethodID dblInitMethodID = env->GetMethodID(doubleClass, "<init>", "(D)V"); + + int parsePos = env->CallIntMethod(position, getIndexMethodID, NULL); + + // make sure the ParsePosition is valid. Actually icu4c would parse a number + // correctly even if the parsePosition is set to -1, but since the RI fails + // for that case we have to fail too + if(parsePos < 0 || parsePos > strlength) { + return NULL; + } + + Formattable res; + + const UnicodeString src((UChar*)str, strlength, strlength); + ParsePosition pp; + + pp.setIndex(parsePos); + + if(lenient) { + unum_setAttribute(fmt, UNUM_LENIENT_PARSE, JNI_TRUE); + } + + ((const NumberFormat*)fmt)->parse(src, res, pp); + + if(lenient) { + unum_setAttribute(fmt, UNUM_LENIENT_PARSE, JNI_FALSE); + } + + env->ReleaseStringChars(text, str); + + if(pp.getErrorIndex() == -1) { + parsePos = pp.getIndex(); + } else { + env->CallVoidMethod(position, setErrorIndexMethodID, + (jint) pp.getErrorIndex()); + return NULL; + } + + Formattable::Type numType; + numType = res.getType(); + UErrorCode fmtStatus; + + double resultDouble; + long resultLong; + int64_t resultInt64; + + switch(numType) { + case Formattable::kDouble: + resultDouble = res.getDouble(); + env->CallVoidMethod(position, setIndexMethodID, (jint) parsePos); + return env->NewObject(doubleClass, dblInitMethodID, + (jdouble) resultDouble); + case Formattable::kLong: + resultLong = res.getLong(); + env->CallVoidMethod(position, setIndexMethodID, (jint) parsePos); + return env->NewObject(longClass, longInitMethodID, + (jlong) resultLong); + case Formattable::kInt64: + resultInt64 = res.getInt64(); + env->CallVoidMethod(position, setIndexMethodID, (jint) parsePos); + return env->NewObject(longClass, longInitMethodID, + (jlong) resultInt64); + default: + break; + } + + return NULL; +} + +static JNINativeMethod gMethods[] = { + /* name, signature, funcPtr */ + {"openRBNFImpl", "(ILjava/lang/String;)I", (void*) openRBNFImpl1}, + {"openRBNFImpl", "(Ljava/lang/String;Ljava/lang/String;)I", + (void*) openRBNFImpl2}, + {"closeRBNFImpl", "(I)V", (void*) closeRBNFImpl}, + {"formatRBNFImpl", + "(IJLjava/text/FieldPosition;Ljava/lang/String;Ljava/lang/StringBuffer;)Ljava/lang/String;", + (void*) formatLongRBNFImpl}, + {"formatRBNFImpl", + "(IDLjava/text/FieldPosition;Ljava/lang/String;Ljava/lang/StringBuffer;)Ljava/lang/String;", + (void*) formatDoubleRBNFImpl}, + {"parseRBNFImpl", + "(ILjava/lang/String;Ljava/text/ParsePosition;Z)Ljava/lang/Number;", + (void*) parseRBNFImpl}, +}; +int register_com_ibm_icu4jni_text_NativeRBNF(JNIEnv* env) { + return jniRegisterNativeMethods(env, + "com/ibm/icu4jni/text/RuleBasedNumberFormat", gMethods, + NELEM(gMethods)); +} diff --git a/icu/src/main/native/RegExInterface.cpp b/icu/src/main/native/RegExInterface.cpp new file mode 100644 index 0000000..afa5cc4 --- /dev/null +++ b/icu/src/main/native/RegExInterface.cpp @@ -0,0 +1,373 @@ +/* + * Copyright (C) 2007 The Android Open Source Project + * + * Licensed 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. + */ + +#include <stdlib.h> +#include <stdio.h> +#include <string.h> + +#include "unicode/uregex.h" +#include "unicode/utypes.h" +#include "unicode/parseerr.h" + +#include <jni.h> +#include <JNIHelp.h> + +static jchar EMPTY_STRING = 0; + +/** + * A data structure that ties together an ICU regular expression and the + * character data it refers to (but does not have a copy of), so we can + * manage memory properly. + */ +typedef struct RegExDataStruct { + // A pointer to the ICU regular expression + URegularExpression* regex; + // A pointer to (a copy of) the input text that *we* manage + jchar* text; +} RegExData; + +static void throwPatternSyntaxException(JNIEnv* env, UErrorCode status, + jstring pattern, UParseError error) +{ + jclass clazz = env->FindClass("java/util/regex/PatternSyntaxException"); + jmethodID method = env->GetMethodID(clazz, "<init>", + "(Ljava/lang/String;Ljava/lang/String;I)V"); + + jstring message = env->NewStringUTF(u_errorName(status)); + jthrowable except = (jthrowable)(env->NewObject(clazz, method, message, + pattern, error.offset)); + env->Throw(except); +} + +static void throwRuntimeException(JNIEnv* env, UErrorCode status) +{ + jniThrowException(env, "java/lang/RuntimeException", u_errorName(status)); +} + +static void _close(JNIEnv* env, jclass clazz, RegExData* data) +{ + if (data->regex != NULL) { + uregex_close(data->regex); + } + + if (data->text != NULL && data->text != &EMPTY_STRING) { + free(data->text); + } + + free(data); +} + +static RegExData* open(JNIEnv* env, jclass clazz, jstring pattern, jint flags) +{ + flags = flags | UREGEX_ERROR_ON_UNKNOWN_ESCAPES; + + RegExData* data = (RegExData*)calloc(sizeof(RegExData), 1); + + UErrorCode status = U_ZERO_ERROR; + UParseError error; + error.offset = -1; + + jchar const * patternRaw; + int patternLen = env->GetStringLength(pattern); + if (patternLen == 0) { + data->regex = uregex_open(&EMPTY_STRING, -1, flags, &error, &status); + } else { + jchar const * patternRaw = env->GetStringChars(pattern, NULL); + data->regex = uregex_open(patternRaw, patternLen, flags, &error, + &status); + env->ReleaseStringChars(pattern, patternRaw); + } + + if (!U_SUCCESS(status)) { + _close(env, clazz, data); + throwPatternSyntaxException(env, status, pattern, error); + data = NULL; + } + + return data; +} + +static RegExData* _clone(JNIEnv* env, jclass clazz, RegExData* data) +{ + UErrorCode status = U_ZERO_ERROR; + + URegularExpression* clonedRegex = uregex_clone(data->regex, &status); + if (!U_SUCCESS(status)) { + throwRuntimeException(env, status); + } + + RegExData* result = (RegExData*)calloc(sizeof(RegExData), 1); + result->regex = clonedRegex; + + return result; +} + +static void setText(JNIEnv* env, jclass clazz, RegExData* data, jstring text) +{ + UErrorCode status = U_ZERO_ERROR; + + uregex_setText(data->regex, &EMPTY_STRING, 0, &status); + if (!U_SUCCESS(status)) { + throwRuntimeException(env, status); + return; + } + + if (data->text != NULL && data->text != &EMPTY_STRING) { + free(data->text); + data->text = NULL; + } + + int textLen = env->GetStringLength(text); + if (textLen == 0) { + data->text = &EMPTY_STRING; + } else { + jchar const * textRaw = env->GetStringChars(text, NULL); + data->text = (jchar*)malloc((textLen + 1) * sizeof(jchar)); + memcpy(data->text, textRaw, textLen * sizeof(jchar)); + data->text[textLen] = 0; + env->ReleaseStringChars(text, textRaw); + } + + uregex_setText(data->regex, data->text, textLen, &status); + if (!U_SUCCESS(status)) { + throwRuntimeException(env, status); + } +} + +static jboolean matches(JNIEnv* env, jclass clazz, RegExData* data, + jint startIndex) +{ + UErrorCode status = U_ZERO_ERROR; + + jboolean result = uregex_matches(data->regex, startIndex, &status); + if (!U_SUCCESS(status)) { + throwRuntimeException(env, status); + } + + return result; +} + +static jboolean lookingAt(JNIEnv* env, jclass clazz, RegExData* data, + jint startIndex) +{ + UErrorCode status = U_ZERO_ERROR; + + jboolean result = uregex_lookingAt(data->regex, startIndex, &status); + if (!U_SUCCESS(status)) { + throwRuntimeException(env, status); + } + + return result; +} + +static jboolean find(JNIEnv* env, jclass clazz, RegExData* data, + jint startIndex) +{ + UErrorCode status = U_ZERO_ERROR; + + jboolean result = uregex_find(data->regex, startIndex, &status); + if (!U_SUCCESS(status)) { + throwRuntimeException(env, status); + } + + return result; +} + +static jboolean findNext(JNIEnv* env, jclass clazz, RegExData* data) +{ + UErrorCode status = U_ZERO_ERROR; + + jboolean result = uregex_findNext(data->regex, &status); + if (!U_SUCCESS(status)) { + throwRuntimeException(env, status); + } + + return result; +} + +static jint groupCount(JNIEnv* env, jclass clazz, RegExData* data) +{ + UErrorCode status = U_ZERO_ERROR; + + jint result = uregex_groupCount(data->regex, &status); + if (!U_SUCCESS(status)) { + throwRuntimeException(env, status); + } + + return result; +} + +static void startEnd(JNIEnv* env, jclass clazz, RegExData* data, + jintArray offsets) +{ + UErrorCode status = U_ZERO_ERROR; + + jint * offsetsRaw = env->GetIntArrayElements(offsets, NULL); + + int groupCount = uregex_groupCount(data->regex, &status); + for (int i = 0; i <= groupCount && U_SUCCESS(status); i++) { + offsetsRaw[2 * i + 0] = uregex_start(data->regex, i, &status); + offsetsRaw[2 * i + 1] = uregex_end(data->regex, i, &status); + } + + env->ReleaseIntArrayElements(offsets, offsetsRaw, 0); + + if (!U_SUCCESS(status)) { + throwRuntimeException(env, status); + } +} + +static void setRegion(JNIEnv* env, jclass clazz, RegExData* data, jint start, + jint end) +{ + UErrorCode status = U_ZERO_ERROR; + uregex_setRegion(data->regex, start, end, &status); + if (!U_SUCCESS(status)) { + throwRuntimeException(env, status); + } +} + +static jint regionStart(JNIEnv* env, jclass clazz, RegExData* data) +{ + UErrorCode status = U_ZERO_ERROR; + int result = uregex_regionStart(data->regex, &status); + if (!U_SUCCESS(status)) { + throwRuntimeException(env, status); + } + return result; +} + +static jint regionEnd(JNIEnv* env, jclass clazz, RegExData* data) +{ + UErrorCode status = U_ZERO_ERROR; + int result = uregex_regionEnd(data->regex, &status); + if (!U_SUCCESS(status)) { + throwRuntimeException(env, status); + } + return result; +} + +static void useTransparentBounds(JNIEnv* env, jclass clazz, RegExData* data, + jboolean value) +{ + UErrorCode status = U_ZERO_ERROR; + uregex_useTransparentBounds(data->regex, value, &status); + if (!U_SUCCESS(status)) { + throwRuntimeException(env, status); + } +} + +static jboolean hasTransparentBounds(JNIEnv* env, jclass clazz, RegExData* data) +{ + UErrorCode status = U_ZERO_ERROR; + jboolean result = uregex_hasTransparentBounds(data->regex, &status); + if (!U_SUCCESS(status)) { + throwRuntimeException(env, status); + } + return result; +} + +static void useAnchoringBounds(JNIEnv* env, jclass clazz, RegExData* data, + jboolean value) +{ + UErrorCode status = U_ZERO_ERROR; + uregex_useAnchoringBounds(data->regex, value, &status); + if (!U_SUCCESS(status)) { + throwRuntimeException(env, status); + } +} + +static jboolean hasAnchoringBounds(JNIEnv* env, jclass clazz, RegExData* data) +{ + UErrorCode status = U_ZERO_ERROR; + jboolean result = uregex_hasAnchoringBounds(data->regex, &status); + if (!U_SUCCESS(status)) { + throwRuntimeException(env, status); + } + return result; +} + +static jboolean hitEnd(JNIEnv* env, jclass clazz, RegExData* data) +{ + UErrorCode status = U_ZERO_ERROR; + jboolean result = uregex_hitEnd(data->regex, &status); + if (!U_SUCCESS(status)) { + throwRuntimeException(env, status); + } + return result; +} + +static jboolean requireEnd(JNIEnv* env, jclass clazz, RegExData* data) +{ + UErrorCode status = U_ZERO_ERROR; + jboolean result = uregex_requireEnd(data->regex, &status); + if (!U_SUCCESS(status)) { + throwRuntimeException(env, status); + } + return result; +} + +static void reset(JNIEnv* env, jclass clazz, RegExData* data, jint position) +{ + UErrorCode status = U_ZERO_ERROR; + uregex_reset(data->regex, position, &status); + if (!U_SUCCESS(status)) { + throwRuntimeException(env, status); + } +} + +/* + * JNI registration. + */ +static JNINativeMethod sMethods[] = { + /* name, signature, funcPtr */ + { "open", "(Ljava/lang/String;I)I", (void*)open }, + { "clone", "(I)I", (void*)_clone }, + { "close", "(I)V", (void*)_close }, + { "setText", "(ILjava/lang/String;)V", (void*)setText }, + { "matches", "(II)Z", (void*)matches }, + { "lookingAt", "(II)Z", (void*)lookingAt }, + { "find", "(II)Z", (void*)find }, + { "findNext", "(I)Z", (void*)findNext }, + { "groupCount", "(I)I", (void*)groupCount }, + { "startEnd", "(I[I)V", (void*)startEnd }, + { "setRegion", "(III)V", (void*)setRegion }, + { "regionStart", "(I)I", (void*)regionStart }, + { "regionEnd", "(I)I", (void*)regionEnd }, + { "useTransparentBounds", "(IZ)V", (void*)useTransparentBounds }, + { "hasTransparentBounds", "(I)Z", (void*)hasTransparentBounds }, + { "useAnchoringBounds", "(IZ)V", (void*)useAnchoringBounds }, + { "hasAnchoringBounds", "(I)Z", (void*)hasAnchoringBounds }, + { "hitEnd", "(I)Z", (void*)hitEnd }, + { "requireEnd", "(I)Z", (void*)requireEnd }, + { "reset", "(II)V", (void*)reset } +}; + +extern "C" int register_com_ibm_icu4jni_regex_NativeRegEx(JNIEnv* env) +{ + jclass clazz; + + clazz = env->FindClass("com/ibm/icu4jni/regex/NativeRegEx"); + if (clazz == NULL) { + return -1; + } + + if (env->RegisterNatives(clazz, sMethods, NELEM(sMethods)) < 0) { + return -1; + } + + return 0; +} diff --git a/icu/src/main/native/ResourceInterface.cpp b/icu/src/main/native/ResourceInterface.cpp new file mode 100644 index 0000000..5f9d442 --- /dev/null +++ b/icu/src/main/native/ResourceInterface.cpp @@ -0,0 +1,1436 @@ +/* + * Copyright (C) 2008 The Android Open Source Project + * + * Licensed 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. + */ + +#include "JNIHelp.h" +#include "AndroidSystemNatives.h" +#include "unicode/numfmt.h" +#include "unicode/locid.h" +#include "unicode/ucal.h" +#include "unicode/gregocal.h" +#include "unicode/ucurr.h" +#include "unicode/calendar.h" +#include "unicode/datefmt.h" +#include "unicode/dtfmtsym.h" +#include "unicode/decimfmt.h" +#include "unicode/dcfmtsym.h" +#include "unicode/uclean.h" +#include "unicode/smpdtfmt.h" +#include "unicode/strenum.h" +#include "unicode/ustring.h" +#include "unicode/timezone.h" +#include "ErrorCode.h" +#include <cutils/log.h> +#include <stdlib.h> +#include <string.h> +#include <time.h> +#include <sys/time.h> + +jclass string_class; + +static UBool icuError(JNIEnv *env, UErrorCode errorcode) +{ + const char *emsg = u_errorName(errorcode); + jclass exception; + + if (U_FAILURE(errorcode)) { + switch (errorcode) { + case U_ILLEGAL_ARGUMENT_ERROR : + exception = env->FindClass("java/lang/IllegalArgumentException"); + break; + case U_INDEX_OUTOFBOUNDS_ERROR : + case U_BUFFER_OVERFLOW_ERROR : + exception = env->FindClass("java/lang/ArrayIndexOutOfBoundsException"); + break; + case U_UNSUPPORTED_ERROR : + exception = env->FindClass("java/lang/UnsupportedOperationException"); + break; + default : + exception = env->FindClass("java/lang/RuntimeException"); + } + + return (env->ThrowNew(exception, emsg) != 0); + } + return 0; +} + +static Locale getLocale(JNIEnv *env, jstring locale) { + const char *name = env->GetStringUTFChars(locale, NULL); + Locale result = Locale::createFromName(name); + env->ReleaseStringUTFChars(locale, name); + return result; +} + +static jstring getJStringFromUnicodeString(JNIEnv *env, UnicodeString string) { + + UErrorCode status = U_ZERO_ERROR; + + int stringLength = string.length(); + jchar *res = (jchar *) malloc(sizeof(jchar) * (stringLength + 1)); + string.extract(res, stringLength+1, status); + if(U_FAILURE(status)) { + free(res); + LOGI("Error getting string for getJStringFromUnicodeString"); + status = U_ZERO_ERROR; + return NULL; + } + jstring result = env->NewString(res, stringLength); + free(res); + return result; +} + +static void addObject(JNIEnv *env, jobjectArray result, const char *keyStr, jobject elem, int index) { + jclass objArray_class = env->FindClass("java/lang/Object"); + jobjectArray element = env->NewObjectArray(2, objArray_class, NULL); + jstring key = env->NewStringUTF(keyStr); + env->SetObjectArrayElement(element, 0, key); + env->SetObjectArrayElement(element, 1, elem); + env->SetObjectArrayElement(result, index, element); + env->DeleteLocalRef(key); + env->DeleteLocalRef(element); +} + +static jint getFractionDigitsNative(JNIEnv* env, jclass clazz, + jstring currencyCode) { + // LOGI("ENTER getFractionDigitsNative"); + + UErrorCode status = U_ZERO_ERROR; + + NumberFormat *fmt = NumberFormat::createCurrencyInstance(status); + if(U_FAILURE(status)) { + return -1; + } + + const jchar *cCode = env->GetStringChars(currencyCode, NULL); + fmt->setCurrency(cCode, status); + env->ReleaseStringChars(currencyCode, cCode); + if(U_FAILURE(status)) { + return -1; + } + + // for CurrencyFormats the minimum and maximum fraction digits are the same. + int result = fmt->getMinimumFractionDigits(); + delete(fmt); + return result; +} + +static jstring getCurrencyCodeNative(JNIEnv* env, jclass clazz, + jstring key) { + // LOGI("ENTER getCurrencyCodeNative"); + + UErrorCode status = U_ZERO_ERROR; + + UResourceBundle *supplData = ures_openDirect(NULL, "supplementalData", &status); + if(U_FAILURE(status)) { + return NULL; + } + + UResourceBundle *currencyMap = ures_getByKey(supplData, "CurrencyMap", NULL, &status); + if(U_FAILURE(status)) { + ures_close(supplData); + return NULL; + } + + const char *keyChars = env->GetStringUTFChars(key, NULL); + UResourceBundle *currency = ures_getByKey(currencyMap, keyChars, NULL, &status); + env->ReleaseStringUTFChars(key, keyChars); + if(U_FAILURE(status)) { + ures_close(currencyMap); + ures_close(supplData); + return NULL; + } + + UResourceBundle *currencyElem = ures_getByIndex(currency, 0, NULL, &status); + if(U_FAILURE(status)) { + ures_close(currency); + ures_close(currencyMap); + ures_close(supplData); + return env->NewStringUTF("None"); + } + + // check if there is a to date. If there is, the currency isn't used anymore. + UResourceBundle *currencyTo = ures_getByKey(currencyElem, "to", NULL, &status); + if(!U_FAILURE(status)) { + // return and let the ResourceBundle throw an exception + ures_close(currencyElem); + ures_close(currency); + ures_close(currencyMap); + ures_close(supplData); + return NULL; + } + status = U_ZERO_ERROR; + ures_close(currencyTo); + + UResourceBundle *currencyId = ures_getByKey(currencyElem, "id", NULL, &status); + if(U_FAILURE(status)) { + // No id defined for this country + ures_close(currencyElem); + ures_close(currency); + ures_close(currencyMap); + ures_close(supplData); + return env->NewStringUTF("None"); + } + + int length; + const jchar *id = ures_getString(currencyId, &length, &status); + if(U_FAILURE(status)) { + ures_close(currencyId); + ures_close(currencyElem); + ures_close(currency); + ures_close(currencyMap); + ures_close(supplData); + return env->NewStringUTF("None"); + } + + ures_close(currencyId); + ures_close(currencyElem); + ures_close(currency); + ures_close(currencyMap); + ures_close(supplData); + + if(length == 0) { + return env->NewStringUTF("None"); + } + return env->NewString(id, length); +} + +static jstring getCurrencySymbolNative(JNIEnv* env, jclass clazz, + jstring locale, jstring currencyCode) { + // LOGI("ENTER getCurrencySymbolNative"); + + UErrorCode status = U_ZERO_ERROR; + + const char *locName = env->GetStringUTFChars(locale, NULL); + UResourceBundle *root = ures_open(NULL, locName, &status); + env->ReleaseStringUTFChars(locale, locName); + if(U_FAILURE(status)) { + return NULL; + } + + UResourceBundle *rootElems = ures_getByKey(root, "Currencies", NULL, &status); + if(U_FAILURE(status)) { + ures_close(root); + return NULL; + } + + const char *currName = env->GetStringUTFChars(currencyCode, NULL); + UResourceBundle *currencyElems = ures_getByKey(rootElems, currName, NULL, &status); + env->ReleaseStringUTFChars(currencyCode, currName); + if(U_FAILURE(status)) { + ures_close(rootElems); + ures_close(root); + return NULL; + } + + int currSymbL; + const jchar *currSymbU = ures_getStringByIndex(currencyElems, 0, &currSymbL, &status); + if(U_FAILURE(status)) { + ures_close(currencyElems); + ures_close(rootElems); + ures_close(root); + return NULL; + } + + ures_close(currencyElems); + ures_close(rootElems); + ures_close(root); + + if(currSymbL == 0) { + return NULL; + } + return env->NewString(currSymbU, currSymbL); +} + +static jstring getDisplayCountryNative(JNIEnv* env, jclass clazz, + jstring targetLocale, jstring locale) { + // LOGI("ENTER getDisplayCountryNative"); + + UErrorCode status = U_ZERO_ERROR; + + Locale loc = getLocale(env, locale); + Locale targetLoc = getLocale(env, targetLocale); + + UnicodeString string; + targetLoc.getDisplayCountry(loc, string); + + jstring result = getJStringFromUnicodeString(env, string); + + return result; +} + +static jstring getDisplayLanguageNative(JNIEnv* env, jclass clazz, + jstring targetLocale, jstring locale) { + // LOGI("ENTER getDisplayLanguageNative"); + + Locale loc = getLocale(env, locale); + Locale targetLoc = getLocale(env, targetLocale); + + UnicodeString string; + targetLoc.getDisplayLanguage(loc, string); + + jstring result = getJStringFromUnicodeString(env, string); + + return result; +} + +static jstring getDisplayVariantNative(JNIEnv* env, jclass clazz, + jstring targetLocale, jstring locale) { + // LOGI("ENTER getDisplayVariantNative"); + + Locale loc = getLocale(env, locale); + Locale targetLoc = getLocale(env, targetLocale); + + UnicodeString string; + targetLoc.getDisplayVariant(loc, string); + + jstring result = getJStringFromUnicodeString(env, string); + + return result; +} + +static jstring getISO3CountryNative(JNIEnv* env, jclass clazz, jstring locale) { + // LOGI("ENTER getISO3CountryNative"); + + Locale loc = getLocale(env, locale); + + const char *string = loc.getISO3Country(); + + jstring result = env->NewStringUTF(string); + + return result; +} + +static jstring getISO3LanguageNative(JNIEnv* env, jclass clazz, jstring locale) { + // LOGI("ENTER getISO3LanguageNative"); + + Locale loc = getLocale(env, locale); + + const char *string = loc.getISO3Language(); + + jstring result = env->NewStringUTF(string); + + return result; +} + +static jobjectArray getISOCountriesNative(JNIEnv* env, jclass clazz) { + // LOGI("ENTER getISOCountriesNative"); + + const char* const* strings = Locale::getISOCountries(); + + int count = 0; + while(strings[count] != NULL) { + count++; + } + + jobjectArray result = env->NewObjectArray(count, string_class, NULL); + + jstring res; + for(int i = 0; i < count; i++) { + res = env->NewStringUTF(strings[i]); + env->SetObjectArrayElement(result, i, res); + env->DeleteLocalRef(res); + } + return result; +} + +static jobjectArray getISOLanguagesNative(JNIEnv* env, jclass clazz) { + // LOGI("ENTER getISOLanguagesNative"); + + const char* const* strings = Locale::getISOLanguages(); + + const char *string = strings[0]; + + int count = 0; + while(strings[count] != NULL) { + count++; + } + + jobjectArray result = env->NewObjectArray(count, string_class, NULL); + + jstring res; + for(int i = 0; i < count; i++) { + res = env->NewStringUTF(strings[i]); + env->SetObjectArrayElement(result, i, res); + env->DeleteLocalRef(res); + } + return result; +} + +static jobjectArray getAvailableLocalesNative(JNIEnv* env, jclass clazz) { + // LOGI("ENTER getAvailableLocalesNative"); + + int count = uloc_countAvailable(); + + jobjectArray result = env->NewObjectArray(count, string_class, NULL); + + jstring res; + const char * string; + for(int i = 0; i < count; i++) { + string = uloc_getAvailable(i); + res = env->NewStringUTF(string); + env->SetObjectArrayElement(result, i, res); + env->DeleteLocalRef(res); + } + + return result; +} + +static void getTimeZonesNative(JNIEnv* env, jclass clazz, + jobjectArray outerArray, jstring locale) { + // LOGI("ENTER getTimeZonesNative"); + + UErrorCode status = U_ZERO_ERROR; + + jobjectArray zoneIdArray; + jobjectArray longStdTimeArray; + jobjectArray shortStdTimeArray; + jobjectArray longDlTimeArray; + jobjectArray shortDlTimeArray; + + jstring content; + jstring strObj; + const jchar *res; + UnicodeString resU; + jint length; + const UnicodeString *zoneID; + DateFormat *df; + + UnicodeString longPattern("zzzz",""); + UnicodeString shortPattern("z",""); + + Locale loc = getLocale(env, locale); + + SimpleDateFormat longFormat(longPattern, loc, status); + SimpleDateFormat shortFormat(shortPattern, loc, status); + + + zoneIdArray = (jobjectArray) env->GetObjectArrayElement(outerArray, 0); + longStdTimeArray = (jobjectArray) env->GetObjectArrayElement(outerArray, 1); + shortStdTimeArray = (jobjectArray) env->GetObjectArrayElement(outerArray, 2); + longDlTimeArray = (jobjectArray) env->GetObjectArrayElement(outerArray, 3); + shortDlTimeArray = (jobjectArray) env->GetObjectArrayElement(outerArray, 4); + + int count = env->GetArrayLength(zoneIdArray); + + TimeZone* zones[count]; + + // get all timezone objects + for(int i = 0; i < count; i++) { + strObj = (jstring) env->GetObjectArrayElement(zoneIdArray, i); + length = env->GetStringLength(strObj); + res = env->GetStringChars(strObj, NULL); + const UnicodeString zoneID((UChar *)res, length); + env->ReleaseStringChars(strObj, res); + zones[i] = TimeZone::createTimeZone(zoneID); + env->DeleteLocalRef(strObj); + } + + // 15th January 2008 + UDate date1 = 1203105600000.0; + // 15th July 2008 + UDate date2 = 1218826800000.0; + + for (int i = 0; i < count; i++) { + TimeZone *tz = zones[i]; + longFormat.setTimeZone(*tz); + shortFormat.setTimeZone(*tz); + + int32_t daylightOffset; + int32_t rawOffset; + UDate standardDate; + UDate daylightSavingDate; + tz->getOffset(date1, false, rawOffset, daylightOffset, status); + if (daylightOffset != 0) { + // The Timezone is reporting that we are in daylight time + // for the winter date. The dates are for the wrong hemisphere, + // swap them. + standardDate = date2; + daylightSavingDate = date1; + } else { + standardDate = date1; + daylightSavingDate = date2; + } + + UnicodeString shortDayLight; + UnicodeString longDayLight; + UnicodeString shortStandard; + UnicodeString longStandard; + + shortFormat.format(daylightSavingDate, shortDayLight); + content = getJStringFromUnicodeString(env, shortDayLight); + env->SetObjectArrayElement(shortDlTimeArray, i, content); + env->DeleteLocalRef(content); + + shortFormat.format(standardDate, shortStandard); + content = getJStringFromUnicodeString(env, shortStandard); + env->SetObjectArrayElement(shortStdTimeArray, i, content); + env->DeleteLocalRef(content); + + longFormat.format (daylightSavingDate, longDayLight); + content = getJStringFromUnicodeString(env, longDayLight); + env->SetObjectArrayElement(longDlTimeArray, i, content); + env->DeleteLocalRef(content); + + longFormat.format (standardDate, longStandard); + content = getJStringFromUnicodeString(env, longStandard); + env->SetObjectArrayElement(longStdTimeArray, i, content); + env->DeleteLocalRef(content); + delete(tz); + } +} + + + + +static jstring getDisplayTimeZoneNative(JNIEnv* env, jclass clazz, + jstring zoneID, jboolean isDST, jint style, jstring localeID) { + + // Build TimeZone object + const jchar* idChars = env->GetStringChars(zoneID, NULL); + jint idLength = env->GetStringLength(zoneID); + UnicodeString idString((UChar*)idChars, idLength); + TimeZone* zone = TimeZone::createTimeZone(idString); + env->ReleaseStringChars(zoneID, idChars); + + // Build Locale object (can we rely on zero termination of JNI result?) + const char* localeChars = env->GetStringUTFChars(localeID, NULL); + jint localeLength = env->GetStringLength(localeID); + Locale locale = Locale::createFromName(localeChars); + + // Try to get the display name of the TimeZone according to the Locale + UnicodeString buffer; + zone->getDisplayName((UBool)isDST, (style == 0 ? TimeZone::SHORT : TimeZone::LONG), locale, buffer); + const UChar* tempChars = buffer.getBuffer(); + int tempLength = buffer.length(); + jstring result = env->NewString((jchar*)tempChars, tempLength); + env->ReleaseStringUTFChars(localeID, localeChars); + + // Clean up everything + delete(zone); + + return result; +} + +static void getDayInitVector(JNIEnv *env, UResourceBundle *gregorian, int *values) { + + UErrorCode status = U_ZERO_ERROR; + + // get the First day of week and the minimal days in first week numbers + UResourceBundle *gregorianElems = ures_getByKey(gregorian, "DateTimeElements", NULL, &status); + if(U_FAILURE(status)) { + return; + } + + int intVectSize; + const int *result; + result = ures_getIntVector(gregorianElems, &intVectSize, &status); + if(U_FAILURE(status)) { + ures_close(gregorianElems); + return; + } + + if(intVectSize == 2) { + values[0] = result[0]; + values[1] = result[1]; + } + + ures_close(gregorianElems); + +} + +static jobjectArray getAmPmMarkers(JNIEnv *env, UResourceBundle *gregorian) { + + jobjectArray amPmMarkers; + jstring pmU, amU; + + UErrorCode status = U_ZERO_ERROR; + + UResourceBundle *gregorianElems; + + gregorianElems = ures_getByKey(gregorian, "AmPmMarkers", NULL, &status); + if(U_FAILURE(status)) { + return NULL; + } + + int lengthAm, lengthPm; + + ures_resetIterator(gregorianElems); + + const jchar* am = ures_getStringByIndex(gregorianElems, 0, &lengthAm, &status); + const jchar* pm = ures_getStringByIndex(gregorianElems, 1, &lengthPm, &status); + + if(U_FAILURE(status)) { + ures_close(gregorianElems); + return NULL; + } + + amPmMarkers = env->NewObjectArray(2, string_class, NULL); + amU = env->NewString(am, lengthAm); + env->SetObjectArrayElement(amPmMarkers, 0, amU); + env->DeleteLocalRef(amU); + pmU = env->NewString(pm, lengthPm); + env->SetObjectArrayElement(amPmMarkers, 1, pmU); + env->DeleteLocalRef(pmU); + ures_close(gregorianElems); + + return amPmMarkers; +} + +static jobjectArray getEras(JNIEnv* env, UResourceBundle *gregorian) { + + jobjectArray eras; + jstring eraU; + const jchar* era; + + UErrorCode status = U_ZERO_ERROR; + + UResourceBundle *gregorianElems; + UResourceBundle *eraElems; + + gregorianElems = ures_getByKey(gregorian, "eras", NULL, &status); + if(U_FAILURE(status)) { + return NULL; + } + + eraElems = ures_getByKey(gregorianElems, "abbreviated", NULL, &status); + if(U_FAILURE(status)) { + ures_close(gregorianElems); + return NULL; + } + + int eraLength; + + int eraCount = ures_getSize(eraElems); + eras = env->NewObjectArray(eraCount, string_class, NULL); + + ures_resetIterator(eraElems); + for(int i = 0; i < eraCount; i++) { + era = ures_getStringByIndex(eraElems, i, &eraLength, &status); + if(U_FAILURE(status)) { + ures_close(gregorianElems); + ures_close(eraElems); + return NULL; + } + eraU = env->NewString(era, eraLength); + env->SetObjectArrayElement(eras, i, eraU); + env->DeleteLocalRef(eraU); + } + ures_close(eraElems); + ures_close(gregorianElems); + + return eras; +} + +static jobjectArray getMonthNames(JNIEnv *env, UResourceBundle *gregorian) { + + UErrorCode status = U_ZERO_ERROR; + + const jchar* month; + jstring monthU; + + UResourceBundle *gregorianElems = ures_getByKey(gregorian, "monthNames", NULL, &status); + if(U_FAILURE(status)) { + return NULL; + } + + UResourceBundle *monthNameElems = ures_getByKey(gregorianElems, "format", NULL, &status); + if(U_FAILURE(status)) { + ures_close(gregorianElems); + return NULL; + } + + UResourceBundle *monthNameElemsFormat = ures_getByKey(monthNameElems, "wide", NULL, &status); + if(U_FAILURE(status)) { + ures_close(monthNameElems); + ures_close(gregorianElems); + return NULL; + } + + int monthNameLength; + ures_resetIterator(monthNameElemsFormat); + int monthCount = ures_getSize(monthNameElemsFormat); + jobjectArray months = env->NewObjectArray(monthCount + 1, string_class, NULL); + for(int i = 0; i < monthCount; i++) { + month = ures_getStringByIndex(monthNameElemsFormat, i, &monthNameLength, &status); + if(U_FAILURE(status)) { + ures_close(monthNameElemsFormat); + ures_close(monthNameElems); + ures_close(gregorianElems); + return NULL; + } + monthU = env->NewString(month, monthNameLength); + env->SetObjectArrayElement(months, i, monthU); + env->DeleteLocalRef(monthU); + } + + monthU = env->NewStringUTF(""); + env->SetObjectArrayElement(months, monthCount, monthU); + env->DeleteLocalRef(monthU); + + ures_close(monthNameElemsFormat); + ures_close(monthNameElems); + ures_close(gregorianElems); + return months; +} + +static jobjectArray getShortMonthNames(JNIEnv *env, UResourceBundle *gregorian) { + + UErrorCode status = U_ZERO_ERROR; + + const jchar* shortMonth; + jstring shortMonthU; + + UResourceBundle *gregorianElems = ures_getByKey(gregorian, "monthNames", NULL, &status); + if(U_FAILURE(status)) { + return NULL; + } + + UResourceBundle *monthNameElems = ures_getByKey(gregorianElems, "format", NULL, &status); + if(U_FAILURE(status)) { + ures_close(gregorianElems); + return NULL; + } + + UResourceBundle *monthNameElemsFormat = ures_getByKey(monthNameElems, "abbreviated", NULL, &status); + if(U_FAILURE(status)) { + ures_close(monthNameElems); + ures_close(gregorianElems); + return NULL; + } + + int shortMonthNameLength; + ures_resetIterator(monthNameElemsFormat); + int shortMonthCount = ures_getSize(monthNameElemsFormat); + // the array length is +1 because the harmony locales had an empty string at the end of their month name array + jobjectArray shortMonths = env->NewObjectArray(shortMonthCount + 1, string_class, NULL); + for(int i = 0; i < shortMonthCount; i++) { + shortMonth = ures_getStringByIndex(monthNameElemsFormat, i, &shortMonthNameLength, &status); + if(U_FAILURE(status)) { + ures_close(monthNameElemsFormat); + ures_close(monthNameElems); + ures_close(gregorianElems); + return NULL; + } + shortMonthU = env->NewString(shortMonth, shortMonthNameLength); + env->SetObjectArrayElement(shortMonths, i, shortMonthU); + env->DeleteLocalRef(shortMonthU); + } + + shortMonthU = env->NewStringUTF(""); + env->SetObjectArrayElement(shortMonths, shortMonthCount, shortMonthU); + env->DeleteLocalRef(shortMonthU); + + ures_close(monthNameElemsFormat); + ures_close(monthNameElems); + ures_close(gregorianElems); + return shortMonths; +} + +static jobjectArray getWeekdayNames(JNIEnv *env, UResourceBundle *gregorian) { + + UErrorCode status = U_ZERO_ERROR; + + const jchar* day; + jstring dayU; + + UResourceBundle *gregorianElems = ures_getByKey(gregorian, "dayNames", NULL, &status); + if(U_FAILURE(status)) { + return NULL; + } + + UResourceBundle *dayNameElems = ures_getByKey(gregorianElems, "format", NULL, &status); + if(U_FAILURE(status)) { + ures_close(gregorianElems); + return NULL; + } + + UResourceBundle *dayNameElemsFormat = ures_getByKey(dayNameElems, "wide", NULL, &status); + if(U_FAILURE(status)) { + ures_close(dayNameElems); + ures_close(gregorianElems); + return NULL; + } + + int dayNameLength; + ures_resetIterator(dayNameElemsFormat); + int dayCount = ures_getSize(dayNameElemsFormat); + jobjectArray weekdays = env->NewObjectArray(dayCount + 1, string_class, NULL); + // first entry in the weekdays array is an empty string + env->SetObjectArrayElement(weekdays, 0, env->NewStringUTF("")); + for(int i = 0; i < dayCount; i++) { + day = ures_getStringByIndex(dayNameElemsFormat, i, &dayNameLength, &status); + if(U_FAILURE(status)) { + ures_close(dayNameElemsFormat); + ures_close(dayNameElems); + ures_close(gregorianElems); + return NULL; + } + dayU = env->NewString(day, dayNameLength); + env->SetObjectArrayElement(weekdays, i + 1, dayU); + env->DeleteLocalRef(dayU); + } + + ures_close(dayNameElemsFormat); + ures_close(dayNameElems); + ures_close(gregorianElems); + return weekdays; + +} + +static jobjectArray getShortWeekdayNames(JNIEnv *env, UResourceBundle *gregorian) { + + UErrorCode status = U_ZERO_ERROR; + + const jchar* shortDay; + jstring shortDayU; + + UResourceBundle *gregorianElems = ures_getByKey(gregorian, "dayNames", NULL, &status); + if(U_FAILURE(status)) { + return NULL; + } + + UResourceBundle *dayNameElems = ures_getByKey(gregorianElems, "format", NULL, &status); + if(U_FAILURE(status)) { + ures_close(gregorianElems); + return NULL; + } + + UResourceBundle *dayNameElemsFormat = ures_getByKey(dayNameElems, "abbreviated", NULL, &status); + if(U_FAILURE(status)) { + ures_close(dayNameElems); + ures_close(gregorianElems); + return NULL; + } + + int shortDayNameLength; + ures_resetIterator(dayNameElemsFormat); + int shortDayCount = ures_getSize(dayNameElemsFormat); + jobjectArray shortWeekdays = env->NewObjectArray(shortDayCount + 1, string_class, NULL); + env->SetObjectArrayElement(shortWeekdays, 0, env->NewStringUTF("")); + for(int i = 0; i < shortDayCount; i++) { + shortDay = ures_getStringByIndex(dayNameElemsFormat, i, &shortDayNameLength, &status); + if(U_FAILURE(status)) { + ures_close(dayNameElemsFormat); + ures_close(dayNameElems); + ures_close(gregorianElems); + return NULL; + } + shortDayU = env->NewString(shortDay, shortDayNameLength); + env->SetObjectArrayElement(shortWeekdays, i + 1, shortDayU); + env->DeleteLocalRef(shortDayU); + } + + ures_close(dayNameElemsFormat); + ures_close(dayNameElems); + ures_close(gregorianElems); + return shortWeekdays; +} + +static jstring getDecimalPatternChars(JNIEnv *env, UResourceBundle *rootElems) { + + UErrorCode status = U_ZERO_ERROR; + + int zeroL, digitL, decSepL, groupL, listL, percentL, permillL, expL, currSepL, minusL; + int patternLength; + + jchar *patternChars; + + const jchar* zero = ures_getStringByIndex(rootElems, 4, &zeroL, &status); + + const jchar* digit = ures_getStringByIndex(rootElems, 5, &digitL, &status); + + const jchar* decSep = ures_getStringByIndex(rootElems, 0, &decSepL, &status); + + const jchar* group = ures_getStringByIndex(rootElems, 1, &groupL, &status); + + const jchar* list = ures_getStringByIndex(rootElems, 2, &listL, &status); + + const jchar* percent = ures_getStringByIndex(rootElems, 3, &percentL, &status); + + const jchar* permill = ures_getStringByIndex(rootElems, 8, &permillL, &status); + + const jchar* exp = ures_getStringByIndex(rootElems, 7, &expL, &status); + + const jchar* currSep = ures_getStringByIndex(rootElems, 0, &currSepL, &status); + + const jchar* minus = ures_getStringByIndex(rootElems, 6, &minusL, &status); + + if(U_FAILURE(status)) { + return NULL; + } + + + patternChars = (jchar *) malloc(11 * sizeof(jchar)); + + patternChars[0] = 0; + + u_strncat(patternChars, zero, 1); + u_strncat(patternChars, digit, 1); + u_strncat(patternChars, decSep, 1); + u_strncat(patternChars, group, 1); + u_strncat(patternChars, list, 1); + u_strncat(patternChars, percent, 1); + u_strncat(patternChars, permill, 1); + u_strncat(patternChars, exp, 1); + u_strncat(patternChars, currSep, 1); + u_strncat(patternChars, minus, 1); + + jstring decimalPatternChars = env->NewString(patternChars, 10); + + free(patternChars); + + return decimalPatternChars; +} + +static jstring getIntCurrencyCode(JNIEnv *env, jclass clazz, jstring locale) { + + const char *locStr = env->GetStringUTFChars(locale, NULL); + char country[3] = {0,0,0}; + + // getting the 2 character country name + if(strlen(locStr) < 5) { + env->ReleaseStringUTFChars(locale, locStr); + return NULL; + } + if(locStr[3] < 'A' || locStr[3] > 'Z' || locStr[4] < 'A' || locStr[4] > 'Z') { + env->ReleaseStringUTFChars(locale, locStr); + return NULL; + } + country[0] = locStr[3]; + country[1] = locStr[4]; + + env->ReleaseStringUTFChars(locale, locStr); + + return getCurrencyCodeNative(env, clazz, env->NewStringUTF(country)); +} + +static jstring getCurrencySymbol(JNIEnv *env, jclass clazz, jstring locale, jstring intCurrencySymbol) { + + jstring result = getCurrencySymbolNative(env, clazz, locale, intCurrencySymbol); + if(result == intCurrencySymbol) { + return NULL; + } + return result; + +} + +static jobjectArray getContentImpl(JNIEnv* env, jclass clazz, + jstring locale, jboolean needsTZ) { + + UErrorCode status = U_ZERO_ERROR; + + const char *loc = env->GetStringUTFChars(locale, NULL); + UResourceBundle *root = ures_openU(NULL, loc, &status); + + env->ReleaseStringUTFChars(locale, loc); + if(U_FAILURE(status)) { + LOGI("Error getting resources"); + status = U_ZERO_ERROR; + return NULL; + } + + + + jclass obj_class = env->FindClass("java/lang/Object"); + jclass integer_class = env->FindClass("java/lang/Integer"); + jmethodID integerInit = env->GetMethodID(integer_class, "<init>", "(I)V"); + jobjectArray result; + + jobject firstDayOfWeek = NULL; + jobject minimalDaysInFirstWeek = NULL; + jobjectArray amPmMarkers = NULL; + jobjectArray eras = NULL; + jstring localPatternChars = NULL; + jobjectArray weekdays = NULL; + jobjectArray shortWeekdays = NULL; + jobjectArray months = NULL; + jobjectArray shortMonths = NULL; + jstring time_SHORT = NULL; + jstring time_MEDIUM = NULL; + jstring time_LONG = NULL; + jstring time_FULL = NULL; + jstring date_SHORT = NULL; + jstring date_MEDIUM = NULL; + jstring date_LONG = NULL; + jstring date_FULL = NULL; + jstring decimalPatternChars = NULL; + jstring naN = NULL; + jstring infinity = NULL; + jstring currencySymbol = NULL; + jstring intCurrencySymbol = NULL; + jstring numberPattern = NULL; + jstring integerPattern = NULL; + jstring currencyPattern = NULL; + jstring percentPattern = NULL; + jobjectArray zones = NULL; + + int counter = 0; + + int firstDayVals[2] = {-1, -1}; + + const jchar* nan = (const jchar *)NULL; + const jchar* inf = (const jchar *)NULL; + int nanL, infL; + + + UResourceBundle *gregorian; + UResourceBundle *gregorianElems; + UResourceBundle *rootElems; + + + + + // get the resources needed + rootElems = ures_getByKey(root, "calendar", NULL, &status); + if(U_FAILURE(status)) { + return NULL; + } + + gregorian = ures_getByKey(rootElems, "gregorian", NULL, &status); + if(U_FAILURE(status)) { + ures_close(rootElems); + return NULL; + } + + + + // adding the first day of week and minimal days in first week values + getDayInitVector(env, gregorian, firstDayVals); + if((firstDayVals[0] != -1) && (firstDayVals[1] != -1)) { + firstDayOfWeek = env->NewObject(integer_class, integerInit, firstDayVals[0]); + minimalDaysInFirstWeek = env->NewObject(integer_class, integerInit, firstDayVals[1]); + // adding First_Day and Minimal_Days integer to the result + counter += 2; + } + + + // adding ampm string array to the result"); + amPmMarkers = getAmPmMarkers(env, gregorian); + if(amPmMarkers != NULL) { + counter++; + } + + + // adding eras string array to the result + eras = getEras(env, gregorian); + if(eras != NULL) { + counter++; + } + + + // local pattern chars are initially always the same + localPatternChars = env->NewStringUTF("GyMdkHmsSEDFwWahKzZ"); + // adding local pattern chars string to the result + counter++; + + + // adding month names string array to the result + months = getMonthNames(env, gregorian); + if(months != NULL) { + counter++; + } + + + // adding short month names string array to the result + shortMonths = getShortMonthNames(env, gregorian); + if(shortMonths != NULL) { + counter++; + } + + + // adding day names string array to the result + weekdays = getWeekdayNames(env, gregorian); + if(weekdays != NULL) { + counter++; + } + + + // adding short day names string array to the result + shortWeekdays = getShortWeekdayNames(env, gregorian); + if(shortWeekdays != NULL) { + counter++; + } + + const UChar *pattern; + jchar check[2] = {0, 0}; + u_uastrcpy(check, "v"); + jchar replacement[2] = {0, 0}; + u_uastrcpy(replacement, "z"); + jchar *pos; + jchar *patternCopy; + int patternLength; + + // adding date and time format patterns to the result + gregorianElems = ures_getByKey(gregorian, "DateTimePatterns", NULL, &status); + if(U_FAILURE(status)) { + status = U_ZERO_ERROR; + goto endOfCalendar; + } + + pattern = ures_getStringByIndex(gregorianElems, 0, &patternLength, &status); + // there are some patterns in icu that use the pattern character 'v' + // java doesn't accept this, so it gets replaced by 'z' which has + // about the same result as 'v', the timezone name. + // 'v' -> "PT", 'z' -> "PST", v is the generic timezone and z the standard tz + // "vvvv" -> "Pacific Time", "zzzz" -> "Pacific Standard Time" + patternCopy = (jchar *) malloc((patternLength + 1) * sizeof(jchar)); + u_strcpy(patternCopy, pattern); + if(U_FAILURE(status)) { + free(patternCopy); + status = U_ZERO_ERROR; + goto endOfCalendar; + } + while((pos = u_strchr(patternCopy, check[0])) != NULL) { + u_memset(pos, replacement[0], 1); + } + time_FULL = env->NewString(patternCopy, patternLength); + free(patternCopy); + counter++; + + pattern = ures_getStringByIndex(gregorianElems, 1, &patternLength, &status); + if(U_FAILURE(status)) { + status = U_ZERO_ERROR; + goto endOfCalendar; + } + time_LONG = env->NewString(pattern, patternLength); + counter++; + + pattern = ures_getStringByIndex(gregorianElems, 2, &patternLength, &status); + if(U_FAILURE(status)) { + status = U_ZERO_ERROR; + goto endOfCalendar; + } + time_MEDIUM = env->NewString(pattern, patternLength); + counter++; + + pattern = ures_getStringByIndex(gregorianElems, 3, &patternLength, &status); + if(U_FAILURE(status)) { + status = U_ZERO_ERROR; + goto endOfCalendar; + } + time_SHORT = env->NewString(pattern, patternLength); + counter++; + + pattern = ures_getStringByIndex(gregorianElems, 4, &patternLength, &status); + if(U_FAILURE(status)) { + status = U_ZERO_ERROR; + goto endOfCalendar; + } + date_FULL = env->NewString(pattern, patternLength); + counter++; + + pattern = ures_getStringByIndex(gregorianElems, 5, &patternLength, &status); + if(U_FAILURE(status)) { + status = U_ZERO_ERROR; + goto endOfCalendar; + } + date_LONG = env->NewString(pattern, patternLength); + counter++; + + pattern = ures_getStringByIndex(gregorianElems, 6, &patternLength, &status); + if(U_FAILURE(status)) { + status = U_ZERO_ERROR; + goto endOfCalendar; + } + date_MEDIUM = env->NewString(pattern, patternLength); + counter++; + + pattern = ures_getStringByIndex(gregorianElems, 7, &patternLength, &status); + if(U_FAILURE(status)) { + status = U_ZERO_ERROR; + goto endOfCalendar; + } + date_SHORT = env->NewString(pattern, patternLength); + counter++; + + +endOfCalendar: + + if(gregorianElems != NULL) { + ures_close(gregorianElems); + } + ures_close(gregorian); + ures_close(rootElems); + + + rootElems = ures_getByKey(root, "NumberElements", NULL, &status); + if(U_FAILURE(status)) { + status = U_ZERO_ERROR; + } + + if(ures_getSize(rootElems) >= 11) { + + // adding decimal pattern chars to the result + decimalPatternChars = getDecimalPatternChars(env, rootElems); + if(decimalPatternChars != NULL) { + counter++; + } + + // adding NaN pattern char to the result + nan = ures_getStringByIndex(rootElems, 10, &nanL, &status); + if(U_SUCCESS(status)) { + naN = env->NewString(nan, nanL); + counter++; + } + status = U_ZERO_ERROR; + + // adding infinity pattern char to the result + inf = ures_getStringByIndex(rootElems, 9, &infL, &status); + if(U_SUCCESS(status)) { + infinity = env->NewString(inf, infL); + counter++; + } + status = U_ZERO_ERROR; + } + + ures_close(rootElems); + + + // adding intl currency code to result + intCurrencySymbol = getIntCurrencyCode(env, clazz, locale); + if(intCurrencySymbol != NULL) { + // adding currency symbol to result + currencySymbol = getCurrencySymbol(env, clazz, locale, intCurrencySymbol); + } else { + intCurrencySymbol = env->NewStringUTF("XXX"); + } + if(currencySymbol == NULL) { + currencySymbol = env->NewStringUTF("\u00a4"); + } + counter += 2; + + + // adding number format patterns to the result + int numOfEntries; + int decSepOffset; + NumberFormat *nf; + jchar *tmpPattern; + + rootElems = ures_getByKey(root, "NumberPatterns", NULL, &status); + if(U_FAILURE(status)) { + status = U_ZERO_ERROR; + goto zones; + } + + numOfEntries = ures_getSize(rootElems); + if(numOfEntries < 3) { + ures_close(rootElems); + goto zones; + } + + // number pattern + pattern = ures_getStringByIndex(rootElems, 0, &patternLength, &status); + if(U_FAILURE(status)) { + status = U_ZERO_ERROR; + ures_close(rootElems); + goto zones; + } + numberPattern = env->NewString(pattern, patternLength); + counter++; + + // integer pattern derived from number pattern + // We need to convert a C string literal to a UChar string for u_strcspn. + static const char c_decSep[] = "."; + UChar decSep[sizeof(c_decSep)]; + u_charsToUChars(c_decSep, decSep, sizeof(c_decSep)); + decSepOffset = u_strcspn(pattern, decSep); + tmpPattern = (jchar *) malloc((decSepOffset + 1) * sizeof(jchar)); + u_strncpy(tmpPattern, pattern, decSepOffset); + integerPattern = env->NewString(tmpPattern, decSepOffset); + free(tmpPattern); + counter++; + + // currency pattern + pattern = ures_getStringByIndex(rootElems, 1, &patternLength, &status); + if(U_FAILURE(status)) { + status = U_ZERO_ERROR; + ures_close(rootElems); + goto zones; + } + currencyPattern = env->NewString(pattern, patternLength); + counter++; + + // percent pattern + pattern = ures_getStringByIndex(rootElems, 2, &patternLength, &status); + if(U_FAILURE(status)) { + status = U_ZERO_ERROR; + ures_close(rootElems); + goto zones; + } + percentPattern = env->NewString(pattern, patternLength); + counter++; + + ures_close(rootElems); + +zones: + + ures_close(root); + + + if(needsTZ == JNI_TRUE) { + counter++; //add empty timezone + } + + + + // collect all content and put it into an array + result = env->NewObjectArray(counter, obj_class, NULL); + + int index = 0; + + if(needsTZ == JNI_TRUE) { + addObject(env, result, "timezones", NULL, index++); + } + if(firstDayOfWeek != NULL && index < counter) { + addObject(env, result, "First_Day", firstDayOfWeek, index++); + } + if(minimalDaysInFirstWeek != NULL && index < counter) { + addObject(env, result, "Minimal_Days", minimalDaysInFirstWeek, index++); + } + if(amPmMarkers != NULL && index < counter) { + addObject(env, result, "ampm", amPmMarkers, index++); + } + if(eras != NULL && index < counter) { + addObject(env, result, "eras", eras, index++); + } + if(localPatternChars != NULL && index < counter) { + addObject(env, result, "LocalPatternChars", localPatternChars, index++); + } + if(weekdays != NULL && index < counter) { + addObject(env, result, "weekdays", weekdays, index++); + } + if(shortWeekdays != NULL && index < counter) { + addObject(env, result, "shortWeekdays", shortWeekdays, index++); + } + if(months != NULL && index < counter) { + addObject(env, result, "months", months, index++); + } + if(shortMonths != NULL && index < counter) { + addObject(env, result, "shortMonths", shortMonths, index++); + } + if(time_SHORT != NULL && index < counter) { + addObject(env, result, "Time_SHORT", time_SHORT, index++); + } + if(time_MEDIUM != NULL && index < counter) { + addObject(env, result, "Time_MEDIUM", time_MEDIUM, index++); + } + if(time_LONG != NULL && index < counter) { + addObject(env, result, "Time_LONG", time_LONG, index++); + } + if(time_FULL != NULL && index < counter) { + addObject(env, result, "Time_FULL", time_FULL, index++); + } + if(date_SHORT != NULL && index < counter) { + addObject(env, result, "Date_SHORT", date_SHORT, index++); + } + if(date_MEDIUM != NULL && index < counter) { + addObject(env, result, "Date_MEDIUM", date_MEDIUM, index++); + } + if(date_LONG != NULL && index < counter) { + addObject(env, result, "Date_LONG", date_LONG, index++); + } + if(date_FULL != NULL && index < counter) { + addObject(env, result, "Date_FULL", date_FULL, index++); + } + if(decimalPatternChars != NULL && index < counter) { + addObject(env, result, "DecimalPatternChars", decimalPatternChars, index++); + } + if(naN != NULL && index < counter) { + addObject(env, result, "NaN", naN, index++); + } + if(infinity != NULL && index < counter) { + addObject(env, result, "Infinity", infinity, index++); + } + if(currencySymbol != NULL && index < counter) { + addObject(env, result, "CurrencySymbol", currencySymbol, index++); + } + if(intCurrencySymbol != NULL && index < counter) { + addObject(env, result, "IntCurrencySymbol", intCurrencySymbol, index++); + } + if(numberPattern != NULL && index < counter) { + addObject(env, result, "Number", numberPattern, index++); + } + if(integerPattern != NULL && index < counter) { + addObject(env, result, "Integer", integerPattern, index++); + } + if(currencyPattern != NULL && index < counter) { + addObject(env, result, "Currency", currencyPattern, index++); + } + if(percentPattern != NULL && index < counter) { + addObject(env, result, "Percent", percentPattern, index++); + } + + return result; + +} + +static JNINativeMethod gMethods[] = { + /* name, signature, funcPtr */ + {"getFractionDigitsNative", "(Ljava/lang/String;)I", + (void*) getFractionDigitsNative}, + {"getCurrencyCodeNative", "(Ljava/lang/String;)Ljava/lang/String;", + (void*) getCurrencyCodeNative}, + {"getCurrencySymbolNative", "(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;", + (void*) getCurrencySymbolNative}, + {"getDisplayCountryNative", + "(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;", + (void*) getDisplayCountryNative}, + {"getDisplayLanguageNative", + "(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;", + (void*) getDisplayLanguageNative}, + {"getDisplayVariantNative", + "(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;", + (void*) getDisplayVariantNative}, + {"getISO3CountryNative", + "(Ljava/lang/String;)Ljava/lang/String;", + (void*) getISO3CountryNative}, + {"getISO3LanguageNative", + "(Ljava/lang/String;)Ljava/lang/String;", + (void*) getISO3LanguageNative}, + {"getISOCountriesNative", "()[Ljava/lang/String;", + (void*) getISOCountriesNative}, + {"getISOLanguagesNative", "()[Ljava/lang/String;", + (void*) getISOLanguagesNative}, + {"getAvailableLocalesNative", "()[Ljava/lang/String;", + (void*) getAvailableLocalesNative}, + {"getTimeZonesNative", + "([[Ljava/lang/String;Ljava/lang/String;)V", + (void*) getTimeZonesNative}, + {"getDisplayTimeZoneNative", + "(Ljava/lang/String;ZILjava/lang/String;)Ljava/lang/String;", + (void*) getDisplayTimeZoneNative}, + {"getContentImpl", + "(Ljava/lang/String;Z)[[Ljava/lang/Object;", + (void*) getContentImpl}, +}; + +int register_com_ibm_icu4jni_util_Resources(JNIEnv* env) { + + // initializing String + + jclass stringclass = env->FindClass("java/lang/String"); + + if(stringclass == NULL) { + LOGE("Can't find java/lang/String"); + jniThrowException(env, "java/lang/ClassNotFoundException", "java.lang.String"); + return -1; + } + + string_class = (jclass) env->NewGlobalRef(stringclass); + + return jniRegisterNativeMethods(env, + "com/ibm/icu4jni/util/Resources", gMethods, + NELEM(gMethods)); +} diff --git a/icu/src/main/native/sub.mk b/icu/src/main/native/sub.mk new file mode 100644 index 0000000..2f160f5 --- /dev/null +++ b/icu/src/main/native/sub.mk @@ -0,0 +1,30 @@ +# This file is included by the top-level libcore Android.mk. +# It's not a normal makefile, so we don't include CLEAR_VARS +# or BUILD_*_LIBRARY. + +LOCAL_SRC_FILES := \ + BidiWrapperInterface.c \ + BreakIteratorInterface.c \ + DecimalFormatInterface.cpp \ + CharacterInterface.c \ + ConverterInterface.c \ + CollationInterface.c \ + RegExInterface.cpp \ + ResourceInterface.cpp \ + RBNFInterface.cpp \ + ErrorCode.c + +LOCAL_C_INCLUDES += \ + external/icu4c/common \ + external/icu4c/i18n + +# Any shared/static libs that are listed here must also +# be listed in libs/nativehelper/Android.mk. +# TODO: fix this requirement + +LOCAL_SHARED_LIBRARIES += \ + libicudata \ + libicuuc \ + libicui18n + +LOCAL_STATIC_LIBRARIES += |