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.header;
020
021import org.jpos.iso.ISOUtil;
022
023/**
024 * BASE1 header implementation.
025 * <pre>
026 *   0 hlen;         Fld  1: Header Length        1B      (Byte     0)
027 *   1 hformat;      Fld  2: Header Format        8N,bit  (Byte     1)
028 *   2 format;       Fld  3: Text Format          1B      (Byte     2)
029 *   3 len[2];       Fld  4: Total Message Length 2B      (Byte  3- 4)
030 *   5 dstId[3];     Fld  5: Destination Id       6N,BCD  (Byte  5- 7)
031 *   8 srcId[3];     Fld  6: Source Id            6N,BCD  (Byte  8-10)
032 *  11 rtCtl;        Fld  7: Round-Trip Ctrl Info 8N,bit  (Byte    11)
033 *  12 flags[2];     Fld  8: BASE I Flags        16N,bit  (Byte 12-13)
034 *  14 status[3];    Fld  9: Message Status Flags 24bits  (Byte 14-16)
035 *  17 batchNbr;     Fld 10: Batch Number        1B       (Byte    17)
036 *  18 reserved[3];  Fld 11: Reserved            3B       (Byte 18-20)
037 *  21 userInfo;     Fld 12: User Info           1B       (Byte    21)
038 *  The following fields are only presend in a reject message
039 *  22 bitmap;       Fld 13: Bitmap              2B       (Byte 22-23)
040 *  24 rejectdata;   Fld 14: Reject Data Group   2B       (Byte 24-25)
041 * </pre>
042 *
043 */
044public class BASE1Header extends BaseHeader {
045
046    private static final long serialVersionUID = 6466427524726021374L;
047    /** Fixed length of a BASE-1 header in bytes. */
048    public static final int LENGTH = 22;
049
050    /** Creates a BASE1Header with source and destination both set to {@code "000000"}. */
051    public BASE1Header() {
052        this("000000", "000000");
053    }
054    /** Creates a BASE1Header with the given source and destination and default format (2).
055     * @param source 6-digit source ID
056     * @param destination 6-digit destination ID
057     */
058    public BASE1Header(String source, String destination) {
059        super();
060        header = new byte[LENGTH];
061        header[0] = LENGTH; // hlen
062        setHFormat(1);
063        setFormat(2);
064        setSource(source);
065        setDestination(destination);
066    }
067    /** Creates a BASE1Header with explicit format.
068     * @param source 6-digit source ID
069     * @param destination 6-digit destination ID
070     * @param format header format code
071     */
072    public BASE1Header(String source, String destination, int format) {
073        super();
074        header = new byte[LENGTH];
075        header[0] = LENGTH; // hlen
076        setHFormat(1);
077        setFormat(format);
078        setSource(source);
079        setDestination(destination);
080    }
081    /** Creates a BASE1Header from a raw byte array.
082     * @param header raw header bytes
083     */
084    public BASE1Header(byte[] header) {
085        super(header);
086    }
087
088    /**
089     * Returns the header length field value.
090     * @return header length
091     */
092    public int getHLen() {
093        return header[0] & 0xFF;
094    }
095    /**
096     * Sets the header format byte.
097     * @param hformat header format value
098     */
099    public void setHFormat(int hformat) {
100        header[1] = (byte) hformat;
101    }
102    /**
103     * Returns the message format code.
104     * @return message format code
105     */
106    public int getFormat() {
107        return header[2] & 0xFF;
108    }
109    /**
110     * Sets the routing control byte.
111     * @param i routing control value
112     */
113    public void setRtCtl(int i) {
114        header[11] = (byte) i;
115    }
116    /**
117     * Sets the flags field.
118     * @param i flags value
119     */
120    public void setFlags(int i) {
121        header[12] = (byte) (i >> 8 & 0xFF);
122        header[13] = (byte) (i & 0xFF);
123    }
124    /**
125     * Sets the status field.
126     * @param i status value
127     */
128    public void setStatus(int i) {
129        header[14] = (byte) (i >> 16 & 0xFF);
130        header[15] = (byte) (i >> 8 & 0xFF);
131        header[16] = (byte) (i & 0xFF);
132    }
133    /**
134     * Sets the batch number.
135     * @param i batch number
136     */
137    public void setBatchNumber(int i) {
138        header[17] = (byte) (i & 0xFF);
139    }
140    /**
141     * Sets the message format code.
142     * @param format the message format code
143     */
144    public void setFormat(int format) {
145        header[2] = (byte) format;
146    }
147    /**
148     * Sets the message length field in the header.
149     * @param len the payload length (header length will be added automatically)
150     */
151    public void setLen(int len) {
152        len += header.length;
153        header[3]  = (byte) (len >> 8 & 0xff);
154        header[4]  = (byte) (len        & 0xff);
155    }
156    public void setDestination(String dest) {
157        byte[] d = ISOUtil.str2bcd(dest, true);
158        System.arraycopy(d, 0, header, 5, 3);
159    }
160    public void setSource(String src) {
161        byte[] d = ISOUtil.str2bcd(src, true);
162        System.arraycopy(d, 0, header, 8, 3);
163    }
164    public String getSource() {
165        return ISOUtil.bcd2str (this.header, 8, 6, false);
166    }
167    public String getDestination() {
168        return ISOUtil.bcd2str (this.header, 5, 6, false);
169    }
170    public void swapDirection() {
171        if (header != null && header.length >= LENGTH) {
172            byte[] source = new byte[3];
173            System.arraycopy(header, 8, source, 0, 3);
174            System.arraycopy(header, 5, header, 8, 3);
175            System.arraycopy(source, 0, header, 5, 3);
176        }
177    }
178    /**
179     * Returns true if this message is a BASE-1 reject.
180     * @return true if the message is rejected
181     */
182    public boolean isRejected() {
183        // Header length must be 26 or gerater
184        // And header field 13 bit 1 must be 1 (field 13 starts at byte 22)
185        return getLength() >= 26 && (header[22] & 0x80) == 0x80;
186    }
187        
188    /**
189     * Gets the BASE 1 Reject Code.
190     * 
191     * @return If the message is a reject return the Reject Code Otherwise return "" 
192     */ 
193    public String getRejectCode() {
194        return isRejected() ? ISOUtil.bcd2str (this.header, 24, 4, false) : "";
195    }
196
197    /*
198     * parse header contributed by santhoshvee@yahoo.co.uk in jpos-dev mailing list
199     */
200    /**
201     * Formats the header fields as a human-readable diagnostic string.
202     * @return formatted header dump
203     */
204    public String formatHeader() {
205        String h = ISOUtil.hexString(this.header);
206        String lf = System.getProperty("line.separator");
207        StringBuffer d = new StringBuffer();
208        d.append(lf);
209        d.append("[H 01] Header length         "); d.append(h.substring(0, 2));   d.append(lf);
210        d.append("[H 02] Header format         "); d.append(h.substring(2, 4));   d.append(lf);
211        d.append("[H 03] Text format           "); d.append(h.substring(4, 6));   d.append(lf);
212        d.append("[H 04] Total length          "); d.append(h.substring(6, 10));  d.append(lf);
213        d.append("[H 05] Destination ID        "); d.append(h.substring(10, 16)); d.append(lf);
214        d.append("[H 06] Source ID             "); d.append(h.substring(16, 22)); d.append(lf);
215        d.append("[H 07] Round-trip ctrl info  "); d.append(h.substring(22, 24)); d.append(lf);
216        d.append("[H 08] BASE I flags          "); d.append(h.substring(24, 28)); d.append(lf);
217        d.append("[H 09] Message status flags  "); d.append(h.substring(28, 34)); d.append(lf);
218        d.append("[H 10] Batch number          "); d.append(h.substring(34, 36)); d.append(lf);
219        d.append("[H 11] Reserved              "); d.append(h.substring(36, 42)); d.append(lf);
220        d.append("[H 12] User info             "); d.append(h.substring(42, 44)); d.append(lf);
221        if (isRejected()) {
222            d.append("[H 13] Bitmap                "); d.append(h.substring(44, 48)); d.append(lf);
223            d.append("[H 14] Reject data group     "); d.append(h.substring(48, 52)); d.append(lf);
224            d.append("Original header              "); d.append(h.substring(52)); d.append(lf);
225        }
226        return d.toString();
227    }
228}