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 ISOBinaryFieldPackager extends ISOFieldPackager 029{ 030 private BinaryInterpreter interpreter; 031 private Prefixer prefixer; 032 033 /** 034 * Constructs a default ISOBinaryFieldPackager. There is no length prefix and a 035 * literal interpretation. The set methods must be called to make this 036 * ISOBinaryFieldPackager useful. 037 */ 038 public ISOBinaryFieldPackager() 039 { 040 super(); 041 this.interpreter = LiteralBinaryInterpreter.INSTANCE; 042 this.prefixer = NullPrefixer.INSTANCE; 043 } 044 045 /** 046 * Creates an ISOBinaryFieldPackager. 047 * @param maxLength The maximum length of the field in characters or bytes depending on the datatype. 048 * @param description The description of the field. For human readable output. 049 * @param interpreter The interpreter used to encode the field. 050 * @param prefixer The type of length prefixer used to encode this field. 051 */ 052 public ISOBinaryFieldPackager(int maxLength, String description, 053 BinaryInterpreter interpreter, Prefixer prefixer) 054 { 055 super(maxLength, description); 056 this.interpreter = interpreter; 057 this.prefixer = prefixer; 058 } 059 060 /** 061 * Creates an ISOBinaryFieldPackager. 062 * @param interpreter The interpreter used to encode the field. 063 * @param prefixer The type of length prefixer used to encode this field. 064 */ 065 public ISOBinaryFieldPackager(BinaryInterpreter interpreter, Prefixer prefixer) 066 { 067 super(); 068 this.interpreter = interpreter; 069 this.prefixer = prefixer; 070 } 071 072 /** 073 * Sets the Interpreter. 074 * @param interpreter The interpreter to use in packing and unpacking. 075 */ 076 public void setInterpreter(BinaryInterpreter interpreter) 077 { 078 this.interpreter = interpreter; 079 } 080 081 /** 082 * Sets the length prefixer. 083 * @param prefixer The length prefixer to use during packing and unpacking. 084 */ 085 public void setPrefixer(Prefixer prefixer) 086 { 087 this.prefixer = prefixer; 088 } 089 090 public int getMaxPackedLength() 091 { 092 return prefixer.getPackedLength() + interpreter.getPackedLength(getLength()); 093 } 094 095 /** 096 * Convert the component into a byte[]. 097 */ 098 public byte[] pack(ISOComponent c) throws ISOException 099 { 100 try 101 { 102 byte[] data = c.getBytes(); 103 int packedLength = prefixer.getPackedLength(); 104 if (packedLength == 0 && data.length != getLength()) { 105 throw new ISOException("Binary data length not the same as the packager length (" + data.length + "/" + getLength() + ")"); 106 } 107 byte[] ret = new byte[interpreter.getPackedLength(data.length) + packedLength]; 108 prefixer.encodeLength(data.length, ret); 109 interpreter.interpret(data, ret, packedLength); 110 return ret; 111 } catch(Exception e) { 112 throw new ISOException(makeExceptionMessage(c, "packing"), e); 113 } 114 } 115 116 public int unpack(ISOComponent c, byte[] b, int offset) throws ISOException 117 { 118 try 119 { 120 int len = prefixer.decodeLength(b, offset); 121 if (len == -1) { 122 // The prefixer doesn't know how long the field is, so use 123 // maxLength instead 124 len = getLength(); 125 } 126 else if (getLength() > 0 && len > getLength()) 127 throw new ISOException("Field length " + len + " too long. Max: " + getLength()); 128 int lenLen = prefixer.getPackedLength(); 129 byte[] unpacked = interpreter.uninterpret(b, offset + lenLen, len); 130 c.setValue(unpacked); 131 return lenLen + interpreter.getPackedLength(len); 132 } catch(Exception e) 133 { 134 throw new ISOException(makeExceptionMessage(c, "unpacking"), e); 135 } 136 } 137 138 /** Unpack from an input stream */ 139 public void unpack (ISOComponent c, InputStream in) 140 throws IOException, ISOException 141 { 142 try 143 { 144 int lenLen = prefixer.getPackedLength (); 145 int len; 146 if (lenLen == 0) 147 { 148 len = getLength(); 149 } else 150 { 151 len = prefixer.decodeLength (readBytes (in, lenLen), 0); 152 if (getLength() > 0 && len > 0 && len > getLength()) 153 throw new ISOException("Field length " + len + " too long. Max: " + getLength()); 154 } 155 int packedLen = interpreter.getPackedLength(len); 156 byte[] unpacked = interpreter.uninterpret(readBytes (in, packedLen), 0, len); 157 c.setValue(unpacked); 158 } catch(ISOException e) 159 { 160 throw new ISOException(makeExceptionMessage(c, "unpacking"), e); 161 } 162 } 163 164 /** 165 * component factory 166 * @param fieldNumber - the field number 167 * @return the newly created component 168 */ 169 public ISOComponent createComponent(int fieldNumber) { 170 return new ISOBinaryField (fieldNumber); 171 } 172 173 /** Create a nice readable message for errors */ 174 private String makeExceptionMessage(ISOComponent c, String operation) { 175 Object fieldKey = "unknown"; 176 if (c != null) 177 { 178 try 179 { 180 fieldKey = c.getKey(); 181 } catch (Exception ignore) 182 { 183 } 184 } 185 return getClass().getName() + ": Problem " + operation + " field " + fieldKey; 186 } 187 188 189 /** 190 * Checks the length of the data against the maximum, and throws an IllegalArgumentException. 191 * This is designed to be called from field Packager constructors and the setLength() 192 * method. 193 * @param len The length of the data for this field packager. 194 * @param maxLength The maximum length allowed for this type of field packager. 195 * This depends on the prefixer that is used. 196 * @throws IllegalArgumentException If len > maxLength. 197 */ 198 protected void checkLength(int len, int maxLength) throws IllegalArgumentException 199 { 200 if (len > maxLength) 201 { 202 throw new IllegalArgumentException("Length " + len + " too long for " + getClass().getName()); 203 } 204 } 205}