PageRenderTime 56ms CodeModel.GetById 18ms RepoModel.GetById 0ms app.codeStats 0ms

/tags/2.3.12/freemarker/src/freemarker/template/utility/StringUtil.java

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