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>
namespace icu {
class MessageFormat;
class UnicodeString;
}
class BMessageFormat: public BFormat {
public:
status_t Format(BString& buffer, const BString message,
const int32 arg);
BMessageFormat(const BString pattern);
~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");
if (fStrategy & alert) {
BString text;
BMessageFormat().Format(text, B_TRANSLATE(
static BMessageFormat format(B_TRANSLATE(
"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());
BAlert *alert = new BAlert(B_TRANSLATE("New messages"), text.String(),
@@ -97,11 +98,11 @@ NotifyFilter::MailboxSynced(status_t status)
}
if (fStrategy & log_window) {
BString message;
BMessageFormat().Format(message, B_TRANSLATE(
"{0, plural, one{# new message} other{# new messages}}"),
fNNewMessages);
static BMessageFormat format(B_TRANSLATE("{0, plural, "
"one{# new message} other{# new messages}}"));
BString message;
format.Format(message, fNNewMessages);
fMailProtocol.ShowMessage(message.String());
}
+5 -3
View File
@@ -443,10 +443,12 @@ AboutView::AboutView()
B_ALIGN_VERTICAL_UNSET));
// CPU count, type and clock speed
BString processorLabel;
BMessageFormat().Format(processorLabel, B_TRANSLATE_COMMENT(
static BMessageFormat format(B_TRANSLATE_COMMENT(
"{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;
cpu_topology_node_info* topology = NULL;
+3 -2
View File
@@ -151,9 +151,10 @@ StatusView::ShowInfo(const FileInfo* info)
fSizeView->SetText(label);
if (info->count > 0) {
static BMessageFormat format(B_TRANSLATE("{0, plural, "
"one{# file}, other{# files}}"));
BString label;
BMessageFormat().Format(label, B_TRANSLATE("{0, plural, one{# file}, "
"other{# files}}"), info->count);
format.Format(label, info->count);
fCountView->SetText(label);
} else {
fCountView->SetText(kEmptyStr);
+4 -2
View File
@@ -609,9 +609,11 @@ public:
private:
BString _GetLabel() const
{
static BMessageFormat format(B_TRANSLATE("{0, plural, "
"one{# item} other{# items}}"));
BString label;
BMessageFormat().Format(label, B_TRANSLATE("{0, plural, one{# item} "
"other{# items}}"), fItemCount);
format.Format(label, fItemCount);
return label;
}
+6 -3
View File
@@ -829,16 +829,19 @@ TInfoView::Draw(BRect updateRect)
MovePenTo(10, fFontHeight + 5);
static BMessageFormat format(B_TRANSLATE("%width x %height @ {0, plural, "
"one{# pixel/pixel} other{# pixels/pixel}}"));
BString dimensionsInfo;
BMessageFormat().Format(dimensionsInfo,
B_TRANSLATE("%width x %height @ {0, plural, one{# pixel/pixel} "
"other{# pixels/pixel}}"), pixelSize);
format.Format(dimensionsInfo, pixelSize);
BString rep;
rep << hPixelCount;
dimensionsInfo.ReplaceAll("%width", rep);
rep = "";
rep << vPixelCount;
dimensionsInfo.ReplaceAll("%height", rep);
invalRect.Set(10, 5, 10 + StringWidth(fInfoStr), fFontHeight+7);
SetHighColor(ViewColor());
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
// languages behave differently.
BMessageFormat().Format(strAbout, B_TRANSLATE(
static BMessageFormat format(B_TRANSLATE(
"You completed the game in "
"{0, plural, one{# click} other{# clicks}}.\n"),
fButtonClicks);
"{0, plural, one{# click} other{# clicks}}.\n"));
format.Format(strAbout, fButtonClicks);
BAlert* alert = new BAlert("about",
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)
:
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*
StatusSlider::UpdateText() const
{
BMessageFormat().Format(fStr, fStatusPrefix, Value());
fStr.Truncate(0);
fFormat.Format(fStr, Value());
return fStr.String();
}
+2 -1
View File
@@ -10,6 +10,7 @@
//#define BEOS_R5_COMPATIBLE
#include <MessageFormat.h>
#include <Slider.h>
#include <String.h>
@@ -26,7 +27,7 @@ public:
virtual const char* UpdateText() const;
private:
const char* fStatusPrefix;
BMessageFormat fFormat;
mutable BString fStr;
};
+93 -10
View File
@@ -4,6 +4,7 @@
*/
#include <MessageFormat.h>
#include <Autolock.h>
#include <FormattingConventionsPrivate.h>
#include <LanguagePrivate.h>
@@ -12,9 +13,74 @@
#include <unicode/msgfmt.h>
status_t
BMessageFormat::Format(BString& output, const BString message, const int32 arg)
BMessageFormat::BMessageFormat(const BString pattern)
: 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;
UErrorCode error = U_ZERO_ERROR;
@@ -22,15 +88,8 @@ BMessageFormat::Format(BString& output, const BString message, const int32 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;
buffer = formatter.format(arguments, 1, buffer, pos, error);
buffer = fFormatter->format(arguments, 1, buffer, pos, error);
if (!U_SUCCESS(error))
return B_ERROR;
@@ -39,3 +98,27 @@ BMessageFormat::Format(BString& output, const BString message, const int32 arg)
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)
itemString << B_TRANSLATE("no items");
else {
BMessageFormat().Format(itemString, B_TRANSLATE_COMMENT(
static BMessageFormat format(B_TRANSLATE_COMMENT(
"{0, plural, one{# item} other{# items}}",
"Number of selected items: \"1 item\" or \"2 items\""),
fLastCount);
"Number of selected items: \"1 item\" or \"2 items\""));
format.Format(itemString, fLastCount);
}
}
+8 -6
View File
@@ -675,23 +675,25 @@ BInfoWindow::MessageReceived(BMessage* message)
void
BInfoWindow::GetSizeString(BString &result, off_t size, int32 fileCount)
{
char sizeBuffer[128];
BMessageFormat messageFormat;
static BMessageFormat sizeFormat(B_TRANSLATE(
"{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));
if (size >= kKBSize) {
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
// which could be part of the localekit itself
}
if (fileCount != 0) {
result << " ";
messageFormat.Format(result, B_TRANSLATE(
"{0, plural, one{for # file} other{for # files}}"), fileCount);
countFormat.Format(result, fileCount);
}
}
+3 -3
View File
@@ -542,10 +542,10 @@ DeskbarView::_BuildMenu()
// The New E-mail query
if (fNewMessages > 0) {
static BMessageFormat format(B_TRANSLATE(
"{0, plural, one{# new message} other{# new messages}}"));
BString string;
BMessageFormat().Format(string, B_TRANSLATE(
"{0, plural, one{# new message} other{# new messages}}"),
fNewMessages);
format.Format(string, fNewMessages);
_GetNewQueryRef(ref);
+12 -10
View File
@@ -178,9 +178,10 @@ MailDaemonApp::ReadyToRun()
BString string;
if (fNewMessages > 0) {
BMessageFormat().Format(string, B_TRANSLATE(
"{0, plural, one{# new message} other{# new messages}}"),
fNewMessages);
static BMessageFormat format(B_TRANSLATE(
"{0, plural, one{# new message} other{# new messages}}"));
format.Format(string, fNewMessages);
} else
string = B_TRANSLATE("No new messages");
@@ -344,11 +345,12 @@ MailDaemonApp::MessageReceived(BMessage* msg)
case 'numg':
{
int32 numMessages = msg->FindInt32("num_messages");
BMessageFormat().Format(fAlertString, B_TRANSLATE(
"{0, plural, one{# new message} other{# new messages}} "
"for %name\n"), numMessages);
static BMessageFormat format(B_TRANSLATE("{0, plural, "
"one{# new message} other{# new messages}} for %name\n"));
int32 numMessages = msg->FindInt32("num_messages");
fAlertString.Truncate(0);
format.Format(fAlertString, numMessages);
fAlertString.ReplaceFirst("%name", msg->FindString("name"));
break;
}
@@ -369,9 +371,9 @@ MailDaemonApp::MessageReceived(BMessage* msg)
BString string;
if (fNewMessages > 0) {
BMessageFormat().Format(string, B_TRANSLATE(
"{0, plural, one{# new message.} other{# new messages.}}"),
fNewMessages);
static BMessageFormat format(B_TRANSLATE(
"{0, plural, one{# new message.} other{# new messages.}}"));
format.Format(string, fNewMessages);
} else
string << B_TRANSLATE("No new messages.");
+2 -2
View File
@@ -27,7 +27,6 @@ void
MessageFormatTest::TestFormat()
{
BString output;
BMessageFormat formatter;
struct Test {
const char* locale;
@@ -55,9 +54,10 @@ MessageFormatTest::TestFormat()
NextSubTest();
output.Truncate(0);
BLanguage language(tests[i].locale);
BMessageFormat formatter(tests[i].pattern);
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(BString(tests[i].expected), output);
}