Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(35)

Side by Side Diff: chrome/android/java/src/org/chromium/chrome/browser/payments/CurrencyStringFormatter.java

Issue 2629883004: [Payment Request] Update the CurrencyStringFormatter to call the native impl. (Closed)
Patch Set: Created 3 years, 11 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
1 // Copyright 2016 The Chromium Authors. All rights reserved. 1 // Copyright 2016 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 package org.chromium.chrome.browser.payments; 5 package org.chromium.chrome.browser.payments;
6 6
7 import java.text.DecimalFormatSymbols; 7 import org.chromium.base.annotations.JNINamespace;
8 import java.util.Currency; 8
9 import java.util.Locale; 9 import java.util.Locale;
10 import java.util.regex.Matcher;
11 import java.util.regex.Pattern; 10 import java.util.regex.Pattern;
12 11
13 /** 12 /**
14 * Formatter for currency strings that can be too large to parse into numbers. 13 * Formatter for currency amounts.
15 * https://w3c.github.io/browser-payment-api/specs/paymentrequest.html#currencya mount 14 * https://w3c.github.io/browser-payment-api/specs/paymentrequest.html#currencya mount
16 */ 15 */
16 @JNINamespace("payments")
17 public class CurrencyStringFormatter { 17 public class CurrencyStringFormatter {
18 // Amount value pattern and capture group numbers. 18 // Amount value pattern.
19 private static final String AMOUNT_VALUE_PATTERN = "^(-?)([0-9]+)(\\.([0-9]+ ))?$"; 19 private static final String AMOUNT_VALUE_PATTERN = "^(-?)([0-9]+)(\\.([0-9]+ ))?$";
20 private static final int OPTIONAL_NEGATIVE_GROUP = 1;
21 private static final int DIGITS_BETWEEN_NEGATIVE_AND_PERIOD_GROUP = 2;
22 private static final int DIGITS_AFTER_PERIOD_GROUP = 4;
23 20
24 // Max currency code length. Maximum length of currency code can be at most 2048. 21 // Max currency code length. Maximum length of currency code can be at most 2048.
25 private static final int MAX_CURRENCY_CODE_LEN = 2048; 22 private static final int MAX_CURRENCY_CODE_LEN = 2048;
26 23
27 // Currency code exceeding 6 chars will be ellipsized during formatting for display. 24 // Currency code exceeding 6 chars will be ellipsized during formatting for display.
28 private static final int MAX_CURRENCY_CHARS = 6; 25 private static final int MAX_CURRENCY_CHARS = 6;
29 26
30 // Unicode character for ellipsis. 27 // Unicode character for ellipsis.
31 private static final String ELLIPSIS = "\u2026"; 28 private static final String ELLIPSIS = "\u2026";
32 29
33 // Formatting constants.
34 private static final int DIGIT_GROUPING_SIZE = 3;
35
36 private final Pattern mAmountValuePattern;
37
38 /** 30 /**
39 * The currency formatted for display. Currency can be any string of at most 31 * The currency formatted for display. Currency can be any string of at most
40 * 2048 characters.Currency code more than 6 character is formatted to first 32 * 2048 characters.Currency code more than 6 character is formatted to first
41 * 5 characters and ellipsis. 33 * 5 characters and ellipsis.
42 */ 34 */
43 public final String mFormattedCurrencyCode; 35 public final String mFormattedCurrencyCode;
44 36
45 /** 37 /**
46 * The symbol for the currency specified on the bill. For example, the symbo l for "USD" is "$". 38 * Pointer to the native implementation.
47 */ 39 */
48 private final String mCurrencySymbol; 40 private final long mCurrencyFormatterAndroid;
49 41
50 /** 42 private final Pattern mAmountValuePattern;
please use gerrit instead 2017/01/13 21:16:25 No longer used in this file, so can be deleted.
Mathieu 2017/01/13 22:27:01 Done.
51 * The number of digits after the decimal separator for the currency specifi ed on the bill. For
52 * example, 2 for "USD" and 0 for "JPY".
53 */
54 private final int mDefaultFractionDigits;
55
56 /**
57 * The number grouping separator for the current locale. For example, "," in US. 3-digit groups
58 * are assumed.
59 */
60 private final char mGroupingSeparator;
61
62 /**
63 * The monetary decimal separator for the current locale. For example, "." i n US and "," in
64 * France.
65 */
66 private final char mMonetaryDecimalSeparator;
67 43
68 /** 44 /**
69 * Builds the formatter for the given currency code and the current user loc ale. 45 * Builds the formatter for the given currency code and the current user loc ale.
70 * 46 *
71 * @param currencyCode The currency code. Most commonly, this follows ISO 42 17 format: 3 upper 47 * @param currencyCode The currency code. Most commonly, this follows ISO 4 217 format: 3 upper
72 * case ASCII letters. For example, "USD". Format is not restricted. Should 48 * case ASCII letters. For example, "USD". Format is no t restricted. Should
73 * not be null. 49 * not be null.
50 * @param currencySystem URI specifying the ISO4217 currency code specificat ion. See for
51 * details: https://w3c.github.io/browser-payment-api/#paymentcurre ncyamount-dictionary
52 * Can be null, in which case "urn:iso:std:iso:4217" is assumed.
74 * @param userLocale User's current locale. Should not be null. 53 * @param userLocale User's current locale. Should not be null.
75 */ 54 */
76 public CurrencyStringFormatter(String currencyCode, Locale userLocale) { 55 public CurrencyStringFormatter(String currencyCode, String currencySystem, L ocale userLocale) {
please use gerrit instead 2017/01/13 21:16:24 @Nullable String currencySystem.
Mathieu 2017/01/13 22:27:01 Done.
77 assert currencyCode != null : "currencyCode should not be null"; 56 assert currencyCode != null : "currencyCode should not be null";
78 assert userLocale != null : "userLocale should not be null"; 57 assert userLocale != null : "userLocale should not be null";
79 58
59 // Note that this technically leaks the native object.
please use gerrit instead 2017/01/13 21:16:25 Is there a remedy for the leak?
Mathieu 2017/01/13 22:27:01 added destroy() (Calling nativeDestroy() which cal
60 mCurrencyFormatterAndroid = nativeInitCurrencyFormatterAndroid(
61 currencyCode, currencySystem == null ? "" : currencySystem, user Locale.toString());
62
80 mAmountValuePattern = Pattern.compile(AMOUNT_VALUE_PATTERN); 63 mAmountValuePattern = Pattern.compile(AMOUNT_VALUE_PATTERN);
81
82 mFormattedCurrencyCode = currencyCode.length() <= MAX_CURRENCY_CHARS 64 mFormattedCurrencyCode = currencyCode.length() <= MAX_CURRENCY_CHARS
please use gerrit instead 2017/01/13 21:16:25 Can we similarly format the currency code in C++?
Mathieu 2017/01/13 22:27:01 Sure thing, done
83 ? currencyCode 65 ? currencyCode
84 : currencyCode.substring(0, MAX_CURRENCY_CHARS - 1) + ELLIPSIS; 66 : currencyCode.substring(0, MAX_CURRENCY_CHARS - 1) + ELLIPSIS;
85
86 String currencySymbol;
87 int defaultFractionDigits;
88 try {
89 Currency currency = Currency.getInstance(currencyCode);
90 currencySymbol = currency.getSymbol();
91 defaultFractionDigits = currency.getDefaultFractionDigits();
92 } catch (IllegalArgumentException e) {
93 // The spec does not limit the currencies to official ISO 4217 curre ncy code list, which
94 // is used by java.util.Currency. For example, "BTX" (bitcoin) is no t an official ISO
95 // 4217 currency code, but is allowed by the spec.
96 currencySymbol = "";
97 defaultFractionDigits = 0;
98 }
99
100 // If the prefix of the currency symbol matches the prefix of the curren cy code, remove the
101 // matching prefix from the symbol. The UI already shows the currency co de, so there's no
102 // need to show duplicate information.
103 String symbol = "";
104 for (int i = 0; i < currencySymbol.length(); i++) {
105 if (i >= currencyCode.length() || currencySymbol.charAt(i) != curren cyCode.charAt(i)) {
106 symbol = currencySymbol.substring(i);
107 break;
108 }
109 }
110 mCurrencySymbol = symbol;
111
112 mDefaultFractionDigits = defaultFractionDigits;
113
114 // Use the symbols from user's current locale. For example, use "," for decimal separator in
115 // France, even if paying in "USD".
116 DecimalFormatSymbols symbols = new DecimalFormatSymbols(userLocale);
117 mGroupingSeparator = symbols.getGroupingSeparator();
118 mMonetaryDecimalSeparator = symbols.getMonetaryDecimalSeparator();
119 } 67 }
120 68
121 /** 69 /**
122 * Returns true if the amount value string is in valid format. 70 * Returns true if the amount value string is in valid format.
123 * 71 *
124 * @param amountValue The number to check for validity. 72 * @param amountValue The number to check for validity.
125 * @return Whether the number is in valid format. 73 * @return Whether the number is in valid format.
126 */ 74 */
127 public boolean isValidAmountValue(String amountValue) { 75 public boolean isValidAmountValue(String amountValue) {
please use gerrit instead 2017/01/13 21:16:25 No longer used in production code. Can be deleted.
Mathieu 2017/01/13 22:27:01 Done.
128 return amountValue != null && mAmountValuePattern.matcher(amountValue).m atches(); 76 return amountValue != null && mAmountValuePattern.matcher(amountValue).m atches();
129 } 77 }
130 78
131 /** 79 /**
132 * Returns true if the currency code string is in valid format. 80 * Returns true if the currency code string is in valid format.
133 * 81 *
134 * @param amountCurrencyCode The currency code to check for validity. 82 * @param amountCurrencyCode The currency code to check for validity.
135 * @return Whether the currency code is in valid format. 83 * @return Whether the currency code is in valid format.
136 */ 84 */
137 public boolean isValidAmountCurrencyCode(String amountCurrencyCode) { 85 public boolean isValidAmountCurrencyCode(String amountCurrencyCode) {
138 return amountCurrencyCode != null && amountCurrencyCode.length() <= MAX_ CURRENCY_CODE_LEN; 86 return amountCurrencyCode != null && amountCurrencyCode.length() <= MAX_ CURRENCY_CODE_LEN;
139 } 87 }
140 88
141 /** @return The currency code formatted for display. */ 89 /** @return The currency code formatted for display. */
142 public String getFormattedCurrencyCode() { 90 public String getFormattedCurrencyCode() {
143 return mFormattedCurrencyCode; 91 return mFormattedCurrencyCode;
144 } 92 }
145 93
146 /** 94 /**
147 * Formats the currency string for display. Does not parse the string into a number, because it 95 * Formats the currency string for display. Does not parse the string into a number, because it
148 * might be too large. The number is formatted for the current locale and fo llows the symbol of 96 * might be too large. The number is formatted for the current locale and ca n include a
149 * the currency code. 97 * currency symbol (e.g. $) anywhere in the string, but will not contain the currency code
98 * (e.g. USD/US). All spaces in the currency are unicode non-breaking space.
150 * 99 *
151 * @param amountValue The number to format. Should be in "^-?[0-9]+(\.[0-9]+ )?$" format. Should 100 * @param amountValue The number to format. Should be in "^-?[0-9]+(\.[0-9]+ )?$" format. Should
152 * not be null. 101 * not be null.
153 * @return The currency symbol followed by a space and the formatted number. 102 * @return The amount formatted with the specified currency. See description for details.
154 */ 103 */
155 public String format(String amountValue) { 104 public String format(String amountValue) {
156 assert amountValue != null : "amountValue should not be null"; 105 assert amountValue != null : "amountValue should not be null";
157 106
158 Matcher m = mAmountValuePattern.matcher(amountValue); 107 return nativeFormat(mCurrencyFormatterAndroid, amountValue);
108 }
159 109
160 // Required to capture the groups. 110 private native long nativeInitCurrencyFormatterAndroid(
161 boolean matches = m.matches(); 111 String currencyCode, String currencySystem, String localeName);
162 assert matches; 112 private native String nativeFormat(
163 113 long nativeCurrencyFormatterAndroid, String amountValue);
164 StringBuilder result = new StringBuilder(m.group(OPTIONAL_NEGATIVE_GROUP ));
165 result.append(mCurrencySymbol);
166 int digitStart = result.length();
167
168 result.append(m.group(DIGITS_BETWEEN_NEGATIVE_AND_PERIOD_GROUP));
169 for (int i = result.length() - DIGIT_GROUPING_SIZE; i > digitStart;
170 i -= DIGIT_GROUPING_SIZE) {
171 result.insert(i, mGroupingSeparator);
172 }
173
174 String decimals = m.group(DIGITS_AFTER_PERIOD_GROUP);
175 int numberOfDecimals = decimals == null ? 0 : decimals.length();
176
177 if (numberOfDecimals > 0 || mDefaultFractionDigits > 0) {
178 result.append(mMonetaryDecimalSeparator);
179 if (null != decimals) result.append(decimals);
180
181 for (int i = numberOfDecimals; i < mDefaultFractionDigits; i++) {
182 result.append("0");
183 }
184 }
185
186 return result.toString();
187 }
188 } 114 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698