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; 020 021import java.io.IOException; 022import java.io.InputStream; 023 024/** 025 * Generic class for handling binary fields in Tag-Len-Value format 026 * <code> 027 * Format is assemblied by header formatter 028 * Where 029 * TT is the n>0 digit field number (Tag) 030 * LL is the n>=0 digit field length (if n=0 it's means fixed length field with prefixer) 031 * .. is the field content 032 * </code> 033 * @author Mikolaj Sosna 034 * @version $Revision: 2854 $ $Date: 2010-01-02 11:34:31 +0100 (sob) $ 035 */ 036public class ISOFormattableBinaryFieldPackager extends ISOFieldPackager 037{ 038 private Prefixer tagPrefixer; 039 private BinaryInterpreter interpreter; 040 private Padder padder; 041 private Prefixer prefixer; 042 private IsoFieldHeaderFormatter headerFormatter; 043 044 /** 045 * Constructs a default ISOTagBinaryFieldPackager. There is ASCII tag L prefixer, no padding, 046 * no length prefix and a literal binary interpretation. The set methods must be called to 047 * make this ISOBaseFieldPackager useful. 048 */ 049 public ISOFormattableBinaryFieldPackager() { 050 super(); 051 this.tagPrefixer = AsciiPrefixer.L; 052 this.interpreter = LiteralBinaryInterpreter.INSTANCE; 053 this.padder = NullPadder.INSTANCE; 054 this.prefixer = NullPrefixer.INSTANCE; 055 this.headerFormatter = IsoFieldHeaderFormatter.TAG_FIRST; 056 } 057 058 /** 059 * Constructs an ISOTagBinaryFieldPackager with a specific Padder, Interpreter and Prefixer. 060 * The length and description should be set with setLength() and setDescription methods. 061 * @param tagPrefixer The type of tag prefixer used to encode tag. 062 * @param padder The type of padding used. 063 * @param interpreter The interpreter used to encode the field. 064 * @param prefixer The type of length prefixer used to encode this field. 065 */ 066 public ISOFormattableBinaryFieldPackager(Prefixer tagPrefixer, Padder padder, 067 BinaryInterpreter interpreter, Prefixer prefixer) { 068 super(); 069 this.tagPrefixer = tagPrefixer; 070 this.padder = padder; 071 this.interpreter = interpreter; 072 this.prefixer = prefixer; 073 this.headerFormatter = IsoFieldHeaderFormatter.TAG_FIRST; 074 } 075 076/** 077 * Constructs an ISOTagBinaryFieldPackager with a specific Padder, Interpreter and Prefixer. 078 * The length and description should be set with setLength() and setDescription methods. 079 * @param tagPrefixer The type of tag prefixer used to encode tag. 080 * @param padder The type of padding used. 081 * @param interpreter The interpreter used to encode the field. 082 * @param lengthPrefixer The type of length prefixer used to encode this field. 083 * @param headerFormatter The format of TAG TT and Length LL part 084 */ 085 public ISOFormattableBinaryFieldPackager(Prefixer tagPrefixer, Padder padder, 086 BinaryInterpreter interpreter, Prefixer lengthPrefixer, 087 IsoFieldHeaderFormatter headerFormatter) { 088 super(); 089 this.tagPrefixer = tagPrefixer; 090 this.padder = padder; 091 this.interpreter = interpreter; 092 this.prefixer = lengthPrefixer; 093 this.headerFormatter = headerFormatter; 094 } 095 096 /** 097 * Creates an ISOTagBinaryFieldPackager. 098 * @param maxLength The maximum length of the field in characters or bytes depending on the datatype. 099 * @param description The description of the field. For human readable output. 100 * @param tagPrefixer The type of tag prefixer used to encode tag. 101 * @param interpreter The interpreter used to encode the field. 102 * @param padder The type of padding used. 103 * @param lengthPrefixer The type of length prefixer used to encode this field. 104 * @param headerFormatter The format of TAG TT and Length LL part 105 */ 106 public ISOFormattableBinaryFieldPackager(int maxLength, String description, Prefixer tagPrefixer, 107 Padder padder, BinaryInterpreter interpreter, Prefixer lengthPrefixer, 108 IsoFieldHeaderFormatter headerFormatter) { 109 super(maxLength, description); 110 this.tagPrefixer = tagPrefixer; 111 this.padder = padder; 112 this.interpreter = interpreter; 113 this.prefixer = lengthPrefixer; 114 this.headerFormatter = headerFormatter; 115 } 116 117 /** 118 * Sets the Padder. 119 * @param padder The padder to use during packing and unpacking. 120 */ 121 public void setPadder(Padder padder) { 122 this.padder = padder; 123 } 124 125 /** 126 * Sets the Interpreter. 127 * @param interpreter The interpreter to use in packing and unpacking. 128 */ 129 public void setInterpreter(BinaryInterpreter interpreter) { 130 this.interpreter = interpreter; 131 } 132 133 /** 134 * Sets the length prefixer. 135 * @param prefixer The length prefixer to use during packing and unpacking. 136 */ 137 public void setPrefixer(Prefixer prefixer) { 138 this.prefixer = prefixer; 139 } 140 141 /** 142 * Gets the formatter, which assembles tag TT and length LL parts in required format 143 * @return the formatter of the header part (length and tag parts) 144 */ 145 public IsoFieldHeaderFormatter getHeaderFormatter() { 146 return headerFormatter; 147 } 148 149 /** 150 * Sets the formatter, which assembles tag TT and length LL parts in required format 151 * @param headerFormatter the formatter of the header part (length and tag parts) 152 */ 153 public void setHeaderFormatter(IsoFieldHeaderFormatter headerFormatter) { 154 this.headerFormatter = headerFormatter; 155 } 156 157 /** 158 * Returns the prefixer's packed length and the interpreter's packed length. 159 * @see ISOFieldPackager#getMaxPackedLength() 160 */ 161 @Override 162 public int getMaxPackedLength() { 163 return tagPrefixer.getPackedLength() + prefixer.getPackedLength() + interpreter.getPackedLength(getLength()); 164 } 165 166 /** 167 * Create a nice readable message for errors 168 */ 169 private String makeExceptionMessage(ISOComponent c, String operation) { 170 Object fieldKey = "unknown"; 171 if (c != null) 172 try{ 173 fieldKey = c.getKey(); 174 } catch (Exception ignore){} 175 return this.getClass().getName() + ": Problem " + operation + " field " + fieldKey; 176 } 177 178 /** 179 * Convert the component into a byte[]. 180 */ 181 @Override 182 public byte[] pack(ISOComponent c) throws ISOException { 183 try{ 184 byte[] valueBytes = (byte[])c.getValue(); 185 if (valueBytes.length < 0 || valueBytes.length > getLength()) 186 throw new ISOException("Field length " + valueBytes.length + " too long. Max: " + getLength()); 187 188 int tag = (Integer)c.getKey(); 189 byte[] paddedValueBytes = valueBytes; 190 191 if (!(padder instanceof NullPadder)) //for save few cycles 192 paddedValueBytes = ISOUtil.hex2byte(padder.pad(ISOUtil.hexString(valueBytes), getLength())); 193 194 byte[] rawData = new byte[tagPrefixer.getPackedLength() + prefixer.getPackedLength() + interpreter.getPackedLength(paddedValueBytes.length)]; 195 byte[] rawTagData = new byte[tagPrefixer.getPackedLength()]; 196 tagPrefixer.encodeLength(tag, rawTagData); 197 198 byte[] rawLen = new byte[prefixer.getPackedLength()]; 199 prefixer.encodeLength(!headerFormatter.isTagFirst() ? paddedValueBytes.length + tagPrefixer.getPackedLength() : paddedValueBytes.length, rawLen); 200 201 headerFormatter.format(tagPrefixer, prefixer, rawTagData, rawLen, rawData); 202 203 interpreter.interpret(paddedValueBytes, rawData, headerFormatter.getTotalLength(tagPrefixer, prefixer)); 204 205 return rawData; 206 } catch(Exception e) { 207 throw new ISOException(makeExceptionMessage(c, "packing"), e); 208 } 209 } 210 211 /** 212 * Unpacks the byte array into the component. 213 * @param c The component to unpack into. 214 * @param b The byte array to unpack. 215 * @param offset The index in the byte array to start unpacking from. 216 * @return The number of bytes consumed unpacking the component. 217 */ 218 @Override 219 public int unpack(ISOComponent c, byte[] b, int offset) throws ISOException { 220 try{ 221 int tagLen = tagPrefixer.getPackedLength(); 222 c.setFieldNumber(tagPrefixer.decodeLength(b, offset + headerFormatter.getTagIndex(prefixer))); 223 int len = prefixer.decodeLength(b, offset + headerFormatter.getLengthIndex(tagPrefixer)); 224 if (!headerFormatter.isTagFirst()) { 225 len -= tagPrefixer.getPackedLength(); 226 } 227 if (len == -1) { 228 // The prefixer doesn't know how long the field is, so use 229 // maxLength instead 230 len = getLength(); 231 } else if (getLength() > 0 && len > getLength()) { 232 throw new ISOException("Field length " + len + " too long. Max: " + getLength()); 233 } 234 int lenLen = prefixer.getPackedLength(); 235 byte[] unpacked = interpreter.uninterpret(b, offset + tagPrefixer.getPackedLength() + prefixer.getPackedLength(), len); 236 237 byte[] paddedValueBytes = unpacked; 238 if (!(padder instanceof NullPadder)) //for save few cycles 239 paddedValueBytes = ISOUtil.hex2byte(padder.unpad(ISOUtil.hexString(unpacked))); 240 241 c.setValue(paddedValueBytes); 242 return tagLen + lenLen + interpreter.getPackedLength(len); 243 } catch(Exception e){ 244 throw new ISOException(makeExceptionMessage(c, "unpacking"), e); 245 } 246 } 247 248 /** 249 * Unpack the input stream into the component. 250 * @param c The Component to unpack into. 251 * @param in Input stream where the packed bytes come from. 252 * @exception IOException Thrown if there's a problem reading the input stream. 253 */ 254 @Override 255 public void unpack (ISOComponent c, InputStream in) 256 throws IOException, ISOException { 257 258 try { 259 int tagLen = tagPrefixer.getPackedLength(); 260 int lenLen = prefixer.getPackedLength() == 0 ? getLength() : prefixer.getPackedLength(); 261 int len = -1; 262 if (headerFormatter.getTagIndex(prefixer) == 0) { 263 c.setFieldNumber(tagPrefixer.decodeLength(readBytes(in, tagLen), 0)); 264 len = prefixer.decodeLength(readBytes(in, lenLen), 0); 265 } else { 266 len = prefixer.decodeLength(readBytes(in, lenLen), 0); 267 c.setFieldNumber(tagPrefixer.decodeLength(readBytes(in, tagLen), 0)); 268 } 269 if (getLength() > 0 && len > 0 && len > getLength()) { 270 throw new ISOException("Field length " + len + " too long. Max: " + getLength()); 271 } 272 int packedLen = interpreter.getPackedLength(len); 273 byte[] unpacked = interpreter.uninterpret(readBytes (in, packedLen), 0, len); 274 c.setValue(unpacked); 275 } catch(ISOException e){ 276 throw new ISOException(makeExceptionMessage(c, "unpacking"), e); 277 } 278 } 279 280}