# HG changeset patch # User japod@localhost # Date 1223669425 -7200 # Node ID be49855c7fa29260ab3e76aa9c16df8ab6cde7a0 # Parent a3144e7f9c9092571b1421e0ba1407a9a1fe6725 solution07 task3 diff -r a3144e7f9c90 -r be49855c7fa2 task3/solution07/src/org/apidesign/apifest08/currency/anothervendor/ZigZaggingBidirectionalConvertor.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/task3/solution07/src/org/apidesign/apifest08/currency/anothervendor/ZigZaggingBidirectionalConvertor.java Fri Oct 10 22:10:25 2008 +0200 @@ -0,0 +1,130 @@ +/* + * To change this template, choose Tools | Templates + * and open the template in the editor. + */ + +package org.apidesign.apifest08.currency.anothervendor; + +import java.math.BigDecimal; +import java.util.Currency; +import java.util.Iterator; +import org.apidesign.apifest08.currency.Convertor; +import org.apidesign.apifest08.currency.IllegalRequestSubtypeException; +import org.apidesign.apifest08.currency.MonetaryAmount; + + +/** + * A convertor that changes the rate by a step amount on every conversion it performs. + * @author jdvorak + */ +public class ZigZaggingBidirectionalConvertor implements Convertor { + + private final MonetaryAmount srcRateAmount; + + private final Currency tgtCurrency; + + private final Iterator rateIterator; + + /** + * Costructor with the precise amounts. + * @param srcRateAmount + * @param tgtCurrency + * @param startAmount + * @param endAmount + * @param stepAmount + */ + public ZigZaggingBidirectionalConvertor( final MonetaryAmount srcRateAmount, final Currency tgtCurrency, final BigDecimal startAmount, final BigDecimal endAmount, final BigDecimal stepAmount ) { + this.srcRateAmount = srcRateAmount; + this.tgtCurrency = tgtCurrency; + this.rateIterator = new ZigZaggingBigDecimalIterator( startAmount, endAmount, stepAmount ); + } + + /** + * Do the conversion; updates the rate. + * @param req + * @return + * @throws org.apidesign.apifest08.currency.IllegalRequestSubtypeException + */ + public synchronized ConversionResult convert( ConversionRequest req ) throws IllegalRequestSubtypeException { + if ( tgtCurrency.equals( req.getTgtCurrency() ) ) { + if ( srcRateAmount.getCurrency().equals( req.getSrcAmount().getCurrency() ) ) { + final BigDecimal tgtRateAmount = rateIterator.next(); + final BigDecimal tgtAmount = req.getSrcAmount().getAmount().multiply( tgtRateAmount ).divide( srcRateAmount.getAmount() ); + return new ConversionResult( new MonetaryAmount( tgtAmount, tgtCurrency ) ); + } + } + if ( srcRateAmount.getCurrency().equals( req.getTgtCurrency() ) ) { + if ( tgtCurrency.equals( req.getSrcAmount().getCurrency() ) ) { + final BigDecimal tgtRateAmount = rateIterator.next(); + final BigDecimal tgtAmount = req.getSrcAmount().getAmount().multiply( srcRateAmount.getAmount() ).divide( tgtRateAmount ); + return new ConversionResult( new MonetaryAmount( tgtAmount, srcRateAmount.getCurrency() ) ); + } + } + return new ConversionResult( null ); + } + +} + +/** + * An iterator that goes zig first, then zag, then zig again, then zag again, ad libitum. + * @author jdvorak + */ +class ZigZaggingBigDecimalIterator implements Iterator { + + private BigDecimal zigBounce; + + private BigDecimal zagBounce; + + private BigDecimal step; + + private BigDecimal currentValue; + + protected ZigZaggingBigDecimalIterator( final BigDecimal zagBounce, final BigDecimal zigBounce, final BigDecimal step ) { + this. zigBounce = zigBounce; + this.zagBounce = zagBounce; + this.currentValue = zagBounce; + this.step = step; + + if ( zigBounce == null ) { + throw new NullPointerException( "zigAmount" ); + } + if ( zagBounce == null ) { + throw new NullPointerException( "zagAmount" ); + } + if ( step == null ) { + throw new NullPointerException( "stepAmount" ); + } + final int stepSign = step.signum(); + if ( stepSign == 0 ) { + throw new IllegalArgumentException( "stepAmount can't be zero" ); + } + if ( stepSign * zigBounce.compareTo( zagBounce ) < 0 ) { + throw new IllegalArgumentException( "stepAmount shall have the same sign as endAmount - startAmount" ); + } + } + + public boolean hasNext() { + return true; + } + + public BigDecimal next() { + final BigDecimal result = currentValue; + + currentValue = currentValue.add( step ); + final int stepSign = step.signum(); + final int currentMinusZigSign = currentValue.compareTo( zigBounce ); + if ( stepSign * currentMinusZigSign >= 0 ) { + final BigDecimal temp = zigBounce; + zigBounce = zagBounce; + zagBounce = temp; + step = step.negate(); + } + + return result; + } + + public void remove() { + throw new UnsupportedOperationException("Removal is not supported."); + } + +} \ No newline at end of file diff -r a3144e7f9c90 -r be49855c7fa2 task3/solution07/test/org/apidesign/apifest08/test/Task3Test.java --- a/task3/solution07/test/org/apidesign/apifest08/test/Task3Test.java Fri Oct 10 22:07:25 2008 +0200 +++ b/task3/solution07/test/org/apidesign/apifest08/test/Task3Test.java Fri Oct 10 22:10:25 2008 +0200 @@ -1,7 +1,11 @@ package org.apidesign.apifest08.test; +import java.math.BigDecimal; +import java.util.Currency; import junit.framework.TestCase; import org.apidesign.apifest08.currency.Convertor; +import org.apidesign.apifest08.currency.MonetaryAmount; +import org.apidesign.apifest08.currency.anothervendor.ZigZaggingBidirectionalConvertor; /** The exchange rates are not always the same. They are changing. Day by day, * hour by hour, minute by minute. For every bank it is important to always @@ -28,6 +32,10 @@ protected void tearDown() throws Exception { } + protected static final Currency CZK = Currency.getInstance( "CZK" ); + protected static final Currency SKK = Currency.getInstance( "SKK" ); + protected static final Currency USD = Currency.getInstance( "USD" ); + // Backward compatibly enhance your existing API to support following // usecases: // @@ -51,7 +59,11 @@ // then 1USD = 15.02CZK // and so on and on up to 1USD = 16CZK // and then another round to 15, etc. - return null; + final MonetaryAmount oneUSD = new MonetaryAmount( 1, USD ); + final BigDecimal startAmount = new BigDecimal( 16 ); + final BigDecimal endAmount = new BigDecimal( 15 ); + final BigDecimal stepAmount = BigDecimal.ONE.movePointLeft( 2 ).negate(); + return new ContractImposingDelegatingConvertor( new ZigZaggingBidirectionalConvertor( oneUSD, CZK, startAmount, endAmount, stepAmount ) ); } public void testFewQueriesForOnlineConvertor() { @@ -64,20 +76,39 @@ doFewQueriesForOnlineConvertor(c); } - static void doFewQueriesForOnlineConvertor(Convertor c) { + static void doFewQueriesForOnlineConvertor( final Convertor c ) { // convert $5 to CZK using c: + final Convertor.ConversionResult r1 = c.convert( new Convertor.ConversionRequest( new MonetaryAmount( 5, USD ), CZK ) ); + final MonetaryAmount a1 = r1.getNetAmount(); //assertEquals("Result is 80 CZK"); + assertNotNull( a1 ); + assertEquals( 80.0, a1.getAmount().doubleValue() ); + assertEquals( CZK, a1.getCurrency() ); // convert $8 to CZK using c: + final Convertor.ConversionResult r2 = c.convert( new Convertor.ConversionRequest( new MonetaryAmount( 8, USD ), CZK ) ); + final MonetaryAmount a2 = r2.getNetAmount(); //assertEquals("Result is 127.92 CZK"); + assertNotNull( a2 ); + assertEquals( 12792.0, a2.getAmount().movePointRight( 2 ).doubleValue() ); + assertEquals( CZK, a2.getCurrency() ); // convert $1 to CZK using c: + final Convertor.ConversionResult r3 = c.convert( new Convertor.ConversionRequest( new MonetaryAmount( 1, USD ), CZK ) ); + final MonetaryAmount a3 = r3.getNetAmount(); //assertEquals("Result is 15.98 CZK"); + assertNotNull( a3 ); + assertEquals( 1598.0, a3.getAmount().movePointRight( 2 ).doubleValue() ); + assertEquals( CZK, a3.getCurrency() ); // convert 15.97CZK to USD using c: + final BigDecimal s4 = new BigDecimal( 1597 ).movePointLeft( 2 ); + final Convertor.ConversionResult r4 = c.convert( new Convertor.ConversionRequest( new MonetaryAmount( s4, CZK ), USD ) ); + final MonetaryAmount a4 = r4.getNetAmount(); //assertEquals("Result is 1$"); - - fail("Implement me!"); + assertNotNull( a4 ); + assertEquals( 1.0, a4.getAmount().doubleValue() ); + assertEquals( USD, a4.getCurrency() ); } /** Join the convertors and show they behave sane. @@ -88,16 +119,26 @@ return; } - Convertor c = Task2Test.merge( + final Convertor c = Task2Test.merge( createOnlineCZKUSDConvertor(), Task1Test.createSKKtoCZK() ); - // convert 16CZK to SKK using c: + // convert 16CZK using c: + final Convertor.ConversionResult r4 = c.convert( new Convertor.ConversionRequest( new MonetaryAmount( 16, CZK ), SKK ) ); + final MonetaryAmount a4 = r4.getNetAmount(); // assertEquals("Result is 20 SKK"); + assertNotNull( a4 ); + assertEquals( 20.0, a4.getAmount().doubleValue() ); + assertEquals( SKK, a4.getCurrency() ); // convert 500SKK to CZK using c: + final Convertor.ConversionResult r5 = c.convert( new Convertor.ConversionRequest( new MonetaryAmount( 500, SKK ), CZK ) ); + final MonetaryAmount a5 = r5.getNetAmount(); // assertEquals("Result is 400 CZK"); + assertNotNull( a5 ); + assertEquals( 400.0, a5.getAmount().doubleValue() ); + assertEquals( CZK, a5.getCurrency() ); doFewQueriesForOnlineConvertor(c); }