1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package org.apache.ibatis.migration;
17
18 import java.io.File;
19 import java.io.FileInputStream;
20 import java.io.FilterReader;
21 import java.io.IOException;
22 import java.io.InputStream;
23 import java.io.InputStreamReader;
24 import java.io.Reader;
25 import java.nio.charset.Charset;
26 import java.util.Properties;
27
28 public class MigrationReader extends FilterReader {
29
30
31 private final String lineSeparator = System.getProperty("line.separator", "\n");
32
33 private static final String UNDO_TAG = "@UNDO";
34
35 private boolean undo;
36
37 private Properties variables;
38
39 private Part part = Part.NEW_LINE;
40
41 private VariableStatus variableStatus = VariableStatus.NOTHING;
42
43 private char previousChar;
44
45 private int undoIndex;
46
47 private int afterCommentPrefixIndex;
48
49 private int afterDoubleSlashIndex;
50
51 private boolean inUndo;
52
53 private final StringBuilder buffer = new StringBuilder();
54
55 private final StringBuilder lineBuffer = new StringBuilder();
56
57 private final VariableReplacer replacer;
58
59 private enum Part {
60 NOT_UNDO_LINE,
61
62 NEW_LINE,
63
64 COMMENT_PREFIX,
65
66 AFTER_COMMENT_PREFIX,
67
68 DOUBLE_SLASH,
69
70 AFTER_DOUBLE_SLASH,
71
72 UNDO_TAG,
73
74 AFTER_UNDO_TAG
75 }
76
77 private enum VariableStatus {
78 NOTHING,
79
80 FOUND_DOLLAR,
81
82 FOUND_OPEN_BRACE,
83
84 FOUND_POSSIBLE_VARIABLE
85 }
86
87 public MigrationReader(File file, String charset, boolean undo, Properties variables) throws IOException {
88 this(new FileInputStream(file), charset, undo, variables);
89 }
90
91 public MigrationReader(InputStream inputStream, String charset, boolean undo, Properties variables) {
92 super(scriptFileReader(inputStream, charset));
93 this.undo = undo;
94 this.variables = variables;
95 replacer = new VariableReplacer(this.variables);
96 }
97
98 @Override
99 public int read(char[] cbuf, int off, int len) throws IOException {
100 if (!undo && inUndo) {
101 if (buffer.length() > 0) {
102 return readFromBuffer(cbuf, off, len);
103 }
104 return -1;
105 }
106 while (buffer.length() == 0) {
107 int result = in.read(cbuf, off, len);
108 if (result == -1) {
109 if (lineBuffer.length() > 0 && (!undo || inUndo)) {
110 addToBuffer(lineBuffer);
111 }
112 if (buffer.length() > 0) {
113 break;
114 }
115 return -1;
116 }
117
118 for (int i = off; i < off + result; i++) {
119 char c = cbuf[i];
120
121 determinePart(c);
122 searchVariable(c);
123
124 if (c == '\r' || c == '\n' && previousChar != '\r') {
125 switch (part) {
126 case AFTER_UNDO_TAG:
127 if (!undo) {
128
129 lineBuffer.setLength(0);
130 int bufferLen = buffer.length();
131 if (bufferLen == 0) {
132 return -1;
133 }
134 return readFromBuffer(cbuf, off, len);
135 }
136 addToBuffer(lineBuffer.delete(afterCommentPrefixIndex, afterDoubleSlashIndex)
137 .insert(afterCommentPrefixIndex, ' '));
138 inUndo = true;
139 break;
140 case NOT_UNDO_LINE:
141 if (!undo || inUndo) {
142 addToBuffer(lineBuffer);
143 } else {
144 lineBuffer.setLength(0);
145 }
146 break;
147 default:
148 break;
149 }
150 part = Part.NEW_LINE;
151 } else if (c == '\n') {
152
153 part = Part.NEW_LINE;
154 } else {
155 lineBuffer.append(c);
156 }
157 previousChar = c;
158 }
159 }
160 return readFromBuffer(cbuf, off, len);
161 }
162
163 private void addToBuffer(StringBuilder line) {
164 replaceVariables(line);
165 buffer.append(line).append(lineSeparator);
166 lineBuffer.setLength(0);
167 }
168
169 private void replaceVariables(StringBuilder line) {
170 if (variableStatus == VariableStatus.FOUND_POSSIBLE_VARIABLE) {
171 String lineBufferStr = line.toString();
172 String processed = replacer.replace(lineBufferStr);
173 if (!lineBufferStr.equals(processed)) {
174 line.setLength(0);
175 line.append(processed);
176 }
177 }
178 variableStatus = VariableStatus.NOTHING;
179 }
180
181 private int readFromBuffer(char[] cbuf, int off, int len) {
182 int bufferLen = buffer.length();
183 int read = bufferLen > len ? len : bufferLen;
184 buffer.getChars(0, read, cbuf, off);
185 buffer.delete(0, read);
186 return read;
187 }
188
189 private void determinePart(char c) {
190 switch (part) {
191 case NEW_LINE:
192 if (inUndo) {
193 part = Part.NOT_UNDO_LINE;
194 } else if (c == 0x09 || c == 0x20) {
195
196 } else if (c == '/' || c == '-') {
197 part = Part.COMMENT_PREFIX;
198 } else {
199 part = Part.NOT_UNDO_LINE;
200 }
201 break;
202 case COMMENT_PREFIX:
203 if ((c == '/' || c == '-') && c == previousChar) {
204 part = Part.AFTER_COMMENT_PREFIX;
205 afterCommentPrefixIndex = lineBuffer.length() + 1;
206 } else {
207 part = Part.NOT_UNDO_LINE;
208 }
209 break;
210 case AFTER_COMMENT_PREFIX:
211 if (c == 0x09 || c == 0x20) {
212
213 } else if (c == '/') {
214 part = Part.DOUBLE_SLASH;
215 } else {
216 part = Part.NOT_UNDO_LINE;
217 }
218 break;
219 case DOUBLE_SLASH:
220 if (c == '/' && c == previousChar) {
221 part = Part.AFTER_DOUBLE_SLASH;
222 afterDoubleSlashIndex = lineBuffer.length() + 1;
223 undoIndex = 0;
224 } else {
225 part = Part.NOT_UNDO_LINE;
226 }
227 break;
228 case AFTER_DOUBLE_SLASH:
229 if (c == 0x09 || c == 0x20) {
230
231 } else if (c == UNDO_TAG.charAt(undoIndex)) {
232 part = Part.UNDO_TAG;
233 undoIndex = 1;
234 } else {
235 part = Part.NOT_UNDO_LINE;
236 }
237 break;
238 case UNDO_TAG:
239 if (c != UNDO_TAG.charAt(undoIndex)) {
240 part = Part.NOT_UNDO_LINE;
241 } else if (++undoIndex >= UNDO_TAG.length()) {
242 part = Part.AFTER_UNDO_TAG;
243 }
244 break;
245 default:
246 break;
247 }
248 }
249
250 private void searchVariable(char c) {
251
252 switch (variableStatus) {
253 case NOTHING:
254 if ((part == Part.NOT_UNDO_LINE || part == Part.AFTER_UNDO_TAG) && c == '$') {
255 variableStatus = VariableStatus.FOUND_DOLLAR;
256 }
257 break;
258 case FOUND_DOLLAR:
259 variableStatus = c == '{' ? VariableStatus.FOUND_OPEN_BRACE : VariableStatus.NOTHING;
260 break;
261 case FOUND_OPEN_BRACE:
262 if (c == '}') {
263 variableStatus = VariableStatus.FOUND_POSSIBLE_VARIABLE;
264 }
265 break;
266 default:
267 break;
268 }
269 }
270
271 @Override
272 public int read() throws IOException {
273 char[] buf = new char[1];
274 int result = read(buf, 0, 1);
275 return result == -1 ? -1 : (int) buf[0];
276 }
277
278 protected static Reader scriptFileReader(InputStream inputStream, String charset) {
279 if (charset == null || charset.length() == 0) {
280 return new InputStreamReader(inputStream);
281 }
282 return new InputStreamReader(inputStream, Charset.forName(charset));
283 }
284 }