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.tlv.packager;
020
021import org.jpos.iso.ISOComponent;
022import org.jpos.iso.ISOException;
023import org.jpos.iso.ISOField;
024import org.jpos.iso.ISOFieldPackager;
025import org.jpos.iso.TaggedFieldPackager;
026
027import java.io.IOException;
028import java.io.InputStream;
029import java.nio.ByteBuffer;
030
031/**
032 * Field Separator Terminated packager
033 *
034 * @author Vishnu Pillai
035 */
036public class IF_FSTBINARY extends ISOFieldPackager implements TaggedFieldPackager {
037
038    private byte terminator = (byte) 0xFF;
039    private String token;
040
041    /** Default constructor. */
042    public IF_FSTBINARY() {
043        super();
044    }
045
046    @Override
047    public void setToken(String token) {
048        if (token == null || token.length() != 2) {
049            throw new IllegalArgumentException("IF_FSTBINARY needs a HEX token of 2 characters.");
050        }
051        this.token = token;
052        this.terminator = (byte) Integer.parseInt(token, 16);
053    }
054
055    @Override
056    public String getToken() {
057        return token;
058    }
059
060    /**
061     * Constructs a packager with the given length and description.
062     * @param len         - field len
063     * @param description symbolic descrption
064     */
065    public IF_FSTBINARY(int len, String description) {
066        super(len, description);
067    }
068
069    /**
070     * @param c - a component
071     * @return packed component
072     * @throws org.jpos.iso.ISOException on pack/unpack error
073     */
074    public byte[] pack(ISOComponent c) throws ISOException {
075        int len;
076        byte[] s = c.getBytes();
077
078        if ((len = s.length) > getLength()) {
079            throw new ISOException(
080                    "Invalid length " + len + " packing IF_FSTBINARY field "
081                            + c.getKey() + " max length=" + getLength()
082            );
083        }
084        byte[] b = new byte[s.length + 1];
085        System.arraycopy(s, 0, b, 0, s.length);
086        b[b.length - 2] = terminator;
087        return b;
088    }
089
090    /**
091     * @param c      - the Component to unpack
092     * @param b      - binary image
093     * @param offset - starting offset within the binary image
094     * @return consumed bytes
095     * @throws org.jpos.iso.ISOException on pack/unpack error
096     */
097    public int unpack(ISOComponent c, byte[] b, int offset)
098            throws ISOException {
099        if (!(c instanceof ISOField))
100            throw new ISOException
101                    (c.getClass().getName() + " is not an ISOField");
102        int length = -1;
103        for (int i = 0; i < getMaxPackedLength(); i++) {
104            byte dataByte = b[offset + i];
105            if (dataByte == terminator) {
106                length = i;
107                break;
108            }
109        }
110        if (length >= 0) {
111            byte[] value = new byte[length];
112            System.arraycopy(b, offset, value, 0, length);
113            c.setValue(value);
114            return length + 1;
115        } else {
116            throw new ISOException("Terminating Backslash does not exist");
117        }
118    }
119
120    public void unpack(ISOComponent c, InputStream in)
121            throws IOException, ISOException {
122
123        if (!(c instanceof ISOField))
124            throw new ISOException
125                    (c.getClass().getName() + " is not an ISOField");
126
127        boolean endFound = false;
128        if (in.markSupported()) {
129            in.mark(getMaxPackedLength());
130        }
131        ByteBuffer buf = ByteBuffer.allocate(getMaxPackedLength());
132
133        for (int i = 0; i < getMaxPackedLength() && in.available() > 0; i++) {
134            byte dataByte = (byte) in.read();
135            if (dataByte == terminator) {
136                endFound = true;
137                break;
138            } else {
139                buf.put(dataByte);
140            }
141        }
142        if (endFound) {
143            byte[] data = byteBufferToBytes(buf);
144            c.setValue(data);
145        } else {
146            if (in.markSupported()) {
147                in.reset();
148            }
149            throw new ISOException("Terminating Backslash does not exist");
150        }
151    }
152
153    private byte[] byteBufferToBytes(ByteBuffer buffer) {
154        int dataLength = buffer.position();
155        byte[] bytes = new byte[dataLength];
156        buffer.position(0);
157        buffer.get(bytes);
158        buffer.position(dataLength);
159        return bytes;
160    }
161
162    public int getMaxPackedLength() {
163        return getLength() + 1;
164    }
165}
166