1   package junit.samples.money;
2   
3   import java.util.*;
4   
5   /***
6    * A MoneyBag defers exchange rate conversions. For example adding 
7    * 12 Swiss Francs to 14 US Dollars is represented as a bag 
8    * containing the two Monies 12 CHF and 14 USD. Adding another
9    * 10 Swiss francs gives a bag with 22 CHF and 14 USD. Due to 
10   * the deferred exchange rate conversion we can later value a 
11   * MoneyBag with different exchange rates.
12   *
13   * A MoneyBag is represented as a list of Monies and provides 
14   * different constructors to create a MoneyBag. 
15   */ 
16  class MoneyBag implements IMoney {
17  	private Vector fMonies= new Vector(5);
18  
19  	static IMoney create(IMoney m1, IMoney m2) {
20  		MoneyBag result= new MoneyBag();
21  		m1.appendTo(result);
22  		m2.appendTo(result);
23  		return result.simplify();
24  	}
25  	public IMoney add(IMoney m) {
26  		return m.addMoneyBag(this);
27  	}
28  	public IMoney addMoney(Money m) { 
29  		return MoneyBag.create(m, this);
30  	}
31  	public IMoney addMoneyBag(MoneyBag s) {
32  		return MoneyBag.create(s, this);
33  	}
34  	void appendBag(MoneyBag aBag) {
35  		for (Enumeration e= aBag.fMonies.elements(); e.hasMoreElements(); )
36  			appendMoney((Money)e.nextElement());
37  	}
38  	void appendMoney(Money aMoney) {
39  		if (aMoney.isZero()) return;
40  		IMoney old= findMoney(aMoney.currency());
41  		if (old == null) {
42  			fMonies.addElement(aMoney);
43  			return;
44  		}
45  		fMonies.removeElement(old);
46  		IMoney sum= old.add(aMoney);
47  		if (sum.isZero()) 
48  			return;
49  		fMonies.addElement(sum);
50  	}
51  	public boolean equals(Object anObject) {
52  		if (isZero())
53  			if (anObject instanceof IMoney)
54  				return ((IMoney)anObject).isZero();
55  
56  		if (anObject instanceof MoneyBag) {
57  			MoneyBag aMoneyBag= (MoneyBag)anObject;
58  			if (aMoneyBag.fMonies.size() != fMonies.size())
59  				return false;
60  
61  		    for (Enumeration e= fMonies.elements(); e.hasMoreElements(); ) {
62  		        Money m= (Money) e.nextElement();
63  				if (!aMoneyBag.contains(m))
64  					return false;
65  			}
66  			return true;
67  		}
68  		return false;
69  	}
70  	private Money findMoney(String currency) {
71  		for (Enumeration e= fMonies.elements(); e.hasMoreElements(); ) {
72  			Money m= (Money) e.nextElement();
73  			if (m.currency().equals(currency))
74  				return m;
75  		}
76  		return null;
77  	}
78  	private boolean contains(Money m) {
79  		Money found= findMoney(m.currency());
80  		if (found == null) return false;
81  		return found.amount() == m.amount();
82  	}
83  	public int hashCode() {
84  		int hash= 0;
85  	    for (Enumeration e= fMonies.elements(); e.hasMoreElements(); ) {
86  	        Object m= e.nextElement();
87  			hash^= m.hashCode();
88  		}
89  	    return hash;
90  	}
91  	public boolean isZero() {
92  		return fMonies.size() == 0;
93  	}
94  	public IMoney multiply(int factor) {
95  		MoneyBag result= new MoneyBag();
96  		if (factor != 0) {
97  			for (Enumeration e= fMonies.elements(); e.hasMoreElements(); ) {
98  				Money m= (Money) e.nextElement();
99  				result.appendMoney((Money)m.multiply(factor));
100 			}
101 		}
102 		return result;
103 	}
104 	public IMoney negate() {
105 		MoneyBag result= new MoneyBag();
106 	    for (Enumeration e= fMonies.elements(); e.hasMoreElements(); ) {
107 	        Money m= (Money) e.nextElement();
108 	        result.appendMoney((Money)m.negate());
109 		}
110 		return result;
111 	}
112 	private IMoney simplify() {
113 		if (fMonies.size() == 1)
114 			return (IMoney)fMonies.elements().nextElement();
115 		return this;
116 	}
117 	public IMoney subtract(IMoney m) {
118 		return add(m.negate());
119 	}
120 	public String toString() {
121 		StringBuffer buffer = new StringBuffer();
122 		buffer.append("{");
123 		for (Enumeration e= fMonies.elements(); e.hasMoreElements(); )
124 		    buffer.append(e.nextElement());
125 		buffer.append("}");
126 		return buffer.toString();
127 	}
128 	public void appendTo(MoneyBag m) {
129 		m.appendBag(this);
130 	}
131 }