org.javamoney.moneta.internal.format.CurrencyToken Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of moneta-bp Show documentation
Show all versions of moneta-bp Show documentation
JSR 354 provides an API for representing, transporting, and performing comprehensive calculations with
Money and Currency.
This module implements JSR 354.
/*
* Copyright (c) 2012, 2014, Credit Suisse (Anatole Tresch), Werner Keil and others by the @author tag.
*
* Licensed 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.
*/
package org.javamoney.moneta.internal.format;
import org.javamoney.moneta.format.CurrencyStyle;
import javax.money.CurrencyUnit;
import javax.money.Monetary;
import javax.money.MonetaryAmount;
import javax.money.MonetaryException;
import javax.money.format.MonetaryParseException;
import java.io.IOException;
import java.util.Currency;
import java.util.Locale;
import java.util.Objects;
import java.util.ResourceBundle;
/**
* Implements a {@link FormatToken} that adds a localizable {@link String}, read
* by key from a {@link ResourceBundle}.
*
* @author Anatole Tresch
*/
final class CurrencyToken implements FormatToken {
/**
* The style defining, how the currency should be localized.
*/
private CurrencyStyle style = CurrencyStyle.CODE;
/**
* The target locale.
*/
private final Locale locale;
/**
* Creates a new {@link CurrencyToken}.
*
* @param style The style defining, how the currency should be localized, not
* {@code null}.
* @param locale The target locale, not {@code null}.
*/
public CurrencyToken(CurrencyStyle style, Locale locale) {
Objects.requireNonNull(locale, "Locale null");
this.locale = locale;
if (style!=null) {
this.style = style;
}
}
/**
* Explicitly configure the {@link CurrencyStyle} to be used.
*
* @param style the {@link CurrencyStyle}, not {@code null}.
* @return this token instance, for chaining.
*/
public CurrencyToken setCurrencyStyle(CurrencyStyle style) {
Objects.requireNonNull(style, "CurrencyStyle null");
this.style = style;
return this;
}
/**
* Access the {@link CurrencyStyle} used for formatting.
*
* @return the current {@link CurrencyStyle}, never {@code null}.
*/
public CurrencyStyle getCurrencyStyle() {
return this.style;
}
/**
* Evaluate the formatted(localized) token.
*
* @param amount the {@link MonetaryAmount} containing the {@link CurrencyUnit}
* to be formatted.
* @return the formatted currency.
*/
protected String getToken(MonetaryAmount amount) {
switch (style) {
case NUMERIC_CODE:
return String.valueOf(amount.getCurrency()
.getNumericCode());
case NAME:
return getCurrencyName(amount.getCurrency());
case SYMBOL:
// Fix for https://github.com/JavaMoney/jsr354-ri/issues/151
if("BG".equals(locale.getCountry())){
return "лв";
}
return getCurrencySymbol(amount.getCurrency());
default:
case CODE:
return amount.getCurrency().getCurrencyCode();
}
}
/**
* This method tries to evaluate the localized display name for a
* {@link CurrencyUnit}. It uses {@link Currency#getDisplayName(Locale)} if
* the given currency code maps to a JDK {@link Currency} instance.
*
* If not found {@code currency.getCurrencyCode()} is returned.
*
* @param currency The currency, not {@code null}
* @return the formatted currency name.
*/
private String getCurrencyName(CurrencyUnit currency) {
Currency jdkCurrency = getCurrency(currency.getCurrencyCode());
if (jdkCurrency!=null) {
return jdkCurrency.getDisplayName(locale);
}
return currency.getCurrencyCode();
}
/**
* Method to safely access a {@link java.util.Currency}.
* If no such currency exists, null is returned.
*
* @param currencyCode the currency code , not null.
* @return the corresponding currency instance, or null.
*/
private Currency getCurrency(String currencyCode) {
try {
return Currency.getInstance(currencyCode);
} catch (Exception e) {
return null;
}
}
/**
* This method tries to evaluate the localized symbol name for a
* {@link CurrencyUnit}. It uses {@link Currency#getSymbol(Locale)} if the
* given currency code maps to a JDK {@link Currency} instance.
*
* If not found {@code currency.getCurrencyCode()} is returned.
*
* @param currency The currency, not {@code null}
* @return the formatted currency symbol.
*/
private String getCurrencySymbol(CurrencyUnit currency) {
Currency jdkCurrency = getCurrency(currency.getCurrencyCode());
if (jdkCurrency!=null) {
return jdkCurrency.getSymbol(locale);
}
return currency.getCurrencyCode();
}
/**
* Parses a currency from the given {@link ParseContext}. Depending on the
* current {@link CurrencyStyle} it interprets the next non empty token,
* either as
*
* - currency code
*
- currency symbol
*
* Parsing of localized currency names or numeric code is not supported.
*
* @throws UnsupportedOperationException if the {@link CurrencyStyle} is configured to us currency
* names, or numeric codes for formatting.
*/
@Override
public void parse(ParseContext context)
throws MonetaryParseException {
String token = context.lookupNextToken();
while (token!=null) {
if (!token.trim().isEmpty()) {
break;
}
context.consume(token);
token = context.lookupNextToken();
}
if(token==null){
throw new MonetaryException("Error parsing CurrencyUnit: token expected");
}
try {
CurrencyUnit cur;
switch (style) {
case CODE:
if (!Monetary.isCurrencyAvailable(token)) {
// Perhaps blank is missing between currency code and number...
String subCurrency = parseCurrencyCode(token);
cur = Monetary.getCurrency(subCurrency);
context.consume(subCurrency);
} else {
cur = Monetary.getCurrency(token);
context.consume(token);
}
break;
case SYMBOL:
if (token.startsWith("$")) {
throw new MonetaryParseException("$ is not a unique currency symbol.", token,
context.getErrorIndex());
} else if (token.startsWith("€")) {
cur = Monetary.getCurrency("EUR");
context.consume("€");
} else if (token.startsWith("£")) {
cur = Monetary.getCurrency("GBP");
context.consume("£");
} else {
cur = Monetary.getCurrency(token);
context.consume(token);
}
context.setParsedCurrency(cur);
break;
case NAME:
case NUMERIC_CODE:
default:
throw new UnsupportedOperationException("Not yet implemented");
}
if (cur!=null) {
context.setParsedCurrency(cur);
}
} catch (Exception e) {
throw new MonetaryException("Error parsing CurrencyUnit.", e);
}
}
/**
* Tries to split up a letter based first part, e.g. for evaluating a ISO currency code from an input as
* 'CHF100.34'.
* @param token the input token
* @return the first letter based part, or the full token.
*/
private String parseCurrencyCode(String token) {
int letterIndex = 0;
for (char ch : token.toCharArray()) {
if (Character.isLetter(ch)) {
letterIndex++;
} else {
return token.substring(0, letterIndex);
}
}
return token;
}
/**
* Prints the {@link CurrencyUnit} of the given {@link MonetaryAmount} to
* the given {@link Appendable}.
*
* @throws IOException may be thrown by the {@link Appendable}
*/
@Override
public void print(Appendable appendable, MonetaryAmount amount)
throws IOException {
appendable.append(getToken(amount));
}
/*
* (non-Javadoc)
*
* @see java.lang.Object#toString()
*/
@Override
public String toString() {
return "CurrencyToken [locale=" + locale + ", style=" + style + ']';
}
}