1/*
2 * Copyright 2015 Google Inc.
3 *
4 * This code is free software; you can redistribute it and/or modify it
5 * under the terms of the GNU General Public License version 2 only, as
6 * published by the Free Software Foundation.  Google designates this
7 * particular file as subject to the "Classpath" exception as provided
8 * by Google in the LICENSE file that accompanied this code.
9 *
10 * This code is distributed in the hope that it will be useful, but WITHOUT
11 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
12 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
13 * version 2 for more details (a copy is included in the LICENSE file that
14 * accompanied this code).
15 *
16 * You should have received a copy of the GNU General Public License version
17 * 2 along with this work; if not, write to the Free Software Foundation,
18 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
19 */
20
21#include "jni.h"
22#include "jvm.h"
23#include "JNIHelp.h"
24#include "unicode/uchar.h"
25#include "unicode/uscript.h"
26#include <math.h>
27#include <stdio.h> // For BUFSIZ
28
29#define NATIVE_METHOD(className, functionName, signature) \
30{ #functionName, signature, (void*)(className ## _ ## functionName) }
31
32JNIEXPORT jboolean JNICALL
33Character_isLowerCaseImpl(JNIEnv* env, jclass, jint codePoint) {
34  return u_islower(codePoint);
35}
36
37JNIEXPORT jboolean JNICALL
38Character_isUpperCaseImpl(JNIEnv* env, jclass, jint codePoint) {
39  return u_isupper(codePoint);
40}
41
42JNIEXPORT jboolean JNICALL
43Character_isTitleCaseImpl(JNIEnv* env, jclass, jint codePoint) {
44  return u_istitle(codePoint);
45}
46
47JNIEXPORT jboolean JNICALL
48Character_isDigitImpl(JNIEnv* env, jclass, jint codePoint) {
49  return u_isdigit(codePoint);
50}
51
52JNIEXPORT jboolean JNICALL
53Character_isLetterImpl(JNIEnv* env, jclass, jint codePoint) {
54  return u_isalpha(codePoint);
55}
56
57JNIEXPORT jboolean JNICALL
58Character_isLetterOrDigitImpl(JNIEnv* env, jclass, jint codePoint) {
59  return u_isalnum(codePoint);
60}
61
62JNIEXPORT jboolean JNICALL
63Character_isAlphabeticImpl(JNIEnv* env, jclass, jint codePoint) {
64  return u_hasBinaryProperty(codePoint, UCHAR_ALPHABETIC);
65}
66
67JNIEXPORT jboolean JNICALL
68Character_isIdeographicImpl(JNIEnv* env, jclass, jint codePoint) {
69  return u_hasBinaryProperty(codePoint, UCHAR_IDEOGRAPHIC);
70}
71
72JNIEXPORT jint JNICALL
73Character_getTypeImpl(JNIEnv* env, jclass, jint codePoint) {
74  return u_charType(codePoint);
75}
76
77JNIEXPORT jboolean JNICALL
78Character_isUnicodeIdentifierStartImpl(JNIEnv* env, jclass, jint codePoint) {
79  return u_isIDStart(codePoint);
80}
81
82JNIEXPORT jboolean JNICALL
83Character_isUnicodeIdentifierPartImpl(JNIEnv* env, jclass, jint codePoint) {
84  return u_isIDPart(codePoint);
85}
86
87JNIEXPORT jint JNICALL
88Character_toLowerCaseImpl(JNIEnv* env, jclass, jint codePoint) {
89  return u_tolower(codePoint);
90}
91
92JNIEXPORT jint JNICALL
93Character_toUpperCaseImpl(JNIEnv* env, jclass, jint codePoint) {
94  return u_toupper(codePoint);
95}
96
97JNIEXPORT jint JNICALL
98Character_toTitleCaseImpl(JNIEnv* env, jclass, jint codePoint) {
99  return u_totitle(codePoint);
100}
101
102JNIEXPORT jint JNICALL
103Character_digitImpl(JNIEnv* env, jclass, jint codePoint, jint radix) {
104  return u_digit(codePoint, radix);
105}
106
107JNIEXPORT jint JNICALL
108Character_getNumericValueImpl(JNIEnv* env, jclass, jint codePoint) {
109  double result = u_getNumericValue(codePoint);
110  if (result == U_NO_NUMERIC_VALUE) {
111    return -1;
112  } else if (result < 0 || floor(result + 0.5) != result) {
113    return -2;
114  }
115  return static_cast<jint>(result);
116}
117
118JNIEXPORT jboolean JNICALL
119Character_isWhitespaceImpl(JNIEnv* env, jclass, jint codePoint) {
120  return u_isWhitespace(codePoint);
121}
122
123JNIEXPORT jbyte JNICALL
124Character_getDirectionalityImpl(JNIEnv* env, jclass, jint codePoint) {
125  return u_charDirection(codePoint);
126}
127
128JNIEXPORT jboolean JNICALL
129Character_isMirroredImpl(JNIEnv* env, jclass, jint codePoint) {
130  return u_isMirrored(codePoint);
131}
132
133JNIEXPORT jboolean JNICALL
134Character_isDefinedImpl(JNIEnv* env, jclass, jint codePoint) {
135  return u_isdefined(codePoint);
136}
137
138JNIEXPORT jboolean JNICALL
139Character_isIdentifierIgnorableImpl(JNIEnv* env, jclass, jint codePoint) {
140    return u_isIDIgnorable(codePoint);
141}
142
143JNIEXPORT jboolean JNICALL
144Character_isSpaceCharImpl(JNIEnv*, jclass, jint codePoint) {
145    return u_isJavaSpaceChar(codePoint);
146}
147
148JNIEXPORT jstring JNICALL
149Character_getNameImpl(JNIEnv* env, jclass, jint codePoint) {
150    // U_UNICODE_CHAR_NAME gives us the modern names for characters. For control characters,
151    // we need U_EXTENDED_CHAR_NAME to get "NULL" rather than "BASIC LATIN 0" and so on.
152    // We could just use U_EXTENDED_CHAR_NAME except that it returns strings for characters
153    // that aren't unassigned but that don't have names, and those strings aren't in the form
154    // Java specifies.
155    bool isControl = (codePoint <= 0x1f || (codePoint >= 0x7f && codePoint <= 0x9f));
156    UCharNameChoice nameType = isControl ? U_EXTENDED_CHAR_NAME : U_UNICODE_CHAR_NAME;
157    UErrorCode status = U_ZERO_ERROR;
158    char buf[BUFSIZ]; // TODO: is there a more sensible upper bound?
159    int32_t byteCount = u_charName(codePoint, nameType, &buf[0], sizeof(buf), &status);
160    return (U_FAILURE(status) || byteCount == 0) ? NULL : env->NewStringUTF(buf);
161}
162
163static JNINativeMethod gMethods[] = {
164  NATIVE_METHOD(Character, digitImpl, "!(II)I"),
165  NATIVE_METHOD(Character, getDirectionalityImpl, "!(I)B"),
166  NATIVE_METHOD(Character, getNameImpl, "(I)Ljava/lang/String;"),
167  NATIVE_METHOD(Character, getNumericValueImpl, "!(I)I"),
168  NATIVE_METHOD(Character, getTypeImpl, "!(I)I"),
169  NATIVE_METHOD(Character, isAlphabeticImpl, "!(I)Z"),
170  NATIVE_METHOD(Character, isDefinedImpl, "!(I)Z"),
171  NATIVE_METHOD(Character, isDigitImpl, "!(I)Z"),
172  NATIVE_METHOD(Character, isIdentifierIgnorableImpl, "!(I)Z"),
173  NATIVE_METHOD(Character, isIdeographicImpl, "!(I)Z"),
174  NATIVE_METHOD(Character, isLetterImpl, "!(I)Z"),
175  NATIVE_METHOD(Character, isLetterOrDigitImpl, "!(I)Z"),
176  NATIVE_METHOD(Character, isLowerCaseImpl, "!(I)Z"),
177  NATIVE_METHOD(Character, isMirroredImpl, "!(I)Z"),
178  NATIVE_METHOD(Character, isSpaceCharImpl, "!(I)Z"),
179  NATIVE_METHOD(Character, isTitleCaseImpl, "!(I)Z"),
180  NATIVE_METHOD(Character, isUnicodeIdentifierPartImpl, "!(I)Z"),
181  NATIVE_METHOD(Character, isUnicodeIdentifierStartImpl, "!(I)Z"),
182  NATIVE_METHOD(Character, isUpperCaseImpl, "!(I)Z"),
183  NATIVE_METHOD(Character, isWhitespaceImpl, "!(I)Z"),
184  NATIVE_METHOD(Character, toLowerCaseImpl, "!(I)I"),
185  NATIVE_METHOD(Character, toTitleCaseImpl, "!(I)I"),
186  NATIVE_METHOD(Character, toUpperCaseImpl, "!(I)I"),
187};
188
189void register_java_lang_Character(JNIEnv* env) {
190  jniRegisterNativeMethods(env, "java/lang/Character", gMethods, NELEM(gMethods));
191}
192