Coverage Report

Created: 2018-09-25 14:53

/src/mozilla-central/intl/icu/source/i18n/numparse_currency.cpp
Line
Count
Source (jump to first uncovered line)
1
// © 2018 and later: Unicode, Inc. and others.
2
// License & terms of use: http://www.unicode.org/copyright.html
3
4
#include "unicode/utypes.h"
5
6
#if !UCONFIG_NO_FORMATTING
7
8
// Allow implicit conversion from char16_t* to UnicodeString for this file:
9
// Helpful in toString methods and elsewhere.
10
#define UNISTR_FROM_STRING_EXPLICIT
11
12
#include "numparse_types.h"
13
#include "numparse_currency.h"
14
#include "ucurrimp.h"
15
#include "unicode/errorcode.h"
16
#include "numparse_utils.h"
17
18
using namespace icu;
19
using namespace icu::numparse;
20
using namespace icu::numparse::impl;
21
22
23
CombinedCurrencyMatcher::CombinedCurrencyMatcher(const CurrencySymbols& currencySymbols, const DecimalFormatSymbols& dfs,
24
                                                 parse_flags_t parseFlags, UErrorCode& status)
25
        : fCurrency1(currencySymbols.getCurrencySymbol(status)),
26
          fCurrency2(currencySymbols.getIntlCurrencySymbol(status)),
27
          fUseFullCurrencyData(0 == (parseFlags & PARSE_FLAG_NO_FOREIGN_CURRENCY)),
28
          afterPrefixInsert(dfs.getPatternForCurrencySpacing(UNUM_CURRENCY_INSERT, false, status)),
29
          beforeSuffixInsert(dfs.getPatternForCurrencySpacing(UNUM_CURRENCY_INSERT, true, status)),
