001/**
002 * Copyright (c) 2012, 2015, Credit Suisse (Anatole Tresch), Werner Keil and others by the @author tag.
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
005 * use this file except in compliance with the License. You may obtain a copy of
006 * the License at
007 *
008 * http://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
012 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
013 * License for the specific language governing permissions and limitations under
014 * the License.
015 */
016package org.javamoney.moneta;
017
018import java.io.IOException;
019import java.math.BigDecimal;
020import java.util.Objects;
021import java.util.Optional;
022
023import javax.money.CurrencyUnit;
024import javax.money.MonetaryAmount;
025import javax.money.Monetary;
026import javax.money.format.AmountFormatContext;
027import javax.money.format.AmountFormatContextBuilder;
028import javax.money.format.MonetaryAmountFormat;
029import javax.money.format.MonetaryParseException;
030
031/**
032 * class to format and parse a text string such as 'EUR 25.25' or vice versa.
033 * This class will used to toString and parse in all implementation on Moneta.
034 * {@link Money#toString()}
035 * {@link Money#parse(CharSequence)}
036 * {@link FastMoney#toString()}
037 * {@link FastMoney#parse(CharSequence)}
038 * {@link RoundedMoney#toString()}
039 * {@link RoundedMoney#parse(CharSequence)}
040 * @author Otavio Santana
041 */
042public final class ToStringMonetaryAmountFormat implements MonetaryAmountFormat {
043
044    private static final String CONTEXT_PREFIX = "ToString_";
045
046    private final ToStringMonetaryAmountFormatStyle style;
047
048    private final AmountFormatContext context;
049
050    private ToStringMonetaryAmountFormat(ToStringMonetaryAmountFormatStyle style) {
051        this.style = Objects.requireNonNull(style);
052        context = AmountFormatContextBuilder.of(CONTEXT_PREFIX + style).build();
053    }
054
055    public static ToStringMonetaryAmountFormat of(
056            ToStringMonetaryAmountFormatStyle style) {
057        return new ToStringMonetaryAmountFormat(style);
058    }
059
060    @Override
061    public String queryFrom(MonetaryAmount amount) {
062                return Optional.ofNullable(amount).map(MonetaryAmount::toString)
063                                .orElse("null");
064    }
065
066    @Override
067    public AmountFormatContext getContext() {
068        return context;
069    }
070
071    @Override
072    public void print(Appendable appendable, MonetaryAmount amount)
073            throws IOException {
074        appendable.append(queryFrom(amount));
075
076    }
077
078    @Override
079    public MonetaryAmount parse(CharSequence text)
080            throws MonetaryParseException {
081                try {
082                        ParserMonetaryAmount amount = parserMonetaryAmount(text);
083                        return style.to(amount);
084                } catch (Exception e) {
085                        throw new MonetaryParseException(e.getMessage(), text, 0);
086                }
087    }
088
089    private ParserMonetaryAmount parserMonetaryAmount(CharSequence text) throws Exception {
090        String[] array = Objects.requireNonNull(text).toString().split(" ");
091        if(array.length != 2) {
092                throw new MonetaryParseException("An error happened when try to parse the Monetary Amount.",text,0);
093        }
094        CurrencyUnit currencyUnit = Monetary.getCurrency(array[0]);
095        BigDecimal number = new BigDecimal(array[1]);
096        return new ParserMonetaryAmount(currencyUnit, number);
097    }
098
099    private class ParserMonetaryAmount {
100        ParserMonetaryAmount(CurrencyUnit currencyUnit, BigDecimal number) {
101            this.currencyUnit = currencyUnit;
102            this.number = number;
103        }
104
105        private final CurrencyUnit currencyUnit;
106        private final BigDecimal number;
107    }
108
109    /**
110     * indicates with implementation will used to format or parser in
111     * ToStringMonetaryAmountFormat
112     */
113    public enum ToStringMonetaryAmountFormatStyle {
114        /**
115         * {@link Money}
116         */
117        MONEY {
118            @Override
119            MonetaryAmount to(ParserMonetaryAmount amount) {
120                return Money.of(amount.number, amount.currencyUnit);
121            }
122        },
123        /**
124         * {@link FastMoney}
125         */
126        FAST_MONEY {
127            @Override
128            MonetaryAmount to(ParserMonetaryAmount amount) {
129                return FastMoney.of(amount.number, amount.currencyUnit);
130            }
131        },
132        /**
133         * {@link RoundedMoney}
134         */
135        ROUNDED_MONEY {
136            @Override
137            MonetaryAmount to(ParserMonetaryAmount amount) {
138                return RoundedMoney.of(amount.number, amount.currencyUnit);
139            }
140        };
141
142        private static final long serialVersionUID = 6606016328162974467L;
143        abstract MonetaryAmount to(ParserMonetaryAmount amount);
144    }
145
146}