1 | /* |
2 | Copyright (C) 2002-2004 MySQL AB |
3 | |
4 | This program is free software; you can redistribute it and/or modify |
5 | it under the terms of version 2 of the GNU General Public License as |
6 | published by the Free Software Foundation. |
7 | |
8 | There are special exceptions to the terms and conditions of the GPL |
9 | as it is applied to this software. View the full text of the |
10 | exception in file EXCEPTIONS-CONNECTOR-J in the directory of this |
11 | software distribution. |
12 | |
13 | This program is distributed in the hope that it will be useful, |
14 | but WITHOUT ANY WARRANTY; without even the implied warranty of |
15 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
16 | GNU General Public License for more details. |
17 | |
18 | You should have received a copy of the GNU General Public License |
19 | along with this program; if not, write to the Free Software |
20 | Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
21 | |
22 | |
23 | |
24 | */ |
25 | package com.mysql.jdbc; |
26 | |
27 | /** |
28 | * EscapeTokenizer breaks up an SQL statement into SQL and escape code parts. |
29 | * |
30 | * @author Mark Matthews |
31 | */ |
32 | public class EscapeTokenizer { |
33 | // ~ Instance fields |
34 | // -------------------------------------------------------- |
35 | |
36 | private int bracesLevel = 0; |
37 | |
38 | private boolean emittingEscapeCode = false; |
39 | |
40 | private boolean inComment = false; |
41 | |
42 | private boolean inQuotes = false; |
43 | |
44 | private char lastChar = 0; |
45 | |
46 | private char lastLastChar = 0; |
47 | |
48 | private int pos = 0; |
49 | |
50 | private char quoteChar = 0; |
51 | |
52 | private boolean sawVariableUse = false; |
53 | |
54 | private String source = null; |
55 | |
56 | private int sourceLength = 0; |
57 | |
58 | // ~ Constructors |
59 | // ----------------------------------------------------------- |
60 | |
61 | /** |
62 | * Creates a new EscapeTokenizer object. |
63 | * |
64 | * @param s |
65 | * the string to tokenize |
66 | */ |
67 | public EscapeTokenizer(String s) { |
68 | this.source = s; |
69 | this.sourceLength = s.length(); |
70 | this.pos = 0; |
71 | } |
72 | |
73 | // ~ Methods |
74 | // ---------------------------------------------------------------- |
75 | |
76 | /** |
77 | * Does this tokenizer have more tokens available? |
78 | * |
79 | * @return if this tokenizer has more tokens available |
80 | */ |
81 | public synchronized boolean hasMoreTokens() { |
82 | return (this.pos < this.sourceLength); |
83 | } |
84 | |
85 | /** |
86 | * Returns the next token |
87 | * |
88 | * @return the next token. |
89 | */ |
90 | public synchronized String nextToken() { |
91 | StringBuffer tokenBuf = new StringBuffer(); |
92 | |
93 | if (this.emittingEscapeCode) { |
94 | tokenBuf.append("{"); //$NON-NLS-1$ |
95 | this.emittingEscapeCode = false; |
96 | } |
97 | |
98 | for (; this.pos < this.sourceLength; this.pos++) { |
99 | char c = this.source.charAt(this.pos); |
100 | |
101 | // Detect variable usage |
102 | |
103 | if (!this.inQuotes && c == '@') { |
104 | this.sawVariableUse = true; |
105 | } |
106 | |
107 | if (c == '\'' || c == '"') { |
108 | if (this.inQuotes && c == quoteChar) { |
109 | if (this.pos + 1 < this.sourceLength) { |
110 | if (this.source.charAt(this.pos + 1) == quoteChar) { |
111 | // Doubled-up quote escape |
112 | tokenBuf.append(quoteChar); |
113 | tokenBuf.append(quoteChar); |
114 | this.pos++; |
115 | continue; |
116 | } |
117 | } |
118 | } |
119 | if (this.lastChar != '\\') { |
120 | if (this.inQuotes) { |
121 | if (this.quoteChar == c) { |
122 | this.inQuotes = false; |
123 | } |
124 | } else { |
125 | this.inQuotes = true; |
126 | this.quoteChar = c; |
127 | } |
128 | } else if (this.lastLastChar == '\\') { |
129 | if (this.inQuotes) { |
130 | if (this.quoteChar == c) { |
131 | this.inQuotes = false; |
132 | } |
133 | } else { |
134 | this.inQuotes = true; |
135 | this.quoteChar = c; |
136 | } |
137 | } |
138 | |
139 | tokenBuf.append(c); |
140 | } else if (c == '-') { |
141 | if ((this.lastChar == '-') |
142 | && ((this.lastLastChar != '\\') & !this.inQuotes)) { |
143 | this.inComment = true; |
144 | } |
145 | |
146 | tokenBuf.append(c); |
147 | } else if ((c == '\n') || (c == '\r')) { |
148 | this.inComment = false; |
149 | |
150 | tokenBuf.append(c); |
151 | } else if (c == '{') { |
152 | if (this.inQuotes || this.inComment) { |
153 | tokenBuf.append(c); |
154 | } else { |
155 | this.bracesLevel++; |
156 | |
157 | if (this.bracesLevel == 1) { |
158 | this.pos++; |
159 | this.emittingEscapeCode = true; |
160 | |
161 | return tokenBuf.toString(); |
162 | } |
163 | |
164 | tokenBuf.append(c); |
165 | } |
166 | } else if (c == '}') { |
167 | tokenBuf.append(c); |
168 | |
169 | if (!this.inQuotes && !this.inComment) { |
170 | this.lastChar = c; |
171 | |
172 | this.bracesLevel--; |
173 | |
174 | if (this.bracesLevel == 0) { |
175 | this.pos++; |
176 | |
177 | return tokenBuf.toString(); |
178 | } |
179 | } |
180 | } else { |
181 | tokenBuf.append(c); |
182 | } |
183 | |
184 | this.lastLastChar = this.lastChar; |
185 | this.lastChar = c; |
186 | } |
187 | |
188 | return tokenBuf.toString(); |
189 | } |
190 | |
191 | boolean sawVariableUse() { |
192 | return this.sawVariableUse; |
193 | } |
194 | } |