avatar

945

Provide a default persistence delegate for currency objects, so they can be serialized and sent through the queues. by mrs, 06 Jun, 2009 05:53 AM
Diff this changeset:
Currency.java
cyberfox 1   package com.jbidwatcher.util;
cyberfox 2   /*
cyberfox 3    * Copyright (c) 2000-2007, CyberFOX Software, Inc. All Rights Reserved.
cyberfox 4    *
cyberfox 5    * Developed by mrs (Morgan Schweers)
cyberfox 6    */
cyberfox 7   
mrs      8   import com.jbidwatcher.util.config.JConfig;
mrs      9   
cyberfox 10  import java.text.NumberFormat;
cyberfox 11  import java.util.Locale;
mrs      12  import java.util.Map;
mrs      13  import java.util.HashMap;
mrs      14  import java.beans.PersistenceDelegate;
mrs      15  import java.beans.DefaultPersistenceDelegate;
cyberfox 16  
cyberfox 17  public class Currency implements Comparable {
cyberfox 18    private static NumberFormat df = NumberFormat.getNumberInstance(Locale.US); // We create a lot of these, so minimizing memory usage is good.
cyberfox 19    public static final int NONE=0, US_DOLLAR=1, UK_POUND=2, JP_YEN=3, GER_MARK=4, FR_FRANC=5, CAN_DOLLAR=6;
mrs      20    public static final int EURO=7, AU_DOLLAR=8, CH_FRANC=9, NT_DOLLAR=10, TW_DOLLAR=10, HK_DOLLAR=11;
mrs      21    public static final int MY_REAL=12, SG_DOLLAR=13, IND_RUPEE=14;
cyberfox 22    private static Currency _noValue = null;
cyberfox 23  
cyberfox 24    /** 
cyberfox 25     * @brief This provides a concept of a currency value that is
cyberfox 26     * invalid, not just 'zero' in some arbitrary currency.
cyberfox 27     * 
cyberfox 28     * @return A single, consistent, 'Empty Value', which indicates an
cyberfox 29     * invalid currency.
cyberfox 30     */
cyberfox 31    public static Currency NoValue() {
cyberfox 32      if(_noValue == null) _noValue = new Currency(NONE, 0.0);
cyberfox 33  
cyberfox 34      return _noValue;
cyberfox 35    }
cyberfox 36  
mrs      37    protected int mCurrencyType;
mrs      38    protected double mValue;
cyberfox 39    private static final char pound = '\u00A3';
mrs      40    private static final Character objPound = '\u00A3';
cyberfox 41  
mrs      42    private static Map<Integer,Double> sCurrencyMap = new HashMap<Integer,Double>();
mrs      43  
mrs      44    /**
mrs      45     * Convert a non-US currency to USD, usually for sorting purposes.
mrs      46     *
mrs      47     * Takes two values (usd, non-usd) which are believed to be
mrs      48     * equivalent, and a currency amount to convert based off the
mrs      49     * ratio between the first two.  If the USD amount is null or $0,
mrs      50     * it looks in a table it keeps around for converting.  If that fails,
mrs      51     * it just returns the non-USD's value.
mrs      52     *
mrs      53     * @param usd - A sample US dollar amount.
mrs      54     * @param nonusd - A non-US dollar amount that is equivalent to the usd paramter.
mrs      55     * @param cvt - The non-USD amount to convert to USD.
mrs      56     *
mrs      57     * @return - 'cvt' converted by the ratio of usd:nonusd, or by an internal table if
mrs      58     * it couldn't figure out the ratio, or just the non-usd's amount as a USD amount if
mrs      59     * there wasn't even an entry in the table.
mrs      60     */
cyberfox 61    public static Currency convertToUSD(Currency usd, Currency nonusd, Currency cvt) {
cyberfox 62      if(cvt != null && !cvt.isNull() && cvt.getCurrencyType() != US_DOLLAR) {
mrs      63        double multiple;
mrs      64        if(usd == null || usd.isNull() || usd.getValue() == 0.0 ||
mrs      65            nonusd == null || nonusd.isNull() || nonusd.getValue() == 0.0) {
mrs      66          if(sCurrencyMap.containsKey(cvt.getCurrencyType())) {
mrs      67            multiple = sCurrencyMap.get(cvt.getCurrencyType());
mrs      68          } else {
mrs      69            //  If we have nothing else to go on, treat it as exactly equal to USD.
mrs      70            multiple = 1.0;
mrs      71          }
mrs      72        } else {
mrs      73          multiple = usd.getValue() / nonusd.getValue();
mrs      74          if(multiple != 0.0) sCurrencyMap.put(nonusd.getCurrencyType(), multiple);
mrs      75        }
cyberfox 76        return getCurrency(US_DOLLAR, multiple*cvt.getValue());
cyberfox 77      }
cyberfox 78  
cyberfox 79      return cvt;
cyberfox 80    }
cyberfox 81  
cyberfox 82    /*!@class CurrencyTypeException
cyberfox 83     *
cyberfox 84     * @brief A class to yell about currency type comparison exceptions.
cyberfox 85     *
cyberfox 86     * This is used when comparing two currencies of disparate monies.
cyberfox 87     */
mrs      88    public static class CurrencyTypeException extends Exception {
cyberfox 89      String _associatedString;
cyberfox 90  
cyberfox 91      public CurrencyTypeException(String inString) {
cyberfox 92        _associatedString = inString;
cyberfox 93      }
cyberfox 94      public String toString() {
cyberfox 95        return _associatedString;
cyberfox 96      }
cyberfox 97    }
cyberfox 98  
mrs      99    private static final Integer CurDollar = US_DOLLAR;  //  American Dollar
mrs      100   private static final Integer CurPound = UK_POUND;    //  British Pound
mrs      101   private static final Integer CurYen = JP_YEN;        //  Japanese Yen
mrs      102   private static final Integer CurMark = GER_MARK;     //  German Mark
mrs      103   private static final Integer CurFranc = FR_FRANC;    //  French Franc
mrs      104   private static final Integer CurSwiss = CH_FRANC;    //  Swiss Franc
mrs      105   private static final Integer CurCan = CAN_DOLLAR;    //  Canadian Dollar
mrs      106   private static final Integer CurEuro = EURO;         //  Euro
mrs      107   private static final Integer CurAu = AU_DOLLAR;      //  Australian Dollar
mrs      108   private static final Integer CurTaiwan = NT_DOLLAR;  //  New Taiwanese Dollar
mrs      109   private static final Integer CurHK = HK_DOLLAR;      //  Hong Kong Dollar
mrs      110   private static final Integer CurMyr = MY_REAL;       //  Malaysia Real(?)
mrs      111   private static final Integer CurSGD = SG_DOLLAR;     //  Singapore Dollar
mrs      112   private static final Integer CurRupee = IND_RUPEE;   //  Indian Rupee
cyberfox 113 
cyberfox 114   //  The fundamental list of the textual representation for different
cyberfox 115   //  currencies, and the Currency type it translates to.
cyberfox 116   private static final Object xlateTable[][] = {
cyberfox 117     { "USD",    CurDollar },
cyberfox 118     { "US $",   CurDollar },
cyberfox 119     { "AU $",   CurAu },
cyberfox 120     { "au$",    CurAu },
cyberfox 121     { "AU",     CurAu },
cyberfox 122     { "AUD",    CurAu },
cyberfox 123     { "US",     CurDollar },
cyberfox 124     { "USD $",  CurDollar },
cyberfox 125     { "$",      CurDollar },
cyberfox 126     { "C",      CurCan },
cyberfox 127     { "C $",    CurCan },
cyberfox 128     { "CAD",    CurCan },
cyberfox 129     { "c$",     CurCan },
cyberfox 130     { "GBP",    CurPound },
cyberfox 131     { objPound.toString(), CurPound },
cyberfox 132     { "pound", CurPound },
cyberfox 133     { "\u00A3", CurPound },
cyberfox 134     { "&pound", CurPound },
cyberfox 135     { "Y",      CurYen },
cyberfox 136     { "JPY",    CurYen },
cyberfox 137     { "&yen",   CurYen },
cyberfox 138     { "\u00A5", CurYen },
cyberfox 139     { "DM",     CurMark },
cyberfox 140     { "FRF",    CurFranc },
cyberfox 141     { "fr",     CurFranc },
cyberfox 142     { "CHF",    CurSwiss },
cyberfox 143     { "chf",    CurSwiss },
cyberfox 144     { "dm",     CurMark },
cyberfox 145     { "\u20AC", CurEuro },
cyberfox 146     { "eur",    CurEuro },
cyberfox 147     { "EUR",    CurEuro },
cyberfox 148     { "Eur",    CurEuro },
cyberfox 149     { "NT$",    CurTaiwan },
cyberfox 150     { "nt$",    CurTaiwan },
cyberfox 151     { "NTD",    CurTaiwan },
cyberfox 152     { "HK$",    CurHK },
cyberfox 153     { "hk$",    CurHK },
mrs      154     { "HKD",    CurHK },
mrs      155     { "MYR",    CurMyr },
mrs      156     { "myr",    CurMyr },
mrs      157     { "SGD",    CurSGD },
mrs      158     { "sgd",    CurSGD },
mrs      159     { "INR",    CurRupee },
mrs      160     { "inr",    CurRupee }
cyberfox 161   };
cyberfox 162 
cyberfox 163   /** 
cyberfox 164    * @brief Convert from a string containing a recognized symbol into
cyberfox 165    * a currency type.
cyberfox 166    * 
cyberfox 167    * @param symbol - The string representation of a currency.
cyberfox 168    * 
cyberfox 169    * @return - The integer value associated with the provided
cyberfox 170    * currency, or NONE for unrecognized currencies.
cyberfox 171    */
cyberfox 172   private int xlateSymbolToType(String symbol) {
mrs      173     for (Object[] aXlateTable : xlateTable) {
mrs      174       if (symbol.equals(aXlateTable[0])) {
mrs      175         return (Integer) aXlateTable[1];
cyberfox 176       }
cyberfox 177     }
cyberfox 178 
cyberfox 179     return NONE;
cyberfox 180   }
cyberfox 181 
cyberfox 182   private boolean isDigit(char ch) {
cyberfox 183     return(ch>='0' && ch<='9');
cyberfox 184   }
cyberfox 185 
cyberfox 186   public static Currency getCurrency(String wholeValue) {
mrs      187     if(wholeValue == null || wholeValue.length() == 0 || wholeValue.startsWith("UNK")) return NoValue();
cyberfox 188 
cyberfox 189     return new Currency(wholeValue);
cyberfox 190   }
cyberfox 191 
cyberfox 192   public static Currency getCurrency(int whatType, double startValue) {
cyberfox 193     if(whatType == NONE) return NoValue();
cyberfox 194 
cyberfox 195     return new Currency(whatType, startValue);
cyberfox 196   }
cyberfox 197 
cyberfox 198   public static Currency getCurrency(String symbol, double startValue) {
cyberfox 199     if(symbol == null || symbol.equalsIgnoreCase("UNK")) return NoValue();
cyberfox 200 
cyberfox 201     return new Currency(symbol, startValue);
cyberfox 202   }
cyberfox 203 
cyberfox 204   public static Currency getCurrency(String symbol, String startValue) {
cyberfox 205     if(symbol == null || symbol.equalsIgnoreCase("UNK")) return NoValue();
cyberfox 206 
cyberfox 207     return new Currency(symbol, startValue);
cyberfox 208   }
cyberfox 209 
cyberfox 210   public Currency(String wholeValue) {
cyberfox 211     setValues(wholeValue);
cyberfox 212   }
cyberfox 213 
cyberfox 214   public Currency(int whatType, double startValue) {
cyberfox 215     setValues(whatType, startValue);
cyberfox 216   }
cyberfox 217 
cyberfox 218   public Currency(String symbol, double startValue) {
cyberfox 219     setValues(symbol, startValue);
cyberfox 220   }
cyberfox 221 
cyberfox 222   public Currency(String symbol, String startValue) {
cyberfox 223     setValues(symbol, Double.parseDouble(startValue));
cyberfox 224   }
cyberfox 225 
cyberfox 226   private int checkLengthMatchStart(String value, String currencyName) {
cyberfox 227     String lowVal = value.toLowerCase();
cyberfox 228     String curNam = currencyName.toLowerCase();
cyberfox 229     if(lowVal.startsWith(curNam + " ")) {
cyberfox 230       return currencyName.length()+1;
cyberfox 231     }
cyberfox 232     if(lowVal.startsWith(curNam)) {
cyberfox 233       int len = currencyName.length();
cyberfox 234       while(len < value.length() && !Character.isDigit(value.charAt(len))) len++;
cyberfox 235       return len;
cyberfox 236     }
cyberfox 237 
cyberfox 238     return 0;
cyberfox 239   }
cyberfox 240 
cyberfox 241   /** 
cyberfox 242    * @brief Provided an entire string containing a currency prefix and
cyberfox 243    * an amount, extract the two and set this object's value to equal
cyberfox 244    * the result.
cyberfox 245    *
cyberfox 246    * Is there a reason this doesn't use xlateSymbolToType?
cyberfox 247    * BUGBUG -- mrs: 03-January-2003 01:28
cyberfox 248    * 
cyberfox 249    * @param wholeValue - The string containing an entire currency+amount text.
cyberfox 250    */
cyberfox 251   private void setValues(String wholeValue) {
cyberfox 252     if(wholeValue == null || wholeValue.equals("null")) {
cyberfox 253       setValues(Currency.NONE, 0.0);
cyberfox 254     } else {
mrs      255       char firstChar = wholeValue.charAt(0);
cyberfox 256 
mrs      257       int eurLen = checkLengthMatchStart(wholeValue, "EUR");
mrs      258       int gbpLen = checkLengthMatchStart(wholeValue, "GBP");
mrs      259       int frfLen = checkLengthMatchStart(wholeValue, "FRF");
mrs      260       int chfLen = checkLengthMatchStart(wholeValue, "CHF");
mrs      261       int cdnLen = checkLengthMatchStart(wholeValue, "CAD");
mrs      262       int ntdLen = checkLengthMatchStart(wholeValue, "NTD");
mrs      263       int audLen = checkLengthMatchStart(wholeValue, "AUD");
mrs      264       int usdLen = checkLengthMatchStart(wholeValue, "USD");
cyberfox 265 
mrs      266       String parseCurrency;
mrs      267       String valuePortion;
cyberfox 268       if(wholeValue.startsWith("US $")) {
cyberfox 269         parseCurrency = "US $";
cyberfox 270         valuePortion = wholeValue.substring(4);
cyberfox 271       } else if(wholeValue.startsWith("USD $")) {
cyberfox 272         //  In case eBay ever corrects to the RIGHT currency code for USD.
cyberfox 273         parseCurrency = "USD $";
cyberfox 274         valuePortion = wholeValue.substring(5);
cyberfox 275       } else if(wholeValue.startsWith("AU $")) {
cyberfox 276         parseCurrency = "AU $";
cyberfox 277         valuePortion = wholeValue.substring(4);
cyberfox 278       } else if(usdLen != 0) {
cyberfox 279         parseCurrency = "USD";
cyberfox 280         valuePortion = wholeValue.substring(usdLen);
mrs      281       } else if(eurLen != 0) {
cyberfox 282         parseCurrency = "EUR";
mrs      283         valuePortion = wholeValue.substring(eurLen);
cyberfox 284       } else if(gbpLen != 0) {
cyberfox 285         parseCurrency = "GBP";
cyberfox 286         valuePortion = wholeValue.substring(gbpLen);
cyberfox 287       } else if(frfLen != 0) {
cyberfox 288         parseCurrency = "FRF";
cyberfox 289         valuePortion = wholeValue.substring(frfLen);
cyberfox 290       } else if(chfLen != 0) {
cyberfox 291         parseCurrency = "CHF";
cyberfox 292         valuePortion = wholeValue.substring(chfLen);
cyberfox 293       } else if(cdnLen != 0) {
cyberfox 294         parseCurrency = "CAD";
cyberfox 295         valuePortion = wholeValue.substring(cdnLen);
cyberfox 296       } else if(ntdLen != 0) {
cyberfox 297         parseCurrency = "NTD";
cyberfox 298         valuePortion = wholeValue.substring(ntdLen);
cyberfox 299       } else if(audLen != 0) {
cyberfox 300         parseCurrency = "AUD";
cyberfox 301         valuePortion = wholeValue.substring(audLen);
cyberfox 302       } else if(wholeValue.startsWith("NT$")) {
cyberfox 303         parseCurrency = "NTD";
cyberfox 304         valuePortion = wholeValue.substring(3);
mrs      305       } else if(wholeValue.startsWith("SGD")) {
mrs      306         parseCurrency = "SGD";
mrs      307         valuePortion = wholeValue.substring(3);
mrs      308       } else if(wholeValue.startsWith("sgd")) {
mrs      309         parseCurrency = "SGD";
mrs      310         valuePortion = wholeValue.substring(3);
mrs      311       } else if(wholeValue.startsWith("INR")) {
mrs      312         parseCurrency = "INR";
mrs      313         valuePortion = wholeValue.substring(3);
mrs      314       } else if(wholeValue.startsWith("inr")) {
mrs      315         parseCurrency = "INR";
mrs      316         valuePortion = wholeValue.substring(3);
cyberfox 317       } else if(wholeValue.startsWith("nt$")) {
cyberfox 318         parseCurrency = "NTD";
cyberfox 319         valuePortion = wholeValue.substring(3);
cyberfox 320       } else if(wholeValue.startsWith("au$")) {
cyberfox 321         parseCurrency = "AUD";
cyberfox 322         valuePortion = wholeValue.substring(3);
cyberfox 323       } else if(wholeValue.startsWith("C $")) {
cyberfox 324         parseCurrency = "C $";
cyberfox 325         valuePortion = wholeValue.substring(3);
cyberfox 326       } else if(wholeValue.charAt(0) == pound) {
cyberfox 327         parseCurrency = "GBP";
cyberfox 328         valuePortion = wholeValue.substring(1);
cyberfox 329       } else {
cyberfox 330         if(!isDigit(firstChar) && firstChar != '$') {
cyberfox 331           int semiIndex = wholeValue.indexOf(";");
cyberfox 332           if(semiIndex == -1) {
cyberfox 333             semiIndex = wholeValue.indexOf(" ");
cyberfox 334           }
cyberfox 335           if(semiIndex != -1) {
cyberfox 336             parseCurrency = wholeValue.substring(0, semiIndex);
cyberfox 337             valuePortion = wholeValue.substring(parseCurrency.length()+1);
cyberfox 338           } else {
cyberfox 339             parseCurrency = "$";
cyberfox 340             valuePortion = wholeValue;
cyberfox 341           }
cyberfox 342         } else {
cyberfox 343           parseCurrency = "$";
cyberfox 344           if(isDigit(firstChar)) {
cyberfox 345             valuePortion = wholeValue;
cyberfox 346           } else {
cyberfox 347             valuePortion = wholeValue.substring(1);
cyberfox 348           }
cyberfox 349         }
cyberfox 350       }
cyberfox 351 
cyberfox 352       //  Kill off non-digit characters.
mrs      353       while(valuePortion.length() != 0 && !Character.isDigit(valuePortion.charAt(0))) valuePortion = valuePortion.substring(1);
cyberfox 354 
cyberfox 355       //  If anything's left, try and parse it.
mrs      356       if(valuePortion.length() != 0) {
mrs      357         double actualValue;
cyberfox 358         try {
mrs      359           String cvt = valuePortion;
mrs      360           //  Convert [###.###.]###,## to [###,###,]###.##
mrs      361           if(cvt.length() > 2 && cvt.charAt(cvt.length()-3) == ',') {
mrs      362             cvt = cvt.substring(0, cvt.length()-3).replaceAll("\\.",",") + '.' + cvt.substring(cvt.length()-2);
mrs      363 //            System.out.println("Converting '" + cvt + "': " + df.parse(cvt).doubleValue());
mrs      364           }
mrs      365           actualValue = df.parse(cvt).doubleValue();
cyberfox 366         } catch(java.text.ParseException e) {
mrs      367           JConfig.log().handleException("currency parse!", e);
cyberfox 368           actualValue = 0.0;
cyberfox 369         }
cyberfox 370 
cyberfox 371         setValues(parseCurrency, actualValue);
cyberfox 372       } else {
cyberfox 373         setValues(null);
cyberfox 374       }
cyberfox 375     }
cyberfox 376   }
cyberfox 377 
cyberfox 378   /** 
cyberfox 379    * @brief If it's set as two seperate entries, then we use the MUCH
cyberfox 380    * cleaner xlateSymbolToType function.
cyberfox 381    *
cyberfox 382    * This should be the basic method that setValues works also.
cyberfox 383    * 
cyberfox 384    * @param symbol - The string form of a currency symbol.
cyberfox 385    * @param startValue - The amount associated with the currency.
cyberfox 386    */
cyberfox 387   private void setValues(String symbol, double startValue) {
cyberfox 388     setValues(xlateSymbolToType(symbol), startValue);
cyberfox 389   }
cyberfox 390 
cyberfox 391   /** 
cyberfox 392    * @brief The underlying setter that assigns the currency and amounts.
cyberfox 393    * 
cyberfox 394    * @param whatType - The Currency type to set to.
cyberfox 395    * @param startValue - The amount represented.
cyberfox 396    */
cyberfox 397   private void setValues(int whatType, double startValue) {
mrs      398     mCurrencyType = whatType;
mrs      399     mValue = startValue;
cyberfox 400     df.setMinimumFractionDigits(2);
cyberfox 401     df.setMaximumFractionDigits(2);
cyberfox 402   }
cyberfox 403 
cyberfox 404   /** 
cyberfox 405    * @brief Get the full, storable textual name for the currency type
cyberfox 406    * of this object.
cyberfox 407    * 
cyberfox 408    * @return A string containing a full ISO currency name.
cyberfox 409    */
cyberfox 410   public String fullCurrencyName() {
mrs      411     switch(mCurrencyType) {
cyberfox 412       case US_DOLLAR: return("USD");
cyberfox 413       case AU_DOLLAR: return("AUD");
cyberfox 414       case NT_DOLLAR: return("NTD");
cyberfox 415       case HK_DOLLAR: return("HKD");
mrs      416       case MY_REAL: return("MYR");
mrs      417       case SG_DOLLAR: return("SGD");
mrs      418       case IND_RUPEE: return("INR");
cyberfox 419       case UK_POUND: return("GBP");
cyberfox 420       case JP_YEN: return("JPY");
cyberfox 421       case GER_MARK: return("DM");
cyberfox 422       case FR_FRANC: return("FRF");
cyberfox 423       case CH_FRANC: return("CHF");
cyberfox 424       case CAN_DOLLAR: return("CAD");
cyberfox 425       case EURO: return("EUR");
cyberfox 426       default: return("UNK");
cyberfox 427     }
cyberfox 428   }
cyberfox 429 
mrs      430   public double getValue() { return mValue; }
cyberfox 431 
cyberfox 432   public String fullCurrency() {
cyberfox 433     return fullCurrencyName() + " " + getValueString();
cyberfox 434   }
cyberfox 435 
cyberfox 436   /**
cyberfox 437    * @brief Add two currencies and return a new currency containing
cyberfox 438    * the result of the two added together.
cyberfox 439    * 
cyberfox 440    * @param addValue - The currency value/amount to add.  It must be
cyberfox 441    * of the same currency type as 'this'.
cyberfox 442    * 
cyberfox 443    * @return A new currency object containing the sum of the two
cyberfox 444    *         amounts provided, with the same currency type as them.
cyberfox 445    *
cyberfox 446    * @throws CurrencyTypeException if the two objects are of different currencies.
cyberfox 447    */
cyberfox 448   public Currency add(Currency addValue) throws CurrencyTypeException {
cyberfox 449     if(addValue == null) throw new CurrencyTypeException("Cannot add null Currency.");
cyberfox 450 
mrs      451     if(addValue.getCurrencyType() == mCurrencyType) {
mrs      452       return new Currency(mCurrencyType, mValue + addValue.getValue());
cyberfox 453     }
cyberfox 454 
mrs      455     //  If only one currency is known, return the result as the known currency.
mrs      456     if (mCurrencyType == NONE) return new Currency(addValue.getCurrencyType(), mValue + addValue.getValue());
mrs      457     if (addValue.getCurrencyType() == NONE) return new Currency(mCurrencyType, mValue + addValue.getValue());
mrs      458 
cyberfox 459     throw new CurrencyTypeException("Cannot add " + fullCurrencyName() + " to " + addValue.fullCurrencyName() + ".");
cyberfox 460   }
cyberfox 461 
cyberfox 462   /**
cyberfox 463    * @brief Subtract two currencies and return a new currency containing
cyberfox 464    * the result of the passed value subtracted from this objects value.
cyberfox 465    *
cyberfox 466    * @param subValue - The currency value/amount to subtract.  It must be
cyberfox 467    * of the same currency type as 'this'.
cyberfox 468    *
cyberfox 469    * @return A new currency object containing the difference of the two
cyberfox 470    *         amounts provided, with the same currency type as them.
cyberfox 471    *
cyberfox 472    * @throws CurrencyTypeException if the two objects are of different currencies.
cyberfox 473    */
cyberfox 474   public Currency subtract(Currency subValue) throws CurrencyTypeException {
mrs      475     if(subValue == null) throw new CurrencyTypeException("Cannot subtract null Currency.");
cyberfox 476 
mrs      477     if(subValue.getCurrencyType() == mCurrencyType) {
mrs      478       return new Currency(mCurrencyType, mValue - subValue.getValue());
cyberfox 479     }
cyberfox 480 
mrs      481     //  If only one currency is known, return the result as the known currency.
mrs      482     if(mCurrencyType == NONE) return new Currency(subValue.getCurrencyType(), mValue - subValue.getValue());
mrs      483     if(subValue.getCurrencyType() == NONE) return new Currency(mCurrencyType, mValue - subValue.getValue());
mrs      484 
mrs      485     throw new CurrencyTypeException("Cannot subtract " + fullCurrencyName() + " from " + subValue.fullCurrencyName() + ".");
cyberfox 486   }
cyberfox 487 
mrs      488   public int getCurrencyType() { return mCurrencyType; }
cyberfox 489 
cyberfox 490   public String getCurrencySymbol() {
mrs      491     switch(mCurrencyType) {
cyberfox 492       case US_DOLLAR: return("$");
cyberfox 493       case NT_DOLLAR: return("nt$");
cyberfox 494       case HK_DOLLAR: return("hk$");
mrs      495       case MY_REAL: return("myr");
mrs      496       case SG_DOLLAR: return("sgd");
mrs      497       case IND_RUPEE: return("Rs.");
cyberfox 498       case UK_POUND: return(objPound.toString());
cyberfox 499       case JP_YEN: return("\u00A5"); //  HACKHACK
cyberfox 500       case FR_FRANC: return("fr");
cyberfox 501       case CH_FRANC: return("chf");
cyberfox 502       case GER_MARK: return("dm");
cyberfox 503       case CAN_DOLLAR: return("c$");
cyberfox 504       case AU_DOLLAR: return("au$");
cyberfox 505       case EURO: return("\u20AC");
cyberfox 506       default: return("unk");
cyberfox 507     }
cyberfox 508   }
cyberfox 509 
cyberfox 510   /** 
cyberfox 511    * @brief Format the currency and amount as appropriate for the
cyberfox 512    * current locale.
cyberfox 513    *
cyberfox 514    * This is kind of interesting, because it will display in one
cyberfox 515    * fashion, but when it snipes or bids, it's all against the
cyberfox 516    * US sites, so it's all operating in US forms at that point.
cyberfox 517    * 
cyberfox 518    * @return A nicely formatted, locale-correct money value, prefixed
cyberfox 519    * with the best currency symbol for the currency type.
cyberfox 520    */
cyberfox 521   public String toString() {
cyberfox 522     if(isNull()) {
cyberfox 523       return("null");
cyberfox 524     } else {
cyberfox 525       String cvtToString = getCurrencySymbol();
cyberfox 526 
mrs      527       cvtToString += df.format(mValue);
cyberfox 528 
cyberfox 529       return(cvtToString);
cyberfox 530     }
cyberfox 531   }
cyberfox 532 
cyberfox 533   /** 
cyberfox 534    * @brief Format the amount as appropriate for the current locale.
cyberfox 535    *
cyberfox 536    * This is kind of interesting, because it will display in one
cyberfox 537    * fashion, but when it snipes or bids, it's all against the
cyberfox 538    * US sites, so it's all operating in US forms at that point.
cyberfox 539    * 
cyberfox 540    * @return A nicely formatted, locale-correct money value, prefixed
cyberfox 541    * with the best currency symbol for the currency type.
cyberfox 542    */
cyberfox 543   public String getValueString() {
cyberfox 544     if(isNull()) {
cyberfox 545       return("null");
cyberfox 546     } else {
mrs      547       return df.format(mValue);
cyberfox 548     }
cyberfox 549   }
cyberfox 550 
cyberfox 551   /** 
cyberfox 552    * @brief Implementing equals means I should implement hashCode().
cyberfox 553    * 
cyberfox 554    * @return - The hash code of the string consisting of the full
cyberfox 555    * currency named followed by the value as a string.  Null/invalid
cyberfox 556    * currency entries return 0.
cyberfox 557    */
cyberfox 558   public int hashCode() {
cyberfox 559     if(isNull()) return 0;
cyberfox 560 
cyberfox 561     String tmp = fullCurrencyName() + getValueString();
cyberfox 562     return tmp.hashCode();
cyberfox 563   }
cyberfox 564 
cyberfox 565   /** 
cyberfox 566    * @brief Must be able to compare currency values for equality.
cyberfox 567    * 
cyberfox 568    * @param inValue - The value to compare against.
cyberfox 569    * 
cyberfox 570    * @return True if the two values are the same, or the currency and
cyberfox 571    * amount are the same.  False otherwise, including false if it is
cyberfox 572    * an entirely different class.  Differing currencies are always
cyberfox 573    * unequal.
cyberfox 574    */
cyberfox 575   public boolean equals(Object inValue) {
cyberfox 576     //  Be careful not to compare with null.
cyberfox 577     if(inValue == null) return false;
cyberfox 578     //  Shortcut for this.equals(this)
cyberfox 579     if(inValue == this) return true;
cyberfox 580     //  Is it this class even?
cyberfox 581     if(!(inValue instanceof Currency)) return false;
cyberfox 582     //  Okay, now cast it because it's safe.
mrs      583     Currency otherValue = (Currency) inValue;
mrs      584     boolean sameCurrency = (otherValue.getCurrencyType() == mCurrencyType);
mrs      585     boolean sameValue = ((int) (otherValue.getValue() * 1000)) == ((int) (mValue * 1000));
cyberfox 586 
cyberfox 587     return(sameCurrency && sameValue);
cyberfox 588   }
cyberfox 589 
cyberfox 590   /** 
cyberfox 591    * @brief Determine if (this < otherValue).
cyberfox 592    *
cyberfox 593    * This only works for items of the same currency type.
cyberfox 594    * 
cyberfox 595    * @param otherValue - The value to compare against.
cyberfox 596    * 
cyberfox 597    * @return - True if this amount is less than the otherValue amount
cyberfox 598    * and both currency types are equal.  If the otherValue is null,
cyberfox 599    * the same object as this (this.less(this)), or this amount is
cyberfox 600    * actually less, then it returns false.
cyberfox 601    *
cyberfox 602    * @throws CurrencyTypeException if you try to compare different currencies.
cyberfox 603    */
cyberfox 604   public boolean less(Currency otherValue) throws CurrencyTypeException {
cyberfox 605     //  Be careful
cyberfox 606     if(otherValue == null) return false;
cyberfox 607     //  Shortcut
cyberfox 608     if(otherValue == this) return false;
cyberfox 609 
mrs      610     boolean sameCurrency = (otherValue.getCurrencyType() == mCurrencyType);
cyberfox 611     if(!sameCurrency) {
cyberfox 612       throw new CurrencyTypeException("Cannot compare different currencies.");
cyberfox 613     }
cyberfox 614 
mrs      615     boolean lowerValue = Double.compare((double) ((int) (otherValue.getValue() * 1000)), (double) (int) (mValue * 1000)) == 1;
cyberfox 616 
cyberfox 617     return(lowerValue);
cyberfox 618   }
cyberfox 619 
cyberfox 620   /** 
cyberfox 621    * @brief Utility function to check if this is a purely invalid currency.
cyberfox 622    *
cyberfox 623    * It should probably check against the invalid currency object first...
cyberfox 624    * 
cyberfox 625    * @return True if this is a 'null currency' object.
cyberfox 626    */
cyberfox 627   public boolean isNull() {
mrs      628     return(mValue == 0.0 && mCurrencyType == NONE);
cyberfox 629   }
cyberfox 630 
cyberfox 631   /** 
cyberfox 632    * @brief The comparable interface defines this, and so I'm
cyberfox 633    * comparing using the well defined set of rules for Comparables.
cyberfox 634    *
cyberfox 635    * Defined with 'equals' and less', but both should be special cases
cyberfox 636    * of this, since some checks are duplicated.
cyberfox 637    * 
cyberfox 638    * @param o - The object to compare against.
cyberfox 639    * 
cyberfox 640    * @return -1 if o's class is Currency, it's the same currency type,
cyberfox 641    *         and the amount of this is less than o's amount.
cyberfox 642    *          0 if o's class is Currency, it's the same currency type,
cyberfox 643    *         and the amount of this is the same as o's amount.
cyberfox 644    *          1 if o's class is Currency, it's the same currency type,
cyberfox 645    *         and the amount of this is greater than o's amount.
cyberfox 646    *
cyberfox 647    * @throws ClassCastException if you try to compareTo non-Currency classes.
cyberfox 648    */
cyberfox 649   public int compareTo(Object o) {
cyberfox 650     //  We are always greater than null
cyberfox 651     if(o == null) return 1;
cyberfox 652     //  We are always equal to ourselves
cyberfox 653     if(o == this) return 0;
cyberfox 654     //  This is an incorrect usage and should be caught.
cyberfox 655     if(!(o instanceof Currency)) throw new ClassCastException("Currency cannot compareTo different classes!");
cyberfox 656 
cyberfox 657     //  Okay, now cast it because it's safe.
mrs      658     Currency otherValue = (Currency) o;
cyberfox 659 
cyberfox 660     if(otherValue.isNull()) return 1;
cyberfox 661     if(isNull()) return -1;
cyberfox 662     try {
cyberfox 663       if(less(otherValue)) return -1;
cyberfox 664     } catch(ClassCastException e) {
cyberfox 665       /* This should be impossible */
cyberfox 666       throw new ClassCastException("Currency cannot compareTo different classes!\n" + e);
cyberfox 667     } catch (CurrencyTypeException e) {
cyberfox 668         //  Can't re-throw (or not catch!) because Object.compareTo doesn't throw CurrencyTypeException!
cyberfox 669         throw new ClassCastException("Currency cannot compareTo different currencies!\n" + e);
cyberfox 670     }
cyberfox 671     if(equals(otherValue)) return 0;
cyberfox 672     return 1;
cyberfox 673   }
mrs      674 
mrs      675   public static PersistenceDelegate getDelegate() {
mrs      676     return new DefaultPersistenceDelegate(new String[]{"mCurrencyType", "mValue"});
mrs      677   }
cyberfox 678 }

Check out the code: svn co jbidwatcher/trunk/src/com/jbidwatcher/util/Currency.java