All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.rx.bean.DataTable Maven / Gradle / Ivy

There is a newer version: 3.0.0
Show newest version
package org.rx.bean;

import com.alibaba.fastjson2.JSONObject;
import lombok.*;
import lombok.extern.slf4j.Slf4j;
import org.h2.expression.Alias;
import org.h2.expression.Expression;
import org.h2.expression.ExpressionColumn;
import org.h2.expression.aggregate.Aggregate;
import org.h2.expression.aggregate.AggregateType;
import org.h2.jdbc.JdbcConnection;
import org.h2.jdbc.JdbcResultSet;
import org.h2.result.LocalResult;
import org.h2.value.ValueToObjectConverter;
import org.rx.core.StringBuilder;
import org.rx.core.*;
import org.rx.exception.InvalidException;
import org.rx.third.guava.CaseFormat;

import java.lang.reflect.Type;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;

import static org.rx.core.Extends.*;
import static org.rx.core.Sys.fromJson;

@Slf4j
@SuppressWarnings(Constants.NON_RAW_TYPES)
@NoArgsConstructor
public class DataTable implements Extends {
    private static final long serialVersionUID = -7379386582995440975L;
    public static final String HS_COLUMN_TYPE = "HS_COLUMN_TYPE";
    public static final String HS_COUNT_MAP = "HS_COUNT_MAP";

    public static DataTable read(ResultSet resultSet) {
        return read(resultSet, false);
    }

    @SneakyThrows
    public static DataTable read(ResultSet resultSet, boolean preferColumnName) {
        DataTable dt = new DataTable();
        try (ResultSet rs = resultSet) {
            ResultSetMetaData metaData = rs.getMetaData();
            dt.setTableName(metaData.getTableName(1));
            int columnCount = metaData.getColumnCount();
            for (int i = 1; i <= columnCount; i++) {
                dt.addColumn(preferColumnName ? metaData.getColumnName(i) : metaData.getColumnLabel(i));
            }

            List buf = new ArrayList<>(columnCount);
            while (rs.next()) {
                buf.clear();
                for (int i = 1; i <= columnCount; i++) {
                    buf.add(rs.getObject(i));
                }
                dt.addRow(buf.toArray());
            }
        }
        return dt;
    }

    @SneakyThrows
    public static DataTable read(JdbcResultSet resultSet) {
        DataTable dt = new DataTable();
        try (JdbcResultSet rs = resultSet) {
            LocalResult result = (LocalResult) rs.getResult();
            //include orderby expr
            Expression[] exprs = Reflects.readField(result, "expressions");
            if (exprs.length > 0) {
                dt.setTableName(exprs[0].getTableName());
            }
            for (Expression expr : exprs) {
                addColumnName(dt, expr);
            }

            JdbcConnection conn = Reflects.readField(rs, "conn");
            int columnCount = exprs.length;
            List buf = new ArrayList<>(columnCount);
            while (rs.next()) {
                buf.clear();
                for (int i = 1; i <= columnCount; i++) {
                    buf.add(ValueToObjectConverter.valueToDefaultObject(rs.getInternal(i), conn, true));
                }
                dt.addRow(buf.toArray());
            }
        }
        return dt;
    }

    static void addColumnName(DataTable dt, Expression expr) {
        if (tryAs(expr, ExpressionColumn.class, p -> {
            String col = p.getOriginalColumnName();
            if (col == null) {
                col = p.getColumn().getName();
            }
            dt.addColumns(col);
        }) || tryAs(expr, Aggregate.class, p -> {
            if (p.getAggregateType() == AggregateType.COUNT_ALL
                    || p.getAggregateType() == AggregateType.COUNT) {
                String label = p.toString();
                dt.addColumn(label);
                //todo COUNT with no label
                return;
            }
            Expression subExpr = p.getSubexpression(0);
            addColumnName(dt, subExpr);
        }) || tryAs(expr, Alias.class, p -> {
            Expression subExpr = p.getNonAliasExpression();
            Aggregate aggregate = as(subExpr, Aggregate.class);
            if (aggregate != null
                    && (aggregate.getAggregateType() == AggregateType.COUNT_ALL
                    || aggregate.getAggregateType() == AggregateType.COUNT)) {
                String label = p.getAlias(null, 0);
                dt.addColumn(label).attr(HS_COUNT_MAP, Tuple.of(subExpr.toString(), label));
                return;
            }
            addColumnName(dt, subExpr);
        })) ;
    }

