001/*
002 * jPOS Project [http://jpos.org]
003 * Copyright (C) 2000-2026 jPOS Software SRL
004 *
005 * This program is free software: you can redistribute it and/or modify
006 * it under the terms of the GNU Affero General Public License as
007 * published by the Free Software Foundation, either version 3 of the
008 * License, or (at your option) any later version.
009 *
010 * This program is distributed in the hope that it will be useful,
011 * but WITHOUT ANY WARRANTY; without even the implied warranty of
012 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
013 * GNU Affero General Public License for more details.
014 *
015 * You should have received a copy of the GNU Affero General Public License
016 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
017 */
018
019package org.jpos.iso;
020
021import org.jpos.iso.packager.XMLPackager;
022
023import java.io.*;
024import java.math.BigDecimal;
025import java.util.Objects;
026
027public class ISOAmount 
028    extends ISOComponent 
029    implements Cloneable, Externalizable
030{
031    static final long serialVersionUID = -6130248734056876225L;
032    private int fieldNumber;
033    private int currencyCode;
034    private String value;
035    private BigDecimal amount;
036
037    public ISOAmount () {
038        super();
039        setFieldNumber (-1);
040    }
041    public ISOAmount (int fieldNumber) {
042        super ();
043        setFieldNumber (fieldNumber);
044    }
045    public ISOAmount (int fieldNumber, int currencyCode, BigDecimal amount) throws ISOException {
046        super ();
047        setFieldNumber(fieldNumber);
048        this.currencyCode = currencyCode;
049        try {
050            this.amount = amount.setScale(ISOCurrency.getCurrency(currencyCode).getDecimals());
051        } catch (ArithmeticException e) {
052            throw new ISOException (
053              "rounding problem, amount=" + amount + " scale=" + ISOCurrency.getCurrency(currencyCode).getDecimals()
054            );
055        }
056    }
057    public Object getKey() {
058        return fieldNumber;
059    }
060    public Object getValue() throws ISOException {
061        if (value == null) {
062            StringBuilder sb = new StringBuilder();
063            sb.append (ISOUtil.zeropad (Integer.toString(currencyCode), 3));
064            sb.append (Integer.toString (amount.scale()));
065            sb.append (
066                ISOUtil.zeropad (
067                    amount.movePointRight(amount.scale()).toString(),12
068                )
069            );
070            value = sb.toString();
071        }
072        return value;
073
074    }
075    public void setValue (Object obj) throws ISOException {
076        if (obj instanceof String) {
077            String s = (String) obj;
078            if (s.length() < 12) {
079                throw new ISOException (
080                    "ISOAmount invalid length " + s.length()
081                );
082            }
083            try {
084                currencyCode = Integer.parseInt (s.substring(0,3));
085                int dec = Integer.parseInt (s.substring(3,4));
086                amount = new BigDecimal (s.substring(4)).movePointLeft (dec);
087                value  = s;
088            } catch (NumberFormatException e) {
089                throw new ISOException (e.getMessage());
090            }
091        }
092    }
093    public void setFieldNumber (int fieldNumber) {
094        this.fieldNumber = fieldNumber;
095    }
096
097    @Override
098    public int getFieldNumber () {
099        return fieldNumber;
100    }
101    public BigDecimal getAmount () {
102        return amount;
103    }
104    public int getScale() {
105        return amount.scale() % 10;
106    }
107    public String getScaleAsString() {
108        return Integer.toString(getScale());
109    }
110    public int getCurrencyCode() {
111        return currencyCode;
112    }
113    public String getCurrencyCodeAsString() throws ISOException {
114        return ISOUtil.zeropad(Integer.toString(currencyCode),3);
115    }
116    public String getAmountAsLegacyString() throws ISOException {
117        return ISOUtil.zeropad (amount.unscaledValue().toString(), 12);
118    }
119    public String getAmountAsString() throws ISOException {
120        StringBuilder sb = new StringBuilder(16);
121        sb.append (ISOUtil.zeropad (Integer.toString (currencyCode),3));
122        sb.append (Integer.toString(amount.scale() % 10));
123        sb.append (ISOUtil.zeropad (amount.unscaledValue().toString(), 12));
124        return sb.toString();
125    }
126    public byte[] pack() throws ISOException {
127        throw new ISOException ("Not available");
128    }
129    public int unpack(byte[] b) throws ISOException {
130        throw new ISOException ("Not available");
131    }
132    public void unpack(InputStream in) throws ISOException {
133        throw new ISOException ("Not available");
134    }
135    public void dump (PrintStream p, String indent) {
136        p.println (indent +"<"+XMLPackager.ISOFIELD_TAG + " " 
137          +XMLPackager.ID_ATTR +"=\"" +fieldNumber +"\" "
138          +"currency=\"" +ISOUtil.zeropad (currencyCode, 3)+"\" "
139          +XMLPackager.TYPE_ATTR +"=\"amount\" "
140          +XMLPackager.VALUE_ATTR+"=\"" + amount.toString() +"\"/>"
141        );
142    }
143    public void writeExternal (ObjectOutput out) throws IOException {
144        out.writeShort (fieldNumber);
145        try {
146            out.writeUTF ((String) getValue());
147        } catch (ISOException e) {
148            throw new IOException (e);
149        }
150    }
151    public void readExternal  (ObjectInput in) 
152        throws IOException, ClassNotFoundException
153    {
154        fieldNumber = in.readShort ();
155        try {
156            setValue(in.readUTF());
157        } catch (ISOException e) {
158            throw new IOException (e.getMessage());
159        }
160    }
161
162    @Override
163    public boolean equals(Object o) {
164        if (this == o) return true;
165        if (o == null || getClass() != o.getClass()) return false;
166        ISOAmount isoAmount = (ISOAmount) o;
167        return fieldNumber == isoAmount.fieldNumber &&
168          currencyCode == isoAmount.currencyCode &&
169          Objects.equals(amount, isoAmount.amount);
170    }
171
172    @Override
173    public int hashCode() {
174        return Objects.hash(fieldNumber, currencyCode, amount);
175    }
176
177    @Override
178    public String toString() {
179        return "ISOAmount{" +
180          "fieldNumber=" + fieldNumber +
181          ", currencyCode=" + currencyCode +
182          ", amount=" + amount +
183          '}';
184    }
185}
186