class JavaAggregate extends com.lealone.sql.expression.aggregate.Aggregate {

    private final UserAggregate userAggregate;
    private final Expression[] args;
    private int[] argTypes;
    private Connection userConnection;

    private Aggregate aggregate;

    public JavaAggregate(UserAggregate userAggregate, Expression[] args, Select select) {
        super(select);
        this.userAggregate = userAggregate;
        this.args = args;
    }

    public UserAggregate getUserAggregate() {
        return userAggregate;
    }

    public Expression[] getArgs() {
        return args;
    }

    @Override
    public int getScale() {
        return DataType.getDataType(dataType).defaultScale;
    }

    @Override
    public long getPrecision() {
        return Integer.MAX_VALUE;
    }

    @Override
    public int getDisplaySize() {
        return Integer.MAX_VALUE;
    }

    @Override
    public int getCost() {
        int cost = 5;
        for (Expression e : args) {
            cost += e.getCost();
        }
        return cost;
    }

    @Override
    public Expression optimize(ServerSession session) {
        userConnection = session.createNestedConnection(false);
        int len = args.length;
        argTypes = new int[len];
        for (int i = 0; i < len; i++) {
            Expression expr = args[i];
            args[i] = expr.optimize(session);
            int type = expr.getType();
            argTypes[i] = type;
        }
        try {
            Aggregate aggregate = getInstance();
            dataType = aggregate.getInternalType(argTypes);
        } catch (SQLException e) {
            throw DbException.convert(e);
        }
        return this;
    }

    @Override
    public String getSQL() {
        StatementBuilder buff = new StatementBuilder();
        buff.append(LealoneSQLParser.quoteIdentifier(userAggregate.getName())).append('(');
        for (Expression e : args) {
            buff.appendExceptFirst(", ");
            buff.append(e.getSQL());
        }
        return buff.append(')').toString();
    }

    private Aggregate getInstance() throws SQLException {
        if (aggregate == null) {
            aggregate = userAggregate.getInstance();
            aggregate.init(userConnection);
        }
        return aggregate;
    }

    @Override
    public Value getValue(ServerSession session) {
        HashMap<Expression, Object> group = select.getCurrentGroup();
        if (group == null) {
            throw DbException.get(ErrorCode.INVALID_USE_OF_AGGREGATE_FUNCTION_1, getSQL());
        }
        try {
            Aggregate agg = (Aggregate) group.get(this);
            if (agg == null) {
                agg = getInstance();
            }
            Object obj = agg.getResult();
            if (obj == null) {
                return ValueNull.INSTANCE;
            }
            return DataType.convertToValue(session, obj, dataType);
        } catch (SQLException e) {
            throw DbException.convert(e);
        }
    }

    @Override
    public void updateAggregate(ServerSession session) {<FILL_FUNCTION_BODY>}

    @Override
    public <R> R accept(ExpressionVisitor<R> visitor) {
        return visitor.visitJavaAggregate(this);
    }
}

        HashMap<Expression, Object> group = select.getCurrentGroup();
        if (group == null) {
            // this is a different level (the enclosing query)
            return;
        }

        int groupRowId = select.getCurrentGroupRowId();
        if (lastGroupRowId == groupRowId) {
            // already visited
            return;
        }
        lastGroupRowId = groupRowId;

        Aggregate agg = (Aggregate) group.get(this);
        try {
            if (agg == null) {
                agg = getInstance();
                group.put(this, agg);
            }
            Object[] argValues = new Object[args.length];
            Object arg = null;
            for (int i = 0, len = args.length; i < len; i++) {
                Value v = args[i].getValue(session);
                v = v.convertTo(argTypes[i]);
                arg = v.getObject();
                argValues[i] = arg;
            }
            if (args.length == 1) {
                agg.add(arg);
            } else {
                agg.add(argValues);
            }
        } catch (SQLException e) {
            throw DbException.convert(e);
        }
    
<methods>public void <init>(com.lealone.sql.query.Select) ,public R accept(ExpressionVisitor<R>) ,public static com.lealone.sql.expression.aggregate.Aggregate create(int, com.lealone.sql.expression.Expression, com.lealone.sql.query.Select, boolean) ,public static int getAggregateType(java.lang.String) ,public com.lealone.sql.expression.Expression getOn() ,public int getType() <variables>private static final HashMap<java.lang.String,java.lang.Integer> AGGREGATES,public static final int AVG,public static final int BIT_AND,public static final int BIT_OR,public static final int BOOL_AND,public static final int BOOL_OR,public static final int COUNT,public static final int COUNT_ALL,public static final int GROUP_CONCAT,public static final int HISTOGRAM,public static final int MAX,public static final int MIN,public static final int SELECTIVITY,public static final int STDDEV_POP,public static final int STDDEV_SAMP,public static final int SUM,public static final int VAR_POP,public static final int VAR_SAMP,protected int dataType,protected int lastGroupRowId,protected final non-sealed com.lealone.sql.query.Select select