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