001 package org.apache.torque.engine.sql;
002
003 /*
004 * Licensed to the Apache Software Foundation (ASF) under one
005 * or more contributor license agreements. See the NOTICE file
006 * distributed with this work for additional information
007 * regarding copyright ownership. The ASF licenses this file
008 * to you under the Apache License, Version 2.0 (the
009 * "License"); you may not use this file except in compliance
010 * with the License. You may obtain a copy of the License at
011 *
012 * http://www.apache.org/licenses/LICENSE-2.0
013 *
014 * Unless required by applicable law or agreed to in writing,
015 * software distributed under the License is distributed on an
016 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
017 * KIND, either express or implied. See the License for the
018 * specific language governing permissions and limitations
019 * under the License.
020 */
021
022 import java.io.IOException;
023 import java.io.Reader;
024 import java.util.List;
025 import java.util.ArrayList;
026
027 /**
028 * A simple Scanner implementation that scans an
029 * sql file into usable tokens. Used by SQLToAppData.
030 *
031 * @author <a href="mailto:leon@opticode.co.za">Leon Messerschmidt</a>
032 * @author <a href="mailto:jon@latchkey.com">Jon S. Stevens</a>
033 * @author <a href="mailto:andyhot@di.uoa.gr">Andreas Andreou</a>
034 * @version $Id: SQLScanner.java,v 1.1 2007-10-21 07:57:27 abyrne Exp $
035 */
036 public class SQLScanner
037 {
038 /** white spaces */
039 private static final String WHITE = "\f\r\t\n ";
040 /** alphabetic characters */
041 private static final String ALFA
042 = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
043 /** numbers */
044 private static final String NUMER = "0123456789";
045 /** alphanumeric */
046 private static final String ALFANUM = ALFA + NUMER;
047 /** special characters */
048 private static final String SPECIAL = ";(),'";
049 /** comment */
050 private static final char COMMENT_POUND = '#';
051 /** comment */
052 private static final char COMMENT_SLASH = '/';
053 /** comment */
054 private static final char COMMENT_STAR = '*';
055 /** comment */
056 private static final char COMMENT_DASH = '-';
057
058 /** the input reader */
059 private Reader in;
060 /** character */
061 private int chr;
062 /** token */
063 private String token;
064 /** list of tokens */
065 private List tokens;
066 /** line */
067 private int line;
068 /** column */
069 private int col;
070
071 /**
072 * Creates a new scanner with no Reader
073 */
074 public SQLScanner()
075 {
076 this(null);
077 }
078
079 /**
080 * Creates a new scanner with an Input Reader
081 *
082 * @param input the input reader
083 */
084 public SQLScanner(Reader input)
085 {
086 setInput(input);
087 }
088
089 /**
090 * Set the Input
091 *
092 * @param input the input reader
093 */
094 public void setInput(Reader input)
095 {
096 in = input;
097 }
098
099
100 /**
101 * Reads the next character and increments the line and column counters.
102 *
103 * @throws IOException If an I/O error occurs
104 */
105 private void readChar() throws IOException
106 {
107 boolean wasLine = (char) chr == '\r';
108 chr = in.read();
109 if ((char) chr == '\n' || (char) chr == '\r' || (char) chr == '\f')
110 {
111 col = 0;
112 if (!wasLine || (char) chr != '\n')
113 {
114 line++;
115 }
116 }
117 else
118 {
119 col++;
120 }
121 }
122
123 /**
124 * Scans an identifier.
125 *
126 * @throws IOException If an I/O error occurs
127 */
128 private void scanIdentifier () throws IOException
129 {
130 token = "";
131 char c = (char) chr;
132 while (chr != -1 && WHITE.indexOf(c) == -1 && SPECIAL.indexOf(c) == -1)
133 {
134 token = token + (char) chr;
135 readChar();
136 c = (char) chr;
137 }
138 int start = col - token.length();
139 tokens.add(new Token(token, line, start));
140 }
141
142 /**
143 * Scans an identifier which had started with the negative sign.
144 *
145 * @throws IOException If an I/O error occurs
146 */
147 private void scanNegativeIdentifier () throws IOException
148 {
149 token = "-";
150 char c = (char) chr;
151 while (chr != -1 && WHITE.indexOf(c) == -1 && SPECIAL.indexOf(c) == -1)
152 {
153 token = token + (char) chr;
154 readChar();
155 c = (char) chr;
156 }
157 int start = col - token.length();
158 tokens.add(new Token(token, line, start));
159 }
160
161 /**
162 * Scan the input Reader and returns a list of tokens.
163 *
164 * @return a list of tokens
165 * @throws IOException If an I/O error occurs
166 */
167 public List scan () throws IOException
168 {
169 line = 1;
170 col = 0;
171 boolean inComment = false;
172 boolean inCommentSlashStar = false;
173 boolean inCommentDash = false;
174
175 boolean inNegative;
176
177 tokens = new ArrayList();
178 readChar();
179 while (chr != -1)
180 {
181 char c = (char) chr;
182 inNegative = false;
183
184 if (c == COMMENT_DASH)
185 {
186 readChar();
187 if ((char) chr == COMMENT_DASH)
188 {
189 inCommentDash = true;
190 }
191 else
192 {
193 inNegative = true;
194 c = (char) chr;
195 }
196 }
197
198 if (inCommentDash)
199 {
200 if (c == '\n' || c == '\r')
201 {
202 inCommentDash = false;
203 }
204 readChar();
205 }
206 else if (c == COMMENT_POUND)
207 {
208 inComment = true;
209 readChar();
210 }
211 else if (c == COMMENT_SLASH)
212 {
213 readChar();
214 if ((char) chr == COMMENT_STAR)
215 {
216 inCommentSlashStar = true;
217 }
218 }
219 else if (inComment || inCommentSlashStar)
220 {
221 if (c == '*')
222 {
223 readChar();
224 if ((char) chr == COMMENT_SLASH)
225 {
226 inCommentSlashStar = false;
227 }
228 }
229 else if (c == '\n' || c == '\r')
230 {
231 inComment = false;
232 }
233 readChar();
234 }
235 else if (ALFANUM.indexOf(c) >= 0)
236 {
237 if (inNegative)
238 {
239 scanNegativeIdentifier();
240 }
241 else
242 {
243 scanIdentifier();
244 }
245 }
246 else if (SPECIAL.indexOf(c) >= 0)
247 {
248 tokens.add(new Token("" + c, line, col));
249 readChar();
250 }
251 else
252 {
253 readChar();
254 }
255 }
256 return tokens;
257 }
258 }