BMessageFormat: parse the pattern at construction

* Instead of parsing the pattern everytime Format() is called, parse it
only once when the object is created.
* Adjust all callers to make use of the feature and reuse the instance
as much as possible. This also allows calling B_TRANSLATE only once
instead of everytime the formatting needs to be done. We use either a
static instance (when the message pattern is constant) or a field (when
it is not known to be constant).
* Since the BMessageFormat instances are now reused, add locking to
avoid race conditions (ICU itself is thread safe, but the format pattern
is recreated when the locale is changed)
This commit is contained in:
Adrien Destugues
2014-10-08 15:12:48 +02:00
parent 0e7fcd84af
commit 961fdd8cc3
15 changed files with 179 additions and 59 deletions
+24 -2
View File
@@ -9,10 +9,32 @@
#include <Format.h> #include <Format.h>
namespace icu {
class MessageFormat;
class UnicodeString;
}
class BMessageFormat: public BFormat { class BMessageFormat: public BFormat {
public: public:
status_t Format(BString& buffer, const BString message, BMessageFormat(const BString pattern);
const int32 arg); ~BMessageFormat();
status_t InitCheck();
status_t SetLanguage(const BLanguage& newLanguage);
status_t SetFormattingConventions(
const BFormattingConventions&
conventions);
status_t Format(BString& buffer, const int32 arg) const;
private:
status_t _Initialize(const icu::UnicodeString&);
private:
status_t fInitStatus;
icu::MessageFormat* fFormatter;
}; };
@@ -68,11 +68,12 @@ NotifyFilter::MailboxSynced(status_t status)
system_beep("New E-mail"); system_beep("New E-mail");
if (fStrategy & alert) { if (fStrategy & alert) {
BString text; static BMessageFormat format(B_TRANSLATE(
BMessageFormat().Format(text, B_TRANSLATE(
"You have {0, plural, one{# new message} other{# new messages}} " "You have {0, plural, one{# new message} other{# new messages}} "
"for %account."), fNNewMessages); "for %account."));
BString text;
format.Format(text, fNNewMessages);
text.ReplaceFirst("%account", fMailProtocol.AccountSettings().Name()); text.ReplaceFirst("%account", fMailProtocol.AccountSettings().Name());
BAlert *alert = new BAlert(B_TRANSLATE("New messages"), text.String(), BAlert *alert = new BAlert(B_TRANSLATE("New messages"), text.String(),
@@ -97,11 +98,11 @@ NotifyFilter::MailboxSynced(status_t status)
} }
if (fStrategy & log_window) { if (fStrategy & log_window) {
BString message; static BMessageFormat format(B_TRANSLATE("{0, plural, "
BMessageFormat().Format(message, B_TRANSLATE( "one{# new message} other{# new messages}}"));
"{0, plural, one{# new message} other{# new messages}}"),
fNNewMessages);
BString message;
format.Format(message, fNNewMessages);
fMailProtocol.ShowMessage(message.String()); fMailProtocol.ShowMessage(message.String());
} }
+5 -3
View File
@@ -443,10 +443,12 @@ AboutView::AboutView()
B_ALIGN_VERTICAL_UNSET)); B_ALIGN_VERTICAL_UNSET));
// CPU count, type and clock speed // CPU count, type and clock speed
BString processorLabel; static BMessageFormat format(B_TRANSLATE_COMMENT(
BMessageFormat().Format(processorLabel, B_TRANSLATE_COMMENT(
"{0, plural, one{Processor:} other{# Processors:}}", "{0, plural, one{Processor:} other{# Processors:}}",
"\"Processor:\" or \"2 Processors:\""), systemInfo.cpu_count); "\"Processor:\" or \"2 Processors:\""));
BString processorLabel;
format.Format(processorLabel, systemInfo.cpu_count);
uint32 topologyNodeCount = 0; uint32 topologyNodeCount = 0;
cpu_topology_node_info* topology = NULL; cpu_topology_node_info* topology = NULL;
+3 -2
View File
@@ -151,9 +151,10 @@ StatusView::ShowInfo(const FileInfo* info)
fSizeView->SetText(label); fSizeView->SetText(label);
if (info->count > 0) { if (info->count > 0) {
static BMessageFormat format(B_TRANSLATE("{0, plural, "
"one{# file}, other{# files}}"));
BString label; BString label;
BMessageFormat().Format(label, B_TRANSLATE("{0, plural, one{# file}, " format.Format(label, info->count);
"other{# files}}"), info->count);
fCountView->SetText(label); fCountView->SetText(label);
} else { } else {
fCountView->SetText(kEmptyStr); fCountView->SetText(kEmptyStr);
+4 -2
View File
@@ -609,9 +609,11 @@ public:
private: private:
BString _GetLabel() const BString _GetLabel() const
{ {
static BMessageFormat format(B_TRANSLATE("{0, plural, "
"one{# item} other{# items}}"));
BString label; BString label;
BMessageFormat().Format(label, B_TRANSLATE("{0, plural, one{# item} " format.Format(label, fItemCount);
"other{# items}}"), fItemCount);
return label; return label;
} }
+6 -3
View File
@@ -829,16 +829,19 @@ TInfoView::Draw(BRect updateRect)
MovePenTo(10, fFontHeight + 5); MovePenTo(10, fFontHeight + 5);
static BMessageFormat format(B_TRANSLATE("%width x %height @ {0, plural, "
"one{# pixel/pixel} other{# pixels/pixel}}"));
BString dimensionsInfo; BString dimensionsInfo;
BMessageFormat().Format(dimensionsInfo, format.Format(dimensionsInfo, pixelSize);
B_TRANSLATE("%width x %height @ {0, plural, one{# pixel/pixel} "
"other{# pixels/pixel}}"), pixelSize);
BString rep; BString rep;
rep << hPixelCount; rep << hPixelCount;
dimensionsInfo.ReplaceAll("%width", rep); dimensionsInfo.ReplaceAll("%width", rep);
rep = ""; rep = "";
rep << vPixelCount; rep << vPixelCount;
dimensionsInfo.ReplaceAll("%height", rep); dimensionsInfo.ReplaceAll("%height", rep);
invalRect.Set(10, 5, 10 + StringWidth(fInfoStr), fFontHeight+7); invalRect.Set(10, 5, 10 + StringWidth(fInfoStr), fFontHeight+7);
SetHighColor(ViewColor()); SetHighColor(ViewColor());
FillRect(invalRect); FillRect(invalRect);
+3 -3
View File
@@ -305,10 +305,10 @@ PairsWindow::MessageReceived(BMessage* message)
// Note: in english the singular form is never used, but other // Note: in english the singular form is never used, but other
// languages behave differently. // languages behave differently.
BMessageFormat().Format(strAbout, B_TRANSLATE( static BMessageFormat format(B_TRANSLATE(
"You completed the game in " "You completed the game in "
"{0, plural, one{# click} other{# clicks}}.\n"), "{0, plural, one{# click} other{# clicks}}.\n"));
fButtonClicks); format.Format(strAbout, fButtonClicks);
BAlert* alert = new BAlert("about", BAlert* alert = new BAlert("about",
strAbout.String(), strAbout.String(),
+3 -2
View File
@@ -15,7 +15,7 @@ StatusSlider::StatusSlider(const char* name, const char* label,
const char* statusPrefix, BMessage* message, int32 minValue, int32 maxValue) const char* statusPrefix, BMessage* message, int32 minValue, int32 maxValue)
: :
BSlider(name, label, message, minValue, maxValue, B_HORIZONTAL), BSlider(name, label, message, minValue, maxValue, B_HORIZONTAL),
fStatusPrefix(statusPrefix) fFormat(statusPrefix)
{ {
} }
@@ -23,6 +23,7 @@ StatusSlider::StatusSlider(const char* name, const char* label,
const char* const char*
StatusSlider::UpdateText() const StatusSlider::UpdateText() const
{ {
BMessageFormat().Format(fStr, fStatusPrefix, Value()); fStr.Truncate(0);
fFormat.Format(fStr, Value());
return fStr.String(); return fStr.String();
} }
+2 -1
View File
@@ -10,6 +10,7 @@
//#define BEOS_R5_COMPATIBLE //#define BEOS_R5_COMPATIBLE
#include <MessageFormat.h>
#include <Slider.h> #include <Slider.h>
#include <String.h> #include <String.h>
@@ -26,7 +27,7 @@ public:
virtual const char* UpdateText() const; virtual const char* UpdateText() const;
private: private:
const char* fStatusPrefix; BMessageFormat fFormat;
mutable BString fStr; mutable BString fStr;
}; };
+93 -10
View File
@@ -4,6 +4,7 @@
*/ */
#include <MessageFormat.h> #include <MessageFormat.h>
#include <Autolock.h>
#include <FormattingConventionsPrivate.h> #include <FormattingConventionsPrivate.h>
#include <LanguagePrivate.h> #include <LanguagePrivate.h>
@@ -12,9 +13,74 @@
#include <unicode/msgfmt.h> #include <unicode/msgfmt.h>
status_t BMessageFormat::BMessageFormat(const BString pattern)
BMessageFormat::Format(BString& output, const BString message, const int32 arg) : BFormat()
{ {
_Initialize(UnicodeString::fromUTF8(pattern.String()));
}
BMessageFormat::~BMessageFormat()
{
delete fFormatter;
}
status_t
BMessageFormat::InitCheck()
{
return fInitStatus;
}
status_t
BMessageFormat::SetLanguage(const BLanguage& newLanguage)
{
if (!fFormatter)
return B_NO_INIT;
BAutolock lock(fLock);
if (!lock.IsLocked())
return B_ERROR;
fInitStatus = BFormat::SetLanguage(newLanguage);
if (fInitStatus == B_OK) {
UnicodeString storage;
_Initialize(fFormatter->toPattern(storage));
}
return fInitStatus;
}
status_t
BMessageFormat::SetFormattingConventions(
const BFormattingConventions& conventions)
{
if (!fFormatter)
return B_NO_INIT;
BAutolock lock(fLock);
if (!lock.IsLocked())
return B_ERROR;
fInitStatus = BFormat::SetFormattingConventions(conventions);
if (fInitStatus == B_OK) {
UnicodeString storage;
_Initialize(fFormatter->toPattern(storage));
}
return fInitStatus;
}
status_t
BMessageFormat::Format(BString& output, const int32 arg) const
{
BAutolock lock(fLock);
if (!lock.IsLocked())
return B_ERROR;
UnicodeString buffer; UnicodeString buffer;
UErrorCode error = U_ZERO_ERROR; UErrorCode error = U_ZERO_ERROR;
@@ -22,15 +88,8 @@ BMessageFormat::Format(BString& output, const BString message, const int32 arg)
(int32_t)arg (int32_t)arg
}; };
Locale* icuLocale
= fConventions.UseStringsFromPreferredLanguage()
? BLanguage::Private(&fLanguage).ICULocale()
: BFormattingConventions::Private(&fConventions).ICULocale();
MessageFormat formatter(UnicodeString::fromUTF8(message.String()),
*icuLocale, error);
FieldPosition pos; FieldPosition pos;
buffer = formatter.format(arguments, 1, buffer, pos, error); buffer = fFormatter->format(arguments, 1, buffer, pos, error);
if (!U_SUCCESS(error)) if (!U_SUCCESS(error))
return B_ERROR; return B_ERROR;
@@ -39,3 +98,27 @@ BMessageFormat::Format(BString& output, const BString message, const int32 arg)
return B_OK; return B_OK;
} }
status_t
BMessageFormat::_Initialize(const UnicodeString& pattern)
{
UErrorCode error = U_ZERO_ERROR;
Locale* icuLocale
= fConventions.UseStringsFromPreferredLanguage()
? BLanguage::Private(&fLanguage).ICULocale()
: BFormattingConventions::Private(&fConventions).ICULocale();
fFormatter = new MessageFormat(pattern, *icuLocale, error);
if (fFormatter == NULL)
fInitStatus = B_NO_MEMORY;
if (!U_SUCCESS(error)) {
delete fFormatter;
fInitStatus = B_ERROR;
fFormatter = NULL;
}
return fInitStatus;
}
+3 -3
View File
@@ -232,10 +232,10 @@ BCountView::Draw(BRect updateRect)
if (fLastCount == 0) if (fLastCount == 0)
itemString << B_TRANSLATE("no items"); itemString << B_TRANSLATE("no items");
else { else {
BMessageFormat().Format(itemString, B_TRANSLATE_COMMENT( static BMessageFormat format(B_TRANSLATE_COMMENT(
"{0, plural, one{# item} other{# items}}", "{0, plural, one{# item} other{# items}}",
"Number of selected items: \"1 item\" or \"2 items\""), "Number of selected items: \"1 item\" or \"2 items\""));
fLastCount); format.Format(itemString, fLastCount);
} }
} }
+8 -6
View File
@@ -675,23 +675,25 @@ BInfoWindow::MessageReceived(BMessage* message)
void void
BInfoWindow::GetSizeString(BString &result, off_t size, int32 fileCount) BInfoWindow::GetSizeString(BString &result, off_t size, int32 fileCount)
{ {
char sizeBuffer[128]; static BMessageFormat sizeFormat(B_TRANSLATE(
BMessageFormat messageFormat; "{0, plural, one{(# byte)} other{(# bytes)}}"));
static BMessageFormat countFormat(B_TRANSLATE(
"{0, plural, one{for # file} other{for # files}}"));
char sizeBuffer[128];
result << string_for_size((double)size, sizeBuffer, sizeof(sizeBuffer)); result << string_for_size((double)size, sizeBuffer, sizeof(sizeBuffer));
if (size >= kKBSize) { if (size >= kKBSize) {
result << " "; result << " ";
messageFormat.Format(result, B_TRANSLATE(
"{0, plural, one{(# byte)} other{(# bytes)}}"), size); sizeFormat.Format(result, size);
// "bytes" translation could come from string_for_size // "bytes" translation could come from string_for_size
// which could be part of the localekit itself // which could be part of the localekit itself
} }
if (fileCount != 0) { if (fileCount != 0) {
result << " "; result << " ";
messageFormat.Format(result, B_TRANSLATE( countFormat.Format(result, fileCount);
"{0, plural, one{for # file} other{for # files}}"), fileCount);
} }
} }
+3 -3
View File
@@ -542,10 +542,10 @@ DeskbarView::_BuildMenu()
// The New E-mail query // The New E-mail query
if (fNewMessages > 0) { if (fNewMessages > 0) {
static BMessageFormat format(B_TRANSLATE(
"{0, plural, one{# new message} other{# new messages}}"));
BString string; BString string;
BMessageFormat().Format(string, B_TRANSLATE( format.Format(string, fNewMessages);
"{0, plural, one{# new message} other{# new messages}}"),
fNewMessages);
_GetNewQueryRef(ref); _GetNewQueryRef(ref);
+12 -10
View File
@@ -178,9 +178,10 @@ MailDaemonApp::ReadyToRun()
BString string; BString string;
if (fNewMessages > 0) { if (fNewMessages > 0) {
BMessageFormat().Format(string, B_TRANSLATE( static BMessageFormat format(B_TRANSLATE(
"{0, plural, one{# new message} other{# new messages}}"), "{0, plural, one{# new message} other{# new messages}}"));
fNewMessages);
format.Format(string, fNewMessages);
} else } else
string = B_TRANSLATE("No new messages"); string = B_TRANSLATE("No new messages");
@@ -344,11 +345,12 @@ MailDaemonApp::MessageReceived(BMessage* msg)
case 'numg': case 'numg':
{ {
int32 numMessages = msg->FindInt32("num_messages"); static BMessageFormat format(B_TRANSLATE("{0, plural, "
BMessageFormat().Format(fAlertString, B_TRANSLATE( "one{# new message} other{# new messages}} for %name\n"));
"{0, plural, one{# new message} other{# new messages}} "
"for %name\n"), numMessages);
int32 numMessages = msg->FindInt32("num_messages");
fAlertString.Truncate(0);
format.Format(fAlertString, numMessages);
fAlertString.ReplaceFirst("%name", msg->FindString("name")); fAlertString.ReplaceFirst("%name", msg->FindString("name"));
break; break;
} }
@@ -369,9 +371,9 @@ MailDaemonApp::MessageReceived(BMessage* msg)
BString string; BString string;
if (fNewMessages > 0) { if (fNewMessages > 0) {
BMessageFormat().Format(string, B_TRANSLATE( static BMessageFormat format(B_TRANSLATE(
"{0, plural, one{# new message.} other{# new messages.}}"), "{0, plural, one{# new message.} other{# new messages.}}"));
fNewMessages); format.Format(string, fNewMessages);
} else } else
string << B_TRANSLATE("No new messages."); string << B_TRANSLATE("No new messages.");
+2 -2
View File
@@ -27,7 +27,6 @@ void
MessageFormatTest::TestFormat() MessageFormatTest::TestFormat()
{ {
BString output; BString output;
BMessageFormat formatter;
struct Test { struct Test {
const char* locale; const char* locale;
@@ -55,9 +54,10 @@ MessageFormatTest::TestFormat()
NextSubTest(); NextSubTest();
output.Truncate(0); output.Truncate(0);
BLanguage language(tests[i].locale); BLanguage language(tests[i].locale);
BMessageFormat formatter(tests[i].pattern);
formatter.SetLanguage(language); formatter.SetLanguage(language);
result = formatter.Format(output, tests[i].pattern, tests[i].number); result = formatter.Format(output, tests[i].number);
CPPUNIT_ASSERT_EQUAL(B_OK, result); CPPUNIT_ASSERT_EQUAL(B_OK, result);
CPPUNIT_ASSERT_EQUAL(BString(tests[i].expected), output); CPPUNIT_ASSERT_EQUAL(BString(tests[i].expected), output);
} }