View Javadoc

1   /*
2    *  Copyright 2009-2013 Stephen Colebourne
3    *
4    *  Licensed under the Apache License, Version 2.0 (the "License");
5    *  you may not use this file except in compliance with the License.
6    *  You may obtain a copy of the License at
7    *
8    *      http://www.apache.org/licenses/LICENSE-2.0
9    *
10   *  Unless required by applicable law or agreed to in writing, software
11   *  distributed under the License is distributed on an "AS IS" BASIS,
12   *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   *  See the License for the specific language governing permissions and
14   *  limitations under the License.
15   */
16  package org.joda.money;
17  
18  import java.io.Externalizable;
19  import java.io.IOException;
20  import java.io.InvalidClassException;
21  import java.io.InvalidObjectException;
22  import java.io.ObjectInput;
23  import java.io.ObjectOutput;
24  import java.io.StreamCorruptedException;
25  import java.math.BigDecimal;
26  import java.math.BigInteger;
27  
28  /**
29   * A package scoped class used to manage serialization efficiently.
30   * <p>
31   * This class is mutable and intended for use by a single thread.
32   */
33  final class Ser implements Externalizable {
34  
35      /** Type for BigMoney. */
36      static final byte BIG_MONEY = 'B';
37      /** Type for Money. */
38      static final byte MONEY = 'M';
39      /** Type for CurrencyUnit. */
40      static final byte CURRENCY_UNIT = 'C';  // not in use yet
41  
42      /** The type. */
43      private byte type;
44      /** The data object. */
45      private Object object;
46  
47      /**
48       * Constructor for serialization.
49       */
50      public Ser() {
51      }
52  
53      /**
54       * Constructor for package.
55       * 
56       * @param type  the type
57       * @param object  the object
58       */
59      Ser(byte type, Object object) {
60          this.type = type;
61          this.object = object;
62      }
63  
64      //-----------------------------------------------------------------------
65      /**
66       * Outputs the data.
67       *
68       * @serialData One byte type code, then data specific to the type.
69       * @param out  the output stream
70       * @throws IOException if an error occurs
71       */
72      public void writeExternal(ObjectOutput out) throws IOException {
73          out.writeByte(type);
74          switch (type) {
75              case BIG_MONEY: {
76                  BigMoney obj = (BigMoney) object;
77                  writeBigMoney(out, obj);
78                  return;
79              }
80              case MONEY: {
81                  Money obj = (Money) object;
82                  writeBigMoney(out, obj.toBigMoney());
83                  return;
84              }
85              case CURRENCY_UNIT: {
86                  CurrencyUnit obj = (CurrencyUnit) object;
87                  writeCurrency(out, obj);
88                  return;
89              }
90          }
91          throw new InvalidClassException("Joda-Money bug: Serialization broken");
92      }
93  
94      private void writeBigMoney(ObjectOutput out, BigMoney obj) throws IOException {
95          writeCurrency(out, obj.getCurrencyUnit());
96          byte[] bytes = obj.getAmount().unscaledValue().toByteArray();
97          out.writeInt(bytes.length);
98          out.write(bytes);
99          out.writeInt(obj.getScale());
100     }
101 
102     private void writeCurrency(ObjectOutput out, CurrencyUnit obj) throws IOException {
103         out.writeUTF(obj.getCode());
104         out.writeShort(obj.getNumericCode());
105         out.writeShort(obj.getDefaultFractionDigits());
106     }
107 
108     /**
109      * Outputs the data.
110      *
111      * @param in  the input stream
112      * @throws IOException if an error occurs
113      */
114     public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
115         type = in.readByte();
116         switch (type) {
117             case BIG_MONEY: {
118                 object = readBigMoney(in);
119                 return;
120             }
121             case MONEY: {
122                 object = new Money(readBigMoney(in));
123                 return;
124             }
125             case CURRENCY_UNIT: {
126                 object = readCurrency(in);
127                 return;
128             }
129         }
130         throw new StreamCorruptedException("Serialization input has invalid type");
131     }
132 
133     private BigMoney readBigMoney(ObjectInput in) throws IOException {
134         CurrencyUnit currency = readCurrency(in);
135         byte[] bytes = new byte[in.readInt()];
136         in.readFully(bytes);
137         BigDecimal bd = new BigDecimal(new BigInteger(bytes), in.readInt());
138         BigMoney bigMoney = new BigMoney(currency, bd);
139         return bigMoney;
140     }
141 
142     private CurrencyUnit readCurrency(ObjectInput in) throws IOException {
143         String code = in.readUTF();
144         CurrencyUnit singletonCurrency = CurrencyUnit.of(code);
145         if (singletonCurrency.getNumericCode() != in.readShort()) {
146             throw new InvalidObjectException("Deserialization found a mismatch in the numeric code for currency " + code);
147         }
148         if (singletonCurrency.getDefaultFractionDigits() != in.readShort()) {
149             throw new InvalidObjectException("Deserialization found a mismatch in the decimal places for currency " + code);
150         }
151         return singletonCurrency;
152     }
153 
154     /**
155      * Returns the object that will replace this one.
156      * 
157      * @return the read object, should never be null
158      */
159     private Object readResolve() {
160         return object;
161     }
162 
163 }