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.math.BigDecimal; 022import java.nio.ByteBuffer; 023import java.nio.charset.Charset; 024import java.nio.charset.StandardCharsets; 025import java.text.DecimalFormat; 026import java.time.Duration; 027import java.util.ArrayList; 028import java.util.Arrays; 029import java.util.BitSet; 030import java.util.List; 031import java.util.Random; 032import java.util.StringJoiner; 033import java.util.StringTokenizer; 034import java.util.concurrent.locks.LockSupport; 035import java.util.regex.Pattern; 036import java.util.stream.Collectors; 037 038/** 039 * various functions needed to pack/unpack ISO-8583 fields 040 * 041 * @author apr@jpos.org 042 * @author Hani S. Kirollos 043 * @author Alwyn Schoeman 044 * @author Federico Gonzalez 045 * @version $Id$ 046 * @see ISOComponent 047 */ 048@SuppressWarnings("unused") 049public class ISOUtil { 050 /** 051 * All methods in this class are static, so there's usually no need to instantiate it 052 * We provide this public constructor in order to deal with some legacy script integration 053 * that needs an instance of this class in a rendering context. 054 */ 055 public ISOUtil() { 056 super(); 057 } 058 public static final String[] hexStrings; 059 060 static { 061 hexStrings = new String[256]; 062 for (int i = 0; i < 256; i++ ) { 063 StringBuilder d = new StringBuilder(2); 064 char ch = Character.forDigit((byte)i >> 4 & 0x0F, 16); 065 d.append(Character.toUpperCase(ch)); 066 ch = Character.forDigit((byte)i & 0x0F, 16); 067 d.append(Character.toUpperCase(ch)); 068 hexStrings[i] = d.toString(); 069 } 070 071 } 072 073 /** 074 * Default encoding (charset) for bytes transmissions over network 075 * @deprecated use {@link #CHARSET} instead 076 */ 077 @Deprecated 078 public static final String ENCODING = "ISO8859_1"; 079 public static final Pattern unicodePattern = Pattern.compile("u00([0-9a-fA-F]{2})+"); 080 081 /** 082 * Default charset for bytes transmissions over network 083 */ 084 public static final Charset CHARSET = StandardCharsets.ISO_8859_1; 085 public static final Charset EBCDIC = Charset.forName("IBM1047"); 086 087 public static final byte STX = 0x02; 088 public static final byte FS = 0x1C; 089 public static final byte US = 0x1F; 090 public static final byte RS = 0x1D; 091 public static final byte GS = 0x1E; 092 public static final byte ETX = 0x03; 093 094 public static String ebcdicToAscii(byte[] e) { 095 return EBCDIC.decode(ByteBuffer.wrap(e)).toString(); 096 } 097 public static String ebcdicToAscii(byte[] e, int offset, int len) { 098 return EBCDIC.decode(ByteBuffer.wrap(e, offset, len)).toString(); 099 } 100 public static byte[] ebcdicToAsciiBytes (byte[] e) { 101 return ebcdicToAsciiBytes (e, 0, e.length); 102 } 103 public static byte[] ebcdicToAsciiBytes (byte[] e, int offset, int len) { 104 return ebcdicToAscii(e, offset, len).getBytes(CHARSET); 105 } 106 public static byte[] asciiToEbcdic(String s) { 107 return EBCDIC.encode(s).array(); 108 } 109 public static byte[] asciiToEbcdic(byte[] a) { 110 return EBCDIC.encode(new String(a, CHARSET)).array(); 111 } 112 public static void asciiToEbcdic(String s, byte[] e, int offset) { 113 System.arraycopy (asciiToEbcdic(s), 0, e, offset, s.length()); 114 } 115 public static void asciiToEbcdic(byte[] s, byte[] e, int offset) { 116 asciiToEbcdic(new String(s, CHARSET), e, offset); 117 } 118 119 /** 120 * pad to the left 121 * @param s - original string 122 * @param len - desired len 123 * @param c - padding char 124 * @return padded string 125 * @throws ISOException on error 126 */ 127 public static String padleft(String s, int len, char c) 128 throws ISOException 129 { 130 s = s.trim(); 131 if (s.length() > len) 132 throw new ISOException("invalid len " +s.length() + "/" +len); 133 StringBuilder d = new StringBuilder (len); 134 int fill = len - s.length(); 135 while (fill-- > 0) 136 d.append (c); 137 d.append(s); 138 return d.toString(); 139 } 140 141 /** 142 * pad to the right 143 * 144 * @param s - 145 * original string 146 * @param len - 147 * desired len 148 * @param c - 149 * padding char 150 * @return padded string 151 * @throws ISOException if String's length greater than pad length 152 */ 153 public static String padright(String s, int len, char c) throws ISOException { 154 s = s.trim(); 155 if (s.length() > len) 156 throw new ISOException("invalid len " + s.length() + "/" + len); 157 StringBuilder d = new StringBuilder(len); 158 int fill = len - s.length(); 159 d.append(s); 160 while (fill-- > 0) 161 d.append(c); 162 return d.toString(); 163 } 164 165 /** 166 * trim String (if not null) 167 * @param s String to trim 168 * @return String (may be null) 169 */ 170 public static String trim (String s) { 171 return s != null ? s.trim() : null; 172 } 173 174 /** 175 * left pad with '0' 176 * @param s - original string 177 * @param len - desired len 178 * @return zero padded string 179 * @throws ISOException if string's length greater than len 180 */ 181 public static String zeropad(String s, int len) throws ISOException { 182 return padleft(s, len, '0'); 183 } 184 185 /** 186 * zeropads a long without throwing an ISOException (performs modulus operation) 187 * 188 * @param l the long 189 * @param len the length 190 * @return zeropadded value 191 */ 192 public static String zeropad(long l, int len) { 193 try { 194 return padleft (Long.toString ((long) (l % Math.pow (10, len))), len, '0'); 195 } catch (ISOException ignored) { } 196 return null; // should never happen 197 } 198 199 /** 200 * pads to the right 201 * @param s - original string 202 * @param len - desired len 203 * @return space padded string 204 */ 205 public static String strpad(String s, int len) { 206 StringBuilder d = new StringBuilder(s); 207 while (d.length() < len) 208 d.append(' '); 209 return d.toString(); 210 } 211 public static String zeropadRight (String s, int len) { 212 StringBuilder d = new StringBuilder(s); 213 while (d.length() < len) 214 d.append('0'); 215 return d.toString(); 216 } 217 218 /** 219 * Pads {@code data} as per ISO/IEC 9797-1, method 2. 220 * @param data Data to be padded. 221 * @return Returns {@code data} padded as per ISO/IEC 9797-1, method 2. 222 */ 223 public static byte[] padISO9797Method2(byte[] data) { 224 byte[] t = new byte[data.length - data.length % 8 + 8]; 225 System.arraycopy(data, 0, t, 0, data.length); 226 for (int i = data.length; i < t.length; i++) 227 t[i] = (byte) (i == data.length ? 0x80 : 0x00); 228 data = t; 229 return data; 230 } 231 232 /** 233 * converts to BCD 234 * @param s - the number 235 * @param padLeft - flag indicating left/right padding 236 * @param d The byte array to copy into. 237 * @param offset Where to start copying into. 238 * @return BCD representation of the number 239 */ 240 public static byte[] str2bcd(String s, boolean padLeft, byte[] d, int offset) { 241 int len = s.length(); 242 int start = (len & 1) == 1 && padLeft ? 1 : 0; 243 for (int i=start; i < len+start; i++) 244 d [offset + (i >> 1)] |= s.charAt(i-start)-'0' << ((i & 1) == 1 ? 0 : 4); 245 return d; 246 } 247 248 /** 249 * converts to BCD 250 * @param s - the number 251 * @param padLeft - flag indicating left/right padding 252 * @param d The byte array to copy into. 253 * @param offset Where to start copying into. 254 * @return BCD representation of the number 255 */ 256 public static byte[] str2hex(String s, boolean padLeft, byte[] d, int offset) { 257 int len = s.length(); 258 int start = (len & 1) == 1 && padLeft ? 1 : 0; 259 for (int i=start; i < len+start; i++) 260 d [offset + (i >> 1)] |= Character.digit(s.charAt(i-start),16) << ((i & 1) == 1 ? 0 : 4); 261 return d; 262 } 263 264 /** 265 * converts to BCD 266 * @param s - the number 267 * @param padLeft - flag indicating left/right padding 268 * @return BCD representation of the number 269 */ 270 public static byte[] str2bcd(String s, boolean padLeft) { 271 int len = s.length(); 272 byte[] d = new byte[ len+1 >> 1 ]; 273 return str2bcd(s, padLeft, d, 0); 274 } 275 /** 276 * converts to BCD 277 * @param s - the number 278 * @param padLeft - flag indicating left/right padding 279 * @param fill - fill value 280 * @return BCD representation of the number 281 */ 282 public static byte[] str2bcd(String s, boolean padLeft, byte fill) { 283 int len = s.length(); 284 byte[] d = new byte[ len+1 >> 1 ]; 285 if (d.length > 0) { 286 if (padLeft) 287 d[0] = (byte) ((fill & 0xF) << 4); 288 int start = (len & 1) == 1 && padLeft ? 1 : 0; 289 int i; 290 for (i=start; i < len+start; i++) 291 d [i >> 1] |= s.charAt(i-start)-'0' << ((i & 1) == 1 ? 0 : 4); 292 if ((i & 1) == 1) 293 d [i >> 1] |= fill & 0xF; 294 } 295 return d; 296 } 297 /** 298 * converts a BCD representation of a number to a String 299 * @param b - BCD representation 300 * @param offset - starting offset 301 * @param len - BCD field len 302 * @param padLeft - was padLeft packed? 303 * @return the String representation of the number 304 */ 305 public static String bcd2str(byte[] b, int offset, 306 int len, boolean padLeft) 307 { 308 StringBuilder d = new StringBuilder(len); 309 int start = (len & 1) == 1 && padLeft ? 1 : 0; 310 for (int i=start; i < len+start; i++) { 311 int shift = (i & 1) == 1 ? 0 : 4; 312 char c = Character.forDigit ( 313 b[offset+ (i>>1)] >> shift & 0x0F, 16); 314 if (c == 'd') 315 c = '='; 316 d.append (Character.toUpperCase (c)); 317 } 318 return d.toString(); 319 } 320 /** 321 * converts a a byte array to a String with padding support 322 * @param b - HEX representation 323 * @param offset - starting offset 324 * @param len - BCD field len 325 * @param padLeft - was padLeft packed? 326 * @return the String representation of the number 327 */ 328 public static String hex2str(byte[] b, int offset, 329 int len, boolean padLeft) 330 { 331 StringBuilder d = new StringBuilder(len); 332 int start = (len & 1) == 1 && padLeft ? 1 : 0; 333 334 for (int i=start; i < len+start; i++) { 335 int shift = (i & 1) == 1 ? 0 : 4; 336 char c = Character.forDigit ( 337 b[offset+ (i>>1)] >> shift & 0x0F, 16); 338 d.append (Character.toUpperCase (c)); 339 } 340 return d.toString(); 341 } 342 343 /** 344 * converts a byte array to hex string 345 * (suitable for dumps and ASCII packaging of Binary fields 346 * @param b - byte array 347 * @return String representation 348 */ 349 public static String hexString(byte[] b) { 350 StringBuilder d = new StringBuilder(b.length * 2); 351 for (byte aB : b) { 352 d.append(hexStrings[(int) aB & 0xFF]); 353 } 354 return d.toString(); 355 } 356 /** 357 * converts a byte array to printable characters 358 * @param b - byte array 359 * @return String representation 360 */ 361 public static String dumpString(byte[] b) { 362 StringBuilder d = new StringBuilder(b.length * 2); 363 for (byte aB : b) { 364 char c = (char) aB; 365 if (Character.isISOControl(c)) { 366 switch (c) { 367 case '\r': 368 d.append("{CR}"); 369 break; 370 case '\n': 371 d.append("{LF}"); 372 break; 373 case '\000': 374 d.append("{NULL}"); 375 break; 376 case '\001': 377 d.append("{SOH}"); 378 break; 379 case '\002': 380 d.append("{STX}"); 381 break; 382 case '\003': 383 d.append("{ETX}"); 384 break; 385 case '\004': 386 d.append("{EOT}"); 387 break; 388 case '\005': 389 d.append("{ENQ}"); 390 break; 391 case '\006': 392 d.append("{ACK}"); 393 break; 394 case '\007': 395 d.append("{BEL}"); 396 break; 397 case '\020': 398 d.append("{DLE}"); 399 break; 400 case '\025': 401 d.append("{NAK}"); 402 break; 403 case '\026': 404 d.append("{SYN}"); 405 break; 406 case '\034': 407 d.append("{FS}"); 408 break; 409 case '\036': 410 d.append("{RS}"); 411 break; 412 default: 413 d.append('['); 414 d.append(hexStrings[(int) aB & 0xFF]); 415 d.append(']'); 416 break; 417 } 418 } else 419 d.append(c); 420 421 } 422 return d.toString(); 423 } 424 /** 425 * converts a byte array to hex string 426 * (suitable for dumps and ASCII packaging of Binary fields 427 * @param b - byte array 428 * @param offset - starting position 429 * @param len the length 430 * @return String representation 431 */ 432 public static String hexString(byte[] b, int offset, int len) { 433 StringBuilder d = new StringBuilder(len * 2); 434 len += offset; 435 for (int i=offset; i<len; i++) { 436 d.append(hexStrings[(int)b[i] & 0xFF]); 437 } 438 return d.toString(); 439 } 440 441 /** 442 * bit representation of a BitSet 443 * suitable for dumps and debugging 444 * @param b - the BitSet 445 * @return string representing the bits (i.e. 011010010...) 446 */ 447 public static String bitSet2String (BitSet b) { 448 int len = b.size(); // BBB Should be length()? 449 len = len > 128 ? 128: len; // BBB existence of 3rd bitmap not considered here 450 StringBuilder d = new StringBuilder(len); 451 for (int i=0; i<len; i++) 452 d.append (b.get(i) ? '1' : '0'); 453 return d.toString(); 454 } 455 /** 456 * converts a BitSet into a binary field 457 * used in pack routines 458 * 459 * This method will set bits 0 (and 65) if there's a secondary (and tertiary) bitmap 460 * (i.e., if the bitmap length is > 64 (and > 128)) 461 * 462 * @param b - the BitSet 463 * @return binary representation 464 */ 465 public static byte[] bitSet2byte (BitSet b) 466 { 467 int len = b.length()+62 >>6 <<6; // +62 because we don't use bit 0 in the BitSet 468 byte[] d = new byte[len >> 3]; 469 for (int i=0; i<len; i++) 470 if (b.get(i+1)) // +1 because we don't use bit 0 of the BitSet 471 d[i >> 3] |= 0x80 >> i % 8; 472 if (len>64) 473 d[0] |= 0x80; 474 if (len>128) 475 d[8] |= 0x80; 476 return d; 477 } 478 479 /** 480 * converts a BitSet into a binary field 481 * used in pack routines 482 * 483 * This method will set bits 0 (and 65) if there's a secondary (and tertiary) bitmap 484 * (i.e., if the bitmap length is > 64 (and > 128)) 485 * 486 * @param b - the BitSet 487 * @param bytes - number of bytes to return 488 * @return binary representation 489 */ 490 public static byte[] bitSet2byte (BitSet b, int bytes) 491 { 492 int len = bytes * 8; 493 494 byte[] d = new byte[bytes]; 495 for (int i=0; i<len; i++) 496 if (b.get(i+1)) // +1 because we don't use bit 0 of the BitSet 497 d[i >> 3] |= 0x80 >> i % 8; 498 if (len>64) 499 d[0] |= 0x80; 500 if (len>128) 501 d[8] |= 0x80; 502 return d; 503 } 504 505 /* 506 * Convert BitSet to int value. 507 */ 508 public static int bitSet2Int(BitSet bs) { 509 int total = 0; 510 int b = bs.length() - 1; 511 if (b > 0) { 512 int value = (int) Math.pow(2,b); 513 for (int i = 0; i <= b; i++) { 514 if (bs.get(i)) 515 total += value; 516 517 value = value >> 1; 518 } 519 } 520 521 return total; 522 } 523 524 /* 525 * Convert int value to BitSet. 526 */ 527 public static BitSet int2BitSet(int value) { 528 529 return int2BitSet(value,0); 530 } 531 532 /* 533 * Convert int value to BitSet. 534 */ 535 public static BitSet int2BitSet(int value, int offset) { 536 537 BitSet bs = new BitSet(); 538 539 String hex = Integer.toHexString(value); 540 hex2BitSet(bs, hex.getBytes(), offset); 541 542 return bs; 543 } 544 545 /** 546 * Converts a binary representation of a Bitmap field 547 * into a Java BitSet 548 * @param b - binary representation 549 * @param offset - staring offset 550 * @param bitZeroMeansExtended - true for ISO-8583 551 * @return java BitSet object 552 */ 553 public static BitSet byte2BitSet 554 (byte[] b, int offset, boolean bitZeroMeansExtended) 555 { 556 int len = bitZeroMeansExtended ? 557 (b[offset] & 0x80) == 0x80 ? 128 : 64 : 64; 558 BitSet bmap = new BitSet (len); 559 for (int i=0; i<len; i++) 560 if ((b[offset + (i >> 3)] & 0x80 >> i % 8) > 0) 561 bmap.set(i+1); 562 return bmap; 563 } 564 /** 565 * Converts a binary representation of a Bitmap field 566 * into a Java BitSet 567 * @param b - binary representation 568 * @param offset - staring offset 569 * @param maxBits - max number of bits (supports 64,128 or 192) 570 * @return java BitSet object 571 */ 572 public static BitSet byte2BitSet (byte[] b, int offset, int maxBits) { 573 boolean b1= (b[offset] & 0x80) == 0x80; 574 boolean b65= (b.length > offset+8) && ((b[offset+8] & 0x80) == 0x80); 575 576 int len= (maxBits > 128 && b1 && b65) ? 192 : 577 (maxBits > 64 && b1) ? 128 : 578 (maxBits < 64) ? maxBits : 64; 579 580 BitSet bmap = new BitSet (len); 581 for (int i=0; i<len; i++) 582 if ((b[offset + (i >> 3)] & 0x80 >> i % 8) > 0) 583 bmap.set(i+1); 584 return bmap; 585 } 586 587 /** 588 * Converts a binary representation of a Bitmap field 589 * into a Java BitSet. 590 * 591 * The byte[] will be fully consumed, and fed into the given BitSet starting at bitOffset+1 592 * 593 * @param bmap - BitSet 594 * @param b - hex representation 595 * @param bitOffset - (i.e. 0 for primary bitmap, 64 for secondary) 596 * @return the same java BitSet object given as first argument 597 */ 598 public static BitSet byte2BitSet (BitSet bmap, byte[] b, int bitOffset) 599 { 600 int len = b.length << 3; 601 for (int i=0; i<len; i++) 602 if ((b[i >> 3] & 0x80 >> i % 8) > 0) 603 bmap.set(bitOffset + i + 1); 604 return bmap; 605 } 606 607 /** 608 * Converts an ASCII representation of a Bitmap field 609 * into a Java BitSet 610 * @param b - hex representation 611 * @param offset - starting offset 612 * @param bitZeroMeansExtended - true for ISO-8583 613 * @return java BitSet object 614 */ 615 public static BitSet hex2BitSet 616 (byte[] b, int offset, boolean bitZeroMeansExtended) 617 { 618 int len = bitZeroMeansExtended ? 619 (Character.digit((char)b[offset],16) & 0x08) == 8 ? 128 : 64 : 620 64; 621 BitSet bmap = new BitSet (len); 622 for (int i=0; i<len; i++) { 623 int digit = Character.digit((char)b[offset + (i >> 2)], 16); 624 if ((digit & 0x08 >> i%4) > 0) 625 bmap.set(i+1); 626 } 627 return bmap; 628 } 629 630 /** 631 * Converts an ASCII representation of a Bitmap field 632 * into a Java BitSet 633 * @param b - hex representation 634 * @param offset - starting offset 635 * @param maxBits - max number of bits (supports 8, 16, 24, 32, 48, 52, 64,.. 128 or 192) 636 * @return java BitSet object 637 */ 638 public static BitSet hex2BitSet (byte[] b, int offset, int maxBits) { 639 int len = maxBits > 64 ? 640 (Character.digit((char)b[offset],16) & 0x08) == 8 ? 128 : 64 : 641 maxBits; 642 if (len > 64 && maxBits > 128 && 643 b.length > offset+16 && 644 (Character.digit((char)b[offset+16],16) & 0x08) == 8) 645 { 646 len = 192; 647 } 648 BitSet bmap = new BitSet (len); 649 for (int i=0; i<len; i++) { 650 int digit = Character.digit((char)b[offset + (i >> 2)], 16); 651 if ((digit & 0x08 >> i%4) > 0) { 652 bmap.set(i+1); 653 if (i==65 && maxBits > 128) // BBB this is redundant (check already done outside 654 len = 192; // BBB of the loop), but I'll leave it for now.. 655 } 656 } 657 return bmap; 658 } 659 660 /** 661 * Converts an ASCII representation of a Bitmap field 662 * into a Java BitSet 663 * @param bmap - BitSet 664 * @param b - hex representation 665 * @param bitOffset - (i.e. 0 for primary bitmap, 64 for secondary) 666 * @return java BitSet object 667 */ 668 public static BitSet hex2BitSet (BitSet bmap, byte[] b, int bitOffset) 669 { 670 int len = b.length << 2; 671 for (int i=0; i<len; i++) { 672 int digit = Character.digit((char)b[i >> 2], 16); 673 if ((digit & 0x08 >> i%4) > 0) 674 bmap.set (bitOffset + i + 1); 675 } 676 return bmap; 677 } 678 679 /** 680 * @param b source byte array 681 * @param offset starting offset 682 * @param len number of bytes in destination (processes len*2) 683 * @return byte[len] 684 */ 685 public static byte[] hex2byte (byte[] b, int offset, int len) { 686 byte[] d = new byte[len]; 687 for (int i=0; i<len*2; i++) { 688 int shift = i%2 == 1 ? 0 : 4; 689 d[i>>1] |= Character.digit((char) b[offset+i], 16) << shift; 690 } 691 return d; 692 } 693 /** 694 * Converts a hex string into a byte array 695 * @param s source string (with Hex representation) 696 * @return byte array 697 */ 698 public static byte[] hex2byte (String s) { 699 return hex2byte (s, CHARSET); 700 } 701 /** 702 * Converts a hex string into a byte array 703 * @param s source string (with Hex representation) 704 * @param charset character set to be used 705 * @return byte array 706 */ 707 public static byte[] hex2byte (String s, Charset charset) { 708 if (charset == null) charset = CHARSET; 709 if (s.length() % 2 == 0) { 710 return hex2byte (s.getBytes(charset), 0, s.length() >> 1); 711 } else { 712 // Padding left zero to make it even size #Bug raised by tommy 713 return hex2byte("0"+s, charset); 714 } 715 } 716 717 /** 718 * Converts a byte array into a hex string 719 * @param bs source byte array 720 * @return hexadecimal representation of bytes 721 */ 722 public static String byte2hex(byte[] bs) { 723 return byte2hex(bs, 0, bs.length); 724 } 725 726 /** 727 * Converts an integer into a byte array of hex 728 * 729 * @param value 730 * @return bytes representation of integer 731 */ 732 public static byte[] int2byte(int value) { 733 if (value < 0) { 734 return new byte[]{(byte) (value >>> 24 & 0xFF), (byte) (value >>> 16 & 0xFF), 735 (byte) (value >>> 8 & 0xFF), (byte) (value & 0xFF)}; 736 } else if (value <= 0xFF) { 737 return new byte[]{(byte) (value & 0xFF)}; 738 } else if (value <= 0xFFFF) { 739 return new byte[]{(byte) (value >>> 8 & 0xFF), (byte) (value & 0xFF)}; 740 } else if (value <= 0xFFFFFF) { 741 return new byte[]{(byte) (value >>> 16 & 0xFF), (byte) (value >>> 8 & 0xFF), 742 (byte) (value & 0xFF)}; 743 } else { 744 return new byte[]{(byte) (value >>> 24 & 0xFF), (byte) (value >>> 16 & 0xFF), 745 (byte) (value >>> 8 & 0xFF), (byte) (value & 0xFF)}; 746 } 747 } 748 749 /** 750 * Converts a byte array of hex into an integer 751 * 752 * @param bytes 753 * @return integer representation of bytes 754 */ 755 public static int byte2int(byte[] bytes) { 756 if (bytes == null || bytes.length == 0) { 757 return 0; 758 } 759 ByteBuffer byteBuffer = ByteBuffer.allocate(4); 760 for (int i = 0; i < 4 - bytes.length; i++) { 761 byteBuffer.put((byte) 0); 762 } 763 for (int i = 0; i < bytes.length; i++) { 764 byteBuffer.put(bytes[i]); 765 } 766 byteBuffer.position(0); 767 return byteBuffer.getInt(); 768 } 769 770 /** 771 * Converts a byte array into a string of lower case hex chars. 772 * 773 * @param bs A byte array 774 * @param off The index of the first byte to read 775 * @param length The number of bytes to read. 776 * @return the string of hex chars. 777 */ 778 public static String byte2hex(byte[] bs, int off, int length) { 779 if (bs.length <= off || bs.length < off + length) 780 throw new IllegalArgumentException(); 781 StringBuilder sb = new StringBuilder(length * 2); 782 byte2hexAppend(bs, off, length, sb); 783 return sb.toString(); 784 } 785 786 private static void byte2hexAppend(byte[] bs, int off, int length, StringBuilder sb) { 787 if (bs.length <= off || bs.length < off + length) 788 throw new IllegalArgumentException(); 789 sb.ensureCapacity(sb.length() + length * 2); 790 for (int i = off; i < off + length; i++) { 791 sb.append(Character.forDigit(bs[i] >>> 4 & 0xf, 16)); 792 sb.append(Character.forDigit(bs[i] & 0xf, 16)); 793 } 794 } 795 796 /** 797 * format double value 798 * @param d the amount 799 * @param len the field len 800 * @return a String of fieldLen characters (right justified) 801 */ 802 public static String formatDouble(double d, int len) { 803 String prefix = Long.toString((long) d); 804 String suffix = Integer.toString ( 805 (int) (Math.round(d * 100f) % 100) ); 806 try { 807 if (len > 3) 808 prefix = ISOUtil.padleft(prefix,len-3,' '); 809 suffix = ISOUtil.zeropad(suffix, 2); 810 } catch (ISOException e) { 811 // should not happen 812 } 813 return prefix + "." + suffix; 814 } 815 /** 816 * prepare long value used as amount for display 817 * (implicit 2 decimals) 818 * @param l value 819 * @param len display len 820 * @return formated field 821 * @exception ISOException 822 */ 823 public static String formatAmount(long l, int len) throws ISOException { 824 String buf = Long.toString(l); 825 if (l < 100) 826 buf = zeropad(buf, 3); 827 StringBuilder s = new StringBuilder(padleft (buf, len-1, ' ') ); 828 s.insert(len-3, '.'); 829 return s.toString(); 830 } 831 832 /** 833 * XML normalizer 834 * @param s source String 835 * @param canonical true if we want to normalize \r and \n as well 836 * @return normalized string suitable for XML Output 837 */ 838 public static String normalize (String s, boolean canonical) { 839 StringBuilder str = new StringBuilder(); 840 841 int len = s != null ? s.length() : 0; 842 for (int i = 0; i < len; i++) { 843 char ch = s.charAt(i); 844 switch (ch) { 845 case '<': 846 str.append("<"); 847 break; 848 case '>': 849 str.append(">"); 850 break; 851 case '&': 852 str.append("&"); 853 break; 854 case '"': 855 str.append("""); 856 break; 857 case '\'': 858 str.append("'"); 859 break; 860 case '\r': 861 case '\n': 862 if (!canonical) { 863 str.append("&#"); 864 str.append(Integer.toString(ch & 0xFF)); 865 str.append(';'); 866 break; 867 } 868 // else, default append char 869 default: 870 if (ch < 0x20) { 871 str.append(String.format("\\u%04x", (int) (ch & 0xFF))); 872 } else { 873 str.append(ch); 874 } 875 } 876 } 877 return str.toString(); 878 } 879 880 public static String stripUnicode (String s) { 881 StringBuilder sb = new StringBuilder(); 882 int len = s != null ? s.length() : 0; 883 boolean escape = false; 884 for (int i = 0; i < len; i++) { 885 char ch = s.charAt(i); 886 if (ch == '\\' && i < len-5 && isInternalUnicodeSequence(s.substring(i+1, i+6))) { 887 sb.append((char) (Character.digit(s.charAt(i + 4), 16) << 4 | (Character.digit(s.charAt(i + 5), 16)))); 888 i += 5; 889 } else 890 sb.append(ch); 891 } 892 return sb.toString(); 893 } 894 895 private static boolean isInternalUnicodeSequence(String s) { 896 return unicodePattern.matcher(s).matches(); 897 } 898 /** 899 * XML normalizer (default canonical) 900 * @param s source String 901 * @return normalized string suitable for XML Output 902 */ 903 public static String normalize (String s) { 904 return normalize(s, true); 905 } 906 /** 907 * Protects PAN, Track2, CVC (suitable for logs). 908 * 909 * <pre> 910 * "40000101010001" is converted to "400001____0001" 911 * "40000101010001=020128375" is converted to "400001____0001=0201_____" 912 * "40000101010001D020128375" is converted to "400001____0001D0201_____" 913 * "123" is converted to "___" 914 * </pre> 915 * @param s string to be protected 916 * @param mask char used to protect the string 917 * @return 'protected' String 918 */ 919 920 public static String protect(String s, char mask) { 921 // Validation for minimum length 922 if (s.length() <= 4) { 923 char[] maskedArray = new char[s.length()]; 924 Arrays.fill(maskedArray, mask);// 6 (BIN) + 4 (last digits) = 10 925 return new String(maskedArray); 926 } 927 StringBuilder ps = new StringBuilder(s); 928 929 // Identify the positions of separators (^ and =) 930 String separator = s.contains("^") ? "^" : s.contains("=") ? "=" : null; 931 int firstSeparatorIndex = separator != null ? ps.indexOf(separator) : s.length(); 932 if (firstSeparatorIndex < 6) { 933 return s; // nothing to do 934 } 935 int lastDigitIndex = firstSeparatorIndex - 4; 936 937 // Replace characters with underscore except BIN, last four digits and separators 938 for (int i = 6; i < lastDigitIndex; i++) { 939 ps.setCharAt(i, mask); 940 } 941 if (separator != null) { 942 for (int i = firstSeparatorIndex + 1; i < ps.length(); i++) { 943 char c = ps.charAt(i); 944 if ((c != '=' && c != '^')) { 945 ps.setCharAt(i, mask); 946 } 947 } 948 } 949 return ps.toString(); 950 } 951 public static String protect(String s) { 952 return protect(s, '_'); 953 } 954 public static int[] toIntArray(String s) { 955 StringTokenizer st = new StringTokenizer (s); 956 int[] array = new int [st.countTokens()]; 957 for (int i=0; st.hasMoreTokens(); i++) 958 array[i] = Integer.parseInt (st.nextToken()); 959 return array; 960 } 961 public static String[] toStringArray(String s) { 962 StringTokenizer st = new StringTokenizer (s); 963 String[] array = new String [st.countTokens()]; 964 for (int i=0; st.hasMoreTokens(); i++) 965 array[i] = st.nextToken(); 966 return array; 967 } 968 /** 969 * Bitwise XOR between corresponding bytes 970 * @param op1 byteArray1 971 * @param op2 byteArray2 972 * @return an array of length = the smallest between op1 and op2 973 */ 974 public static byte[] xor (byte[] op1, byte[] op2) { 975 byte[] result; 976 // Use the smallest array 977 if (op2.length > op1.length) { 978 result = new byte[op1.length]; 979 } 980 else { 981 result = new byte[op2.length]; 982 } 983 for (int i = 0; i < result.length; i++) { 984 result[i] = (byte)(op1[i] ^ op2[i]); 985 } 986 return result; 987 } 988 /** 989 * Bitwise XOR between corresponding byte arrays represented in hex 990 * @param op1 hexstring 1 991 * @param op2 hexstring 2 992 * @return an array of length = the smallest between op1 and op2 993 */ 994 public static String hexor (String op1, String op2) { 995 byte[] xor = xor (hex2byte (op1), hex2byte (op2)); 996 return hexString (xor); 997 } 998 999 /** 1000 * Trims a byte[] to a certain length 1001 * @param array the byte[] to be trimmed 1002 * @param length the wanted length 1003 * @return the trimmed byte[] 1004 */ 1005 public static byte[] trim (byte[] array, int length) { 1006 byte[] trimmedArray = new byte[length]; 1007 System.arraycopy(array, 0, trimmedArray, 0, length); 1008 return trimmedArray; 1009 } 1010 1011 /** 1012 * Concatenates two byte arrays (array1 and array2) 1013 * @param array1 first part 1014 * @param array2 last part 1015 * @return the concatenated array 1016 */ 1017 public static byte[] concat (byte[] array1, byte[] array2) { 1018 byte[] concatArray = new byte[array1.length + array2.length]; 1019 System.arraycopy(array1, 0, concatArray, 0, array1.length); 1020 System.arraycopy(array2, 0, concatArray, array1.length, array2.length); 1021 return concatArray; 1022 } 1023 1024 /** 1025 * Concatenates two byte arrays (array1 and array2) 1026 * @param array1 first part 1027 * @param beginIndex1 initial index 1028 * @param length1 length 1029 * @param array2 last part 1030 * @param beginIndex2 last part index 1031 * @param length2 last part length 1032 * @return the concatenated array 1033 */ 1034 public static byte[] concat (byte[] array1, int beginIndex1, int length1, byte[] array2, 1035 int beginIndex2, int length2) { 1036 byte[] concatArray = new byte[length1 + length2]; 1037 System.arraycopy(array1, beginIndex1, concatArray, 0, length1); 1038 System.arraycopy(array2, beginIndex2, concatArray, length1, length2); 1039 return concatArray; 1040 } 1041 /** 1042 * Causes the currently executing thread to sleep (temporarily cease 1043 * execution) for the specified number of milliseconds. The thread 1044 * does not lose ownership of any monitors. 1045 * 1046 * @param millis the length of time to sleep in milliseconds. 1047 */ 1048 public static void sleep (long millis) { 1049 if (millis > 0) 1050 LockSupport.parkNanos(Duration.ofMillis(millis).toNanos()); 1051 else if (millis == 0) 1052 Thread.yield(); 1053 else 1054 throw new IllegalArgumentException ("timeout value is negative"); 1055 } 1056 1057 /** 1058 * Left unPad with '0' 1059 * @param s - original string 1060 * @return zero unPadded string 1061 */ 1062 public static String zeroUnPad( String s ) { 1063 return unPadLeft(s, '0'); 1064 } 1065 /** 1066 * Right unPad with ' ' 1067 * @param s - original string 1068 * @return blank unPadded string 1069 */ 1070 public static String blankUnPad( String s ) { 1071 return unPadRight(s, ' '); 1072 } 1073 1074 /** 1075 * Unpad from right. 1076 * @param s - original string 1077 * @param c - padding char 1078 * @return unPadded string. 1079 */ 1080 public static String unPadRight(String s, char c) { 1081 int end = s.length(); 1082 if (end == 0) 1083 return s; 1084 while ( 0 < end && s.charAt(end-1) == c) end --; 1085 return 0 < end ? s.substring( 0, end ): s.substring( 0, 1 ); 1086 } 1087 1088 /** 1089 * Unpad from left. 1090 * @param s - original string 1091 * @param c - padding char 1092 * @return unPadded string. 1093 */ 1094 public static String unPadLeft(String s, char c) { 1095 int fill = 0, end = s.length(); 1096 if (end == 0) 1097 return s; 1098 while ( fill < end && s.charAt(fill) == c) fill ++; 1099 return fill < end ? s.substring( fill, end ): s.substring( fill-1, end ); 1100 } 1101 1102 /** 1103 * @return true if the string is zero-filled ( 0 char filled ) 1104 **/ 1105 public static boolean isZero( String s ) { 1106 int i = 0, len = s.length(); 1107 while ( i < len && s.charAt( i ) == '0'){ 1108 i++; 1109 } 1110 return i >= len; 1111 } 1112 1113 /** 1114 * @return true if the string is blank filled (space char filled) 1115 */ 1116 public static boolean isBlank( String s ){ 1117 return s.trim().length() == 0; 1118 } 1119 1120 /** 1121 * Return true if the string is alphanum. 1122 **/ 1123 public static boolean isAlphaNumeric ( String s ) { 1124 int len = s.length(); 1125 for (int i=0; i<len; i++) { 1126 char c = s.charAt(i); 1127 if (!Character.isLetterOrDigit(s.charAt(i))) 1128 return false; 1129 } 1130 return len > 0; 1131 } 1132 1133 /** 1134 * Return true if the string represent a number 1135 * in the specified radix. 1136 * <br><br> 1137 **/ 1138 public static boolean isNumeric ( String s, int radix ) { 1139 int i = 0, len = s.length(); 1140 while ( i < len && Character.digit(s.charAt(i), radix) > -1 ){ 1141 i++; 1142 } 1143 return i >= len && len > 0; 1144 } 1145 1146 /** 1147 * Converts a BitSet into an extended binary field 1148 * used in pack routines. The result is always in the 1149 * extended format: (16 bytes of length) 1150 * <br><br> 1151 * @param b the BitSet 1152 * @return binary representation 1153 */ 1154 public static byte[] bitSet2extendedByte ( BitSet b ){ 1155 int len = 128; 1156 byte[] d = new byte[len >> 3]; 1157 for ( int i=0; i<len; i++ ) 1158 if (b.get(i+1)) 1159 d[i >> 3] |= 0x80 >> i % 8; 1160 d[0] |= 0x80; 1161 return d; 1162 } 1163 1164 /** 1165 * Converts a String to an integer of base radix. 1166 * <br><br> 1167 * String constraints are: 1168 * <li>Number must be less than 10 digits</li> 1169 * <li>Number must be positive</li> 1170 * @param s String representation of number 1171 * @param radix Number base to use 1172 * @return integer value of number 1173 * @throws NumberFormatException 1174 */ 1175 public static int parseInt (String s, int radix) throws NumberFormatException { 1176 int length = s.length(); 1177 if (length > 9) 1178 throw new NumberFormatException ("Number can have maximum 9 digits"); 1179 int result; 1180 int index = 0; 1181 int digit = Character.digit (s.charAt(index++), radix); 1182 if (digit == -1) 1183 throw new NumberFormatException ("String contains non-digit"); 1184 result = digit; 1185 while (index < length) { 1186 result *= radix; 1187 digit = Character.digit (s.charAt(index++), radix); 1188 if (digit == -1) 1189 throw new NumberFormatException ("String contains non-digit"); 1190 result += digit; 1191 } 1192 return result; 1193 } 1194 1195 /** 1196 * Converts a String to an integer of radix 10. 1197 * <br><br> 1198 * String constraints are: 1199 * <li>Number must be less than 10 digits</li> 1200 * <li>Number must be positive</li> 1201 * @param s String representation of number 1202 * @return integer value of number 1203 * @throws NumberFormatException 1204 */ 1205 public static int parseInt (String s) throws NumberFormatException { 1206 return parseInt (s, 10); 1207 } 1208 1209 /** 1210 * Converts a character array to an integer of base radix. 1211 * <br><br> 1212 * Array constraints are: 1213 * <li>Number must be less than 10 digits</li> 1214 * <li>Number must be positive</li> 1215 * @param cArray Character Array representation of number 1216 * @param radix Number base to use 1217 * @return integer value of number 1218 * @throws NumberFormatException 1219 */ 1220 public static int parseInt (char[] cArray, int radix) throws NumberFormatException { 1221 int length = cArray.length; 1222 if (length > 9) 1223 throw new NumberFormatException ("Number can have maximum 9 digits"); 1224 int result; 1225 int index = 0; 1226 int digit = Character.digit(cArray[index++], radix); 1227 if (digit == -1) 1228 throw new NumberFormatException ("Char array contains non-digit"); 1229 result = digit; 1230 while (index < length) { 1231 result *= radix; 1232 digit = Character.digit(cArray[index++],radix); 1233 if (digit == -1) 1234 throw new NumberFormatException ("Char array contains non-digit"); 1235 result += digit; 1236 } 1237 return result; 1238 } 1239 1240 /** 1241 * Converts a character array to an integer of radix 10. 1242 * <br><br> 1243 * Array constraints are: 1244 * <li>Number must be less than 10 digits</li> 1245 * <li>Number must be positive</li> 1246 * @param cArray Character Array representation of number 1247 * @return integer value of number 1248 * @throws NumberFormatException 1249 */ 1250 public static int parseInt (char[] cArray) throws NumberFormatException { 1251 return parseInt (cArray,10); 1252 } 1253 1254 /** 1255 * Converts a byte array to an integer of base radix. 1256 * <br><br> 1257 * Array constraints are: 1258 * <li>Number must be less than 10 digits</li> 1259 * <li>Number must be positive</li> 1260 * @param bArray Byte Array representation of number 1261 * @param radix Number base to use 1262 * @return integer value of number 1263 * @throws NumberFormatException 1264 */ 1265 public static int parseInt (byte[] bArray, int radix) throws NumberFormatException { 1266 int length = bArray.length; 1267 if (length > 9) 1268 throw new NumberFormatException ("Number can have maximum 9 digits"); 1269 int result; 1270 int index = 0; 1271 int digit = Character.digit((char)bArray[index++], radix); 1272 if (digit == -1) 1273 throw new NumberFormatException ("Byte array contains non-digit"); 1274 result = digit; 1275 while (index < length) { 1276 result *= radix; 1277 digit = Character.digit((char)bArray[index++],radix); 1278 if (digit == -1) 1279 throw new NumberFormatException ("Byte array contains non-digit"); 1280 result += digit; 1281 } 1282 return result; 1283 } 1284 1285 /** 1286 * Converts a byte array to an integer of radix 10. 1287 * <br><br> 1288 * Array constraints are: 1289 * <li>Number must be less than 10 digits</li> 1290 * <li>Number must be positive</li> 1291 * @param bArray Byte Array representation of number 1292 * @return integer value of number 1293 * @throws NumberFormatException 1294 */ 1295 public static int parseInt (byte[] bArray) throws NumberFormatException { 1296 return parseInt (bArray,10); 1297 } 1298 private static String hexOffset (int i) { 1299 i = i>>4 << 4; 1300 int w = i > 0xFFFF ? 8 : 4; 1301 try { 1302 return zeropad (Integer.toString (i, 16), w); 1303 } catch (ISOException e) { 1304 // should not happen 1305 return e.getMessage(); 1306 } 1307 } 1308 1309 /** 1310 * @param b a byte[] buffer 1311 * @return hexdump 1312 */ 1313 public static String hexdump (byte[] b) { 1314 return hexdump (b, 0, b.length); 1315 } 1316 1317 /** 1318 * @param b a byte[] buffer 1319 * @param offset starting offset 1320 */ 1321 public static String hexdump (byte[] b, int offset) { 1322 return hexdump (b, offset, b.length-offset); 1323 } 1324 1325 /** 1326 * @param b a byte[] buffer 1327 * @param offset starting offset 1328 * @param len the Length 1329 * @return hexdump 1330 */ 1331 public static String hexdump (byte[] b, int offset, int len) { 1332 StringBuilder sb = new StringBuilder (); 1333 StringBuilder hex = new StringBuilder (); 1334 StringBuilder ascii = new StringBuilder (); 1335 String sep = " "; 1336 String lineSep = System.getProperty ("line.separator"); 1337 len = offset + len; 1338 1339 for (int i=offset; i<len; i++) { 1340 hex.append(hexStrings[(int)b[i] & 0xFF]); 1341 hex.append (' '); 1342 char c = (char) b[i]; 1343 ascii.append (c >= 32 && c < 127 ? c : '.'); 1344 1345 int j = i % 16; 1346 switch (j) { 1347 case 7 : 1348 hex.append (' '); 1349 break; 1350 case 15 : 1351 sb.append (hexOffset (i)); 1352 sb.append (sep); 1353 sb.append (hex.toString()); 1354 sb.append (' '); 1355 sb.append (ascii.toString()); 1356 sb.append (lineSep); 1357 hex = new StringBuilder (); 1358 ascii = new StringBuilder (); 1359 break; 1360 } 1361 } 1362 if (hex.length() > 0) { 1363 while (hex.length () < 49) 1364 hex.append (' '); 1365 1366 sb.append (hexOffset (len)); 1367 sb.append (sep); 1368 sb.append (hex.toString()); 1369 sb.append (' '); 1370 sb.append (ascii.toString()); 1371 sb.append (lineSep); 1372 } 1373 return sb.toString(); 1374 } 1375 /** 1376 * pads a string with 'F's (useful for pinoffset management) 1377 * @param s an [hex]string 1378 * @param len desired length 1379 * @return string right padded with 'F's 1380 */ 1381 public static String strpadf (String s, int len) { 1382 StringBuilder d = new StringBuilder(s); 1383 while (d.length() < len) 1384 d.append('F'); 1385 return d.toString(); 1386 } 1387 /** 1388 * reverse the effect of strpadf 1389 * @param s F padded string 1390 * @return trimmed string 1391 */ 1392 public static String trimf (String s) { 1393 if (s != null) { 1394 int l = s.length(); 1395 if (l > 0) { 1396 while (--l >= 0) { 1397 if (s.charAt (l) != 'F') 1398 break; 1399 } 1400 s = l == 0 ? "" : s.substring (0, l+1); 1401 } 1402 } 1403 return s; 1404 } 1405 1406 /** 1407 * return the last n characters of the passed String, left padding where required with 0 1408 * 1409 * @param s 1410 * String to take from 1411 * @param n nuber of characters to take 1412 * 1413 * @return String (may be null) 1414 */ 1415 public static String takeLastN(String s,int n) throws ISOException { 1416 if (s.length()>n) { 1417 return s.substring(s.length()-n); 1418 } 1419 else { 1420 if (s.length()<n){ 1421 return zeropad(s,n); 1422 } 1423 else { 1424 return s; 1425 } 1426 } 1427 } 1428 1429 /** 1430 * return the first n characters of the passed String, left padding where required with 0 1431 * 1432 * @param s 1433 * String to take from 1434 * @param n nuber of characters to take 1435 * 1436 * @return String (may be null) 1437 */ 1438 public static String takeFirstN(String s,int n) throws ISOException { 1439 if (s.length()>n) { 1440 return s.substring(0,n); 1441 } 1442 else { 1443 if (s.length()<n){ 1444 return zeropad(s,n); 1445 } 1446 else { 1447 return s; 1448 } 1449 } 1450 } 1451 public static String millisToString (long millis) { 1452 StringBuilder sb = new StringBuilder(); 1453 if (millis < 0) { 1454 millis = -millis; 1455 sb.append('-'); 1456 } 1457 int ms = (int) (millis % 1000); 1458 millis /= 1000; 1459 int dd = (int) (millis/86400); 1460 millis -= dd * 86400; 1461 int hh = (int) (millis/3600); 1462 millis -= hh * 3600; 1463 int mm = (int) (millis/60); 1464 millis -= mm * 60; 1465 int ss = (int) millis; 1466 if (dd > 0) { 1467 sb.append (Long.toString(dd)); 1468 sb.append ("d "); 1469 } 1470 sb.append (zeropad (hh, 2)); 1471 sb.append (':'); 1472 sb.append (zeropad (mm, 2)); 1473 sb.append (':'); 1474 sb.append (zeropad (ss, 2)); 1475 sb.append ('.'); 1476 sb.append (zeropad (ms, 3)); 1477 return sb.toString(); 1478 } 1479 1480 /** 1481 * Format a string containing a amount conversion rate in the proper format 1482 * <p/> 1483 * Format: 1484 * The leftmost digit (i.e., position 1) of this data element denotes the number of 1485 * positions the decimal separator must be moved from the right. Positions 2–8 of 1486 * this data element specify the rate. For example, a conversion rate value of 1487 * 91234567 in this data element would equate to 0.001234567. 1488 * 1489 * @param convRate - amount conversion rate 1490 * @return a string containing a amount conversion rate in the proper format, 1491 * witch is suitable for create fields 10 and 11 1492 * @throws ISOException 1493 */ 1494 public static String formatAmountConversionRate(double convRate) throws ISOException { 1495 if (convRate == 0) 1496 return null; 1497 BigDecimal cr = new BigDecimal(convRate); 1498 int x = 7 - cr.precision() + cr.scale(); 1499 String bds = cr.movePointRight(cr.scale()).toString(); 1500 if (x > 9) 1501 bds = ISOUtil.zeropad(bds, bds.length() + x - 9); 1502 String ret = ISOUtil.zeropadRight(bds, 7); 1503 return Math.min(9, x) + ISOUtil.takeFirstN(ret, 7); 1504 } 1505 1506 /** 1507 * Parse currency amount conversion rate string 1508 * <p/> 1509 * Suitble for parse fields 10 and 11 1510 * 1511 * @param convRate amount conversation rate 1512 * @return parsed currency amount conversation rate 1513 * @throws IllegalArgumentException 1514 */ 1515 public static double parseAmountConversionRate(String convRate) { 1516 if (convRate == null || convRate.length() != 8) 1517 throw new IllegalArgumentException("Invalid amount converion rate argument: '" + 1518 convRate + "'"); 1519 BigDecimal bd = new BigDecimal(convRate); 1520 int pow = bd.movePointLeft(7).intValue(); 1521 bd = new BigDecimal(convRate.substring(1)); 1522 return bd.movePointLeft(pow).doubleValue(); 1523 } 1524 1525 1526 /** 1527 * Converts a string[] or multiple strings into one comma-delimited String. 1528 * 1529 * Takes care of escaping commas using a backlash 1530 * @see ISOUtil#commaDecode(String) 1531 * @param ss string array to be comma encoded 1532 * @return comma encoded string 1533 */ 1534 public static String commaEncode (String... ss) { 1535 return charEncode(',', ss); 1536 } 1537 1538 /** 1539 * Decodes a comma encoded String as encoded by commaEncode 1540 * @see ISOUtil#commaEncode(String[]) 1541 * @param s the command encoded String 1542 * @return String[] 1543 */ 1544 public static String[] commaDecode (String s) { 1545 return charDecode(',', s); 1546 } 1547 1548 /** 1549 * Decodes a comma encoded String returning element in position i 1550 * @param s comma encoded string 1551 * @param i position (starts at 0) 1552 * @return element in position i of comma encoded string, or null 1553 */ 1554 public static String commaDecode (String s, int i) { 1555 String[] ss = commaDecode(s); 1556 int l = ss.length; 1557 return i >= 0 && i < l ? ss[i] : null; 1558 } 1559 1560 /** 1561 * Compute card's check digit (LUHN) 1562 * @param p PAN (without checkdigit) 1563 * @return the checkdigit 1564 */ 1565 public static char calcLUHN (String p) { 1566 int i, crc; 1567 int odd = p.length() % 2; 1568 1569 for (i=crc=0; i<p.length(); i++) { 1570 char c = p.charAt(i); 1571 if (!Character.isDigit (c)) { 1572 throw new IllegalArgumentException("Invalid PAN " + p); 1573 } 1574 c = (char) (c - '0'); 1575 if (i % 2 != odd) 1576 crc+= c*2 >= 10 ? c*2 -9 : c*2; 1577 else 1578 crc+=c; 1579 } 1580 1581 return (char) ((crc % 10 == 0 ? 0 : 10 - crc % 10) + '0'); 1582 } 1583 1584 public static String getRandomDigits(Random r, int l, int radix) { 1585 StringBuilder sb = new StringBuilder(); 1586 for (int i=0; i<l; i++) { 1587 sb.append (r.nextInt(radix)); 1588 } 1589 return sb.toString(); 1590 } 1591 1592 // See http://stackoverflow.com/questions/3263892/format-file-size-as-mb-gb-etc 1593 // and http://physics.nist.gov/cuu/Units/binary.html 1594 public static String readableFileSize(long size) { 1595 if(size <= 0) return "0"; 1596 final String[] units = new String[] { "Bi", "KiB", "MiB", "GiB", "TiB", "PiB", "EiB" }; 1597 int digitGroups = (int) (Math.log10(size)/Math.log10(1024)); 1598 return new DecimalFormat("#,##0.#").format(size/Math.pow(1024, digitGroups)) + " " + units[digitGroups]; 1599 } 1600 1601 /** 1602 * At times when the charset is not the default usual one the dump will have more unprintable characters than printable. 1603 * The charset will allow printing of more printable character. Usually when your data is in EBCDIC format you will run into this. 1604 * 1605 * The standard hexdump that exists would print a byte array of F0F1F2 as 1606 * F0 F1 F2 ... 1607 * 1608 * This hexdump, if the Charset.forName("IBM1047") is passedin as charset will print 1609 * F0 F1 F2 | 123 1610 * 1611 * 1612 * @param array 1613 * the array that needs to be dumped. 1614 * @param offset 1615 * From where the data needs to be dumped. 1616 * @param length 1617 * The number of byte that ned to be dumped. 1618 * @param charSet 1619 * The Charset encoding the array is i. 1620 * @return The hexdump string. 1621 */ 1622 public static String hexDump(byte[] array, int offset, int length, Charset charSet) { 1623 // https://gist.github.com/jen20/906db194bd97c14d91df 1624 final int width = 16; 1625 1626 StringBuilder builder = new StringBuilder(); 1627 1628 for (int rowOffset = offset; rowOffset < offset + length; rowOffset += width) { 1629 builder.append(String.format("%06d: ", rowOffset)); 1630 1631 for (int index = 0; index < width; index++) { 1632 if (rowOffset + index < array.length) { 1633 builder.append(String.format("%02x ", array[rowOffset + index]).toUpperCase()); 1634 } 1635 else { 1636 builder.append(" "); 1637 } 1638 } 1639 1640 if (rowOffset < array.length) { 1641 int asciiWidth = Math.min(width, array.length - rowOffset); 1642 builder.append(" | "); 1643 builder.append(new String(array, rowOffset, asciiWidth, charSet).replaceAll("\r\n", " ") 1644 .replaceAll("\n", " ")); 1645 } 1646 1647 builder.append(String.format("%n")); 1648 } 1649 1650 return builder.toString(); 1651 } 1652 1653 public static byte[] decodeHexDump(String s) { 1654 return hex2byte( 1655 Arrays.stream(s.split("\\r\\n|[\\r\\n]")) 1656 .map(x -> 1657 x.replaceAll("^.{4} ", ""). 1658 replaceAll("\\s\\s", " "). 1659 replaceAll("(([0-9A-F][0-9A-F]\\s){1,16}).*$", "$1"). 1660 replaceAll("\\s", "") 1661 ).collect(Collectors.joining())); 1662 } 1663 1664 /** 1665 * Converts a string[] or multiple strings into one char-delimited String. 1666 * 1667 * Takes care of escaping char using a backlash 1668 * 1669 * NOTE: for backward compatibility, an empty String returns a zero-length array 1670 * 1671 * @see ISOUtil#charDecode(char, String) 1672 * @param delimiter char used to delimit 1673 * @param ss string array to be char encoded 1674 * @return char encoded string 1675 */ 1676 public static String charEncode (char delimiter, String... ss) { 1677 StringJoiner sj = new StringJoiner(String.valueOf(delimiter)); 1678 for (String s : ss) { 1679 if (s == null || s.length() == 0) { 1680 sj.add(""); 1681 continue; 1682 } 1683 StringBuilder sb = new StringBuilder(); 1684 for (char c : s.toCharArray()) { 1685 if (c == delimiter || c == '\\') { 1686 sb.append("\\"); 1687 } 1688 sb.append(c); 1689 } 1690 sj.add(sb.toString()); 1691 } 1692 return sj.toString(); 1693 } 1694 1695 /** 1696 * Decodes a char encoded String as encoded by charEncode 1697 * @see ISOUtil#charEncode(char, String[]) 1698 * @param delimiter char used to delimit 1699 * @param s the char encoded String 1700 * @return String[] 1701 */ 1702 public static String[] charDecode (char delimiter, String s) { 1703 if (s == null) { 1704 return null; 1705 } 1706 if (s.length() == 0) { 1707 return new String[0]; 1708 } 1709 List<String> l = new ArrayList<>(); 1710 StringBuilder sb = new StringBuilder(); 1711 boolean escaped = false; 1712 for (int i = 0; i < s.length(); i++) { 1713 char c = s.charAt(i); 1714 if (!escaped) { 1715 if (c == '\\'){ 1716 escaped = true; 1717 continue; 1718 } 1719 if (c == delimiter){ 1720 l.add(sb.toString()); 1721 sb = new StringBuilder(); 1722 continue; 1723 } 1724 } 1725 sb.append(c); 1726 escaped = false; 1727 } 1728 l.add(sb.toString()); 1729 return l.toArray(new String[l.size()]); 1730 } 1731 1732 /** 1733 * Decodes a char encoded String returning element in position i 1734 * @param s comma encoded string 1735 * @param delimiter char used to delimit 1736 * @param i position (starts at 0) 1737 * @return element in position i of comma encoded string, or empty 1738 */ 1739 public static String charDecode(String s, char delimiter, int i) { 1740 if (s == null || s.length() == 0) { 1741 return ""; 1742 } 1743 String[] split = charDecode(delimiter, s); 1744 if (split.length <= 0) { 1745 return ""; 1746 } else { 1747 return i < split.length ? split[i] : ""; 1748 } 1749 } 1750 1751 public static String toUnicodeString(String input) { 1752 StringBuilder sb = new StringBuilder(); 1753 for (char c : input.toCharArray()) { 1754 sb.append(String.format("\\u%04x", (int) c)); 1755 } 1756 return sb.toString(); 1757 } 1758 1759 public static String toASCII(String s) { 1760 return toSingleByte (s, 0x7F); 1761 } 1762 public static String toLatin(String s) { 1763 return toSingleByte (s, 0xFF); 1764 } 1765 1766 private static String toSingleByte(String s, int mask) { 1767 StringBuilder sb = new StringBuilder(); 1768 s.codePoints().forEach(cp -> { 1769 if (cp > mask) { 1770 sb.append(" "); 1771 if (cp > 0xFFFF) // multi-byte character 1772 sb.append(" "); 1773 } else 1774 sb.appendCodePoint(cp); // Append the single-byte character 1775 }); 1776 return sb.toString(); 1777 } 1778 1779}