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 * @author joconnor 026 * @version $Revision$ $Date$ 027 */ 028public class ISOStringFieldPackager extends ISOFieldPackager 029{ 030 private Interpreter interpreter; 031 private Padder padder; 032 private Prefixer prefixer; 033 034 /** 035 * Constructs a default ISOStringFieldPackager. There is no padding, 036 * no length prefix and a literal interpretation. The set methods must be called to 037 * make this ISOBaseFieldPackager useful. 038 */ 039 public ISOStringFieldPackager() 040 { 041 super(); 042 this.padder = NullPadder.INSTANCE; 043 this.interpreter = LiteralInterpreter.INSTANCE; 044 this.prefixer = NullPrefixer.INSTANCE; 045 } 046 047 /** 048 * Constructs an ISOStringFieldPackager with a specific Padder, Interpreter and Prefixer. 049 * The length and description should be set with setLength() and setDescription methods. 050 * @param padder The type of padding used. 051 * @param interpreter The interpreter used to encode the field. 052 * @param prefixer The type of length prefixer used to encode this field. 053 */ 054 public ISOStringFieldPackager(Padder padder, Interpreter interpreter, Prefixer prefixer) 055 { 056 super(); 057 this.padder = padder; 058 this.interpreter = interpreter; 059 this.prefixer = prefixer; 060 } 061 062 /** 063 * Creates an ISOStringFieldPackager. 064 * @param maxLength The maximum length of the field in characters or bytes depending on the datatype. 065 * @param description The description of the field. For human readable output. 066 * @param interpreter The interpreter used to encode the field. 067 * @param padder The type of padding used. 068 * @param prefixer The type of length prefixer used to encode this field. 069 */ 070 public ISOStringFieldPackager(int maxLength, String description, Padder padder, 071 Interpreter interpreter, Prefixer prefixer) 072 { 073 super(maxLength, description); 074 this.padder = padder; 075 this.interpreter = interpreter; 076 this.prefixer = prefixer; 077 } 078 079 /** 080 * Sets the Padder. 081 * @param padder The padder to use during packing and unpacking. 082 */ 083 public void setPadder(Padder padder) 084 { 085 this.padder = padder; 086 } 087 088 /** 089 * Sets the Interpreter. 090 * @param interpreter The interpreter to use in packing and unpacking. 091 */ 092 public void setInterpreter(Interpreter interpreter) 093 { 094 this.interpreter = interpreter; 095 } 096 097 /** 098 * Sets the length prefixer. 099 * @param prefixer The length prefixer to use during packing and unpacking. 100 */ 101 public void setPrefixer(Prefixer prefixer) 102 { 103 this.prefixer = prefixer; 104 } 105 106 /** 107 * Returns the prefixer's packed length and the interpreter's packed length. 108 */ 109 public int getMaxPackedLength() 110 { 111 return prefixer.getPackedLength() + interpreter.getPackedLength(getLength()); 112 } 113 114 /** Create a nice readable message for errors */ 115 private String makeExceptionMessage(ISOComponent c, String operation) { 116 Object fieldKey = "unknown"; 117 if (c != null) 118 { 119 try 120 { 121 fieldKey = c.getKey(); 122 } catch (Exception ignore) 123 { 124 } 125 } 126 return this.getClass().getName() + ": Problem " + operation + " field " + fieldKey; 127 } 128 129 /** 130 * Convert the component into a byte[]. 131 * @return byte array representation of component 132 * @throws org.jpos.iso.ISOException 133 */ 134 @Override 135 public byte[] pack(ISOComponent c) throws ISOException 136 { 137 try 138 { 139 String data; 140 if(c.getValue() instanceof byte[]) 141 data = new String(c.getBytes(), ISOUtil.CHARSET); // transparent handling of complex fields 142 else 143 data = (String)c.getValue(); 144 145 if (data.length() > getLength()) 146 { 147 throw new ISOException("Field length " + data.length() + " too long. Max: " + getLength()); 148 } 149 String paddedData = padder.pad(data, getLength()); 150 byte[] rawData = new byte[prefixer.getPackedLength() 151 + interpreter.getPackedLength(paddedData.length())]; 152 prefixer.encodeLength(paddedData.length(), rawData); 153 interpreter.interpret(paddedData, rawData, prefixer.getPackedLength()); 154 return rawData; 155 } catch(Exception e) 156 { 157 throw new ISOException(makeExceptionMessage(c, "packing"), e); 158 } 159 } 160 161 /** 162 * Unpacks the byte array into the component. 163 * @param c The component to unpack into. 164 * @param b The byte array to unpack. 165 * @param offset The index in the byte array to start unpacking from. 166 * @return The number of bytes consumed unpacking the component. 167 */ 168 public int unpack(ISOComponent c, byte[] b, int offset) throws ISOException 169 { 170 try 171 { 172 int len = prefixer.decodeLength(b, offset); 173 if (len == -1) { 174 // The prefixer doesn't know how long the field is, so use 175 // maxLength instead 176 len = trim ? Math.min(getLength(),b.length-offset) : getLength(); 177 } 178 else if (getLength() > 0 && len > getLength()) 179 throw new ISOException("Field length " + len + " too long. Max: " + getLength()); 180 181 int lenLen = prefixer.getPackedLength(); 182 String unpacked = interpreter.uninterpret(b, offset + lenLen, len); 183 c.setValue(unpacked); 184 return lenLen + interpreter.getPackedLength(len); 185 } catch(Exception e) 186 { 187 throw new ISOException(makeExceptionMessage(c, "unpacking"), e); 188 } 189 } 190 191 /** 192 * Unpack the input stream into the component. 193 * @param c The Component to unpack into. 194 * @param in Input stream where the packed bytes come from. 195 * @exception IOException Thrown if there's a problem reading the input stream. 196 */ 197 public void unpack (ISOComponent c, InputStream in) 198 throws IOException, ISOException 199 { 200 try 201 { 202 int lenLen = prefixer.getPackedLength (); 203 int len; 204 if (lenLen == 0) 205 { 206 len = getLength(); 207 } else 208 { 209 len = prefixer.decodeLength (readBytes (in, lenLen), 0); 210 if (getLength() > 0 && len > 0 && len > getLength()) 211 throw new ISOException("Field length " + len + " too long. Max: " + getLength()); 212 } 213 int packedLen = interpreter.getPackedLength(len); 214 String unpacked = interpreter.uninterpret(readBytes (in, packedLen), 0, len); 215 c.setValue(unpacked); 216 } catch(ISOException e) 217 { 218 throw new ISOException(makeExceptionMessage(c, "unpacking"), e); 219 } 220 } 221 222 @Override 223 public void setTrim (boolean trim) { 224 super.setTrim (trim); 225 if (trim) 226 padder = NullPadder.INSTANCE; // no padding 227 } 228 229 /** 230 * Checks the length of the data against the maximum, and throws an IllegalArgumentException. 231 * This is designed to be called from field Packager constructors and the setLength() 232 * method. 233 * @param len The length of the data for this field packager. 234 * @param maxLength The maximum length allowed for this type of field packager. 235 * This depends on the prefixer that is used. 236 * @throws IllegalArgumentException If len > maxLength. 237 */ 238 protected void checkLength(int len, int maxLength) throws IllegalArgumentException 239 { 240 if (len > maxLength) 241 { 242 throw new IllegalArgumentException("Length " + len + " too long for " + getClass().getName()); 243 } 244 } 245}