Project Report: fawkez

Packagesummary org.jcoderz.phoenix.sqlparser

org.jcoderz.phoenix.sqlparser.SqlScanner

LineHitsNoteSource
1  /*
2   * $Id: SqlScanner.java 1264 2008-12-12 13:32:27Z amandel $
3   *
4   * Copyright 2006, The jCoderZ.org Project. All rights reserved.
5   *
6   * Redistribution and use in source and binary forms, with or without
7   * modification, are permitted provided that the following conditions are
8   * met:
9   *
10   *    * Redistributions of source code must retain the above copyright
11   *      notice, this list of conditions and the following disclaimer.
12   *    * Redistributions in binary form must reproduce the above
13   *      copyright notice, this list of conditions and the following
14   *      disclaimer in the documentation and/or other materials
15   *      provided with the distribution.
16   *    * Neither the name of the jCoderZ.org Project nor the names of
17   *      its contributors may be used to endorse or promote products
18   *      derived from this software without specific prior written
19   *      permission.
20   *
21   * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS "AS IS" AND
22   * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23   * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
24   * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS
25   * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
26   * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
27   * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
28   * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
29   * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
30   * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
31   * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32   */
33  package org.jcoderz.phoenix.sqlparser;
34  
35  import java.io.BufferedInputStream;
36  import java.io.FileInputStream;
37  import java.io.IOException;
38  import java.io.InputStream;
39  import java.math.BigDecimal;
40  import java.util.ArrayList;
41  import java.util.Iterator;
42  import java.util.List;
43  
44  import org.jcoderz.commons.util.Constants;
45  
46  /**
47   * Simple SQL Scanner.
48   *
49   * @author Michael Griffel
50   */
51  public final class SqlScanner
52        implements ScannerInterface
53  {
54     private final BufferedInputStream mInputStream;
55100    private int mColumn = 0;
56100    private int mLine = 1;
57100    private boolean mReportWhitespace = true;
58100    private int mSaveColumn = 0;
59  
60     /**
61      * create a new SQL Scanner.
62      * @param input the input stream to read SQL data from
63      */
64     public SqlScanner (InputStream input)
65100    {
66100       mInputStream = new BufferedInputStream(input);
67100    }
68  
69     /**
70      * Returns the reportWhitespace.
71      * @return the reportWhitespace.
72      */
73     public boolean isSetReportWhitespace ()
74     {
750       return mReportWhitespace;
76     }
77  
78     /**
79      * Sets the reportWhitespace to given <code>reportWhitespace</code>.
80      * @param reportWhitespace The reportWhitespace to set.
81      */
82     public void setReportWhitespace (boolean reportWhitespace)
83     {
84100       mReportWhitespace = reportWhitespace;
85100    }
86  
87     /**
88      * Returns the line.
89      * @return the line.
90      */
91     public int getLine ()
92     {
930       return mLine;
94     }
95  
96     /**
97      * Returns the offset.
98      * @return the offset.
99      */
100     public int getColumn ()
101     {
1020       return mColumn;
103     }
104  
105     /**
106      * This is just a wrapper around the real nextToken() method for logging.
107      * @return the next token
108      * @throws ParseException if a syntax error is encountered
109      * @see org.jcoderz.phoenix.sqlparser.ScannerInterface#nextToken()
110      */
111     public Token nextToken ()
112           throws ParseException
113     {
114100       return getNextToken();
115     }
116  
117     /** {@inheritDoc} */
118 (1)(2)(3)(4)   private Token getNextToken ()
119           throws ParseException
120     {
121        for (;;)
122        {
123100          mark();
124100          final int c = read();
125  
126100          if (c == -1) // EOF
127           {
128100             return new Token(TokenType.EOF);
129           }
130100          else if (isNewlineChar((char) c))
131           {
132100             final Token t = eatNewline(c);
133100             if (mReportWhitespace)
134              {
135100                return t;
136              }
137              continue;
138           }
139100          else if (Character.isWhitespace((char) c))
140           {
141100             final Token t = eatWhitespaces(c);
142100             if (mReportWhitespace)
143              {
144100                return t;
145              }
146              continue;
147           }
148100          else if (c == '(')
149           {
150100             return new Token(TokenType.OPEN_PAREN, asString(c));
151           }
152100          else if (c == ')')
153           {
154100             return new Token(TokenType.CLOSE_PAREN, asString(c));
155           }
156100          else if (c == ';')
157           {
158100             return new Token(TokenType.SEMICOLON, asString(c));
159           }
160100          else if (c == ',')
161           {
162100             return new Token(TokenType.COMMA, asString(c));
163           }
164100          else if (c == '/') // maybe block comment or single slash
165           {
1660             mark();
1670             if (read() == '*') // a block comment
168              {
1690                final String comment = eatBlockComment();
1700                return new Token(TokenType.COMMENT, comment);
171              }
1720             reset();
1730             return new Token(TokenType.SLASH, asString(c));
174           }
175100          else if (c == '-') // comment or numeric
176           {
177100             mark();
178100             final int d = read();
179  
180              final Token t;
181100             if (d == '-') // -> comment
182              {
183100                final StringBuffer sb = new StringBuffer();
184100                sb.append("--");
185                 for (;;)
186                 {
187100                   mark();
188100                   final int e = read();
189100                   if (e == '\n' || e == -1) // end of line or eof
190                    {
191100                      reset();
192100                      break;
193                    }
194100                   sb.append((char) e);
195100                }
196100                t = new Token(TokenType.COMMENT, sb.toString());
197100             }
1980             else if (Character.isDigit((char) d))// (negative) nummeric
199              {
2000                final StringBuffer sb = new StringBuffer();
2010                sb.append('-');
2020                sb.append((char) d);
203                 for (;;)
204                 {
2050                   mark();
2060                   final int e = read();
2070                   if (! Character.isDigit((char) e))
208                    {
2090                      reset();
2100                      break;
211                    }
2120                   sb.append((char) e);
2130                }
214  
2150                final String negativeNumeric = sb.toString();
216                 try
217                 {
2180                   Integer.parseInt(negativeNumeric);
2190                   t = new Token(TokenType.NUMERIC_LITERAL, negativeNumeric);
220                 }
2210                catch (NumberFormatException shouldNotOccur)
222                 {
2230                   throw new ParseException("Cannot parse negative numberic '"
224                          + negativeNumeric
225                          + "'", shouldNotOccur, mLine, mColumn);
2260                }
2270             }
228              // operator '- ', '-(' or '-function'
2290             else if (d == '(' || Character.isLetter((char) d)
230                    || Character.isWhitespace((char) d))
231              {
2320                reset();
2330                return new Token(TokenType.OPERATOR, asString(c));
234              }
235              else
236              {
2370                throw new ParseException("Unexpected char '" + (char) d
238                     + "', expected '-' or digit.", mLine, mColumn);
239              }
240100             return t;
241           }
242100          else if (c == '"' || c == '\'') // literal
243           {
244100             final String literal = readStringLiteral(c);
245100             return new Token(TokenType.STRING_LITERAL, literal);
246           }
247           else // keywords, identifier
248           {
249100             final String word = readWord(c);
250  
251              try
252              {
253 (5)               // FIXME: prefix keyword? otherwise 'comma' will be a keyword
254100                if (!TokenType.OPERATOR.toString().equalsIgnoreCase(word))
255                 {
256100                    final TokenType tokenType
257                        = TokenType.fromString(
258                              word.toLowerCase(Constants.SYSTEM_LOCALE));
259100                    return new Token(tokenType, word);
260                 }
261              }
262100             catch (IllegalArgumentException ignore)
263              {
264                 // not a known keyword
2650             }
266  
267              // numeric literal?
268              try
269              {
270100(6)               new BigDecimal(word); // well-formed?
271100                return new Token(TokenType.NUMERIC_LITERAL, word);
272              }
273100             catch (NumberFormatException ignore)
274              {
275                 // not a numeric
276              }
277  
278              // otherwise it must be a identifier (hopefully)
279100             return new Token(TokenType.IDENTIFIER, word);
280           }
281        }
282     }
283  
284     private String eatBlockComment ()
285           throws ParseException
286     {
287        // read block comment
2880       final StringBuffer sb = new StringBuffer();
2890       sb.append("/*");
290        for (;;)
291        {
2920          mark();
2930          final int d = read();
2940          if (d == '*') // maybe end of block comment
295           {
2960             mark();
2970             if (read() != '/') // not end of block comment
298              {
2990                reset();
3000                sb.append((char) d);
3010                continue;
302              }
3030             sb.append("*/");
3040             break;
305           }
3060          else if (isNewlineChar((char) d))
307           {
3080             ++mLine; mColumn = 0;
309           }
3100          sb.append((char) d);
3110       }
3120       return sb.toString();
313     }
314  
315     private String readWord (int c)
316           throws ParseException
317     {
318100       final StringBuffer sb = new StringBuffer();
319100       sb.append((char) c);
320        for (;;)
321        {
322100          mark();
323100          final int d = read();
324  
325100          if (isSpecialCharacter((char) d))
326           {
327100             reset();
328100             break;
329           }
330100          sb.append((char) d);
331100       }
332100       return sb.toString();
333     }
334  
335     private String readStringLiteral (int c)
336           throws ParseException
337     {
338100       final StringBuffer sb = new StringBuffer();
339100       sb.append((char) c);
340        for (;;)
341        {
342100          final int d = read();
343100          sb.append((char) d);
344  
345100          if (d == '"' || d == '\'')
346           {
347100             break;
348           }
349100       }
350100       return sb.toString();
351     }
352  
353     private static boolean isSpecialCharacter (char c)
354     {
355100       return (Character.isWhitespace(c) || c == '(' || c == ')'
356              || c == ';' || c == ',' || c == '-');
357     }
358  
359     private Token eatNewline (int c)
360           throws ParseException
361     {
362        final Token t;
363100       if (c == Constants.LINE_FEED_CHAR) // UNIX newline?
364        {
365100          ++mLine; mColumn = 0;
366100          t = new Token(TokenType.NEWLINE, asString(Constants.LINE_FEED_CHAR));
367        }
3680       else if (c == Constants.CARRIAGE_RETURN_CHAR) // WINDOWS newline?
369        {
3700          mark();
3710          if (read() != Constants.LINE_FEED_CHAR) // eat LF
372           {
3730             reset();
374           }
3750          ++mLine; mColumn = 0;
3760          t = new Token(TokenType.NEWLINE,
377                   asString(Constants.CARRIAGE_RETURN_CHAR)
378                   + asString(Constants.LINE_FEED_CHAR));
379        }
380        else
381        {
3820          throw new ParseException("Unexpected newline char '"
383                 + (char) c + "'", mLine, mColumn);
384        }
385100       return t;
386     }
387  
388     private Token eatWhitespaces (int c)
389           throws ParseException
390     {
391100       final StringBuffer sb = new StringBuffer();
392100(7)      sb.append((char) c); // TODO: assertTrue(isWhitespace(c));
393        for (;;)
394        {
395100          mark();
396100          final int d = read();
397  
398100          if (Character.isWhitespace((char) d)
399                 && ! isNewlineChar((char) d))
400           {
401100             sb.append((char) d);
402           }
403           else // not a whitespace, or is newline
404           { // which must be reported separately
405100             reset();
406100             break;
407           }
408100       }
409100       return new Token(TokenType.WHITESPACE, sb.toString());
410     }
411  
412     private void reset ()
413           throws ParseException
414     {
415        try
416        {
417100          mInputStream.reset();
418100          mColumn = mSaveColumn;
419        }
4200       catch (IOException e)
421        {
4220          final ParseException pe
423              = new ParseException(e, mLine, mColumn);
4240          pe.initCause(e);
4250          throw pe;
426100       }
427100    }
428  
429     private void mark ()
430     {
431100       mSaveColumn = mColumn;
432100       mInputStream.mark(Integer.MAX_VALUE);
433100    }
434  
435     private static String asString (int c)
436     {
437100       return Character.toString((char) c);
438     }
439  
440     private int read ()
441           throws ParseException
442     {
443100       int c = -1;
444        try
445        {
446100          ++mColumn;
447100          c = mInputStream.read();
448        }
4490       catch (IOException e)
450        {
4510          throw new ParseException(e, mLine, mColumn);
452100       }
453100       return c;
454     }
455  
456     private static boolean isNewlineChar (char c)
457     {
458100       return (c == Constants.LINE_FEED_CHAR
459                || c == Constants.CARRIAGE_RETURN_CHAR);
460     }
461  
462     /**
463      * Simple SQL Scanner that reads the file given at argument 1 and dumps
464      * the tokens to <code>stderr</code> and the content on <code>stdout</code>.
465      *
466      * @param args command line arguments
467      * @throws Exception An error occurred
468      */
469     public static void main (String[] args)
470 (8)         throws Exception
471     {
4720       final SqlScanner scanner
473           = new SqlScanner(new FileInputStream(args[0]));
474  
4750       final List tokens = new ArrayList();
476  
477        for (;;)
478        {
4790          final Token t = scanner.nextToken();
4800          System.err.println(scanner.getLine() + ": "
481                 + scanner.getColumn() + " = " + t);
4820          tokens.add(t);
4830          if (t.getType() == TokenType.EOF)
484           {
4850             break;
486           }
4870       }
488  
4890       for (final Iterator iterator = tokens.iterator(); iterator.hasNext();)
490        {
4910          final Token t = (Token) iterator.next();
4920          System.out.print(t.getValue());
4930       }
4940       System.out.flush();
4950    }
496  }

Findings in this File

f (9) System.out.print is used main class
f (10) System.out.print is used main class
c (1) 118 : 4 Cyclomatic Complexity is 30 (max allowed is 12).
c (2) 118 : 4 Cyclomatic Complexity is 30 (max allowed is 20).
d (3) 118 : 4 Method length is 163 lines (max allowed is 100).
c (4) 118 : 4 Return count is 15 (max allowed is 1).
i (5) 253 : 0 Comment matches to-do format '(TODO|FIXME|CHECKME)'.
i (6) 270 : 0 org.jcoderz.phoenix.sqlparser.SqlScanner.getNextToken() ignores return value of new java.math.BigDecimal(String)
i (7) 392 : 0 Comment matches to-do format '(TODO|FIXME|CHECKME)'.
d (8) 470 : 17 A method/constructor shouldn't explicitly throw java.lang.Exception