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