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:
@@ -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());
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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(),
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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.");
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user