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