30
0
          fLocaleName(dfs.getLocale().getName(), -1, status) {
31
0
    utils::copyCurrencyCode(fCurrencyCode, currencySymbols.getIsoCode());
32
0
33
0
    // Pre-load the long names for the current locale and currency
34
0
    // if we are parsing without the full currency data.
35
0
    if (!fUseFullCurrencyData) {
36
0
        for (int32_t i=0; i<StandardPlural::COUNT; i++) {
37
0
            auto plural = static_cast<StandardPlural::Form>(i);
38
0
            fLocalLongNames[i] = currencySymbols.getPluralName(plural, status);
39
0
        }
40
0
    }
41
0
42
0
    // TODO: Figure out how to make this faster and re-enable.
43
0
    // Computing the "lead code points" set for fastpathing is too slow to use in production.
44
0
    // See http://bugs.icu-project.org/trac/ticket/13584
45
0
//    // Compute the full set of characters that could be the first in a currency to allow for
46
0
//    // efficient smoke test.
47
0
//    fLeadCodePoints.add(fCurrency1.char32At(0));
48
0
//    fLeadCodePoints.add(fCurrency2.char32At(0));
49
0
//    fLeadCodePoints.add(beforeSuffixInsert.char32At(0));
50
0
//    uprv_currencyLeads(fLocaleName.data(), fLeadCodePoints, status);
51
0
//    // Always apply case mapping closure for currencies
52
0
//    fLeadCodePoints.closeOver(USET_ADD_CASE_MAPPINGS);
53
0
//    fLeadCodePoints.freeze();
54
0
}
55
56
bool
57
0
CombinedCurrencyMatcher::match(StringSegment& segment, ParsedNumber& result, UErrorCode& status) const {
58
0
    if (result.currencyCode[0] != 0) {
59
0
        return false;
60
0
    }
61
0
62
0
    // Try to match a currency spacing separator.
63
0
    int32_t initialOffset = segment.getOffset();
64
0
    bool maybeMore = false;
65
0
    if (result.seenNumber() && !beforeSuffixInsert.isEmpty()) {
66
0
        int32_t overlap = segment.getCommonPrefixLength(beforeSuffixInsert);
67
0
        if (overlap == beforeSuffixInsert.length()) {
68
0
            segment.adjustOffset(overlap);
69
0
            // Note: let currency spacing be a weak match. Don't update chars consumed.
70
0
        }
71
0
        maybeMore = maybeMore || overlap == segment.length();
72
0
    }
73
0
74
0
    // Match the currency string, and reset if we didn't find one.
75
0
    maybeMore = maybeMore || matchCurrency(segment, result, status);
76
0
    if (result.currencyCode[0] == 0) {
77
0
        segment.setOffset(initialOffset);
78
0
        return maybeMore;
79
0
    }
80
0
81
0
    // Try to match a currency spacing separator.
82
0
    if (!result.seenNumber() && !afterPrefixInsert.isEmpty()) {
83
0
        int32_t overlap = segment.getCommonPrefixLength(afterPrefixInsert);
84
0
        if (overlap == afterPrefixInsert.length()) {
85
0
            segment.adjustOffset(overlap);
86
0
            // Note: let currency spacing be a weak match. Don't update chars consumed.
87
0
        }
88
0
        maybeMore = maybeMore || overlap == segment.length();
89
0
    }
90
0
91
0
    return maybeMore;
92
0
}
93
94
bool CombinedCurrencyMatcher::matchCurrency(StringSegment& segment, ParsedNumber& result,
95
0
                                            UErrorCode& status) const {
96
0
    bool maybeMore = false;
97
0
98
0
    int32_t overlap1;
99
0
    if (!fCurrency1.isEmpty()) {
100
0
        overlap1 = segment.getCaseSensitivePrefixLength(fCurrency1);
101
0
    } else {
102
0
        overlap1 = -1;
103
0
    }
104
0
    maybeMore = maybeMore || overlap1 == segment.length();
105
0
    if (overlap1 == fCurrency1.length()) {
106
0
        utils::copyCurrencyCode(result.currencyCode, fCurrencyCode);
107
0
        segment.adjustOffset(overlap1);
108
0
        result.setCharsConsumed(segment);
109
0
        return maybeMore;
110
0
    }
111
0
112
0
    int32_t overlap2;
113
0
    if (!fCurrency2.isEmpty()) {
114
0
        overlap2 = segment.getCaseSensitivePrefixLength(fCurrency2);
115
0
    } else {
116
0
        overlap2 = -1;
117
0
    }
118
0
    maybeMore = maybeMore || overlap2 == segment.length();
119
0
    if (overlap2 == fCurrency2.length()) {
120
0
        utils::copyCurrencyCode(result.currencyCode, fCurrencyCode);
121
0
        segment.adjustOffset(overlap2);
122
0
        result.setCharsConsumed(segment);
123
0
        return maybeMore;
124
0
    }
125
0
126
0
    if (fUseFullCurrencyData) {
127
0
        // Use the full currency data.
128
0
        // NOTE: This call site should be improved with #13584.
129
0
        const UnicodeString segmentString = segment.toTempUnicodeString();
130
0
131
0
        // Try to parse the currency
132
0
        ParsePosition ppos(0);
133
0
        int32_t partialMatchLen = 0;
134
0
        uprv_parseCurrency(
135
0
                fLocaleName.data(),
136
0
                segmentString,
137
0
                ppos,
138
0
                UCURR_SYMBOL_NAME, // checks for both UCURR_SYMBOL_NAME and UCURR_LONG_NAME
139
0
                &partialMatchLen,
140
0
                result.currencyCode,
141
0
                status);
142
0
        maybeMore = maybeMore || partialMatchLen == segment.length();
143
0
144
0
        if (U_SUCCESS(status) && ppos.getIndex() != 0) {
145
0
            // Complete match.
146
0
            // NOTE: The currency code should already be saved in the ParsedNumber.
147
0
            segment.adjustOffset(ppos.getIndex());
148
0
            result.setCharsConsumed(segment);
149
0
            return maybeMore;
150
0
        }
151
0
152
0
    } else {
153
0
        // Use the locale long names.
154
0
        int32_t longestFullMatch = 0;
155
0
        for (int32_t i=0; i<StandardPlural::COUNT; i++) {
156
0
            const UnicodeString& name = fLocalLongNames[i];
157
0
            int32_t overlap = segment.getCommonPrefixLength(name);
158
0
            if (overlap == name.length() && name.length() > longestFullMatch) {
159
0
                longestFullMatch = name.length();
160
0
            }
161
0
            maybeMore = maybeMore || overlap > 0;
162
0
        }
163
0
        if (longestFullMatch > 0) {
164
0
            utils::copyCurrencyCode(result.currencyCode, fCurrencyCode);
165
0
            segment.adjustOffset(longestFullMatch);
166
0
            result.setCharsConsumed(segment);
167
0
            return maybeMore;
168
0
        }
169
0
    }
170
0
171
0
    // No match found.
172
0
    return maybeMore;
173
0
}
174
175
0
bool CombinedCurrencyMatcher::smokeTest(const StringSegment&) const {
176
0
    // TODO: See constructor
177
0
    return true;
178
0
    //return segment.startsWith(fLeadCodePoints);
179
0
}
180
181
0
UnicodeString CombinedCurrencyMatcher::toString() const {
182
0
    return u"<CombinedCurrencyMatcher>";
183
0
}
184
185
186
#endif /* #if !UCONFIG_NO_FORMATTING */