001: // Copyright (c) 2003-2007, Jodd Team (jodd.sf.net). All Rights Reserved.
002:
003: package jodd.db;
004:
005: import jodd.util.StringUtil;
006: import jodd.util.collection.IntArrayList;
007:
008: import java.util.Map;
009: import java.util.HashMap;
010: import java.util.Iterator;
011:
012: /**
013: * SQL parameters parser that recognizes named and ordinal parameters.
014: */
015: class DbQueryParser {
016:
017: public static final String SQL_SEPARATORS = " \n\r\f\t,()=<>&|+-=/*'^![]#~\\";
018:
019: boolean prepared;
020:
021: String sql;
022:
023: // ---------------------------------------------------------------- ctors
024:
025: DbQueryParser() {
026: }
027:
028: DbQueryParser(String sql) {
029: parseSql(sql);
030: }
031:
032: // ---------------------------------------------------------------- parameters
033:
034: private Map namedParameterLocationMap;
035:
036: private void storeNamedParameter(String name, int position) {
037: IntArrayList locations = (IntArrayList) namedParameterLocationMap
038: .get(name);
039: if (locations == null) {
040: locations = new IntArrayList();
041: namedParameterLocationMap.put(name, locations);
042: }
043: locations.add(position);
044: }
045:
046: IntArrayList getNamedParameterPositions(String name) {
047: return (IntArrayList) namedParameterLocationMap.get(name);
048: }
049:
050: IntArrayList getExistingNamedParameterPositions(String name) {
051: IntArrayList positions = (IntArrayList) namedParameterLocationMap
052: .get(name);
053: if (positions == null) {
054: throw new DbSqlException("Named parameter '" + name
055: + "' not found.");
056: }
057: return positions;
058: }
059:
060: Iterator iterateNamedParameters() {
061: return namedParameterLocationMap.keySet().iterator();
062: }
063:
064: // ---------------------------------------------------------------- parser
065:
066: void parseSql(String sqlString) {
067: namedParameterLocationMap = new HashMap();
068: int stringLength = sqlString.length();
069: StringBuilder pureSql = new StringBuilder(stringLength);
070: boolean inQuote = false;
071: int index = 0;
072: int paramCount = 0;
073: while (index < stringLength) {
074: char c = sqlString.charAt(index);
075: if (inQuote == true) {
076: if (c == '\'') {
077: inQuote = false;
078: }
079: } else if (c == '\'') {
080: inQuote = true;
081: } else if (c == ':') {
082: int right = StringUtil.indexOfChars(sqlString,
083: SQL_SEPARATORS, index + 1);
084: if (right < 0) {
085: right = stringLength;
086: }
087: String param = sqlString.substring(index + 1, right);
088: paramCount++;
089: storeNamedParameter(param, paramCount);
090: pureSql.append('?');
091: index = right;
092: continue;
093: } else if (c == '?') { // either an ordinal or positional parameter
094: if ((index < stringLength - 1)
095: && (Character.isDigit(sqlString
096: .charAt(index + 1)))) { // positional parameter
097: int right = StringUtil.indexOfChars(sqlString,
098: SQL_SEPARATORS, index + 1);
099: if (right < 0) {
100: right = stringLength;
101: }
102: String param = sqlString
103: .substring(index + 1, right);
104: try {
105: Integer.parseInt(param);
106: } catch (NumberFormatException nfex) {
107: throw new DbSqlException(
108: "Positional parameter '"
109: + nfex
110: + "' is not an integral ordinal.",
111: nfex);
112: }
113: paramCount++;
114: storeNamedParameter(param, paramCount);
115: pureSql.append('?');
116: index = right;
117: continue;
118: }
119: paramCount++; // ordinal param
120: }
121: pureSql.append(c);
122: index++;
123: }
124: this .prepared = (paramCount != 0);
125: this.sql = pureSql.toString();
126: }
127: }
|