View Javadoc
1   package org.nuiton.util.sql;
2   
3   /*-
4    * #%L
5    * Nuiton Utils
6    * %%
7    * Copyright (C) 2004 - 2016 CodeLutin
8    * %%
9    * This program is free software: you can redistribute it and/or modify
10   * it under the terms of the GNU Lesser General Public License as
11   * published by the Free Software Foundation, either version 3 of the
12   * License, or (at your option) any later version.
13   * 
14   * This program is distributed in the hope that it will be useful,
15   * but WITHOUT ANY WARRANTY; without even the implied warranty of
16   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17   * GNU General Lesser Public License for more details.
18   * 
19   * You should have received a copy of the GNU General Lesser Public
20   * License along with this program.  If not, see
21   * <http://www.gnu.org/licenses/lgpl-3.0.html>.
22   * #L%
23   */
24  
25  import java.io.IOException;
26  import java.io.Reader;
27  import java.io.StringReader;
28  import java.util.Iterator;
29  import java.util.NoSuchElementException;
30  
31  /**
32   * Create an iterable on a SQL text content. The content is iterated on each SQL
33   * statement. For information the class handles semi-colon in quote.
34   * 
35   * File example:
36   * INSERT INTO client (prenom, age) VALUES ('John', 11);
37   * INSERT INTO client (prenom, age) VALUES ('Jack', 12);
38   * 
39   * Then:
40   * SqlFileReader reader = new SqlFileReader(stream);
41   * for (String sql : reader) {
42   *     // process sql variable
43   * }
44   * 
45   * @author jruchaud
46   */
47  public class SqlFileReader implements Iterable<String> {
48      
49      protected Reader source;
50  
51      public SqlFileReader(String source) {
52          this.source = new StringReader(source);
53      }
54  
55      public SqlFileReader(Reader source) {
56          this.source = source;
57      }
58  
59      @Override
60      public Iterator<String> iterator() {
61          return new SqlFileReaderIterator(source);
62      }
63      
64      enum SqlFileParserState {NORMAL, QUOTE, COMMENT};
65          
66      /**
67       * Use to create an iterator on the iterable.
68       */
69      public static class SqlFileReaderIterator implements Iterator<String> {
70  
71          protected Reader source;
72          
73          protected StringBuilder buffer;
74      
75          /** The variable is used to keep if the iterator reach the end */
76          protected int scanner;
77          
78          public SqlFileReaderIterator(Reader source) {
79              this.source = source;
80              this.buffer = new StringBuilder();
81          }
82  
83          @Override
84          public boolean hasNext() {
85              return this.scanner != - 1;
86          }
87  
88          @Override
89          public String next() {
90              if (this.scanner == -1) {
91                  throw new NoSuchElementException();
92              }
93              
94              SqlFileParserState state = SqlFileParserState.NORMAL;
95              this.buffer.setLength(0);
96                      
97              try {
98                  
99                  while ((this.scanner = this.source.read()) != -1) {
100                     char character = (char) this.scanner;
101                     
102                     switch (state) {
103                         case NORMAL:
104                             switch (character) {
105                                 // Remove useless character
106                                 case '\n':
107                                 case '\r':
108                                     break;
109 
110                                 // Search the end of query
111                                 case ';':
112                                     this.buffer.append(";");
113                                     return this.buffer.toString();
114 
115                                 // Enter comment state if you have --
116                                 case '-':
117                                     int length = this.buffer.length();
118                                     if (length != 0 && this.buffer.charAt(length - 1) == '-') {
119                                         state = SqlFileParserState.COMMENT;
120                                     }
121                                     this.buffer.append(character);
122                                     break;
123 
124                                 // Enter quote state
125                                 case '\'':
126                                     state = SqlFileParserState.QUOTE;
127                                     this.buffer.append(character);
128                                     break;
129 
130                                 // By default append character
131                                 default:
132                                     this.buffer.append(character);
133                                     break;
134                             }
135                             break;
136                             
137                         case QUOTE:
138                             // Remove useless character
139                             switch (character) {
140                                 // Search the end of quote
141                                 case '\'':
142                                     state = SqlFileParserState.NORMAL;
143                                     this.buffer.append(character);
144                                     break;
145 
146                                 // By default append character
147                                 default:
148                                     this.buffer.append(character);
149                                     break;
150 
151                             }
152                             break;
153                             
154                         case COMMENT:
155                             switch (character) {
156                                 // Search the end of comment
157                                 case '\n':
158                                 case '\r':
159                                     return this.buffer.toString();
160 
161                                 // By default append character
162                                 default:
163                                     this.buffer.append(character);
164                                     break;
165 
166                             }
167                             break;
168                     }
169                 }
170                 
171             } catch (IOException ex) {
172                 throw new RuntimeException(ex);
173             }
174             
175             return this.buffer.toString();
176         }
177 
178         @Override
179         public void remove() {
180             throw new UnsupportedOperationException("remove");
181         }
182 
183     }
184 }