xref: /aosp_15_r20/external/libtextclassifier/native/utils/calendar/calendar-javaicu.cc (revision 993b0882672172b81d12fad7a7ac0c3e5c824a12)
1 /*
2  * Copyright (C) 2018 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #include "utils/calendar/calendar-javaicu.h"
18 
19 #include "annotator/types.h"
20 #include "utils/base/statusor.h"
21 #include "utils/java/jni-base.h"
22 #include "utils/java/jni-helper.h"
23 
24 namespace libtextclassifier3 {
25 namespace {
26 
27 // Generic version of icu::Calendar::add with error checking.
CalendarAdd(JniCache * jni_cache,JNIEnv * jenv,jobject calendar,jint field,jint value)28 bool CalendarAdd(JniCache* jni_cache, JNIEnv* jenv, jobject calendar,
29                  jint field, jint value) {
30   return JniHelper::CallVoidMethod(jenv, calendar, jni_cache->calendar_add,
31                                    field, value)
32       .ok();
33 }
34 
35 // Generic version of icu::Calendar::get with error checking.
CalendarGet(JniCache * jni_cache,JNIEnv * jenv,jobject calendar,jint field,jint * value)36 bool CalendarGet(JniCache* jni_cache, JNIEnv* jenv, jobject calendar,
37                  jint field, jint* value) {
38   TC3_ASSIGN_OR_RETURN_FALSE(
39       *value,
40       JniHelper::CallIntMethod(jenv, calendar, jni_cache->calendar_get, field));
41   return true;
42 }
43 
44 // Generic version of icu::Calendar::set with error checking.
CalendarSet(JniCache * jni_cache,JNIEnv * jenv,jobject calendar,jint field,jint value)45 bool CalendarSet(JniCache* jni_cache, JNIEnv* jenv, jobject calendar,
46                  jint field, jint value) {
47   return JniHelper::CallVoidMethod(jenv, calendar, jni_cache->calendar_set,
48                                    field, value)
49       .ok();
50 }
51 
52 // Extracts the first tag from a BCP47 tag (e.g. "en" for "en-US").
GetFirstBcp47Tag(const std::string & tag)53 std::string GetFirstBcp47Tag(const std::string& tag) {
54   for (size_t i = 0; i < tag.size(); ++i) {
55     if (tag[i] == '_' || tag[i] == '-') {
56       return std::string(tag, 0, i);
57     }
58   }
59   return tag;
60 }
61 
62 }  // anonymous namespace
63 
Calendar(JniCache * jni_cache)64 Calendar::Calendar(JniCache* jni_cache)
65     : jni_cache_(jni_cache),
66       jenv_(jni_cache_ ? jni_cache->GetEnv() : nullptr),
67       calendar_(nullptr, jenv_) {}
68 
Initialize(const std::string & time_zone,const std::string & locale,int64 time_ms_utc)69 bool Calendar::Initialize(const std::string& time_zone,
70                           const std::string& locale, int64 time_ms_utc) {
71   if (!jni_cache_ || !jenv_) {
72     TC3_LOG(ERROR) << "Initialize without env";
73     return false;
74   }
75 
76   // We'll assume the day indices match later on, so verify it here.
77   if (jni_cache_->calendar_sunday != kSunday ||
78       jni_cache_->calendar_monday != kMonday ||
79       jni_cache_->calendar_tuesday != kTuesday ||
80       jni_cache_->calendar_wednesday != kWednesday ||
81       jni_cache_->calendar_thursday != kThursday ||
82       jni_cache_->calendar_friday != kFriday ||
83       jni_cache_->calendar_saturday != kSaturday) {
84     TC3_LOG(ERROR) << "day of the week indices mismatch";
85     return false;
86   }
87 
88   // Get the time zone.
89   TC3_ASSIGN_OR_RETURN_FALSE(ScopedLocalRef<jstring> java_time_zone_str,
90                              JniHelper::NewStringUTF(jenv_, time_zone.c_str()));
91   TC3_ASSIGN_OR_RETURN_FALSE(
92       ScopedLocalRef<jobject> java_time_zone,
93       JniHelper::CallStaticObjectMethod(jenv_, jni_cache_->timezone_class.get(),
94                                         jni_cache_->timezone_get_timezone,
95                                         java_time_zone_str.get()));
96   if (java_time_zone == nullptr) {
97     TC3_LOG(ERROR) << "failed to get timezone";
98     return false;
99   }
100 
101   // Get the locale.
102   ScopedLocalRef<jobject> java_locale(nullptr, jenv_);
103   if (jni_cache_->locale_for_language_tag) {
104     // API level 21+, we can actually parse language tags.
105     TC3_ASSIGN_OR_RETURN_FALSE(ScopedLocalRef<jstring> java_locale_str,
106                                JniHelper::NewStringUTF(jenv_, locale.c_str()));
107 
108     TC3_ASSIGN_OR_RETURN_FALSE(
109         java_locale,
110         JniHelper::CallStaticObjectMethod(jenv_, jni_cache_->locale_class.get(),
111                                           jni_cache_->locale_for_language_tag,
112                                           java_locale_str.get()));
113   } else {
114     // API level <21. We can't parse tags, so we just use the language.
115     TC3_ASSIGN_OR_RETURN_FALSE(
116         ScopedLocalRef<jstring> java_language_str,
117         JniHelper::NewStringUTF(jenv_, GetFirstBcp47Tag(locale).c_str()));
118 
119     TC3_ASSIGN_OR_RETURN_FALSE(
120         java_locale, JniHelper::NewObject(jenv_, jni_cache_->locale_class.get(),
121                                           jni_cache_->locale_init_string,
122                                           java_language_str.get()));
123   }
124   if (java_locale == nullptr) {
125     TC3_LOG(ERROR) << "failed to get locale";
126     return false;
127   }
128 
129   // Get the calendar.
130   TC3_ASSIGN_OR_RETURN_FALSE(
131       calendar_, JniHelper::CallStaticObjectMethod(
132                      jenv_, jni_cache_->calendar_class.get(),
133                      jni_cache_->calendar_get_instance, java_time_zone.get(),
134                      java_locale.get()));
135   if (calendar_ == nullptr) {
136     TC3_LOG(ERROR) << "failed to get calendar";
137     return false;
138   }
139 
140   // Set the time.
141   if (!JniHelper::CallVoidMethod(jenv_, calendar_.get(),
142                                  jni_cache_->calendar_set_time_in_millis,
143                                  time_ms_utc)
144            .ok()) {
145     TC3_LOG(ERROR) << "failed to set time";
146     return false;
147   }
148   return true;
149 }
150 
GetFirstDayOfWeek(int * value) const151 bool Calendar::GetFirstDayOfWeek(int* value) const {
152   if (!jni_cache_ || !jenv_ || !calendar_) return false;
153 
154   TC3_ASSIGN_OR_RETURN_FALSE(
155       *value,
156       JniHelper::CallIntMethod(jenv_, calendar_.get(),
157                                jni_cache_->calendar_get_first_day_of_week));
158   return true;
159 }
160 
GetTimeInMillis(int64 * value) const161 bool Calendar::GetTimeInMillis(int64* value) const {
162   if (!jni_cache_ || !jenv_ || !calendar_) return false;
163 
164   TC3_ASSIGN_OR_RETURN_FALSE(
165       *value,
166       JniHelper::CallLongMethod(jenv_, calendar_.get(),
167                                 jni_cache_->calendar_get_time_in_millis));
168 
169   return true;
170 }
171 
CalendarLib()172 CalendarLib::CalendarLib() {
173   TC3_LOG(FATAL) << "Java ICU CalendarLib must be initialized with a JniCache.";
174 }
175 
CalendarLib(const std::shared_ptr<JniCache> & jni_cache)176 CalendarLib::CalendarLib(const std::shared_ptr<JniCache>& jni_cache)
177     : jni_cache_(jni_cache) {}
178 
179 // Below is the boilerplate code for implementing the specialisations of
180 // get/set/add for the various field types.
181 #define TC3_DEFINE_FIELD_ACCESSOR(NAME, FIELD, KIND, TYPE)      \
182   bool Calendar::KIND##NAME(TYPE value) const {                 \
183     if (!jni_cache_ || !jenv_ || !calendar_) return false;      \
184     return Calendar##KIND(jni_cache_, jenv_, calendar_.get(),   \
185                           jni_cache_->calendar_##FIELD, value); \
186   }
187 #define TC3_DEFINE_ADD(NAME, CONST) \
188   TC3_DEFINE_FIELD_ACCESSOR(NAME, CONST, Add, int)
189 #define TC3_DEFINE_SET(NAME, CONST) \
190   TC3_DEFINE_FIELD_ACCESSOR(NAME, CONST, Set, int)
191 #define TC3_DEFINE_GET(NAME, CONST) \
192   TC3_DEFINE_FIELD_ACCESSOR(NAME, CONST, Get, int*)
193 
194 TC3_DEFINE_ADD(Second, second)
195 TC3_DEFINE_ADD(Minute, minute)
196 TC3_DEFINE_ADD(HourOfDay, hour_of_day)
197 TC3_DEFINE_ADD(DayOfMonth, day_of_month)
198 TC3_DEFINE_ADD(Year, year)
199 TC3_DEFINE_ADD(Month, month)
200 TC3_DEFINE_GET(DayOfWeek, day_of_week)
201 TC3_DEFINE_SET(ZoneOffset, zone_offset)
202 TC3_DEFINE_SET(DstOffset, dst_offset)
203 TC3_DEFINE_SET(Year, year)
204 TC3_DEFINE_SET(Month, month)
205 TC3_DEFINE_SET(DayOfYear, day_of_year)
206 TC3_DEFINE_SET(DayOfMonth, day_of_month)
207 TC3_DEFINE_SET(DayOfWeek, day_of_week)
208 TC3_DEFINE_SET(HourOfDay, hour_of_day)
209 TC3_DEFINE_SET(Minute, minute)
210 TC3_DEFINE_SET(Second, second)
211 TC3_DEFINE_SET(Millisecond, millisecond)
212 
213 #undef TC3_DEFINE_FIELD_ACCESSOR
214 #undef TC3_DEFINE_ADD
215 #undef TC3_DEFINE_SET
216 #undef TC3_DEFINE_GET
217 
218 }  // namespace libtextclassifier3
219