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 }