Home | History | Annotate | Download | only in calendar
      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/java/scoped_local_ref.h"
     21 
     22 namespace libtextclassifier3 {
     23 namespace {
     24 
     25 // Generic version of icu::Calendar::add with error checking.
     26 bool CalendarAdd(JniCache* jni_cache, JNIEnv* jenv, jobject calendar,
     27                  jint field, jint value) {
     28   jenv->CallVoidMethod(calendar, jni_cache->calendar_add, field, value);
     29   return !jni_cache->ExceptionCheckAndClear();
     30 }
     31 
     32 // Generic version of icu::Calendar::get with error checking.
     33 bool CalendarGet(JniCache* jni_cache, JNIEnv* jenv, jobject calendar,
     34                  jint field, jint* value) {
     35   *value = jenv->CallIntMethod(calendar, jni_cache->calendar_get, field);
     36   return !jni_cache->ExceptionCheckAndClear();
     37 }
     38 
     39 // Generic version of icu::Calendar::set with error checking.
     40 bool CalendarSet(JniCache* jni_cache, JNIEnv* jenv, jobject calendar,
     41                  jint field, jint value) {
     42   jenv->CallVoidMethod(calendar, jni_cache->calendar_set, field, value);
     43   return !jni_cache->ExceptionCheckAndClear();
     44 }
     45 
     46 // Extracts the first tag from a BCP47 tag (e.g. "en" for "en-US").
     47 std::string GetFirstBcp47Tag(const std::string& tag) {
     48   for (size_t i = 0; i < tag.size(); ++i) {
     49     if (tag[i] == '_' || tag[i] == '-') {
     50       return std::string(tag, 0, i);
     51     }
     52   }
     53   return tag;
     54 }
     55 
     56 }  // anonymous namespace
     57 
     58 Calendar::Calendar(JniCache* jni_cache)
     59     : jni_cache_(jni_cache),
     60       jenv_(jni_cache_ ? jni_cache->GetEnv() : nullptr) {}
     61 
     62 bool Calendar::Initialize(const std::string& time_zone,
     63                           const std::string& locale, int64 time_ms_utc) {
     64   if (!jni_cache_ || !jenv_) {
     65     TC3_LOG(ERROR) << "Initialize without env";
     66     return false;
     67   }
     68 
     69   // We'll assume the day indices match later on, so verify it here.
     70   if (jni_cache_->calendar_sunday !=
     71           static_cast<int>(DateParseData::RelationType::SUNDAY) ||
     72       jni_cache_->calendar_monday !=
     73           static_cast<int>(DateParseData::RelationType::MONDAY) ||
     74       jni_cache_->calendar_tuesday !=
     75           static_cast<int>(DateParseData::RelationType::TUESDAY) ||
     76       jni_cache_->calendar_wednesday !=
     77           static_cast<int>(DateParseData::RelationType::WEDNESDAY) ||
     78       jni_cache_->calendar_thursday !=
     79           static_cast<int>(DateParseData::RelationType::THURSDAY) ||
     80       jni_cache_->calendar_friday !=
     81           static_cast<int>(DateParseData::RelationType::FRIDAY) ||
     82       jni_cache_->calendar_saturday !=
     83           static_cast<int>(DateParseData::RelationType::SATURDAY)) {
     84     TC3_LOG(ERROR) << "day of the week indices mismatch";
     85     return false;
     86   }
     87 
     88   // Get the time zone.
     89   ScopedLocalRef<jstring> java_time_zone_str(
     90       jenv_->NewStringUTF(time_zone.c_str()));
     91   ScopedLocalRef<jobject> java_time_zone(jenv_->CallStaticObjectMethod(
     92       jni_cache_->timezone_class.get(), jni_cache_->timezone_get_timezone,
     93       java_time_zone_str.get()));
     94   if (jni_cache_->ExceptionCheckAndClear() || !java_time_zone) {
     95     TC3_LOG(ERROR) << "failed to get timezone";
     96     return false;
     97   }
     98 
     99   // Get the locale.
    100   ScopedLocalRef<jobject> java_locale;
    101   if (jni_cache_->locale_for_language_tag) {
    102     // API level 21+, we can actually parse language tags.
    103     ScopedLocalRef<jstring> java_locale_str(
    104         jenv_->NewStringUTF(locale.c_str()));
    105     java_locale.reset(jenv_->CallStaticObjectMethod(
    106         jni_cache_->locale_class.get(), jni_cache_->locale_for_language_tag,
    107         java_locale_str.get()));
    108   } else {
    109     // API level <21. We can't parse tags, so we just use the language.
    110     ScopedLocalRef<jstring> java_language_str(
    111         jenv_->NewStringUTF(GetFirstBcp47Tag(locale).c_str()));
    112     java_locale.reset(jenv_->NewObject(jni_cache_->locale_class.get(),
    113                                        jni_cache_->locale_init_string,
    114                                        java_language_str.get()));
    115   }
    116   if (jni_cache_->ExceptionCheckAndClear() || !java_locale) {
    117     TC3_LOG(ERROR) << "failed to get locale";
    118     return false;
    119   }
    120 
    121   // Get the calendar.
    122   calendar_.reset(jenv_->CallStaticObjectMethod(
    123       jni_cache_->calendar_class.get(), jni_cache_->calendar_get_instance,
    124       java_time_zone.get(), java_locale.get()));
    125   if (jni_cache_->ExceptionCheckAndClear() || !calendar_) {
    126     TC3_LOG(ERROR) << "failed to get calendar";
    127     return false;
    128   }
    129 
    130   // Set the time.
    131   jenv_->CallVoidMethod(calendar_.get(),
    132                         jni_cache_->calendar_set_time_in_millis, time_ms_utc);
    133   if (jni_cache_->ExceptionCheckAndClear()) {
    134     TC3_LOG(ERROR) << "failed to set time";
    135     return false;
    136   }
    137   return true;
    138 }
    139 
    140 bool Calendar::GetFirstDayOfWeek(int* value) const {
    141   if (!jni_cache_ || !jenv_ || !calendar_) return false;
    142   *value = jenv_->CallIntMethod(calendar_.get(),
    143                                 jni_cache_->calendar_get_first_day_of_week);
    144   return !jni_cache_->ExceptionCheckAndClear();
    145 }
    146 
    147 bool Calendar::GetTimeInMillis(int64* value) const {
    148   if (!jni_cache_ || !jenv_ || !calendar_) return false;
    149   *value = jenv_->CallLongMethod(calendar_.get(),
    150                                  jni_cache_->calendar_get_time_in_millis);
    151   return !jni_cache_->ExceptionCheckAndClear();
    152 }
    153 
    154 CalendarLib::CalendarLib() {
    155   TC3_LOG(FATAL) << "Java ICU CalendarLib must be initialized with a JniCache.";
    156 }
    157 
    158 CalendarLib::CalendarLib(const std::shared_ptr<JniCache>& jni_cache)
    159     : jni_cache_(jni_cache) {}
    160 
    161 // Below is the boilerplate code for implementing the specialisations of
    162 // get/set/add for the various field types.
    163 #define TC3_DEFINE_FIELD_ACCESSOR(NAME, FIELD, KIND, TYPE)      \
    164   bool Calendar::KIND##NAME(TYPE value) const {                 \
    165     if (!jni_cache_ || !jenv_ || !calendar_) return false;      \
    166     return Calendar##KIND(jni_cache_, jenv_, calendar_.get(),   \
    167                           jni_cache_->calendar_##FIELD, value); \
    168   }
    169 #define TC3_DEFINE_ADD(NAME, CONST) \
    170   TC3_DEFINE_FIELD_ACCESSOR(NAME, CONST, Add, int)
    171 #define TC3_DEFINE_SET(NAME, CONST) \
    172   TC3_DEFINE_FIELD_ACCESSOR(NAME, CONST, Set, int)
    173 #define TC3_DEFINE_GET(NAME, CONST) \
    174   TC3_DEFINE_FIELD_ACCESSOR(NAME, CONST, Get, int*)
    175 
    176 TC3_DEFINE_ADD(Second, second)
    177 TC3_DEFINE_ADD(Minute, minute)
    178 TC3_DEFINE_ADD(HourOfDay, hour_of_day)
    179 TC3_DEFINE_ADD(DayOfMonth, day_of_month)
    180 TC3_DEFINE_ADD(Year, year)
    181 TC3_DEFINE_ADD(Month, month)
    182 TC3_DEFINE_GET(DayOfWeek, day_of_week)
    183 TC3_DEFINE_SET(ZoneOffset, zone_offset)
    184 TC3_DEFINE_SET(DstOffset, dst_offset)
    185 TC3_DEFINE_SET(Year, year)
    186 TC3_DEFINE_SET(Month, month)
    187 TC3_DEFINE_SET(DayOfYear, day_of_year)
    188 TC3_DEFINE_SET(DayOfMonth, day_of_month)
    189 TC3_DEFINE_SET(DayOfWeek, day_of_week)
    190 TC3_DEFINE_SET(HourOfDay, hour_of_day)
    191 TC3_DEFINE_SET(Minute, minute)
    192 TC3_DEFINE_SET(Second, second)
    193 TC3_DEFINE_SET(Millisecond, millisecond)
    194 
    195 #undef TC3_DEFINE_FIELD_ACCESSOR
    196 #undef TC3_DEFINE_ADD
    197 #undef TC3_DEFINE_SET
    198 #undef TC3_DEFINE_GET
    199 
    200 }  // namespace libtextclassifier3
    201