PageRenderTime 41ms CodeModel.GetById 31ms RepoModel.GetById 0ms app.codeStats 1ms

/src/main/java/org/rythmengine/utils/S.java

http://github.com/greenlaw110/Rythm
Java | 1578 lines | 701 code | 124 blank | 753 comment | 209 complexity | 0297f1beba562f59635ee75ac2855975 MD5 | raw file
  1. /*
  2. * Copyright (C) 2013-2016 The Rythm Engine project
  3. * for LICENSE and other details see:
  4. * https://github.com/rythmengine/rythmengine
  5. */
  6. package org.rythmengine.utils;
  7. /*-
  8. * #%L
  9. * Rythm Template Engine
  10. * %%
  11. * Copyright (C) 2017 - 2021 OSGL (Open Source General Library)
  12. * %%
  13. * Licensed under the Apache License, Version 2.0 (the "License");
  14. * you may not use this file except in compliance with the License.
  15. * You may obtain a copy of the License at
  16. *
  17. * http://www.apache.org/licenses/LICENSE-2.0
  18. *
  19. * Unless required by applicable law or agreed to in writing, software
  20. * distributed under the License is distributed on an "AS IS" BASIS,
  21. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  22. * See the License for the specific language governing permissions and
  23. * limitations under the License.
  24. * #L%
  25. */
  26. import com.alibaba.fastjson.JSON;
  27. import org.apache.commons.lang3.StringEscapeUtils;
  28. import org.rythmengine.RythmEngine;
  29. import org.rythmengine.conf.RythmConfiguration;
  30. import org.rythmengine.extension.IDateFormatFactory;
  31. import org.rythmengine.extension.IFormatter;
  32. import org.rythmengine.extension.II18nMessageResolver;
  33. import org.rythmengine.extension.Transformer;
  34. import org.rythmengine.internal.CacheKey;
  35. import org.rythmengine.logger.ILogger;
  36. import org.rythmengine.logger.Logger;
  37. import org.rythmengine.template.ITemplate;
  38. import java.io.UnsupportedEncodingException;
  39. import java.lang.reflect.Array;
  40. import java.net.URLEncoder;
  41. import java.text.*;
  42. import java.util.*;
  43. /**
  44. * A utility class providing String manipulation methods. Commonly used in template engine process.
  45. * <p/>
  46. * <p>Note all methods defined in this class is null safe.
  47. * if any input is <code>null</code> the return value is <code>""</code></p>
  48. * <p/>
  49. * <p>An instance of this utility is exposed to any Rythm template
  50. * via {@link org.rythmengine.template.TemplateBase#s()} method and can be used
  51. * in template source code freely. E.g.</p>
  52. * <p/>
  53. * <pre><code>
  54. * {@literal @}if(s().empty(name)) {
  55. * <div class="alert alert-error">user name is empty!</div>
  56. * }
  57. * </code></pre>
  58. */
  59. public class S {
  60. public static final S INSTANCE = new S();
  61. public static final String EMPTY_STR = "";
  62. @SuppressWarnings("unused")
  63. private static final ILogger logger = Logger.get(S.class);
  64. /**
  65. * Determine if a given String is null or empty. By empty it
  66. * means equals to <code>""</code> after do a {@link String#trim()}
  67. * operation on it
  68. *
  69. * @param s
  70. * @return true if the String specified is empty or null
  71. */
  72. public static boolean isEmpty(String s) {
  73. return null == s || "".equals(s.trim());
  74. }
  75. /**
  76. * Alias of {@link #isEmpty(String)}
  77. *
  78. * @param s
  79. * @return true if the given string is empty
  80. */
  81. public static boolean empty(String s) {
  82. return null == s || "".equals(s.trim());
  83. }
  84. /**
  85. * Determine if a given String is NOT null or empty.
  86. *
  87. * @param s
  88. * @return false if the String specified is empty or null
  89. * @see #isEmpty(String)
  90. */
  91. public static boolean isNotEmpty(String s) {
  92. return !isEmpty(s);
  93. }
  94. /**
  95. * Alias of {@link #isNotEmpty(String)}
  96. *
  97. * @param s
  98. * @return true if the give string is not empty
  99. */
  100. public static boolean notEmpty(String s) {
  101. return !isEmpty(s);
  102. }
  103. /**
  104. * Determine if a given Object instance is null or empty after it
  105. * converted to a String.
  106. *
  107. * @param o
  108. * @return true if the object string representation is empty
  109. * @see #isEmpty(String)
  110. */
  111. public static boolean isEmpty(Object o) {
  112. return null == o || "".equals(o.toString().trim());
  113. }
  114. /**
  115. * Alias of {@link #isEmpty(Object)}
  116. *
  117. * @param o
  118. * @return true if the object string representation is empty
  119. */
  120. public static boolean empty(Object o) {
  121. return null == o || "".equals(str(o).trim());
  122. }
  123. /**
  124. * Determine if a given Object instance is NOT null or empty.
  125. *
  126. * @param o
  127. * @return false if the String specified is empty or null
  128. * @see #isEmpty(Object)
  129. */
  130. public static boolean isNotEmpty(Object o) {
  131. return !isEmpty(o);
  132. }
  133. /**
  134. * Alias of {@link #isNotEmpty(Object)}
  135. *
  136. * @param o
  137. * @return true if object str representation is not empty
  138. */
  139. public static boolean notEmpty(Object o) {
  140. return !isEmpty(o);
  141. }
  142. /**
  143. * The modifier used to indicate the comparison should
  144. * ignore case
  145. *
  146. * @see #isEqual(String, String, int)
  147. */
  148. public static final int IGNORECASE = 0x00001000;
  149. /**
  150. * The modifier used to indicate the comparison should
  151. * ignore space
  152. *
  153. * @see #isEqual(String, String, int)
  154. */
  155. public static final int IGNORESPACE = 0x00002000;
  156. /**
  157. * Check if two String is equal. This comparison is {@link #IGNORECASE case sensitive}
  158. * and {@link #IGNORESPACE space sensitive}
  159. *
  160. * @param s1
  161. * @param s2
  162. * @return true if the two specified Strings are equal to each other
  163. */
  164. public static boolean isEqual(String s1, String s2) {
  165. return isEqual(s1, s2, 0);
  166. }
  167. /**
  168. * Check if two String is not equal. This comparison is {@link #IGNORECASE case sensitive}
  169. * and {@link #IGNORESPACE space sensitive}
  170. *
  171. * @param s1
  172. * @param s2
  173. * @return true if the two specified Strings are not equal to each other
  174. */
  175. public static boolean isNotEqual(String s1, String s2) {
  176. return !isEqual(s1, s2, 0);
  177. }
  178. /**
  179. * Alias of {@link #isEqual(String, String)}
  180. *
  181. * @param s1
  182. * @param s2
  183. * @return true if s1 equals s2
  184. */
  185. @Transformer
  186. public static boolean eq(String s1, String s2) {
  187. return isEqual(s1, s2, 0);
  188. }
  189. /**
  190. * Alias of {@link #isNotEqual(String, String)}
  191. *
  192. * @param s1
  193. * @param s2
  194. * @return true if s1 not equals s2
  195. */
  196. @Transformer
  197. public static boolean ne(String s1, String s2) {
  198. return !isEqual(s1, s2, 0);
  199. }
  200. /**
  201. * Check if two Object is equal after converted into String
  202. *
  203. * @param o1
  204. * @param o2
  205. * @return true if the specified two object instance are equal after converting to String
  206. */
  207. public static boolean isEqual(Object o1, Object o2) {
  208. return isEqual(str(o1), str(o2));
  209. }
  210. /**
  211. * Check if two Object is not equal after converted into String
  212. *
  213. * @param o1
  214. * @param o2
  215. * @return true if the specified two object instance are not equal after converting to String
  216. */
  217. public static boolean isNotEqual(Object o1, Object o2) {
  218. return !isEqual(str(o1), str(o2));
  219. }
  220. /**
  221. * Alias of {@link #isEqual(Object, Object)}
  222. *
  223. * @param o1
  224. * @param o2
  225. * @return true if o1's str equals o2's str
  226. */
  227. public static boolean eq(Object o1, Object o2) {
  228. return isEqual(str(o1), str(o2), 0);
  229. }
  230. /**
  231. * Alias of {@link #isNotEqual(Object, Object)}
  232. *
  233. * @param o1
  234. * @param o2
  235. * @return true if o1's str not equals o2's str
  236. */
  237. public static boolean ne(Object o1, Object o2) {
  238. return isEqual(str(o1), str(o2), 0);
  239. }
  240. /**
  241. * Alias of {@link #isEqual(String, String, int)}
  242. *
  243. * @param s1
  244. * @param s2
  245. * @param modifier
  246. * @return true if o1's str equals o2's str
  247. */
  248. public static boolean eq(String s1, String s2, int modifier) {
  249. return isEqual(s1, s2, modifier);
  250. }
  251. /**
  252. * Alias of {@link #isNotEqual(String, String, int)}
  253. *
  254. * @param s1
  255. * @param s2
  256. * @param modifier
  257. * @return true if o1's str not equals o2's str
  258. */
  259. public static boolean ne(String s1, String s2, int modifier) {
  260. return !isEqual(s1, s2, modifier);
  261. }
  262. /**
  263. * Determine whether two string instance is equal based on
  264. * the modifier passed in.
  265. * <p/>
  266. * <p>
  267. * is 2 strings equal case insensitive?
  268. * <code>S.isEqual(s1, s2, S.IGNORECASE)</code>
  269. * </p>
  270. * <p/>
  271. * <p>
  272. * is 2 strings equals case and space insensitive?
  273. * <code>S.isEqual(s1, s2, S.IGNORECASE & S.IGNORESPACE)</code>
  274. * </p>
  275. *
  276. * @param s1
  277. * @param s2
  278. * @param modifier
  279. * @return true if s1 equals s2
  280. */
  281. public static boolean isEqual(String s1, String s2, int modifier) {
  282. if (s1 == s2) return true;
  283. if (null == s1) {
  284. return s2 == null;
  285. }
  286. if (null == s2) {
  287. return false;
  288. }
  289. if ((modifier & IGNORESPACE) != 0) {
  290. s1 = s1.trim();
  291. s2 = s2.trim();
  292. }
  293. if ((modifier & IGNORECASE) != 0) {
  294. return s1.equalsIgnoreCase(s2);
  295. } else {
  296. return s1.equals(s2);
  297. }
  298. }
  299. /**
  300. * The counter function of {@link #isEqual(String, String, int)}
  301. *
  302. * @param s1
  303. * @param s2
  304. * @param modifier
  305. * @return true if s1 not equals s2
  306. */
  307. public static boolean isNotEqual(String s1, String s2, int modifier) {
  308. return !isEqual(s1, s2, modifier);
  309. }
  310. /**
  311. * Alias of {@link #toString(Object)}
  312. *
  313. * @param o
  314. * @return the string representation of object
  315. */
  316. public static String str(Object o) {
  317. return null == o ? "" : o.toString();
  318. }
  319. /**
  320. * Alias of {@link #toString(Object)}
  321. *
  322. * @param o
  323. * @return the string representation of object
  324. */
  325. public static String string(Object o) {
  326. return null == o ? "" : o.toString();
  327. }
  328. /**
  329. * Safe convert an Object to String. if the Object
  330. * is <code>null</code> than <code>""</code> is
  331. * returned
  332. *
  333. * @param o
  334. * @return String representation of the object
  335. */
  336. public static String toString(Object o) {
  337. return null == o ? "" : o.toString();
  338. }
  339. /**
  340. * Remove all line breaks from string representation of specified object O
  341. *
  342. * @param o
  343. * @return String
  344. */
  345. public static String removeAllLineBreaks(Object o) {
  346. String s = str(o);
  347. return s.replaceAll("[\n\r]+", " ");
  348. }
  349. /**
  350. * Return a {@link org.rythmengine.utils.RawData} type wrapper of
  351. * an object without any escaping.
  352. *
  353. * @param o
  354. * @return raw data
  355. */
  356. @Transformer
  357. public static RawData raw(Object o) {
  358. return new RawData(o);
  359. }
  360. /**
  361. * Return a {@link org.rythmengine.utils.RawData} type wrapper of
  362. * an object without escaping or if the current template exists
  363. * return the escape specified by the current escape scheme of the current
  364. * render template
  365. * <p/>
  366. * <p>Object is {@link #toString(Object) converted to String} before escaping</p>
  367. *
  368. * @param o
  369. * @return escaped data
  370. */
  371. @Transformer(requireTemplate = true)
  372. public static RawData escape(Object o) {
  373. return escape(null, o);
  374. }
  375. /**
  376. * The template implicit argument version of {@link #escape(Object)}
  377. *
  378. * @param template
  379. * @param o
  380. * @return escaped data
  381. */
  382. public static RawData escape(ITemplate template, Object o) {
  383. if (empty(o)) return RawData.NULL;
  384. Escape escape;
  385. if (null != template) {
  386. escape = template.__curEscape();
  387. } else {
  388. RythmEngine engine = RythmEngine.get();
  389. if (null != engine) {
  390. escape = engine.conf().defaultCodeType().escape();
  391. } else {
  392. escape = Escape.RAW;
  393. }
  394. }
  395. return escape.apply(o);
  396. }
  397. /**
  398. * Return a {@link org.rythmengine.utils.RawData} type wrapper of
  399. * an object with specified escaping scheme.
  400. * <p/>
  401. * <p>
  402. * You can pass any type of object to specify the escaping scheme. However
  403. * they will in the end converted to {@link #toString(Object) converted to String}
  404. * and then determine which escaping to use:
  405. * <p/>
  406. * <ul>
  407. * <li>json: {@link #escapeJSON(Object)} </li>
  408. * <li>xml: {@link #escapeXML(Object)} </li>
  409. * <li>javascript|js: {@link #escapeJavaScript(Object)} </li>
  410. * <li>csv: {@link #escapeCSV(Object)} </li>
  411. * <li>html: {@link #escapeCSV(Object)} </li>
  412. * </ul>
  413. * </p>
  414. * <p/>
  415. * <p>Note Object instance is converted to String before escaping</p>
  416. *
  417. * @param o
  418. * @param escape
  419. * @return escaped data
  420. */
  421. @Transformer
  422. public static RawData escape(Object o, Object escape) {
  423. if (isEmpty(o)) return RawData.NULL;
  424. if (o instanceof RawData)
  425. return (RawData) o;
  426. if (escape instanceof Escape) return ((Escape) escape).apply(o);
  427. if (isEmpty(escape)) return escape(o);
  428. String se = escape.toString();
  429. if ("json".equalsIgnoreCase(se)) return escapeJson(o);
  430. if ("xml".equalsIgnoreCase(se)) return escapeXml(o);
  431. if ("javascript".equalsIgnoreCase(se) || "js".equalsIgnoreCase(se)) return escapeJavaScript(o);
  432. if ("csv".equalsIgnoreCase(se)) return escapeCsv(o);
  433. if ("html".equalsIgnoreCase(se)) return escapeHtml(o);
  434. if ("raw".equalsIgnoreCase(se)) return raw(o);
  435. throw new IllegalArgumentException("Unknown escape scheme: " + se);
  436. }
  437. /**
  438. * Return a {@link org.rythmengine.utils.RawData} type wrapper of
  439. * an object with HTML escaping
  440. * <p/>
  441. * <p>Object is {@link #toString(Object) converted to String} before escaping</p>
  442. *
  443. * @param o
  444. * @return html escaped data
  445. */
  446. @Transformer
  447. public static RawData escapeHTML(Object o) {
  448. if (null == o) return RawData.NULL;
  449. if (o instanceof RawData) {
  450. return (RawData) o;
  451. }
  452. return new RawData(StringEscapeUtils.escapeHtml4(o.toString()));
  453. }
  454. /**
  455. * Alias of {@link #escapeHTML(Object)}
  456. *
  457. * @param o
  458. * @return html escaped data
  459. */
  460. public static RawData escapeHtml(Object o) {
  461. return escapeHTML(o);
  462. }
  463. /**
  464. * Return a {@link org.rythmengine.utils.RawData} type wrapper of
  465. * an object with CSV escaping
  466. * <p/>
  467. * <p>Object is {@link #toString(Object) converted to String} before escaping</p>
  468. *
  469. * @param o
  470. * @return csv escaped data
  471. */
  472. @Transformer
  473. public static RawData escapeCSV(Object o) {
  474. if (null == o) return RawData.NULL;
  475. if (o instanceof RawData) {
  476. return (RawData) o;
  477. }
  478. //return new RawData(StringEscapeUtils.escapeCsv(o.toString()));
  479. // fix https://github.com/greenlaw110/Rythm/issues/155
  480. return Escape.CSV.apply_(o.toString());
  481. }
  482. /**
  483. * Alias of {@link #escapeCSV(Object)}
  484. *
  485. * @param o
  486. * @return CSV escaped data
  487. */
  488. public static RawData escapeCsv(Object o) {
  489. return escapeCSV(o);
  490. }
  491. /**
  492. * Return a {@link org.rythmengine.utils.RawData} type wrapper of
  493. * an object with JSON escaping
  494. * <p/>
  495. * <p>Object is {@link #toString(Object) converted to String} before escaping</p>
  496. * <p/>
  497. * <p>After the object get escaped, the output string is safe to put into a
  498. * JSON block</p>
  499. *
  500. * @param o
  501. * @return JSON escaped data
  502. */
  503. @Transformer
  504. public static RawData escapeJSON(Object o) {
  505. if (null == o) return RawData.NULL;
  506. if (o instanceof RawData)
  507. return (RawData) o;
  508. String s0 = o.toString();
  509. s0 = StringEscapeUtils.escapeJson(s0);
  510. return new RawData(s0);
  511. }
  512. /**
  513. * Alias of {@link #escapeCSV(Object)}
  514. *
  515. * @param o
  516. * @return JSON escaped data
  517. */
  518. public static RawData escapeJson(Object o) {
  519. return escapeJSON(o);
  520. }
  521. /**
  522. * Return a {@link org.rythmengine.utils.RawData} type wrapper of
  523. * an object with JavaScript escaping
  524. * <p/>
  525. * <p>Object is {@link #toString(Object) converted to String} before escaping</p>
  526. * <p/>
  527. * <p>After the object get escaped, the output string is safe to put inside a pair of
  528. * JavaScript quotation marks</p>
  529. *
  530. * @param o
  531. * @return JavaScript escaped data
  532. */
  533. @Transformer
  534. public static RawData escapeJavaScript(Object o) {
  535. if (null == o) return RawData.NULL;
  536. if (o instanceof RawData)
  537. return (RawData) o;
  538. return new RawData(StringEscapeUtils.escapeEcmaScript(o.toString()));
  539. }
  540. /**
  541. * Alias of {@link #escapeJavaScript(Object)}
  542. *
  543. * @param o
  544. * @return JavaScript escaped data
  545. */
  546. public static RawData escapeJavascript(Object o) {
  547. return escapeJavaScript(o);
  548. }
  549. /**
  550. * Alias of {@link #escapeJavaScript(Object)}
  551. *
  552. * @param o
  553. * @return JavaScript escaped data
  554. */
  555. @Transformer
  556. public static RawData escapeJS(Object o) {
  557. return escapeJavaScript(o);
  558. }
  559. /**
  560. * Return a {@link org.rythmengine.utils.RawData} type wrapper of
  561. * an object with Java escaping
  562. * <p/>
  563. * <p>Object is {@link #toString(Object) converted to String} before escaping</p>
  564. * <p/>
  565. * <p>After the object get escaped, the output string is safe to put inside a pair of
  566. * Java quotation marks</p>
  567. *
  568. * @param o
  569. * @return Java escaped data
  570. */
  571. public static RawData escapeJava(Object o) {
  572. if (null == o) return RawData.NULL;
  573. if (o instanceof RawData)
  574. return (RawData) o;
  575. return new RawData(StringEscapeUtils.escapeJava(o.toString()));
  576. }
  577. /**
  578. * Return a {@link org.rythmengine.utils.RawData} type wrapper of
  579. * an object with XML escaping
  580. * <p/>
  581. * <p>Object is {@link #toString(Object) converted to String} before escaping</p>
  582. * <p/>
  583. * <p>After the object get escaped, the output string is safe to put inside a XML
  584. * attribute
  585. * @param o
  586. * @return XML escaped data
  587. */
  588. @Transformer
  589. public static RawData escapeXML(Object o) {
  590. if (null == o) return RawData.NULL;
  591. if (o instanceof RawData)
  592. return (RawData) o;
  593. return new RawData(_escapeXML(o.toString()));
  594. }
  595. // The code is copied from
  596. // https://github.com/httl/httl/blob/master/httl/src/main/java/httl/util/StringUtils.java#L464
  597. // we use this method to replace apache StringEscapeUtils, which is slower
  598. private static String _escapeXML(String value) {
  599. int len = value.length();
  600. if (0 == len) {
  601. return value;
  602. }
  603. StringBuilder buf = null;
  604. for (int i = 0; i < len; i++) {
  605. char ch = value.charAt(i);
  606. switch (ch) {
  607. case '<':
  608. if (buf == null) {
  609. buf = new StringBuilder(len * 2);
  610. if (i > 0) {
  611. buf.append(value.substring(0, i));
  612. }
  613. }
  614. buf.append("&lt;");
  615. break;
  616. case '>':
  617. if (buf == null) {
  618. buf = new StringBuilder(len * 2);
  619. if (i > 0) {
  620. buf.append(value.substring(0, i));
  621. }
  622. }
  623. buf.append("&gt;");
  624. break;
  625. case '\"':
  626. if (buf == null) {
  627. buf = new StringBuilder(len * 2);
  628. if (i > 0) {
  629. buf.append(value.substring(0, i));
  630. }
  631. }
  632. buf.append("&quot;");
  633. break;
  634. case '\'':
  635. if (buf == null) {
  636. buf = new StringBuilder(len * 2);
  637. if (i > 0) {
  638. buf.append(value.substring(0, i));
  639. }
  640. }
  641. buf.append("&apos;");
  642. break;
  643. case '&':
  644. if (buf == null) {
  645. buf = new StringBuilder(len * 2);
  646. if (i > 0) {
  647. buf.append(value.substring(0, i));
  648. }
  649. }
  650. buf.append("&amp;");
  651. break;
  652. default:
  653. if (buf != null) {
  654. buf.append(ch);
  655. }
  656. break;
  657. }
  658. }
  659. if (buf != null) {
  660. return buf.toString();
  661. }
  662. return value;
  663. }
  664. /**
  665. * Alias of {@link #escapeXML(Object)}
  666. *
  667. * @param o
  668. * @return XML escaped data
  669. */
  670. public static RawData escapeXml(Object o) {
  671. if (null == o) return RawData.NULL;
  672. if (o instanceof RawData)
  673. return (RawData) o;
  674. return new RawData(_escapeXML(o.toString()));
  675. }
  676. /**
  677. * Escape for regular expression
  678. *
  679. * @param o
  680. * @return Regex escaped data
  681. */
  682. public static RawData escapeRegex(Object o) {
  683. if (null == o) return RawData.NULL;
  684. if (o instanceof RawData)
  685. return (RawData) o;
  686. String s = o.toString();
  687. return new RawData(s.replaceAll("([\\/\\*\\{\\}\\<\\>\\-\\\\\\!])", "\\\\$1"));
  688. }
  689. /**
  690. * Strip the prefix and suffix from an object's String representation and
  691. * return the result
  692. * <p/>
  693. * <p>For example: </p>
  694. * <p/>
  695. * <pre><code>Object o = "xxBByy";
  696. * String s = S.strip(o, "xx", "yy")</code></pre>
  697. * <p/>
  698. * <p>At the end above code, <code>s</code> should be "BB"</p>
  699. *
  700. * @param o
  701. * @param prefix
  702. * @param suffix
  703. * @return the String result
  704. */
  705. public static String strip(Object o, String prefix, String suffix) {
  706. if (null == o) return "";
  707. String s = o.toString();
  708. s = s.trim();
  709. if (s.startsWith(prefix)) s = s.substring(prefix.length());
  710. if (s.endsWith(suffix)) s = s.substring(0, s.length() - suffix.length());
  711. return s;
  712. }
  713. /**
  714. * Strip the brace from an object's string representation and return the result
  715. *
  716. * @param o
  717. * @return the string result
  718. */
  719. public static String stripBrace(Object o) {
  720. return strip(o, "(", ")");
  721. }
  722. /**
  723. * Strip the quotation mark from an object's string representation and return the result
  724. *
  725. * @param o
  726. * @return the String result
  727. */
  728. public static String stripQuotation(Object o) {
  729. return strip(strip(o, "\"", "\""), "'", "'");
  730. }
  731. /**
  732. * Strip off both brace and quotation
  733. *
  734. * @param o
  735. * @return the string result
  736. */
  737. public static String stripBraceAndQuotation(Object o) {
  738. if (null == o) return "";
  739. String s = stripBrace(o);
  740. s = stripQuotation(s);
  741. return s;
  742. }
  743. /**
  744. * Shrink spaces in an object's string representation by merge multiple
  745. * spaces, tabs into one space, and multiple line breaks into one line break
  746. *
  747. * @param o
  748. * @return the string result
  749. */
  750. public static String shrinkSpace(Object o) {
  751. if (null == o) return "";
  752. return o.toString().replaceAll("[\r\n]+", "\n").replaceAll("[ \\t\\x0B\\f]+", " ");
  753. }
  754. private static final Range<Integer> digits = F.R(0x30, 0x3a);
  755. private static final Range<Integer> uppers = F.R(0x41, 0x5b);
  756. private static final Range<Integer> lowers = F.R(0x61, 0x7b);
  757. /**
  758. * check the given char to be a digit or alphabetic
  759. * @param c
  760. * @return true if this is a digit an upper case char or a lowercase char
  761. */
  762. public static boolean isDigitsOrAlphabetic(char c) {
  763. int i = (int)c;
  764. return digits.include(i) || uppers.include(i) || lowers.include(i);
  765. }
  766. /**
  767. * Capitalize the first character of every word of the specified object's
  768. * string representation. Words are separated by space
  769. *
  770. * @param o
  771. * @return the string result
  772. */
  773. @Transformer
  774. public static String capitalizeWords(Object o) {
  775. if (null == o) return "";
  776. String source = o.toString();
  777. char prevc = ' '; // first char of source is capitalized
  778. StringBuilder sb = new StringBuilder();
  779. for (int i = 0; i < source.length(); i++) {
  780. char c = source.charAt(i);
  781. if (c != ' ' && !isDigitsOrAlphabetic(prevc)) {
  782. sb.append(Character.toUpperCase(c));
  783. } else {
  784. sb.append(c);
  785. }
  786. prevc = c;
  787. }
  788. return sb.toString();
  789. }
  790. /**
  791. * Replace accent character (usually found in European languages) of the String representation of a
  792. * give object to non-accent char.
  793. *
  794. * @param o
  795. * @return the string result
  796. */
  797. @Transformer
  798. public static String noAccents(Object o) {
  799. if (null == o) return "";
  800. String string = o.toString();
  801. return Normalizer.normalize(string, Normalizer.Form.NFKC).replaceAll("[àáâãäåāąă]", "a").replaceAll("[çćčĉċ]", "c").replaceAll("[ďđð]", "d").replaceAll("[èéêëēęěĕė]", "e").replaceAll("[ƒſ]", "f").replaceAll("[ĝğġģ]", "g").replaceAll("[ĥħ]", "h").replaceAll("[ìíîïīĩĭįı]", "i").replaceAll("[ijĵ]", "j").replaceAll("[ķĸ]", "k").replaceAll("[łľĺļŀ]", "l").replaceAll("[ñńňņʼnŋ]", "n").replaceAll("[òóôõöøōőŏœ]", "o").replaceAll("[Þþ]", "p").replaceAll("[ŕřŗ]", "r").replaceAll("[śšşŝș]", "s").replaceAll("[ťţŧț]", "t").replaceAll("[ùúûüūůűŭũų]", "u").replaceAll("[ŵ]", "w").replaceAll("[ýÿŷ]", "y").replaceAll("[žżź]", "z").replaceAll("[æ]", "ae").replaceAll("[ÀÁÂÃÄÅĀĄĂ]", "A").replaceAll("[ÇĆČĈĊ]", "C").replaceAll("[ĎĐÐ]", "D").replaceAll("[ÈÉÊËĒĘĚĔĖ]", "E").replaceAll("[ĜĞĠĢ]", "G").replaceAll("[ĤĦ]", "H").replaceAll("[ÌÍÎÏĪĨĬĮİ]", "I").replaceAll("[Ĵ]", "J").replaceAll("[Ķ]", "K").replaceAll("[ŁĽĹĻĿ]", "L").replaceAll("[ÑŃŇŅŊ]", "N").replaceAll("[ÒÓÔÕÖØŌŐŎ]", "O").replaceAll("[ŔŘŖ]", "R").replaceAll("[ŚŠŞŜȘ]", "S").replaceAll("[ÙÚÛÜŪŮŰŬŨŲ]", "U").replaceAll("[Ŵ]", "W").replaceAll("[ÝŶŸ]", "Y").replaceAll("[ŹŽŻ]", "Z").replaceAll("[ß]", "ss");
  802. }
  803. /**
  804. * Make the first character be lowercase of the given object's string representation
  805. *
  806. * @param o
  807. * @return the string result
  808. */
  809. @Transformer
  810. public static String lowerFirst(Object o) {
  811. if (null == o) return "";
  812. String string = o.toString();
  813. if (string.length() == 0) {
  814. return string;
  815. }
  816. return ("" + string.charAt(0)).toLowerCase() + string.substring(1);
  817. }
  818. /**
  819. * Make the first character be uppercase of the given object's string representation
  820. *
  821. * @param o
  822. * @return the string result
  823. */
  824. @Transformer
  825. public static String capFirst(Object o) {
  826. if (null == o) return "";
  827. String string = o.toString();
  828. if (string.length() == 0) {
  829. return string;
  830. }
  831. return ("" + string.charAt(0)).toUpperCase() + string.substring(1);
  832. }
  833. /**
  834. * Turn an object's String representation into Camel Case
  835. *
  836. * @param obj
  837. * @return the string result
  838. */
  839. @Transformer
  840. public static String camelCase(Object obj) {
  841. if (null == obj) return "";
  842. String string = obj.toString();
  843. //string = noAccents(string);
  844. //string = string.replaceAll("[^\\w ]", "");
  845. StringBuilder result = new StringBuilder(string.length());
  846. String[] sa = string.split(" ");
  847. int l = sa.length;
  848. for (int i = 0; i < l; ++i) {
  849. if (i > 0) result.append(" ");
  850. for (String s : sa[i].split("_")) {
  851. result.append(capFirst(s));
  852. }
  853. }
  854. return result.toString();
  855. }
  856. /**
  857. * Bridge String.split() method to Object
  858. * @param o
  859. * @param sep
  860. * @return string array separated by sep
  861. */
  862. @Transformer
  863. public static String[] divide(Object o, String sep) {
  864. return str(o).split(sep);
  865. }
  866. /**
  867. * Bridge String.toUpperCase() method to Object
  868. * @param o
  869. * @return the lower case of string presentation of o
  870. */
  871. @Transformer
  872. public static String lowerCase(Object o) {
  873. return str(o).toLowerCase();
  874. }
  875. /**
  876. * Bridge String.toUpperCase() method to Object
  877. * @param o
  878. * @return the upper case of string presentation of o
  879. */
  880. @Transformer
  881. public static String upperCase(Object o) {
  882. return str(o).toUpperCase();
  883. }
  884. /**
  885. * get length of specified object
  886. *
  887. * <ul>
  888. * <li>If o is a Collection or Map, then return size of it</li>
  889. * <li>If o is an array, then return length of it</li>
  890. * <li>Otherwise return length() of String representation of the object</li>
  891. * </ul>
  892. * @param o
  893. * @return length
  894. */
  895. @Transformer
  896. public static int len(Object o) {
  897. if (o instanceof Collection) {
  898. return ((Collection)o).size();
  899. } else if (o instanceof Map) {
  900. return ((Map)o).size();
  901. } else if (o.getClass().isArray()) {
  902. return Array.getLength(o);
  903. } else {
  904. return str(o).length();
  905. }
  906. }
  907. /**
  908. * Change line break in the data string into <tt><br/></tt>
  909. * @param data
  910. * @return raw data of transformed result
  911. */
  912. @Transformer
  913. public static RawData nl2br(RawData data) {
  914. return new RawData(data.toString().replaceAll("(\\r\\n|\\n|\\r)", "<br/>"));
  915. }
  916. /**
  917. * Change line break in the data string into <tt><br/></tt>
  918. * @param data
  919. * @return raw data of transformed result
  920. */
  921. public static RawData nl2br(Object data) {
  922. return new RawData(StringEscapeUtils.escapeHtml4(str(data)).replaceAll("(\\r\\n|\\n|\\r)", "<br/>"));
  923. }
  924. /**
  925. * Change space in the data string into <tt>&nbsp;</tt>
  926. * @param data
  927. * @return raw data of transformed result
  928. */
  929. @Transformer
  930. public static RawData sp2nbsp(RawData data) {
  931. return new RawData(data.toString().replace(" ", "&nbsp;"));
  932. }
  933. /**
  934. * Change space in the data string into <tt>&nbsp</tt>
  935. * @param data
  936. * @return raw data of transformed result
  937. */
  938. public static RawData sp2nbsp(Object data) {
  939. return new RawData(StringEscapeUtils.escapeHtml4(str(data)).replace(" ", "&nbsp;"));
  940. }
  941. /**
  942. * encode using utf-8
  943. *
  944. * @param data
  945. * @return encoded
  946. */
  947. @Transformer
  948. public static String urlEncode(Object data) {
  949. if (null == data) return "";
  950. String entity = data.toString();
  951. try {
  952. String encoding = "utf-8";
  953. return URLEncoder.encode(entity, encoding);
  954. } catch (UnsupportedEncodingException e) {
  955. Logger.error(e, entity);
  956. }
  957. return entity;
  958. }
  959. /**
  960. * Format a number using default pattern, language and locale
  961. * @param number
  962. * @return the formatted string
  963. */
  964. public static String format(Number number) {
  965. return format(null, number, null, null);
  966. }
  967. /**
  968. * Format number with specified template
  969. * @param template
  970. * @param number
  971. * @return the formatted string
  972. */
  973. public static String format(ITemplate template, Number number) {
  974. return format(template, number, null, null);
  975. }
  976. /**
  977. * Format the number with specified pattern, language and locale
  978. * @param number
  979. * @param pattern
  980. * @param locale
  981. * @return the formatted String
  982. * @see DecimalFormatSymbols
  983. */
  984. @Transformer(requireTemplate = true)
  985. public static String format(Number number, String pattern, Locale locale) {
  986. return format(null, number, pattern, locale);
  987. }
  988. /**
  989. * Format the number with specified template, pattern, language and locale
  990. * @param number
  991. * @param pattern
  992. * @param locale
  993. * @return the formatted String
  994. * @see DecimalFormatSymbols
  995. */
  996. public static String format(ITemplate template, Number number, String pattern, Locale locale) {
  997. if (null == number) {
  998. throw new NullPointerException();
  999. }
  1000. if (null == locale) {
  1001. locale = I18N.locale(template);
  1002. }
  1003. NumberFormat nf;
  1004. if (null == pattern) nf = NumberFormat.getNumberInstance(locale);
  1005. else {
  1006. DecimalFormatSymbols symbols = new DecimalFormatSymbols(locale);
  1007. nf = new DecimalFormat(pattern, symbols);
  1008. }
  1009. return nf.format(number);
  1010. }
  1011. /**
  1012. * Format a number with specified pattern
  1013. *
  1014. * @param number
  1015. * @param pattern
  1016. * @return formatted String
  1017. */
  1018. @Transformer(requireTemplate = true)
  1019. public static String format(Number number, String pattern) {
  1020. return format(null, number, pattern, null);
  1021. }
  1022. /**
  1023. * Format a number with specified engine, pattern
  1024. *
  1025. * @param number
  1026. * @param pattern
  1027. * @return formatted String
  1028. */
  1029. public static String format(ITemplate template, Number number, String pattern) {
  1030. return format(template, number, pattern, null);
  1031. }
  1032. /**
  1033. * Format general object: for the sake of dynamic expr evaluation
  1034. * @param o - the object to format
  1035. * @return a formatted string representation of the object
  1036. */
  1037. public static String format(Object o) {
  1038. if (null == o) return "";
  1039. return format(null, o, null, null, null);
  1040. }
  1041. /**
  1042. * Format a date with engine's default format corresponding
  1043. * to the engine's locale configured
  1044. *
  1045. * @param date
  1046. * @return the formatted String
  1047. */
  1048. @Transformer(requireTemplate = true)
  1049. public static String format(Date date) {
  1050. return format(date, null, null, null);
  1051. }
  1052. /**
  1053. * Generalize format parameter for the sake of dynamic expr evaluation
  1054. * @param template
  1055. * @param o
  1056. * @return a formatted string representation of the object
  1057. */
  1058. public static String format(ITemplate template, Object o) {
  1059. if (null == o) return "";
  1060. return format(template, o, null, null, null);
  1061. }
  1062. /**
  1063. * Format a date with specified engine's default format corresponding
  1064. * to the engine's locale configured
  1065. *
  1066. * @param date
  1067. * @return the formatted String
  1068. */
  1069. public static String format(ITemplate template, Date date) {
  1070. return format(template, date, null, null, null);
  1071. }
  1072. /**
  1073. * Format a date with specified pattern
  1074. *
  1075. * @param date
  1076. * @param pattern
  1077. * @return formated string
  1078. */
  1079. @Transformer(requireTemplate = true)
  1080. public static String format(Date date, String pattern) {
  1081. return format(date, pattern, null, null);
  1082. }
  1083. /**
  1084. * Generalize format parameter for the sake of dynamic evaluation
  1085. * @param o
  1086. * @param pattern
  1087. * @return a formatted string representation of the object
  1088. */
  1089. public static String format(Object o, String pattern) {
  1090. if (null == o) return "";
  1091. return format(null, o, pattern, null, null);
  1092. }
  1093. /**
  1094. * Format a date with specified pattern
  1095. *
  1096. * @param template
  1097. * @param date
  1098. * @param pattern
  1099. * @return formated string
  1100. */
  1101. public static String format(ITemplate template, Date date, String pattern) {
  1102. return format(template, date, pattern, null, null);
  1103. }
  1104. /**
  1105. * Generalize format parameter for the sake of dynamic evaluation
  1106. * @param o
  1107. * @param pattern
  1108. * @return a formatted string representation of the object
  1109. */
  1110. public static String format(ITemplate template, Object o, String pattern) {
  1111. if (null == o) return "";
  1112. return format(template, o, pattern, null, null);
  1113. }
  1114. /**
  1115. * Transformer. Format a date with specified pattern, language and locale
  1116. * @param date
  1117. * @param pattern
  1118. * @param locale
  1119. * @return the formatted String
  1120. */
  1121. @Transformer(requireTemplate = true)
  1122. public static String format(Date date, String pattern, Locale locale) {
  1123. return format(date, pattern, locale, null);
  1124. }
  1125. private static String styleFormatDate(Date date, int style, ITemplate template) {
  1126. Locale locale = I18N.locale(template);
  1127. DateFormat format = DateFormat.getDateInstance(style, locale);
  1128. return format.format(date);
  1129. }
  1130. private static String styleFormatTime(Date date, int style, ITemplate template) {
  1131. Locale locale = I18N.locale(template);
  1132. DateFormat format = DateFormat.getTimeInstance(style, locale);
  1133. return format.format(date);
  1134. }
  1135. private static String styleFormatDateTime(Date date, int style, ITemplate template) {
  1136. Locale locale = I18N.locale(template);
  1137. DateFormat format = DateFormat.getDateTimeInstance(style, style, locale);
  1138. return format.format(date);
  1139. }
  1140. public static String longDate(ITemplate template, Date date) {
  1141. return styleFormatDate(date, DateFormat.LONG, template);
  1142. }
  1143. @Transformer(requireTemplate = true)
  1144. public static String longDate(Date date) {
  1145. return longDate(null, date);
  1146. }
  1147. public static String longTime(ITemplate template, Date date) {
  1148. return styleFormatTime(date, DateFormat.LONG, template);
  1149. }
  1150. @Transformer(requireTemplate = true)
  1151. public static String longTime(Date date) {
  1152. return longTime(null, date);
  1153. }
  1154. public static String longDateTime(ITemplate template, Date date) {
  1155. return styleFormatDateTime(date, DateFormat.LONG, template);
  1156. }
  1157. @Transformer(requireTemplate = true)
  1158. public static String longDateTime(Date date) {
  1159. return longDateTime(null, date);
  1160. }
  1161. public static String mediumDate(ITemplate template, Date date) {
  1162. return styleFormatDate(date, DateFormat.MEDIUM, template);
  1163. }
  1164. @Transformer(requireTemplate = true)
  1165. public static String mediumDate(Date date) {
  1166. return mediumDate(null, date);
  1167. }
  1168. public static String mediumTime(ITemplate template, Date date) {
  1169. return styleFormatTime(date, DateFormat.MEDIUM, template);
  1170. }
  1171. @Transformer(requireTemplate = true)
  1172. public static String mediumTime(Date date) {
  1173. return mediumTime(null, date);
  1174. }
  1175. public static String mediumDateTime(ITemplate template, Date date) {
  1176. return styleFormatDateTime(date, DateFormat.MEDIUM, template);
  1177. }
  1178. @Transformer(requireTemplate = true)
  1179. public static String mediumDateTime(Date date) {
  1180. return mediumDateTime(null, date);
  1181. }
  1182. public static String shortDate(ITemplate template, Date date) {
  1183. return styleFormatDate(date, DateFormat.SHORT, template);
  1184. }
  1185. @Transformer(requireTemplate = true)
  1186. public static String shortDate(Date date) {
  1187. return shortDate(null, date);
  1188. }
  1189. public static String shortTime(ITemplate template, Date date) {
  1190. return styleFormatTime(date, DateFormat.SHORT, template);
  1191. }
  1192. @Transformer(requireTemplate = true)
  1193. public static String shortTime(Date date) {
  1194. return shortTime(null, date);
  1195. }
  1196. public static String shortDateTime(ITemplate template, Date date) {
  1197. return styleFormatDateTime(date, DateFormat.SHORT, template);
  1198. }
  1199. @Transformer(requireTemplate = true)
  1200. public static String shortDateTime(Date date) {
  1201. return shortDateTime(null, date);
  1202. }
  1203. /**
  1204. * Generalize format parameter for the sake of dynamic evaluation
  1205. * @param o
  1206. * @param pattern
  1207. * @return a formatted string representation of the object
  1208. */
  1209. public static String format(Object o, String pattern, Locale locale) {
  1210. if (null == o) return "";
  1211. return format(null, o, pattern, locale, null);
  1212. }
  1213. /**
  1214. * See {@link #format(org.rythmengine.template.ITemplate, java.util.Date, String, java.util.Locale, String)}
  1215. * @param template
  1216. * @param date
  1217. * @param pattern
  1218. * @param locale
  1219. * @return formatted date string
  1220. */
  1221. public static String format(ITemplate template, Date date, String pattern, Locale locale) {
  1222. return format(template, date, pattern, locale, null);
  1223. }
  1224. /**
  1225. * Generalize format parameter for the sake of dynamic evaluation
  1226. * @param o
  1227. * @param pattern
  1228. * @return a formatted string representation of the object
  1229. */
  1230. public static String format(ITemplate template, Object o, String pattern, Locale locale) {
  1231. if (null == o) return "";
  1232. return format(template, o, pattern, locale, null);
  1233. }
  1234. /**
  1235. * Transformer. Format a date with specified pattern, lang, locale and timezone.
  1236. *
  1237. * @param date
  1238. * @param pattern
  1239. * @param locale
  1240. * @param timezone
  1241. * @return the formatted String
  1242. * @see SimpleDateFormat
  1243. */
  1244. @Transformer(requireTemplate = true)
  1245. public static String format(Date date, String pattern, Locale locale, String timezone) {
  1246. return format(null, date, pattern, locale, timezone);
  1247. }
  1248. /**
  1249. * Generalize format parameter for the sake of dynamic evaluation
  1250. * @param o
  1251. * @param pattern
  1252. * @return a formatted string representation of the object
  1253. */
  1254. public static String format(Object o, String pattern, Locale locale, String timezone) {
  1255. if (null == o) return "";
  1256. return format(null, o, pattern, locale, timezone);
  1257. }
  1258. /**
  1259. * Format a date with specified pattern, lang, locale and timezone. The locale
  1260. * comes from the engine instance specified
  1261. *
  1262. * @param template
  1263. * @param date
  1264. * @param pattern
  1265. * @param locale
  1266. * @param timezone
  1267. * @return format result
  1268. */
  1269. public static String format(ITemplate template, Date date, String pattern, Locale locale, String timezone) {
  1270. if (null == date) throw new NullPointerException();
  1271. RythmEngine engine = null == template ? RythmEngine.get() : template.__engine();
  1272. DateFormat df = (null == engine ? IDateFormatFactory.DefaultDateFormatFactory.INSTANCE : engine.dateFormatFactory()).createDateFormat(template, pattern, locale, timezone);
  1273. return df.format(date);
  1274. }
  1275. /**
  1276. * Generalize format parameter for the sake of dynamic evaluation
  1277. * @param o
  1278. * @param pattern
  1279. * @return a formatted string representation of the object
  1280. */
  1281. public static String format(ITemplate template, Object o, String pattern, Locale locale, String timezone) {
  1282. if (null == o) return "";
  1283. if (o instanceof Date) return format(template, (Date) o, pattern, locale, timezone);
  1284. if (o instanceof Number) return format(template, (Number) o, pattern, locale);
  1285. if (null == locale) {
  1286. locale = I18N.locale(template);
  1287. }
  1288. RythmEngine engine = null == template ? RythmEngine.get() : template.__engine();
  1289. if (null == engine) return o.toString();
  1290. for(IFormatter fmt: engine.extensionManager().formatters()) {
  1291. String s = fmt.format(o, pattern, locale, timezone);
  1292. if (null != s) {
  1293. return s;
  1294. }
  1295. }
  1296. return o.toString();
  1297. }
  1298. /**
  1299. * Format size (e.g. disk space in bytes) into human readable style
  1300. * <ul>
  1301. * <li>When size is smaller than <code>1024L</code>, return size + <code>B</code></li>
  1302. * <li>When size is smaller than <code>1024L ^ 2</code>, return size/1024L + <code>KB</code></li>
  1303. * <li>When size is smaller than <code>1024L ^ 3</code>, return size/1048576L + <code>MB</code></li>
  1304. * <li>When size is smaller than <code>1024L ^ 4</code>, return size/1073741824L + <code>GB</code></li>
  1305. * </ul>
  1306. *
  1307. * <p>The method accept any data type. When <code>null</code> is found then
  1308. * <code>NullPointerException</code> will be thrown out; if an <code>Number</code>
  1309. * is passed in, it will be type cast to <code>Long</code>; otherwise
  1310. * a <code>Long.valueOf(data.toString())</code> is used to find out
  1311. * the number</p>
  1312. *
  1313. * @param data
  1314. * @return formatted string result
  1315. */
  1316. @Transformer
  1317. public static String formatSize(Object data) {
  1318. if (null == data) throw new NullPointerException();
  1319. Long bytes;
  1320. if (data instanceof Number) {
  1321. if (data instanceof Long) {
  1322. bytes = (Long) data;
  1323. } else {
  1324. bytes = ((Number)data).longValue();
  1325. }
  1326. } else {
  1327. bytes = Long.valueOf(data.toString());
  1328. }
  1329. if (bytes < 1024L) {
  1330. return bytes + " B";
  1331. }
  1332. if (bytes < 1048576L) {
  1333. return bytes / 1024L + "KB";
  1334. }
  1335. if (bytes < 1073741824L) {
  1336. return bytes / 1048576L + "MB";
  1337. }
  1338. return bytes / 1073741824L + "GB";
  1339. }
  1340. /**
  1341. * Transformer method. Format given data into currency
  1342. *
  1343. * @param data
  1344. * @return the currency
  1345. * See {@link #formatCurrency(org.rythmengine.template.ITemplate,Object,String,java.util.Locale)}
  1346. */
  1347. @Transformer(requireTemplate = true)
  1348. public static String formatCurrency(Object data) {
  1349. return formatCurrency(null, data, null, null);
  1350. }
  1351. /**
  1352. * See {@link #formatCurrency(org.rythmengine.template.ITemplate, Object)}
  1353. *
  1354. * @param template
  1355. * @param data
  1356. * @return the currency string
  1357. */
  1358. public static String formatCurrency(ITemplate template, Object data) {
  1359. return formatCurrency(template, data, null, null);
  1360. }
  1361. /**
  1362. * Transformer method. Format currency using specified currency code
  1363. *
  1364. * @param data
  1365. * @param currencyCode
  1366. * @return the currency
  1367. * See {@link #formatCurrency(org.rythmengine.template.ITemplate, Object, String, java.util.Locale)}
  1368. */
  1369. @Transformer(requireTemplate = true)
  1370. public static String formatCurrency(Object data, String currencyCode) {
  1371. return formatCurrency(null, data, currencyCode, null);
  1372. }
  1373. /**
  1374. * Transformer method. Format currency using specified parameters
  1375. * @param template
  1376. * @param data
  1377. * @param currencyCode
  1378. * @return the currency string
  1379. * See {@link #formatCurrency(org.rythmengine.template.ITemplate, Object, String, java.util.Locale)}
  1380. */
  1381. public static String formatCurrency(ITemplate template, Object data, String currencyCode) {
  1382. return formatCurrency(template, data, currencyCode, null);
  1383. }
  1384. /**
  1385. * See {@link #formatCurrency(org.rythmengine.template.ITemplate, Object, String, java.util.Locale)}
  1386. *
  1387. * @param data
  1388. * @param currencyCode
  1389. * @param locale
  1390. * @return the currency string
  1391. */
  1392. public static String formatCurrency(Object data, String currencyCode, Locale locale) {
  1393. return formatCurrency(null, data, currencyCode, locale);
  1394. }
  1395. /**
  1396. * Format give data into currency using locale info from the engine specified
  1397. *
  1398. * <p>The method accept any data type. When <code>null</code> is found then
  1399. * <code>NullPointerException</code> will be thrown out; if an <code>Number</code>
  1400. * is passed in, it will be type cast to <code>Number</code>; otherwise
  1401. * a <code>Double.valueOf(data.toString())</code> is used to find out
  1402. * the number</p>
  1403. *
  1404. * @param template
  1405. * @param data
  1406. * @param currencyCode
  1407. * @param locale
  1408. * @return the currency
  1409. */
  1410. public static String formatCurrency(ITemplate template, Object data, String currencyCode, Locale locale) {
  1411. if (null == data) throw new NullPointerException();
  1412. Number number;
  1413. if (data instanceof Number) {
  1414. number = (Number)data;
  1415. } else {
  1416. number = Double.parseDouble(data.toString());
  1417. }
  1418. if (null == locale) locale = I18N.locale(template);
  1419. if (null == locale.getCountry() || locale.getCountry().length() != 2) {
  1420. // try best to guess
  1421. String lan = locale.getLanguage();
  1422. if (eq(lan, "en")) {
  1423. if (null != currencyCode) {
  1424. if (eq("AUD", currencyCode)) {
  1425. locale = new Locale(lan, "AU");
  1426. } else if (eq("USD", currencyCode)) {
  1427. locale = Locale.US;
  1428. } else if (eq("GBP", currencyCode)) {
  1429. locale = Locale.UK;
  1430. }
  1431. }
  1432. } else if (eq(lan, "zh")) {
  1433. locale = Locale.SIMPLIFIED_CHINESE;
  1434. }
  1435. }
  1436. NumberFormat numberFormat = NumberFormat.getCurrencyInstance(locale);
  1437. Currency currency = null;
  1438. if (null == currencyCode) {
  1439. String country = locale.getCountry();
  1440. if (null != country && country.length() == 2) {
  1441. currency = Currency.getInstance(locale);
  1442. }
  1443. if (null == currency) currencyCode = "$"; // default
  1444. }
  1445. if (null == currency) {
  1446. if (currencyCode.length() != 3) {
  1447. // it must be something like '$' or '¥' etc
  1448. } else {
  1449. currency = Currency.getInstance(currencyCode);
  1450. }
  1451. }
  1452. if (null != currency) {
  1453. numberFormat.setCurrency(currency);
  1454. numberFormat.setMaximumFractionDigits(currency.g