PageRenderTime 59ms CodeModel.GetById 17ms RepoModel.GetById 0ms app.codeStats 1ms

/bianca/src/main/java/com/clevercloud/bianca/lib/string/StringModule.java

http://github.com/CleverCloud/Bianca
Java | 6494 lines | 4926 code | 974 blank | 594 comment | 1090 complexity | f89a3a083427accd155fc2116ebd73b1 MD5 | raw file
Possible License(s): GPL-2.0, MPL-2.0-no-copyleft-exception

Large files files are truncated, but you can click here to view the full file

  1. /*
  2. * Copyright (c) 1998-2010 Caucho Technology -- all rights reserved
  3. * Copyright (c) 2011-2012 Clever Cloud SAS -- all rights reserved
  4. *
  5. * This file is part of Bianca(R) Open Source
  6. *
  7. * Each copy or derived work must preserve the copyright notice and this
  8. * notice unmodified.
  9. *
  10. * Bianca Open Source is free software; you can redistribute it and/or modify
  11. * it under the terms of the GNU General Public License as published by
  12. * the Free Software Foundation; either version 2 of the License, or
  13. * (at your option) any later version.
  14. *
  15. * Bianca Open Source is distributed in the hope that it will be useful,
  16. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  17. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, or any warranty
  18. * of NON-INFRINGEMENT. See the GNU General Public License for more
  19. * details.
  20. *
  21. * You should have received a copy of the GNU General Public License
  22. * along with Bianca Open Source; if not, write to the
  23. *
  24. * Free Software Foundation, Inc.
  25. * 59 Temple Place, Suite 330
  26. * Boston, MA 02111-1307 USA
  27. *
  28. * @author Scott Ferguson
  29. * @author Marc-Antoine Perennou <Marc-Antoine@Perennou.com>
  30. */
  31. package com.clevercloud.bianca.lib.string;
  32. import com.clevercloud.bianca.BiancaException;
  33. import com.clevercloud.bianca.BiancaModuleException;
  34. import com.clevercloud.bianca.annotation.*;
  35. import com.clevercloud.bianca.env.*;
  36. import com.clevercloud.bianca.lib.file.BinaryOutput;
  37. import com.clevercloud.bianca.lib.file.FileModule;
  38. import com.clevercloud.bianca.module.AbstractBiancaModule;
  39. import com.clevercloud.util.*;
  40. import com.clevercloud.vfs.Path;
  41. import java.io.IOException;
  42. import java.io.InputStream;
  43. import java.math.BigInteger;
  44. import java.security.MessageDigest;
  45. import java.text.DecimalFormat;
  46. import java.text.DecimalFormatSymbols;
  47. import java.text.NumberFormat;
  48. import java.util.*;
  49. import java.util.logging.Level;
  50. import java.util.logging.Logger;
  51. /**
  52. * PHP functions implemented from the string module
  53. */
  54. public class StringModule extends AbstractBiancaModule {
  55. private static final Logger log =
  56. Logger.getLogger(StringModule.class.getName());
  57. private static final L10N L = new L10N(StringModule.class);
  58. public static final int CODESET = 14;
  59. public static final int CRYPT_SALT_LENGTH = 2;
  60. public static final int CRYPT_STD_DES = 0;
  61. public static final int CRYPT_EXT_DES = 0;
  62. public static final int CRYPT_MD5 = 0;
  63. public static final int CRYPT_BLOWFISH = 0;
  64. public static final int CHAR_MAX = 1;
  65. public static final int LC_CTYPE = 1;
  66. public static final int LC_NUMERIC = 2;
  67. public static final int LC_TIME = 3;
  68. public static final int LC_COLLATE = 4;
  69. public static final int LC_MONETARY = 5;
  70. public static final int LC_ALL = 6;
  71. public static final int LC_MESSAGES = 7;
  72. public static final int NOEXPR = (10 << 15) + 1;
  73. public static final int STR_PAD_LEFT = 1;
  74. public static final int STR_PAD_RIGHT = 0;
  75. public static final int STR_PAD_BOTH = 2;
  76. public static final int YESEXPR = 10 << 15;
  77. public static final int RADIXCHAR = 0x10000;
  78. public static final int DECIMAL_POINT = RADIXCHAR; // Returns same value as RADIXCHAR
  79. public static final int THOUSEP = 0x10001;
  80. public static final int THOUSANDS_SEP = THOUSEP; // Returns same value as THOUSEP
  81. public static final int ABDAY_1 = 0x20000;
  82. public static final int ABDAY_2 = 0x20001;
  83. public static final int ABDAY_3 = 0x20002;
  84. public static final int ABDAY_4 = 0x20003;
  85. public static final int ABDAY_5 = 0x20004;
  86. public static final int ABDAY_6 = 0x20005;
  87. public static final int ABDAY_7 = 0x20006;
  88. public static final int DAY_1 = 0x20007;
  89. public static final int DAY_2 = 0x20008;
  90. public static final int DAY_3 = 0x20009;
  91. public static final int DAY_4 = 0x2000a;
  92. public static final int DAY_5 = 0x2000b;
  93. public static final int DAY_6 = 0x2000c;
  94. public static final int DAY_7 = 0x2000d;
  95. public static final int ABMON_1 = 0x2000e;
  96. public static final int ABMON_2 = 0x2000f;
  97. public static final int ABMON_3 = 0x20010;
  98. public static final int ABMON_4 = 0x20011;
  99. public static final int ABMON_5 = 0x20012;
  100. public static final int ABMON_6 = 0x20013;
  101. public static final int ABMON_7 = 0x20014;
  102. public static final int ABMON_8 = 0x20015;
  103. public static final int ABMON_9 = 0x20016;
  104. public static final int ABMON_10 = 0x20017;
  105. public static final int ABMON_11 = 0x20018;
  106. public static final int ABMON_12 = 0x20019;
  107. public static final int MON_1 = 0x2001a;
  108. public static final int MON_2 = 0x2001b;
  109. public static final int MON_3 = 0x2001c;
  110. public static final int MON_4 = 0x2001d;
  111. public static final int MON_5 = 0x2001e;
  112. public static final int MON_6 = 0x2001f;
  113. public static final int MON_7 = 0x20020;
  114. public static final int MON_8 = 0x20021;
  115. public static final int MON_9 = 0x20022;
  116. public static final int MON_10 = 0x20023;
  117. public static final int MON_11 = 0x20024;
  118. public static final int MON_12 = 0x20025;
  119. public static final int AM_STR = 0x20026;
  120. public static final int PM_STR = 0x20027;
  121. public static final int D_T_FMT = 0x20028;
  122. public static final int D_FMT = 0x20029;
  123. public static final int T_FMT = 0x2002a;
  124. public static final int T_FMT_AMPM = 0x2002b;
  125. public static final int ERA = 0x2002c;
  126. public static final int ERA_D_FMT = 0x2002e;
  127. public static final int ERA_D_T_FMT = 0x20030;
  128. public static final int ERA_T_FMT = 0x20031;
  129. public static final int CRNCYSTR = 0x4000f;
  130. private static final DecimalFormatSymbols DEFAULT_DECIMAL_FORMAT_SYMBOLS;
  131. private static final BigInteger BIG_TEN = new BigInteger("10");
  132. private static final BigInteger BIG_2_64 = BigInteger.ONE.shiftLeft(64);
  133. private static final FreeList<MessageDigest> _md5FreeList = new FreeList<MessageDigest>(16);
  134. private static final int LEVENSHTEIN_MAX_LENGTH = 255;
  135. /**
  136. * Escapes a string using C syntax.
  137. *
  138. * @param source the source string to convert
  139. * @param characters the set of characters to convert
  140. * @return the escaped string
  141. * @see #stripcslashes
  142. */
  143. public static StringValue addcslashes(
  144. Env env, StringValue source, String characters) {
  145. if (characters == null) {
  146. characters = "";
  147. }
  148. boolean[] bitmap = parseCharsetBitmap(env, characters);
  149. int length = source.length();
  150. StringValue sb = new StringValue();
  151. for (int i = 0; i < length; i++) {
  152. char ch = source.charAt(i);
  153. if (ch >= 256 || !bitmap[ch]) {
  154. sb.append(ch);
  155. continue;
  156. }
  157. switch (ch) {
  158. case 0x07:
  159. sb.append("\\a");
  160. break;
  161. case '\b':
  162. sb.append("\\b");
  163. break;
  164. case '\t':
  165. sb.append("\\t");
  166. break;
  167. case '\n':
  168. sb.append("\\n");
  169. break;
  170. case 0xb:
  171. sb.append("\\v");
  172. break;
  173. case '\f':
  174. sb.append("\\f");
  175. break;
  176. case '\r':
  177. sb.append("\\r");
  178. break;
  179. default:
  180. if (ch < 0x20 || ch >= 0x7f) {
  181. // save as octal
  182. sb.append("\\");
  183. sb.append((char) ('0' + ((ch >> 6) & 7)));
  184. sb.append((char) ('0' + ((ch >> 3) & 7)));
  185. sb.append((char) ('0' + ((ch) & 7)));
  186. break;
  187. } else {
  188. sb.append("\\");
  189. sb.append(ch);
  190. break;
  191. }
  192. }
  193. }
  194. return sb;
  195. }
  196. /**
  197. * Parses the cslashes bitmap returning an actual bitmap.
  198. *
  199. * @param charset the bitmap string
  200. * @return the actual bitmap
  201. */
  202. private static boolean[] parseCharsetBitmap(Env env, String charset) {
  203. boolean[] bitmap = new boolean[256];
  204. int length = charset.length();
  205. for (int i = 0; i < length; i++) {
  206. char ch = charset.charAt(i);
  207. // TODO: the bitmap eventual might need to deal with unicode
  208. if (ch >= 256) {
  209. continue;
  210. }
  211. bitmap[ch] = true;
  212. if (length <= i + 3) {
  213. continue;
  214. }
  215. if (charset.charAt(i + 1) != '.' || charset.charAt(i + 2) != '.') {
  216. continue;
  217. }
  218. char last = charset.charAt(i + 3);
  219. if (last < ch) {
  220. env.warning(L.l("character set range is invalid: {0}..{1}",
  221. ch, last));
  222. continue;
  223. }
  224. i += 3;
  225. for (; ch <= last; ch++) {
  226. bitmap[ch] = true;
  227. }
  228. // TODO: handling of '@'?
  229. }
  230. return bitmap;
  231. }
  232. /**
  233. * Escapes a string for db characters.
  234. *
  235. * @param source the source string to convert
  236. * @return the escaped string
  237. */
  238. public static StringValue addslashes(StringValue source) {
  239. StringValue sb = new StringValue();
  240. int length = source.length();
  241. for (int i = 0; i < length; i++) {
  242. char ch = source.charAt(i);
  243. switch (ch) {
  244. case 0x0:
  245. sb.append("\\0");
  246. break;
  247. case '\'':
  248. sb.append("\\'");
  249. break;
  250. case '\"':
  251. sb.append("\\\"");
  252. break;
  253. case '\\':
  254. sb.append("\\\\");
  255. break;
  256. default:
  257. sb.append(ch);
  258. break;
  259. }
  260. }
  261. return sb;
  262. }
  263. /**
  264. * Converts a binary value to a hex value.
  265. */
  266. public static StringValue bin2hex(Env env, InputStream is) {
  267. try {
  268. StringValue sb = new StringValue();
  269. int ch;
  270. while ((ch = is.read()) >= 0) {
  271. int d = (ch >> 4) & 0xf;
  272. if (d < 10) {
  273. sb.append((char) (d + '0'));
  274. } else {
  275. sb.append((char) (d + 'a' - 10));
  276. }
  277. d = (ch) & 0xf;
  278. if (d < 10) {
  279. sb.append((char) (d + '0'));
  280. } else {
  281. sb.append((char) (d + 'a' - 10));
  282. }
  283. }
  284. return sb;
  285. } catch (IOException e) {
  286. throw new BiancaModuleException(e);
  287. }
  288. }
  289. /**
  290. * Alias of rtrim. Removes trailing whitespace.
  291. *
  292. * @param env the bianca environment
  293. * @param str the string to be trimmed
  294. * @param charset optional set of characters to trim
  295. * @return the trimmed string
  296. */
  297. public static StringValue chop(Env env,
  298. StringValue str,
  299. @Optional String charset) {
  300. return rtrim(env, str, charset);
  301. }
  302. /**
  303. * converts a number to its character equivalent
  304. *
  305. * @param value the integer value
  306. * @return the string equivalent
  307. */
  308. public static StringValue chr(Env env, long value) {
  309. StringValue sb = new StringValue();
  310. sb.append((char) value);
  311. return sb;
  312. }
  313. /**
  314. * Splits a string into chunks
  315. *
  316. * @param body the body string
  317. * @param chunkLen the optional chunk length, defaults to 76
  318. * @param end the optional end value, defaults to "\r\n"
  319. */
  320. public static String chunk_split(String body,
  321. @Optional("76") int chunkLen,
  322. @Optional("\"\\r\\n\"") String end) {
  323. if (body == null) {
  324. body = "";
  325. }
  326. if (end == null) {
  327. end = "";
  328. }
  329. if (chunkLen < 1) // TODO: real exn
  330. {
  331. throw new IllegalArgumentException(L.l("bad value {0}", chunkLen));
  332. }
  333. StringBuilder sb = new StringBuilder();
  334. int i = 0;
  335. for (; i + chunkLen <= body.length(); i += chunkLen) {
  336. sb.append(body.substring(i, i + chunkLen));
  337. sb.append(end);
  338. }
  339. if (i < body.length()) {
  340. sb.append(body.substring(i));
  341. sb.append(end);
  342. }
  343. return sb.toString();
  344. }
  345. /**
  346. * Converts from one cyrillic set to another.
  347. * <p/>
  348. * This implementation does nothing, because bianca stores strings as
  349. * 16 bit unicode.
  350. */
  351. public static String convert_cyr_string(Env env,
  352. String str,
  353. String from,
  354. String to) {
  355. env.stub("convert_cyr_string");
  356. return str;
  357. }
  358. public static Value convert_uudecode(Env env, StringValue source) {
  359. int length = source.length();
  360. if (length == 0) {
  361. return BooleanValue.FALSE;
  362. }
  363. StringBuilder builder = new StringBuilder();
  364. int i = 0;
  365. while (i < length) {
  366. int ch1 = source.charAt(i++);
  367. if (ch1 == 0x60 || ch1 == 0x20) {
  368. break;
  369. } else if (ch1 < 0x20 || 0x5f < ch1) {
  370. continue;
  371. }
  372. int sublen = ch1 - 0x20;
  373. while (sublen > 0) {
  374. int code;
  375. code = ((source.charAt(i++) - 0x20) & 0x3f) << 18;
  376. code += ((source.charAt(i++) - 0x20) & 0x3f) << 12;
  377. code += ((source.charAt(i++) - 0x20) & 0x3f) << 6;
  378. code += ((source.charAt(i++) - 0x20) & 0x3f);
  379. builder.append(code >> 16);
  380. if (sublen > 1) {
  381. builder.append(code >> 8);
  382. }
  383. if (sublen > 2) {
  384. builder.append(code);
  385. }
  386. sublen -= 3;
  387. }
  388. }
  389. return env.createString(builder.toString());
  390. }
  391. /**
  392. * uuencode a string.
  393. */
  394. public static Value convert_uuencode(StringValue source) {
  395. if (source.length() == 0) {
  396. return BooleanValue.FALSE;
  397. }
  398. StringValue result = new StringValue();
  399. int i = 0;
  400. int length = source.length();
  401. while (i < length) {
  402. int sublen = length - i;
  403. if (45 < sublen) {
  404. sublen = 45;
  405. }
  406. result.append((char) (sublen + 0x20));
  407. int end = i + sublen;
  408. while (i < end) {
  409. int code = source.charAt(i++) << 16;
  410. if (i < length) {
  411. code += source.charAt(i++) << 8;
  412. }
  413. if (i < length) {
  414. code += source.charAt(i++);
  415. }
  416. result.append(toUUChar(((code >> 18) & 0x3f)));
  417. result.append(toUUChar(((code >> 12) & 0x3f)));
  418. result.append(toUUChar(((code >> 6) & 0x3f)));
  419. result.append(toUUChar(((code) & 0x3f)));
  420. }
  421. result.append('\n');
  422. }
  423. result.append((char) 0x60);
  424. result.append('\n');
  425. return result;
  426. }
  427. /**
  428. * Returns an array of information about the characters.
  429. */
  430. public static Value count_chars(StringValue data,
  431. @Optional("0") int mode) {
  432. int[] count = new int[256];
  433. int length = data.length();
  434. for (int i = 0; i < length; i++) {
  435. count[data.charAt(i) & 0xff] += 1;
  436. }
  437. switch (mode) {
  438. case 0: {
  439. ArrayValue result = new ArrayValueImpl();
  440. for (int i = 0; i < count.length; i++) {
  441. result.put(LongValue.create(i), LongValue.create(count[i]));
  442. }
  443. return result;
  444. }
  445. case 1: {
  446. ArrayValue result = new ArrayValueImpl();
  447. for (int i = 0; i < count.length; i++) {
  448. if (count[i] > 0) {
  449. result.put(LongValue.create(i), LongValue.create(count[i]));
  450. }
  451. }
  452. return result;
  453. }
  454. case 2: {
  455. ArrayValue result = new ArrayValueImpl();
  456. for (int i = 0; i < count.length; i++) {
  457. if (count[i] == 0) {
  458. result.put(LongValue.create(i), LongValue.create(count[i]));
  459. }
  460. }
  461. return result;
  462. }
  463. case 3: {
  464. StringValue sb = new StringValue();
  465. for (int i = 0; i < count.length; i++) {
  466. if (count[i] > 0) {
  467. sb.append((char) i);
  468. }
  469. }
  470. return sb;
  471. }
  472. case 4: {
  473. StringValue sb = new StringValue();
  474. for (int i = 0; i < count.length; i++) {
  475. if (count[i] == 0) {
  476. sb.append((char) i);
  477. }
  478. }
  479. return sb;
  480. }
  481. default:
  482. return BooleanValue.FALSE;
  483. }
  484. }
  485. /**
  486. * Calculates the crc32 value for a string
  487. *
  488. * @param str the string value
  489. * @return the crc32 hash
  490. */
  491. public static long crc32(StringValue str) {
  492. return str.getCrc32Value();
  493. }
  494. public static String crypt(String string, @Optional String salt) {
  495. if (string == null) {
  496. string = "";
  497. }
  498. if (salt == null || salt.equals("")) {
  499. salt = ("" + Crypt.resultToChar(RandomUtil.nextInt(0x40))
  500. + Crypt.resultToChar(RandomUtil.nextInt(0x40)));
  501. }
  502. return Crypt.crypt(string, salt);
  503. }
  504. // TODO: echo
  505. /**
  506. * Explodes a string into an array
  507. *
  508. * @param separator the separator string
  509. * @param string the string to be exploded
  510. * @param limit the max number of elements
  511. * @return an array of exploded values
  512. */
  513. public static Value explode(Env env,
  514. StringValue separator,
  515. StringValue string,
  516. @Optional("0x7fffffff") long limit) {
  517. if (separator.length() == 0) {
  518. env.warning(L.l("Delimiter is empty"));
  519. return BooleanValue.FALSE;
  520. }
  521. int head = 0;
  522. ArrayValue array = new ArrayValueImpl();
  523. int separatorLength = separator.length();
  524. int stringLength = string.length();
  525. long ulimit;
  526. if (limit >= 0) {
  527. ulimit = limit;
  528. } else {
  529. ulimit = 0x7fffffff;
  530. }
  531. for (int i = 0; i < stringLength; ++i) {
  532. if (ulimit <= array.getSize() + 1) {
  533. break;
  534. }
  535. if (string.regionMatches(i, separator.toString(), 0)) {
  536. StringValue chunk = string.substring(head, i);
  537. array.append(chunk);
  538. head = i + separatorLength;
  539. i = head - 1;
  540. }
  541. }
  542. StringValue chunk = string.substring(head);
  543. array.append(chunk);
  544. while (array.getSize() > 0 && limit++ < 0) {
  545. array.pop(env);
  546. }
  547. return array;
  548. }
  549. /**
  550. * Use printf style formatting to write a string to a file.
  551. *
  552. * @param fd the file to write to
  553. * @param format the format string
  554. * @param args the valujes to apply to the format string
  555. */
  556. public static Value fprintf(Env env,
  557. @NotNull BinaryOutput os,
  558. StringValue format,
  559. Value[] args) {
  560. Value value = sprintf(env, format, args);
  561. return FileModule.fwrite(env, os, value.toInputStream(),
  562. Integer.MAX_VALUE);
  563. }
  564. /**
  565. * implodes an array into a string
  566. *
  567. * @param glueV the separator string
  568. * @param piecesV the array to be imploded
  569. * @return a string of imploded values
  570. */
  571. public static Value implode(Env env,
  572. Value glueV,
  573. @Optional Value piecesV) {
  574. StringValue glue;
  575. ArrayValue pieces;
  576. if ((piecesV.isArray() && glueV.isArray())
  577. || glueV.isArray()) {
  578. pieces = glueV.toArrayValue(env);
  579. glue = piecesV.toStringValue();
  580. } else if (piecesV.isArray()) {
  581. pieces = piecesV.toArrayValue(env);
  582. glue = glueV.toStringValue();
  583. } else {
  584. env.warning(L.l("neither argument to implode is an array: {0}, {1}",
  585. glueV.getClass().getName(), piecesV.getClass().getName()));
  586. return NullValue.NULL;
  587. }
  588. StringValue sb = new StringValue();
  589. boolean isFirst = true;
  590. Iterator<Value> iter = pieces.getValueIterator(env);
  591. while (iter.hasNext()) {
  592. if (!isFirst) {
  593. sb = sb.append(glue);
  594. }
  595. isFirst = false;
  596. sb = sb.append(iter.next());
  597. }
  598. return sb;
  599. }
  600. /**
  601. * implodes an array into a string
  602. *
  603. * @param glueV the separator string
  604. * @param piecesV the array to be imploded
  605. * @return a string of imploded values
  606. */
  607. public static Value join(Env env,
  608. Value glueV,
  609. Value piecesV) {
  610. return implode(env, glueV, piecesV);
  611. }
  612. /**
  613. * Lowercases the first character
  614. *
  615. * @param string the input string
  616. */
  617. public static StringValue lcfirst(Env env, StringValue string) {
  618. if (string == null) {
  619. return StringValue.EMPTY;
  620. } else if (string.length() == 0) {
  621. return string;
  622. }
  623. StringValue sb = new StringValue();
  624. sb = sb.append(Character.toLowerCase(string.charAt(0)));
  625. sb = sb.append(string, 1, string.length());
  626. return sb;
  627. }
  628. /**
  629. * Calculate Levenshtein distance between two strings
  630. *
  631. * @param str1 first string
  632. * @param str2 second string
  633. * @param cost_ins defines the cost of insertion
  634. * @param cost_rep defines the cost of replacement
  635. * @param cost_del defines the cost of deletion
  636. * @return int Levenshtein-Distance between the two argument strings
  637. */
  638. public static int levenshtein(Env env, String str1, String str2, @Optional("1") int cost_ins, @Optional("1") int cost_rep, @Optional("1") int cost_del) {
  639. int distance = -1;
  640. int i1, i2, c0, c1, c2, l1, l2;
  641. l1 = str1.length();
  642. l2 = str2.length();
  643. int[] p1 = new int[l2 + 1];
  644. int[] p2 = new int[l2 + 1];
  645. int[] tmp = new int[l2 + 1];
  646. if (l1 == 0) {
  647. return l2 * cost_ins;
  648. }
  649. if (l2 == 0) {
  650. return l1 * cost_del;
  651. }
  652. // TODO: keep this limitation ? really ?
  653. if ((l1 > LEVENSHTEIN_MAX_LENGTH) || (l2 > LEVENSHTEIN_MAX_LENGTH)) {
  654. distance = -1;
  655. } else {
  656. for (i2 = 0; i2 <= l2; i2++) {
  657. p1[i2] = i2 * cost_ins;
  658. }
  659. for (i1 = 0; i1 < l1; i1++) {
  660. p2[0] = p1[0] + cost_del;
  661. for (i2 = 0; i2 < l2; i2++) {
  662. c0 = p1[i2] + ((str1.charAt(i1) == str2.charAt(i2)) ? 0 : cost_rep);
  663. c1 = p1[i2 + 1] + cost_del;
  664. if (c1 < c0) {
  665. c0 = c1;
  666. }
  667. c2 = p2[i2] + cost_ins;
  668. if (c2 < c0) {
  669. c0 = c2;
  670. }
  671. p2[i2 + 1] = c0;
  672. }
  673. tmp = p1;
  674. p1 = p2;
  675. p2 = tmp;
  676. }
  677. c0 = p1[l2];
  678. distance = c0;
  679. }
  680. if (distance < 0) {
  681. env.warning(L.l("Argument string(s) too long"));
  682. }
  683. return distance;
  684. }
  685. /**
  686. * Gets locale-specific symbols.
  687. * XXX: locale charset
  688. */
  689. public static ArrayValue localeconv(Env env) {
  690. ArrayValueImpl array = new ArrayValueImpl();
  691. BiancaLocale money = env.getLocaleInfo().getMonetary();
  692. Locale locale = money.getLocale();
  693. DecimalFormatSymbols decimal = new DecimalFormatSymbols(locale);
  694. Currency currency = NumberFormat.getInstance(locale).getCurrency();
  695. array.put(env.createString("decimal_point"),
  696. new StringValue(decimal.getDecimalSeparator()));
  697. array.put(env.createString("thousands_sep"),
  698. new StringValue(decimal.getGroupingSeparator()));
  699. //array.put("grouping", "");
  700. array.put(env.createString("int_curr_symbol"),
  701. new StringValue(decimal.getInternationalCurrencySymbol()));
  702. array.put(env.createString("currency_symbol"),
  703. new StringValue(decimal.getCurrencySymbol()));
  704. array.put(env.createString("mon_decimal_point"),
  705. new StringValue(decimal.getMonetaryDecimalSeparator()));
  706. array.put(env.createString("mon_thousands_sep"),
  707. new StringValue(decimal.getGroupingSeparator()));
  708. //array.put("mon_grouping", "");
  709. array.put(new StringValue("positive_sign"), StringValue.EMPTY);
  710. array.put(env.createString("negative_sign"),
  711. new StringValue(decimal.getMinusSign()));
  712. array.put(env.createString("int_frac_digits"),
  713. LongValue.create(currency.getDefaultFractionDigits()));
  714. array.put(env.createString("frac_digits"),
  715. LongValue.create(currency.getDefaultFractionDigits()));
  716. //array.put("p_cs_precedes", "");
  717. //array.put("p_sep_by_space", "");
  718. //array.put("n_cs_precedes", "");
  719. //array.put("n_sep_by_space", "");
  720. //array.put("p_sign_posn", "");
  721. //array.put("n_sign_posn", "");
  722. return array;
  723. }
  724. /**
  725. * Removes leading whitespace.
  726. *
  727. * @param string the string to be trimmed
  728. * @param characters optional set of characters to trim
  729. * @return the trimmed string
  730. */
  731. public static StringValue ltrim(Env env,
  732. StringValue string,
  733. @Optional String characters) {
  734. if (characters == null) {
  735. characters = "";
  736. }
  737. boolean[] trim;
  738. if (characters.equals("")) {
  739. trim = TRIM_WHITESPACE;
  740. } else {
  741. trim = parseCharsetBitmap(env, characters);
  742. }
  743. for (int i = 0; i < string.length(); i++) {
  744. char ch = string.charAt(i);
  745. if (ch >= 256 || !trim[ch]) {
  746. if (i == 0) {
  747. return string;
  748. } else {
  749. return string.substring(i);
  750. }
  751. }
  752. }
  753. return StringValue.EMPTY;
  754. }
  755. /**
  756. * returns the md5 hash
  757. *
  758. * @param source the string
  759. * @param rawOutput if true, return the raw binary
  760. * @return a string of imploded values
  761. */
  762. public static Value md5(Env env,
  763. InputStream is,
  764. @Optional boolean rawOutput) {
  765. try {
  766. MessageDigest md = _md5FreeList.allocate();
  767. if (md == null) {
  768. md = MessageDigest.getInstance("MD5");
  769. }
  770. md.reset();
  771. int ch;
  772. while ((ch = is.read()) >= 0) {
  773. md.update((byte) ch);
  774. }
  775. byte[] digest = md.digest();
  776. _md5FreeList.free(md);
  777. return hashToValue(digest, rawOutput);
  778. } catch (Exception e) {
  779. throw new BiancaModuleException(e);
  780. }
  781. }
  782. /**
  783. * returns the md5 hash
  784. *
  785. * @param source the string
  786. * @param rawOutput if true, return the raw binary
  787. * @return a string of imploded values
  788. */
  789. public static Value md5_file(Env env,
  790. Path source,
  791. @Optional boolean rawOutput) {
  792. try {
  793. MessageDigest md = MessageDigest.getInstance("MD5");
  794. InputStream is = null;
  795. try {
  796. is = source.openRead();
  797. int d;
  798. while ((d = is.read()) >= 0) {
  799. md.update((byte) d);
  800. }
  801. byte[] digest = md.digest();
  802. return hashToValue(digest, rawOutput);
  803. } catch (IOException e) {
  804. log.log(Level.FINE, e.toString(), e);
  805. return BooleanValue.FALSE;
  806. } finally {
  807. try {
  808. if (is != null) {
  809. is.close();
  810. }
  811. } catch (IOException e) {
  812. }
  813. }
  814. } catch (Exception e) {
  815. throw new BiancaModuleException(e);
  816. }
  817. }
  818. /**
  819. * Returns the metaphone of a string.
  820. * This implementation produces identical results to the php version,
  821. * which does contain some bugs.
  822. */
  823. public static String metaphone(String string) {
  824. if (string == null) {
  825. string = "";
  826. }
  827. int length = string.length();
  828. int index = 0;
  829. char ch = 0;
  830. // ignore everything up until first letter
  831. for (; index < length; index++) {
  832. ch = toUpperCase(string.charAt(index));
  833. if ('A' <= ch && ch <= 'Z') {
  834. break;
  835. }
  836. }
  837. if (index == length) {
  838. return "";
  839. }
  840. int lastIndex = length - 1;
  841. StringBuilder result = new StringBuilder(length);
  842. // special case first letter
  843. char nextCh = index < lastIndex
  844. ? toUpperCase(string.charAt(index + 1))
  845. : 0;
  846. switch (ch) {
  847. case 'A':
  848. if (nextCh == 'E') {
  849. result.append('E');
  850. index += 2;
  851. } else {
  852. result.append('A');
  853. index += 1;
  854. }
  855. break;
  856. case 'E':
  857. case 'I':
  858. case 'O':
  859. case 'U':
  860. result.append(ch);
  861. index += 1;
  862. break;
  863. case 'G':
  864. case 'K':
  865. case 'P':
  866. if (nextCh == 'N') {
  867. result.append('N');
  868. index += 2;
  869. }
  870. break;
  871. case 'W':
  872. if (nextCh == 'H' || nextCh == 'R') {
  873. result.append(nextCh);
  874. index += 2;
  875. } else {
  876. switch (nextCh) {
  877. case 'A':
  878. case 'E':
  879. case 'I':
  880. case 'O':
  881. case 'U':
  882. result.append('W');
  883. index += 2;
  884. break;
  885. default:
  886. break;
  887. }
  888. }
  889. break;
  890. case 'X':
  891. result.append('S');
  892. index += 1;
  893. break;
  894. default:
  895. break;
  896. }
  897. // the rest of the letters
  898. char prevCh;
  899. for (; index < length; index++) {
  900. if (index > 0) {
  901. prevCh = toUpperCase(string.charAt(index - 1));
  902. } else {
  903. prevCh = 0;
  904. }
  905. ch = toUpperCase(string.charAt(index));
  906. if (ch < 'A' || ch > 'Z') {
  907. continue;
  908. }
  909. if (ch == prevCh && ch != 'C') {
  910. continue;
  911. }
  912. if (index + 1 < length) {
  913. nextCh = toUpperCase(string.charAt(index + 1));
  914. } else {
  915. nextCh = 0;
  916. }
  917. char nextnextCh;
  918. if (index + 2 < length) {
  919. nextnextCh = toUpperCase(string.charAt(index + 2));
  920. } else {
  921. nextnextCh = 0;
  922. }
  923. switch (ch) {
  924. case 'B':
  925. if (prevCh != 'M') {
  926. result.append('B');
  927. }
  928. break;
  929. case 'C':
  930. switch (nextCh) {
  931. case 'E':
  932. case 'I':
  933. case 'Y':
  934. // makesoft
  935. if (nextCh == 'I' && nextnextCh == 'A') {
  936. result.append('X');
  937. } else if (prevCh == 'S') {
  938. } else {
  939. result.append('S');
  940. }
  941. break;
  942. default:
  943. if (nextCh == 'H') {
  944. result.append('X');
  945. index++;
  946. } else {
  947. result.append('K');
  948. }
  949. break;
  950. }
  951. break;
  952. case 'D':
  953. if (nextCh == 'G') {
  954. switch (nextnextCh) {
  955. case 'E':
  956. case 'I':
  957. case 'Y':
  958. // makesoft
  959. result.append('J');
  960. index++;
  961. break;
  962. default:
  963. result.append('T');
  964. break;
  965. }
  966. } else {
  967. result.append('T');
  968. }
  969. break;
  970. case 'G':
  971. if (nextCh == 'H') {
  972. boolean isSilent = false;
  973. if (index - 3 >= 0) {
  974. char prev3Ch = toUpperCase(string.charAt(index - 3));
  975. switch (prev3Ch) {
  976. // noghtof
  977. case 'B':
  978. case 'D':
  979. case 'H':
  980. isSilent = true;
  981. break;
  982. default:
  983. break;
  984. }
  985. }
  986. if (!isSilent) {
  987. if (index - 4 >= 0) {
  988. char prev4Ch = toUpperCase(string.charAt(index - 4));
  989. isSilent = (prev4Ch == 'H');
  990. }
  991. }
  992. if (!isSilent) {
  993. result.append('F');
  994. index++;
  995. }
  996. } else if (nextCh == 'N') {
  997. char nextnextnextCh;
  998. if (index + 3 < length) {
  999. nextnextnextCh = toUpperCase(string.charAt(index + 3));
  1000. } else {
  1001. nextnextnextCh = 0;
  1002. }
  1003. if (nextnextCh < 'A' || nextnextCh > 'Z') {
  1004. } else if (nextnextCh == 'E' && nextnextnextCh == 'D') {
  1005. } else {
  1006. result.append('K');
  1007. }
  1008. } else if (prevCh == 'G') {
  1009. result.append('K');
  1010. } else {
  1011. switch (nextCh) {
  1012. case 'E':
  1013. case 'I':
  1014. case 'Y':
  1015. // makesoft
  1016. result.append('J');
  1017. break;
  1018. default:
  1019. result.append('K');
  1020. break;
  1021. }
  1022. }
  1023. break;
  1024. case 'H':
  1025. case 'W':
  1026. case 'Y':
  1027. switch (nextCh) {
  1028. case 'A':
  1029. case 'E':
  1030. case 'I':
  1031. case 'O':
  1032. case 'U':
  1033. // followed by a vowel
  1034. if (ch == 'H') {
  1035. switch (prevCh) {
  1036. case 'C':
  1037. case 'G':
  1038. case 'P':
  1039. case 'S':
  1040. case 'T':
  1041. // affecth
  1042. break;
  1043. default:
  1044. result.append('H');
  1045. break;
  1046. }
  1047. } else {
  1048. result.append(ch);
  1049. }
  1050. break;
  1051. default:
  1052. // not followed by a vowel
  1053. break;
  1054. }
  1055. break;
  1056. case 'K':
  1057. if (prevCh != 'C') {
  1058. result.append('K');
  1059. }
  1060. break;
  1061. case 'P':
  1062. if (nextCh == 'H') {
  1063. result.append('F');
  1064. } else {
  1065. result.append('P');
  1066. }
  1067. break;
  1068. case 'Q':
  1069. result.append('K');
  1070. break;
  1071. case 'S':
  1072. if (nextCh == 'I' && (nextnextCh == 'O' || nextnextCh == 'A')) {
  1073. result.append('X');
  1074. } else if (nextCh == 'H') {
  1075. result.append('X');
  1076. index++;
  1077. } else {
  1078. result.append('S');
  1079. }
  1080. break;
  1081. case 'T':
  1082. if (nextCh == 'I' && (nextnextCh == 'O' || nextnextCh == 'A')) {
  1083. result.append('X');
  1084. } else if (nextCh == 'H') {
  1085. result.append('0');
  1086. index++;
  1087. } else {
  1088. result.append('T');
  1089. }
  1090. break;
  1091. case 'V':
  1092. result.append('F');
  1093. break;
  1094. case 'X':
  1095. result.append('K');
  1096. result.append('S');
  1097. break;
  1098. case 'Z':
  1099. result.append('S');
  1100. break;
  1101. case 'F':
  1102. case 'J':
  1103. case 'L':
  1104. case 'M':
  1105. case 'N':
  1106. case 'R':
  1107. result.append(ch);
  1108. break;
  1109. default:
  1110. break;
  1111. }
  1112. }
  1113. return result.toString();
  1114. }
  1115. /**
  1116. * Returns a formatted money value.
  1117. * XXX: locale charset
  1118. *
  1119. * @param format the format
  1120. * @param value the value
  1121. * @return a string of formatted values
  1122. */
  1123. public static String money_format(Env env, String format, double value) {
  1124. BiancaLocale monetary = env.getLocaleInfo().getMonetary();
  1125. Locale locale = monetary.getLocale();
  1126. return NumberFormat.getCurrencyInstance(locale).format(value);
  1127. }
  1128. /**
  1129. * TODO: finish implementation of nl_langinfo
  1130. */
  1131. public static String nl_langinfo(Env env, int item) {
  1132. BiancaLocale money = env.getLocaleInfo().getMonetary();
  1133. Locale locale = money.getLocale();
  1134. DecimalFormatSymbols decimal = new DecimalFormatSymbols(locale);
  1135. Currency currency = NumberFormat.getInstance(locale).getCurrency();
  1136. String result;
  1137. switch (item) {
  1138. default:
  1139. result = Boolean.FALSE.toString();
  1140. break;
  1141. }
  1142. return result;
  1143. }
  1144. /**
  1145. * Returns a formatted number.
  1146. *
  1147. * @param value the value
  1148. * @param decimals the number of decimals
  1149. * @param pointValue the decimal point string
  1150. * @param groupValue the thousands separator
  1151. * @return a string of the formatted number
  1152. */
  1153. public static String number_format(Env env,
  1154. double value,
  1155. @Optional int decimals,
  1156. @Optional Value pointValue,
  1157. @Optional Value groupValue) {
  1158. boolean isGroupDefault = (groupValue instanceof DefaultValue);
  1159. boolean isPointDefault = (pointValue instanceof DefaultValue);
  1160. if (!isPointDefault && isGroupDefault) {
  1161. env.warning(L.l("wrong parameter count"));
  1162. return null;
  1163. }
  1164. String pattern;
  1165. char point = '.';
  1166. if (!pointValue.isNull()) {
  1167. String pointString = pointValue.toString();
  1168. point = (pointString.length() == 0) ? 0 : pointString.charAt(0);
  1169. }
  1170. char group = ',';
  1171. if (!groupValue.isNull()) {
  1172. String groupString = groupValue.toString();
  1173. group = (groupString.length() == 0) ? 0 : groupString.charAt(0);
  1174. }
  1175. if (decimals > 0) {
  1176. StringBuilder patternBuilder = new StringBuilder(6 + decimals);
  1177. patternBuilder.append(group == 0 ? "###0." : "#,##0.");
  1178. for (int i = 0; i < decimals; i++) {
  1179. patternBuilder.append('0');
  1180. }
  1181. pattern = patternBuilder.toString();
  1182. } else {
  1183. pattern = group == 0 ? "###0" : "#,##0";
  1184. }
  1185. DecimalFormatSymbols decimalFormatSymbols;
  1186. if (point == '.' && group == ',') {
  1187. decimalFormatSymbols = DEFAULT_DECIMAL_FORMAT_SYMBOLS;
  1188. } else {
  1189. decimalFormatSymbols = new DecimalFormatSymbols();
  1190. decimalFormatSymbols.setDecimalSeparator(point);
  1191. decimalFormatSymbols.setGroupingSeparator(group);
  1192. decimalFormatSymbols.setZeroDigit('0');
  1193. }
  1194. DecimalFormat format = new DecimalFormat(pattern, decimalFormatSymbols);
  1195. String result = format.format(value);
  1196. if (point == 0 && decimals > 0) {
  1197. // no way to get DecimalFormat to output nothing for the point,
  1198. // so remove it here
  1199. int i = result.lastIndexOf(point);
  1200. return result.substring(0, i) + result.substring(i + 1, result.length());
  1201. } else {
  1202. return result;
  1203. }
  1204. }
  1205. /**
  1206. * Converts the first character to an integer.
  1207. *
  1208. * @param string the string to be converted
  1209. * @return the integer value
  1210. */
  1211. public static long ord(StringValue string) {
  1212. if (string.length() == 0) {
  1213. return 0;
  1214. } else {
  1215. return string.charAt(0);
  1216. }
  1217. }
  1218. /**
  1219. * Parses the string as a query string.
  1220. *
  1221. * @param env the calling environment
  1222. * @param str the query string
  1223. * @param array the optional result array
  1224. */
  1225. @UsesSymbolTable
  1226. public static Value parse_str(Env env, StringValue str,
  1227. @Optional @Reference Value ref) {
  1228. boolean isRef = ref instanceof Var;
  1229. ArrayValue result = null;
  1230. if (isRef) {
  1231. result = new ArrayValueImpl();
  1232. ref.set(result);
  1233. } else if (ref instanceof ArrayValue) {
  1234. result = (ArrayValue) ref;
  1235. isRef = true;
  1236. } else {
  1237. result = new ArrayValueImpl();
  1238. }
  1239. return StringUtility.parseStr(env,
  1240. str,
  1241. result,
  1242. isRef,
  1243. env.getHttpInputEncoding());
  1244. }
  1245. /**
  1246. * Prints the string.
  1247. *
  1248. * @param env the bianca environment
  1249. * @param value the string to print
  1250. */
  1251. public static long print(Env env, Value value) {
  1252. value.print(env);
  1253. return 1;
  1254. }
  1255. /**
  1256. * print to the output with a formatter
  1257. *
  1258. * @param env the bianca environment
  1259. * @param format the format string
  1260. * @param args the format arguments
  1261. * @return the formatted string
  1262. */
  1263. public static int printf(Env env, StringValue format, Value[] args) {
  1264. Value str = sprintf(env, format, args);
  1265. str.print(env);
  1266. return str.length();
  1267. }
  1268. /**
  1269. * Converts a RFC2045 quoted printable string to a string.
  1270. */
  1271. // TODO: i18n
  1272. public static String quoted_printable_decode(String str) {
  1273. if (str == null) {
  1274. str = "";
  1275. }
  1276. StringBuilder sb = new StringBuilder();
  1277. int length = str.length();
  1278. for (int i = 0; i < length; i++) {
  1279. char ch = str.charAt(i);
  1280. if (33 <= ch && ch <= 60) {
  1281. sb.append(ch);
  1282. } else if (62 <= ch && ch <= 126) {
  1283. sb.append(ch);
  1284. } else if (ch == ' ' || ch == '\t') {
  1285. if (i + 1 < str.length()
  1286. && (str.charAt(i + 1) == '\r' || str.charAt(i + 1) == '\n')) {
  1287. sb.append('=');
  1288. sb.append(toUpperHexChar(ch >> 4));
  1289. sb.append(toUpperHexChar(ch));
  1290. } else {
  1291. sb.append(ch);
  1292. }
  1293. } else if (ch == '\r' || ch == '\n') {
  1294. sb.append(ch);
  1295. } else {
  1296. sb.append('=');
  1297. sb.append(toUpperHexChar(ch >> 4));
  1298. sb.append(toUpperHexChar(ch));
  1299. }
  1300. }
  1301. return sb.toString();
  1302. }
  1303. /**
  1304. * Escapes meta characters.
  1305. *
  1306. * @param string the string to be quoted
  1307. * @return the quoted
  1308. */
  1309. public static Value quotemeta(StringValue string) {
  1310. int len = string.length();
  1311. StringValue sb = new StringValue();
  1312. for (int i = 0; i < len; i++) {
  1313. char ch = string.charAt(i);
  1314. switch (ch) {
  1315. case '.':
  1316. case '\\':
  1317. case '+':
  1318. case '*':
  1319. case '?':
  1320. case '[':
  1321. case '^':
  1322. case ']':
  1323. case '(':
  1324. case ')':
  1325. case '$':
  1326. sb.append("\\");
  1327. sb.append(ch);
  1328. break;
  1329. default:
  1330. sb.append(ch);
  1331. break;
  1332. }
  1333. }
  1334. return sb;
  1335. }
  1336. private static final boolean[] TRIM_WHITESPACE = new boolean[256];
  1337. static {
  1338. TRIM_WHITESPACE['\0'] = true;
  1339. TRIM_WHITESPACE['\b'] = true;
  1340. TRIM_WHITESPACE[' '] = true;
  1341. TRIM_WHITESPACE['\t'] = true;
  1342. TRIM_WHITESPACE['\r'] = true;
  1343. TRIM_WHITESPACE['\n'] = true;
  1344. TRIM_WHITESPACE[0x0B] = true;
  1345. }
  1346. /**
  1347. * Removes trailing whitespace.
  1348. *
  1349. * @param env the bianca environment
  1350. * @param string the string to be trimmed
  1351. * @param characters optional set of characters to trim
  1352. * @return the trimmed string
  1353. */
  1354. public static StringValue rtrim(Env env,
  1355. StringValue string,
  1356. @Optional String characters) {
  1357. if (characters == null) {
  1358. characters = "";
  1359. }
  1360. boolean[] trim;
  1361. if (characters.equals("")) {
  1362. trim = TRIM_WHITESPACE;
  1363. } else {
  1364. trim = parseCharsetBitmap(env, characters);
  1365. }
  1366. for (int i = string.length() - 1; i >= 0; i--) {
  1367. char ch = string.charAt(i);
  1368. if (ch >= 256 || !trim[ch]) {
  1369. if (i == string.length()) {
  1370. return string;
  1371. } else {
  1372. return (StringValue) string.subSequence(0, i + 1);
  1373. }
  1374. }
  1375. }
  1376. return StringValue.EMPTY;
  1377. }
  1378. /**
  1379. * Sets locale configuration.
  1380. */
  1381. public static Value setlocale(Env env,
  1382. int category,
  1383. Value localeArg,
  1384. Value[] fallback) {
  1385. LocaleInfo localeInfo = env.getLocaleInfo();
  1386. if (localeArg instanceof ArrayValue) {
  1387. for (Value value : ((ArrayValue) localeArg).values()) {
  1388. BiancaLocale locale = setLocale(localeInfo,
  1389. category,
  1390. value.toString());
  1391. if (locale != null) {
  1392. return env.createString(locale.toString());
  1393. }
  1394. }
  1395. } else {
  1396. BiancaLocale locale = setLocale(localeInfo,
  1397. category,
  1398. localeArg.toString());
  1399. if (locale != null) {
  1400. return env.createString(locale.toString());
  1401. }
  1402. }
  1403. for (int i = 0; i < fallback.length; i++) {
  1404. BiancaLocale locale = setLocale(localeInfo,
  1405. category,
  1406. fallback[i].toString());
  1407. if (locale != null) {
  1408. return env.createString(locale.toString());
  1409. }
  1410. }
  1411. return BooleanValue.FALSE;
  1412. }
  1413. /**
  1414. * Sets locale configuration.
  1415. */
  1416. private static BiancaLocale setLocale(LocaleInfo localeInfo,
  1417. int category,
  1418. String localeName) {
  1419. BiancaLocale locale = findLocale(localeName);
  1420. if (locale == null) {
  1421. return null;
  1422. }
  1423. switch (category) {
  1424. case LC_ALL:
  1425. localeInfo.setAll(locale);
  1426. return localeInfo.getMessages();
  1427. case LC_COLLATE:
  1428. localeInfo.setCollate(locale);
  1429. return localeInfo.getCollate();
  1430. case LC_CTYPE:
  1431. localeInfo.setCtype(locale);
  1432. return localeInfo.getCtype();
  1433. case LC_MONETARY:
  1434. localeInfo.setMonetary(locale);
  1435. return localeInfo.getMonetary();
  1436. case LC_NUMERIC:
  1437. localeInfo.setNumeric(locale);
  1438. return localeInfo.getNumeric();
  1439. case LC_TIME:
  1440. localeInfo.setTime(locale);
  1441. return localeInfo.getTime();
  1442. case LC_MESSAGES:
  1443. localeInfo.setMessages(locale);
  1444. return localeInfo.getMessages();
  1445. default:
  1446. return null;
  1447. }
  1448. }
  1449. /*
  1450. * Example locale: fr_FR.UTF-8@euro, french (on Windows)
  1451. * (French, France, UTF-8, with euro currency support)
  1452. */
  1453. private static BiancaLocale findLocale(String localeName) {
  1454. String language;
  1455. String country;
  1456. String charset = null;
  1457. String variant = null;
  1458. CharBuffer sb = CharBuffer.allocate();
  1459. int len = localeName.length();
  1460. int i = 0;
  1461. char ch = 0;
  1462. while (i < len && (ch = localeName.charAt(i++)) != '-' && ch != '_') {
  1463. sb.append(ch);
  1464. }
  1465. language = sb.toString();
  1466. sb.clear();
  1467. while (i < len && (ch = localeName.charAt(i)) != '.' && ch != '@') {
  1468. sb.append(ch);
  1469. i++;
  1470. }
  1471. if (ch == '.') {
  1472. i++;
  1473. }
  1474. country = sb.toString();
  1475. sb.clear();
  1476. while (i < len && (ch = localeName.charAt(i)) != '@') {
  1477. sb.append(ch);
  1478. i++;
  1479. }
  1480. if (sb.length() > 0) {
  1481. charset = sb.toString();
  1482. }
  1483. if (i + 1 < len) {
  1484. variant = localeName.substring(i + 1);
  1485. }
  1486. Locale locale;
  1487. // java versions >= 1.5 should automatically use the euro sign
  1488. if (variant != null && !variant.equalsIgnoreCase("euro")) {
  1489. locale = new Locale(language, country, variant);
  1490. } else if (country != null) {
  1491. locale = new Locale(language, country);
  1492. } else {
  1493. locale = new Locale(language);
  1494. }
  1495. if (isValidLocale(locale)) {
  1496. return new BiancaLocale(locale, charset);
  1497. } else {
  1498. return null;
  1499. }
  1500. }
  1501. /**
  1502. * Returns true if the locale is supported.
  1503. */
  1504. private static boolean isValidLocale(Locale locale) {
  1505. Locale[] validLocales = Locale.getAvailableLocales();
  1506. for (int i = 0; i < validLocales.length; i++) {
  1507. if (validLocales[i].equals(locale)) {
  1508. return true;
  1509. }
  1510. }
  1511. return false;
  1512. }
  1513. /**
  1514. * returns the md5 hash
  1515. *
  1516. * @param source the string
  1517. * @param rawOutput if true, return the raw binary
  1518. * @return a string of imploded values
  1519. */
  1520. public static Value sha1(Env env,
  1521. String source,
  1522. @Optional boolean rawOutput) {
  1523. if (source == null

Large files files are truncated, but you can click here to view the full file