PageRenderTime 57ms CodeModel.GetById 19ms RepoModel.GetById 0ms app.codeStats 0ms

/branches/2.3-gae/freemarker/src/freemarker/template/utility/StringUtil.java

#
Java | 1468 lines | 1268 code | 40 blank | 160 comment | 246 complexity | 159d51c3d22f67ed31d5114d03412da4 MD5 | raw file
Possible License(s): Apache-2.0, BSD-3-Clause

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

  1. /*
  2. * Copyright (c) 2003 The Visigoth Software Society. All rights
  3. * reserved.
  4. *
  5. * Redistribution and use in source and binary forms, with or without
  6. * modification, are permitted provided that the following conditions
  7. * are met:
  8. *
  9. * 1. Redistributions of source code must retain the above copyright
  10. * notice, this list of conditions and the following disclaimer.
  11. *
  12. * 2. Redistributions in binary form must reproduce the above copyright
  13. * notice, this list of conditions and the following disclaimer in
  14. * the documentation and/or other materials provided with the
  15. * distribution.
  16. *
  17. * 3. The end-user documentation included with the redistribution, if
  18. * any, must include the following acknowledgement:
  19. * "This product includes software developed by the
  20. * Visigoth Software Society (http://www.visigoths.org/)."
  21. * Alternately, this acknowledgement may appear in the software itself,
  22. * if and wherever such third-party acknowledgements normally appear.
  23. *
  24. * 4. Neither the name "FreeMarker", "Visigoth", nor any of the names of the
  25. * project contributors may be used to endorse or promote products derived
  26. * from this software without prior written permission. For written
  27. * permission, please contact visigoths@visigoths.org.
  28. *
  29. * 5. Products derived from this software may not be called "FreeMarker" or "Visigoth"
  30. * nor may "FreeMarker" or "Visigoth" appear in their names
  31. * without prior written permission of the Visigoth Software Society.
  32. *
  33. * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
  34. * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
  35. * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
  36. * DISCLAIMED. IN NO EVENT SHALL THE VISIGOTH SOFTWARE SOCIETY OR
  37. * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
  38. * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
  39. * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
  40. * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
  41. * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
  42. * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
  43. * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  44. * SUCH DAMAGE.
  45. * ====================================================================
  46. *
  47. * This software consists of voluntary contributions made by many
  48. * individuals on behalf of the Visigoth Software Society. For more
  49. * information on the Visigoth Software Society, please see
  50. * http://www.visigoths.org/
  51. */
  52. package freemarker.template.utility;
  53. import java.io.UnsupportedEncodingException;
  54. import java.util.HashMap;
  55. import java.util.Locale;
  56. import java.util.Map;
  57. import java.util.StringTokenizer;
  58. import freemarker.core.Environment;
  59. import freemarker.core.ParseException;
  60. import freemarker.template.Template;
  61. /**
  62. * Some text related utilities.
  63. *
  64. * @version $Id: StringUtil.java,v 1.48 2005/06/01 22:39:08 ddekany Exp $
  65. */
  66. public class StringUtil {
  67. private static final char[] ESCAPES = createEscapes();
  68. /*
  69. * For better performance most methods are folded down. Don't you scream... :)
  70. */
  71. /**
  72. * HTML encoding (does not convert line breaks).
  73. * Replaces all '>' '<' '&' and '"' with entity reference
  74. */
  75. public static String HTMLEnc(String s) {
  76. return XMLEncNA(s);
  77. }
  78. /**
  79. * XML Encoding.
  80. * Replaces all '>' '<' '&', "'" and '"' with entity reference
  81. */
  82. public static String XMLEnc(String s) {
  83. return XMLOrXHTMLEnc(s, "'");
  84. }
  85. /**
  86. * XHTML Encoding.
  87. * Replaces all '>' '<' '&', "'" and '"' with entity reference
  88. * suitable for XHTML decoding in common user agents (including legacy
  89. * user agents, which do not decode "'" to "'", so "'" is used
  90. * instead [see http://www.w3.org/TR/xhtml1/#C_16])
  91. */
  92. public static String XHTMLEnc(String s) {
  93. return XMLOrXHTMLEnc(s, "'");
  94. }
  95. private static String XMLOrXHTMLEnc(String s, String aposReplacement) {
  96. int ln = s.length();
  97. for (int i = 0; i < ln; i++) {
  98. char c = s.charAt(i);
  99. if (c == '<' || c == '>' || c == '&' || c == '"' || c == '\'') {
  100. StringBuffer b =
  101. new StringBuffer(s.substring(0, i));
  102. switch (c) {
  103. case '<': b.append("&lt;"); break;
  104. case '>': b.append("&gt;"); break;
  105. case '&': b.append("&amp;"); break;
  106. case '"': b.append("&quot;"); break;
  107. case '\'': b.append(aposReplacement); break;
  108. }
  109. i++;
  110. int next = i;
  111. while (i < ln) {
  112. c = s.charAt(i);
  113. if (c == '<' || c == '>' || c == '&' || c == '"' || c == '\'') {
  114. b.append(s.substring(next, i));
  115. switch (c) {
  116. case '<': b.append("&lt;"); break;
  117. case '>': b.append("&gt;"); break;
  118. case '&': b.append("&amp;"); break;
  119. case '"': b.append("&quot;"); break;
  120. case '\'': b.append(aposReplacement); break;
  121. }
  122. next = i + 1;
  123. }
  124. i++;
  125. }
  126. if (next < ln) b.append(s.substring(next));
  127. s = b.toString();
  128. break;
  129. } // if c ==
  130. } // for
  131. return s;
  132. }
  133. /**
  134. * XML encoding without replacing apostrophes.
  135. * @see #XMLEnc(String)
  136. */
  137. public static String XMLEncNA(String s) {
  138. int ln = s.length();
  139. for (int i = 0; i < ln; i++) {
  140. char c = s.charAt(i);
  141. if (c == '<' || c == '>' || c == '&' || c == '"') {
  142. StringBuffer b =
  143. new StringBuffer(s.substring(0, i));
  144. switch (c) {
  145. case '<': b.append("&lt;"); break;
  146. case '>': b.append("&gt;"); break;
  147. case '&': b.append("&amp;"); break;
  148. case '"': b.append("&quot;"); break;
  149. }
  150. i++;
  151. int next = i;
  152. while (i < ln) {
  153. c = s.charAt(i);
  154. if (c == '<' || c == '>' || c == '&' || c == '"') {
  155. b.append(s.substring(next, i));
  156. switch (c) {
  157. case '<': b.append("&lt;"); break;
  158. case '>': b.append("&gt;"); break;
  159. case '&': b.append("&amp;"); break;
  160. case '"': b.append("&quot;"); break;
  161. }
  162. next = i + 1;
  163. }
  164. i++;
  165. }
  166. if (next < ln) b.append(s.substring(next));
  167. s = b.toString();
  168. break;
  169. } // if c ==
  170. } // for
  171. return s;
  172. }
  173. /**
  174. * XML encoding for attributes valies quoted with <tt>"</tt> (not with <tt>'</tt>!).
  175. * Also can be used for HTML attributes that are quoted with <tt>"</tt>.
  176. * @see #XMLEnc(String)
  177. */
  178. public static String XMLEncQAttr(String s) {
  179. int ln = s.length();
  180. for (int i = 0; i < ln; i++) {
  181. char c = s.charAt(i);
  182. if (c == '<' || c == '&' || c == '"') {
  183. StringBuffer b =
  184. new StringBuffer(s.substring(0, i));
  185. switch (c) {
  186. case '<': b.append("&lt;"); break;
  187. case '&': b.append("&amp;"); break;
  188. case '"': b.append("&quot;"); break;
  189. }
  190. i++;
  191. int next = i;
  192. while (i < ln) {
  193. c = s.charAt(i);
  194. if (c == '<' || c == '&' || c == '"') {
  195. b.append(s.substring(next, i));
  196. switch (c) {
  197. case '<': b.append("&lt;"); break;
  198. case '&': b.append("&amp;"); break;
  199. case '"': b.append("&quot;"); break;
  200. }
  201. next = i + 1;
  202. }
  203. i++;
  204. }
  205. if (next < ln) {
  206. b.append(s.substring(next));
  207. }
  208. s = b.toString();
  209. break;
  210. } // if c ==
  211. } // for
  212. return s;
  213. }
  214. /**
  215. * XML encoding without replacing apostrophes and quotation marks and
  216. * greater-thans (except in {@code ]]>}).
  217. * @see #XMLEnc(String)
  218. */
  219. public static String XMLEncNQG(String s) {
  220. int ln = s.length();
  221. for (int i = 0; i < ln; i++) {
  222. char c = s.charAt(i);
  223. if (c == '<'
  224. || (c == '>' && i > 1
  225. && s.charAt(i - 1) == ']'
  226. && s.charAt(i - 2) == ']')
  227. || c == '&') {
  228. StringBuffer b =
  229. new StringBuffer(s.substring(0, i));
  230. switch (c) {
  231. case '<': b.append("&lt;"); break;
  232. case '>': b.append("&gt;"); break;
  233. case '&': b.append("&amp;"); break;
  234. default: throw new RuntimeException("Bug: unexpected char");
  235. }
  236. i++;
  237. int next = i;
  238. while (i < ln) {
  239. c = s.charAt(i);
  240. if (c == '<'
  241. || (c == '>' && i > 1
  242. && s.charAt(i - 1) == ']'
  243. && s.charAt(i - 2) == ']')
  244. || c == '&') {
  245. b.append(s.substring(next, i));
  246. switch (c) {
  247. case '<': b.append("&lt;"); break;
  248. case '>': b.append("&gt;"); break;
  249. case '&': b.append("&amp;"); break;
  250. default: throw new RuntimeException("Bug: unexpected char");
  251. }
  252. next = i + 1;
  253. }
  254. i++;
  255. }
  256. if (next < ln) {
  257. b.append(s.substring(next));
  258. }
  259. s = b.toString();
  260. break;
  261. } // if c ==
  262. } // for
  263. return s;
  264. }
  265. /**
  266. * Rich Text Format encoding (does not replace line breaks).
  267. * Escapes all '\' '{' '}' and '"'
  268. */
  269. public static String RTFEnc(String s) {
  270. int ln = s.length();
  271. for (int i = 0; i < ln; i++) {
  272. char c = s.charAt(i);
  273. if (c == '\\' || c == '{' || c == '}') {
  274. StringBuffer b =
  275. new StringBuffer(s.substring(0, i));
  276. switch (c) {
  277. case '\\': b.append("\\\\"); break;
  278. case '{': b.append("\\{"); break;
  279. case '}': b.append("\\}"); break;
  280. }
  281. i++;
  282. int next = i;
  283. while (i < ln) {
  284. c = s.charAt(i);
  285. if (c == '\\' || c == '{' || c == '}') {
  286. b.append(s.substring(next, i));
  287. switch (c) {
  288. case '\\': b.append("\\\\"); break;
  289. case '{': b.append("\\{"); break;
  290. case '}': b.append("\\}"); break;
  291. }
  292. next = i + 1;
  293. }
  294. i++;
  295. }
  296. if (next < ln) b.append(s.substring(next));
  297. s = b.toString();
  298. break;
  299. } // if c ==
  300. } // for
  301. return s;
  302. }
  303. /**
  304. * URL encoding (like%20this).
  305. */
  306. public static String URLEnc(String s, String charset)
  307. throws UnsupportedEncodingException {
  308. int ln = s.length();
  309. int i;
  310. for (i = 0; i < ln; i++) {
  311. char c = s.charAt(i);
  312. if (!(c >= 'a' && c <= 'z' || c >= 'A' && c <= 'Z'
  313. || c >= '0' && c <= '9'
  314. || c == '_' || c == '-' || c == '.' || c == '!' || c == '~'
  315. || c >= '\'' && c <= '*')) {
  316. break;
  317. }
  318. }
  319. if (i == ln) {
  320. // Nothing to escape
  321. return s;
  322. }
  323. StringBuffer b = new StringBuffer(ln + ln / 3 + 2);
  324. b.append(s.substring(0, i));
  325. int encstart = i;
  326. for (i++; i < ln; i++) {
  327. char c = s.charAt(i);
  328. if (c >= 'a' && c <= 'z' || c >= 'A' && c <= 'Z'
  329. || c >= '0' && c <= '9'
  330. || c == '_' || c == '-' || c == '.' || c == '!' || c == '~'
  331. || c >= '\'' && c <= '*') {
  332. if (encstart != -1) {
  333. byte[] o = s.substring(encstart, i).getBytes(charset);
  334. for (int j = 0; j < o.length; j++) {
  335. b.append('%');
  336. byte bc = o[j];
  337. int c1 = bc & 0x0F;
  338. int c2 = (bc >> 4) & 0x0F;
  339. b.append((char) (c2 < 10 ? c2 + '0' : c2 - 10 + 'A'));
  340. b.append((char) (c1 < 10 ? c1 + '0' : c1 - 10 + 'A'));
  341. }
  342. encstart = -1;
  343. }
  344. b.append(c);
  345. } else {
  346. if (encstart == -1) {
  347. encstart = i;
  348. }
  349. }
  350. }
  351. if (encstart != -1) {
  352. byte[] o = s.substring(encstart, i).getBytes(charset);
  353. for (int j = 0; j < o.length; j++) {
  354. b.append('%');
  355. byte bc = o[j];
  356. int c1 = bc & 0x0F;
  357. int c2 = (bc >> 4) & 0x0F;
  358. b.append((char) (c2 < 10 ? c2 + '0' : c2 - 10 + 'A'));
  359. b.append((char) (c1 < 10 ? c1 + '0' : c1 - 10 + 'A'));
  360. }
  361. }
  362. return b.toString();
  363. }
  364. private static char[] createEscapes()
  365. {
  366. char[] escapes = new char['\\' + 1];
  367. for(int i = 0; i < 32; ++i)
  368. {
  369. escapes[i] = 1;
  370. }
  371. escapes['\\'] = '\\';
  372. escapes['\''] = '\'';
  373. escapes['"'] = '"';
  374. escapes['<'] = 'l';
  375. escapes['>'] = 'g';
  376. escapes['&'] = 'a';
  377. escapes['\b'] = 'b';
  378. escapes['\t'] = 't';
  379. escapes['\n'] = 'n';
  380. escapes['\f'] = 'f';
  381. escapes['\r'] = 'r';
  382. escapes['$'] = '$';
  383. return escapes;
  384. }
  385. public static String FTLStringLiteralEnc(String s)
  386. {
  387. StringBuffer buf = null;
  388. int l = s.length();
  389. int el = ESCAPES.length;
  390. for(int i = 0; i < l; i++)
  391. {
  392. char c = s.charAt(i);
  393. if(c < el)
  394. {
  395. char escape = ESCAPES[c];
  396. switch(escape)
  397. {
  398. case 0:
  399. {
  400. if (buf != null) {
  401. buf.append(c);
  402. }
  403. break;
  404. }
  405. case 1:
  406. {
  407. if (buf == null) {
  408. buf = new StringBuffer(s.length() + 3);
  409. buf.append(s.substring(0, i));
  410. }
  411. // hex encoding for characters below 0x20
  412. // that have no other escape representation
  413. buf.append("\\x00");
  414. int c2 = (c >> 4) & 0x0F;
  415. c = (char) (c & 0x0F);
  416. buf.append((char) (c2 < 10 ? c2 + '0' : c2 - 10 + 'A'));
  417. buf.append((char) (c < 10 ? c + '0' : c - 10 + 'A'));
  418. break;
  419. }
  420. default:
  421. {
  422. if (buf == null) {
  423. buf = new StringBuffer(s.length() + 2);
  424. buf.append(s.substring(0, i));
  425. }
  426. buf.append('\\');
  427. buf.append(escape);
  428. }
  429. }
  430. } else {
  431. if (buf != null) {
  432. buf.append(c);
  433. }
  434. }
  435. }
  436. return buf == null ? s : buf.toString();
  437. }
  438. /**
  439. * FTL string literal decoding.
  440. *
  441. * \\, \", \', \n, \t, \r, \b and \f will be replaced according to
  442. * Java rules. In additional, it knows \g, \l, \a and \{ which are
  443. * replaced with &lt;, >, &amp; and { respectively.
  444. * \x works as hexadecimal character code escape. The character
  445. * codes are interpreted according to UCS basic plane (Unicode).
  446. * "f\x006Fo", "f\x06Fo" and "f\x6Fo" will be "foo".
  447. * "f\x006F123" will be "foo123" as the maximum number of digits is 4.
  448. *
  449. * All other \X (where X is any character not mentioned above or End-of-string)
  450. * will cause a ParseException.
  451. *
  452. * @param s String literal <em>without</em> the surrounding quotation marks
  453. * @return String with all escape sequences resolved
  454. * @throws ParseException if there string contains illegal escapes
  455. */
  456. public static String FTLStringLiteralDec(String s) throws ParseException {
  457. int idx = s.indexOf('\\');
  458. if (idx == -1) {
  459. return s;
  460. }
  461. int lidx = s.length() - 1;
  462. int bidx = 0;
  463. StringBuffer buf = new StringBuffer(lidx);
  464. do {
  465. buf.append(s.substring(bidx, idx));
  466. if (idx >= lidx) {
  467. throw new ParseException("The last character of string literal is backslash", 0,0);
  468. }
  469. char c = s.charAt(idx + 1);
  470. switch (c) {
  471. case '"':
  472. buf.append('"');
  473. bidx = idx + 2;
  474. break;
  475. case '\'':
  476. buf.append('\'');
  477. bidx = idx + 2;
  478. break;
  479. case '\\':
  480. buf.append('\\');
  481. bidx = idx + 2;
  482. break;
  483. case 'n':
  484. buf.append('\n');
  485. bidx = idx + 2;
  486. break;
  487. case 'r':
  488. buf.append('\r');
  489. bidx = idx + 2;
  490. break;
  491. case 't':
  492. buf.append('\t');
  493. bidx = idx + 2;
  494. break;
  495. case 'f':
  496. buf.append('\f');
  497. bidx = idx + 2;
  498. break;
  499. case 'b':
  500. buf.append('\b');
  501. bidx = idx + 2;
  502. break;
  503. case 'g':
  504. buf.append('>');
  505. bidx = idx + 2;
  506. break;
  507. case 'l':
  508. buf.append('<');
  509. bidx = idx + 2;
  510. break;
  511. case 'a':
  512. buf.append('&');
  513. bidx = idx + 2;
  514. break;
  515. case '{':
  516. buf.append('{');
  517. bidx = idx + 2;
  518. break;
  519. case 'x': {
  520. idx += 2;
  521. int x = idx;
  522. int y = 0;
  523. int z = lidx > idx + 3 ? idx + 3 : lidx;
  524. while (idx <= z) {
  525. char b = s.charAt(idx);
  526. if (b >= '0' && b <= '9') {
  527. y <<= 4;
  528. y += b - '0';
  529. } else if (b >= 'a' && b <= 'f') {
  530. y <<= 4;
  531. y += b - 'a' + 10;
  532. } else if (b >= 'A' && b <= 'F') {
  533. y <<= 4;
  534. y += b - 'A' + 10;
  535. } else {
  536. break;
  537. }
  538. idx++;
  539. }
  540. if (x < idx) {
  541. buf.append((char) y);
  542. } else {
  543. throw new ParseException("Invalid \\x escape in a string literal",0,0);
  544. }
  545. bidx = idx;
  546. break;
  547. }
  548. default:
  549. throw new ParseException("Invalid escape sequence (\\" + c + ") in a string literal",0,0);
  550. }
  551. idx = s.indexOf('\\', bidx);
  552. } while (idx != -1);
  553. buf.append(s.substring(bidx));
  554. return buf.toString();
  555. }
  556. public static Locale deduceLocale(String input) {
  557. Locale locale = Locale.getDefault();
  558. if (input.charAt(0) == '"') input = input.substring(1, input.length() -1);
  559. StringTokenizer st = new StringTokenizer(input, ",_ ");
  560. String lang = "", country = "";
  561. if (st.hasMoreTokens()) {
  562. lang = st.nextToken();
  563. }
  564. if (st.hasMoreTokens()) {
  565. country = st.nextToken();
  566. }
  567. if (!st.hasMoreTokens()) {
  568. locale = new Locale(lang, country);
  569. }
  570. else {
  571. locale = new Locale(lang, country, st.nextToken());
  572. }
  573. return locale;
  574. }
  575. public static String capitalize(String s) {
  576. StringTokenizer st = new StringTokenizer(s, " \t\r\n", true);
  577. StringBuffer buf = new StringBuffer(s.length());
  578. while (st.hasMoreTokens()) {
  579. String tok = st.nextToken();
  580. buf.append(tok.substring(0, 1).toUpperCase());
  581. buf.append(tok.substring(1).toLowerCase());
  582. }
  583. return buf.toString();
  584. }
  585. public static boolean getYesNo(String s) {
  586. if (s.startsWith("\"")) {
  587. s = s.substring(1, s.length() -1);
  588. }
  589. if (s.equalsIgnoreCase("n")
  590. || s.equalsIgnoreCase("no")
  591. || s.equalsIgnoreCase("f")
  592. || s.equalsIgnoreCase("false")) {
  593. return false;
  594. }
  595. else if (s.equalsIgnoreCase("y")
  596. || s.equalsIgnoreCase("yes")
  597. || s.equalsIgnoreCase("t")
  598. || s.equalsIgnoreCase("true")) {
  599. return true;
  600. }
  601. throw new IllegalArgumentException("Illegal boolean value: " + s);
  602. }
  603. /**
  604. * Splits a string at the specified character.
  605. */
  606. public static String[] split(String s, char c) {
  607. int i, b, e;
  608. int cnt;
  609. String res[];
  610. int ln = s.length();
  611. i = 0;
  612. cnt = 1;
  613. while ((i = s.indexOf(c, i)) != -1) {
  614. cnt++;
  615. i++;
  616. }
  617. res = new String[cnt];
  618. i = 0;
  619. b = 0;
  620. while (b <= ln) {
  621. e = s.indexOf(c, b);
  622. if (e == -1) e = ln;
  623. res[i++] = s.substring(b, e);
  624. b = e + 1;
  625. }
  626. return res;
  627. }
  628. /**
  629. * Splits a string at the specified string.
  630. */
  631. public static String[] split(String s, String sep, boolean caseInsensitive) {
  632. String splitString = caseInsensitive ? sep.toLowerCase() : sep;
  633. String input = caseInsensitive ? s.toLowerCase() : s;
  634. int i, b, e;
  635. int cnt;
  636. String res[];
  637. int ln = s.length();
  638. int sln = sep.length();
  639. if (sln == 0) throw new IllegalArgumentException(
  640. "The separator string has 0 length");
  641. i = 0;
  642. cnt = 1;
  643. while ((i = input.indexOf(splitString, i)) != -1) {
  644. cnt++;
  645. i += sln;
  646. }
  647. res = new String[cnt];
  648. i = 0;
  649. b = 0;
  650. while (b <= ln) {
  651. e = input.indexOf(splitString, b);
  652. if (e == -1) e = ln;
  653. res[i++] = s.substring(b, e);
  654. b = e + sln;
  655. }
  656. return res;
  657. }
  658. /**
  659. * Replaces all occurrences of a sub-string in a string.
  660. * @param text The string where it will replace <code>oldsub</code> with
  661. * <code>newsub</code>.
  662. * @return String The string after the replacements.
  663. */
  664. public static String replace(String text,
  665. String oldsub,
  666. String newsub,
  667. boolean caseInsensitive,
  668. boolean firstOnly)
  669. {
  670. StringBuffer buf;
  671. int tln;
  672. int oln = oldsub.length();
  673. if (oln == 0) {
  674. int nln = newsub.length();
  675. if (nln == 0) {
  676. return text;
  677. } else {
  678. if (firstOnly) {
  679. return newsub + text;
  680. } else {
  681. tln = text.length();
  682. buf = new StringBuffer(tln + (tln + 1) * nln);
  683. buf.append(newsub);
  684. for (int i = 0; i < tln; i++) {
  685. buf.append(text.charAt(i));
  686. buf.append(newsub);
  687. }
  688. return buf.toString();
  689. }
  690. }
  691. } else {
  692. oldsub = caseInsensitive ? oldsub.toLowerCase() : oldsub;
  693. String input = caseInsensitive ? text.toLowerCase() : text;
  694. int e = input.indexOf(oldsub);
  695. if (e == -1) {
  696. return text;
  697. }
  698. int b = 0;
  699. tln = text.length();
  700. buf = new StringBuffer(
  701. tln + Math.max(newsub.length() - oln, 0) * 3);
  702. do {
  703. buf.append(text.substring(b, e));
  704. buf.append(newsub);
  705. b = e + oln;
  706. e = input.indexOf(oldsub, b);
  707. } while (e != -1 && !firstOnly);
  708. buf.append(text.substring(b));
  709. return buf.toString();
  710. }
  711. }
  712. /**
  713. * Removes the line-break from the end of the string.
  714. */
  715. public static String chomp(String s) {
  716. if (s.endsWith("\r\n")) return s.substring(0, s.length() - 2);
  717. if (s.endsWith("\r") || s.endsWith("\n"))
  718. return s.substring(0, s.length() - 1);
  719. return s;
  720. }
  721. /**
  722. * Converts the parameter with <code>toString</code> (if not
  723. * <code>null</code>)and passes it to {@link #jQuote(String)}.
  724. */
  725. public static String jQuote(Object obj) {
  726. return jQuote(obj != null ? obj.toString() : null);
  727. }
  728. /**
  729. * Quotes string as Java Language string literal.
  730. * Returns string <code>"null"</code> if <code>s</code>
  731. * is <code>null</code>.
  732. */
  733. public static String jQuote(String s) {
  734. if (s == null) {
  735. return "null";
  736. }
  737. int ln = s.length();
  738. StringBuffer b = new StringBuffer(ln + 4);
  739. b.append('"');
  740. for (int i = 0; i < ln; i++) {
  741. char c = s.charAt(i);
  742. if (c == '"') {
  743. b.append("\\\"");
  744. } else if (c == '\\') {
  745. b.append("\\\\");
  746. } else if (c < 0x20) {
  747. if (c == '\n') {
  748. b.append("\\n");
  749. } else if (c == '\r') {
  750. b.append("\\r");
  751. } else if (c == '\f') {
  752. b.append("\\f");
  753. } else if (c == '\b') {
  754. b.append("\\b");
  755. } else if (c == '\t') {
  756. b.append("\\t");
  757. } else {
  758. b.append("\\u00");
  759. int x = c / 0x10;
  760. b.append((char) (x < 0xA ? x + '0' : x - 0xA + 'A'));
  761. x = c & 0xF;
  762. b.append((char) (x < 0xA ? x + '0' : x - 0xA + 'A'));
  763. }
  764. } else {
  765. b.append(c);
  766. }
  767. } // for each characters
  768. b.append('"');
  769. return b.toString();
  770. }
  771. /**
  772. * Converts the parameter with <code>toString</code> (if not
  773. * <code>null</code>)and passes it to {@link #jQuoteNoXSS(String)}.
  774. */
  775. public static String jQuoteNoXSS(Object obj) {
  776. return jQuoteNoXSS(obj != null ? obj.toString() : null);
  777. }
  778. /**
  779. * Same as {@link #jQuoteNoXSS(String)} but also escapes <code>'&lt;'</code>
  780. * as <code>\u003C</code>. This is used for log messages to prevent XSS
  781. * on poorly written Web-based log viewers.
  782. */
  783. public static String jQuoteNoXSS(String s) {
  784. if (s == null) {
  785. return "null";
  786. }
  787. int ln = s.length();
  788. StringBuffer b = new StringBuffer(ln + 4);
  789. b.append('"');
  790. for (int i = 0; i < ln; i++) {
  791. char c = s.charAt(i);
  792. if (c == '"') {
  793. b.append("\\\"");
  794. } else if (c == '\\') {
  795. b.append("\\\\");
  796. } else if (c == '<') {
  797. b.append("\\u003C");
  798. } else if (c < 0x20) {
  799. if (c == '\n') {
  800. b.append("\\n");
  801. } else if (c == '\r') {
  802. b.append("\\r");
  803. } else if (c == '\f') {
  804. b.append("\\f");
  805. } else if (c == '\b') {
  806. b.append("\\b");
  807. } else if (c == '\t') {
  808. b.append("\\t");
  809. } else {
  810. b.append("\\u00");
  811. int x = c / 0x10;
  812. b.append((char) (x < 0xA ? x + '0' : x - 0xA + 'A'));
  813. x = c & 0xF;
  814. b.append((char) (x < 0xA ? x + '0' : x - 0xA + 'A'));
  815. }
  816. } else {
  817. b.append(c);
  818. }
  819. } // for each characters
  820. b.append('"');
  821. return b.toString();
  822. }
  823. /**
  824. * Escapes the <code>String</code> with the escaping rules of Java language
  825. * string literals, so it is safe to insert the value into a string literal.
  826. * The resulting string will not be quoted.
  827. *
  828. * <p>All characters under UCS code point 0x20 will be escaped.
  829. * Where they have no dedicated escape sequence in Java, they will
  830. * be replaced with hexadecimal escape (<tt>\</tt><tt>u<i>XXXX</i></tt>).
  831. *
  832. * @see #jQuote(String)
  833. */
  834. public static String javaStringEnc(String s) {
  835. int ln = s.length();
  836. for (int i = 0; i < ln; i++) {
  837. char c = s.charAt(i);
  838. if (c == '"' || c == '\\' || c < 0x20) {
  839. StringBuffer b = new StringBuffer(ln + 4);
  840. b.append(s.substring(0, i));
  841. while (true) {
  842. if (c == '"') {
  843. b.append("\\\"");
  844. } else if (c == '\\') {
  845. b.append("\\\\");
  846. } else if (c < 0x20) {
  847. if (c == '\n') {
  848. b.append("\\n");
  849. } else if (c == '\r') {
  850. b.append("\\r");
  851. } else if (c == '\f') {
  852. b.append("\\f");
  853. } else if (c == '\b') {
  854. b.append("\\b");
  855. } else if (c == '\t') {
  856. b.append("\\t");
  857. } else {
  858. b.append("\\u00");
  859. int x = c / 0x10;
  860. b.append((char)
  861. (x < 0xA ? x + '0' : x - 0xA + 'a'));
  862. x = c & 0xF;
  863. b.append((char)
  864. (x < 0xA ? x + '0' : x - 0xA + 'a'));
  865. }
  866. } else {
  867. b.append(c);
  868. }
  869. i++;
  870. if (i >= ln) {
  871. return b.toString();
  872. }
  873. c = s.charAt(i);
  874. }
  875. } // if has to be escaped
  876. } // for each characters
  877. return s;
  878. }
  879. /**
  880. * Escapes a <code>String</code> according the JavaScript string literal
  881. * escaping rules. The resulting string will not be quoted.
  882. *
  883. * <p>It escapes both <tt>'</tt> and <tt>"</tt>.
  884. * In additional it escapes <tt>></tt> as <tt>\></tt> (to avoid
  885. * <tt>&lt;/script></tt>).
  886. *
  887. * <p>All characters under UCS code point 0x20 will be escaped.
  888. * Where they have no dedicated escape sequence in JavaScript, they will
  889. * be replaced with hexadecimal escape (<tt>\</tt><tt>u<i>XXXX</i></tt>).
  890. */
  891. public static String javaScriptStringEnc(String s) {
  892. int ln = s.length();
  893. for (int i = 0; i < ln; i++) {
  894. char c = s.charAt(i);
  895. if (c == '"' || c == '\'' || c == '\\' || c == '>' || c < 0x20) {
  896. StringBuffer b = new StringBuffer(ln + 4);
  897. b.append(s.substring(0, i));
  898. while (true) {
  899. if (c == '"') {
  900. b.append("\\\"");
  901. } else if (c == '\'') {
  902. b.append("\\'");
  903. } else if (c == '\\') {
  904. b.append("\\\\");
  905. } else if (c == '>') {
  906. b.append("\\>");
  907. } else if (c < 0x20) {
  908. if (c == '\n') {
  909. b.append("\\n");
  910. } else if (c == '\r') {
  911. b.append("\\r");
  912. } else if (c == '\f') {
  913. b.append("\\f");
  914. } else if (c == '\b') {
  915. b.append("\\b");
  916. } else if (c == '\t') {
  917. b.append("\\t");
  918. } else {
  919. b.append("\\x");
  920. int x = c / 0x10;
  921. b.append((char)
  922. (x < 0xA ? x + '0' : x - 0xA + 'A'));
  923. x = c & 0xF;
  924. b.append((char)
  925. (x < 0xA ? x + '0' : x - 0xA + 'A'));
  926. }
  927. } else {
  928. b.append(c);
  929. }
  930. i++;
  931. if (i >= ln) {
  932. return b.toString();
  933. }
  934. c = s.charAt(i);
  935. }
  936. } // if has to be escaped
  937. } // for each characters
  938. return s;
  939. }
  940. /**
  941. * Escapes a <code>String</code> according the JSON string literal
  942. * escaping rules. The resulting string will <em>not</em> be quoted;
  943. * the caller have to ensure that they are there in the final output.
  944. *
  945. * <p>Beware, it doesn't escape <tt>'</tt>, as JSON string must be delimited with
  946. * <tt>"</tt>, and JSON has no <tt>\'</tt> escape either!
  947. *
  948. * <p>It will escape <tt>/</tt> as <tt>\/</tt> if it's after <tt>&lt;</tt>,
  949. * to avoid <tt>&lt;/script></tt>.
  950. *
  951. * <p>It will escape <tt>></tt> as <tt>\</tt><tt>u003E</tt> if it's after
  952. * <tt>]]</tt>, to avoid closing a CDATA section.
  953. *
  954. * <p>All characters under UCS code point 0x20 will be escaped.
  955. * Where they have no dedicated escape sequence in JSON, they will
  956. * be replaced with hexadecimal escape (<tt>\</tt><tt>u<i>XXXX</i></tt>).
  957. */
  958. public static String jsonStringEnc(String s) {
  959. int ln = s.length();
  960. for (int i = 0; i < ln; i++) {
  961. char c = s.charAt(i);
  962. if (c == '"' || c == '\\' || c < 0x20
  963. || (c == '/' && i > 0 && s.charAt(i -1) == '<')
  964. || (c == '>' && i > 1
  965. && s.charAt(i - 1) == ']' && s.charAt(i - 2) == ']')) {
  966. StringBuffer b = new StringBuffer(ln + 4);
  967. b.append(s.substring(0, i));
  968. while (true) {
  969. if (c == '"') {
  970. b.append("\\\"");
  971. } else if (c == '\\') {
  972. b.append("\\\\");
  973. } else if (c == '/' && i > 0 && s.charAt(i -1) == '<') {
  974. b.append("\\/");
  975. } else if (c == '>' && i > 1
  976. && s.charAt(i - 1) == ']' && s.charAt(i - 2) == ']') {
  977. b.append("\\u003E");
  978. } else if (c < 0x20) {
  979. if (c == '\n') {
  980. b.append("\\n");
  981. } else if (c == '\r') {
  982. b.append("\\r");
  983. } else if (c == '\f') {
  984. b.append("\\f");
  985. } else if (c == '\b') {
  986. b.append("\\b");
  987. } else if (c == '\t') {
  988. b.append("\\t");
  989. } else {
  990. b.append("\\u00");
  991. int x = c / 0x10;
  992. b.append((char)
  993. (x < 0xA ? x + '0' : x - 0xA + 'A'));
  994. x = c & 0xF;
  995. b.append((char)
  996. (x < 0xA ? x + '0' : x - 0xA + 'A'));
  997. }
  998. } else {
  999. b.append(c);
  1000. }
  1001. i++;
  1002. if (i >= ln) {
  1003. return b.toString();
  1004. }
  1005. c = s.charAt(i);
  1006. }
  1007. } // if has to be escaped
  1008. } // for each characters
  1009. return s;
  1010. }
  1011. /**
  1012. * Parses a name-value pair list, where the pairs are separated with comma,
  1013. * and the name and value is separated with colon.
  1014. * The keys and values can contain only letters, digits and <tt>_</tt>. They
  1015. * can't be quoted. White-space around the keys and values are ignored. The
  1016. * value can be omitted if <code>defaultValue</code> is not null. When a
  1017. * value is omitted, then the colon after the key must be omitted as well.
  1018. * The same key can't be used for multiple times.
  1019. *
  1020. * @param s the string to parse.
  1021. * For example: <code>"strong:100, soft:900"</code>.
  1022. * @param defaultValue the value used when the value is omitted in a
  1023. * key-value pair.
  1024. *
  1025. * @return the map that contains the name-value pairs.
  1026. *
  1027. * @throws java.text.ParseException if the string is not a valid name-value
  1028. * pair list.
  1029. */
  1030. public static Map parseNameValuePairList(String s, String defaultValue)
  1031. throws java.text.ParseException {
  1032. Map map = new HashMap();
  1033. char c = ' ';
  1034. int ln = s.length();
  1035. int p = 0;
  1036. int keyStart;
  1037. int valueStart;
  1038. String key;
  1039. String value;
  1040. fetchLoop: while (true) {
  1041. // skip ws
  1042. while (p < ln) {
  1043. c = s.charAt(p);
  1044. if (!Character.isWhitespace(c)) {
  1045. break;
  1046. }
  1047. p++;
  1048. }
  1049. if (p == ln) {
  1050. break fetchLoop;
  1051. }
  1052. keyStart = p;
  1053. // seek key end
  1054. while (p < ln) {
  1055. c = s.charAt(p);
  1056. if (!(Character.isLetterOrDigit(c) || c == '_')) {
  1057. break;
  1058. }
  1059. p++;
  1060. }
  1061. if (keyStart == p) {
  1062. throw new java.text.ParseException(
  1063. "Expecting letter, digit or \"_\" "
  1064. + "here, (the first character of the key) but found "
  1065. + jQuote(String.valueOf(c))
  1066. + " at position " + p + ".",
  1067. p);
  1068. }
  1069. key = s.substring(keyStart, p);
  1070. // skip ws
  1071. while (p < ln) {
  1072. c = s.charAt(p);
  1073. if (!Character.isWhitespace(c)) {
  1074. break;
  1075. }
  1076. p++;
  1077. }
  1078. if (p == ln) {
  1079. if (defaultValue == null) {
  1080. throw new java.text.ParseException(
  1081. "Expecting \":\", but reached "
  1082. + "the end of the string "
  1083. + " at position " + p + ".",
  1084. p);
  1085. }
  1086. value = defaultValue;
  1087. } else if (c != ':') {
  1088. if (defaultValue == null || c != ',') {
  1089. throw new java.text.ParseException(
  1090. "Expecting \":\" here, but found "
  1091. + jQuote(String.valueOf(c))
  1092. + " at position " + p + ".",
  1093. p);
  1094. }
  1095. // skip ","
  1096. p++;
  1097. value = defaultValue;
  1098. } else {
  1099. // skip ":"
  1100. p++;
  1101. // skip ws
  1102. while (p < ln) {
  1103. c = s.charAt(p);
  1104. if (!Character.isWhitespace(c)) {
  1105. break;
  1106. }
  1107. p++;
  1108. }
  1109. if (p == ln) {
  1110. throw new java.text.ParseException(
  1111. "Expecting the value of the key "
  1112. + "here, but reached the end of the string "
  1113. + " at position " + p + ".",
  1114. p);
  1115. }
  1116. valueStart = p;
  1117. // seek value end
  1118. while (p < ln) {
  1119. c = s.charAt(p);
  1120. if (!(Character.isLetterOrDigit(c) || c == '_')) {
  1121. break;
  1122. }
  1123. p++;
  1124. }
  1125. if (valueStart == p) {
  1126. throw new java.text.ParseException(
  1127. "Expecting letter, digit or \"_\" "
  1128. + "here, (the first character of the value) "
  1129. + "but found "
  1130. + jQuote(String.valueOf(c))
  1131. + " at position " + p + ".",
  1132. p);
  1133. }
  1134. value = s.substring(valueStart, p);
  1135. // skip ws
  1136. while (p < ln) {
  1137. c = s.charAt(p);
  1138. if (!Character.isWhitespace(c)) {
  1139. break;
  1140. }
  1141. p++;
  1142. }
  1143. // skip ","
  1144. if (p < ln) {
  1145. if (c != ',') {
  1146. throw new java.text.ParseException(
  1147. "Excpecting \",\" or the end "
  1148. + "of the string here, but found "
  1149. + jQuote(String.valueOf(c))
  1150. + " at position " + p + ".",
  1151. p);
  1152. } else {
  1153. p++;
  1154. }
  1155. }
  1156. }
  1157. // store the key-value pair
  1158. if (map.put(key, value) != null) {
  1159. throw new java.text.ParseException(
  1160. "Dublicated key: "
  1161. + jQuote(key), keyStart);
  1162. }
  1163. }
  1164. return map;
  1165. }
  1166. /**
  1167. * @return whether the name is a valid XML tagname.
  1168. * (This routine might only be 99% accurate. Should maybe REVISIT)
  1169. */
  1170. static public boolean isXMLID(String name) {
  1171. for (int i=0; i<name.length(); i++) {
  1172. char c = name.charAt(i);
  1173. if (i==0) {
  1174. if (c== '-' || c=='.' || Character.isDigit(c))
  1175. return false;
  1176. }
  1177. if (!Character.isLetterOrDigit(c) && c != ':' && c != '_' && c != '-' && c!='.') {
  1178. return false;
  1179. }
  1180. }
  1181. return true;
  1182. }
  1183. /**
  1184. * @return whether the qname matches the combination of nodeName, nsURI, and environment prefix settings.
  1185. */
  1186. static public boolean matchesName(String qname, String nodeName, String nsURI, Environment env) {
  1187. String defaultNS = env.getDefaultNS();
  1188. if ((defaultNS != null) && defaultNS.equals(nsURI)) {
  1189. return qname.equals(nodeName)
  1190. || qname.equals(Template.DEFAULT_NAMESPACE_PREFIX + ":" + nodeName);
  1191. }
  1192. if ("".equals(nsURI)) {
  1193. if (defaultNS != null) {
  1194. return qname.equals(Template.NO_NS_PREFIX + ":" + nodeName);
  1195. } else {
  1196. return qname.equals(nodeName) || qname.equals(Template.NO_NS_PREFIX + ":" + nodeName);
  1197. }
  1198. }
  1199. String prefix = env.getPrefixForNamespace(nsURI);
  1200. if (prefix == null) {
  1201. return false; // Is this the right thing here???
  1202. }
  1203. return qname.equals(prefix + ":" + nodeName);
  1204. }
  1205. /**
  1206. * Pads the string at the left with spaces until it reaches the desired
  1207. * length. If the string is longer than this length, then it returns the
  1208. * unchanged string.
  1209. *
  1210. * @param s the string that will be padded.
  1211. * @param minLength the length to reach.
  1212. */
  1213. public static String leftPad(String s, int minLength) {
  1214. return leftPad(s, minLength, ' ');
  1215. }
  1216. /**
  1217. * Pads the string at the left with the specified character until it reaches
  1218. * the desired length. If the string is longer than this length, then it
  1219. * returns the unchanged string.
  1220. *
  1221. * @param s the string that will be padded.
  1222. * @param minLength the length to reach.
  1223. * @param filling the filling pattern.
  1224. */
  1225. public static String leftPad(String s, int minLength, char filling) {
  1226. int ln = s.length();
  1227. if (minLength <= ln) {
  1228. return s;
  1229. }
  1230. StringBuffer res = new StringBuffer(minLength);
  1231. int dif = minLength - ln;
  1232. for (int i = 0; i < dif; i++) {
  1233. res.append(filling);
  1234. }
  1235. res.append(s);
  1236. return res.toString();
  1237. }
  1238. /**
  1239. * Pads the string at the left with a filling pattern until it reaches the
  1240. * desired length. If the string is longer than this length, then it returns
  1241. * the unchanged string. For example: <code>leftPad('ABC', 9, '1234')</code>
  1242. * returns <code>"123412ABC"</code>.
  1243. *
  1244. * @param s the string that will be padded.
  1245. * @param minLength the length to reach.
  1246. * @param filling the filling pattern. Must be at least 1 characters long.
  1247. * Can't be <code>null</code>.
  1248. */
  1249. public static String leftPad(String s, int minLength, String filling) {
  1250. int ln = s.length();
  1251. if (minLength <= ln) {
  1252. return s;
  1253. }
  1254. StringBuffer res = new StringBuffer(minLength);
  1255. int dif = minLength - ln;
  1256. int fln = filling.length();
  1257. if (fln == 0) {
  1258. throw new IllegalArgumentException(
  1259. "The \"filling\" argument can't be 0 length string.");
  1260. }
  1261. int cnt = dif / fln;
  1262. for (int i = 0; i < cnt; i++) {
  1263. res.append(filling);
  1264. }
  1265. cnt = dif % fln;
  1266. for (int i = 0; i < cnt; i++) {
  1267. res.append(filling.charAt(i));
  1268. }
  1269. res.append(s);
  1270. return res.toString();
  1271. }
  1272. /**
  1273. * Pads the string at the right with spaces until it reaches the desired
  1274. * length. If the string is longer than this length, then it returns the
  1275. * unchanged string.
  1276. *
  1277. * @param s the string that will be padded.
  1278. * @param minLength the length to reach.
  1279. */
  1280. public static String rightPad(String s, int minLength) {
  1281. return rightPad(s, minLength, ' ');
  1282. }
  1283. /**
  1284. * Pads the string at the right with the specified character until it
  1285. * reaches the desired length. If the string is longer than this length,
  1286. * then it returns the unchanged string.
  1287. *
  1288. * @param s the string that will be padded.
  1289. * @param minLength the length to reach.
  1290. * @param filling the filling pattern.
  1291. */
  1292. public static String rightPad(String s, int minLength, char filling) {
  1293. int ln = s.length();
  1294. if (minLength <= ln) {
  1295. return s;
  1296. }
  1297. StringBuffer res = new StringBuffer(minLength);
  1298. res.append(s);
  1299. int dif = minLength - ln;
  1300. for (int i = 0; i < dif; i++) {
  1301. res.append(filling);
  1302. }
  1303. return res.toString();
  1304. }
  1305. /**
  1306. * Pads the string at the right with a filling pattern until it reaches the
  1307. * desired length. If the string is longer than this length, then it returns
  1308. * the unchanged string. For example: <code>rightPad('ABC', 9, '1234')</code>
  1309. * returns <code>"ABC412341"</code>. Note that the filling pattern is
  1310. * started as if you overlay <code>"123412341"</code> with the left-aligned
  1311. * <code>"ABC"</code>, so it s…

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