1 package org.apidesign.apifest08.test;
3 import org.apidesign.apifest08.currency.TimeRangeSpecificConvertor;
4 import java.text.DateFormat;
5 import java.text.ParseException;
6 import java.text.SimpleDateFormat;
7 import java.util.Currency;
9 import junit.framework.TestCase;
10 import org.apidesign.apifest08.currency.ConversionRate;
11 import org.apidesign.apifest08.currency.Convertor;
12 import org.apidesign.apifest08.currency.Convertor.ConversionResult;
13 import org.apidesign.apifest08.currency.MonetaryAmount;
14 import org.apidesign.apifest08.currency.TableConvertor;
15 import org.apidesign.apifest08.currency.TimeRangeSpecificConvertor.TimeSpecificConversionRequest;
17 /** The exchange rates are not always the same. They are changing. However
18 * as in order to predict the future, one needs to understand own past. That is
19 * why it is important to know the exchange rate as it was at any time during
22 * Today's quest is to enhance the convertor API to deal with dates.
23 * One shall be able to convert a currency at any date. Each currencies rate shall
24 * be associated with a range between two Date objects. In order
25 * to keep compatibility with old API that knew nothing about dates, the
26 * rates associated then are applicable "for eternity". Any use of existing
27 * convert methods that do not accept a Date argument, uses the current
28 * System.currentTimeMillis() as default date.
30 public class Task4Test extends TestCase {
31 public Task4Test(String testName) {
36 protected void setUp() throws Exception {
40 protected void tearDown() throws Exception {
43 protected static final Currency CZK = Currency.getInstance( "CZK" );
44 protected static final Currency SKK = Currency.getInstance( "SKK" );
45 protected static final Currency USD = Currency.getInstance( "USD" );
47 // Backward compatibly enhance your existing API to support following
51 /** Takes a convertor with any rates associated and creates new convertor
52 * that returns the same values as the old one for time between from to till.
53 * Otherwise it returns no results. This is just a helper method that
54 * shall call some real one in the API.
56 * @param old existing convertor
57 * @param from initial date (inclusive); null means since the Big Bang
58 * @param till final date (exclusive); null means until the End of Universe
59 * @return new convertor
61 public static Convertor limitTo( final Convertor old, final Date from, final Date till ) {
62 return new TimeRangeSpecificConvertor( old, from, till );
65 private static final DateFormat DATE_FORMAT = new SimpleDateFormat( "yyyy-MM-dd HH:mm Z" );
67 protected static Date parseGmtDate( final String string ) {
68 final String zonedString = string + " GMT+0:00";
70 return DATE_FORMAT.parse( zonedString );
71 } catch ( final ParseException ex ) {
72 throw new IllegalArgumentException( "Cannot parse " + zonedString, ex );
76 public void testCompositionOfLimitedConvertors() throws Exception {
77 final Date d1 = parseGmtDate( "2008-10-01 0:00" );
78 final Date d2 = parseGmtDate( "2008-10-02 0:00" );
79 final Date d3 = parseGmtDate( "2008-10-03 0:00" );
81 final Convertor c = Task2Test.merge(
82 limitTo(Task1Test.createCZKtoUSD(), d1, d2),
83 limitTo(Task1Test.createSKKtoCZK(), d2, d3)
86 // convert $5 to CZK using c:
87 final ConversionResult r1 = c.convert( new TimeSpecificConversionRequest( new MonetaryAmount( 5, USD ), CZK ) );
88 final MonetaryAmount a1 = r1.getNetAmount();
89 // cannot convert as no rate is applicable to current date
92 // convert $8 to CZK using c:
93 final ConversionResult r2 = c.convert( new TimeSpecificConversionRequest( new MonetaryAmount( 8, USD ), CZK ) );
94 final MonetaryAmount a2 = r2.getNetAmount();
95 // cannot convert as no rate is applicable to current date
98 // convert 1003CZK to USD using c:
99 final ConversionResult r3 = c.convert( new TimeSpecificConversionRequest( new MonetaryAmount( 1003, CZK ), USD ) );
100 final MonetaryAmount a3 = r3.getNetAmount();
101 // cannot convert as no rate is applicable to current date
104 // convert 16CZK using c:
105 final ConversionResult r4 = c.convert( new TimeSpecificConversionRequest( new MonetaryAmount( 16, CZK ), USD ) );
106 final MonetaryAmount a4 = r4.getNetAmount();
107 // cannot convert as no rate is applicable to current date
110 // convert 500SKK to CZK using c:
111 final ConversionResult r5 = c.convert( new TimeSpecificConversionRequest( new MonetaryAmount( 500, SKK ), CZK ) );
112 final MonetaryAmount a5 = r5.getNetAmount();
113 // cannot convert as no rate is applicable to current date
116 // convert $5 to CZK using c at 2008-10-01 6:00 GMT:
117 final ConversionResult r6 = c.convert( new TimeSpecificConversionRequest( new MonetaryAmount( 5, USD ), CZK, parseGmtDate( "2008-10-01 6:00" ) ) );
118 final MonetaryAmount a6 = r6.getNetAmount();
119 // assertEquals("Result is 85 CZK");
121 assertEquals( 85.0, a6.getAmount().doubleValue() );
122 assertEquals( CZK, a6.getCurrency() );
124 // convert $8 to CZK using c at 2008-10-01 6:00 GMT:
125 final ConversionResult r7 = c.convert( new TimeSpecificConversionRequest( new MonetaryAmount( 8, USD ), CZK, parseGmtDate( "2008-10-01 6:00" ) ) );
126 final MonetaryAmount a7 = r7.getNetAmount();
127 // assertEquals("Result is 136 CZK");
129 assertEquals( 136.0, a7.getAmount().doubleValue() );
130 assertEquals( CZK, a7.getCurrency() );
132 // convert 1003CZK to USD using c at 2008-10-01 6:00 GMT:
133 final ConversionResult r8 = c.convert( new TimeSpecificConversionRequest( new MonetaryAmount( 1003, CZK ), USD, parseGmtDate( "2008-10-01 6:00" ) ) );
134 final MonetaryAmount a8 = r8.getNetAmount();
135 // assertEquals("Result is 59 USD");
137 assertEquals( 59.0, a8.getAmount().doubleValue() );
138 assertEquals( USD, a8.getCurrency() );
140 // convert 16CZK using c at 2008-10-02 9:00 GMT:
141 final ConversionResult r9 = c.convert( new TimeSpecificConversionRequest( new MonetaryAmount( 16, CZK ), SKK, parseGmtDate( "2008-10-02 9:00" ) ) );
142 final MonetaryAmount a9 = r9.getNetAmount();
143 // assertEquals("Result is 20 SKK");
145 assertEquals( 20.0, a9.getAmount().doubleValue() );
146 assertEquals( SKK, a9.getCurrency() );
148 // convert 500SKK to CZK using c at 2008-10-02 9:00 GMT:
149 final ConversionResult r10 = c.convert( new TimeSpecificConversionRequest( new MonetaryAmount( 500, SKK ), CZK, parseGmtDate( "2008-10-02 9:00" ) ) );
150 final MonetaryAmount a10 = r10.getNetAmount();
151 // assertEquals("Result is 400 CZK");
152 assertNotNull( a10 );
153 assertEquals( 400.0, a10.getAmount().doubleValue() );
154 assertEquals( CZK, a10.getCurrency() );
156 // convert 500SKK to CZK using c at 2008-10-01 6:00 GMT:
157 final ConversionResult r11 = c.convert( new TimeSpecificConversionRequest( new MonetaryAmount( 500, SKK ), CZK, parseGmtDate( "2008-10-01 6:00" ) ) );
158 final MonetaryAmount a11 = r11.getNetAmount();
159 // cannot convert as no rate is applicable to current date
163 /** Create convertor that understands two currencies, CZK and
164 * SKK. Make 100 SKK == 90 CZK.
166 * @return prepared convertor ready for converting SKK to CZK and CZK to SKK
168 public static Convertor createSKKtoCZK2() {
169 final TableConvertor convertor = new TableConvertor();
170 final MonetaryAmount amountInSKK = new MonetaryAmount( 100, SKK );
171 final MonetaryAmount amountInCZK = new MonetaryAmount( 90, CZK );
172 convertor.putIntoTable( new ConversionRate( amountInSKK, amountInCZK ) );
173 convertor.putIntoTable( new ConversionRate( amountInCZK, amountInSKK ) );
174 return new ContractImposingDelegatingConvertor( convertor ).test();
177 public void testDateConvetorWithTwoDifferentRates() throws Exception {
178 final Date d1 = parseGmtDate( "2008-10-01 0:00" );
179 final Date d2 = parseGmtDate( "2008-10-02 0:00" );
180 final Date d3 = parseGmtDate( "2008-10-03 0:00" );
182 final Convertor c = Task2Test.merge(
183 limitTo(createSKKtoCZK2(), d1, d2),
184 limitTo(Task1Test.createSKKtoCZK(), d2, d3)
187 // convert 500SKK to CZK using c at 2008-10-02 9:00 GMT:
188 final ConversionResult r1 = c.convert( new TimeSpecificConversionRequest( new MonetaryAmount( 500, SKK ), CZK, parseGmtDate( "2008-10-02 9:00" ) ) );
189 final MonetaryAmount a1 = r1.getNetAmount();
190 // assertEquals("Result is 400 CZK");
192 assertEquals( 400.0, a1.getAmount().doubleValue() );
193 assertEquals( CZK, a1.getCurrency() );
195 // convert 500SKK to CZK using c at 2008-10-01 6:00 GMT:
196 final ConversionResult r2 = c.convert( new TimeSpecificConversionRequest( new MonetaryAmount( 500, SKK ), CZK, parseGmtDate( "2008-10-01 6:00" ) ) );
197 final MonetaryAmount a2 = r2.getNetAmount();
198 // assertEquals("Result is 450 CZK");
200 assertEquals( 450.0, a2.getAmount().doubleValue() );
201 assertEquals( CZK, a2.getCurrency() );