001 /**
002 * Copyright 2010-2013 The Kuali Foundation
003 *
004 * Licensed under the Educational Community License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 * http://www.opensource.org/licenses/ecl2.php
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 */
016 package org.kuali.common.jdbc.reader;
017
018 import java.io.BufferedReader;
019 import java.io.IOException;
020 import java.util.List;
021
022 import org.apache.commons.lang3.StringUtils;
023 import org.kuali.common.jdbc.reader.model.Comments;
024 import org.kuali.common.jdbc.reader.model.Delimiter;
025 import org.kuali.common.jdbc.reader.model.DelimiterMode;
026 import org.kuali.common.jdbc.reader.model.LineSeparator;
027 import org.kuali.common.jdbc.sql.model.SqlMetaData;
028 import org.kuali.common.util.Assert;
029
030 public final class DefaultSqlReader implements SqlReader {
031
032 public DefaultSqlReader() {
033 this(Delimiter.DEFAULT_DELIMITER, LineSeparator.DEFAULT_VALUE, DEFAULT_TRIM, Comments.DEFAULT_COMMENTS);
034 }
035
036 public DefaultSqlReader(String delimiter) {
037 this(new Delimiter(delimiter), LineSeparator.DEFAULT_VALUE, DEFAULT_TRIM, Comments.DEFAULT_COMMENTS);
038 }
039
040 public DefaultSqlReader(DelimiterMode delimiterMode) {
041 this(new Delimiter(delimiterMode), LineSeparator.DEFAULT_VALUE, DEFAULT_TRIM, Comments.DEFAULT_COMMENTS);
042 }
043
044 public DefaultSqlReader(String delimiter, DelimiterMode delimiterMode) {
045 this(new Delimiter(delimiter, delimiterMode), LineSeparator.DEFAULT_VALUE, DEFAULT_TRIM, Comments.DEFAULT_COMMENTS);
046 }
047
048 public DefaultSqlReader(Delimiter delimiter, LineSeparator lineSeparator, boolean trim, Comments comments) {
049 Assert.noNulls(delimiter, lineSeparator, comments);
050 this.delimiter = delimiter;
051 this.lineSeparator = lineSeparator;
052 this.trim = trim;
053 this.comments = comments;
054 this.lineSeparatorLength = this.lineSeparator.getValue().length();
055 this.delimiterLength = delimiter.getValue().length();
056 }
057
058 public static final boolean DEFAULT_TRIM = true;
059
060 private final Delimiter delimiter;
061 private final LineSeparator lineSeparator;
062 private final boolean trim;
063 private final Comments comments;
064 private final int lineSeparatorLength;
065 private final int delimiterLength;
066
067 /**
068 * Extract one complete SQL statement from the BufferedReader. Return <code>null</code> after all SQL statements have been read.
069 */
070 @Override
071 public String getSql(BufferedReader reader) throws IOException {
072 // Extract one line of text from the file
073 String line = reader.readLine();
074
075 // Trim all whitespace
076 String trimmedLine = StringUtils.trimToNull(line);
077
078 // Begin a new SQL statement
079 StringBuilder sb = new StringBuilder();
080
081 // Iterate until we have exhausted the BufferedReader
082 while (line != null) {
083
084 // Examine the trimmed line to determine if we have hit the end of a SQL statement
085 // The only methods used to determine the end of a SQL statement are
086 // 1 - the delimiter being on a line all on it's own with nothing else but whitespace
087 // 2 - the delimiter being at the end of a line after whitespace is trimmed off
088 if (isEndOfSqlStatement(trimmedLine, delimiter)) {
089 // We hit the end of a SQL statement, return what we've got so far
090 return getReturnValue(sb.toString() + trimmedLine, trim, lineSeparator);
091 }
092
093 // If this is a comment (and we are ignoring comments) skip this line
094 if (!ignore(comments, sb, trimmedLine)) {
095 // Otherwise append the line and add back in the line separator that was removed by readLine()
096 sb.append(line + lineSeparator.getValue());
097 }
098
099 // Read another line of text from the file
100 line = reader.readLine();
101
102 // Trim all whitespace
103 trimmedLine = StringUtils.trimToNull(line);
104 }
105
106 // There might be SQL at the end of the file
107 // The trailing SQL might not be terminated by the delimiter
108 String result = getReturnValue(sb.toString(), trim, lineSeparator);
109
110 if (result == null) {
111 // If there is no SQL at the end, return null
112 return null;
113 } else {
114 // Otherwise return the final SQL statement
115 return result;
116 }
117 }
118
119 /**
120 * Calculate total number of SQL statements + aggregate size
121 */
122 @Override
123 public SqlMetaData getMetaData(BufferedReader reader) throws IOException {
124 long count = 0; // Track number of individual SQL statements
125 long size = 0; // Track overall size of the combined SQL statements
126
127 // Read a line of text from the file
128 String line = reader.readLine();
129
130 // Trim all whitespace
131 String trimmedLine = StringUtils.trimToNull(line);
132
133 // Iterate until we have exhausted the BufferedReader
134 while (line != null) {
135
136 // Add the length of the current line to the overall size total
137 size += line.length();
138
139 // If this line terminates the SQL statement, increment the overall count
140 if (isEndOfSqlStatement(trimmedLine, delimiter)) {
141 count++;
142 }
143
144 // Read the next line from the BufferedReader
145 line = reader.readLine();
146
147 // Trim all whitespace
148 trimmedLine = StringUtils.trimToNull(line);
149 }
150
151 // Return total count and overall size
152 return new SqlMetaData(count, size);
153 }
154
155 protected String getReturnValue(String sql, boolean trim, LineSeparator lineSeparator) {
156
157 // If the SQL ends with the delimiter, remove it
158 if (StringUtils.endsWith(sql, delimiter.getValue())) {
159 int endIndex = sql.length() - delimiterLength;
160 sql = StringUtils.substring(sql, 0, endIndex);
161 }
162
163 // Trim all whitespace on either side of the SQL statement
164 if (trim) {
165 sql = StringUtils.trimToNull(sql);
166 }
167
168 if (sql == null) {
169 // If the SQL is nothing but whitespace, return null
170 return null;
171 } else if (StringUtils.endsWith(sql, lineSeparator.getValue())) {
172 // If the SQL ends with the line separator, remove it
173 int endIndex = sql.length() - lineSeparatorLength;
174 return StringUtils.substring(sql, 0, endIndex);
175 } else {
176 // Otherwise return the SQL as is
177 return sql;
178 }
179 }
180
181 protected boolean isEndOfSqlStatement(String trimmedLine, Delimiter delimiter) {
182 switch (delimiter.getMode()) {
183 case END_OF_LINE:
184 return StringUtils.endsWith(trimmedLine, delimiter.getValue());
185 case OWN_LINE:
186 return StringUtils.equals(trimmedLine, delimiter.getValue());
187 default:
188 throw new IllegalArgumentException("Delimiter mode [" + delimiter.getMode() + "] is unknown");
189 }
190 }
191
192 protected boolean proceed(String line, String trimmedLine, Delimiter delimiter) {
193 if (line == null) {
194 return false;
195 }
196 boolean endOfSqlStatement = isEndOfSqlStatement(trimmedLine, delimiter);
197 return !endOfSqlStatement;
198 }
199
200 protected boolean ignore(Comments comments, StringBuilder sql, String trimmedLine) {
201 if (!comments.isIgnore()) {
202 return false;
203 }
204 if (!StringUtils.isBlank(sql.toString())) {
205 return false;
206 }
207 return isSqlComment(trimmedLine, comments.getTokens());
208 }
209
210 protected boolean isSqlComment(String trimmedLine, List<String> commentTokens) {
211 for (String commentToken : commentTokens) {
212 if (StringUtils.startsWith(trimmedLine, commentToken)) {
213 return true;
214 }
215 }
216 return false;
217 }
218
219 public Delimiter getDelimiter() {
220 return delimiter;
221 }
222
223 public LineSeparator getLineSeparator() {
224 return lineSeparator;
225 }
226
227 public boolean isTrim() {
228 return trim;
229 }
230
231 public Comments getComments() {
232 return comments;
233 }
234
235 }