package org.nutz.dao.impl.sql;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.nutz.dao.Condition;
import org.nutz.dao.entity.Entity;
import org.nutz.dao.entity.Record;
import org.nutz.dao.impl.sql.pojo.AbstractPItem;
import org.nutz.dao.impl.sql.pojo.StaticPItem;
import org.nutz.dao.jdbc.ValueAdaptor;
import org.nutz.dao.pager.Pager;
import org.nutz.dao.sql.DaoStatement;
import org.nutz.dao.sql.PItem;
import org.nutz.dao.sql.Sql;
import org.nutz.dao.sql.SqlCallback;
import org.nutz.dao.sql.VarIndex;
import org.nutz.dao.sql.VarSet;
import org.nutz.dao.util.Pojos;
import org.nutz.lang.Each;
import org.nutz.lang.Lang;
import org.nutz.lang.Strings;
public class NutSql extends NutStatement implements Sql {
private static final long serialVersionUID = 1L;
protected String sourceSql;
protected VarSet vars;
protected List<VarSet> rows;
protected VarSet params;
protected SqlCallback callback;
protected VarIndex varIndex;
protected VarIndex paramIndex;
protected Map<String, ValueAdaptor> customValueAdaptor;
protected List<PItem> items;
public NutSql(String source) {
this(source, null);
}
public NutSql(String source, SqlCallback callback) {
this.setSourceSql(source);
this.callback = callback;
this.vars = new SimpleVarSet();
this.rows = new ArrayList<VarSet>();
this.params = new SimpleVarSet();
this.rows.add(params);
customValueAdaptor = new HashMap<String, ValueAdaptor>();
}
public void setSourceSql(String sql) {
this.sourceSql = sql;
SqlLiteral literal = literal();
this.varIndex = literal.getVarIndexes();
this.paramIndex = literal.getParamIndexes();
if (getSqlType() == null)
setSqlType(literal.getType());
String[] ss = literal.stack.cloneChain();
PItem[] tmp = new PItem[ss.length];
for (String var : varIndex.getOrders()) {
int[] is = varIndex.indexesOf(var);
if (is != null) {
for (int i : is) {
tmp[i] = new SqlVarPItem(var);
}
}
}
for (String param : paramIndex.getOrders()) {
int[] is = paramIndex.indexesOf(param);
if (is != null) {
for (int i : is) {
tmp[i] = new SqlParamPItem(param);
}
}
}
for (int i = 0; i < tmp.length; i++) {
if (tmp[i] == null) {
tmp[i] = new StaticPItem(ss[i], true);
}
}
this.items = Arrays.asList(tmp);
}
protected int _params_count() {
int count = 0;
Entity<?> en = getEntity();
for (PItem item : items) {
count += item.paramCount(en);
}
return count;
}
public ValueAdaptor[] getAdaptors() {
ValueAdaptor[] adaptors = new ValueAdaptor[_params_count()];
int i = 0;
for (PItem item : items)
i = item.joinAdaptor(getEntity(), adaptors, i);
return adaptors;
}
public Object[][] getParamMatrix() {
int pc = _params_count();
int row_count = rows.size();
if (rows.size() > 1 && params.size() == 0 && rows.get(0).size() != 0) {
row_count--;
}
Object[][] re = new Object[row_count][pc];
for (int z = 0; z < row_count; z++) {
VarSet row = rows.get(z);
int i = 0;
for (PItem item : items)
i = item.joinParams(getEntity(), row, re[z], i);
}
return re;
}
public String toPreparedStatement() {
StringBuilder sb = new StringBuilder();
for (PItem item : items)
item.joinSql(getEntity(), sb);
return sb.toString();
}
public void onBefore(Connection conn) throws SQLException {}
public void onAfter(Connection conn, ResultSet rs, Statement stmt) throws SQLException {
if (callback != null)
getContext().setResult(callback.invoke(conn, rs, this));
}
public DaoStatement setPager(Pager pager) {
getContext().setPager(pager);
return this;
}
public VarSet vars() {
return vars;
}
public VarSet params() {
return params;
}
public void setValueAdaptor(String name, ValueAdaptor adaptor) {
this.customValueAdaptor.put(name, adaptor);
}
public VarIndex varIndex() {
return varIndex;
}
public VarIndex paramIndex() {
return paramIndex;
}
public void addBatch() {
params = new SimpleVarSet();
rows.add(params);
}
public void clearBatch() {
params = new SimpleVarSet();
rows.clear();
rows.add(params);
}
public Sql setEntity(Entity<?> entity) {
super.setEntity(entity);
return this;
}
public Sql setCallback(SqlCallback callback) {
this.callback = callback;
return this;
}
public Sql setCondition(Condition cnd) {
vars.set("condition", cnd);
return this;
}
public Sql duplicate() {
return new NutSql(sourceSql, callback);
}
public String getSourceSql() {
return sourceSql;
}
class SqlVarPItem extends AbstractPItem {
/**
*
*/
private static final long serialVersionUID = 2655530650031939556L;
public String name;
public SqlVarPItem(String name) {
this.name = name;
}
public void joinSql(Entity<?> en, StringBuilder sb) {
Object val = vars.get(name);
if (val != null) {
if (val instanceof PItem) {
((PItem) val).joinSql(en, sb);
}
else if (val instanceof Condition) {
sb.append(' ').append(Pojos.formatCondition(en, (Condition) val));
} else {
sb.append(val);
}
}
}
public int joinAdaptor(Entity<?> en, ValueAdaptor[] adaptors, int off) {
Object val = vars.get(name);
if (val != null) {
if (val instanceof PItem) {
return ((PItem) val).joinAdaptor(en, adaptors, off);
}
}
return off;
}
public int paramCount(Entity<?> en) {
Object val = vars.get(name);
if (val != null) {
if (val instanceof PItem) {
return ((PItem) val).paramCount(en);
}
}
return 0;
}
public int joinParams(Entity<?> en, Object obj, Object[] params, int off) {
Object val = vars.get(name);
if (val != null) {
if (val instanceof PItem) {
return ((PItem) val).joinParams(en, obj, params, off);
}
}
return off;
}
}
class SqlParamPItem extends AbstractPItem {
/**
*
*/
private static final long serialVersionUID = 1494513192752663060L;
public String name;
public SqlParamPItem(String name) {
this.name = name;
}
public void joinSql(Entity<?> en, StringBuilder sb) {
Object val = rows.get(0).get(name);
if (val == null) {
sb.append("?");
} else if (val instanceof PItem) {
((PItem) val).joinSql(en, sb);
} else if (val.getClass().isArray()) {
sb.append(Strings.dup("?,", Lang.length(val)));
sb.setLength(sb.length() - 1);
} else if (val instanceof Condition) {
sb.append(' ').append(Pojos.formatCondition(en, (Condition) val));
} else {
sb.append("?");
}
}
public int joinAdaptor(final Entity<?> en, final ValueAdaptor[] adaptors, final int off) {
if (!customValueAdaptor.isEmpty()) {
ValueAdaptor custom = customValueAdaptor.get(name);
if (custom != null) {
adaptors[off] = custom;
return off + 1;
}
}
Object val = rows.get(0).get(name);
if (val == null && rows.size() > 1) {
for (VarSet vs : rows) {
val = vs.get(name);
if (val != null)
break;
}
}
if (val == null) {
adaptors[off] = getAdapterBy(null);
return off + 1;
} else if (val instanceof PItem) {
return ((PItem) val).joinAdaptor(en, adaptors, off);
} else if (val.getClass().isArray()) {
int len = Lang.length(val);
Lang.each(val, new Each<Object>() {
public void invoke(int index, Object ele, int length) {
adaptors[off + index] = getAdapterBy(ele);
}
});
return off + len;
// } else if (val instanceof Condition) {
} else {
adaptors[off] = getAdapterBy(val);
return off + 1;
}
}
public int joinParams(Entity<?> en, Object obj, final Object[] params, final int off) {
VarSet row = (VarSet) obj;
Object val = row.get(name);
if (val == null) {
return off + 1;
} else if (val instanceof PItem) {
return ((PItem) val).joinParams(en, null, params, off);
} else if (val.getClass().isArray()) {
int len = Lang.length(val);
Lang.each(val, new Each<Object>() {
public void invoke(int index, Object ele, int length) {
params[off + index] = ele;
}
});
return off + len;
// } else if (val instanceof Condition) {
} else {
params[off] = val;
return off + 1;
}
}
public int paramCount(Entity<?> en) {
Object val = rows.get(0).get(name);
if (val == null) {
return 1;
} else if (val instanceof PItem) {
return ((PItem) val).paramCount(en);
} else if (val.getClass().isArray()) {
return Lang.length(val);
} else if (val instanceof Condition) {
return 0;
} else {
return 1;
}
}
}
/**
* 若需要定制参数字符和变量字符,覆盖本方法,通过SqlLiteral的构造方法指定之
*/
protected SqlLiteral literal() {
return new SqlLiteral().valueOf(sourceSql);
}
public Sql setParam(String name, Object value) {
params().set(name, value);
return this;
}
public Sql setVar(String name, Object value) {
vars().set(name, value);
return this;
}
public Record getOutParams() {
return getContext().attr(Record.class, "OUT");
}
}