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

com.alibaba.simpleEL.dialect.ql.QLPreprocessor Maven / Gradle / Ivy

The newest version!
package com.alibaba.simpleEL.dialect.ql;

import java.io.PrintWriter;
import java.io.StringWriter;
import java.lang.reflect.Method;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.Map;
import java.util.concurrent.atomic.AtomicLong;

import com.alibaba.simpleEL.ELException;
import com.alibaba.simpleEL.JavaSource;
import com.alibaba.simpleEL.Preprocessor;
import com.alibaba.simpleEL.dialect.ql.ast.QLBetweenExpr;
import com.alibaba.simpleEL.dialect.ql.ast.QLBinaryOpExpr;
import com.alibaba.simpleEL.dialect.ql.ast.QLBinaryOperator;
import com.alibaba.simpleEL.dialect.ql.ast.QLExpr;
import com.alibaba.simpleEL.dialect.ql.ast.QLIdentifierExpr;
import com.alibaba.simpleEL.dialect.ql.ast.QLLimit;
import com.alibaba.simpleEL.dialect.ql.ast.QLOrderBy;
import com.alibaba.simpleEL.dialect.ql.ast.QLOrderByItem;
import com.alibaba.simpleEL.dialect.ql.ast.QLOrderByMode;
import com.alibaba.simpleEL.dialect.ql.ast.QLSelect;
import com.alibaba.simpleEL.dialect.ql.ast.QLVariantRefExpr;
import com.alibaba.simpleEL.dialect.ql.parser.QLSelectParser;
import com.alibaba.simpleEL.dialect.ql.visitor.QLAstVisitor;
import com.alibaba.simpleEL.dialect.ql.visitor.QLAstVisitorAdapter;
import com.alibaba.simpleEL.dialect.ql.visitor.QLOutputAstVisitor;
import com.alibaba.simpleEL.preprocess.TemplatePreProcessor;

public class QLPreprocessor extends TemplatePreProcessor implements Preprocessor {

    public static final String ATTR_TYPE          = "_TYPE_";

    private String             packageName        = "com.alibaba.simpleEL.dialect.ql.gen";
    private final AtomicLong   classIdSeed        = new AtomicLong(10000L);

    private String             srcCollectionName  = "_src_";
    private String             destCollectionName = "_dest_";

    @Override
    public JavaSource handle(Map context, String ql) {
        StringWriter writer = new StringWriter();
        PrintWriter out = new PrintWriter(writer);

        QLSelectParser parser = new QLSelectParser(ql);

        QLSelect select = parser.select();

        final String className = "GenClass_" + classIdSeed.getAndIncrement();

        gen_package(out);
        gen_import(out);
        gen_class_start(out, className);
        gen_eval_start(out);
        gen_eval_body(context, select, out);
        gen_eval_end(out);
        gen_class_end(out);

        String text = writer.toString();

        return new JavaSource(packageName, className, text);
    }

    public void gen_package(PrintWriter out) {
        out.println("package " + packageName + ";");
        out.println();
    }

    public void gen_import(PrintWriter out) {
        out.println("import java.util.*;");
        out.println("import static java.lang.Math.*;");
        out.println("import com.alibaba.simpleEL.Expr;");
        out.println("import static com.alibaba.simpleEL.TypeUtils.*;");
        out.println();
    }

    public void gen_class_start(PrintWriter out, String className) {
        out.println("public class " + className + " implements Expr {");
        out.println();
    }

    public void gen_class_end(PrintWriter out) {
        out.println();
        out.println("}");
    }

    public void gen_eval_start(PrintWriter out) {
        out.println("	public Object eval(Map ctx) {");
    }

    public void gen_eval_end(PrintWriter out) {
        out.println();
        out.println("		return null;");
        out.println("	}");
    }

    public void gen_eval_body(Map context, QLSelect select, PrintWriter out) {
        Class clazz = (Class) context.get("class");

        if (clazz == null) {
            throw new IllegalArgumentException("context not contains 'class'");
        }

        String className = clazz.getName();
        className = className.replaceAll("\\$", ".");

        String entryVarName = "item";

        out.println("		List<" + className + "> " + srcCollectionName + " = (List<" + className
                    + ">) ctx.get(\"_src_\");");
        out.println("		List<" + className + "> " + destCollectionName + " = (List<" + className
                    + ">) ctx.get(\"_dest_\");");

        final QLLimit limit = select.getLimit();
        if (limit != null) {
            if (limit.getOffset() != null) {
                out.println("		final int offset = " + limit.getOffset() + ";");
            }
            out.println("		final int rowCount = " + limit.getRowCount() + ";");
            out.println();

            if (limit.getOffset() != null) {
                out.println("		int rowIndex = 0;");
            }
        }

        out.println("		for (" + className + " " + entryVarName + " : " + srcCollectionName + ") {");
        out.println();

        gen_where(context, select, out);

        out.println();
        out.println("		}");

        gen_orderBy(context, select, out);
        gen_limit(context, select, out);
    }

