Coverage Report - org.joda.money.format.MoneyFormatterBuilder
 
Classes in this File Line Coverage Branch Coverage Complexity
MoneyFormatterBuilder
100%
31/31
100%
4/4
1.5
MoneyFormatterBuilder$1
N/A
N/A
1.5
MoneyFormatterBuilder$SingletonPrinters
100%
5/5
N/A
1.5
MoneyFormatterBuilder$Singletons
100%
8/8
N/A
1.5
MoneyFormatterBuilder$Singletons$1
100%
13/13
100%
2/2
1.5
MoneyFormatterBuilder$Singletons$2
100%
13/13
100%
2/2
1.5
MoneyFormatterBuilder$Singletons$3
100%
16/16
100%
8/8
1.5
 
 1  
 /*
 2  
  *  Copyright 2009-2013 Stephen Colebourne
 3  
  *
 4  
  *  Licensed under the Apache License, Version 2.0 (the "License");
 5  
  *  you may not use this file except in compliance with the License.
 6  
  *  You may obtain a copy of the License at
 7  
  *
 8  
  *      http://www.apache.org/licenses/LICENSE-2.0
 9  
  *
 10  
  *  Unless required by applicable law or agreed to in writing, software
 11  
  *  distributed under the License is distributed on an "AS IS" BASIS,
 12  
  *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 13  
  *  See the License for the specific language governing permissions and
 14  
  *  limitations under the License.
 15  
  */
 16  
 package org.joda.money.format;
 17  
 
 18  
 import java.io.IOException;
 19  
 import java.util.ArrayList;
 20  
 import java.util.List;
 21  
 import java.util.Locale;
 22  
 
 23  
 import org.joda.money.BigMoney;
 24  
 import org.joda.money.CurrencyUnit;
 25  
 import org.joda.money.IllegalCurrencyException;
 26  
 
 27  
 /**
 28  
  * Provides the ability to build a formatter for monetary values.
 29  
  * <p>
 30  
  * This class is mutable and intended for use by a single thread.
 31  
  * A new instance should be created for each use.
 32  
  * The formatters produced by the builder are immutable and thread-safe.
 33  
  */
 34  
 public final class MoneyFormatterBuilder {
 35  
 
 36  
     /**
 37  
      * The printers.
 38  
      */
 39  332
     private final List<MoneyPrinter> printers = new ArrayList<MoneyPrinter>();
 40  
     /**
 41  
      * The parsers.
 42  
      */
 43  332
     private final List<MoneyParser> parsers = new ArrayList<MoneyParser>();
 44  
 
 45  
     //-----------------------------------------------------------------------
 46  
     /**
 47  
      * Constructor, creating a new empty builder.
 48  
      */
 49  332
     public MoneyFormatterBuilder() {
 50  332
     }
 51  
 
 52  
     //-----------------------------------------------------------------------
 53  
     /**
 54  
      * Appends the amount to the builder using a standard format.
 55  
      * <p>
 56  
      * The format used is {@link MoneyAmountStyle#ASCII_DECIMAL_POINT_GROUP3_COMMA}.
 57  
      * The amount is the value itself, such as '12.34'.
 58  
      * 
 59  
      * @return this, for chaining, never null
 60  
      */
 61  
     public MoneyFormatterBuilder appendAmount() {
 62  10
         AmountPrinterParser pp = new AmountPrinterParser(MoneyAmountStyle.ASCII_DECIMAL_POINT_GROUP3_COMMA);
 63  10
         return appendInternal(pp, pp);
 64  
     }
 65  
 
 66  
     /**
 67  
      * Appends the amount to the builder using a grouped localized format.
 68  
      * <p>
 69  
      * The format used is {@link MoneyAmountStyle#LOCALIZED_GROUPING}.
 70  
      * The amount is the value itself, such as '12.34'.
 71  
      * 
 72  
      * @return this, for chaining, never null
 73  
      */
 74  
     public MoneyFormatterBuilder appendAmountLocalized() {
 75  76
         AmountPrinterParser pp = new AmountPrinterParser(MoneyAmountStyle.LOCALIZED_GROUPING);
 76  76
         return appendInternal(pp, pp);
 77  
     }
 78  
 
 79  
     /**
 80  
      * Appends the amount to the builder using the specified amount style.
 81  
      * <p>
 82  
      * The amount is the value itself, such as '12.34'.
 83  
      * <p>
 84  
      * The amount style allows the formatting of the number to be controlled in detail.
 85  
      * See {@link MoneyAmountStyle} for more details.
 86  
      * 
 87  
      * @param style  the style to use, not null
 88  
      * @return this, for chaining, never null
 89  
      */
 90  
     public MoneyFormatterBuilder appendAmount(MoneyAmountStyle style) {
 91  20
         MoneyFormatter.checkNotNull(style, "MoneyAmountStyle must not be null");
 92  19
         AmountPrinterParser pp = new AmountPrinterParser(style);
 93  19
         return appendInternal(pp, pp);
 94  
     }
 95  
 
 96  
     //-----------------------------------------------------------------------
 97  
     /**
 98  
      * Appends the currency code to the builder.
 99  
      * <p>
 100  
      * The currency code is the three letter ISO code, such as 'GBP'.
 101  
      * 
 102  
      * @return this, for chaining, never null
 103  
      */
 104  
     public MoneyFormatterBuilder appendCurrencyCode() {
 105  137
         return appendInternal(Singletons.CODE, Singletons.CODE);
 106  
     }
 107  
 
 108  
     /**
 109  
      * Appends the currency code to the builder.
 110  
      * <p>
 111  
      * The numeric code is the ISO numeric code, such as '826' and is
 112  
      * zero padded to three digits.
 113  
      * 
 114  
      * @return this, for chaining, never null
 115  
      */
 116  
     public MoneyFormatterBuilder appendCurrencyNumeric3Code() {
 117  5
         return appendInternal(Singletons.NUMERIC_3_CODE, Singletons.NUMERIC_3_CODE);
 118  
     }
 119  
 
 120  
     /**
 121  
      * Appends the currency code to the builder.
 122  
      * <p>
 123  
      * The numeric code is the ISO numeric code, such as '826'.
 124  
      * 
 125  
      * @return this, for chaining, never null
 126  
      */
 127  
     public MoneyFormatterBuilder appendCurrencyNumericCode() {
 128  8
         return appendInternal(Singletons.NUMERIC_CODE, Singletons.NUMERIC_CODE);
 129  
     }
 130  
 
 131  
     /**
 132  
      * Appends the localized currency symbol to the builder.
 133  
      * <p>
 134  
      * The localized currency symbol is the symbol as chosen by the locale
 135  
      * of the formatter.
 136  
      * <p>
 137  
      * Symbols cannot be parsed.
 138  
      * 
 139  
      * @return this, for chaining, never null
 140  
      */
 141  
     public MoneyFormatterBuilder appendCurrencySymbolLocalized() {
 142  2
         return appendInternal(SingletonPrinters.LOCALIZED_SYMBOL, null);
 143  
     }
 144  
 
 145  
     /**
 146  
      * Appends a literal to the builder.
 147  
      * <p>
 148  
      * The localized currency symbol is the symbol as chosen by the locale
 149  
      * of the formatter.
 150  
      * 
 151  
      * @param literal  the literal to append, null or empty ignored
 152  
      * @return this, for chaining, never null
 153  
      */
 154  
     public MoneyFormatterBuilder appendLiteral(CharSequence literal) {
 155  139
         if (literal == null || literal.length() == 0) {
 156  2
             return this;
 157  
         }
 158  137
         LiteralPrinterParser pp = new LiteralPrinterParser(literal.toString());
 159  137
         return appendInternal(pp, pp);
 160  
     }
 161  
 
 162  
     //-----------------------------------------------------------------------
 163  
     /**
 164  
      * Appends the printers and parsers from the specified formatter to this builder.
 165  
      * <p>
 166  
      * If the specified formatter cannot print, then the the output of this
 167  
      * builder will be unable to print. If the specified formatter cannot parse,
 168  
      * then the output of this builder will be unable to parse.
 169  
      * 
 170  
      * @param formatter  the formatter to append, not null
 171  
      * @return this for chaining, never null
 172  
      */
 173  
     public MoneyFormatterBuilder append(MoneyFormatter formatter) {
 174  1
         MoneyFormatter.checkNotNull(formatter, "MoneyFormatter must not be null");
 175  1
         formatter.appendTo(this);
 176  1
         return this;
 177  
     }
 178  
 
 179  
     /**
 180  
      * Appends the specified printer and parser to this builder.
 181  
      * <p>
 182  
      * If null is specified then the formatter will be unable to print/parse.
 183  
      * 
 184  
      * @param printer  the printer to append, null makes the formatter unable to print
 185  
      * @param parser  the parser to append, null makes the formatter unable to parse
 186  
      * @return this for chaining, never null
 187  
      */
 188  
     public MoneyFormatterBuilder append(MoneyPrinter printer, MoneyParser parser) {
 189  133
         return appendInternal(printer, parser);
 190  
     }
 191  
 
 192  
     //-----------------------------------------------------------------------
 193  
     /**
 194  
      * Appends the specified printer and parser to this builder.
 195  
      * <p>
 196  
      * Either the printer or parser must be non-null.
 197  
      * 
 198  
      * @param printer  the printer to append, null makes the formatter unable to print
 199  
      * @param parser  the parser to append, null makes the formatter unable to parse
 200  
      * @return this for chaining, never null
 201  
      */
 202  
     private MoneyFormatterBuilder appendInternal(MoneyPrinter printer, MoneyParser parser) {
 203  527
         printers.add(printer);
 204  527
         parsers.add(parser);
 205  527
         return this;
 206  
     }
 207  
 
 208  
     //-----------------------------------------------------------------------
 209  
     /**
 210  
      * Builds the formatter from the builder using the default locale.
 211  
      * <p>
 212  
      * Once the builder is in the correct state it must be converted to a
 213  
      * {@code MoneyFormatter} to be used. Calling this method does not
 214  
      * change the state of this instance, so it can still be used.
 215  
      * <p>
 216  
      * This method uses the default locale within the returned formatter.
 217  
      * It can be changed by calling {@link MoneyFormatter#withLocale(Locale)}.
 218  
      * 
 219  
      * @return the formatter built from this builder, never null
 220  
      */
 221  
     public MoneyFormatter toFormatter() {
 222  319
         return toFormatter(Locale.getDefault());
 223  
     }
 224  
 
 225  
     /**
 226  
      * Builds the formatter from the builder setting the locale.
 227  
      * <p>
 228  
      * Once the builder is in the correct state it must be converted to a
 229  
      * {@code MoneyFormatter} to be used. Calling this method does not
 230  
      * change the state of this instance, so it can still be used.
 231  
      * <p>
 232  
      * This method uses the specified locale within the returned formatter.
 233  
      * It can be changed by calling {@link MoneyFormatter#withLocale(Locale)}.
 234  
      * 
 235  
      * @param locale  the initial locale for the formatter, not null
 236  
      * @return the formatter built from this builder, never null
 237  
      */
 238  
     @SuppressWarnings("cast")
 239  
     public MoneyFormatter toFormatter(Locale locale) {
 240  330
         MoneyFormatter.checkNotNull(locale, "Locale must not be null");
 241  330
         MoneyPrinter[] printersCopy = (MoneyPrinter[]) printers.toArray(new MoneyPrinter[printers.size()]);
 242  330
         MoneyParser[] parsersCopy = (MoneyParser[]) parsers.toArray(new MoneyParser[parsers.size()]);
 243  330
         return new MoneyFormatter(locale, printersCopy, parsersCopy);
 244  
     }
 245  
 
 246  
 //    /**
 247  
 //     * Returns a copy of this instance with the specified pattern.
 248  
 //     * <p>
 249  
 //     * The specified pattern is used for positive and zero amounts, while for
 250  
 //     * negative amounts it is prefixed by the negative sign.
 251  
 //     * <p>
 252  
 //     * A pattern is a simple way to define the characters which surround the numeric value.
 253  
 //     * For example, {@code ${amount} ${code}} will print the ISO code after the value,
 254  
 //     * producing an output like {@code 12.34 GBP}.
 255  
 //     * Similarly, {@code -${symbol}${amount}} will produce the output {@code -£12.34}.
 256  
 //     * <p>
 257  
 //     * The pattern contains the following elements:<br />
 258  
 //     * <ul>
 259  
 //     * <li>{@code ${amount}} : the monetary amount itself
 260  
 //     * <li>{@code ${code}} : the letter-based currency code, such as 'USD'
 261  
 //     * <li>{@code ${numericCode}} : the numeric currency code, such as '840'
 262  
 //     * <li>{@code ${symbol}} : the currency symbol, such as '$'
 263  
 //     * </ul>
 264  
 //     * 
 265  
 //     * @param pattern  the pattern
 266  
 //     * @return the new instance, never null
 267  
 //     */
 268  
 //    public MoneyFormatterBuilder withPattern(String pattern) {
 269  
 //        return withPattern(pattern, "-" + pattern, pattern);
 270  
 //    }
 271  
 //
 272  
 //    /**
 273  
 //     * Returns a copy of this instance with the specified positive and negative pattern.
 274  
 //     * <p>
 275  
 //     * The specified positive pattern is also used for zero amounts.
 276  
 //     * <p>
 277  
 //     * A pattern is a simple way to define the characters which surround the numeric value.
 278  
 //     * For example, {@code ${amount} ${code}} will print the ISO code after the value,
 279  
 //     * producing an output like {@code 12.34 GBP}.
 280  
 //     * Similarly, {@code -${symbol}${amount}} will produce the output {@code -£12.34}.
 281  
 //     * <p>
 282  
 //     * The pattern contains the following elements:<br />
 283  
 //     * <ul>
 284  
 //     * <li>{@code ${amount}} : the monetary amount itself
 285  
 //     * <li>{@code ${code}} : the letter-based currency code, such as 'USD'
 286  
 //     * <li>{@code ${numericCode}} : the numeric currency code, such as '840'
 287  
 //     * <li>{@code ${symbol}} : the currency symbol, such as '$'
 288  
 //     * </ul>
 289  
 //     * 
 290  
 //     * @param positivePattern  the positive pattern
 291  
 //     * @param negativePattern  the negative pattern
 292  
 //     * @return the new instance, never null
 293  
 //     */
 294  
 //    public MoneyFormatterBuilder withPattern(String positivePattern, String negativePattern) {
 295  
 //        return withPattern(positivePattern, negativePattern, positivePattern);
 296  
 //    }
 297  
 //
 298  
 //    /**
 299  
 //     * Returns a copy of this instance with the specified positive, negative and zero pattern.
 300  
 //     * <p>
 301  
 //     * The positive pattern is used for positive amounts, the negative pattern for negative amounts
 302  
 //     * and the zero pattern for zero amounts.
 303  
 //     * <p>
 304  
 //     * A pattern is a simple way to define the characters which surround the numeric value.
 305  
 //     * For example, {@code ${amount} ${code}} will print the ISO code after the value,
 306  
 //     * producing an output like {@code 12.34 GBP}.
 307  
 //     * Similarly, {@code -${symbol}${amount}} will produce the output {@code -£12.34}.
 308  
 //     * <p>
 309  
 //     * The pattern contains the following elements:<br />
 310  
 //     * <ul>
 311  
 //     * <li>{@code ${amount}} : the monetary amount itself
 312  
 //     * <li>{@code ${code}} : the letter-based currency code, such as 'USD'
 313  
 //     * <li>{@code ${numericCode}} : the numeric currency code, such as '840'
 314  
 //     * <li>{@code ${symbol}} : the currency symbol, such as '$'
 315  
 //     * </ul>
 316  
 //     * 
 317  
 //     * @param positivePattern  the positive pattern
 318  
 //     * @param negativePattern  the negative pattern
 319  
 //     * @param zeroPattern  the zero pattern
 320  
 //     * @return the new instance, never null
 321  
 //     */
 322  
 //    public MoneyFormatterBuilder withPattern(String positivePattern, String negativePattern, String zeroPattern) {
 323  
 //        MoneyUtils.checkNotNull(positivePattern, "Positive pattern must not be null");
 324  
 //        MoneyUtils.checkNotNull(negativePattern, "Negative pattern must not be null");
 325  
 //        MoneyUtils.checkNotNull(zeroPattern, "Zero pattern must not be null");
 326  
 //        return new MoneyFormatterBuilder(iLocale, iZeroCharacter, iDecimalPointCharacter, iGroupingCharacter,
 327  
 //                iGrouping, iGroupingSize, iForceDecimalPoint, positivePattern, negativePattern, zeroPattern);
 328  
 //    }
 329  
 
 330  
     //-----------------------------------------------------------------------
 331  
     /**
 332  
      * Handles the singleton outputs.
 333  
      */
 334  5
     private static enum Singletons implements MoneyPrinter, MoneyParser {
 335  1
         CODE("${code}") {
 336  
             public void print(MoneyPrintContext context, Appendable appendable, BigMoney money) throws IOException {
 337  12
                 appendable.append(money.getCurrencyUnit().getCode());
 338  8
             }
 339  
             public void parse(MoneyParseContext context) {
 340  28
                 int endPos = context.getIndex() + 3;
 341  28
                 if (endPos > context.getTextLength()) {
 342  3
                     context.setError();
 343  
                 } else {
 344  25
                     String code = context.getTextSubstring(context.getIndex(), endPos);
 345  
                     try {
 346  25
                         context.setCurrency(CurrencyUnit.of(code));
 347  22
                         context.setIndex(endPos);
 348  3
                     } catch (IllegalCurrencyException ex) {
 349  3
                         context.setError();
 350  22
                     }
 351  
                 }
 352  28
             }
 353  
         },
 354  1
         NUMERIC_3_CODE("${numeric3Code}") {
 355  
             public void print(MoneyPrintContext context, Appendable appendable, BigMoney money) throws IOException {
 356  1
                 appendable.append(money.getCurrencyUnit().getNumeric3Code());
 357  1
             }
 358  
             public void parse(MoneyParseContext context) {
 359  4
                 int endPos = context.getIndex() + 3;
 360  4
                 if (endPos > context.getTextLength()) {
 361  2
                     context.setError();
 362  
                 } else {
 363  2
                     String code = context.getTextSubstring(context.getIndex(), endPos);
 364  
                     try {
 365  2
                         context.setCurrency(CurrencyUnit.ofNumericCode(code));
 366  1
                         context.setIndex(endPos);
 367  1
                     } catch (IllegalCurrencyException ex) {
 368  1
                         context.setError();
 369  1
                     }
 370  
                 }
 371  4
             }
 372  
         },
 373  1
         NUMERIC_CODE("${numericCode}") {
 374  
             public void print(MoneyPrintContext context, Appendable appendable, BigMoney money) throws IOException {
 375  1
                 appendable.append(Integer.toString(money.getCurrencyUnit().getNumericCode()));
 376  1
             }
 377  
             public void parse(MoneyParseContext context) {
 378  7
                 int count = 0;
 379  31
                 for ( ; count < 3 && context.getIndex() + count < context.getTextLength(); count++) {
 380  14
                     char ch = context.getText().charAt(context.getIndex() + count);
 381  14
                     if (ch < '0' || ch > '9') {
 382  1
                         break;
 383  
                     }
 384  
                 }
 385  7
                 int endPos = context.getIndex() + count;
 386  7
                 String code = context.getTextSubstring(context.getIndex(), endPos);
 387  
                 try {
 388  7
                     context.setCurrency(CurrencyUnit.ofNumericCode(code));
 389  4
                     context.setIndex(endPos);
 390  3
                 } catch (IllegalCurrencyException ex) {
 391  3
                     context.setError();
 392  4
                 }
 393  7
             }
 394  
         };
 395  
         private final String toString;
 396  3
         private Singletons(String toString) {
 397  3
             this.toString = toString;
 398  3
         }
 399  
         @Override
 400  
         public String toString() {
 401  8
             return toString;
 402  
         }
 403  
     }
 404  
 
 405  
     //-----------------------------------------------------------------------
 406  
     /**
 407  
      * Handles the singleton outputs.
 408  
      */
 409  2
     private static enum SingletonPrinters implements MoneyPrinter {
 410  1
         LOCALIZED_SYMBOL;
 411  
         public void print(MoneyPrintContext context, Appendable appendable, BigMoney money) throws IOException {
 412  1
             appendable.append(money.getCurrencyUnit().getSymbol(context.getLocale()));
 413  1
         }
 414  
         @Override
 415  
         public String toString() {
 416  1
             return "${symbolLocalized}";
 417  
         }
 418  
     }
 419  
 
 420  
 }