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 java.io.EOFException;
022import java.io.IOException;
023import java.io.InputStream;
024import java.io.ObjectOutput;
025
026/**
027 * base class for the various IF*.java Field Packagers
028 * Implements "FlyWeight" pattern
029 *
030 * @author apr@cs.com.uy
031 * @version $Id$
032 *
033 * @see IFA_AMOUNT
034 * @see IFA_BINARY
035 * @see IFA_BITMAP
036 * @see IFA_FLLCHAR
037 * @see IFA_FLLNUM
038 * @see IFA_LLCHAR
039 * @see IFA_LLLBINARY
040 * @see IFA_LLLCHAR
041 * @see IFA_LLLNUM
042 * @see IFA_LLNUM
043 * @see IFA_NUMERIC
044 * @see IFB_AMOUNT
045 * @see IFB_BINARY
046 * @see IFB_BITMAP
047 * @see IFB_LLBINARY
048 * @see IFB_LLCHAR
049 * @see IFB_LLHBINARY
050 * @see IFB_LLHCHAR
051 * @see IFB_LLHECHAR
052 * @see IFB_LLHNUM
053 * @see IFB_LLLBINARY
054 * @see IFB_LLLCHAR
055 * @see IFB_LLLNUM
056 * @see IFB_LLNUM
057 * @see IFB_NUMERIC
058 * @see IF_CHAR
059 */
060public abstract class ISOFieldPackager {
061    private int len;
062    private String description;
063    /** When {@code true}, values are padded to the field length. */
064    protected boolean pad;
065    /** When {@code true}, values are trimmed before packing. */
066    protected boolean trim;
067
068    /**
069     * Default Constructor
070     */
071    public ISOFieldPackager()
072    {
073        this.len = -1;
074        this.description = null;
075    }
076
077    /**
078     * Creates an ISOFieldPackager with the given length and description.
079     * @param len - field Len
080     * @param description - details
081     */
082    public ISOFieldPackager(int len, String description) {
083        this.len = len;
084        this.description = description;
085    }
086    /**
087     * Returns the field description.
088     * @return field description
089     */
090    public String getDescription() {
091        return description;
092    }
093    /**
094     * Sets the field description.
095     * @param description the description text
096     */
097    public void setDescription(String description) {
098        this.description = description;
099    }
100    /**
101     * Returns the maximum field length.
102     * @return max field length
103     */
104    public int getLength() {
105        return len;
106    }
107    /**
108     * Sets the maximum field length.
109     * @param len the maximum length
110     */
111    public void setLength(int len) {
112        this.len = len;
113    }
114
115    /**
116     * Enables or disables padding for this field.
117     * @param pad true to enable padding
118     */
119    public void setPad(boolean pad) {
120        this.pad = pad;
121    }
122
123    /**
124     * Enables or disables trimming for this field.
125     * @param trim true to enable trimming
126     */
127    public void setTrim(boolean trim) {
128        this.trim = trim;
129    }
130
131    /**
132     * Returns the maximum number of bytes this packager can produce.
133     * @return maximum packed length in bytes
134     */
135    public abstract int getMaxPackedLength();
136
137    /**
138     * Creates an {@link ISOComponent} instance appropriate for this packager.
139     * @param fieldNumber the field number to assign to the new component
140     * @return a new ISOComponent
141     */
142    public ISOComponent createComponent(int fieldNumber) {
143        return new ISOField (fieldNumber);
144    }
145    /**
146     * Packs the given component into a byte array.
147     * @param c - a component
148     * @return packed component
149     * @exception ISOException on packing error
150     */
151    public abstract byte[] pack (ISOComponent c) throws ISOException;
152
153    /**
154     * Unpacks a field from the binary image into the given component.
155     * @param c - the Component to unpack
156     * @param b - binary image
157     * @param offset - starting offset within the binary image
158     * @return consumed bytes
159     * @exception ISOException on unpacking error
160     */
161    public abstract int unpack (ISOComponent c, byte[] b, int offset)
162        throws ISOException;
163
164    /**
165     * Unpacks a field from an input stream into the given component.
166     * @param c  - the Component to unpack
167     * @param in - input stream
168     * @throws IOException on I/O failure
169     * @throws ISOException on unpacking error
170     */
171    public void unpack (ISOComponent c, InputStream in) 
172        throws IOException, ISOException
173    {
174        unpack (c, readBytes (in, getMaxPackedLength ()), 0);
175    }
176    /**
177     * Packs the component to an ObjectOutput stream.
178     * @param c   - the Component to pack
179     * @param out - output stream
180     * @throws ISOException on packing error
181     * @throws IOException on I/O failure
182     */
183    public void pack (ISOComponent c, ObjectOutput out) 
184        throws IOException, ISOException
185    {
186        out.write (pack (c));
187    }
188
189    /**
190     * Reads exactly {@code l} bytes from the input stream.
191     * @param in the input stream
192     * @param l the number of bytes to read
193     * @return byte array of length {@code l}
194     * @throws IOException on I/O failure or premature end of stream
195     */
196    protected byte[] readBytes (InputStream in, int l) throws IOException {
197        byte[] b = new byte [l];
198        int n = 0;
199        while (n < l) {
200            int count = in.read(b, n, l - n);
201            if (count < 0)
202                throw new EOFException();
203            n += count;
204        }
205        return b;
206    }
207}
208