    public void gen_limit(final Map context, final QLSelect select, final PrintWriter out) {
        QLLimit limit = select.getLimit();

        if (limit == null) {
            return;
        }

        out.println();

        if (limit.getOffset() != null) {
            out.println("		for (int i = 0; i < offset && i < _dest_.size(); ++i) {");
            out.println("			_dest_.remove(0);");
            out.println("		}");
        }

        out.println("		while(_dest_.size() > rowCount) {");
        out.println("			_dest_.remove(_dest_.size() - 1);");
        out.println("		}");
    }

    public void gen_orderBy(final Map context, final QLSelect select, final PrintWriter out) {
        final Class clazz = (Class) context.get("class");

        if (clazz == null) {
            throw new IllegalArgumentException("context not contains 'class'");
        }

        final QLOrderBy orderBy = select.getOrderBy();
        if (orderBy == null || orderBy.getItems().size() == 0) {
            return;
        }

        String className = clazz.getName();
        className = className.replaceAll("\\$", ".");

        for (QLOrderByItem item : orderBy.getItems()) {
            out.println("		{");
            out.println("			Comparator<" + className + "> comparator = new Comparator<" + className + ">() {");
            out.println("				public int compare(" + className + " a, " + className + " b) {");

            if (item.getMode() == QLOrderByMode.DESC) {
                out.println("					if (" + gen_orderByItem(context, "a", item.getExpr()) + " > "
                            + gen_orderByItem(context, "b", item.getExpr()) + ") {");
                out.println("						return -1;");
                out.println("					}");

                out.println("					if (" + gen_orderByItem(context, "a", item.getExpr()) + " < "
                            + gen_orderByItem(context, "b", item.getExpr()) + ") {");
                out.println("						return 1;");
                out.println("					}");
            } else {
                out.println("					if (" + gen_orderByItem(context, "a", item.getExpr()) + " > "
                            + gen_orderByItem(context, "b", item.getExpr()) + ") {");
                out.println("						return 1;");
                out.println("					}");

                out.println("					if (" + gen_orderByItem(context, "a", item.getExpr()) + " < "
                            + gen_orderByItem(context, "b", item.getExpr()) + ") {");
                out.println("						return -1;");
                out.println("					}");
            }

            out.println("					return 0;");
            out.println("				}");
            out.println("			};");
            out.println("			Collections.sort(" + destCollectionName + ", comparator);");
            out.println("		}");
        }
    }

    public String gen_orderByItem(final Map context, final String varName, final QLExpr expr) {
        final Class clazz = (Class) context.get("class");

        if (clazz == null) {
            throw new IllegalArgumentException("context not contains 'class'");
        }

        final StringWriter out = new StringWriter();

        QLAstVisitor visitor = new QLOutputAstVisitor(new PrintWriter(out)) {

            @Override
            public boolean visit(QLIdentifierExpr x) {
                String name = x.getName();

                Method method = getMethod(clazz, name);

                if (method == null) {
                    throw new ELException("getter not found. " + x.getName());
                }

                out.print(varName + "." + method.getName() + "()");

                return true;
            }
        };
        expr.accept(visitor);
        return out.toString();
    }

    public void gen_where(final Map context, final QLSelect select, final PrintWriter out) {
        final Class clazz = (Class) context.get("class");

        if (clazz == null) {
            throw new IllegalArgumentException("context not contains 'class'");
        }

        final QLExpr where = select.getWhere();
        if (where == null) {
            return;
        }

        final QLLimit limit = select.getLimit();

        QLAstVisitorAdapter setTypeVisitor = new SetTypeVisitor(clazz);
        where.accept(setTypeVisitor);

        out.print("			if(");
        QLAstVisitor visitor = new WhereGenOutputVisitor(out, clazz);
        where.accept(visitor);

        out.println(") {");

        if (limit != null && select.getOrderBy() == null) {
            out.println("				if (_dest_.size() >= rowCount) {");
            out.println("					break;");
            out.println("				}");
        }

        out.println("				" + destCollectionName + ".add(item);");
        out.println("			}");
        out.println();
    }

    private static Method getMethod(Class clazz, String name) {
        name = name.substring(0, 1).toUpperCase() + name.substring(1);
        Method method = null;
        try {
            method = clazz.getMethod("get" + name);
        } catch (NoSuchMethodException e) {
            // skip
        } catch (Exception e) {
            throw new ELException("handle ql where error");
        }

        if (method == null) {
            try {
                method = clazz.getMethod("is" + name);
            } catch (NoSuchMethodException e) {
                // skip
            } catch (Exception e) {
                throw new ELException("handle ql where error");
            }
        }

        return method;
    }

    private final class WhereGenOutputVisitor extends QLOutputAstVisitor {

        private final Class clazz;

        private WhereGenOutputVisitor(PrintWriter out, Class clazz){
            super(out);
            this.clazz = clazz;
        }

