Java tutorial
/** * jetbrick-template * http://subchen.github.io/jetbrick-template/ * * Copyright 2010-2013 Guoqiang Chen. All rights reserved. * Email: subchen@gmail.com * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package jetbrick.template.parser; import java.lang.reflect.*; import java.util.*; import javax.lang.model.SourceVersion; import jetbrick.template.JetContext; import jetbrick.template.JetEngine; import jetbrick.template.parser.code.*; import jetbrick.template.parser.grammer.*; import jetbrick.template.parser.grammer.JetTemplateParser.BlockContext; import jetbrick.template.parser.grammer.JetTemplateParser.Break_directiveContext; import jetbrick.template.parser.grammer.JetTemplateParser.ConstantContext; import jetbrick.template.parser.grammer.JetTemplateParser.Continue_directiveContext; import jetbrick.template.parser.grammer.JetTemplateParser.Define_directiveContext; import jetbrick.template.parser.grammer.JetTemplateParser.Define_expressionContext; import jetbrick.template.parser.grammer.JetTemplateParser.DirectiveContext; import jetbrick.template.parser.grammer.JetTemplateParser.Else_directiveContext; import jetbrick.template.parser.grammer.JetTemplateParser.Elseif_directiveContext; import jetbrick.template.parser.grammer.JetTemplateParser.Expr_array_getContext; import jetbrick.template.parser.grammer.JetTemplateParser.Expr_array_listContext; import jetbrick.template.parser.grammer.JetTemplateParser.Expr_class_castContext; import jetbrick.template.parser.grammer.JetTemplateParser.Expr_compare_conditionContext; import jetbrick.template.parser.grammer.JetTemplateParser.Expr_compare_equalityContext; import jetbrick.template.parser.grammer.JetTemplateParser.Expr_compare_notContext; import jetbrick.template.parser.grammer.JetTemplateParser.Expr_compare_relationalContext; import jetbrick.template.parser.grammer.JetTemplateParser.Expr_conditional_ternaryContext; import jetbrick.template.parser.grammer.JetTemplateParser.Expr_constantContext; import jetbrick.template.parser.grammer.JetTemplateParser.Expr_field_accessContext; import jetbrick.template.parser.grammer.JetTemplateParser.Expr_function_callContext; import jetbrick.template.parser.grammer.JetTemplateParser.Expr_groupContext; import jetbrick.template.parser.grammer.JetTemplateParser.Expr_hash_mapContext; import jetbrick.template.parser.grammer.JetTemplateParser.Expr_identifierContext; import jetbrick.template.parser.grammer.JetTemplateParser.Expr_instanceofContext; import jetbrick.template.parser.grammer.JetTemplateParser.Expr_math_binary_basicContext; import jetbrick.template.parser.grammer.JetTemplateParser.Expr_math_binary_bitwiseContext; import jetbrick.template.parser.grammer.JetTemplateParser.Expr_math_binary_shiftContext; import jetbrick.template.parser.grammer.JetTemplateParser.Expr_math_unary_prefixContext; import jetbrick.template.parser.grammer.JetTemplateParser.Expr_math_unary_suffixContext; import jetbrick.template.parser.grammer.JetTemplateParser.Expr_method_invocationContext; import jetbrick.template.parser.grammer.JetTemplateParser.Expr_new_arrayContext; import jetbrick.template.parser.grammer.JetTemplateParser.Expr_new_objectContext; import jetbrick.template.parser.grammer.JetTemplateParser.ExpressionContext; import jetbrick.template.parser.grammer.JetTemplateParser.Expression_listContext; import jetbrick.template.parser.grammer.JetTemplateParser.For_directiveContext; import jetbrick.template.parser.grammer.JetTemplateParser.For_expressionContext; import jetbrick.template.parser.grammer.JetTemplateParser.Hash_map_entry_listContext; import jetbrick.template.parser.grammer.JetTemplateParser.If_directiveContext; import jetbrick.template.parser.grammer.JetTemplateParser.Include_directiveContext; import jetbrick.template.parser.grammer.JetTemplateParser.Invalid_directiveContext; import jetbrick.template.parser.grammer.JetTemplateParser.Put_directiveContext; import jetbrick.template.parser.grammer.JetTemplateParser.Set_directiveContext; import jetbrick.template.parser.grammer.JetTemplateParser.Set_expressionContext; import jetbrick.template.parser.grammer.JetTemplateParser.Stop_directiveContext; import jetbrick.template.parser.grammer.JetTemplateParser.Tag_directiveContext; import jetbrick.template.parser.grammer.JetTemplateParser.TemplateContext; import jetbrick.template.parser.grammer.JetTemplateParser.TextContext; import jetbrick.template.parser.grammer.JetTemplateParser.TypeContext; import jetbrick.template.parser.grammer.JetTemplateParser.Type_argumentsContext; import jetbrick.template.parser.grammer.JetTemplateParser.Type_array_suffixContext; import jetbrick.template.parser.grammer.JetTemplateParser.Type_listContext; import jetbrick.template.parser.grammer.JetTemplateParser.Type_nameContext; import jetbrick.template.parser.grammer.JetTemplateParser.ValueContext; import jetbrick.template.parser.support.*; import jetbrick.template.resource.Resource; import jetbrick.template.runtime.JetTagContext; import jetbrick.template.utils.*; import org.antlr.v4.runtime.ParserRuleContext; import org.antlr.v4.runtime.Token; import org.antlr.v4.runtime.tree.*; import org.slf4j.Logger; import org.slf4j.LoggerFactory; // Visitor ??? Java ? public class JetTemplateCodeVisitor extends AbstractParseTreeVisitor<Code> implements JetTemplateParserVisitor<Code> { private static final Logger log = LoggerFactory.getLogger(JetTemplateCodeVisitor.class); private static final String CONTEXT_NAME = "context"; private final JetEngine engine; private final JetTemplateParser parser; private final VariableResolver resolver; private final Resource resource; private final String encoding; private final boolean trimDirectiveLine; private final boolean trimDirectiveComments; private final String commentsPrefix; private final String commentsSuffix; private SymbolScope scope; // ? private SymbolScope contextScope; // context ?? (??) private BlockCode contextBlockCode; // context ?? (??) private BlockCode textBlockCode; // ( Class Field ) private Map<String, String> textCodeCache; // private Deque<String[]> forStack; // #for ?? #for ?, ? for.index private int uuid = 1; public JetTemplateCodeVisitor(JetEngine engine, VariableResolver resolver, JetTemplateParser parser, Resource resource) { this.engine = engine; this.parser = parser; this.resolver = resolver; this.resource = resource; this.encoding = engine.getConfig().getOutputEncoding(); this.trimDirectiveLine = engine.getConfig().isTrimDirectiveLine(); this.trimDirectiveComments = engine.getConfig().isTrimDirectiveComments(); this.commentsPrefix = engine.getConfig().getTrimDirectiveCommentsPrefix(); this.commentsSuffix = engine.getConfig().getTrimDirectiveCommentsSuffix(); this.scope = new SymbolScope(null); this.textCodeCache = new HashMap<String, String>(32); this.forStack = new ArrayDeque<String[]>(8); } @Override public Code visitTemplate(TemplateContext ctx) { BlockCode code = scope.createBlockCode(128); if (resource.getPackageName() != null) { code.addLine("package " + resource.getPackageName() + ";"); code.addLine(""); } code.addLine("import java.util.*;"); code.addLine("import jetbrick.template.JetContext;"); code.addLine("import jetbrick.template.runtime.*;"); code.addLine(""); code.addLine("@SuppressWarnings({\"all\", \"warnings\", \"unchecked\", \"unused\", \"cast\"})"); code.addLine("public final class " + resource.getClassName() + " extends JetPage {"); scope = scope.push(); textBlockCode = scope.createBlockCode(32); // ? textBlockCode // add render() method code.addLine(""); code.addLine(" @Override"); code.addLine(" public void render(final JetPageContext $ctx) throws Throwable {"); scope = scope.push(); scope.define(CONTEXT_NAME, TypedKlass.JetContext); code.addLine(" final JetContext " + CONTEXT_NAME + " = $ctx.getContext();"); code.addLine(" final JetWriter $out = $ctx.getWriter();"); contextScope = scope; // context ?? contextBlockCode = contextScope.createBlockCode(8); // ? contextBlockCode Code blockCode = ctx.block().accept(this); code.addChild(contextBlockCode); // add context variable definition code.addChild(blockCode); // add children code.addLine(" $out.flush();"); scope = scope.pop(); // exit render() method code.addLine(" }"); // add getName() method code.addLine(""); code.addLine(" @Override"); code.addLine(" public String getName() {"); code.addLine(" return \"" + StringEscapeUtils.escapeJava(resource.getName()) + "\";"); code.addLine(" }"); // add text fields definition code.addLine(""); code.addLine(" public static final String $ENC = \"" + encoding + "\";"); code.addChild(textBlockCode); scope = scope.pop(); // exit class code.addLine("}"); return code; } @Override public Code visitBlock(BlockContext ctx) { BlockCode code = scope.createBlockCode(32); if (ctx.getChildCount() == 0) return code; int size = ctx.children.size(); for (int i = 0; i < size; i++) { ParseTree node = ctx.children.get(i); Code c = node.accept(this); if (node instanceof TextContext) { // TextCode textCode = (TextCode) c; if (trimDirectiveLine || trimDirectiveComments) { ParseTree prev = (i > 0) ? ctx.children.get(i - 1) : null; ParseTree next = (i < size - 1) ? ctx.children.get(i + 1) : null; boolean trimLeft; if (prev == null) { trimLeft = !(ctx.getParent() instanceof TemplateContext); } else { trimLeft = (prev instanceof DirectiveContext); } boolean trimRight; if (next == null) { trimRight = !(ctx.getParent() instanceof TemplateContext); } else { trimRight = (next instanceof DirectiveContext); } // trim if (trimDirectiveComments) { textCode.trimComments(trimLeft, trimRight, commentsPrefix, commentsSuffix); } // trim if (trimDirectiveLine) { textCode.trimEmptyLine(trimLeft, trimRight); } } if (!textCode.isEmpty()) { // ?Text? String source = textCodeCache.get(textCode.getText()); if (source == null) { source = c.getSource(); textCodeCache.put(textCode.getText(), source); // add text into field textBlockCode.addLine(textCode.getTextValueFieldSource()); textBlockCode.addLine(textCode.getTextBytesFieldSource()); } code.addLine(source); } } else { code.addChild(c); } } return code; } @Override public Code visitText(TextContext ctx) { Token token = ((TerminalNode) ctx.getChild(0)).getSymbol(); String text = token.getText(); switch (token.getType()) { case JetTemplateParser.TEXT_CDATA: text = text.substring(3, text.length() - 3); break; case JetTemplateParser.TEXT_ESCAPED_CHAR: text = text.substring(1); break; } String id = getUid("txt"); return new TextCode(id, text); } @Override public Code visitValue(ValueContext ctx) { Code code = ctx.expression().accept(this); String source = code.getSource(); // void?? print ? if (code instanceof SegmentCode) { Class<?> klass = ((SegmentCode) code).getKlass(); if (Void.TYPE.equals(klass)) { return scope.createLineCode(source + "; // line: " + ctx.getStart().getLine()); } } Token token = ((TerminalNode) ctx.getChild(0)).getSymbol(); if (token.getType() == JetTemplateParser.VALUE_ESCAPED_OPEN) { source = "JetUtils.asEscapeHtml(" + source + ")"; } if ("null".equals(source)) { // (??) source = "(Object)null"; } return scope.createLineCode("$out.print(" + source + "); // line: " + ctx.getStart().getLine()); } @Override public Code visitDirective(DirectiveContext ctx) { return ctx.getChild(0).accept(this); } @Override public Code visitDefine_directive(Define_directiveContext ctx) { List<Define_expressionContext> define_expression_list = ctx.define_expression(); BlockCode code = scope.createBlockCode(define_expression_list.size()); for (Define_expressionContext node : define_expression_list) { Code c = node.accept(this); if (c != null) { code.addChild(c); } } return code; } @Override public Code visitDefine_expression(Define_expressionContext ctx) { SegmentCode code = (SegmentCode) ctx.type().accept(this); String name = assert_java_identifier(ctx.IDENTIFIER(), true); if (!scope.define(name, code.getTypedKlass())) { throw reportError("Duplicate local variable " + name, ctx.IDENTIFIER()); } String typeName = code.getTypedKlass().asBoxedTypedKlass().getSource(); return scope.createLineCode(typeName + " " + name + " = (" + typeName + ") " + CONTEXT_NAME + ".get(\"" + name + "\"); // line: " + ctx.getStart().getLine()); } @Override public Code visitSet_directive(Set_directiveContext ctx) { List<Set_expressionContext> set_expression_list = ctx.set_expression(); BlockCode code = scope.createBlockCode(set_expression_list.size()); for (Set_expressionContext node : set_expression_list) { Code c = node.accept(this); if (c != null) { code.addChild(c); } } return code; } @Override public Code visitSet_expression(Set_expressionContext ctx) { String name = assert_java_identifier(ctx.IDENTIFIER(), true); SegmentCode code = (SegmentCode) ctx.expression().accept(this); TypedKlass lhs = null; // ?? TypeContext type = ctx.type(); if (type != null) { // ?? SegmentCode c = (SegmentCode) type.accept(this); lhs = c.getTypedKlass(); if (!scope.define(name, lhs)) { throw reportError("Duplicate local variable " + name, ctx.IDENTIFIER()); } } else { // ?? lhs = scope.resolve(name); if (lhs == null) { lhs = code.getTypedKlass(); scope.define(name, lhs); } } // ? if (!ClassUtils.isAssignable(lhs.getKlass(), code.getKlass())) { // ?? if (!ClassUtils.isAssignable(code.getKlass(), lhs.getKlass())) { // ??? throw reportError("Type mismatch: cannot convert from " + code.getTypedKlass().getSource() + " to " + lhs.getSource(), ctx); } } String source = lhs.getSource() + " " + name + " = (" + lhs.getSource() + ") " + code.getSource() + "; // line: " + ctx.getStart().getLine(); return scope.createLineCode(source); } @Override public Code visitPut_directive(Put_directiveContext ctx) { List<ExpressionContext> expression_list = ctx.expression(); if (expression_list.size() != 2) { throw reportError("Only two arguments can be accepted for #put directive", ctx); } SegmentCode name = (SegmentCode) expression_list.get(0).accept(this); SegmentCode value = (SegmentCode) expression_list.get(1).accept(this); if (!String.class.equals(name.getKlass())) { throw reportError("The first parameter type is not String.class for #put directive", ctx); } return scope.createLineCode(CONTEXT_NAME + ".put(" + name.getSource() + ", " + value.getSource() + "); // line: " + ctx.getStart().getLine()); } @Override public Code visitIf_directive(If_directiveContext ctx) { BlockCode code = scope.createBlockCode(16); SegmentCode expr_code = (SegmentCode) ctx.expression().accept(this); code.addLine("if (" + get_if_expression_source(expr_code) + ") { // line: " + ctx.getStart().getLine()); scope = scope.push(); Code block_code = ctx.block().accept(this); code.addChild(block_code); scope = scope.pop(); code.addLine("}"); // elseif ... List<Elseif_directiveContext> elseif_directive_list = ctx.elseif_directive(); for (Elseif_directiveContext elseif_directive : elseif_directive_list) { Code c = elseif_directive.accept(this); code.addChild(c); } // else ... Else_directiveContext else_directive = ctx.else_directive(); if (else_directive != null) { Code c = else_directive.accept(this); code.addChild(c); } return code; } @Override public Code visitElseif_directive(Elseif_directiveContext ctx) { BlockCode code = scope.createBlockCode(16); SegmentCode expr_code = (SegmentCode) ctx.expression().accept(this); code.addLine( "else if (" + get_if_expression_source(expr_code) + ") { // line: " + ctx.getStart().getLine()); scope = scope.push(); Code block_code = ctx.block().accept(this); code.addChild(block_code); scope = scope.pop(); code.addLine("}"); return code; } @Override public Code visitElse_directive(Else_directiveContext ctx) { BlockCode code = scope.createBlockCode(16); if (ctx.getParent() instanceof If_directiveContext) { code.addLine("else { // line: " + ctx.getStart().getLine()); } scope = scope.push(); Code block_code = ctx.block().accept(this); code.addChild(block_code); scope = scope.pop(); if (ctx.getParent() instanceof If_directiveContext) { code.addLine("}"); } return code; } @Override public Code visitFor_directive(For_directiveContext ctx) { BlockCode code = scope.createBlockCode(16); String id_for = getUid("for"); String id_it = getUid("it"); scope = scope.push(); // ?for??? for ??? ForExpressionCode for_expr_code = (ForExpressionCode) ctx.for_expression().accept(this); // for block forStack.push(new String[] { id_for, "false" }); Code for_block_code = ctx.block().accept(this); scope = scope.pop(); // for-else Else_directiveContext else_directive = ctx.else_directive(); Code for_else_block = (else_directive == null) ? null : else_directive.accept(this); // ?? String[] for_status = forStack.pop(); boolean need_for_status = for_else_block != null || "true".equals(for_status[1]); if (need_for_status) { code.addLine("JetForStatus " + id_for + " = new JetForStatus();"); } code.addLine("Iterator<?> " + id_it + " = JetUtils.asIterator(" + for_expr_code.getSource() + ");"); code.addLine("while (" + id_it + ".hasNext()) { // line: " + ctx.getStart().getLine()); // class item = (class) it.next() ... String typeName = for_expr_code.getKlassName(); code.addLine( " " + typeName + " " + for_expr_code.getName() + " = (" + typeName + ") " + id_it + ".next();"); if (need_for_status) { code.addLine(" " + id_for + ".inc();"); // #for ?? } code.addChild(for_block_code); code.addLine("}"); // for else ... if (for_else_block != null) { code.addLine("if (" + id_for + ".empty()) { // line: " + ctx.getStart().getLine()); code.addChild(for_else_block); code.addLine("}"); } return code; } @Override public Code visitFor_expression(For_expressionContext ctx) { String name = ctx.IDENTIFIER().getText(); SegmentCode code = (SegmentCode) ctx.expression().accept(this); TypedKlass resultKlass = null; TypeContext type = ctx.type(); if (type != null) { // ?? SegmentCode c = (SegmentCode) type.accept(this); resultKlass = c.getTypedKlass(); } else { // ? expression ? Class<?> rhsKlass = code.getKlass(); if (rhsKlass.isArray()) { resultKlass = TypedKlass.create(rhsKlass.getComponentType(), code.getTypeArgs()); } else if (Map.class.isAssignableFrom(rhsKlass)) { resultKlass = TypedKlass.create(Map.Entry.class, code.getTypeArgs()); } else if (Collection.class.isAssignableFrom(rhsKlass)) { if (code.getTypeArgs() != null && code.getTypeArgs().length == 1) { resultKlass = code.getTypeArgs()[0]; } } } if (resultKlass == null) { resultKlass = TypedKlass.Object; } // Boxed ?? from iterator.next() resultKlass = resultKlass.asBoxedTypedKlass(); if (!scope.define(name, resultKlass)) { throw reportError("Duplicate local variable " + name, ctx.IDENTIFIER()); } return new ForExpressionCode(resultKlass, name, code.getSource()); } @Override public Code visitBreak_directive(Break_directiveContext ctx) { assert_inside_of_for_directive(ctx, "#break"); ExpressionContext expression = ctx.expression(); String source; if (expression != null) { SegmentCode c = (SegmentCode) expression.accept(this); source = get_if_expression_source(c); } else { source = "true"; } source = "if (" + source + ") break; // line: " + ctx.getStart().getLine(); return scope.createLineCode(source); } @Override public Code visitContinue_directive(Continue_directiveContext ctx) { assert_inside_of_for_directive(ctx, "#continue"); ExpressionContext expression = ctx.expression(); String source; if (expression != null) { SegmentCode c = (SegmentCode) expression.accept(this); source = get_if_expression_source(c); } else { source = "true"; } source = "if (" + source + ") continue; // line: " + ctx.getStart().getLine(); return scope.createLineCode(source); } @Override public Code visitStop_directive(Stop_directiveContext ctx) { ExpressionContext expression = ctx.expression(); String source; if (expression != null) { SegmentCode c = (SegmentCode) expression.accept(this); source = get_if_expression_source(c); } else { source = "true"; } source = "if (" + source + ") return; // line: " + ctx.getStart().getLine(); return scope.createLineCode(source); } @Override public Code visitInclude_directive(Include_directiveContext ctx) { Expression_listContext expression_list = ctx.expression_list(); SegmentListCode childrenCode = (SegmentListCode) expression_list.accept(this); if (childrenCode.size() > 2) { throw reportError("The arguments do not matched with #include directive.", ctx); } // argument 1: file SegmentCode fileCode = childrenCode.getChild(0); ExpressionContext fileExpression = expression_list.expression(0); if (!String.class.equals(fileCode.getKlass())) { throw reportError("Type mismatch: the first argument cannot convert from " + fileCode.getKlassName() + " to String", fileExpression); } // argument 2: parameters SegmentCode parametersCode = null; if (childrenCode.size() > 1) { parametersCode = childrenCode.getChild(1); if (!(Map.class.equals(parametersCode.getKlass()))) { throw reportError("Type mismatch: the second argument cannot convert from " + parametersCode.getKlassName() + " to Map", expression_list.expression(1)); } } // file ? file.exists() if (fileExpression instanceof Expr_constantContext) { String file = fileCode.getSource(); file = file.substring(1, file.length() - 1); file = StringEscapeUtils.unescapeJava(file); file = PathUtils.getAbsolutionName(resource.getName(), file); if (!engine.lookupResource(file)) { throw reportError("FileNotFoundException: " + file, fileExpression); } } // ?? StringBuilder source = new StringBuilder(); source.append("JetUtils.asInclude($ctx, "); source.append(fileCode.getSource()); source.append(", (Map<String, Object>)"); source.append((parametersCode != null) ? parametersCode.getSource() : "null"); source.append("); // line: "); source.append(ctx.getStart().getLine()); return scope.createLineCode(source.toString()); } @Override public Code visitTag_directive(Tag_directiveContext ctx) { String text = ctx.getChild(0).getText(); String name = text.substring(5, text.length() - 1).trim(); BlockCode code = scope.createBlockCode(32); String id = getUid("tag"); // save global variable SymbolScope old_context_scope = contextScope; BlockCode old_context_block_code = contextBlockCode; // create JetTagContext code.addLine("final JetTagContext " + id + " = new JetTagContext($ctx) {"); scope = scope.push(); code.addLine(" @Override"); code.addLine(" protected void render(final JetWriter $out) throws Throwable {"); scope = scope.push(); contextScope = scope; // Tag context ?? contextBlockCode = contextScope.createBlockCode(8); // ? contextBlockCode Code block = ctx.block().accept(this); code.addChild(contextBlockCode); // add context variable definition code.addChild(block); // add body content code.addLine(" $out.flush();"); scope = scope.pop(); code.addLine(" }"); scope = scope.pop(); code.addLine("};"); // reset to old scope contextScope = old_context_scope; contextBlockCode = old_context_block_code; // finding tag function Class<?>[] parameterTypes = JetTagContext.CLASS_ARRAY; SegmentListCode expr_list_code = null; Expression_listContext expression_list = ctx.expression_list(); if (expression_list != null) { expr_list_code = (SegmentListCode) expression_list.accept(this); parameterTypes = new Class[expr_list_code.size() + 1]; parameterTypes[0] = JetTagContext.class; for (int i = 0; i < expr_list_code.size(); i++) { parameterTypes[i + 1] = expr_list_code.getChild(i).getKlass(); } } Method method = resolver.resolveTagMethod(name, parameterTypes); if (method == null) { throw reportError("Undefined tag definition " + getMethodSignature(name, parameterTypes), ctx); } // source for invoke tag StringBuilder source = new StringBuilder(); source.append(ClassUtils.getShortClassName(method.getDeclaringClass())); source.append('.'); source.append(name); source.append('('); source.append(id); if (expr_list_code != null) { source.append(',').append(expr_list_code.getSource()); } source.append(");"); code.addLine(source.toString()); // return code; } @Override public Code visitInvalid_directive(Invalid_directiveContext ctx) { throw reportError("Missing arguments for " + ctx.getText() + " directive.", ctx); } @Override public Code visitExpr_group(Expr_groupContext ctx) { SegmentCode code = (SegmentCode) ctx.expression().accept(this); String source = "(" + code.getSource() + ")"; return new SegmentCode(code.getTypedKlass(), source); } @Override public Code visitExpr_constant(Expr_constantContext ctx) { return ctx.getChild(0).accept(this); } @Override public Code visitExpr_array_list(Expr_array_listContext ctx) { String source = "Collections.EMPTY_LIST"; Expression_listContext expression_list = ctx.expression_list(); if (expression_list != null) { Code code = expression_list.accept(this); source = "Arrays.asList(" + code.getSource() + ")"; } return new SegmentCode(List.class, source); } @Override public Code visitExpr_hash_map(Expr_hash_mapContext ctx) { String source = "Collections.EMPTY_MAP"; Hash_map_entry_listContext hash_map_entry_list = ctx.hash_map_entry_list(); if (hash_map_entry_list != null) { Code code = hash_map_entry_list.accept(this); source = "JetUtils.asMap(" + code.getSource() + ")"; } return new SegmentCode(Map.class, source); } @Override public Code visitHash_map_entry_list(Hash_map_entry_listContext ctx) { List<ExpressionContext> expression_list = ctx.expression(); SegmentListCode code = new SegmentListCode(expression_list.size()); for (ExpressionContext expression : expression_list) { code.addChild((SegmentCode) expression.accept(this)); } return code; } @Override public Code visitExpr_identifier(Expr_identifierContext ctx) { String name = assert_java_identifier(ctx.IDENTIFIER(), false); // ? for ?? if ("for".equals(name)) { assert_inside_of_for_directive(ctx, "Local variable \"for\""); // ? JetForStatus $for String[] forStatus = forStack.peek(); forStatus[1] = "true"; // for ???? true name = forStatus[0]; // ? forStatus ???? return new SegmentCode(TypedKlass.JetForStatus, name); } // ?? TypedKlass resultKlass = scope.resolve(name); if (resultKlass == null) { // resultKlass = resolver.resolveVariable(name); if (contextScope.define(name, resultKlass)) { if (resultKlass == TypedKlass.Object) { log.warn("line " + ctx.getStart().getLine() + ": Implicit definition for context variable: " + resultKlass.getSource() + " " + name); } // ???Global????? scope.define(name, resultKlass); String klass = resultKlass.asBoxedTypedKlass().getSource(); contextBlockCode.addLine( klass + " " + name + " = (" + klass + ") " + CONTEXT_NAME + ".get(\"" + name + "\");"); } } return new SegmentCode(resultKlass, name); } @Override public Code visitExpr_field_access(Expr_field_accessContext ctx) { ExpressionContext expression = get_not_null_constantContext(ctx.expression()); SegmentCode code = (SegmentCode) expression.accept(this); String name = ctx.IDENTIFIER().getText(); // code = code.asBoxedSegmentCode(); Class<?> beanClass = code.getKlass(); Member member = null; if ((!beanClass.isArray()) || (!"length".equals(name))) { // not array.length member = resolver.resolveProperty(beanClass, name); if (member == null) { // reportError name = name.substring(0, 1).toUpperCase() + name.substring(1); StringBuilder err = new StringBuilder(128); err.append("The method "); err.append("get" + name); err.append("() or "); err.append("is" + name); err.append("() is undefined for the type "); err.append(beanClass.getName()); err.append('.'); throw reportError(err.toString(), ctx.IDENTIFIER()); } } // ?code StringBuilder source = new StringBuilder(); TypedKlass resultKlass = null; String op = ctx.getChild(1).getText(); if (member instanceof Method) { Method method = (Method) member; resultKlass = TypedKlassUtils.getMethodReturnTypedKlass(method); if (method.getParameterTypes().length == 0) { // getXXX() or isXXX() if ("?.".equals(op)) { // NullPointException source.append("(("); source.append(code.getSource()); source.append("==null)?"); source.append(PrimitiveClassUtils.getDefaultValueAsSource(resultKlass.getKlass())); source.append(':'); source.append(code.getSource()); source.append('.'); source.append(method.getName()); source.append("())"); } else { source.append(code.getSource()); source.append('.'); source.append(method.getName()); source.append("()"); } } else { // get(String) if ("?.".equals(op)) { // NullPointException source.append("(("); source.append(code.getSource()); source.append("==null)?"); source.append(PrimitiveClassUtils.getDefaultValueAsSource(resultKlass.getKlass())); source.append(':'); source.append(code.getSource()); source.append(".get(\""); source.append(name); source.append("\"))"); } else { source.append(code.getSource()); source.append(".get(\""); source.append(name); source.append("\")"); } } } else { if (member instanceof Field) { resultKlass = TypedKlassUtils.getFieldTypedKlass((Field) member); } else { // array.length resultKlass = TypedKlass.create(Integer.TYPE); } if ("?.".equals(op)) { // NullPointException source.append("(("); source.append(code.getSource()); source.append("==null)?"); source.append(PrimitiveClassUtils.getDefaultValueAsSource(resultKlass.getKlass())); source.append(':'); source.append(code.getSource()); source.append('.'); source.append(name); source.append(')'); } else { source.append(code.getSource()); source.append('.'); source.append(name); } } return new SegmentCode(resultKlass, source.toString()); } @Override public Code visitExpr_method_invocation(Expr_method_invocationContext ctx) { // ? Class<?> parameterTypes[] = ArrayUtils.EMPTY_CLASS_ARRAY; SegmentListCode expr_list_code = null; Expression_listContext expression_list = ctx.expression_list(); if (expression_list != null) { expr_list_code = (SegmentListCode) expression_list.accept(this); parameterTypes = new Class<?>[expr_list_code.size()]; for (int i = 0; i < expr_list_code.size(); i++) { parameterTypes[i] = expr_list_code.getChild(i).getKlass(); } } // ExpressionContext expression = get_not_null_constantContext(ctx.expression()); SegmentCode code = (SegmentCode) expression.accept(this); code = code.asBoxedSegmentCode(); Class<?> beanClass = code.getKlass(); String name = ctx.IDENTIFIER().getText(); Method bean_method = resolver.resolveMethod(beanClass, name, parameterTypes); Method tool_method = (bean_method != null) ? null : resolver.resolveToolMethod(beanClass, name, parameterTypes); boolean tool_advanced = false; if (tool_method == null) { tool_method = resolver.resolveToolMethod_advanced(beanClass, name, parameterTypes); tool_advanced = true; } if (bean_method == null && tool_method == null) { // reportError StringBuilder err = new StringBuilder(128); err.append("The method "); err.append(name); err.append('('); for (int i = 0; i < parameterTypes.length; i++) { if (i > 0) err.append(','); err.append(parameterTypes[i].getName()); } err.append(") is undefined for the type "); err.append(beanClass.getName()); err.append('.'); throw reportError(err.toString(), ctx.IDENTIFIER()); } // ?code StringBuilder source = new StringBuilder(); String op = ctx.getChild(1).getText(); if (tool_method != null) { // tool method source.append(ClassUtils.getShortClassName(tool_method.getDeclaringClass())); source.append('.'); source.append(name); source.append('('); source.append(code.getSource()); if (tool_advanced) { source.append(",$ctx"); } if (expr_list_code != null) { source.append(','); } } else { if ("?.".equals(op)) { // NullPointException source.append('('); source.append(code.getSource()); source.append("==null)?null:"); source.append(code.getSource()); source.append('.'); source.append(name); source.append('('); } else { source.append(code.getSource()); source.append('.'); source.append(name); source.append('('); } } if (expr_list_code != null) { source.append(expr_list_code.getSource()); } source.append(')'); if ("?.".equals(op)) { // ?()? source.insert(0, '(').append(')'); } // Method method = (bean_method == null) ? tool_method : bean_method; TypedKlass typedKlass = TypedKlassUtils.getMethodReturnTypedKlass(method); return new SegmentCode(typedKlass, source.toString()); } @Override public Code visitExpr_function_call(Expr_function_callContext ctx) { // ? Class<?> parameterTypes[] = ArrayUtils.EMPTY_CLASS_ARRAY; Expression_listContext expression_list = ctx.expression_list(); SegmentListCode expr_list_code = null; if (expression_list != null) { expr_list_code = (SegmentListCode) expression_list.accept(this); parameterTypes = new Class<?>[expr_list_code.size()]; for (int i = 0; i < expr_list_code.size(); i++) { parameterTypes[i] = expr_list_code.getChild(i).getKlass(); } } // String name = ctx.IDENTIFIER().getText(); boolean advanced = false; Method method = resolver.resolveFunction(name, parameterTypes); if (method == null) { method = resolver.resolveFunction_advanced(name, parameterTypes); advanced = true; } if (method == null) { throw reportError("Undefined function " + name + "(...).", ctx.IDENTIFIER()); } // ?code StringBuilder source = new StringBuilder(); source.append(ClassUtils.getShortClassName(method.getDeclaringClass())); source.append('.'); source.append(name); source.append('('); if (advanced) { source.append("$ctx"); } if (expr_list_code != null) { if (advanced) source.append(','); source.append(expr_list_code.getSource()); } source.append(')'); TypedKlass typedKlass = TypedKlassUtils.getMethodReturnTypedKlass(method); return new SegmentCode(typedKlass, source.toString()); } @Override public Code visitExpr_array_get(Expr_array_getContext ctx) { ExpressionContext lhs_expression = get_not_null_constantContext(ctx.expression(0)); ExpressionContext rhs_expression = get_not_null_constantContext(ctx.expression(1)); SegmentCode lhs = (SegmentCode) lhs_expression.accept(this); SegmentCode rhs = (SegmentCode) rhs_expression.accept(this); Class<?> lhsKlass = lhs.getKlass(); if (lhsKlass.isArray()) { if (!ClassUtils.isAssignable(Integer.TYPE, rhs.getKlass())) { throw reportError("Type mismatch: cannot convert from " + rhs.getKlassName() + " to int.", rhs_expression); } String source = lhs.getSource() + "[" + rhs.getSource() + "]"; return new SegmentCode(lhsKlass.getComponentType(), lhs.getTypeArgs(), source); } else { TypedKlass resultKlass = null; // try to List.get(index) or Map.get(name) or JetContext.get(name) if (List.class.isAssignableFrom(lhsKlass)) { if (!ClassUtils.isAssignable(Integer.TYPE, rhs.getKlass())) { throw reportError("The method get(int) in the type List is not applicable for the arguments (" + rhs.getKlassName() + ")", rhs_expression); } // ??List if (lhs.getTypeArgs() != null && lhs.getTypeArgs().length == 1) { resultKlass = lhs.getTypeArgs()[0]; } } else if (Map.class.isAssignableFrom(lhsKlass)) { // ??Map if (lhs.getTypeArgs() != null && lhs.getTypeArgs().length == 2) { resultKlass = lhs.getTypeArgs()[1]; // value } } else if (JetContext.class.isAssignableFrom(lhsKlass)) { if (!String.class.equals(rhs.getKlass())) { throw reportError( "The method get(String) in the type JetContext is not applicable for the arguments (" + rhs.getKlassName() + ")", rhs_expression); } resultKlass = TypedKlass.Object; } else { throw reportError("Operator [] is not applicable for the object (" + lhs.getKlassName() + ").", ctx); } if (resultKlass == null) { resultKlass = TypedKlass.Object; } String source = lhs.getSource() + ".get(" + rhs.getSource() + ")"; return new SegmentCode(resultKlass, source); } } @Override public Code visitExpr_new_object(Expr_new_objectContext ctx) { SegmentCode code = (SegmentCode) ctx.type().accept(this); SegmentListCode expr_list_code = null; Expression_listContext expression_list = ctx.expression_list(); if (expression_list != null) { expr_list_code = (SegmentListCode) expression_list.accept(this); } // Class<?> beanClass = code.getKlass(); Class<?> parameterTypes[] = ArrayUtils.EMPTY_CLASS_ARRAY; if (expr_list_code != null) { parameterTypes = new Class<?>[expr_list_code.size()]; for (int i = 0; i < expr_list_code.size(); i++) { parameterTypes[i] = expr_list_code.getChild(i).getKlass(); } } Constructor<?> constructor = resolver.resolveConstructor(beanClass, parameterTypes); if (constructor == null) { // reportError StringBuilder err = new StringBuilder(128); err.append("The constructor "); err.append('('); for (int i = 0; i < parameterTypes.length; i++) { if (i > 0) err.append(','); err.append(parameterTypes[i].getName()); } err.append(") is undefined for the type "); err.append(beanClass.getName()); err.append('.'); throw reportError(err.toString(), ctx.type()); } // ?? StringBuilder source = new StringBuilder(32); source.append("(new ").append(code.getSource()).append('('); if (expr_list_code != null) { source.append(expr_list_code.getSource()); } source.append("))"); return new SegmentCode(code.getTypedKlass(), source.toString()); } @Override public Code visitExpr_new_array(Expr_new_arrayContext ctx) { SegmentCode code = (SegmentCode) ctx.type().accept(this); if (code.getKlass().isArray()) { throw reportError("Cannot specify an array dimension after an empty dimension", ctx.type()); } StringBuilder typeSource = new StringBuilder(code.getSource()); // ?? StringBuilder source = new StringBuilder(32); source.append("(new ").append(code.getSource()); for (ExpressionContext expression : ctx.expression()) { SegmentCode c = (SegmentCode) expression.accept(this); if (!ClassUtils.isAssignable(Integer.TYPE, c.getKlass())) { throw reportError("Type mismatch: cannot convert from " + c.getKlassName() + " to int.", expression); } source.append('[').append(c.getSource()).append(']'); typeSource.append("[]"); } source.append(')'); TypedKlass resultKlass = resolver.resolveTypedKlass(typeSource.toString()); return new SegmentCode(resultKlass, source.toString()); } @Override public Code visitExpr_class_cast(Expr_class_castContext ctx) { SegmentCode code = (SegmentCode) ctx.type().accept(this); Code expr_code = ctx.expression().accept(this); String source = "((" + code.getSource() + ")" + expr_code.getSource() + ")"; return new SegmentCode(code.getTypedKlass(), source); } @Override public Code visitExpr_instanceof(Expr_instanceofContext ctx) { SegmentCode lhs = (SegmentCode) ctx.expression().accept(this); SegmentCode rhs = (SegmentCode) ctx.type().accept(this); if (!ClassUtils.isAssignable(lhs.getKlass(), rhs.getKlass()) && !ClassUtils.isAssignable(lhs.getKlass(), rhs.getKlass())) { throw reportError( "Incompatible conditional operand types " + lhs.getKlassName() + " and " + rhs.getKlassName(), ctx.getChild(1)); } String source = "(" + lhs.getSource() + " instanceof " + rhs.getSource() + ")"; return new SegmentCode(Boolean.TYPE, source); } @Override public Code visitExpr_math_unary_suffix(Expr_math_unary_suffixContext ctx) { ExpressionContext expression = get_not_null_constantContext(ctx.expression()); SegmentCode code = (SegmentCode) expression.accept(this); String op = ctx.getChild(1).getText(); // ++, -- if (expression.getChildCount() == 1 && expression.getChild(0) instanceof ConstantContext) { throw reportError("Invalid argument to operation " + op + ", required: variable, found Value", expression); } // Class<?> resultKlass = PromotionUtils.get_unary_inc_dec(code.getKlass(), op); if (resultKlass == null) { throw reportError( "The UnaryOperator \"" + op + "\" is not applicable for the operand " + code.getKlassName(), ctx.getChild(1)); } String source = "(" + code.getSource() + op + ")"; return new SegmentCode(code.getTypedKlass(), source); } @Override public Code visitExpr_math_unary_prefix(Expr_math_unary_prefixContext ctx) { ExpressionContext expression = get_not_null_constantContext(ctx.expression()); SegmentCode code = (SegmentCode) expression.accept(this); String op = ctx.getChild(0).getText(); // Class<?> resultKlass; if (op.length() == 1) { // +, -, ~ resultKlass = PromotionUtils.get_unary_basic(code.getKlass(), op); } else { // ++, -- if (expression.getChildCount() == 1 && expression.getChild(0) instanceof ConstantContext) { throw reportError("Invalid argument to operation " + op + ", required: variable, found Value", expression); } resultKlass = PromotionUtils.get_unary_inc_dec(code.getKlass(), op); } if (resultKlass == null) { throw reportError( "The UnaryOperator \"" + op + "\" is not applicable for the operand " + code.getKlassName(), ctx.getChild(0)); } String source = "(" + op + code.getSource() + ")"; return new SegmentCode(code.getTypedKlass(), source); } @Override public Code visitExpr_math_binary_basic(Expr_math_binary_basicContext ctx) { SegmentCode lhs = (SegmentCode) ctx.expression(0).accept(this); SegmentCode rhs = (SegmentCode) ctx.expression(1).accept(this); String op = ctx.getChild(1).getText(); // Class<?> resultKlass = PromotionUtils.get_binary_basic(lhs.getKlass(), rhs.getKlass(), op); if (resultKlass == null) { throw reportError("The BinaryOperator \"" + op + "\" is not applicable for the operands " + lhs.getKlassName() + " and " + rhs.getKlassName(), ctx.getChild(1)); } String source = "(" + lhs.getSource() + op + rhs.getSource() + ")"; return new SegmentCode(resultKlass, source); } @Override public Code visitExpr_math_binary_shift(Expr_math_binary_shiftContext ctx) { SegmentCode lhs = (SegmentCode) ctx.expression(0).accept(this); SegmentCode rhs = (SegmentCode) ctx.expression(1).accept(this); // Combined '>' '>' => '>>' String op = ""; for (int i = 1; i < ctx.getChildCount() - 1; i++) { ParseTree node = ctx.getChild(i); if (node instanceof TerminalNode) { op = op + node.getText(); } } // Class<?> resultKlass = PromotionUtils.get_binary_shift(lhs.getKlass(), rhs.getKlass(), op); if (resultKlass == null) { throw reportError("The BinaryOperator \"" + op + "\" is not applicable for the operands " + lhs.getKlassName() + " and " + rhs.getKlassName(), ctx.getChild(1)); } String source = "(" + lhs.getSource() + op + rhs.getSource() + ")"; return new SegmentCode(resultKlass, source); } @Override public Code visitExpr_math_binary_bitwise(Expr_math_binary_bitwiseContext ctx) { SegmentCode lhs = (SegmentCode) ctx.expression(0).accept(this); SegmentCode rhs = (SegmentCode) ctx.expression(1).accept(this); String op = ctx.getChild(1).getText(); // Class<?> resultKlass = PromotionUtils.get_binary_bitwise(lhs.getKlass(), rhs.getKlass(), op); if (resultKlass == null) { throw reportError("The BinaryOperator \"" + op + "\" is not applicable for the operands " + lhs.getKlassName() + " and " + rhs.getKlassName(), ctx); } String source = "(" + lhs.getSource() + op + rhs.getSource() + ")"; return new SegmentCode(resultKlass, source); } @Override public Code visitExpr_compare_not(Expr_compare_notContext ctx) { SegmentCode code = (SegmentCode) ctx.expression().accept(this); String source = "(!" + get_if_expression_source(code) + ")"; return new SegmentCode(Boolean.TYPE, source); } @Override public Code visitExpr_compare_equality(Expr_compare_equalityContext ctx) { ExpressionContext lhs_expression = get_not_null_constantContext(ctx.expression(0)); ExpressionContext rhs_expression = get_not_null_constantContext(ctx.expression(1)); SegmentCode lhs = (SegmentCode) lhs_expression.accept(this); SegmentCode rhs = (SegmentCode) rhs_expression.accept(this); TerminalNode op = (TerminalNode) ctx.getChild(1); StringBuilder source = new StringBuilder(32); source.append("==".equals(op.getText()) ? "JetUtils.asEquals(" : "JetUtils.asNotEquals("); source.append(lhs.getSource()); source.append(','); source.append(rhs.getSource()); source.append(')'); return new SegmentCode(Boolean.TYPE, source.toString()); } @Override public Code visitExpr_compare_relational(Expr_compare_relationalContext ctx) { ExpressionContext lhs_expression = get_not_null_constantContext(ctx.expression(0)); ExpressionContext rhs_expression = get_not_null_constantContext(ctx.expression(1)); SegmentCode lhs = (SegmentCode) lhs_expression.accept(this); SegmentCode rhs = (SegmentCode) rhs_expression.accept(this); TerminalNode op = (TerminalNode) ctx.getChild(1); Class<?> c1 = lhs.getKlass(); Class<?> c2 = rhs.getKlass(); // boolean pass = true; if (NumberClassUtils.isNumbericClass(c1)) { pass = NumberClassUtils.isNumbericClass(c2); } else if (NumberClassUtils.isNumbericClass(c2)) { pass = false; } else { pass = c1.isAssignableFrom(c2) || c2.isAssignableFrom(c1); } if (pass == false) { throw reportError("The operator " + op.getText() + " is undefined for the argument type(s) " + lhs.getKlassName() + ", " + rhs.getKlassName(), op); } String suffix = ""; switch (op.getSymbol().getType()) { case JetTemplateParser.OP_RELATIONAL_GT: suffix = ">0"; break; case JetTemplateParser.OP_RELATIONAL_LT: suffix = "<0"; break; case JetTemplateParser.OP_RELATIONAL_GE: suffix = ">=0"; break; case JetTemplateParser.OP_RELATIONAL_LE: suffix = "<=0"; break; default: throw reportError("Unexpected operator :" + op.getText(), ctx); } StringBuilder source = new StringBuilder(32); source.append("(JetUtils.asCompareWith("); source.append(lhs.getSource()); source.append(','); source.append(rhs.getSource()); source.append(')'); source.append(suffix); source.append(')'); return new SegmentCode(Boolean.TYPE, source.toString()); } @Override public Code visitExpr_compare_condition(Expr_compare_conditionContext ctx) { ExpressionContext lhs_expression = get_not_null_constantContext(ctx.expression(0)); ExpressionContext rhs_expression = get_not_null_constantContext(ctx.expression(1)); SegmentCode lhs = (SegmentCode) lhs_expression.accept(this); SegmentCode rhs = (SegmentCode) rhs_expression.accept(this); String op = ctx.getChild(1).getText(); String source = "(" + get_if_expression_source(lhs) + op + get_if_expression_source(rhs) + ")"; return new SegmentCode(Boolean.TYPE, source); } @Override public Code visitExpr_conditional_ternary(Expr_conditional_ternaryContext ctx) { SegmentCode lhs = (SegmentCode) ctx.expression(0).accept(this); SegmentCode rhs1 = (SegmentCode) ctx.expression(1).accept(this); SegmentCode rhs2 = (SegmentCode) ctx.expression(2).accept(this); String source = "(" + get_if_expression_source(lhs) + "?" + rhs1.getSource() + ":" + rhs2.getSource() + ")"; TypedKlass klass = PromotionUtils.getResultClassForConditionalOperator(rhs1.getTypedKlass(), rhs2.getTypedKlass()); return new SegmentCode(klass, source); } @Override public Code visitConstant(ConstantContext ctx) { Token token = ((TerminalNode) ctx.getChild(0)).getSymbol(); String text = token.getText(); switch (token.getType()) { case JetTemplateParser.STRING_DOUBLE: return new SegmentCode(String.class, text); case JetTemplateParser.STRING_SINGLE: text = StringEscapeUtils.asCanonicalJavaString(text); return new SegmentCode(String.class, text); case JetTemplateParser.INTEGER: case JetTemplateParser.INTEGER_HEX: case JetTemplateParser.FLOATING_POINT: Class<?> klass; if (text.endsWith("l") || text.endsWith("L")) { klass = Long.TYPE; } else if (text.endsWith("f") || text.endsWith("F")) { klass = Float.TYPE; } else if (text.endsWith("d") || text.endsWith("D")) { klass = Double.TYPE; } else if (token.getType() == JetTemplateParser.FLOATING_POINT) { klass = Double.TYPE; // double } else { klass = Integer.TYPE; } return new SegmentCode(klass, text); case JetTemplateParser.KEYWORD_TRUE: return new SegmentCode(Boolean.TYPE, text); case JetTemplateParser.KEYWORD_FALSE: return new SegmentCode(Boolean.TYPE, text); case JetTemplateParser.KEYWORD_NULL: return new SegmentCode(TypedKlass.NULL, text); default: throw reportError("Unexpected token type :" + token.getType(), ctx); } } @Override public Code visitExpression_list(Expression_listContext ctx) { List<ExpressionContext> expression_list = ctx.expression(); SegmentListCode code = new SegmentListCode(expression_list.size()); for (ExpressionContext expression : expression_list) { Code c = expression.accept(this); code.addChild((SegmentCode) c); } return code; } @Override public Code visitType(TypeContext ctx) { StringBuilder name = new StringBuilder(); for (TerminalNode node : ctx.IDENTIFIER()) { if (name.length() > 0) { name.append('.'); } name.append(node.getText()); } // klass Class<?> klass = resolver.resolveClass(name.toString()); if (klass == null) { throw reportError("java.lang.ClassNotFoundException: " + name.toString(), ctx); } // typeArgs TypedKlass[] typeArgs = TypedKlass.EMPTY_TYPE_ARGS; Type_argumentsContext type_arguments = ctx.type_arguments(); if (type_arguments != null) { SegmentListCode c = (SegmentListCode) type_arguments.accept(this); typeArgs = new TypedKlass[c.size()]; for (int i = 0; i < typeArgs.length; i++) { typeArgs[i] = c.getChild(i).getTypedKlass(); } } // klass ? String array_suffix = ""; List<Type_array_suffixContext> type_array_suffix = ctx.type_array_suffix(); for (Type_array_suffixContext c : type_array_suffix) { Code code = c.accept(this); array_suffix = array_suffix + code.getSource(); } if (array_suffix.length() > 0) { // ?? Array Class, ? resolve String klassName = name.toString() + array_suffix; klass = resolver.resolveClass(klassName); if (klass == null) { throw reportError("java.lang.ClassNotFoundException: " + klassName, ctx); } } // ? Class TypedKlass typedKlass = TypedKlass.create(klass, typeArgs); return new SegmentCode(typedKlass, typedKlass.getSource()); } @Override public Code visitType_array_suffix(Type_array_suffixContext ctx) { return new SegmentCode((TypedKlass) null, "[]"); } @Override public Code visitType_arguments(Type_argumentsContext ctx) { return ctx.type_list().accept(this); } @Override public Code visitType_list(Type_listContext ctx) { List<Type_nameContext> type_name_list = ctx.type_name(); SegmentListCode code = new SegmentListCode(type_name_list.size()); for (Type_nameContext type_name : type_name_list) { Code c = type_name.accept(this); code.addChild((SegmentCode) c); } return code; } @Override public Code visitType_name(Type_nameContext ctx) { TypeContext type = ctx.type(); if (type != null) { return type.accept(this); } // List<?> return new SegmentCode(TypedKlass.WildcharTypedKlass, "?"); } // ----------------------------------------------------------- // ??? java ?? private String assert_java_identifier(TerminalNode node, boolean isDefining) { String name = node.getText(); if ("for".equals(name)) { if (isDefining) { throw reportError("Syntax error on token \"" + name + "\" is not a valid identifier.", node); } return name; } if (CONTEXT_NAME.equals(name)) { if (isDefining) { throw reportError("Duplicate local variable \"" + name + "\" is a reserved identifier.", node); } return name; } if (SourceVersion.isKeyword(name)) { throw reportError("Syntax error on token \"" + name + "\", It is not a valid identifier in Java.", node); } if (name.startsWith("$")) { throw reportError("Local variable \"" + name + "\" can't start with '$', it is a reserved identifier.", node); } return name; } // ctx #for ?, ? for-else ? private void assert_inside_of_for_directive(ParserRuleContext ctx, String name) { // ? forStack ?? ParserRuleContext p = ctx.getParent(); while (p != null) { if (p instanceof For_directiveContext) { return; } if (p instanceof Else_directiveContext) { // ? for-else , ? // if-else , ? #if p = p.getParent(); } p = p.getParent(); } throw reportError(name + " cannot be used outside of a #for directive", ctx); } private ExpressionContext get_not_null_constantContext(ExpressionContext node) { if (node instanceof Expr_constantContext && node.getStart().getType() == JetTemplateParser.KEYWORD_NULL) { throw reportError("Unexpected token: invalid keyword null in here.", node); } return node; } // ?? boolean private String get_if_expression_source(SegmentCode code) { if (Boolean.TYPE.equals(code.getKlass())) { return code.getSource(); } else { return "JetUtils.asBoolean(" + code.getSource() + ")"; } } // ----------------------------------------------------------- // ???? private String getUid(String prefix) { return "$" + prefix + "_" + (uuid++); } private RuntimeException reportError(String message, ParseTree node) { if (node instanceof ParserRuleContext) { parser.notifyErrorListeners(((ParserRuleContext) node).getStart(), message, null); } else if (node instanceof TerminalNode) { parser.notifyErrorListeners(((TerminalNode) node).getSymbol(), message, null); } return new SyntaxErrorException(message); } private String getMethodSignature(String name, Class<?>[] parameterTypes) { StringBuilder sb = new StringBuilder(); sb.append(name).append('('); for (int i = 0; i < parameterTypes.length; i++) { if (i > 0) { sb.append(','); } sb.append(parameterTypes[i].getSimpleName()); } sb.append(')'); return sb.toString(); } }