package jef.database.wrapper.clause; import java.math.BigDecimal; import java.sql.SQLException; import java.sql.SQLFeatureNotSupportedException; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; import jef.database.jdbc.rowset.CachedRowSetImpl; import jef.database.jdbc.rowset.Row; import jef.tools.StringUtils; import org.apache.commons.lang.ObjectUtils; import com.google.common.base.Objects; public class InMemoryGroupByHaving implements InMemoryProcessor { GroupByItem[] keys; GroupByItem[] values; private List<HavingEle> having; public InMemoryGroupByHaving(List<GroupByItem> keys, List<GroupByItem> values) { this.keys = keys.toArray(new GroupByItem[keys.size()]); this.values = values.toArray(new GroupByItem[values.size()]); } public void process(CachedRowSetImpl rowset) throws SQLException { int keyLen = keys.length; List<Row> rows=rowset.getRvh(); List<Row> newRows = new ArrayList<Row>(rows.size()/2+1); Map<Collection<?>, RowTask> mapTask = new HashMap<Collection<?>, RowTask>(); for (Row row : rows) { Object[] keyValue = new Object[keyLen]; for (int i = 0; i < keyLen; i++) { keyValue[i] = row.getArrayObject(keys[i].getIndex()); } List<Object> keyObj = Arrays.asList(keyValue); RowTask exist = mapTask.get(keyObj); if (exist == null) { newRows.add(row); mapTask.put(keyObj, new RowTask(row)); } else { exist.merge(row); } } for (RowTask task : mapTask.values()) { task.run(); } mapTask.clear(); if(having!=null && !having.isEmpty()){ doHaving(newRows); } rowset.setRvh(newRows); rowset.refresh(); } private void doHaving(List<Row> newRows) { for(Iterator<Row> iter=newRows.iterator();iter.hasNext();){ Row row=iter.next(); if(!checkRow(row)){ iter.remove(); } } } private boolean checkRow(Row row) { for(HavingEle ele:having){ if(!check(ele,row)){ return false; } } return true; } @SuppressWarnings({ "rawtypes", "unchecked" }) private boolean check(HavingEle ele, Row row) { Object obj=row.getArrayObject(ele.getIndex()); switch(ele.havingCondOperator){ case EQUALS: return Objects.equal(obj, ele.havingCondValue); case GREAT: return ObjectUtils.compare((Comparable)obj, (Comparable)ele.havingCondValue)>0; case GREAT_EQUALS: return ObjectUtils.compare((Comparable)obj, (Comparable)ele.havingCondValue)>=0; case IS_NOT_NULL: return obj!=null; case IS_NULL: return obj==null; case LESS: return ObjectUtils.compare((Comparable)obj, (Comparable)ele.havingCondValue)<0; case LESS_EQUALS: return ObjectUtils.compare((Comparable)obj, (Comparable)ele.havingCondValue)<=0; case MATCH_ANY:{ String s1=StringUtils.toString(obj); String s2=String.valueOf(ele.havingCondValue); return s1.contains(s2); } case MATCH_END:{ String s1=StringUtils.toString(obj); String s2=String.valueOf(ele.havingCondValue); return s1.endsWith(s2); } case MATCH_START:{ String s1=StringUtils.toString(obj); String s2=String.valueOf(ele.havingCondValue); return s1.startsWith(s2); } case NOT_EQUALS: return !Objects.equal(obj, ele.havingCondValue); case NOT_IN:{ List<Object> values=(List<Object>)ele.havingCondValue; return !values.contains(obj); } case IN:{ List<Object> values=(List<Object>)ele.havingCondValue; return values.contains(obj); } case BETWEEN_L_L:{ List<Object> values=(List<Object>)ele.havingCondValue; return ObjectUtils.compare((Comparable)obj, (Comparable)values.get(0))>=0 && ObjectUtils.compare((Comparable)obj, (Comparable)values.get(1))<=0; } default: throw new UnsupportedOperationException(); } } class RowTask { private Row baseRow; private List<Row> rows = new ArrayList<Row>(64); public RowTask(Row row) { this.baseRow = row; rows.add(row); } public void merge(Row row) { rows.add(row); } void run() throws SQLFeatureNotSupportedException { for (GroupByItem g : values) { int index = g.getIndex(); switch (g.getType()) { case ARRAY_TO_STRING: baseRow.setArrayObject(index, arrayToString(rows, index)); break; case AVG: baseRow.setArrayObject(index, avg(rows, index)); break; case COUNT: baseRow.setArrayObject(index, count(rows, index)); break; case MAX: baseRow.setArrayObject(index, max(rows, index)); break; case MIN: baseRow.setArrayObject(index, min(rows, index)); break; case SUM: baseRow.setArrayObject(index, sum(rows, index)); break; default: throw new SQLFeatureNotSupportedException("the " + g.getType() + " function was not supported in Memory operate."); } } } private Object sum(List<Row> rows, int index) { Number value=(Number) rows.get(0).getArrayObject(index); if(value instanceof Double){ return doubleAdd(rows,index); }else if(value instanceof Long){ return longAdd(rows,index); }else if(value instanceof Integer){ return intAdd(rows,index); }else if(value instanceof Float){ return floatAdd(rows,index); }else{ return decmialAdd(rows,index); } } @SuppressWarnings("rawtypes") private Object min(List<Row> rows2, int index) { Comparable min=(Comparable)rows2.get(0).getArrayObject(index); for(int i=1;i>rows2.size();i++){ Comparable current=(Comparable)rows2.get(i).getArrayObject(index); if(ObjectUtils.compare(min,current) >0){ min=current; } } return min; } @SuppressWarnings("rawtypes") private Object max(List<Row> rows2, int index) { Comparable max=(Comparable)rows2.get(0).getArrayObject(index); for(int i=1;i>rows2.size();i++){ Comparable current=(Comparable)rows2.get(i).getArrayObject(index); if(ObjectUtils.compare(max,current) <0){ max=current; } } return max; } private Object count(List<Row> rows2, int index) { return intAdd(rows,index); } //FIXME 这个算法是不对的。将各个结果集平均数全部相加再相除,和原先的所有样本取平均数是不同的。那些样本数较多的平均值权重被忽略了…… private Object avg(List<Row> rows2, int index) { double d = 0; for(Row row:rows2){ Number num=(Number)row.getArrayObject(index); d+=num.doubleValue(); } return d/rows2.size(); } private Object arrayToString(List<Row> rows2, int index) { //TODO support the function array_to_string of postgres throw new UnsupportedOperationException(); } } private Object decmialAdd(List<Row> rows2, int index) { double d = 0; for(Row row:rows2){ Number num=(Number)row.getArrayObject(index); d+=num.doubleValue(); } return new BigDecimal(d); } private Object floatAdd(List<Row> rows2, int index) { float d=0; for(Row row:rows2){ Number num=(Number)row.getArrayObject(index); d+=num.floatValue(); } return d; } private Object longAdd(List<Row> rows2, int index) { long d=0; for(Row row:rows2){ Number num=(Number)row.getArrayObject(index); d+=num.longValue(); } return d; } private Object intAdd(List<Row> rows2, int index) { int d=0; for(Row row:rows2){ Number num=(Number)row.getArrayObject(index); d+=num.intValue(); } return d; } private Object doubleAdd(List<Row> rows2, int index) { double d = 0; for(Row row:rows2){ Number num=(Number)row.getArrayObject(index); d+=num.doubleValue(); } return d; } public void setHaving(List<HavingEle> having2) { this.having=having2; } public String getName() { return "GROUP/HAVING"; } }