package center.system.msg;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.util.Map;
import java.util.TreeMap;
import javax.xml.crypto.Data;
import ru.vassaev.core.exception.SysException;
import ru.vassaev.core.io.CloseableOutputStream;
import ru.vassaev.core.util.Strings;
/**
* Формат поля ISO8583
*
* @author Vassaev A.V.
* @version 1.0 21/08/2012
*/
public class ISO8583Field extends CloseableOutputStream {
private static final String charset = "ibm866";
// Буфер для поля
private byte[] dta = null;
private boolean left = false;
// Длина буфера для длины поля в байтах
private final int vl;
// Максимальная длина поля в байтах
private final int maxl;
// Длина длины подполя в байтах или в полубайтах
private final int fl;
// Длина кода подполя в байтах или в полубайтах
private final int fc;
// Длина в 16-ричной кодировке
public final boolean isHexH;
// Тело поля в 16-ричной кодировке
public final boolean isHexB;
// Наименование поля
public final String name;
public final boolean bytes;
/**
* Конструктор
*
* @param name
* - наименование поля
* @param frmt
* - формат, заданный строкой 12 - поле 12 байт 12H - поле 12
* байт содержит 24 знака из множества цифр 10-ричной системы
* счислени LL12 - поле максимальной длины 12 байтов, поле длины
* - 2 байта в буквенной-цифровом формате LL12H - поле
* максимальной длины 12 знака из множества цифр 10-рично системы
* счисления (6 байтов), поле длины - 2 байта в
* буквенной-цифровом формате HH11H - поле максимальной длины 6
* (=11/2) байт содержит 11 знаков из множества цифр 10-рично
* системы счисления. поле длины - 2 знака и 1 байт HHH11 - поле
* длины - 3 знака и 2 байта, максимальная длина - 11 знаков
*
* HH99[LLCC] LLL120[LLCC]H
*
*/
public ISO8583Field(String name, String frmt) {
this.name = name;
int l = frmt.length();
int vl = 0;
int i = 0;
boolean isHexH = false;
char ch;
// Читаем информацию о буфере для длины
for (; i < l; i++) {
ch = frmt.charAt(i);
if (ch == 'L')
vl++;
else if (ch == 'H') {
vl++;
isHexH = true;
} else
break;
}
this.isHexH = isHexH;
this.vl = vl;
ch = frmt.charAt(l - 1);
this.isHexB = (l > 1 && (ch == 'H' || ch == 'h' || ch == 'B'));
if (ch == 'h')
left = true;
bytes = (ch == 'B');
i = frmt.indexOf('[', i);
if (i > vl) {
this.maxl = Integer.parseInt(frmt.substring(vl, i));
i++;
int fl_ = 0;
int fc_ = 0;
for (; i < l; i++) {
ch = frmt.charAt(i);
switch (ch) {
case 'L':
fl_++;
break;
case 'C':
fc_++;
break;
}
if (ch == ']')
break;
}
fc = fc_;
fl = fl_;
} else {
this.maxl = Integer.parseInt(frmt.substring(vl, l
- ((isHexB) ? 1 : 0)));
fc = 0;
fl = 0;
}
int fl = ((isHexH) ? vl / 2 + vl % 2 : vl)
+ ((isHexB && !bytes) ? maxl / 2 + maxl % 2 : maxl);
this.dta = new byte[fl];
reset();
}
/**
* Получить значение поля
*
* @return Значение в виде строки
*/
public String getValue() throws SysException {
int hl = (isHexH) ? vl / 2 + vl % 2 : vl;
int bl = getFullLengthByte();
if (bl == 0)
return null;
if (isHexB) {
StringBuffer sb = new StringBuffer();
for (int i = hl; i < bl; i++) {
String v = Integer.toHexString(dta[i] & 0xff).toUpperCase();
if (v.length() == 1)
sb.append('0');
sb.append(v);
}
if (getLengthValue() % 2 == 0)
return sb.toString();
if (left)
return sb.substring(0, sb.length() - 1);
return sb.substring(1);
} else
try {
return new String(dta, hl, bl - hl, charset);
} catch (UnsupportedEncodingException e) {
throw new SysException(e);
}
}
/**
* Установить значение поля
*
* @param value
* - устанавливаемое значение
* @throws SysException
*/
public void setValue(String value) throws SysException {
int l = value.length();
if (l / (bytes ? 2 : 1) > maxl)
throw new SysException("Inequality data and format for field \""
+ name + "\"");
if ((vl == 0 && l / (bytes ? 2 : 1) < maxl) || (vl > 0 & l / (bytes ? 2 : 1) >= Math.pow(10, vl)))
throw new SysException("Inequality data and format for field \""
+ name + "\"");
// Записать тело
if (isHexB) { // TODO
int i = isHexH ? vl / 2 + vl % 2 : vl;
int j = 0;
int k = 0;
if (l % 2 != 0 && !left) {
dta[i] = (byte) (Byte.parseByte(value.substring(j, j + 1), 16) & 0xff);
k = 1;
}
for (; j + k < l; j++) {
int b = Byte.parseByte(value.substring(j + k, j + k + 1), 16) << 4;
j++;
if (j + k < l)
b = b
| Byte.parseByte(value.substring(j + k, j + k + 1),
16);
dta[i + k + (j >> 1)] = (byte) (0xff & b);
}
} else {
int i = isHexH ? vl / 2 + vl % 2 : vl;
byte[] buf;
try {
buf = value.getBytes(charset);
} catch (UnsupportedEncodingException e) {
throw new SysException(e);
}
System.arraycopy(buf, 0, dta, i, buf.length);
}
// Установка длины
if (vl > 0) {
String ls = Strings.lpad(String.valueOf(l / (bytes ? 2 : 1)), vl, '0');
if (isHexH) {
int j = 0;
int k = 0;
if (vl % 2 != 0) {
dta[0] = (byte) (Byte.parseByte(ls.substring(j, j + 1), 16) & 0xff);
k = 1;
}
for (; j + k < vl; j++) {
int b = Byte.parseByte(ls.substring(j + k, j + k + 1), 16) << 4;
j++;
b = b | Byte.parseByte(ls.substring(j + k, j + k + 1), 16);
dta[k + j >> 1] = (byte) (0xff & b);
}
} else {
int j = 0;
for (; j < vl; j++)
dta[j] = (byte) (0xff & ls.charAt(j));
}
}
}
/**
* Возвращает количество знаков в значении поля
*
* @return Количество знаков
*/
public int getLengthValue() {
if (vl > 0) {
if (isHexH) {
StringBuffer sb = new StringBuffer();
for (int k = 0; k < vl / 2 + vl % 2; k++) {
String v = Integer.toHexString(dta[k] & 0xff);
if (v.length() == 1)
sb.append('0');
sb.append(v);
}
return (Integer.parseInt(sb.toString()) * (bytes ? 2 : 1));
}
String ls = new String(dta, 0, vl);
return Integer.parseInt(ls) * (bytes ? 2 : 1);
}
return maxl;
}
/**
* Возвращает полную длину поля в байтах (включая место под длину)
*
* @return длина в байтах
*/
public int getFullLengthByte() {
if (vl > 0) {
if (isHexH) {
StringBuffer sb = new StringBuffer();
for (int k = 0; k < vl / 2 + vl % 2; k++) {
String v = Integer.toHexString(dta[k] & 0xff);
if (v.length() == 1)
sb.append('0');
sb.append(v);
}
int lb = Integer.parseInt(sb.toString()) * (bytes ? 2 : 1);
if (isHexB)
return lb / 2 + lb % 2 + vl / 2 + vl % 2;
return lb + vl / 2 + vl % 2;
}
String ls = new String(dta, 0, vl);
int lb = Integer.parseInt(ls);
if (isHexB)
return lb / 2 + lb % 2 + vl;
return lb + vl;
} else {
if (isHexB)
return maxl / 2 + maxl % 2;
return maxl;
}
}
/**
* Получить данные для передачи по сети
*
* @return массив байт
*/
public byte[] getData() {
if (vl > 0) {
int l = getFullLengthByte();
byte[] res = new byte[l];
System.arraycopy(dta, 0, res, 0, l);
return res;
} else
return dta;
}
/**
* Получить данные для отображения в 16-ричном формате
*
* @return строка данных
*/
public String getDataStr16() {
int l = getFullLengthByte();
StringBuffer sb = new StringBuffer();
for (int i = 0; i < l; i++) {
String b = Integer.toHexString(0xff & dta[i]).trim();
if (b.length() == 1)
sb.append('0');
sb.append(b);
if (i < l - 1)
sb.append(' ');
}
return sb.toString();
}
/**
* Получить формат
*/
public String toString() {
StringBuffer s = new StringBuffer();
char ch = (isHexH) ? 'H' : 'L';
for (int i = 0; i < vl; i++)
s.append(ch);
s.append(maxl);
if (fl > 0 || fc > 0) {
s.append('[');
for (int i = 0; i < fl; i++)
s.append('L');
for (int i = 0; i < fc; i++)
s.append('C');
s.append(']');
}
if (isHexB)
if (left)
s.append('h');
else if (bytes)
s.append('B');
else
s.append('H');
return s.toString();
}
/**
* Получить значение тега
*
* @param tag
* - имя тега
* @return Значение тега
* @throws SysException
*/
public String getTag(String tag) throws SysException {
return getTagOfField(getValue(), tag, fl, fc);
}
/**
* Установить значение тега
*
* @param tag
* - имя тега
* @param val
* - значение тега
* @throws SysException
*/
public void setTag(String tag, String val) throws SysException {
String value = setTagOfField(getValue(), tag, val, fl, fc);
setValue(value);
}
/**
* Получить список всех тегов и их значения
*
* @return множество тегов
* @throws SysException
*/
public Map<String, Object> getTags() throws SysException {
return getTagsOfField(getValue(), fl, fc);
}
/**
* Чтение тега из поля
*
* @param fld
* - поле
* @param tag
* - имя тега
* @param fl
* - длина длины поля
* @param fc
* - длина кода поля
* @return значение
* @throws ru.vassaev.core.exception.SysException
* - общая ошибка
*/
public static String getTagOfField(String fld, String tag, int fl, int fc) {
int len = fld.length();
try {
String t;
String v;
int k = 0;
do {
int i = k;
k = i + fl;
t = fld.substring(i, k); // Длина
i = k;
k = i + fc;
int l = Integer.parseInt(fld.substring(i, k));// Код
i = k;
k = i + l;
v = fld.substring(i, k); // Значение
} while ((!tag.equals(t)) && (k < len));
if (tag.equals(t))
return v;
return null;
} catch (NumberFormatException ex) {
return null;
} catch (ArrayIndexOutOfBoundsException ex) {
return null;
}
}
/**
* Установить тег в поле
*
* @param fld
* - имя поля
* @param tag
* - имя тега
* @param val
* - значение
* @throws SysException
* - общая ошибка
*/
public static String setTagOfField(String fld, String tag, String val,
int fl, int fc) throws SysException {
int lv = val.length();
try {
// Получить длину поля
int len = fld.length();
StringBuffer res = new StringBuffer();
String t = "";
String v = "";
String t1 = "";
int k = 0;
if (k < len)
do {
res.append(t).append(t1).append(v);
int i = k;
k = i + fl;
t = fld.substring(i, k);
i = k;
k = i + fc;
int l = Integer.parseInt(t);
t1 = fld.substring(i, k);
i = k;
k = i + l - fc;
v = fld.substring(i, k);
} while ((!tag.equals(t1)) && (k < len));
if (!tag.equals(t1))
res.append(t).append(t1).append(v);
if (lv > 0)
res.append(Strings.lpad(String.valueOf(lv + fc), fl, '0'))
.append(Strings.lpad(String.valueOf(tag), fc, '0'))
.append(val);
if (k < len)
res.append(fld.substring(k));
return res.toString();
} catch (NumberFormatException ex) {
throw new SysException("This field is not tag field");
} catch (ArrayIndexOutOfBoundsException ex) {
throw new SysException("This field is not tag field");
}
}
/**
* Получить список тегов в поле
*
* @param name
* - имя
* @throws SysException
* - ошибка разбора
* @return Таблицу тегов
*/
public Map<String, Object> getTagsOfField(String fld, int fl, int fc)
throws SysException {
try {
int len = fld.length();
Map<String, Object> res = new TreeMap<String, Object>();
String t;
String v;
int k = 0;
do {
int i = k;
k = i + fl;
t = fld.substring(i, k);// Имя тега
i = k;
k = i + fc;
int l = Integer.parseInt(fld.substring(i, k));// длина тега
i = k;
k = i + l;
v = fld.substring(i, k);// значение тега
res.put(t, v);
} while (k < len);
return res;
} catch (NumberFormatException ex) {
throw new SysException("This field is not tag field");
} catch (ArrayIndexOutOfBoundsException ex) {
throw new SysException("This field is not tag field");
}
}
// Текущая позиция для записи следующего байта из потока
private int p = 0;
public void add(byte val) {
dta[p] = val;
p++;
}
public void reset() {
p = 0;
if (isHexH)
for (int i = 0; i < vl / 2 + vl % 2; i++)
dta[i] = 0;
else
for (int i = 0; i < vl; i++)
dta[i] = '0';
}
/**
* Возвращает текущую позиция для записи следующего байта
*
* @return текущая позиция для записи следующего байта из потока
*/
public int getPosition() {
return p;
}
public boolean isClosed() throws IOException {
return (p >= getFullLengthByte());
}
public void write(int b) throws IOException {
add((byte) (b & 0xff));
}
public static void main(String[] args) throws SysException {
ISO8583Field fld = new ISO8583Field("55", "HHH255B");
fld.setValue("9F2608C4C63949AA4CB87A9F2701009F100706010A0321A0049F370468C6C6019F3602001F950508000810009A031309309C01009F02060000000005005F2A0206439F1A0208409F03060000000000009F3303E0F8C84F07A00000000310109F34035E03009F3501229F1E0831323334353637389F5301528407A00000000310109F0902008C9F410400000001");
System.out.println(fld.getValue());
byte[] d = fld.getData();
int l = d.length;
System.out.println("Length = " + l);
for (int i = 0; i < l; i++)
System.out.print(Integer.toString(d[i] & 0xff, 16) + " ");
}
}