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