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