001    /*
002     *  Copyright 2009-2013 Stephen Colebourne
003     *
004     *  Licensed under the Apache License, Version 2.0 (the "License");
005     *  you may not use this file except in compliance with the License.
006     *  You may obtain a copy of 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,
012     *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013     *  See the License for the specific language governing permissions and
014     *  limitations under the License.
015     */
016    package org.joda.money.format;
017    
018    import java.math.BigDecimal;
019    import java.text.ParsePosition;
020    import java.util.Locale;
021    
022    import org.joda.money.BigMoney;
023    import org.joda.money.CurrencyUnit;
024    
025    /**
026     * Context used when parsing money.
027     * <p>
028     * This class is mutable and intended for use by a single thread.
029     * A new instance is created for each parse.
030     */
031    public final class MoneyParseContext {
032    
033        /**
034         * The locale to parse using.
035         */
036        private Locale locale;
037        /**
038         * The text to parse.
039         */
040        private CharSequence text;
041        /**
042         * The text index.
043         */
044        private int textIndex;
045        /**
046         * The text error index.
047         */
048        private int textErrorIndex = -1;
049        /**
050         * The parsed currency.
051         */
052        private CurrencyUnit currency;
053        /**
054         * The parsed amount.
055         */
056        private BigDecimal amount;
057    
058        /**
059         * Constructor.
060         * 
061         * @param locale  the locale, not null
062         * @param text  the text to parse, not null
063         * @param index  the current text index
064         */
065        MoneyParseContext(Locale locale, CharSequence text, int index) {
066            this.locale = locale;
067            this.text = text;
068            this.textIndex = index;
069        }
070    
071        //-----------------------------------------------------------------------
072        /**
073         * Gets the locale.
074         * 
075         * @return the locale, not null
076         */
077        public Locale getLocale() {
078            return locale;
079        }
080    
081        /**
082         * Sets the locale.
083         * 
084         * @param locale  the locale, not null
085         */
086        public void setLocale(Locale locale) {
087            MoneyFormatter.checkNotNull(locale, "Locale must not be null");
088            this.locale = locale;
089        }
090    
091        /**
092         * Gets the text being parsed.
093         * 
094         * @return the text being parsed, never null
095         */
096        public CharSequence getText() {
097            return text;
098        }
099    
100        /**
101         * Sets the text.
102         * 
103         * @param text  the text being parsed, not null
104         */
105        public void setText(CharSequence text) {
106            MoneyFormatter.checkNotNull(text, "Text must not be null");
107            this.text = text;
108        }
109    
110        /**
111         * Gets the length of the text being parsed.
112         * 
113         * @return the length of the text being parsed
114         */
115        public int getTextLength() {
116            return text.length();
117        }
118    
119        /**
120         * Gets a substring of the text being parsed.
121         * 
122         * @param start  the start index
123         * @param end  the end index
124         * @return the substring, not null
125         */
126        public String getTextSubstring(int start, int end) {
127            return text.subSequence(start, end).toString();
128        }
129    
130        //-----------------------------------------------------------------------
131        /**
132         * Gets the current parse position index.
133         * 
134         * @return the current parse position index
135         */
136        public int getIndex() {
137            return textIndex;
138        }
139    
140        /**
141         * Sets the current parse position index.
142         * 
143         * @param index  the current parse position index
144         */
145        public void setIndex(int index) {
146            this.textIndex = index;
147        }
148    
149        //-----------------------------------------------------------------------
150        /**
151         * Gets the error index.
152         * 
153         * @return the error index, negative if no error
154         */
155        public int getErrorIndex() {
156            return textErrorIndex;
157        }
158    
159        /**
160         * Sets the error index.
161         * 
162         * @param index  the error index
163         */
164        public void setErrorIndex(int index) {
165            this.textErrorIndex = index;
166        }
167    
168        /**
169         * Sets the error index from the current index.
170         */
171        public void setError() {
172            this.textErrorIndex = textIndex;
173        }
174    
175        //-----------------------------------------------------------------------
176        /**
177         * Gets the parsed currency.
178         * 
179         * @return the parsed currency, null if not parsed yet
180         */
181        public CurrencyUnit getCurrency() {
182            return currency;
183        }
184    
185        /**
186         * Sets the parsed currency.
187         * 
188         * @param currency  the parsed currency, may be null
189         */
190        public void setCurrency(CurrencyUnit currency) {
191            this.currency = currency;
192        }
193    
194        //-----------------------------------------------------------------------
195        /**
196         * Gets the parsed amount.
197         * 
198         * @return the parsed amount, null if not parsed yet
199         */
200        public BigDecimal getAmount() {
201            return amount;
202        }
203    
204        /**
205         * Sets the parsed currency.
206         * 
207         * @param amount  the parsed amount, may be null
208         */
209        public void setAmount(BigDecimal amount) {
210            this.amount = amount;
211        }
212    
213        //-----------------------------------------------------------------------
214        /**
215         * Checks if the parse has found an error.
216         * 
217         * @return whether a parse error has occurred
218         */
219        public boolean isError() {
220            return textErrorIndex >= 0;
221        }
222    
223        /**
224         * Checks if the text has been fully parsed such that there is no more text to parse.
225         * 
226         * @return true if fully parsed
227         */
228        public boolean isFullyParsed() {
229            return textIndex == getTextLength();
230        }
231    
232        /**
233         * Checks if the context contains a currency and amount suitable for creating
234         * a monetary value.
235         * 
236         * @return true if able to create a monetary value
237         */
238        public boolean isComplete() {
239            return currency != null && amount != null;
240        }
241    
242        //-----------------------------------------------------------------------
243        /**
244         * Converts the indexes to a parse position.
245         * 
246         * @return the parse position, never null
247         */
248        public ParsePosition toParsePosition() {
249            ParsePosition pp = new ParsePosition(textIndex);
250            pp.setErrorIndex(textErrorIndex);
251            return pp;
252        }
253    
254        /**
255         * Converts the context to a {@code BigMoney}.
256         * 
257         * @return the monetary value, never null
258         * @throws MoneyFormatException if either the currency or amount is missing
259         */
260        public BigMoney toBigMoney() {
261            if (currency == null) {
262                throw new MoneyFormatException("Cannot convert to BigMoney as no currency found");
263            }
264            if (amount == null) {
265                throw new MoneyFormatException("Cannot convert to BigMoney as no amount found");
266            }
267            return BigMoney.of(currency, amount);
268        }
269    
270    }