: NumberFormatterImpl(macros, true, status) {
}
-void NumberFormatterImpl::formatStatic(const MacroProps& macros, DecimalQuantity& inValue,
+int32_t NumberFormatterImpl::formatStatic(const MacroProps& macros, DecimalQuantity& inValue,
NumberStringBuilder& outString, UErrorCode& status) {
NumberFormatterImpl impl(macros, false, status);
MicroProps& micros = impl.preProcessUnsafe(inValue, status);
- if (U_FAILURE(status)) { return; }
+ if (U_FAILURE(status)) { return 0; }
int32_t length = writeNumber(micros, inValue, outString, 0, status);
- writeAffixes(micros, outString, 0, length, status);
+ length += writeAffixes(micros, outString, 0, length, status);
+ return length;
}
int32_t NumberFormatterImpl::getPrefixSuffixStatic(const MacroProps& macros, int8_t signum,
// The "unsafe" method simply re-uses fMicros, eliminating the extra copy operation.
// See MicroProps::processQuantity() for details.
-void NumberFormatterImpl::format(DecimalQuantity& inValue, NumberStringBuilder& outString,
+int32_t NumberFormatterImpl::format(DecimalQuantity& inValue, NumberStringBuilder& outString,
UErrorCode& status) const {
MicroProps micros;
preProcess(inValue, micros, status);
- if (U_FAILURE(status)) { return; }
+ if (U_FAILURE(status)) { return 0; }
int32_t length = writeNumber(micros, inValue, outString, 0, status);
- writeAffixes(micros, outString, 0, length, status);
+ length += writeAffixes(micros, outString, 0, length, status);
+ return length;
}
void NumberFormatterImpl::preProcess(DecimalQuantity& inValue, MicroProps& microsOut,
/**
* Builds and evaluates an "unsafe" MicroPropsGenerator, which is cheaper but can be used only once.
*/
- static void
+ static int32_t
formatStatic(const MacroProps ¯os, DecimalQuantity &inValue, NumberStringBuilder &outString,
UErrorCode &status);
/**
* Evaluates the "safe" MicroPropsGenerator created by "fromMacros".
*/
- void format(DecimalQuantity& inValue, NumberStringBuilder& outString, UErrorCode& status) const;
+ int32_t format(DecimalQuantity& inValue, NumberStringBuilder& outString, UErrorCode& status) const;
/**
* Like format(), but saves the result into an output MicroProps without additional processing.
} else {
U_ASSERT(argLimit == 1);
if (fCompiledPattern.charAt(1) != 0) {
+ // Found prefix
fPrefixLength = fCompiledPattern.charAt(1) - ARG_NUM_LIMIT;
fSuffixOffset = 3 + fPrefixLength;
} else {
+ // No prefix
fPrefixLength = 0;
fSuffixOffset = 2;
}
if (3 + fPrefixLength < fCompiledPattern.length()) {
+ // Found suffix
fSuffixLength = fCompiledPattern.charAt(fSuffixOffset) - ARG_NUM_LIMIT;
} else {
+ // No suffix
fSuffixLength = 0;
}
}
int32_t
SimpleModifier::formatAsPrefixSuffix(NumberStringBuilder &result, int32_t startIndex, int32_t endIndex,
Field field, UErrorCode &status) const {
- if (fSuffixOffset == -1) {
+ if (fSuffixOffset == -1 && fPrefixLength + fSuffixLength > 0) {
// There is no argument for the inner number; overwrite the entire segment with our string.
return result.splice(startIndex, endIndex, fCompiledPattern, 2, 2 + fPrefixLength, field, status);
} else {
}
}
+
+int32_t
+SimpleModifier::formatTwoArgPattern(const SimpleFormatter& compiled, NumberStringBuilder& result,
+ int32_t index, int32_t* outPrefixLength, int32_t* outSuffixLength,
+ Field field, UErrorCode& status) {
+ const UnicodeString& compiledPattern = compiled.compiledPattern;
+ int32_t argLimit = SimpleFormatter::getArgumentLimit(
+ compiledPattern.getBuffer(), compiledPattern.length());
+ if (argLimit != 2) {
+ status = U_INTERNAL_PROGRAM_ERROR;
+ return 0;
+ }
+ int32_t offset = 1; // offset into compiledPattern
+ int32_t length = 0; // chars added to result
+
+ int32_t prefixLength = compiledPattern.charAt(offset);
+ offset++;
+ if (prefixLength < ARG_NUM_LIMIT) {
+ // No prefix
+ prefixLength = 0;
+ } else {
+ prefixLength -= ARG_NUM_LIMIT;
+ result.insert(index + length, compiledPattern, offset, offset + prefixLength, field, status);
+ offset += prefixLength;
+ length += prefixLength;
+ offset++;
+ }
+
+ int32_t infixLength = compiledPattern.charAt(offset);
+ offset++;
+ if (infixLength < ARG_NUM_LIMIT) {
+ // No infix
+ infixLength = 0;
+ } else {
+ infixLength -= ARG_NUM_LIMIT;
+ result.insert(index + length, compiledPattern, offset, offset + infixLength, field, status);
+ offset += infixLength;
+ length += infixLength;
+ offset++;
+ }
+
+ int32_t suffixLength;
+ if (offset == compiledPattern.length()) {
+ // No suffix
+ suffixLength = 0;
+ } else {
+ suffixLength = compiledPattern.charAt(offset) - ARG_NUM_LIMIT;
+ offset++;
+ result.insert(index + length, compiledPattern, offset, offset + suffixLength, field, status);
+ length += suffixLength;
+ }
+
+ *outPrefixLength = prefixLength;
+ *outSuffixLength = suffixLength;
+
+ return length;
+}
+
+
int32_t ConstantMultiFieldModifier::apply(NumberStringBuilder &output, int leftIndex, int rightIndex,
UErrorCode &status) const {
int32_t length = output.insert(leftIndex, fPrefix, status);
/**
* TODO: This belongs in SimpleFormatterImpl. The only reason I haven't moved it there yet is because
- * DoubleSidedStringBuilder is an internal class and SimpleFormatterImpl feels like it should not depend on it.
+ * NumberStringBuilder is an internal class and SimpleFormatterImpl feels like it should not depend on it.
*
* <p>
* Formats a value that is already stored inside the StringBuilder <code>result</code> between the indices
* @return The number of characters (UTF-16 code points) that were added to the StringBuilder.
*/
int32_t
- formatAsPrefixSuffix(NumberStringBuilder &result, int32_t startIndex, int32_t endIndex, Field field,
- UErrorCode &status) const;
+ formatAsPrefixSuffix(NumberStringBuilder& result, int32_t startIndex, int32_t endIndex, Field field,
+ UErrorCode& status) const;
+
+ /**
+ * TODO: Like above, this belongs with the rest of the SimpleFormatterImpl code.
+ * I put it here so that the SimpleFormatter uses in NumberStringBuilder are near each other.
+ *
+ * <p>
+ * Applies the compiled two-argument pattern to the NumberStringBuilder.
+ *
+ * <p>
+ * This method is optimized for the case where the prefix and suffix are often empty, such as
+ * in the range pattern like "{0}-{1}".
+ */
+ static int32_t
+ formatTwoArgPattern(const SimpleFormatter& compiled, NumberStringBuilder& result,
+ int32_t index, int32_t* outPrefixLength, int32_t* outSuffixLength,
+ Field field, UErrorCode& status);
private:
UnicodeString fCompiledPattern;
Field fField;
- bool fStrong;
- int32_t fPrefixLength;
- int32_t fSuffixOffset;
- int32_t fSuffixLength;
+ bool fStrong = false;
+ int32_t fPrefixLength = 0;
+ int32_t fSuffixOffset = -1;
+ int32_t fSuffixLength = 0;
};
/**
}
int32_t NumberStringBuilder::prepareForInsert(int32_t index, int32_t count, UErrorCode &status) {
+ U_ASSERT(index >= 0);
+ U_ASSERT(index <= fLength);
+ U_ASSERT(count >= 0);
if (index == 0 && fZero - count >= 0) {
// Append to start
fZero -= count;
#include "unicode/numberrangeformatter.h"
#include "numrange_impl.h"
+#include "uresimp.h"
+#include "util.h"
using namespace icu;
using namespace icu::number;
return static_cast<int8_t>(a) | (static_cast<int8_t>(b) << 4);
}
+
+class NumberRangeDataSink : public ResourceSink {
+ public:
+ NumberRangeDataSink(NumberRangeData& data) : fData(data) {}
+
+ void put(const char* key, ResourceValue& value, UBool /*noFallback*/, UErrorCode& status) U_OVERRIDE {
+ ResourceTable miscTable = value.getTable(status);
+ if (U_FAILURE(status)) { return; }
+ for (int i = 0; miscTable.getKeyAndValue(i, key, value); i++) {
+ if (uprv_strcmp(key, "range") == 0) {
+ if (fData.rangePattern.getArgumentLimit() == 0) {
+ continue; // have already seen this pattern
+ }
+ fData.rangePattern = {value.getUnicodeString(status), status};
+ } else if (uprv_strcmp(key, "approximately") == 0) {
+ if (fData.approximatelyPattern.getArgumentLimit() == 0) {
+ continue; // have already seen this pattern
+ }
+ fData.approximatelyPattern = {value.getUnicodeString(status), status};
+ }
+ }
+ }
+
+ private:
+ NumberRangeData& fData;
+};
+
+void getNumberRangeData(const char* localeName, const char* nsName, NumberRangeData& data, UErrorCode& status) {
+ if (U_FAILURE(status)) { return; }
+ LocalUResourceBundlePointer rb(ures_open(NULL, localeName, &status));
+ if (U_FAILURE(status)) { return; }
+ NumberRangeDataSink sink(data);
+
+ CharString dataPath;
+ dataPath.append("NumberElements/", -1, status);
+ dataPath.append(nsName, -1, status);
+ dataPath.append("/miscPatterns", -1, status);
+ ures_getAllItemsWithFallback(rb.getAlias(), dataPath.data(), sink, status);
+ if (U_FAILURE(status)) { return; }
+
+ if (uprv_strcmp(nsName, "latn") != 0 && (data.rangePattern.getArgumentLimit() == 0
+ || data.approximatelyPattern.getArgumentLimit() == 0)) {
+ // fall back to Latin data
+ ures_getAllItemsWithFallback(rb.getAlias(), "NumberElements/latn/miscPatterns", sink, status);
+ if (U_FAILURE(status)) { return; }
+ }
+
+ if (data.rangePattern.getArgumentLimit() == 0) {
+ // No data!
+ data.rangePattern = {u"{0} --- {1}", status};
+ }
+ if (data.approximatelyPattern.getArgumentLimit() == 0) {
+ // No data!
+ data.approximatelyPattern = {u"~{0}", status};
+ }
+}
+
} // namespace
fSameFormatters(true), // FIXME
fCollapse(macros.collapse),
fIdentityFallback(macros.identityFallback) {
+ // TODO: get local ns
+ NumberRangeData data;
+ getNumberRangeData(macros.locale.getName(), "latn", data, status);
+ if (U_FAILURE(status)) { return; }
+ fRangeFormatter = data.rangePattern;
+ fApproximatelyModifier = {data.approximatelyPattern, UNUM_FIELD_COUNT, false};
}
void NumberRangeFormatterImpl::format(UFormattedNumberRangeData& data, bool equalBeforeRounding, UErrorCode& status) const {
MicroProps& micros1, MicroProps& micros2,
UErrorCode& status) const {
if (fSameFormatters) {
- // FIXME
- formatterImpl1.format(data.quantity1, data.string, status);
- data.string.insertCodePoint(0, u'~', UNUM_FIELD_COUNT, status);
+ int32_t length = formatterImpl1.format(data.quantity1, data.string, status);
+ fApproximatelyModifier.apply(data.string, 0, length, status);
} else {
formatRange(data, micros1, micros2, status);
}
}
NumberStringBuilder& string = data.string;
+ int32_t lengthPrefix = 0;
int32_t length1 = 0;
- int32_t lengthShared = 0;
+ int32_t lengthInfix = 0;
int32_t length2 = 0;
- #define UPRV_INDEX_0 0
- #define UPRV_INDEX_1 length1
- #define UPRV_INDEX_2 length1 + lengthShared
- #define UPRV_INDEX_3 length1 + lengthShared + length2
+ int32_t lengthSuffix = 0;
+ #define UPRV_INDEX_0 (lengthPrefix)
+ #define UPRV_INDEX_1 (lengthPrefix + length1)
+ #define UPRV_INDEX_2 (lengthPrefix + length1 + lengthInfix)
+ #define UPRV_INDEX_3 (lengthPrefix + length1 + lengthInfix + length2)
+
+ int32_t lengthRange = SimpleModifier::formatTwoArgPattern(
+ fRangeFormatter,
+ string,
+ 0,
+ &lengthPrefix,
+ &lengthSuffix,
+ UNUM_FIELD_COUNT,
+ status);
+ if (U_FAILURE(status)) { return; }
+ lengthInfix = lengthRange - lengthPrefix - lengthSuffix;
- // TODO: Use localized pattern
- lengthShared += string.insert(UPRV_INDEX_0, u" --- ", UNUM_FIELD_COUNT, status);
length1 += NumberFormatterImpl::writeNumber(micros1, data.quantity1, string, UPRV_INDEX_0, status);
length2 += NumberFormatterImpl::writeNumber(micros2, data.quantity2, string, UPRV_INDEX_2, status);
// TODO: Support padding?
if (collapseInner) {
- lengthShared += micros1.modInner->apply(string, UPRV_INDEX_0, UPRV_INDEX_3, status);
+ lengthInfix += micros1.modInner->apply(string, UPRV_INDEX_0, UPRV_INDEX_3, status);
} else {
length1 += micros1.modInner->apply(string, UPRV_INDEX_0, UPRV_INDEX_1, status);
length2 += micros1.modInner->apply(string, UPRV_INDEX_2, UPRV_INDEX_3, status);
}
if (collapseMiddle) {
- lengthShared += micros1.modMiddle->apply(string, UPRV_INDEX_0, UPRV_INDEX_3, status);
+ lengthInfix += micros1.modMiddle->apply(string, UPRV_INDEX_0, UPRV_INDEX_3, status);
} else {
length1 += micros1.modMiddle->apply(string, UPRV_INDEX_0, UPRV_INDEX_1, status);
length2 += micros1.modMiddle->apply(string, UPRV_INDEX_2, UPRV_INDEX_3, status);
#include "unicode/numberformatter.h"
#include "unicode/numberrangeformatter.h"
+#include "unicode/simpleformatter.h"
#include "number_types.h"
#include "number_decimalquantity.h"
#include "number_formatimpl.h"
};
+struct NumberRangeData {
+ SimpleFormatter rangePattern;
+ SimpleFormatter approximatelyPattern;
+};
+
+
class NumberRangeFormatterImpl : public UMemory {
public:
NumberRangeFormatterImpl(const RangeMacroProps& macros, UErrorCode& status);
UNumberRangeCollapse fCollapse;
UNumberRangeIdentityFallback fIdentityFallback;
+ SimpleFormatter fRangeFormatter;
+ SimpleModifier fApproximatelyModifier;
+
void formatSingleValue(UFormattedNumberRangeData& data,
MicroProps& micros1, MicroProps& micros2,
UErrorCode& status) const;