PageRenderTime 36ms CodeModel.GetById 14ms RepoModel.GetById 0ms app.codeStats 0ms

/EQEmuJSM/mysql-connector-java-5.1.13/src/com/mysql/jdbc/EscapeProcessor.java

http://cubbers-eqemu-utils.googlecode.com/
Java | 779 lines | 657 code | 49 blank | 73 comment | 61 complexity | e00bada7a206634bf0bb2fbe3d41e740 MD5 | raw file
Possible License(s): GPL-2.0, LGPL-2.1, Apache-2.0
  1. /*
  2. Copyright 2002-2007 MySQL AB, 2008-2010 Sun Microsystems
  3. All rights reserved. Use is subject to license terms.
  4. The MySQL Connector/J is licensed under the terms of the GPL,
  5. like most MySQL Connectors. There are special exceptions to the
  6. terms and conditions of the GPL as it is applied to this software,
  7. see the FLOSS License Exception available on mysql.com.
  8. This program is free software; you can redistribute it and/or
  9. modify it under the terms of the GNU General Public License as
  10. published by the Free Software Foundation; version 2 of the
  11. License.
  12. This program is distributed in the hope that it will be useful,
  13. but WITHOUT ANY WARRANTY; without even the implied warranty of
  14. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  15. GNU General Public License for more details.
  16. You should have received a copy of the GNU General Public License
  17. along with this program; if not, write to the Free Software
  18. Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
  19. 02110-1301 USA
  20. */
  21. /**
  22. * EscapeProcessor performs all escape code processing as outlined in the JDBC
  23. * spec by JavaSoft.
  24. */
  25. package com.mysql.jdbc;
  26. import java.sql.SQLException;
  27. import java.sql.Time;
  28. import java.sql.Timestamp;
  29. import java.text.SimpleDateFormat;
  30. import java.util.Calendar;
  31. import java.util.Collections;
  32. import java.util.GregorianCalendar;
  33. import java.util.HashMap;
  34. import java.util.Locale;
  35. import java.util.Map;
  36. import java.util.StringTokenizer;
  37. import java.util.TimeZone;
  38. class EscapeProcessor {
  39. private static Map JDBC_CONVERT_TO_MYSQL_TYPE_MAP;
  40. private static Map JDBC_NO_CONVERT_TO_MYSQL_EXPRESSION_MAP;
  41. static {
  42. Map tempMap = new HashMap();
  43. tempMap.put("BIGINT", "0 + ?");
  44. tempMap.put("BINARY", "BINARY");
  45. tempMap.put("BIT", "0 + ?");
  46. tempMap.put("CHAR", "CHAR");
  47. tempMap.put("DATE", "DATE");
  48. tempMap.put("DECIMAL", "0.0 + ?");
  49. tempMap.put("DOUBLE", "0.0 + ?");
  50. tempMap.put("FLOAT", "0.0 + ?");
  51. tempMap.put("INTEGER", "0 + ?");
  52. tempMap.put("LONGVARBINARY", "BINARY");
  53. tempMap.put("LONGVARCHAR", "CONCAT(?)");
  54. tempMap.put("REAL", "0.0 + ?");
  55. tempMap.put("SMALLINT", "CONCAT(?)");
  56. tempMap.put("TIME", "TIME");
  57. tempMap.put("TIMESTAMP", "DATETIME");
  58. tempMap.put("TINYINT", "CONCAT(?)");
  59. tempMap.put("VARBINARY", "BINARY");
  60. tempMap.put("VARCHAR", "CONCAT(?)");
  61. JDBC_CONVERT_TO_MYSQL_TYPE_MAP = Collections.unmodifiableMap(tempMap);
  62. tempMap = new HashMap(JDBC_CONVERT_TO_MYSQL_TYPE_MAP);
  63. tempMap.put("BINARY", "CONCAT(?)");
  64. tempMap.put("CHAR", "CONCAT(?)");
  65. tempMap.remove("DATE");
  66. tempMap.put("LONGVARBINARY", "CONCAT(?)");
  67. tempMap.remove("TIME");
  68. tempMap.remove("TIMESTAMP");
  69. tempMap.put("VARBINARY", "CONCAT(?)");
  70. JDBC_NO_CONVERT_TO_MYSQL_EXPRESSION_MAP = Collections
  71. .unmodifiableMap(tempMap);
  72. }
  73. /**
  74. * Escape process one string
  75. *
  76. * @param sql
  77. * the SQL to escape process.
  78. *
  79. * @return the SQL after it has been escape processed.
  80. *
  81. * @throws java.sql.SQLException
  82. * DOCUMENT ME!
  83. * @throws SQLException
  84. * DOCUMENT ME!
  85. */
  86. public static final Object escapeSQL(String sql,
  87. boolean serverSupportsConvertFn, MySQLConnection conn)
  88. throws java.sql.SQLException {
  89. boolean replaceEscapeSequence = false;
  90. String escapeSequence = null;
  91. if (sql == null) {
  92. return null;
  93. }
  94. /*
  95. * Short circuit this code if we don't have a matching pair of "{}". -
  96. * Suggested by Ryan Gustafason
  97. */
  98. int beginBrace = sql.indexOf('{');
  99. int nextEndBrace = (beginBrace == -1) ? (-1) : sql.indexOf('}',
  100. beginBrace);
  101. if (nextEndBrace == -1) {
  102. return sql;
  103. }
  104. StringBuffer newSql = new StringBuffer();
  105. EscapeTokenizer escapeTokenizer = new EscapeTokenizer(sql);
  106. byte usesVariables = StatementImpl.USES_VARIABLES_FALSE;
  107. boolean callingStoredFunction = false;
  108. while (escapeTokenizer.hasMoreTokens()) {
  109. String token = escapeTokenizer.nextToken();
  110. if (token.length() != 0) {
  111. if (token.charAt(0) == '{') { // It's an escape code
  112. if (!token.endsWith("}")) {
  113. throw SQLError
  114. .createSQLException("Not a valid escape sequence: "
  115. + token, conn.getExceptionInterceptor());
  116. }
  117. if (token.length() > 2) {
  118. int nestedBrace = token.indexOf('{', 2);
  119. if (nestedBrace != -1) {
  120. StringBuffer buf = new StringBuffer(token
  121. .substring(0, 1));
  122. Object remainingResults = escapeSQL(token
  123. .substring(1, token.length() - 1),
  124. serverSupportsConvertFn, conn);
  125. String remaining = null;
  126. if (remainingResults instanceof String) {
  127. remaining = (String) remainingResults;
  128. } else {
  129. remaining = ((EscapeProcessorResult) remainingResults).escapedSql;
  130. if (usesVariables != StatementImpl.USES_VARIABLES_TRUE) {
  131. usesVariables = ((EscapeProcessorResult) remainingResults).usesVariables;
  132. }
  133. }
  134. buf.append(remaining);
  135. buf.append('}');
  136. token = buf.toString();
  137. }
  138. }
  139. // nested escape code
  140. // Compare to tokens with _no_ whitespace
  141. String collapsedToken = removeWhitespace(token);
  142. /*
  143. * Process the escape code
  144. */
  145. if (StringUtils.startsWithIgnoreCase(collapsedToken,
  146. "{escape")) {
  147. try {
  148. StringTokenizer st = new StringTokenizer(token,
  149. " '");
  150. st.nextToken(); // eat the "escape" token
  151. escapeSequence = st.nextToken();
  152. if (escapeSequence.length() < 3) {
  153. newSql.append(token); // it's just part of the
  154. // query, push possible
  155. // syntax errors onto
  156. // server's shoulders
  157. } else {
  158. escapeSequence = escapeSequence.substring(1,
  159. escapeSequence.length() - 1);
  160. replaceEscapeSequence = true;
  161. }
  162. } catch (java.util.NoSuchElementException e) {
  163. newSql.append(token); // it's just part of the
  164. // query, push possible
  165. // syntax errors onto
  166. // server's shoulders
  167. }
  168. } else if (StringUtils.startsWithIgnoreCase(collapsedToken,
  169. "{fn")) {
  170. int startPos = token.toLowerCase().indexOf("fn ") + 3;
  171. int endPos = token.length() - 1; // no }
  172. String fnToken = token.substring(startPos, endPos);
  173. // We need to handle 'convert' by ourselves
  174. if (StringUtils.startsWithIgnoreCaseAndWs(fnToken,
  175. "convert")) {
  176. newSql.append(processConvertToken(fnToken,
  177. serverSupportsConvertFn, conn));
  178. } else {
  179. // just pass functions right to the DB
  180. newSql.append(fnToken);
  181. }
  182. } else if (StringUtils.startsWithIgnoreCase(collapsedToken,
  183. "{d")) {
  184. int startPos = token.indexOf('\'') + 1;
  185. int endPos = token.lastIndexOf('\''); // no }
  186. if ((startPos == -1) || (endPos == -1)) {
  187. newSql.append(token); // it's just part of the
  188. // query, push possible
  189. // syntax errors onto
  190. // server's shoulders
  191. } else {
  192. String argument = token.substring(startPos, endPos);
  193. try {
  194. StringTokenizer st = new StringTokenizer(
  195. argument, " -");
  196. String year4 = st.nextToken();
  197. String month2 = st.nextToken();
  198. String day2 = st.nextToken();
  199. String dateString = "'" + year4 + "-" + month2
  200. + "-" + day2 + "'";
  201. newSql.append(dateString);
  202. } catch (java.util.NoSuchElementException e) {
  203. throw SQLError.createSQLException(
  204. "Syntax error for DATE escape sequence '"
  205. + argument + "'", "42000", conn.getExceptionInterceptor());
  206. }
  207. }
  208. } else if (StringUtils.startsWithIgnoreCase(collapsedToken,
  209. "{ts")) {
  210. processTimestampToken(conn, newSql, token);
  211. } else if (StringUtils.startsWithIgnoreCase(collapsedToken,
  212. "{t")) {
  213. processTimeToken(conn, newSql, token);
  214. } else if (StringUtils.startsWithIgnoreCase(collapsedToken,
  215. "{call")
  216. || StringUtils.startsWithIgnoreCase(collapsedToken,
  217. "{?=call")) {
  218. int startPos = StringUtils.indexOfIgnoreCase(token,
  219. "CALL") + 5;
  220. int endPos = token.length() - 1;
  221. if (StringUtils.startsWithIgnoreCase(collapsedToken,
  222. "{?=call")) {
  223. callingStoredFunction = true;
  224. newSql.append("SELECT ");
  225. newSql.append(token.substring(startPos, endPos));
  226. } else {
  227. callingStoredFunction = false;
  228. newSql.append("CALL ");
  229. newSql.append(token.substring(startPos, endPos));
  230. }
  231. for (int i = endPos - 1; i >= startPos; i--) {
  232. char c = token.charAt(i);
  233. if (Character.isWhitespace(c)) {
  234. continue;
  235. }
  236. if (c != ')') {
  237. newSql.append("()"); // handle no-parenthesis
  238. // no-arg call not
  239. // supported
  240. // by MySQL parser
  241. }
  242. break;
  243. }
  244. } else if (StringUtils.startsWithIgnoreCase(collapsedToken,
  245. "{oj")) {
  246. // MySQL already handles this escape sequence
  247. // because of ODBC. Cool.
  248. newSql.append(token);
  249. }
  250. } else {
  251. newSql.append(token); // it's just part of the query
  252. }
  253. }
  254. }
  255. String escapedSql = newSql.toString();
  256. //
  257. // FIXME: Let MySQL do this, however requires
  258. // lightweight parsing of statement
  259. //
  260. if (replaceEscapeSequence) {
  261. String currentSql = escapedSql;
  262. while (currentSql.indexOf(escapeSequence) != -1) {
  263. int escapePos = currentSql.indexOf(escapeSequence);
  264. String lhs = currentSql.substring(0, escapePos);
  265. String rhs = currentSql.substring(escapePos + 1, currentSql
  266. .length());
  267. currentSql = lhs + "\\" + rhs;
  268. }
  269. escapedSql = currentSql;
  270. }
  271. EscapeProcessorResult epr = new EscapeProcessorResult();
  272. epr.escapedSql = escapedSql;
  273. epr.callingStoredFunction = callingStoredFunction;
  274. if (usesVariables != StatementImpl.USES_VARIABLES_TRUE) {
  275. if (escapeTokenizer.sawVariableUse()) {
  276. epr.usesVariables = StatementImpl.USES_VARIABLES_TRUE;
  277. } else {
  278. epr.usesVariables = StatementImpl.USES_VARIABLES_FALSE;
  279. }
  280. }
  281. return epr;
  282. }
  283. private static void processTimeToken(MySQLConnection conn,
  284. StringBuffer newSql, String token) throws SQLException {
  285. int startPos = token.indexOf('\'') + 1;
  286. int endPos = token.lastIndexOf('\''); // no }
  287. if ((startPos == -1) || (endPos == -1)) {
  288. newSql.append(token); // it's just part of the
  289. // query, push possible
  290. // syntax errors onto
  291. // server's shoulders
  292. } else {
  293. String argument = token.substring(startPos, endPos);
  294. try {
  295. StringTokenizer st = new StringTokenizer(
  296. argument, " :");
  297. String hour = st.nextToken();
  298. String minute = st.nextToken();
  299. String second = st.nextToken();
  300. if (!conn.getUseTimezone()
  301. || !conn.getUseLegacyDatetimeCode()) {
  302. String timeString = "'" + hour + ":"
  303. + minute + ":" + second + "'";
  304. newSql.append(timeString);
  305. } else {
  306. Calendar sessionCalendar = null;
  307. if (conn != null) {
  308. sessionCalendar = conn
  309. .getCalendarInstanceForSessionOrNew();
  310. } else {
  311. sessionCalendar = new GregorianCalendar();
  312. }
  313. try {
  314. int hourInt = Integer.parseInt(hour);
  315. int minuteInt = Integer
  316. .parseInt(minute);
  317. int secondInt = Integer
  318. .parseInt(second);
  319. synchronized (sessionCalendar) {
  320. Time toBeAdjusted = TimeUtil
  321. .fastTimeCreate(
  322. sessionCalendar,
  323. hourInt, minuteInt,
  324. secondInt, conn.getExceptionInterceptor());
  325. Time inServerTimezone = TimeUtil
  326. .changeTimezone(
  327. conn,
  328. sessionCalendar,
  329. null,
  330. toBeAdjusted,
  331. sessionCalendar
  332. .getTimeZone(),
  333. conn
  334. .getServerTimezoneTZ(),
  335. false);
  336. newSql.append("'");
  337. newSql.append(inServerTimezone
  338. .toString());
  339. newSql.append("'");
  340. }
  341. } catch (NumberFormatException nfe) {
  342. throw SQLError
  343. .createSQLException(
  344. "Syntax error in TIMESTAMP escape sequence '"
  345. + token + "'.",
  346. SQLError.SQL_STATE_ILLEGAL_ARGUMENT, conn.getExceptionInterceptor());
  347. }
  348. }
  349. } catch (java.util.NoSuchElementException e) {
  350. throw SQLError.createSQLException(
  351. "Syntax error for escape sequence '"
  352. + argument + "'", "42000", conn.getExceptionInterceptor());
  353. }
  354. }
  355. }
  356. private static void processTimestampToken(MySQLConnection conn,
  357. StringBuffer newSql, String token) throws SQLException {
  358. int startPos = token.indexOf('\'') + 1;
  359. int endPos = token.lastIndexOf('\''); // no }
  360. if ((startPos == -1) || (endPos == -1)) {
  361. newSql.append(token); // it's just part of the
  362. // query, push possible
  363. // syntax errors onto
  364. // server's shoulders
  365. } else {
  366. String argument = token.substring(startPos, endPos);
  367. try {
  368. if (!conn.getUseLegacyDatetimeCode()) {
  369. Timestamp ts = Timestamp.valueOf(argument);
  370. SimpleDateFormat tsdf = new SimpleDateFormat(
  371. "''yyyy-MM-dd HH:mm:ss''", Locale.US); //$NON-NLS-1$
  372. tsdf
  373. .setTimeZone(conn
  374. .getServerTimezoneTZ());
  375. newSql.append(tsdf.format(ts));
  376. } else {
  377. StringTokenizer st = new StringTokenizer(
  378. argument, " .-:");
  379. try {
  380. String year4 = st.nextToken();
  381. String month2 = st.nextToken();
  382. String day2 = st.nextToken();
  383. String hour = st.nextToken();
  384. String minute = st.nextToken();
  385. String second = st.nextToken();
  386. /*
  387. * For now, we get the fractional
  388. * seconds part, but we don't use it, as
  389. * MySQL doesn't support it in it's
  390. * TIMESTAMP data type
  391. *
  392. * String fractionalSecond = "";
  393. *
  394. * if (st.hasMoreTokens()) {
  395. * fractionalSecond = st.nextToken(); }
  396. */
  397. /*
  398. * Use the full format because number
  399. * format will not work for "between"
  400. * clauses.
  401. *
  402. * Ref. Mysql Docs
  403. *
  404. * You can specify DATETIME, DATE and
  405. * TIMESTAMP values using any of a
  406. * common set of formats:
  407. *
  408. * As a string in either 'YYYY-MM-DD
  409. * HH:MM:SS' or 'YY-MM-DD HH:MM:SS'
  410. * format.
  411. *
  412. * Thanks to Craig Longman for pointing
  413. * out this bug
  414. */
  415. if (!conn.getUseTimezone()
  416. && !conn
  417. .getUseJDBCCompliantTimezoneShift()) {
  418. newSql.append("'").append(year4)
  419. .append("-").append(month2)
  420. .append("-").append(day2)
  421. .append(" ").append(hour)
  422. .append(":").append(minute)
  423. .append(":").append(second)
  424. .append("'");
  425. } else {
  426. Calendar sessionCalendar;
  427. if (conn != null) {
  428. sessionCalendar = conn
  429. .getCalendarInstanceForSessionOrNew();
  430. } else {
  431. sessionCalendar = new GregorianCalendar();
  432. sessionCalendar
  433. .setTimeZone(TimeZone
  434. .getTimeZone("GMT"));
  435. }
  436. try {
  437. int year4Int = Integer
  438. .parseInt(year4);
  439. int month2Int = Integer
  440. .parseInt(month2);
  441. int day2Int = Integer
  442. .parseInt(day2);
  443. int hourInt = Integer
  444. .parseInt(hour);
  445. int minuteInt = Integer
  446. .parseInt(minute);
  447. int secondInt = Integer
  448. .parseInt(second);
  449. synchronized (sessionCalendar) {
  450. boolean useGmtMillis = conn
  451. .getUseGmtMillisForDatetimes();
  452. Timestamp toBeAdjusted = TimeUtil
  453. .fastTimestampCreate(
  454. useGmtMillis,
  455. useGmtMillis ? Calendar
  456. .getInstance(TimeZone
  457. .getTimeZone("GMT"))
  458. : null,
  459. sessionCalendar,
  460. year4Int,
  461. month2Int,
  462. day2Int,
  463. hourInt,
  464. minuteInt,
  465. secondInt,
  466. 0);
  467. Timestamp inServerTimezone = TimeUtil
  468. .changeTimezone(
  469. conn,
  470. sessionCalendar,
  471. null,
  472. toBeAdjusted,
  473. sessionCalendar
  474. .getTimeZone(),
  475. conn
  476. .getServerTimezoneTZ(),
  477. false);
  478. newSql.append("'");
  479. String timezoneLiteral = inServerTimezone
  480. .toString();
  481. int indexOfDot = timezoneLiteral
  482. .indexOf(".");
  483. if (indexOfDot != -1) {
  484. timezoneLiteral = timezoneLiteral
  485. .substring(0,
  486. indexOfDot);
  487. }
  488. newSql
  489. .append(timezoneLiteral);
  490. }
  491. newSql.append("'");
  492. } catch (NumberFormatException nfe) {
  493. throw SQLError
  494. .createSQLException(
  495. "Syntax error in TIMESTAMP escape sequence '"
  496. + token
  497. + "'.",
  498. SQLError.SQL_STATE_ILLEGAL_ARGUMENT, conn.getExceptionInterceptor());
  499. }
  500. }
  501. } catch (java.util.NoSuchElementException e) {
  502. throw SQLError.createSQLException(
  503. "Syntax error for TIMESTAMP escape sequence '"
  504. + argument + "'",
  505. "42000", conn.getExceptionInterceptor());
  506. }
  507. }
  508. } catch (IllegalArgumentException illegalArgumentException) {
  509. SQLException sqlEx = SQLError
  510. .createSQLException(
  511. "Syntax error for TIMESTAMP escape sequence '"
  512. + argument + "'",
  513. "42000", conn.getExceptionInterceptor());
  514. sqlEx.initCause(illegalArgumentException);
  515. throw sqlEx;
  516. }
  517. }
  518. }
  519. /**
  520. * Re-writes {fn convert (expr, type)} as cast(expr AS type)
  521. *
  522. * @param functionToken
  523. * @return
  524. * @throws SQLException
  525. */
  526. private static String processConvertToken(String functionToken,
  527. boolean serverSupportsConvertFn, MySQLConnection conn) throws SQLException {
  528. // The JDBC spec requires these types:
  529. //
  530. // BIGINT
  531. // BINARY
  532. // BIT
  533. // CHAR
  534. // DATE
  535. // DECIMAL
  536. // DOUBLE
  537. // FLOAT
  538. // INTEGER
  539. // LONGVARBINARY
  540. // LONGVARCHAR
  541. // REAL
  542. // SMALLINT
  543. // TIME
  544. // TIMESTAMP
  545. // TINYINT
  546. // VARBINARY
  547. // VARCHAR
  548. // MySQL supports these types:
  549. //
  550. // BINARY
  551. // CHAR
  552. // DATE
  553. // DATETIME
  554. // SIGNED (integer)
  555. // UNSIGNED (integer)
  556. // TIME
  557. int firstIndexOfParen = functionToken.indexOf("(");
  558. if (firstIndexOfParen == -1) {
  559. throw SQLError
  560. .createSQLException(
  561. "Syntax error while processing {fn convert (... , ...)} token, missing opening parenthesis in token '"
  562. + functionToken + "'.",
  563. SQLError.SQL_STATE_SYNTAX_ERROR, conn.getExceptionInterceptor());
  564. }
  565. int tokenLength = functionToken.length();
  566. int indexOfComma = functionToken.lastIndexOf(",");
  567. if (indexOfComma == -1) {
  568. throw SQLError
  569. .createSQLException(
  570. "Syntax error while processing {fn convert (... , ...)} token, missing comma in token '"
  571. + functionToken + "'.",
  572. SQLError.SQL_STATE_SYNTAX_ERROR, conn.getExceptionInterceptor());
  573. }
  574. int indexOfCloseParen = functionToken.indexOf(')', indexOfComma);
  575. if (indexOfCloseParen == -1) {
  576. throw SQLError
  577. .createSQLException(
  578. "Syntax error while processing {fn convert (... , ...)} token, missing closing parenthesis in token '"
  579. + functionToken + "'.",
  580. SQLError.SQL_STATE_SYNTAX_ERROR, conn.getExceptionInterceptor());
  581. }
  582. String expression = functionToken.substring(firstIndexOfParen + 1,
  583. indexOfComma);
  584. String type = functionToken.substring(indexOfComma + 1,
  585. indexOfCloseParen);
  586. String newType = null;
  587. String trimmedType = type.trim();
  588. if (StringUtils.startsWithIgnoreCase(trimmedType, "SQL_")) {
  589. trimmedType = trimmedType.substring(4, trimmedType.length());
  590. }
  591. if (serverSupportsConvertFn) {
  592. newType = (String) JDBC_CONVERT_TO_MYSQL_TYPE_MAP.get(trimmedType
  593. .toUpperCase(Locale.ENGLISH));
  594. } else {
  595. newType = (String) JDBC_NO_CONVERT_TO_MYSQL_EXPRESSION_MAP
  596. .get(trimmedType.toUpperCase(Locale.ENGLISH));
  597. // We need a 'special' check here to give a better error message. If
  598. // we're in this
  599. // block, the version of MySQL we're connected to doesn't support
  600. // CAST/CONVERT,
  601. // so we can't re-write some data type conversions
  602. // (date,time,timestamp, datetime)
  603. if (newType == null) {
  604. throw SQLError
  605. .createSQLException(
  606. "Can't find conversion re-write for type '"
  607. + type
  608. + "' that is applicable for this server version while processing escape tokens.",
  609. SQLError.SQL_STATE_GENERAL_ERROR, conn.getExceptionInterceptor());
  610. }
  611. }
  612. if (newType == null) {
  613. throw SQLError.createSQLException("Unsupported conversion type '"
  614. + type.trim() + "' found while processing escape token.",
  615. SQLError.SQL_STATE_GENERAL_ERROR, conn.getExceptionInterceptor());
  616. }
  617. int replaceIndex = newType.indexOf("?");
  618. if (replaceIndex != -1) {
  619. StringBuffer convertRewrite = new StringBuffer(newType.substring(0,
  620. replaceIndex));
  621. convertRewrite.append(expression);
  622. convertRewrite.append(newType.substring(replaceIndex + 1, newType
  623. .length()));
  624. return convertRewrite.toString();
  625. } else {
  626. StringBuffer castRewrite = new StringBuffer("CAST(");
  627. castRewrite.append(expression);
  628. castRewrite.append(" AS ");
  629. castRewrite.append(newType);
  630. castRewrite.append(")");
  631. return castRewrite.toString();
  632. }
  633. }
  634. /**
  635. * Removes all whitespace from the given String. We use this to make escape
  636. * token comparison white-space ignorant.
  637. *
  638. * @param toCollapse
  639. * the string to remove the whitespace from
  640. *
  641. * @return a string with _no_ whitespace.
  642. */
  643. private static String removeWhitespace(String toCollapse) {
  644. if (toCollapse == null) {
  645. return null;
  646. }
  647. int length = toCollapse.length();
  648. StringBuffer collapsed = new StringBuffer(length);
  649. for (int i = 0; i < length; i++) {
  650. char c = toCollapse.charAt(i);
  651. if (!Character.isWhitespace(c)) {
  652. collapsed.append(c);
  653. }
  654. }
  655. return collapsed.toString();
  656. }
  657. }