1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
1.2 +++ b/task4/solution07/src/org/apidesign/apifest08/currency/TimeRangeSpecificConvertor.java Fri Oct 17 17:33:32 2008 +0200
1.3 @@ -0,0 +1,109 @@
1.4 +package org.apidesign.apifest08.currency;
1.5 +
1.6 +import java.util.Currency;
1.7 +import java.util.Date;
1.8 +import org.apidesign.apifest08.currency.Convertor.ConversionRequest;
1.9 +import org.apidesign.apifest08.currency.Convertor.ConversionResult;
1.10 +
1.11 +/**
1.12 + * This {@link Convertor} delegates to an underlying one, provided that the request specifies time between from (included) and till (excluded).
1.13 + * Otherwise it just refuses to convert.
1.14 + * @author jdvorak
1.15 + */
1.16 +public class TimeRangeSpecificConvertor extends DelegatingConvertor {
1.17 +
1.18 + private final Date from;
1.19 + private final Date till;
1.20 +
1.21 + /**
1.22 + * A new time range specific convertor.
1.23 + * @param old the underlying convertor one delegates to
1.24 + * @param from the beginning of the time interval
1.25 + * @param till the end of the time interval
1.26 + * @throws IllegalArgumentException unless from comes before till
1.27 + */
1.28 + public TimeRangeSpecificConvertor( final Convertor old, final Date from, final Date till ) {
1.29 + super( old );
1.30 + this.from = from;
1.31 + this.till = till;
1.32 + if (! from.before( till ) ) {
1.33 + throw new IllegalArgumentException( "from must come before till" );
1.34 + }
1.35 + }
1.36 +
1.37 + /**
1.38 + * The beginning of the time interval.
1.39 + */
1.40 + public Date getFrom() {
1.41 + return from;
1.42 + }
1.43 +
1.44 + /**
1.45 + * The end of the time interval.
1.46 + */
1.47 + public Date getTill() {
1.48 + return till;
1.49 + }
1.50 +
1.51 + /**
1.52 + * The conversion method.
1.53 + * Takes a {@link TimeSpecificConversionRequest}, other {@link ConversionRequest}s will result in an exception being thrown.
1.54 + * @param req the request; must not be null; must be {@link TimeSpecificConversionRequest} or a subclass thereof
1.55 + * @return the response
1.56 + * @throws IllegalRequestSubtypeException iff req is not instance of {@link TimeSpecificConversionRequest}
1.57 + */
1.58 + @Override
1.59 + public ConversionResult convert( final ConversionRequest req ) {
1.60 + if ( req instanceof TimeSpecificConversionRequest ) {
1.61 + final Date time = ( (TimeSpecificConversionRequest) req ).getTime();
1.62 + if ( ( from == null || !from.after(time) ) && ( till == null || time.before(till) ) ) {
1.63 + return super.convert( req );
1.64 + } else {
1.65 + return new ConversionResult( null );
1.66 + }
1.67 + } else {
1.68 + throw new IllegalRequestSubtypeException( this.getClass().getName() + ".convert() requires a TimeSpecificConversionRequest to be passed in" );
1.69 + }
1.70 + }
1.71 +
1.72 + /**
1.73 + * The request for converting a monetary amount into another currency using the conditions that apply at a particular time.
1.74 + * Immutable.
1.75 + */
1.76 + public static class TimeSpecificConversionRequest extends Convertor.ConversionRequest {
1.77 +
1.78 + private final Date time;
1.79 +
1.80 + /**
1.81 + * A request to convert srcAmount into tgtCurrency at the current time.
1.82 + * @param srcAmount the source amount; must not be null
1.83 + * @param tgtCurrency the currency we want it in afterwards; must not be null
1.84 + */
1.85 + public TimeSpecificConversionRequest( final MonetaryAmount srcAmount, final Currency tgtCurrency ) {
1.86 + this( srcAmount, tgtCurrency, new Date() );
1.87 + }
1.88 +
1.89 + /**
1.90 + * A request to convert srcAmount into tgtCurrency at given time.
1.91 + * @param srcAmount the source amount; must not be null
1.92 + * @param tgtCurrency the currency we want it in afterwards; must not be null
1.93 + * @param time the time instant when the conversion is to be carried out
1.94 + */
1.95 + public TimeSpecificConversionRequest( final MonetaryAmount srcAmount, final Currency tgtCurrency, final Date time ) {
1.96 + super( srcAmount, tgtCurrency );
1.97 + this.time = time;
1.98 + if ( time == null ) {
1.99 + throw new NullPointerException( "The time of conversion" );
1.100 + }
1.101 + }
1.102 +
1.103 + /**
1.104 + * The time as of which the conversion is to be carried out.
1.105 + */
1.106 + public Date getTime() {
1.107 + return time;
1.108 + }
1.109 +
1.110 + }
1.111 +
1.112 +}
2.1 --- a/task4/solution07/test/org/apidesign/apifest08/test/Task4Test.java Fri Oct 17 17:32:34 2008 +0200
2.2 +++ b/task4/solution07/test/org/apidesign/apifest08/test/Task4Test.java Fri Oct 17 17:33:32 2008 +0200
2.3 @@ -1,8 +1,18 @@
2.4 package org.apidesign.apifest08.test;
2.5
2.6 +import org.apidesign.apifest08.currency.TimeRangeSpecificConvertor;
2.7 +import java.text.DateFormat;
2.8 +import java.text.ParseException;
2.9 +import java.text.SimpleDateFormat;
2.10 +import java.util.Currency;
2.11 import java.util.Date;
2.12 import junit.framework.TestCase;
2.13 +import org.apidesign.apifest08.currency.ConversionRate;
2.14 import org.apidesign.apifest08.currency.Convertor;
2.15 +import org.apidesign.apifest08.currency.Convertor.ConversionResult;
2.16 +import org.apidesign.apifest08.currency.MonetaryAmount;
2.17 +import org.apidesign.apifest08.currency.TableConvertor;
2.18 +import org.apidesign.apifest08.currency.TimeRangeSpecificConvertor.TimeSpecificConversionRequest;
2.19
2.20 /** The exchange rates are not always the same. They are changing. However
2.21 * as in order to predict the future, one needs to understand own past. That is
2.22 @@ -30,6 +40,10 @@
2.23 protected void tearDown() throws Exception {
2.24 }
2.25
2.26 + protected static final Currency CZK = Currency.getInstance( "CZK" );
2.27 + protected static final Currency SKK = Currency.getInstance( "SKK" );
2.28 + protected static final Currency USD = Currency.getInstance( "USD" );
2.29 +
2.30 // Backward compatibly enhance your existing API to support following
2.31 // usecases:
2.32 //
2.33 @@ -40,62 +54,110 @@
2.34 * shall call some real one in the API.
2.35 *
2.36 * @param old existing convertor
2.37 - * @param from initial date (inclusive)
2.38 - * @param till final date (exclusive)
2.39 + * @param from initial date (inclusive); null means since the Big Bang
2.40 + * @param till final date (exclusive); null means until the End of Universe
2.41 * @return new convertor
2.42 */
2.43 - public static Convertor limitTo(Convertor old, Date from, Date till) {
2.44 - return null;
2.45 + public static Convertor limitTo( final Convertor old, final Date from, final Date till ) {
2.46 + return new TimeRangeSpecificConvertor( old, from, till );
2.47 + }
2.48 +
2.49 + private static final DateFormat DATE_FORMAT = new SimpleDateFormat( "yyyy-MM-dd HH:mm Z" );
2.50 +
2.51 + protected static Date parseGmtDate( final String string ) {
2.52 + final String zonedString = string + " GMT+0:00";
2.53 + try {
2.54 + return DATE_FORMAT.parse( zonedString );
2.55 + } catch ( final ParseException ex ) {
2.56 + throw new IllegalArgumentException( "Cannot parse " + zonedString, ex );
2.57 + }
2.58 }
2.59
2.60 -
2.61 public void testCompositionOfLimitedConvertors() throws Exception {
2.62 - if (Boolean.getBoolean("ignore.failing")) {
2.63 - // implement me! then delete this if statement
2.64 - return;
2.65 - }
2.66 -
2.67 - Date d1 = null; // 2008-10-01 0:00 GMT
2.68 - Date d2 = null; // 2008-10-02 0:00 GMT
2.69 - Date d3 = null; // 2008-10-03 0:00 GMT
2.70 + final Date d1 = parseGmtDate( "2008-10-01 0:00" );
2.71 + final Date d2 = parseGmtDate( "2008-10-02 0:00" );
2.72 + final Date d3 = parseGmtDate( "2008-10-03 0:00" );
2.73
2.74 - Convertor c = Task2Test.merge(
2.75 + final Convertor c = Task2Test.merge(
2.76 limitTo(Task1Test.createCZKtoUSD(), d1, d2),
2.77 limitTo(Task1Test.createSKKtoCZK(), d2, d3)
2.78 );
2.79
2.80 // convert $5 to CZK using c:
2.81 + final ConversionResult r1 = c.convert( new TimeSpecificConversionRequest( new MonetaryAmount( 5, USD ), CZK ) );
2.82 + final MonetaryAmount a1 = r1.getNetAmount();
2.83 // cannot convert as no rate is applicable to current date
2.84 + assertNull( a1 );
2.85
2.86 // convert $8 to CZK using c:
2.87 + final ConversionResult r2 = c.convert( new TimeSpecificConversionRequest( new MonetaryAmount( 8, USD ), CZK ) );
2.88 + final MonetaryAmount a2 = r2.getNetAmount();
2.89 // cannot convert as no rate is applicable to current date
2.90 + assertNull( a2 );
2.91
2.92 // convert 1003CZK to USD using c:
2.93 + final ConversionResult r3 = c.convert( new TimeSpecificConversionRequest( new MonetaryAmount( 1003, CZK ), USD ) );
2.94 + final MonetaryAmount a3 = r3.getNetAmount();
2.95 // cannot convert as no rate is applicable to current date
2.96 + assertNull( a3 );
2.97
2.98 // convert 16CZK using c:
2.99 + final ConversionResult r4 = c.convert( new TimeSpecificConversionRequest( new MonetaryAmount( 16, CZK ), USD ) );
2.100 + final MonetaryAmount a4 = r4.getNetAmount();
2.101 // cannot convert as no rate is applicable to current date
2.102 + assertNull( a4 );
2.103
2.104 // convert 500SKK to CZK using c:
2.105 + final ConversionResult r5 = c.convert( new TimeSpecificConversionRequest( new MonetaryAmount( 500, SKK ), CZK ) );
2.106 + final MonetaryAmount a5 = r5.getNetAmount();
2.107 // cannot convert as no rate is applicable to current date
2.108 + assertNull( a5 );
2.109
2.110 // convert $5 to CZK using c at 2008-10-01 6:00 GMT:
2.111 + final ConversionResult r6 = c.convert( new TimeSpecificConversionRequest( new MonetaryAmount( 5, USD ), CZK, parseGmtDate( "2008-10-01 6:00" ) ) );
2.112 + final MonetaryAmount a6 = r6.getNetAmount();
2.113 // assertEquals("Result is 85 CZK");
2.114 + assertNotNull( a6 );
2.115 + assertEquals( 85.0, a6.getAmount().doubleValue() );
2.116 + assertEquals( CZK, a6.getCurrency() );
2.117
2.118 // convert $8 to CZK using c at 2008-10-01 6:00 GMT:
2.119 + final ConversionResult r7 = c.convert( new TimeSpecificConversionRequest( new MonetaryAmount( 8, USD ), CZK, parseGmtDate( "2008-10-01 6:00" ) ) );
2.120 + final MonetaryAmount a7 = r7.getNetAmount();
2.121 // assertEquals("Result is 136 CZK");
2.122 + assertNotNull( a7 );
2.123 + assertEquals( 136.0, a7.getAmount().doubleValue() );
2.124 + assertEquals( CZK, a7.getCurrency() );
2.125
2.126 // convert 1003CZK to USD using c at 2008-10-01 6:00 GMT:
2.127 + final ConversionResult r8 = c.convert( new TimeSpecificConversionRequest( new MonetaryAmount( 1003, CZK ), USD, parseGmtDate( "2008-10-01 6:00" ) ) );
2.128 + final MonetaryAmount a8 = r8.getNetAmount();
2.129 // assertEquals("Result is 59 USD");
2.130 + assertNotNull( a8 );
2.131 + assertEquals( 59.0, a8.getAmount().doubleValue() );
2.132 + assertEquals( USD, a8.getCurrency() );
2.133
2.134 // convert 16CZK using c at 2008-10-02 9:00 GMT:
2.135 + final ConversionResult r9 = c.convert( new TimeSpecificConversionRequest( new MonetaryAmount( 16, CZK ), SKK, parseGmtDate( "2008-10-02 9:00" ) ) );
2.136 + final MonetaryAmount a9 = r9.getNetAmount();
2.137 // assertEquals("Result is 20 SKK");
2.138 + assertNotNull( a9 );
2.139 + assertEquals( 20.0, a9.getAmount().doubleValue() );
2.140 + assertEquals( SKK, a9.getCurrency() );
2.141
2.142 // convert 500SKK to CZK using c at 2008-10-02 9:00 GMT:
2.143 + final ConversionResult r10 = c.convert( new TimeSpecificConversionRequest( new MonetaryAmount( 500, SKK ), CZK, parseGmtDate( "2008-10-02 9:00" ) ) );
2.144 + final MonetaryAmount a10 = r10.getNetAmount();
2.145 // assertEquals("Result is 400 CZK");
2.146 + assertNotNull( a10 );
2.147 + assertEquals( 400.0, a10.getAmount().doubleValue() );
2.148 + assertEquals( CZK, a10.getCurrency() );
2.149
2.150 // convert 500SKK to CZK using c at 2008-10-01 6:00 GMT:
2.151 + final ConversionResult r11 = c.convert( new TimeSpecificConversionRequest( new MonetaryAmount( 500, SKK ), CZK, parseGmtDate( "2008-10-01 6:00" ) ) );
2.152 + final MonetaryAmount a11 = r11.getNetAmount();
2.153 // cannot convert as no rate is applicable to current date
2.154 + assertNull( a11 );
2.155 }
2.156
2.157 /** Create convertor that understands two currencies, CZK and
2.158 @@ -104,29 +166,39 @@
2.159 * @return prepared convertor ready for converting SKK to CZK and CZK to SKK
2.160 */
2.161 public static Convertor createSKKtoCZK2() {
2.162 - return null;
2.163 + final TableConvertor convertor = new TableConvertor();
2.164 + final MonetaryAmount amountInSKK = new MonetaryAmount( 100, SKK );
2.165 + final MonetaryAmount amountInCZK = new MonetaryAmount( 90, CZK );
2.166 + convertor.putIntoTable( new ConversionRate( amountInSKK, amountInCZK ) );
2.167 + convertor.putIntoTable( new ConversionRate( amountInCZK, amountInSKK ) );
2.168 + return new ContractImposingDelegatingConvertor( convertor ).test();
2.169 }
2.170
2.171 public void testDateConvetorWithTwoDifferentRates() throws Exception {
2.172 - if (Boolean.getBoolean("ignore.failing")) {
2.173 - // implement me! then delete this if statement
2.174 - return;
2.175 - }
2.176 + final Date d1 = parseGmtDate( "2008-10-01 0:00" );
2.177 + final Date d2 = parseGmtDate( "2008-10-02 0:00" );
2.178 + final Date d3 = parseGmtDate( "2008-10-03 0:00" );
2.179
2.180 - Date d1 = null; // 2008-10-01 0:00 GMT
2.181 - Date d2 = null; // 2008-10-02 0:00 GMT
2.182 - Date d3 = null; // 2008-10-03 0:00 GMT
2.183 -
2.184 - Convertor c = Task2Test.merge(
2.185 + final Convertor c = Task2Test.merge(
2.186 limitTo(createSKKtoCZK2(), d1, d2),
2.187 limitTo(Task1Test.createSKKtoCZK(), d2, d3)
2.188 );
2.189
2.190 // convert 500SKK to CZK using c at 2008-10-02 9:00 GMT:
2.191 + final ConversionResult r1 = c.convert( new TimeSpecificConversionRequest( new MonetaryAmount( 500, SKK ), CZK, parseGmtDate( "2008-10-02 9:00" ) ) );
2.192 + final MonetaryAmount a1 = r1.getNetAmount();
2.193 // assertEquals("Result is 400 CZK");
2.194 + assertNotNull( a1 );
2.195 + assertEquals( 400.0, a1.getAmount().doubleValue() );
2.196 + assertEquals( CZK, a1.getCurrency() );
2.197
2.198 // convert 500SKK to CZK using c at 2008-10-01 6:00 GMT:
2.199 + final ConversionResult r2 = c.convert( new TimeSpecificConversionRequest( new MonetaryAmount( 500, SKK ), CZK, parseGmtDate( "2008-10-01 6:00" ) ) );
2.200 + final MonetaryAmount a2 = r2.getNetAmount();
2.201 // assertEquals("Result is 450 CZK");
2.202 + assertNotNull( a2 );
2.203 + assertEquals( 450.0, a2.getAmount().doubleValue() );
2.204 + assertEquals( CZK, a2.getCurrency() );
2.205 }
2.206
2.207 }