MigrationReader.java
- /*
- * Copyright 2010-2023 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * https://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
- package org.apache.ibatis.migration;
- import java.io.File;
- import java.io.FileInputStream;
- import java.io.FilterReader;
- import java.io.IOException;
- import java.io.InputStream;
- import java.io.InputStreamReader;
- import java.io.Reader;
- import java.nio.charset.Charset;
- import java.util.Properties;
- public class MigrationReader extends FilterReader {
- // Note: Cannot use lineSeparator directly at this time due to tests manipulating it
- private final String lineSeparator = System.getProperty("line.separator", "\n");
- private static final String UNDO_TAG = "@UNDO";
- private boolean undo;
- private Properties variables;
- private Part part = Part.NEW_LINE;
- private VariableStatus variableStatus = VariableStatus.NOTHING;
- private char previousChar;
- private int undoIndex;
- private int afterCommentPrefixIndex;
- private int afterDoubleSlashIndex;
- private boolean inUndo;
- private final StringBuilder buffer = new StringBuilder();
- private final StringBuilder lineBuffer = new StringBuilder();
- private final VariableReplacer replacer;
- private enum Part {
- NOT_UNDO_LINE,
- NEW_LINE,
- COMMENT_PREFIX,
- AFTER_COMMENT_PREFIX,
- DOUBLE_SLASH,
- AFTER_DOUBLE_SLASH,
- UNDO_TAG,
- AFTER_UNDO_TAG
- }
- private enum VariableStatus {
- NOTHING,
- FOUND_DOLLAR,
- FOUND_OPEN_BRACE,
- FOUND_POSSIBLE_VARIABLE
- }
- public MigrationReader(File file, String charset, boolean undo, Properties variables) throws IOException {
- this(new FileInputStream(file), charset, undo, variables);
- }
- public MigrationReader(InputStream inputStream, String charset, boolean undo, Properties variables) {
- super(scriptFileReader(inputStream, charset));
- this.undo = undo;
- this.variables = variables;
- replacer = new VariableReplacer(this.variables);
- }
- @Override
- public int read(char[] cbuf, int off, int len) throws IOException {
- if (!undo && inUndo) {
- if (buffer.length() > 0) {
- return readFromBuffer(cbuf, off, len);
- }
- return -1;
- }
- while (buffer.length() == 0) {
- int result = in.read(cbuf, off, len);
- if (result == -1) {
- if (lineBuffer.length() > 0 && (!undo || inUndo)) {
- addToBuffer(lineBuffer);
- }
- if (buffer.length() > 0) {
- break;
- }
- return -1;
- }
- for (int i = off; i < off + result; i++) {
- char c = cbuf[i];
- determinePart(c);
- searchVariable(c);
- if (c == '\r' || c == '\n' && previousChar != '\r') {
- switch (part) {
- case AFTER_UNDO_TAG:
- if (!undo) {
- // Won't read from the file anymore.
- lineBuffer.setLength(0);
- int bufferLen = buffer.length();
- if (bufferLen == 0) {
- return -1;
- }
- return readFromBuffer(cbuf, off, len);
- }
- addToBuffer(lineBuffer.delete(afterCommentPrefixIndex, afterDoubleSlashIndex)
- .insert(afterCommentPrefixIndex, ' '));
- inUndo = true;
- break;
- case NOT_UNDO_LINE:
- if (!undo || inUndo) {
- addToBuffer(lineBuffer);
- } else {
- lineBuffer.setLength(0);
- }
- break;
- default:
- break;
- }
- part = Part.NEW_LINE;
- } else if (c == '\n') {
- // LF after CR
- part = Part.NEW_LINE;
- } else {
- lineBuffer.append(c);
- }
- previousChar = c;
- }
- }
- return readFromBuffer(cbuf, off, len);
- }
- private void addToBuffer(StringBuilder line) {
- replaceVariables(line);
- buffer.append(line).append(lineSeparator);
- lineBuffer.setLength(0);
- }
- private void replaceVariables(StringBuilder line) {
- if (variableStatus == VariableStatus.FOUND_POSSIBLE_VARIABLE) {
- String lineBufferStr = line.toString();
- String processed = replacer.replace(lineBufferStr);
- if (!lineBufferStr.equals(processed)) {
- line.setLength(0);
- line.append(processed);
- }
- }
- variableStatus = VariableStatus.NOTHING;
- }
- private int readFromBuffer(char[] cbuf, int off, int len) {
- int bufferLen = buffer.length();
- int read = bufferLen > len ? len : bufferLen;
- buffer.getChars(0, read, cbuf, off);
- buffer.delete(0, read);
- return read;
- }
- private void determinePart(char c) {
- switch (part) {
- case NEW_LINE:
- if (inUndo) {
- part = Part.NOT_UNDO_LINE;
- } else if (c == 0x09 || c == 0x20) {
- // ignore whitespace
- } else if (c == '/' || c == '-') {
- part = Part.COMMENT_PREFIX;
- } else {
- part = Part.NOT_UNDO_LINE;
- }
- break;
- case COMMENT_PREFIX:
- if ((c == '/' || c == '-') && c == previousChar) {
- part = Part.AFTER_COMMENT_PREFIX;
- afterCommentPrefixIndex = lineBuffer.length() + 1;
- } else {
- part = Part.NOT_UNDO_LINE;
- }
- break;
- case AFTER_COMMENT_PREFIX:
- if (c == 0x09 || c == 0x20) {
- // ignore whitespace
- } else if (c == '/') {
- part = Part.DOUBLE_SLASH;
- } else {
- part = Part.NOT_UNDO_LINE;
- }
- break;
- case DOUBLE_SLASH:
- if (c == '/' && c == previousChar) {
- part = Part.AFTER_DOUBLE_SLASH;
- afterDoubleSlashIndex = lineBuffer.length() + 1;
- undoIndex = 0;
- } else {
- part = Part.NOT_UNDO_LINE;
- }
- break;
- case AFTER_DOUBLE_SLASH:
- if (c == 0x09 || c == 0x20) {
- // ignore whitespace
- } else if (c == UNDO_TAG.charAt(undoIndex)) {
- part = Part.UNDO_TAG;
- undoIndex = 1;
- } else {
- part = Part.NOT_UNDO_LINE;
- }
- break;
- case UNDO_TAG:
- if (c != UNDO_TAG.charAt(undoIndex)) {
- part = Part.NOT_UNDO_LINE;
- } else if (++undoIndex >= UNDO_TAG.length()) {
- part = Part.AFTER_UNDO_TAG;
- }
- break;
- default:
- break;
- }
- }
- private void searchVariable(char c) {
- // This is just a quick check.
- switch (variableStatus) {
- case NOTHING:
- if ((part == Part.NOT_UNDO_LINE || part == Part.AFTER_UNDO_TAG) && c == '$') {
- variableStatus = VariableStatus.FOUND_DOLLAR;
- }
- break;
- case FOUND_DOLLAR:
- variableStatus = c == '{' ? VariableStatus.FOUND_OPEN_BRACE : VariableStatus.NOTHING;
- break;
- case FOUND_OPEN_BRACE:
- if (c == '}') {
- variableStatus = VariableStatus.FOUND_POSSIBLE_VARIABLE;
- }
- break;
- default:
- break;
- }
- }
- @Override
- public int read() throws IOException {
- char[] buf = new char[1];
- int result = read(buf, 0, 1);
- return result == -1 ? -1 : (int) buf[0];
- }
- protected static Reader scriptFileReader(InputStream inputStream, String charset) {
- if (charset == null || charset.length() == 0) {
- return new InputStreamReader(inputStream);
- }
- return new InputStreamReader(inputStream, Charset.forName(charset));
- }
- }