    final List columns = new ArrayList<>();
    final List rows = new ArrayList<>();
    @Getter
    @Setter
    String tableName;
    @Setter
    boolean checkCellsSize = true;
    @Setter
    Iterator fluentRows;
    List readOnlyCols;

    public List> getColumns() {
        if (readOnlyCols == null) {
            readOnlyCols = Collections.unmodifiableList(columns);
        }
        return (List) readOnlyCols;
    }

    public FluentIterable getRows() {
        return new FluentIterable() {
            Iterator cur = rows.iterator();
            final Iterator next = fluentRows;

            @Override
            public boolean hasNext() {
                if (!cur.hasNext()) {
                    if (cur == next || next == null) {
                        return false;
                    }
                    cur = next;
                    return hasNext();
                }
                return true;
            }

            @Override
            public DataRow next() {
                return cur.next();
            }
        };
    }

    public DataTable(String tableName) {
        this.tableName = tableName;
    }

    public  List toList(Type type) {
        return toList(type, false);
    }

    public  List toList(@NonNull Type type, boolean toLowerCamelColumn) {
        if (toLowerCamelColumn) {
            for (DataColumn column : columns) {
                column.setColumnName(CaseFormat.LOWER_UNDERSCORE.to(CaseFormat.LOWER_CAMEL, column.getColumnName()));
            }
        }
        List list = new ArrayList<>();
        int colSize = columns.size();
        Iterator rows = getRows();
        while (rows.hasNext()) {
            List cells = rows.next().items;
            JSONObject j = new JSONObject(colSize);
            for (int i = 0; i < colSize; i++) {
                j.put(columns.get(i).columnName, cells.get(i));
            }
            list.add(fromJson(j, type));
        }
        return list;
    }

    public DataRow addRow(Object... cells) {
        DataRow row = newRow(cells);
        rows.add(row);
        return row;
    }

    public DataRow addRow(DataRow row) {
        if (row.table != this) {
            row = newRow(row.getArray());
        }
        rows.add(row);
        return row;
    }

    public DataRow removeRow(DataRow row) {
        rows.remove(row);
        return row;
    }

    public DataRow newRow(Object... cells) {
        DataRow row = new DataRow(this);
        if (!Arrays.isEmpty(cells)) {
            row.setArray(cells);
        }
        return row;
    }

    public List> addColumns(String... columnNames) {
        List> columns = Linq.from(columnNames).select(this::addColumn).toList();
        return (List) columns;
    }

    public  DataColumn addColumn(String columnName) {
        DataColumn column = new DataColumn<>(this);
        column.ordinal = columns.size();
        column.columnName = columnName;
        columns.add(column);
        return column;
    }

    public  DataColumn removeColumn(String columnName) {
        int index = getColumn(columnName).ordinal;
        DataColumn column = columns.remove(index);
        for (DataRow row : rows) {
            row.items.remove(index);
        }
        return column;
    }

    public  DataColumn getColumn(int ordinal) {
        return columns.get(ordinal);
    }

    public  DataColumn getColumn(String columnName) {
        return Linq.from(columns).first(p -> Strings.hashEquals(p.columnName, columnName));
    }

     void setOrdinal(DataColumn column, int ordinal) {
        if (fluentRows != null) {
            throw new InvalidException("Not supported");
        }
        if (column.ordinal == ordinal) {
            return;
        }

        columns.remove(ordinal);
        columns.add(ordinal, column);
        for (DataRow row : rows) {
            row.items.add(ordinal, row.items.remove(ordinal));
        }
        column.ordinal = ordinal;
    }

     DataColumn setDataType(DataColumn column, Class dataType) {
        if (fluentRows != null) {
            throw new InvalidException("Not supported");
        }
        if (Reflects.isAssignable(column.dataType, dataType)) {
            return (DataColumn) column;
        }

        for (DataRow row : rows) {
            row.items.set(column.ordinal, Reflects.changeType(row.items.get(column.ordinal), dataType));
        }
        column.dataType = dataType;
        return (DataColumn) column;
    }

    @Override
    public String toString() {
        StringBuilder txt = new StringBuilder();
        for (DataColumn column : getColumns()) {
            txt.append(column.getColumnName()).append("\t");
        }
        txt.appendLine();
        Iterator rows = getRows();
        while (rows.hasNext()) {
            for (Object item : rows.next().items) {
                txt.append(item).append("\t");
            }
            txt.appendLine();
        }
        return txt.toString();
    }
}