Chromium Code Reviews| OLD | NEW |
|---|---|
| 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 } |
| OLD | NEW |