KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > derby > impl > tools > ij > StatementFinder


1 /*
2
3    Derby - Class org.apache.derby.impl.tools.ij.StatementFinder
4
5    Licensed to the Apache Software Foundation (ASF) under one or more
6    contributor license agreements. See the NOTICE file distributed with
7    this work for additional information regarding copyright ownership.
8    The ASF licenses this file to You under the Apache License, Version 2.0
9    (the "License"); you may not use this file except in compliance with
10    the License. You may obtain a copy of the License at
11
12       http://www.apache.org/licenses/LICENSE-2.0
13
14    Unless required by applicable law or agreed to in writing, software
15    distributed under the License is distributed on an "AS IS" BASIS,
16    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17    See the License for the specific language governing permissions and
18    limitations under the License.
19
20  */

21
22 package org.apache.derby.impl.tools.ij;
23
24 import java.io.IOException JavaDoc;
25 import java.io.Reader JavaDoc;
26
27 /**
28     StatementGrabber looks through an input stream for
29     the next JSQL statement. A statement is considered to
30     be any tokens up to the next semicolon or EOF.
31     <p>
32     Semicolons inside comments, strings, and delimited identifiers
33     are not considered to be statement terminators but to be
34     part of those tokens.
35     <p>
36     The only comment form currently recognized is the SQL comment,
37     which begins with "--" and ends at the next EOL.
38     <p>
39     Strings and delimited identifiers are permitted to contain
40     newlines; the actual IJ or JSQL parsers will report errors when
41     those cases occur.
42     <p>
43     There are no escaped characters, i.e. "\n" is considered to
44     be two characters, '\' and 'n'.
45
46     @author ames
47  */

