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