        @Override
        public boolean visit(QLIdentifierExpr x) {
            String name = x.getName();

            Method method = getMethod(clazz, name);

            if (method == null) {
                throw new ELException("getter not found. " + x.getName());
            }

            out.print("item." + method.getName() + "()");

            return true;
        }

        public boolean visit(QLBetweenExpr x) {

            if (x.isNot()) {
                out.print(" !(");
            }

            out.print(" _gteq(");
            x.getTestExpr().accept(this);
            out.print(", ");
            x.getBeginExpr().accept(this);

            out.print(") && ");

            out.print(" _lteq(");
            x.getTestExpr().accept(this);
            out.print(", ");
            x.getEndExpr().accept(this);
            out.print(") ");

            if (x.isNot()) {
                out.print(")");
            }
            
            return false;
        }

        @Override
        public boolean visit(QLVariantRefExpr x) {
            Class type = (Class) x.getAttributes().get(ATTR_TYPE);
            
            String variant = x.getName();

            if (variant.startsWith("@")) {
                variant = variant.substring(1);
            }
            
            if (type == null) {
                type = getVariantResolver().getType(variant);
            }

            if (type != null) {
                if (Boolean.class == type || boolean.class == type) {
                    out.print("_bool(ctx.get(\"" + variant + "\"))");
                } else if (String.class == type) {
                    out.print("_string(ctx.get(\"" + variant + "\"))");
                } else if (Byte.class == type || byte.class == type) {
                    out.print("_byte(ctx.get(\"" + variant + "\"))");
                } else if (Short.class == type || short.class == type) {
                    out.print("_short(ctx.get(\"" + variant + "\"))");
                } else if (Integer.class == type || int.class == type) {
                    out.print("_int(ctx.get(\"" + variant + "\"))");
                } else if (Long.class == type || long.class == type) {
                    out.print("_long(ctx.get(\"" + variant + "\"))");
                } else if (Float.class == type || float.class == type) {
                    out.print("_float(ctx.get(\"" + variant + "\"))");
                } else if (Double.class == type || double.class == type) {
                    out.print("_double(ctx.get(\"" + variant + "\"))");
                } else if (BigInteger.class == type) {
                    out.print("_bigInt(ctx.get(\"" + variant + "\"))");
                } else if (BigDecimal.class == type) {
                    out.print("_decimal(ctx.get(\"" + variant + "\"))");
                } else if (java.util.Date.class == type) {
                    out.print("_date(ctx.get(\"" + variant + "\"))");
                } else if (Object.class == type) {
                    out.print("ctx.get(\"" + variant + "\")");
                } else {
                    String className = type.getName();
                    className = className.replaceAll("\\$", "."); // inner
                                                                  // class
                    out.print("((" + className + ")" + "ctx.get(\"" + variant + "\"))");
                }
            } else {
                out.print(x.getName());
            }

            return false;
        }
        
        public boolean visit(QLBinaryOpExpr x) {
            if (x.getLeft() instanceof QLBinaryOpExpr) {
                QLBinaryOpExpr left = (QLBinaryOpExpr) x.getLeft();
                if (left.getOperator().priority > x.getOperator().priority) {
                    out.print('(');
                    left.accept(this);
                    out.print(')');
                } else {
                    left.accept(this);
                }
            } else {
                x.getLeft().accept(this);
            }

            out.print(" ");
            if (QLBinaryOperator.BooleanAnd == x.getOperator()) {
                out.print("&&");
            } else if (QLBinaryOperator.BooleanOr == x.getOperator()) {
                out.print("||");
            } else {
                out.print(x.getOperator().name);
            }
            out.print(" ");

            if (x.getRight() instanceof QLBinaryOpExpr) {
                QLBinaryOpExpr right = (QLBinaryOpExpr) x.getRight();
                if (right.getOperator().priority >= x.getOperator().priority) {
                    out.print('(');
                    right.accept(this);
                    out.print(')');
                } else {
                    right.accept(this);
                }
            } else {
                x.getRight().accept(this);
            }

            return false;
        }
    }

    private final class SetTypeVisitor extends QLAstVisitorAdapter {

        private final Class clazz;

        private SetTypeVisitor(Class clazz){
            this.clazz = clazz;
        }

        @Override
        public boolean visit(QLIdentifierExpr x) {
            String name = x.getName();

            Method method = getMethod(clazz, name);

            if (method == null) {
                throw new ELException("getter not found. " + x.getName());
            }

            x.getAttributes().put(ATTR_TYPE, method.getReturnType());

            return false;
        }
        
        @Override
        public boolean visit(QLBinaryOpExpr x) {
            x.getLeft().accept(this);
            x.getRight().accept(this);

            Class leftType = (Class) x.getLeft().getAttributes().get(ATTR_TYPE);
            Class rightType = (Class) x.getRight().getAttributes().get(ATTR_TYPE);

            if (leftType == null && rightType != null) {
                x.getLeft().getAttributes().put(ATTR_TYPE, rightType);
            }

            if (rightType == null && leftType != null) {
                x.getRight().getAttributes().put(ATTR_TYPE, leftType);
            }

            return false;
        }
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy