blob: aef878c754f29365b02b6ff5f7ba54c4024a1eb6 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "ICUFormatNumberFunctor.hpp"
#include <algorithm>
#include <xalanc/ICUBridge/ICUBridge.hpp>
#include <xalanc/Include/XalanAutoPtr.hpp>
#include <xalanc/PlatformSupport/DOMStringHelper.hpp>
#include <xalanc/PlatformSupport/XalanDecimalFormatSymbols.hpp>
#include <xalanc/PlatformSupport/XalanMessageLoader.hpp>
#include <xalanc/XPath/XPathExecutionContext.hpp>
namespace XALAN_CPP_NAMESPACE {
ICUFormatNumberFunctor::ICUFormatNumberFunctor(MemoryManager& theManager) :
m_decimalFormatCache(theManager),
m_defaultDecimalFormat(theManager, createDecimalFormat(theManager)),
m_memoryManager(theManager)
{
}
ICUFormatNumberFunctor*
ICUFormatNumberFunctor::create(MemoryManager& theManager)
{
typedef ICUFormatNumberFunctor ThisType;
XalanAllocationGuard theGuard(theManager, theManager.allocate(sizeof(ThisType)));
ThisType* const theResult =
new (theGuard.get()) ThisType(theManager);
theGuard.release();
return theResult;
}
ICUFormatNumberFunctor::~ICUFormatNumberFunctor()
{
using std::for_each;
for_each(
m_decimalFormatCache.begin(),
m_decimalFormatCache.end(),
DecimalFormatCacheStruct::DecimalFormatDeleteFunctor(m_memoryManager));
}
void
ICUFormatNumberFunctor::operator() (
XPathExecutionContext& executionContext,
double theNumber,
const XalanDOMString& thePattern,
const XalanDecimalFormatSymbols* theDFS,
XalanDOMString& theResult,
const XalanNode* context,
const Locator* locator) const
{
if (!doFormat(theNumber, thePattern, theResult, theDFS))
{
const XPathExecutionContext::GetCachedString theGuard(executionContext);
executionContext.problem(
XPathExecutionContext::eXSLTProcessor,
XPathExecutionContext::eWarning,
XalanMessageLoader::getMessage(
theGuard.get(),
XalanMessages::FormatNumberFailed),
locator,
context);
}
}
DecimalFormatType *
ICUFormatNumberFunctor::getCachedDecimalFormat(const XalanDecimalFormatSymbols &theDFS) const
{
using std::find_if;
DecimalFormatCacheListType::iterator i =
find_if(
m_decimalFormatCache.begin(),
m_decimalFormatCache.end(),
DecimalFormatCacheStruct::DecimalFormatFindFunctor(&theDFS));
if (i == m_decimalFormatCache.end())
{
return 0;
}
else
{
// Let's do a quick check to see if we found the first entry.
// If so, we don't have to update the cache, so just return the
// appropriate value...
const DecimalFormatCacheListType::iterator theBegin =
m_decimalFormatCache.begin();
if (i == theBegin)
{
return (*i).m_formatter;
}
else
{
// Save the formatter, because splice() may invalidate
// the iterator.
DecimalFormatType* const theFormatter = (*i).m_formatter;
// Move the entry to the beginning the cache
m_decimalFormatCache.splice(theBegin, m_decimalFormatCache, i);
return theFormatter;
}
}
}
bool
ICUFormatNumberFunctor::doFormat(
double theNumber,
const XalanDOMString& thePattern,
XalanDOMString& theResult,
const XalanDecimalFormatSymbols* theDFS) const
{
if (theDFS == 0)
{
return doICUFormat(theNumber, thePattern, theResult);
}
XalanDOMString nonLocalPattern(m_memoryManager);
UnlocalizePatternFunctor formatter(*theDFS);
formatter(thePattern, nonLocalPattern, m_memoryManager);
DecimalFormatType* const theFormatter =
getCachedDecimalFormat(*theDFS);
if (theFormatter != 0)
{
return doICUFormat(
theNumber,
nonLocalPattern,
theResult,
theFormatter);
}
else
{
DFAutoPtrType theDecimalFormatGuard(
m_memoryManager,
createDecimalFormat(*theDFS, m_memoryManager));
if (theDecimalFormatGuard.get() != 0)
{
// OK, there was no error, so cache the instance...
cacheDecimalFormat(theDecimalFormatGuard.get(), *theDFS);
// Release the collator, since it's in the cache and
// will be controlled by the cache...
DecimalFormatType* const theDecimalFormat =
theDecimalFormatGuard.releasePtr();
return doICUFormat(
theNumber,
nonLocalPattern,
theResult,
theDecimalFormat);
}
else
{
return doICUFormat(theNumber,nonLocalPattern,theResult);
}
}
}
DecimalFormatType*
ICUFormatNumberFunctor::createDecimalFormat(
const XalanDecimalFormatSymbols& theXalanDFS,
MemoryManager& theManager)
{
UErrorCode theStatus = U_ZERO_ERROR;
// Use a XalanAutoPtr, to keep this safe until we construct the DecimalFormat instance.
XalanAutoPtr<icu::DecimalFormatSymbols> theDFS(new icu::DecimalFormatSymbols(theStatus));
// We got a XalanDecimalFormatSymbols, so set the
// corresponding data in the ICU DecimalFormatSymbols.
theDFS->setSymbol(
icu::DecimalFormatSymbols::kZeroDigitSymbol,
UChar(theXalanDFS.getZeroDigit()));
theDFS->setSymbol(
icu::DecimalFormatSymbols::kGroupingSeparatorSymbol,
UChar(theXalanDFS.getGroupingSeparator()));
theDFS->setSymbol(
icu::DecimalFormatSymbols::kDecimalSeparatorSymbol,
UChar(theXalanDFS.getDecimalSeparator()));
theDFS->setSymbol(
icu::DecimalFormatSymbols::kPerMillSymbol,
UChar(theXalanDFS.getPerMill()));
theDFS->setSymbol(
icu::DecimalFormatSymbols::kPercentSymbol,
UChar(theXalanDFS.getPercent()));
theDFS->setSymbol(
icu::DecimalFormatSymbols::kDigitSymbol,
UChar(theXalanDFS.getDigit()));
theDFS->setSymbol(
icu::DecimalFormatSymbols::kPatternSeparatorSymbol,
UChar(theXalanDFS.getPatternSeparator()));
theDFS->setSymbol(
icu::DecimalFormatSymbols::kInfinitySymbol,
ICUBridge::XalanDOMStringToUnicodeString(
theManager,
theXalanDFS.getInfinity()));
theDFS->setSymbol(
icu::DecimalFormatSymbols::kNaNSymbol,
ICUBridge::XalanDOMStringToUnicodeString(
theManager,
theXalanDFS.getNaN()));
theDFS->setSymbol(
icu::DecimalFormatSymbols::kMinusSignSymbol,
UChar(theXalanDFS.getMinusSign()));
theDFS->setSymbol(
icu::DecimalFormatSymbols::kCurrencySymbol,
ICUBridge::XalanDOMStringToUnicodeString(
theManager,
theXalanDFS.getCurrencySymbol()));
theDFS->setSymbol(
icu::DecimalFormatSymbols::kIntlCurrencySymbol,
ICUBridge::XalanDOMStringToUnicodeString(
theManager,
theXalanDFS.getInternationalCurrencySymbol()));
theDFS->setSymbol(
icu::DecimalFormatSymbols::kMonetarySeparatorSymbol,
UChar(theXalanDFS.getMonetaryDecimalSeparator()));
// Construct a DecimalFormat instance.
DecimalFormatType* theFormatter = 0;
XalanConstruct(
theManager,
theFormatter,
theStatus);
// Guard this, just in case something happens before
// we return it.
DFAutoPtrType theGuard(theManager, theFormatter);
if (U_SUCCESS(theStatus))
{
// Note that we release the XalanAutoPtr, since the
// DecimalFormat will adopt the DecimalFormatSymbols instance.
theGuard->adoptDecimalFormatSymbols(theDFS.release());
return theGuard.releasePtr();
}
else
{
assert(false);
return 0;
}
}
void
ICUFormatNumberFunctor::cacheDecimalFormat(
DecimalFormatType * theFormatter,
const XalanDecimalFormatSymbols& theDFS) const
{
assert(theFormatter != 0);
// Is the cache full?
if (m_decimalFormatCache.size() == eCacheMax)
{
// Yes, so guard the collator instance, in case pop_back() throws...
DFAutoPtrType theDecimalFormatGuard(
m_memoryManager,
m_decimalFormatCache.back().m_formatter);
m_decimalFormatCache.pop_back();
}
const DecimalFormatCacheListType::value_type emptyDFC(m_memoryManager);
m_decimalFormatCache.push_front(emptyDFC);
DecimalFormatCacheListType::value_type& theEntry =
m_decimalFormatCache.front();
theEntry.m_formatter = theFormatter;
theEntry.m_DFS = theDFS;
}
bool
ICUFormatNumberFunctor::doICUFormat(
double theNumber,
const XalanDOMString& thePattern,
XalanDOMString& theResult,
DecimalFormatType* theFormatter) const
{
UErrorCode theStatus = U_ZERO_ERROR;
if (theFormatter == 0)
{
if (m_defaultDecimalFormat.get() != 0)
{
theFormatter = m_defaultDecimalFormat.get();
}
else
{
return false;
}
}
theFormatter->applyPattern(
ICUBridge::XalanDOMStringToUnicodeString(m_memoryManager, thePattern),
theStatus);
if (U_SUCCESS(theStatus))
{
// Do the format...
icu::UnicodeString theUnicodeResult;
theFormatter->format(theNumber, theUnicodeResult);
ICUBridge::UnicodeStringToXalanDOMString(theUnicodeResult, theResult);
}
return U_SUCCESS(theStatus) ? true : false;
}
XalanDOMString&
ICUFormatNumberFunctor::UnlocalizePatternFunctor::operator()(
const XalanDOMString& thePattern,
XalanDOMString& theResult,
MemoryManager& theManager) const
{
XalanDecimalFormatSymbols defaultDFS(theManager);
XalanDOMString::const_iterator iterator = thePattern.begin();
while( iterator != thePattern.end())
{
if( m_DFS.getDecimalSeparator() == *iterator )
{
theResult.push_back(defaultDFS.getDecimalSeparator());
}
else if(m_DFS.getDigit() == *iterator)
{
theResult.push_back(defaultDFS.getDigit());
}
else if(m_DFS.getGroupingSeparator() == *iterator)
{
theResult.push_back(defaultDFS.getGroupingSeparator());
}
else if(m_DFS.getMinusSign() == *iterator)
{
theResult.push_back(defaultDFS.getMinusSign());
}
else if(m_DFS.getPatternSeparator() == *iterator)
{
theResult.push_back(defaultDFS.getPatternSeparator());
}
else if(m_DFS.getPercent() == *iterator)
{
theResult.push_back(defaultDFS.getPercent());
}
else if(m_DFS.getPerMill() == *iterator)
{
theResult.push_back(defaultDFS.getPerMill());
}
else if(m_DFS.getZeroDigit() == *iterator)
{
theResult.push_back(defaultDFS.getZeroDigit());
}
else
{
switch(*iterator)
{
case XalanUnicode::charFullStop:
case XalanUnicode::charNumberSign:
case XalanUnicode::charComma:
case XalanUnicode::charHyphenMinus:
case XalanUnicode::charSemicolon:
case XalanUnicode::charPercentSign:
case XalanUnicode::charPerMilleSign:
case XalanUnicode::charDigit_0:
{
theResult.push_back(XalanUnicode::charAmpersand);
theResult.push_back(*iterator);
theResult.push_back(XalanUnicode::charAmpersand);
}
}
}
iterator++;
}
return theResult;
}
}