/*
* Copyright 1999-2015 dangdang.com.
* <p>
* 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
*
* http://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.
* </p>
*/
package com.dangdang.ddframe.rdb.sharding.parser.result.router;
import com.google.common.base.Joiner;
import lombok.AccessLevel;
import lombok.Getter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
/**
* SQL构建器.
*
* @author gaohongtao
*/
public class SQLBuilder implements Appendable {
private final List<SQLBuilder> derivedSQLBuilders = new ArrayList<>();
private final List<Object> segments;
private final Map<String, StringToken> tokenMap;
private final List<StringToken> newTokenList = new LinkedList<>();
private StringBuilder currentSegment;
@Getter
private boolean changed;
@Getter(AccessLevel.PRIVATE)
private boolean removeDerivedSQLToken;
private boolean hasExistedDerivedSQLToken;
public SQLBuilder() {
segments = new LinkedList<>();
tokenMap = new HashMap<>();
currentSegment = new StringBuilder();
segments.add(currentSegment);
}
private SQLBuilder(final SQLBuilder originBuilder) {
segments = new LinkedList<>(originBuilder.segments);
tokenMap = new HashMap<>(originBuilder.tokenMap);
changeState();
}
/**
* 增加占位符.
*
* @param token 占位符
*/
public void appendToken(final String token) {
appendToken(token, token);
}
/**
* 增加占位符.
*
* @param label 占位符标签
* @param token 占位符
*/
public void appendToken(final String label, final String token) {
StringToken stringToken;
if (tokenMap.containsKey(label)) {
stringToken = tokenMap.get(label);
} else {
stringToken = new StringToken();
stringToken.label = label;
stringToken.value = token;
tokenMap.put(label, stringToken);
}
stringToken.indices.add(segments.size());
segments.add(stringToken);
currentSegment = new StringBuilder();
segments.add(currentSegment);
}
/**
* 用实际的值替代占位符.
*
* @param label 占位符
* @param token 实际的值
*/
public void buildSQL(final String label, final String token) {
buildSQL(label, token, false);
}
/**
* 用实际的值替代占位符,并可以标记该SQL是否为派生SQL.
*
* @param label 占位符
* @param token 实际的值
* @param isDerived 是否是派生的SQL
*/
public void buildSQL(final String label, final String token, final boolean isDerived) {
if (!tokenMap.containsKey(label)) {
return;
}
if (isDerived) {
hasExistedDerivedSQLToken = true;
}
StringToken labelSQL = tokenMap.get(label);
labelSQL.isDerived = isDerived;
labelSQL.value = token;
changeState();
}
/**
* 记录新的Token.
*
* @param label 占位符
* @param token 实际的值
*/
public void recordNewToken(final String label, final String token) {
StringToken newToken = new StringToken();
newToken.label = label;
newToken.value = token;
newTokenList.add(newToken);
}
/**
* 用实际的值替代占位符,并返回新的构建器.
*
* @return 新SQL构建器
*/
public SQLBuilder buildSQLWithNewToken() {
if (!newTokenList.isEmpty()) {
changeState();
}
SQLBuilder result = new SQLBuilder(this);
for (StringToken each : newTokenList) {
StringToken origin = result.tokenMap.get(each.label);
each.indices.addAll(origin.indices);
result.tokenMap.put(each.label, each);
for (Integer index : origin.indices) {
result.segments.set(index, each);
}
}
derivedSQLBuilders.add(result);
newTokenList.clear();
return result;
}
/**
* 生成SQL语句.
*
* @return SQL语句
*/
public String toSQL() {
clearState();
StringBuilder result = new StringBuilder();
for (Object each : segments) {
result.append(each.toString());
}
return result.toString();
}
@Override
public Appendable append(final CharSequence sql) throws IOException {
currentSegment.append(sql);
changeState();
return this;
}
@Override
public Appendable append(final CharSequence sql, final int start, final int end) throws IOException {
throw new UnsupportedOperationException();
}
@Override
public Appendable append(final char c) throws IOException {
currentSegment.append(c);
changeState();
return this;
}
private void changeState() {
changed = true;
for (SQLBuilder each : derivedSQLBuilders) {
each.changeState();
}
}
private void clearState() {
changed = false;
}
/**
* 移除衍生的SQL片段.
*/
public void removeDerivedSQL() {
if (hasExistedDerivedSQLToken) {
removeDerivedSQLToken = true;
changeState();
}
}
@Override
public String toString() {
StringBuilder result = new StringBuilder();
for (Object each : segments) {
if (each instanceof StringToken) {
result.append(((StringToken) each).toToken());
} else {
result.append(each.toString());
}
}
return result.toString();
}
private class StringToken {
private String label;
private String value;
private boolean isDerived;
private final List<Integer> indices = new LinkedList<>();
String toToken() {
if (isEmptyValueOutput()) {
return "";
}
Joiner joiner = Joiner.on("");
return label.equals(value) ? joiner.join("[Token(", value, ")]") : joiner.join("[", label, "(", value, ")]");
}
private boolean isEmptyValueOutput() {
return null == value || isDerived && isRemoveDerivedSQLToken();
}
@Override
public String toString() {
return isEmptyValueOutput() ? "" : value;
}
}
}