48
49 public class StatementFinder {
50
51     private Reader JavaDoc source;
52     private StringBuffer JavaDoc statement = new StringBuffer JavaDoc();
53     private int state;
54     private boolean atEOF = false;
55     private boolean peekEOF = false;
56     private char peekChar;
57     private boolean peeked = false;
58
59     // state variables
60
private static final int IN_STATEMENT = 0;
61     private static final int IN_STRING = 1;
62     private static final int IN_SQLCOMMENT = 2;
63     private static final int END_OF_STATEMENT = 3;
64     private static final int END_OF_INPUT = 4;
65
66     // special state-changing characters
67
private static final char MINUS = '-';
68     private static final char SINGLEQUOTE = '\'';
69     private static final char DOUBLEQUOTE = '\"';
70     private static final char SEMICOLON = ';';
71     private static final char NEWLINE = '\n';
72     private static final char RETURN = '\r';
73     private static final char SPACE = ' ';
74     private static final char TAB = '\t';
75     private static final char FORMFEED = '\f';
76
77     /**
78         The constructor does not assume the stream is data input
79         or buffered, so it will wrap it appropriately.
80
81         @param s the input stream for reading statements from.
82      */

83     public StatementFinder(Reader JavaDoc s) {
84         source = s;
85     }
86
87     /**
88         Reinit is used to redirect the finder to another stream.
89         The previous stream should not have been in a PEEK state.
90
91         @param s the input stream for reading statements from.
92      */

93     public void ReInit(Reader JavaDoc s) {
94         try {
95             source.close();
96         } catch (IOException JavaDoc ioe) {
97             // just be quiet if it is already gone
98
}
99         source = s;
100         state = IN_STATEMENT;
101         atEOF = false;
102         peekEOF = false;
103         peeked = false;
104     }
105
106     public void close() throws IOException JavaDoc {
107         source.close();
108     }
109
110     /**
111         get the next statement in the input stream. Returns it,
112         dropping its closing semicolon if it has one. If there is
113         no next statement, return a null.
114
115         @return the next statement in the input stream.
116      */

117     public String JavaDoc nextStatement() {
118         boolean haveSemi = false;
119         char nextChar;
120
121         // initialize fields for getting the next statement
122
statement.setLength(0);
123         if (state == END_OF_INPUT) return null;
124
125         state = IN_STATEMENT;
126
127         // skip leading whitespace
128
nextChar = peekChar();
129         if (peekEOF()) {
130             state = END_OF_INPUT;
131             return null;
132         }
133         if (whiteSpace(nextChar)) {
134             while (whiteSpace(peekChar()) && ! peekEOF());
135             if (peekEOF()) {
136                 state = END_OF_INPUT;
137                 return null;
138             }
139         }
140
141         while (state != END_OF_STATEMENT && state != END_OF_INPUT) {
142
143             // get the next character from the input
144
nextChar = readChar();
145             if (atEOF()) {
146                 state = END_OF_INPUT;
147                 break;
148             }
149
150             switch(nextChar) {
151                 case MINUS:
152                     readSingleLineComment(nextChar);
153                     break;
154                 case SINGLEQUOTE:
155                 case DOUBLEQUOTE:
156                     readString(nextChar);
157                     break;
158                 case SEMICOLON:
159                     haveSemi = true;
160                     state = END_OF_STATEMENT;
161                     break;
162                 default:
163                     // keep going, just a normal character
164
break;
165             }
166         }
167
168         if (haveSemi)
169             statement.setLength(statement.length()-1);
170         return statement.toString();
171     }
172
173     /**
174         Determine if the given character is considered whitespace
175
176         @param c the character to consider
177         @return true if the character is whitespace
178      */

179     private boolean whiteSpace(char c) {
180         return (c == SPACE ||
181                 c == TAB ||
182                 c == RETURN ||
183                 c == NEWLINE ||
184                 c == FORMFEED);
185     }
186
187     /**
188         Advance the source stream to the end of a comment if it
189         is on one, assuming the first character of
190         a potential single line comment has been found.
191         If it is not a comment, do not advance the stream.
192         <p>
193         The form of a single line comment is, in regexp, XX.*$,
194         where XX is two instances of commentChar.
195
196         @param commentChar the character whose duplication signifies
197             the start of the comment.
198      */

199     private void readSingleLineComment(char commentChar) {
200         char nextChar;
201
202         nextChar = peekChar();
203         // if next char is EOF, we are done.
204
if (peekEOF()) return;
205
206         // if nextChar is not a minus, it was just a normal minus,
207
// nothing special to do
208
if (nextChar != commentChar) return;
209
210         // we are really in a comment
211
readChar(); // grab the minus for real.
212

213         state = IN_SQLCOMMENT;
214         do {
215             nextChar = peekChar();
216             if (peekEOF()) {
217                 // let the caller process the EOF, don't read it
218
state = IN_STATEMENT;
219                 return;
220             }
221             switch (nextChar) {
222                 case NEWLINE:
223                 case RETURN:
224                     readChar(); // okay to process the character
225
state = IN_STATEMENT;
226                     return;
227                 default:
228                     readChar(); // process the character, still in comment
229
break;
230             }
231         } while (state == IN_SQLCOMMENT); // could be while true...
232
}
233
234     /**
235         Advance the stream to the end of the string.
236         Assumes the opening delimiter of the string has been read.
237         This handles the SQL ability to put the delimiter within
238         the string by doubling it, by reading those as two strings
239         sitting next to one another. I.e, 'Mary''s lamb' is read
240         by this class as two strings, 'Mary' and 's lamb'.
241         <p>
242         The delimiter of the string is expected to be repeated at
243         its other end. If the other flavor of delimiter occurs within
244         the string, it is just a normal character within it.
245         <p>
246         All characters except the delimiter are permitted within the
247         string. If EOF is hit before the closing delimiter is found,
248         the end of the string is assumed. Parsers using this parser
249         will detect the error in that case and return appropriate messages.
250
251         @param stringDelimiter the starting and ending character
252             for the string being read.
253      */

254     private void readString(char stringDelimiter) {
255         state = IN_STRING;
256         do {
257             char nextChar = readChar();
258
259             if (atEOF()) {
260                 state = END_OF_INPUT;
261                 return;
262             }
263
264             if (nextChar == stringDelimiter) {
265                 // we've reached the end of the string
266
state = IN_STATEMENT;
267                 return;
268             }
269
270             // still in string
271
} while (state == IN_STRING); // could be while true...
272
}
273
274     private boolean atEOF() {
275         return atEOF;
276     }
277
278     private boolean peekEOF() {
279         return peekEOF;
280     }
281
282     /**
283         return the next character in the source stream and
284         append it to the statement buffer.
285
286         @return the next character in the source stream.
287      */

288     private char readChar() {
289         if (!peeked) peekChar();
290
291         peeked = false;
292         atEOF = peekEOF;
293
294         if (!atEOF) statement.append(peekChar);
295
296         return peekChar;
297     }
298
299     /**
300         return the next character in the source stream, without
301         advancing.
302
303         @return the next character in the source stream.
304      */

305     private char peekChar() {
306         peeked = true;
307         char c = '\00';
308
309         try {
310             int cInt;
311
312             // REMIND: this is assuming a flat ascii source file.
313
// will need to beef it up at some future point to
314
// understand whether the stream is ascii or something else.
315
cInt = source.read();
316             peekEOF = (cInt == -1);
317             if (!peekEOF) c = (char)cInt;
318         } catch (IOException JavaDoc ie) {
319             throw ijException.iOException(ie);
320         }
321
322         peekChar = c;
323         return c;
324     }
325 }
326
Popular Tags