BPicture: Fix archive constructor.
[haiku.git] / src / kits / locale / FormattingConventions.cpp
blob0ae512fee5665fd848496af6a4094888c7cda15b
1 /*
2 * Copyright 2003-2009, Axel Dörfler, axeld@pinc-software.de.
3 * Copyright 2009-2010, Adrien Destugues, pulkomandy@gmail.com.
4 * Copyright 2010-2011, Oliver Tappe <zooey@hirschkaefer.de>.
5 * Distributed under the terms of the MIT License.
6 */
9 #include <unicode/uversion.h>
10 #include <FormattingConventions.h>
12 #include <AutoDeleter.h>
13 #include <IconUtils.h>
14 #include <List.h>
15 #include <Language.h>
16 #include <Locale.h>
17 #include <LocaleRoster.h>
18 #include <Resources.h>
19 #include <String.h>
20 #include <UnicodeChar.h>
22 #include <unicode/datefmt.h>
23 #include <unicode/locid.h>
24 #include <unicode/smpdtfmt.h>
25 #include <unicode/ulocdata.h>
26 #include <ICUWrapper.h>
28 #include <iostream>
29 #include <map>
30 #include <monetary.h>
31 #include <new>
32 #include <stdarg.h>
33 #include <stdlib.h>
36 // #pragma mark - helpers
39 static bool
40 FormatUsesAmPm(const BString& format)
42 if (format.Length() == 0)
43 return false;
45 bool inQuote = false;
46 for (const char* s = format.String(); *s != '\0'; ++s) {
47 switch (*s) {
48 case '\'':
49 inQuote = !inQuote;
50 break;
51 case 'a':
52 if (!inQuote)
53 return true;
54 break;
58 return false;
62 static void
63 CoerceFormatTo12HourClock(BString& format)
65 char* s = format.LockBuffer(format.Length());
66 if (s == NULL)
67 return;
69 // change format to use h instead of H, k instead of K, and append an
70 // am/pm marker
71 bool inQuote = false;
72 for (; *s != '\0'; ++s) {
73 switch (*s) {
74 case '\'':
75 inQuote = !inQuote;
76 break;
77 case 'H':
78 if (!inQuote)
79 *s = 'h';
80 break;
81 case 'K':
82 if (!inQuote)
83 *s = 'k';
84 break;
87 format.UnlockBuffer(format.Length());
89 format.Append(" a");
93 static void
94 CoerceFormatTo24HourClock(BString& format)
96 char* buffer = format.LockBuffer(format.Length());
97 char* currentPos = buffer;
98 if (currentPos == NULL)
99 return;
101 // change the format to use H instead of h, K instead of k, and determine
102 // and remove the am/pm marker (including leading whitespace)
103 bool inQuote = false;
104 bool lastWasWhitespace = false;
105 uint32 ch;
106 const char* amPmStartPos = NULL;
107 const char* amPmEndPos = NULL;
108 const char* lastWhitespaceStart = NULL;
109 for (char* previousPos = currentPos; (ch = BUnicodeChar::FromUTF8(
110 (const char**)&currentPos)) != 0; previousPos = currentPos) {
111 switch (ch) {
112 case '\'':
113 inQuote = !inQuote;
114 break;
115 case 'h':
116 if (!inQuote)
117 *previousPos = 'H';
118 break;
119 case 'k':
120 if (!inQuote)
121 *previousPos = 'K';
122 break;
123 case 'a':
124 if (!inQuote) {
125 if (lastWasWhitespace)
126 amPmStartPos = lastWhitespaceStart;
127 else
128 amPmStartPos = previousPos;
129 amPmEndPos = currentPos;
131 break;
132 default:
133 if (!inQuote && BUnicodeChar::IsWhitespace(ch)) {
134 if (!lastWasWhitespace) {
135 lastWhitespaceStart = previousPos;
136 lastWasWhitespace = true;
138 continue;
141 lastWasWhitespace = false;
144 format.UnlockBuffer(format.Length());
145 if (amPmStartPos != NULL && amPmEndPos > amPmStartPos)
146 format.Remove(amPmStartPos - buffer, amPmEndPos - amPmStartPos);
150 static void
151 CoerceFormatToAbbreviatedTimezone(BString& format)
153 char* s = format.LockBuffer(format.Length());
154 if (s == NULL)
155 return;
157 // replace a single 'z' with 'V'
158 bool inQuote = false;
159 bool lastWasZ = false;
160 for (; *s != '\0'; ++s) {
161 switch (*s) {
162 case '\'':
163 inQuote = !inQuote;
164 break;
165 case 'z':
166 if (!inQuote && !lastWasZ && *(s+1) != 'z')
167 *s = 'V';
168 lastWasZ = true;
169 continue;
171 lastWasZ = false;
173 format.UnlockBuffer(format.Length());
177 // #pragma mark - BFormattingConventions
180 enum ClockHoursState {
181 CLOCK_HOURS_UNSET = 0,
182 CLOCK_HOURS_24,
183 CLOCK_HOURS_12
187 BFormattingConventions::BFormattingConventions(const char* id)
189 fCachedUse24HourClock(CLOCK_HOURS_UNSET),
190 fExplicitUse24HourClock(CLOCK_HOURS_UNSET),
191 fUseStringsFromPreferredLanguage(false),
192 fICULocale(new icu::Locale(id))
197 BFormattingConventions::BFormattingConventions(
198 const BFormattingConventions& other)
200 fCachedNumericFormat(other.fCachedNumericFormat),
201 fCachedMonetaryFormat(other.fCachedMonetaryFormat),
202 fCachedUse24HourClock(other.fCachedUse24HourClock),
203 fExplicitNumericFormat(other.fExplicitNumericFormat),
204 fExplicitMonetaryFormat(other.fExplicitMonetaryFormat),
205 fExplicitUse24HourClock(other.fExplicitUse24HourClock),
206 fUseStringsFromPreferredLanguage(other.fUseStringsFromPreferredLanguage),
207 fICULocale(new icu::Locale(*other.fICULocale))
209 for (int s = 0; s < B_DATE_FORMAT_STYLE_COUNT; ++s) {
210 fCachedDateFormats[s] = other.fCachedDateFormats[s];
211 fExplicitDateFormats[s] = other.fExplicitDateFormats[s];
213 for (int t = 0; t < B_TIME_FORMAT_STYLE_COUNT; ++t) {
214 fCachedDateTimeFormats[s][t] = other.fCachedDateFormats[s][t];
215 fExplicitDateFormats[s][t] = other.fExplicitDateFormats[s][t];
218 for (int s = 0; s < B_TIME_FORMAT_STYLE_COUNT; ++s) {
219 fCachedTimeFormats[s] = other.fCachedTimeFormats[s];
220 fExplicitTimeFormats[s] = other.fExplicitTimeFormats[s];
225 BFormattingConventions::BFormattingConventions(const BMessage* archive)
227 fCachedUse24HourClock(CLOCK_HOURS_UNSET),
228 fExplicitUse24HourClock(CLOCK_HOURS_UNSET),
229 fUseStringsFromPreferredLanguage(false)
231 BString conventionsID;
232 status_t status = archive->FindString("conventions", &conventionsID);
233 fICULocale = new icu::Locale(conventionsID);
235 for (int s = 0; s < B_DATE_FORMAT_STYLE_COUNT && status == B_OK; ++s) {
236 BString format;
237 status = archive->FindString("dateFormat", s, &format);
238 if (status == B_OK)
239 fExplicitDateFormats[s] = format;
241 status = archive->FindString("timeFormat", s, &format);
242 if (status == B_OK)
243 fExplicitTimeFormats[s] = format;
246 if (status == B_OK) {
247 int8 use24HourClock;
248 status = archive->FindInt8("use24HourClock", &use24HourClock);
249 if (status == B_OK)
250 fExplicitUse24HourClock = use24HourClock;
252 if (status == B_OK) {
253 bool useStringsFromPreferredLanguage;
254 status = archive->FindBool("useStringsFromPreferredLanguage",
255 &useStringsFromPreferredLanguage);
256 if (status == B_OK)
257 fUseStringsFromPreferredLanguage = useStringsFromPreferredLanguage;
262 BFormattingConventions&
263 BFormattingConventions::operator=(const BFormattingConventions& other)
265 if (this == &other)
266 return *this;
268 for (int s = 0; s < B_DATE_FORMAT_STYLE_COUNT; ++s) {
269 fCachedDateFormats[s] = other.fCachedDateFormats[s];
270 fExplicitDateFormats[s] = other.fExplicitDateFormats[s];
271 for (int t = 0; t < B_TIME_FORMAT_STYLE_COUNT; ++t) {
272 fCachedDateTimeFormats[s][t] = other.fCachedDateTimeFormats[s][t];
273 fExplicitDateTimeFormats[s][t]
274 = other.fExplicitDateTimeFormats[s][t];
277 for (int s = 0; s < B_TIME_FORMAT_STYLE_COUNT; ++s) {
278 fCachedTimeFormats[s] = other.fCachedTimeFormats[s];
279 fExplicitTimeFormats[s] = other.fExplicitTimeFormats[s];
281 fCachedNumericFormat = other.fCachedNumericFormat;
282 fCachedMonetaryFormat = other.fCachedMonetaryFormat;
283 fCachedUse24HourClock = other.fCachedUse24HourClock;
285 fExplicitNumericFormat = other.fExplicitNumericFormat;
286 fExplicitMonetaryFormat = other.fExplicitMonetaryFormat;
287 fExplicitUse24HourClock = other.fExplicitUse24HourClock;
289 fUseStringsFromPreferredLanguage = other.fUseStringsFromPreferredLanguage;
291 *fICULocale = *other.fICULocale;
293 return *this;
297 BFormattingConventions::~BFormattingConventions()
299 delete fICULocale;
303 bool
304 BFormattingConventions::operator==(const BFormattingConventions& other) const
306 if (this == &other)
307 return true;
309 for (int s = 0; s < B_DATE_FORMAT_STYLE_COUNT; ++s) {
310 if (fExplicitDateFormats[s] != other.fExplicitDateFormats[s])
311 return false;
313 for (int s = 0; s < B_TIME_FORMAT_STYLE_COUNT; ++s) {
314 if (fExplicitTimeFormats[s] != other.fExplicitTimeFormats[s])
315 return false;
318 return fExplicitNumericFormat == other.fExplicitNumericFormat
319 && fExplicitMonetaryFormat == other.fExplicitMonetaryFormat
320 && fExplicitUse24HourClock == other.fExplicitUse24HourClock
321 && fUseStringsFromPreferredLanguage
322 == other.fUseStringsFromPreferredLanguage
323 && *fICULocale == *other.fICULocale;
327 bool
328 BFormattingConventions::operator!=(const BFormattingConventions& other) const
330 return !(*this == other);
334 const char*
335 BFormattingConventions::ID() const
337 return fICULocale->getName();
341 const char*
342 BFormattingConventions::LanguageCode() const
344 return fICULocale->getLanguage();
348 const char*
349 BFormattingConventions::CountryCode() const
351 const char* country = fICULocale->getCountry();
352 if (country == NULL || country[0] == '\0')
353 return NULL;
355 return country;
359 bool
360 BFormattingConventions::AreCountrySpecific() const
362 return CountryCode() != NULL;
366 status_t
367 BFormattingConventions::GetNativeName(BString& name) const
369 UnicodeString string;
370 fICULocale->getDisplayName(*fICULocale, string);
371 string.toTitle(NULL, *fICULocale);
373 name.Truncate(0);
374 BStringByteSink converter(&name);
375 string.toUTF8(converter);
377 return B_OK;
381 status_t
382 BFormattingConventions::GetName(BString& name,
383 const BLanguage* displayLanguage) const
385 BString displayLanguageID;
386 if (displayLanguage == NULL) {
387 BLanguage defaultLanguage;
388 BLocale::Default()->GetLanguage(&defaultLanguage);
389 displayLanguageID = defaultLanguage.Code();
390 } else {
391 displayLanguageID = displayLanguage->Code();
394 UnicodeString uString;
395 fICULocale->getDisplayName(Locale(displayLanguageID.String()), uString);
396 name.Truncate(0);
397 BStringByteSink stringConverter(&name);
398 uString.toUTF8(stringConverter);
400 return B_OK;
404 BMeasurementKind
405 BFormattingConventions::MeasurementKind() const
407 UErrorCode error = U_ZERO_ERROR;
408 switch (ulocdata_getMeasurementSystem(ID(), &error)) {
409 case UMS_US:
410 return B_US;
411 case UMS_SI:
412 default:
413 return B_METRIC;
418 status_t
419 BFormattingConventions::GetDateFormat(BDateFormatStyle style,
420 BString& outFormat) const
422 if (style < 0 || style >= B_DATE_FORMAT_STYLE_COUNT)
423 return B_BAD_VALUE;
425 outFormat = fExplicitDateFormats[style].Length()
426 ? fExplicitDateFormats[style]
427 : fCachedDateFormats[style];
429 if (outFormat.Length() > 0)
430 return B_OK;
432 ObjectDeleter<DateFormat> dateFormatter(
433 DateFormat::createDateInstance((DateFormat::EStyle)style, *fICULocale));
434 if (dateFormatter.Get() == NULL)
435 return B_NO_MEMORY;
437 SimpleDateFormat* dateFormatterImpl
438 = static_cast<SimpleDateFormat*>(dateFormatter.Get());
440 UnicodeString icuString;
441 dateFormatterImpl->toPattern(icuString);
442 BStringByteSink stringConverter(&outFormat);
443 icuString.toUTF8(stringConverter);
445 fCachedDateFormats[style] = outFormat;
447 return B_OK;
451 status_t
452 BFormattingConventions::GetTimeFormat(BTimeFormatStyle style,
453 BString& outFormat) const
455 if (style < 0 || style >= B_TIME_FORMAT_STYLE_COUNT)
456 return B_BAD_VALUE;
458 outFormat = fExplicitTimeFormats[style].Length()
459 ? fExplicitTimeFormats[style]
460 : fCachedTimeFormats[style];
462 if (outFormat.Length() > 0)
463 return B_OK;
465 ObjectDeleter<DateFormat> timeFormatter(
466 DateFormat::createTimeInstance((DateFormat::EStyle)style, *fICULocale));
467 if (timeFormatter.Get() == NULL)
468 return B_NO_MEMORY;
470 SimpleDateFormat* timeFormatterImpl
471 = static_cast<SimpleDateFormat*>(timeFormatter.Get());
473 UnicodeString icuString;
474 timeFormatterImpl->toPattern(icuString);
475 BStringByteSink stringConverter(&outFormat);
476 icuString.toUTF8(stringConverter);
478 int8 use24HourClock = fExplicitUse24HourClock != CLOCK_HOURS_UNSET
479 ? fExplicitUse24HourClock : fCachedUse24HourClock;
480 if (use24HourClock != CLOCK_HOURS_UNSET) {
481 // adjust to 12/24-hour clock as requested
482 bool localeUses24HourClock = !FormatUsesAmPm(outFormat);
483 if (localeUses24HourClock) {
484 if (use24HourClock == CLOCK_HOURS_12)
485 CoerceFormatTo12HourClock(outFormat);
486 } else {
487 if (use24HourClock == CLOCK_HOURS_24)
488 CoerceFormatTo24HourClock(outFormat);
492 if (style != B_FULL_TIME_FORMAT) {
493 // use abbreviated timezone in short timezone format
494 CoerceFormatToAbbreviatedTimezone(outFormat);
497 fCachedTimeFormats[style] = outFormat;
499 return B_OK;
503 status_t
504 BFormattingConventions::GetDateTimeFormat(BDateFormatStyle dateStyle,
505 BTimeFormatStyle timeStyle, BString& outFormat) const
507 if (dateStyle < 0 || dateStyle >= B_DATE_FORMAT_STYLE_COUNT)
508 return B_BAD_VALUE;
510 if (timeStyle < 0 || timeStyle >= B_TIME_FORMAT_STYLE_COUNT)
511 return B_BAD_VALUE;
513 outFormat = fExplicitDateTimeFormats[dateStyle][timeStyle].Length()
514 ? fExplicitDateTimeFormats[dateStyle][timeStyle]
515 : fCachedDateTimeFormats[dateStyle][timeStyle];
517 if (outFormat.Length() > 0)
518 return B_OK;
520 ObjectDeleter<DateFormat> dateFormatter(
521 DateFormat::createDateTimeInstance((DateFormat::EStyle)dateStyle,
522 (DateFormat::EStyle)timeStyle, *fICULocale));
523 if (dateFormatter.Get() == NULL)
524 return B_NO_MEMORY;
526 SimpleDateFormat* dateFormatterImpl
527 = static_cast<SimpleDateFormat*>(dateFormatter.Get());
529 UnicodeString icuString;
530 dateFormatterImpl->toPattern(icuString);
531 BStringByteSink stringConverter(&outFormat);
532 icuString.toUTF8(stringConverter);
534 fCachedDateTimeFormats[dateStyle][timeStyle] = outFormat;
536 return B_OK;
540 status_t
541 BFormattingConventions::GetNumericFormat(BString& outFormat) const
543 // TODO!
544 return B_UNSUPPORTED;
548 status_t
549 BFormattingConventions::GetMonetaryFormat(BString& outFormat) const
551 // TODO!
552 return B_UNSUPPORTED;
556 void
557 BFormattingConventions::SetExplicitDateFormat(BDateFormatStyle style,
558 const BString& format)
560 fExplicitDateFormats[style] = format;
564 void
565 BFormattingConventions::SetExplicitTimeFormat(BTimeFormatStyle style,
566 const BString& format)
568 fExplicitTimeFormats[style] = format;
572 void
573 BFormattingConventions::SetExplicitDateTimeFormat(BDateFormatStyle dateStyle,
574 BTimeFormatStyle timeStyle, const BString& format)
576 fExplicitDateTimeFormats[dateStyle][timeStyle] = format;
580 void
581 BFormattingConventions::SetExplicitNumericFormat(const BString& format)
583 fExplicitNumericFormat = format;
587 void
588 BFormattingConventions::SetExplicitMonetaryFormat(const BString& format)
590 fExplicitMonetaryFormat = format;
594 bool
595 BFormattingConventions::UseStringsFromPreferredLanguage() const
597 return fUseStringsFromPreferredLanguage;
601 void
602 BFormattingConventions::SetUseStringsFromPreferredLanguage(bool value)
604 fUseStringsFromPreferredLanguage = value;
608 bool
609 BFormattingConventions::Use24HourClock() const
611 int8 use24HourClock = fExplicitUse24HourClock != CLOCK_HOURS_UNSET
612 ? fExplicitUse24HourClock : fCachedUse24HourClock;
614 if (use24HourClock == CLOCK_HOURS_UNSET) {
615 BString format;
616 GetTimeFormat(B_MEDIUM_TIME_FORMAT, format);
617 fCachedUse24HourClock
618 = FormatUsesAmPm(format) ? CLOCK_HOURS_12 : CLOCK_HOURS_24;
619 return fCachedUse24HourClock == CLOCK_HOURS_24;
622 return fExplicitUse24HourClock == CLOCK_HOURS_24;
626 void
627 BFormattingConventions::SetExplicitUse24HourClock(bool value)
629 int8 newUse24HourClock = value ? CLOCK_HOURS_24 : CLOCK_HOURS_12;
630 if (fExplicitUse24HourClock == newUse24HourClock)
631 return;
633 fExplicitUse24HourClock = newUse24HourClock;
635 for (int s = 0; s < B_TIME_FORMAT_STYLE_COUNT; ++s)
636 fCachedTimeFormats[s].Truncate(0);
640 void
641 BFormattingConventions::UnsetExplicitUse24HourClock()
643 fExplicitUse24HourClock = CLOCK_HOURS_UNSET;
645 for (int s = 0; s < B_TIME_FORMAT_STYLE_COUNT; ++s)
646 fCachedTimeFormats[s].Truncate(0);
650 status_t
651 BFormattingConventions::Archive(BMessage* archive, bool deep) const
653 status_t status = archive->AddString("conventions", fICULocale->getName());
654 for (int s = 0; s < B_DATE_FORMAT_STYLE_COUNT && status == B_OK; ++s) {
655 status = archive->AddString("dateFormat", fExplicitDateFormats[s]);
656 if (status == B_OK)
657 status = archive->AddString("timeFormat", fExplicitTimeFormats[s]);
659 if (status == B_OK)
660 status = archive->AddInt8("use24HourClock", fExplicitUse24HourClock);
661 if (status == B_OK) {
662 status = archive->AddBool("useStringsFromPreferredLanguage",
663 fUseStringsFromPreferredLanguage);
666 return status;