package com.lizard.fastdb.jdbc;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import com.lizard.fastdb.DBException;
/**
* SQL命名动态参数类,用来表示解析之后的SQL信息<br>
* 命名参数<:参数名> - <:id>,SELECT * FROM table WHERE id =:id
*
* @author SHEN.GANG
*/
public class NamedSQL
{
/**
* 命名参数被占位符 ? 替换后的标准预编译SQL语句
*/
private String sql;
/**
* 命名参数名称
*/
private List<String> namedParameter = new LinkedList<String>();
public NamedSQL() { }
public void addParameter( String param_name )
{
this.namedParameter.add(param_name);
}
/**
* @return the namedParameter
*/
public List<String> getNamedParameter()
{
return namedParameter;
}
/**
* @return the sql
*/
public String getSQL()
{
return sql;
}
/**
* @param sql the sql to set
*/
public void setSQL(String sql)
{
this.sql = sql;
}
/**
* 解析命名参数值Map对象为预编译SQL语句使用的Object[]对象
*
* @param valueMap 命名参数值Map
* @return 相对应顺序的参数值
*/
public Object[] parseParameterValue(Map<String, Object> paramValues)
{
if( null == namedParameter || namedParameter.size() == 0
|| null == paramValues || paramValues.isEmpty() )
{
return new Object[]{};
}
Object[] paramVals = new Object[namedParameter.size()];
for( int i = 0, len = namedParameter.size(); i < len; i++ )
{
paramVals[i] = paramValues.get(namedParameter.get(i));
}
return paramVals;
}
/**
* NamedSQL缓存,用于避免相同named sql的重复解析,获取更高效率
*/
private static final Map<String, NamedSQL> NAMED_SQL_CACHE= new HashMap<String, NamedSQL>();
private static final String NAMED_SEPARATORS = " \n\r\f\t,()=<>&|+-=/*'^![]#~\\";
/**
* 解析含有命名动态参数的SQL语句<br>
* Example: SELECT * FROM table WHERE id >:id AND time BETWEEN :stime AND :etime
*
* @param namedSql 含有命名动态参数的SQL语句
* @return NamedSQL
*/
public static NamedSQL parse(String namedSql)
{
synchronized(NAMED_SQL_CACHE)
{
NamedSQL namedSQL = NAMED_SQL_CACHE.get(namedSql.toLowerCase());
if( null == namedSQL )
{
namedSQL = parseNamedSQL(namedSql);
NAMED_SQL_CACHE.put(namedSql.toLowerCase(), namedSQL);
}
return namedSQL;
}
}
/**
* 解析含有命名动态参数的SQL语句<br>
* Example: SELECT * FROM table WHERE id >:id AND time BETWEEN :stime AND :etime
*
* @param namedSql 含有命名动态参数的SQL语句
* @return NamedSQL
*/
protected static NamedSQL parseNamedSQL(String namedSql)
{
NamedSQL namedSQL = new NamedSQL();
int nsql_len = namedSql.length();
// 标志一个<:> 是否出现在''引用中,如果出现在引用中,则忽略
boolean inQuote = false;
String param = null;
StringBuilder sqlFrag = new StringBuilder();
int cut_start = 0;
for (int indx = 0; indx < nsql_len; indx++)
{
char c = namedSql.charAt(indx);
// 进入''引用中遍历
if (inQuote)
{
// 本次''引用结束
if ('\'' == c)
{
inQuote = false;
}
}
else if ('\'' == c)
{
inQuote = true;
}
else if (c == ':')
{
// 处理SQL中特殊的 := 赋值操作
if( namedSql.charAt(indx+1) == '=' )
{
continue;
}
int right = firstIndexOfChar(namedSql, NAMED_SEPARATORS, indx + 1);
int chopLocation = right < 0 ? nsql_len : right;
param = namedSql.substring(indx + 1, chopLocation);
if (param == null || param.trim().length() == 0)
{
throw new DBException("Space is not allowed after parameter prefix ':' [" + namedSql + "]");
}
namedSQL.addParameter(param);
sqlFrag.append(namedSql.substring(cut_start, indx));
sqlFrag.append("?");
cut_start = indx + param.length() + 1;
indx = chopLocation - 1;
}
}
sqlFrag.append(namedSql.substring(cut_start, nsql_len));
namedSQL.setSQL(sqlFrag.toString());
sqlFrag = null;
return namedSQL;
}
private static int firstIndexOfChar(String sqlString, String string, int startindex)
{
int matchAt = -1;
for (int i = 0, len = string.length(); i < len; i++)
{
int curMatch = sqlString.indexOf(string.charAt(i), startindex);
if (curMatch >= 0)
{
if (matchAt == -1)
{
matchAt = curMatch;
}
else
{
matchAt = Math.min(matchAt, curMatch);
}
}
}
return matchAt;
}
}