task4/solution06/src/org/apidesign/apifest08/currency/Convertor.java
changeset 64 51f7b894eba6
parent 61 58ec6da75f6f
     1.1 --- a/task4/solution06/src/org/apidesign/apifest08/currency/Convertor.java	Sat Oct 11 23:38:46 2008 +0200
     1.2 +++ b/task4/solution06/src/org/apidesign/apifest08/currency/Convertor.java	Fri Oct 17 17:32:34 2008 +0200
     1.3 @@ -5,8 +5,12 @@
     1.4  import java.math.BigDecimal;
     1.5  import java.math.RoundingMode;
     1.6  import java.util.ArrayList;
     1.7 +import java.util.Calendar;
     1.8  import java.util.Currency;
     1.9 +import java.util.Date;
    1.10 +import java.util.GregorianCalendar;
    1.11  import java.util.List;
    1.12 +import java.util.TimeZone;
    1.13  
    1.14  /**
    1.15   * Currency covertor.
    1.16 @@ -27,7 +31,7 @@
    1.17  		notNull(currencyFirst, "currencyFirst");
    1.18  		notNull(currencySecond, "currencySecond");		
    1.19  		notNull(rateValue, "rateValue");	
    1.20 -		convertorDelegates.add(new ConvertorDelegate(new StaticRateProvider(rateValue), currencyFirst, currencySecond));
    1.21 +		convertorDelegates.add(new ConvertorDelegate(new StaticRateProvider(rateValue), currencyFirst, currencySecond, null, null));
    1.22  	}
    1.23  	
    1.24  	/**
    1.25 @@ -42,7 +46,23 @@
    1.26  		notNull(currencyFirst, "currencyFirst");
    1.27  		notNull(currencySecond, "currencySecond");		
    1.28  		notNull(rateProvider, "rateProvider");
    1.29 -		convertorDelegates.add(new ConvertorDelegate(rateProvider, currencyFirst, currencySecond));
    1.30 +		convertorDelegates.add(new ConvertorDelegate(rateProvider, currencyFirst, currencySecond, null, null));
    1.31 +	}
    1.32 +	
    1.33 +	/**
    1.34 +	 * Create new instance of the convertor. The associated rate(s) is timely limited
    1.35 +	 * by a given 'from' and 'till'. 
    1.36 +	 * @param convertor
    1.37 +	 * @param from
    1.38 +	 * @param till
    1.39 +	 */
    1.40 +	public Convertor(Convertor convertor, Date from, Date till) {
    1.41 +	    notNull(convertor, "convertor");
    1.42 +		notNull(from, "from");
    1.43 +		notNull(till, "till");				
    1.44 +		for(ConvertorDelegate delegate: convertor.convertorDelegates) {
    1.45 +			convertorDelegates.add(new ConvertorDelegate(delegate.rateProvider, delegate.first, delegate.second, from, till));
    1.46 +		}
    1.47  	}
    1.48  	
    1.49  	/**
    1.50 @@ -57,13 +77,16 @@
    1.51  		
    1.52  		for(Convertor convertor: convertors) {
    1.53  			if(convertor != null) {
    1.54 -				convertorDelegates.addAll(convertor.convertorDelegates);
    1.55 +				for(ConvertorDelegate delegate: convertor.convertorDelegates) {
    1.56 +					convertorDelegates.add(new ConvertorDelegate(delegate.rateProvider, delegate.first, delegate.second, delegate.from, delegate.till));
    1.57 +				}
    1.58  			}
    1.59  		}
    1.60  	}
    1.61      	
    1.62  	/**
    1.63 -	 * Converts an amount value between the two currencies of this converter.
    1.64 +	 * Converts an amount value between the two currencies of this converter. The rate is taken 
    1.65 +	 * for current time.
    1.66  	 *  
    1.67  	 * @param amount an amount
    1.68  	 * @param fromCurrency an amount currency
    1.69 @@ -74,42 +97,64 @@
    1.70  	 * @throws UnsupportedConversionException if the conversion between a given currencies is not supported.
    1.71  	 */
    1.72  	public Amount convert(BigDecimal amount, Currency fromCurrency, Currency toCurrency) throws ConversionException {
    1.73 +		return convert(amount, fromCurrency, toCurrency, new Date(System.currentTimeMillis()));
    1.74 +	}
    1.75 +
    1.76 +	/**
    1.77 +	 * Converts an amount value between the two currencies of this converter and its 
    1.78 +	 * associated rate for a given time.
    1.79 +	 *  
    1.80 +	 * @param amount an amount
    1.81 +	 * @param fromCurrency an amount currency
    1.82 +	 * @param toCurrency to a target currency
    1.83 +	 * @param time time
    1.84 +	 * @return a converted amount value
    1.85 +	 * 
    1.86 +	 * @throws ConversionException if the conversion fails
    1.87 +	 * @throws UnsupportedConversionException if the conversion between a given currencies and time is not supported.
    1.88 +	 */
    1.89 +	public Amount convert(BigDecimal amount, Currency fromCurrency, Currency toCurrency, Date time) throws ConversionException {
    1.90  		notNull(amount, "amount");
    1.91  		notNull(fromCurrency, "fromCurrency");
    1.92 -		notNull(toCurrency, "toCurrency");
    1.93 +		notNull(toCurrency, "toCurrency");		
    1.94 +		notNull(time, "time");
    1.95 +		
    1.96  		ConvertorDelegate appropriateDelegate = null;
    1.97 -		
    1.98  		//try find an appropriate delegate for conversion
    1.99  		for(ConvertorDelegate delegate : convertorDelegates) {
   1.100 -			if(delegate.isConversionSupported(fromCurrency, toCurrency)) {
   1.101 -				appropriateDelegate = delegate;
   1.102 -				break;
   1.103 +			if(delegate.isConversionSupported(fromCurrency, toCurrency) && delegate.isConversionSupported(time)) {				
   1.104 +				appropriateDelegate = delegate;				
   1.105  			}
   1.106  		}
   1.107 -		
   1.108  		if(appropriateDelegate == null) {
   1.109 -			throw new UnsupportedConversionException(fromCurrency, toCurrency);
   1.110 +			throw new UnsupportedConversionException(fromCurrency, toCurrency, time);
   1.111  		}
   1.112  		
   1.113 -		return appropriateDelegate.convert(amount, fromCurrency, toCurrency);	
   1.114 +		return appropriateDelegate.convert(amount, fromCurrency, toCurrency);
   1.115  	}
   1.116  	
   1.117  	/**
   1.118  	 * Internal delegate implements a logic for conversion between two currencies
   1.119 -	 * and vice versa.
   1.120 +	 * and vice versa. There could be time limitation for the associated rate.
   1.121 +	 * 
   1.122  	 * @see #isConversionSupported(Currency, Currency)
   1.123  	 */
   1.124  	private static class ConvertorDelegate {
   1.125 +		
   1.126  		private final Currency first;
   1.127  		private final Currency second;
   1.128  		private final RateProvider rateProvider;
   1.129 +		private final Date from;
   1.130 +		private final Date till;
   1.131  		public static final BigDecimal one = new BigDecimal(1);
   1.132  		
   1.133  		
   1.134 -		private ConvertorDelegate(RateProvider rateProvider, Currency currencyFirst, Currency currencySecond) {
   1.135 +		private ConvertorDelegate(RateProvider rateProvider, Currency currencyFirst, Currency currencySecond, Date from, Date till) {
   1.136  			this.rateProvider = rateProvider;
   1.137  			this.first = currencyFirst;
   1.138  			this.second = currencySecond;
   1.139 +			this.from = from;
   1.140 +			this.till = till;
   1.141  		}
   1.142  		
   1.143  		private Amount convert(BigDecimal amount, Currency fromCurrency, Currency toCurrency) throws ConversionException {
   1.144 @@ -147,6 +192,38 @@
   1.145  		private boolean isConversionSupported(Currency fromCurrency, Currency toCurrency) {
   1.146  			return ((fromCurrency == first || fromCurrency == second) && (toCurrency == first || toCurrency == second));
   1.147  		}
   1.148 +		
   1.149 +		/**
   1.150 +		 * @return <code>true</code> if the delegate is able to convert in a given
   1.151 +		 * time.
   1.152 +		 */
   1.153 +		private boolean isConversionSupported(Date time) {
   1.154 +			boolean retVal;
   1.155 +			if(this.from != null && this.till != null) {
   1.156 +				retVal = getDateInGMT(from).getTime() <= getDateInGMT(time).getTime() && getDateInGMT(till).getTime() >= getDateInGMT(time).getTime();  
   1.157 +		    } else {
   1.158 +				retVal = true; //delegate is applicable "for eternity"				
   1.159 +			}
   1.160 +			return retVal;
   1.161 +		}
   1.162 +		
   1.163 +		private Date getDateInGMT(Date currentDate) {
   1.164 +			TimeZone tz = TimeZone.getTimeZone("GMT");
   1.165 +			
   1.166 +			Calendar mbCal = new GregorianCalendar(tz);
   1.167 +			mbCal.setTimeInMillis(currentDate.getTime());
   1.168 +
   1.169 +			Calendar cal = Calendar.getInstance();
   1.170 +			cal.set(Calendar.YEAR, mbCal.get(Calendar.YEAR));
   1.171 +			cal.set(Calendar.MONTH, mbCal.get(Calendar.MONTH));
   1.172 +			cal.set(Calendar.DAY_OF_MONTH, mbCal.get(Calendar.DAY_OF_MONTH));
   1.173 +			cal.set(Calendar.HOUR_OF_DAY, mbCal.get(Calendar.HOUR_OF_DAY));
   1.174 +			cal.set(Calendar.MINUTE, mbCal.get(Calendar.MINUTE));
   1.175 +			cal.set(Calendar.SECOND, mbCal.get(Calendar.SECOND));
   1.176 +			cal.set(Calendar.MILLISECOND, mbCal.get(Calendar.MILLISECOND));
   1.177 +
   1.178 +			return cal.getTime();
   1.179 +		}
   1.180  	}
   1.181  	
   1.182  	/**