#include "ucln_cmn.h"
#include "ustr_imp.h"
#include "charstr.h"
+#include "bytesinkutil.h"
U_CDECL_BEGIN
static UBool U_CALLCONV locale_cleanup(void);
return uloc_getKeywordValue(fullName, keywordName, buffer, bufLen, &status);
}
+void
+Locale::getKeywordValue(StringPiece keywordName, ByteSink& sink, UErrorCode& status) const {
+ if (U_FAILURE(status)) {
+ return;
+ }
+
+ if (fIsBogus) {
+ status = U_ILLEGAL_ARGUMENT_ERROR;
+ return;
+ }
+
+ // TODO: Remove the need for a const char* to a NUL terminated buffer.
+ const CharString keywordName_nul(keywordName, status);
+ if (U_FAILURE(status)) {
+ return;
+ }
+
+ LocalMemory<char> scratch;
+ int32_t scratch_capacity = 16; // Arbitrarily chosen default size.
+
+ char* buffer;
+ int32_t result_capacity, reslen;
+
+ for (;;) {
+ if (scratch.allocateInsteadAndReset(scratch_capacity) == nullptr) {
+ status = U_MEMORY_ALLOCATION_ERROR;
+ return;
+ }
+
+ buffer = sink.GetAppendBuffer(
+ /*min_capacity=*/scratch_capacity,
+ /*desired_capacity_hint=*/scratch_capacity,
+ scratch.getAlias(),
+ scratch_capacity,
+ &result_capacity);
+
+ reslen = uloc_getKeywordValue(
+ fullName,
+ keywordName_nul.data(),
+ buffer,
+ result_capacity,
+ &status);
+
+ if (status != U_BUFFER_OVERFLOW_ERROR) {
+ break;
+ }
+
+ scratch_capacity = reslen;
+ status = U_ZERO_ERROR;
+ }
+
+ if (U_FAILURE(status)) {
+ return;
+ }
+
+ sink.Append(buffer, reslen);
+ if (status == U_STRING_NOT_TERMINATED_WARNING) {
+ status = U_ZERO_ERROR; // Terminators not used.
+ }
+}
+
+void
+Locale::getUnicodeKeywordValue(StringPiece keywordName,
+ ByteSink& sink,
+ UErrorCode& status) const {
+ // TODO: Remove the need for a const char* to a NUL terminated buffer.
+ const CharString keywordName_nul(keywordName, status);
+ if (U_FAILURE(status)) {
+ return;
+ }
+
+ const char* legacy_key = uloc_toLegacyKey(keywordName_nul.data());
+
+ if (legacy_key == nullptr) {
+ status = U_ILLEGAL_ARGUMENT_ERROR;
+ return;
+ }
+
+ CharString legacy_value;
+ {
+ CharStringByteSink sink(&legacy_value);
+ getKeywordValue(legacy_key, sink, status);
+ }
+
+ if (U_FAILURE(status)) {
+ return;
+ }
+
+ const char* unicode_value = uloc_toUnicodeLocaleType(
+ keywordName_nul.data(), legacy_value.data());
+
+ if (unicode_value == nullptr) {
+ status = U_ILLEGAL_ARGUMENT_ERROR;
+ return;
+ }
+
+ sink.Append(unicode_value, uprv_strlen(unicode_value));
+}
+
void
Locale::setKeywordValue(const char* keywordName, const char* keywordValue, UErrorCode &status)
{
}
}
+void
+Locale::setKeywordValue(StringPiece keywordName,
+ StringPiece keywordValue,
+ UErrorCode& status) {
+ // TODO: Remove the need for a const char* to a NUL terminated buffer.
+ const CharString keywordName_nul(keywordName, status);
+ const CharString keywordValue_nul(keywordValue, status);
+ setKeywordValue(keywordName_nul.data(), keywordValue_nul.data(), status);
+}
+
+void
+Locale::setUnicodeKeywordValue(StringPiece keywordName,
+ StringPiece keywordValue,
+ UErrorCode& status) {
+ // TODO: Remove the need for a const char* to a NUL terminated buffer.
+ const CharString keywordName_nul(keywordName, status);
+ const CharString keywordValue_nul(keywordValue, status);
+
+ if (U_FAILURE(status)) {
+ return;
+ }
+
+ const char* legacy_key = uloc_toLegacyKey(keywordName_nul.data());
+
+ if (legacy_key == nullptr) {
+ status = U_ILLEGAL_ARGUMENT_ERROR;
+ return;
+ }
+
+ const char* legacy_value =
+ uloc_toLegacyType(keywordName_nul.data(), keywordValue_nul.data());
+
+ if (legacy_value == nullptr) {
+ status = U_ILLEGAL_ARGUMENT_ERROR;
+ return;
+ }
+
+ setKeywordValue(legacy_key, legacy_value, status);
+}
+
const char *
Locale::getBaseName() const {
return baseName;
/**
* Gets the value for a keyword.
*
+ * This uses legacy keyword=value pairs, like "collation=phonebook".
+ *
+ * ICU4C doesn't do automatic conversion between legacy and Unicode
+ * keywords and values in getters and setters (as opposed to ICU4J).
+ *
* @param keywordName name of the keyword for which we want the value. Case insensitive.
* @param buffer The buffer to receive the keyword value.
* @param bufferCapacity The capacity of receiving buffer
*/
int32_t getKeywordValue(const char* keywordName, char *buffer, int32_t bufferCapacity, UErrorCode &status) const;
+#ifndef U_HIDE_DRAFT_API
+ /**
+ * Gets the value for a keyword.
+ *
+ * This uses legacy keyword=value pairs, like "collation=phonebook".
+ *
+ * ICU4C doesn't do automatic conversion between legacy and Unicode
+ * keywords and values in getters and setters (as opposed to ICU4J).
+ *
+ * @param keywordName name of the keyword for which we want the value.
+ * @param sink the sink to receive the keyword value.
+ * @param status error information if getting the value failed.
+ * @draft ICU 63
+ */
+ void getKeywordValue(StringPiece keywordName, ByteSink& sink, UErrorCode& status) const;
+
+ /**
+ * Gets the value for a keyword.
+ *
+ * This uses legacy keyword=value pairs, like "collation=phonebook".
+ *
+ * ICU4C doesn't do automatic conversion between legacy and Unicode
+ * keywords and values in getters and setters (as opposed to ICU4J).
+ *
+ * @param keywordName name of the keyword for which we want the value.
+ * @param status error information if getting the value failed.
+ * @return the keyword value.
+ * @draft ICU 63
+ */
+ template<typename StringClass>
+ inline StringClass getKeywordValue(StringPiece keywordName, UErrorCode& status) const;
+
+ /**
+ * Gets the Unicode value for a Unicode keyword.
+ *
+ * This uses Unicode key-value pairs, like "co-phonebk".
+ *
+ * ICU4C doesn't do automatic conversion between legacy and Unicode
+ * keywords and values in getters and setters (as opposed to ICU4J).
+ *
+ * @param keywordName name of the keyword for which we want the value.
+ * @param sink the sink to receive the keyword value.
+ * @param status error information if getting the value failed.
+ * @draft ICU 63
+ */
+ void getUnicodeKeywordValue(StringPiece keywordName, ByteSink& sink, UErrorCode& status) const;
+
+ /**
+ * Gets the Unicode value for a Unicode keyword.
+ *
+ * This uses Unicode key-value pairs, like "co-phonebk".
+ *
+ * ICU4C doesn't do automatic conversion between legacy and Unicode
+ * keywords and values in getters and setters (as opposed to ICU4J).
+ *
+ * @param keywordName name of the keyword for which we want the value.
+ * @param status error information if getting the value failed.
+ * @return the keyword value.
+ * @draft ICU 63
+ */
+ template<typename StringClass>
+ inline StringClass getUnicodeKeywordValue(StringPiece keywordName, UErrorCode& status) const;
+#endif // U_HIDE_DRAFT_API
+
/**
* Sets or removes the value for a keyword.
*
* For removing all keywords, use getBaseName(),
* and construct a new Locale if it differs from getName().
*
+ * This uses legacy keyword=value pairs, like "collation=phonebook".
+ *
+ * ICU4C doesn't do automatic conversion between legacy and Unicode
+ * keywords and values in getters and setters (as opposed to ICU4J).
+ *
* @param keywordName name of the keyword to be set. Case insensitive.
* @param keywordValue value of the keyword to be set. If 0-length or
* NULL, will result in the keyword being removed. No error is given if
*/
void setKeywordValue(const char* keywordName, const char* keywordValue, UErrorCode &status);
+#ifndef U_HIDE_DRAFT_API
+ /**
+ * Sets or removes the value for a keyword.
+ *
+ * For removing all keywords, use getBaseName(),
+ * and construct a new Locale if it differs from getName().
+ *
+ * This uses legacy keyword=value pairs, like "collation=phonebook".
+ *
+ * ICU4C doesn't do automatic conversion between legacy and Unicode
+ * keywords and values in getters and setters (as opposed to ICU4J).
+ *
+ * @param keywordName name of the keyword to be set.
+ * @param keywordValue value of the keyword to be set. If 0-length or
+ * NULL, will result in the keyword being removed. No error is given if
+ * that keyword does not exist.
+ * @param status Returns any error information while performing this operation.
+ * @draft ICU 63
+ */
+ void setKeywordValue(StringPiece keywordName, StringPiece keywordValue, UErrorCode& status);
+
+ /**
+ * Sets or removes the Unicode value for a Unicode keyword.
+ *
+ * For removing all keywords, use getBaseName(),
+ * and construct a new Locale if it differs from getName().
+ *
+ * This uses Unicode key-value pairs, like "co-phonebk".
+ *
+ * ICU4C doesn't do automatic conversion between legacy and Unicode
+ * keywords and values in getters and setters (as opposed to ICU4J).
+ *
+ * @param keywordName name of the keyword to be set.
+ * @param keywordValue value of the keyword to be set. If 0-length or
+ * NULL, will result in the keyword being removed. No error is given if
+ * that keyword does not exist.
+ * @param status Returns any error information while performing this operation.
+ * @draft ICU 63
+ */
+ void setUnicodeKeywordValue(StringPiece keywordName, StringPiece keywordValue, UErrorCode& status);
+#endif // U_HIDE_DRAFT_API
+
/**
* returns the locale's three-letter language code, as specified
* in ISO draft standard ISO-639-2.
return fullName;
}
+#ifndef U_HIDE_DRAFT_API
+
+template<typename StringClass> inline StringClass
+Locale::getKeywordValue(StringPiece keywordName, UErrorCode& status) const
+{
+ StringClass result;
+ StringByteSink<StringClass> sink(&result);
+ getKeywordValue(keywordName, sink, status);
+ return result;
+}
+
+template<typename StringClass> inline StringClass
+Locale::getUnicodeKeywordValue(StringPiece keywordName, UErrorCode& status) const
+{
+ StringClass result;
+ StringByteSink<StringClass> sink(&result);
+ getUnicodeKeywordValue(keywordName, sink, status);
+ return result;
+}
+
+#endif // U_HIDE_DRAFT_API
+
inline UBool
Locale::isBogus(void) const {
return fIsBogus;
TESTCASE_AUTO(TestKeywordVariants);
TESTCASE_AUTO(TestCreateUnicodeKeywords);
TESTCASE_AUTO(TestKeywordVariantParsing);
+ TESTCASE_AUTO(TestGetKeywordValueStdString);
+ TESTCASE_AUTO(TestGetUnicodeKeywordValueStdString);
TESTCASE_AUTO(TestSetKeywordValue);
+ TESTCASE_AUTO(TestSetKeywordValueStringPiece);
+ TESTCASE_AUTO(TestSetUnicodeKeywordValueStringPiece);
TESTCASE_AUTO(TestGetBaseName);
#if !UCONFIG_NO_FILE_IO
TESTCASE_AUTO(TestGetLocale);
}
}
+void
+LocaleTest::TestGetKeywordValueStdString(void) {
+ IcuTestErrorCode status(*this, "TestGetKeywordValueStdString()");
+
+ static const char tag[] = "fa-u-nu-latn";
+ static const char keyword[] = "numbers";
+ static const char expected[] = "latn";
+
+ Locale l = Locale::forLanguageTag(tag, status);
+ status.errIfFailureAndReset("\"%s\"", tag);
+
+ std::string result = l.getKeywordValue<std::string>(keyword, status);
+ status.errIfFailureAndReset("\"%s\"", keyword);
+ assertEquals(keyword, expected, result.c_str());
+}
+
+void
+LocaleTest::TestGetUnicodeKeywordValueStdString(void) {
+ IcuTestErrorCode status(*this, "TestGetUnicodeKeywordValueStdString()");
+
+ static const char keyword[] = "co";
+ static const char expected[] = "phonebk";
+
+ static const Locale l("de@calendar=buddhist;collation=phonebook");
+
+ std::string result = l.getUnicodeKeywordValue<std::string>(keyword, status);
+ status.errIfFailureAndReset("\"%s\"", keyword);
+ assertEquals(keyword, expected, result.c_str());
+}
+
void
LocaleTest::TestSetKeywordValue(void) {
static const struct {
}
}
+void
+LocaleTest::TestSetKeywordValueStringPiece(void) {
+ IcuTestErrorCode status(*this, "TestSetKeywordValueStringPiece()");
+ Locale l(Locale::getGerman());
+
+ l.setKeywordValue(StringPiece("collation"), StringPiece("phonebook"), status);
+ l.setKeywordValue(StringPiece("calendarxxx", 8), StringPiece("buddhistxxx", 8), status);
+
+ static const char expected[] = "de@calendar=buddhist;collation=phonebook";
+ assertEquals("", expected, l.getName());
+}
+
+void
+LocaleTest::TestSetUnicodeKeywordValueStringPiece(void) {
+ IcuTestErrorCode status(*this, "TestSetUnicodeKeywordValueStringPiece()");
+ Locale l(Locale::getGerman());
+
+ l.setUnicodeKeywordValue(StringPiece("co"), StringPiece("phonebk"), status);
+ status.errIfFailureAndReset();
+
+ l.setUnicodeKeywordValue(StringPiece("caxxx", 2), StringPiece("buddhistxxx", 8), status);
+ status.errIfFailureAndReset();
+
+ static const char expected[] = "de@calendar=buddhist;collation=phonebook";
+ assertEquals("", expected, l.getName());
+}
+
void
LocaleTest::TestGetBaseName(void) {
static const struct {
/* Test getting keyword values */
void TestKeywordVariantParsing(void);
+ void TestGetKeywordValueStdString(void);
+ void TestGetUnicodeKeywordValueStdString(void);
/* Test setting keyword values */
void TestSetKeywordValue(void);
+ void TestSetKeywordValueStringPiece(void);
+ void TestSetUnicodeKeywordValueStringPiece(void);
/* Test getting the locale base name */
void TestGetBaseName(void);