Package com.sun.tools.javac.processing

Source Code of com.sun.tools.javac.processing.ControllerProcessor$ModelElementVisitor

package com.sun.tools.javac.processing;

import java.io.IOException;
import java.io.Writer;
import java.util.List;
import java.util.Set;
import java.util.Stack;

import javax.annotation.processing.AbstractProcessor;
import javax.annotation.processing.Filer;
import javax.annotation.processing.Messager;
import javax.annotation.processing.ProcessingEnvironment;
import javax.annotation.processing.RoundEnvironment;
import javax.annotation.processing.SupportedAnnotationTypes;
import javax.lang.model.element.Element;
import javax.lang.model.element.ElementKind;
import javax.lang.model.element.ExecutableElement;
import javax.lang.model.element.Modifier;
import javax.lang.model.element.NestingKind;
import javax.lang.model.element.PackageElement;
import javax.lang.model.element.TypeElement;
import javax.lang.model.element.TypeParameterElement;
import javax.lang.model.element.VariableElement;
import javax.lang.model.type.ArrayType;
import javax.lang.model.type.DeclaredType;
import javax.lang.model.type.TypeKind;
import javax.lang.model.type.TypeMirror;
import javax.lang.model.util.Elements;
import javax.lang.model.util.SimpleElementVisitor7;
import javax.lang.model.SourceVersion;
import javax.tools.JavaFileObject;

import douyu.http.HttpMethod;
import douyu.mvc.Action;
import douyu.mvc.Controller;
import douyu.mvc.Model;

/**
*
* @author ZHH
*
*/

//如果用这个,则在java1.6中会出错
//见com.sun.tools.javac.processing.JavacProcessingEnvironment.ProcessorState.checkSourceVersionCompatibility
//Override掉超类的getSupportedSourceVersion()更合适一些
//@SupportedSourceVersion(SourceVersion.RELEASE_7)
@SupportedAnnotationTypes("douyu.mvc.Controller")
//@SupportedAnnotationTypes("*")
public class ControllerProcessor extends AbstractProcessor {

  private static String SUFFIX = "$DOUYU";
  private static String CONTROLLER_VAR_NAME = "_c";

  private static int varCount = 1;

  @Override
  public SourceVersion getSupportedSourceVersion() {
    return SourceVersion.latest();
  }

  @Override
  public boolean process(Set<? extends TypeElement> tes, RoundEnvironment renv) {
    //JavacProcessingEnvironment处理到最后一个Round时,我们就不再管它了。
    if (tes == null || tes.size() == 0)
      return false;

    //renv.getRootElements()返回的Element并不是都有@Controller的
    //按理说用@SupportedAnnotationTypes指明我的Processor只处理哪些注解,javac就应该返回带有@Controller注解的Element,
    //但是实际情况并不是这样,所以这里要再判断一下。
    for (Element element : renv.getRootElements()) {
      ElementKind kind = element.getKind();
      if (kind != ElementKind.CLASS) //有可能是package-info.java中指定的PackageElement(也就是PackageSymbol)
        continue;
      //只处理最顶层的类
      NestingKind nestingKind = ((TypeElement) element).getNestingKind();
      if (nestingKind != NestingKind.TOP_LEVEL)
        continue;

      Controller controller = element.getAnnotation(Controller.class);
      if (controller == null)
        continue;

      varCount = 1; //在开发阶段,如果不断更改源文件,会不断重新编译,这个值如果不清零,会一直加

      String className = ((TypeElement) element).getQualifiedName().toString() + SUFFIX;
      new ControllerElementVisitor(controller, processingEnv).visit(element).writeTo(className);
    }

    return true;
  }

  private static class ControllerElementVisitor extends SimpleElementVisitor7<ControllerElementVisitor, Void> {
    private final ProcessingEnvironment processingEnv;
    private final Elements elementUtils;
    private final Messager messager;
    private final Filer filer;
    private final Controller controller;
    private final PrettyPrinter p = new PrettyPrinter();

    public ControllerElementVisitor(Controller controller, ProcessingEnvironment processingEnv) {
      this.processingEnv = processingEnv;
      this.elementUtils = processingEnv.getElementUtils();
      this.messager = processingEnv.getMessager();
      this.filer = processingEnv.getFiler();
      this.controller = controller;
    }

    public void writeTo(String className) {
      try {
        JavaFileObject jfo = filer.createSourceFile(className, (Element[]) null);
        Writer w = jfo.openWriter();
        w.write(p.toString());
        //在com.sun.tools.javac.processing.JavacProcessingEnvironment.doProcessing中会调用filer.warnIfUnclosedFiles()
        //所以必需要close,否则JavacProcessingEnvironment会出错
        w.close();
      } catch (IOException e) {
        e.printStackTrace();
      }
    }

    @Override
    public ControllerElementVisitor visitType(TypeElement e, Void v) {
      //带有@Controller的类必需是public的
      if (!e.getModifiers().contains(Modifier.PUBLIC)) {
        messager.printMessage(javax.tools.Diagnostic.Kind.ERROR, "class with @Controller must be 'public'", e);
        return this;
      }

      List<? extends Element> enclosedElements = e.getEnclosedElements();

      //带有@Controller的类必需有一个public的默认构造函数
      boolean find = false;
      for (Element element : enclosedElements) {
        if (element.getKind() == ElementKind.CONSTRUCTOR && element.getModifiers().contains(Modifier.PUBLIC)
            && ((ExecutableElement) element).getParameters().size() == 0) {
          find = true;
          break;
        }
      }

      if (!find) {
        messager.printMessage(javax.tools.Diagnostic.Kind.ERROR,
            "class with @Controller must contain a 'public' 0-parameter constructor", e);
        return this;
      }

      PackageElement pkg = elementUtils.getPackageOf(e);
      if (!pkg.isUnnamed()) {
        p.println("package " + pkg.getQualifiedName() + ";");
        p.println();
      }

      String simpleName = e.getSimpleName().toString();
      p.print("public class ").append(simpleName).append(SUFFIX).append(" extends org.douyu.mvc.AbstractContext {");
      p.println();

      p.tab++;
      p.print("private static ").append(simpleName).append(" ").append(CONTROLLER_VAR_NAME).append(" = new ").append(
          simpleName).append("();");
      p.println();
      p.println();

      p.println("protected void executeAction() throws Exception {");
      p.tab++;

      p.println("String p;");
      p.print("if(actionName == null) actionName = \"").append(controller.defaultAction()).append("\";");
      p.println();
      p.println();

      for (Element element : enclosedElements) {
        if (element.getKind() == ElementKind.METHOD)
          this.visit(element);
      }

      p.println("else {");
      p.tab++;
      p.println("response.sendError(404, request.getRequestURI());");
      p.tab--;
      p.println("}");

      p.tab--;
      p.println("}");
      p.tab--;
      p.println("}");
      return this;
    }

    private boolean isFirstAction = true;

    @Override
    public ControllerElementVisitor visitExecutable(ExecutableElement e, Void v) {
      ElementKind kind = e.getKind();
      if (kind != ElementKind.METHOD)
        return this;

      Set<Modifier> modifiers = e.getModifiers();
      if (!modifiers.contains(Modifier.PUBLIC) || modifiers.contains(Modifier.STATIC))
        return this;

      HttpMethod[] httpMethods = null;

      Action action = e.getAnnotation(Action.class);
      if (action != null) {
        httpMethods = action.httpMethods();
      }
      if (httpMethods == null || httpMethods.length == 0) {
        httpMethods = controller.httpMethods();
      }
      StringBuilder methods = new StringBuilder("checkHttpMethods(");
      String sep = "";
      if (httpMethods != null && httpMethods.length > 0) {
        for (HttpMethod hm : httpMethods) {
          methods.append(sep).append("\"").append(hm.name()).append("\"");
          sep = ",";
        }
      }

      methods.append(");");
      if (isFirstAction) {
        p.print("if(actionName.equals(\"").append(e.getSimpleName()).append("\")) {");
        isFirstAction = false;
      } else {
        p.print("else if(actionName.equals(\"").append(e.getSimpleName()).append("\")) {");
      }
      p.println();
      p.tab++;
      p.println(methods);

      printMethodParameters(p, CONTROLLER_VAR_NAME, e, processingEnv, null);

      p.tab--;
      p.println("}");
      return this;
    }

    @Override
    public ControllerElementVisitor visitVariable(VariableElement e, Void v) {
      return this;
    }

    @Override
    public ControllerElementVisitor visitTypeParameter(TypeParameterElement e, Void v) {
      return this;
    }

    @Override
    public ControllerElementVisitor visitPackage(PackageElement e, Void v) {
      return this;
    }

    @Override
    protected ControllerElementVisitor defaultAction(Element e, Void v) {
      return this;
    }
  }

  private static class ModelElementVisitor extends SimpleElementVisitor7<ModelElementVisitor, Void> {
    private final ProcessingEnvironment processingEnv;
    private final Messager messager;
    private final PrettyPrinter p;
    private final String prefixModelVarName;
    private final String invokerModelVarName;

    public ModelElementVisitor(PrettyPrinter p, String prefixModelVarName, String invokerModelVarName,
        ProcessingEnvironment processingEnv) {
      this.processingEnv = processingEnv;
      this.messager = processingEnv.getMessager();
      this.p = p;
      this.prefixModelVarName = prefixModelVarName;
      this.invokerModelVarName = invokerModelVarName;
    }

    @Override
    public ModelElementVisitor visitType(TypeElement e, Void v) {
      //带有@Model的类必需是public的
      if (!e.getModifiers().contains(Modifier.PUBLIC)) {
        messager.printMessage(javax.tools.Diagnostic.Kind.ERROR, "class with @Model must be 'public'", e);
        return this;
      }

      List<? extends Element> enclosedElements = e.getEnclosedElements();

      //带有@Model的类必需有一个public的默认构造函数
      boolean find = false;
      for (Element element : enclosedElements) {
        if (element.getKind() == ElementKind.CONSTRUCTOR && element.getModifiers().contains(Modifier.PUBLIC)
            && ((ExecutableElement) element).getParameters().size() == 0) {
          find = true;
          break;
        }
      }

      if (!find) {
        messager.printMessage(javax.tools.Diagnostic.Kind.ERROR,
            "class with @Model must contain a 'public' 0-parameter constructor", e);
        return this;
      }

      String qualifiedName = e.getQualifiedName().toString();
      p.print(qualifiedName).append(" ").append(invokerModelVarName).append(" = new ").append(qualifiedName).append("();");
      p.println();

      for (Element element : enclosedElements) {
        if (element.getKind() == ElementKind.METHOD) {
          ExecutableElement method = (ExecutableElement) element;
          Set<Modifier> modifiers = method.getModifiers();
          if (!modifiers.contains(Modifier.PUBLIC) || modifiers.contains(Modifier.STATIC))
            continue;

          if (method.getReturnType().getKind() != TypeKind.VOID)
            continue;

          if (method.getParameters().size() == 0)
            continue;

          if (!method.getSimpleName().toString().startsWith("set"))
            continue;

          printMethodParameters(p, invokerModelVarName, method, processingEnv, prefixModelVarName);
        }
      }
      return this;
    }

    @Override
    public ModelElementVisitor visitExecutable(ExecutableElement e, Void v) {
      return this;
    }

    @Override
    public ModelElementVisitor visitVariable(VariableElement e, Void v) {
      return this;
    }

    @Override
    public ModelElementVisitor visitTypeParameter(TypeParameterElement e, Void v) {
      return this;
    }

    @Override
    public ModelElementVisitor visitPackage(PackageElement e, Void v) {
      return this;
    }

    @Override
    protected ModelElementVisitor defaultAction(Element e, Void v) {
      return this;
    }
  }

  //TODO 是否考虑重构,做成ActionParameterType类那样吗?
  //难点是下面每个if语句并没有统一规律,不好生成字符串。
  private static void printMethodParameters(PrettyPrinter p, String invoker, ExecutableElement e,
      ProcessingEnvironment processingEnv, String varPrefix) {
    List<? extends VariableElement> vars = e.getParameters();
    if (vars == null || vars.size() == 0) {
      p.print(invoker).append(".").append(e.getSimpleName()).append("();");
      p.println();
    } else {
      String sep = "";
      StringBuilder s = new StringBuilder();

      TypeMirror varType;
      String type;
      String name;

      for (VariableElement var : vars) {
        varType = var.asType();
        type = varType.toString();
        name = var.getSimpleName().toString();
        if (varPrefix != null)
          name = varPrefix + "." + name;

        if ("douyu.mvc.Context".equals(type) || "douyu.mvc.ModelManager".equals(type)
            || "douyu.mvc.ViewManager".equals(type) || "douyu.mvc.ControllerManager".equals(type)) {
          s.append(sep).append("this");
        } else if ("javax.servlet.http.HttpServletRequest".equals(type) || "javax.servlet.ServletRequest".equals(type)) {
          s.append(sep).append("request");
        } else if ("javax.servlet.http.HttpServletResponse".equals(type) || "javax.servlet.ServletResponse".equals(type)) {
          s.append(sep).append("response");
        } else if ("javax.servlet.ServletContext".equals(type)) {
          s.append(sep).append("servletContext");
        } else if ("java.io.PrintWriter".equals(type) || "java.io.Writer".equals(type)) {
          s.append(sep).append("response.getWriter()");
        } else if ("java.lang.String".equals(type)) {
          s.append(sep).append("request.getParameter(\"");
          s.append(name).append("\")");
        } else if ("int".equals(type)) {
          p.print("int ").append("v").append(varCount).append(" = 0;");
          p.println();
          p.print("p = request.getParameter(\"").append(name).append("\");");
          p.println();

          p.print("if(p != null) v").append(varCount).append(" = Integer.parseInt(p);");
          p.println();
          p.println();

          s.append(sep).append("v").append(varCount);
          varCount++;
        } else if ("long".equals(type)) {
          p.print("long ").append("v").append(varCount).append(" = 0L;");
          p.println();
          p.print("p = request.getParameter(\"").append(name).append("\");");
          p.println();

          p.print("if(p != null) v").append(varCount).append(" = Long.parseLong(p);");
          p.println();
          p.println();

          s.append(sep).append("v").append(varCount);
          varCount++;
        } else if ("float".equals(type)) {
          p.print("float ").append("v").append(varCount).append(" = 0.0F;");
          p.println();
          p.print("p = request.getParameter(\"").append(name).append("\");");
          p.println();

          p.print("if(p != null) v").append(varCount).append(" = Float.parseFloat(p);");
          p.println();
          p.println();

          s.append(sep).append("v").append(varCount);
          varCount++;
        } else if ("double".equals(type)) {
          p.print("double ").append("v").append(varCount).append(" = 0.0D;");
          p.println();
          p.print("p = request.getParameter(\"").append(name).append("\");");
          p.println();

          p.print("if(p != null) v").append(varCount).append(" = Double.parseDouble(p);");
          p.println();
          p.println();

          s.append(sep).append("v").append(varCount);
          varCount++;
        } else if ("boolean".equals(type)) {
          p.print("boolean ").append("v").append(varCount).append(" = false;");
          p.println();
          p.print("p = request.getParameter(\"").append(name).append("\");");
          p.println();

          p.print("if(p != null) v").append(varCount).append(" = Boolean.parseBoolean(p);");
          p.println();
          p.println();

          s.append(sep).append("v").append(varCount);
          varCount++;
        } else if ("byte".equals(type)) {
          p.print("byte ").append("v").append(varCount).append(" = 0;");
          p.println();
          p.print("p = request.getParameter(\"").append(name).append("\");");
          p.println();

          p.print("if(p != null) v").append(varCount).append(" = Byte.parseByte(p);");
          p.println();
          p.println();

          s.append(sep).append("v").append(varCount);
          varCount++;
        } else if ("short".equals(type)) {
          p.print("short ").append("v").append(varCount).append(" = 0;");
          p.println();
          p.print("p = request.getParameter(\"").append(name).append("\");");
          p.println();

          p.print("if(p != null) v").append(varCount).append(" = Short.parseShort(p);");
          p.println();
          p.println();

          s.append(sep).append("v").append(varCount);
          varCount++;
        } else if ("char".equals(type)) {
          p.print("char ").append("v").append(varCount).append(" = 0;");
          p.println();

          p.print("p = request.getParameter(\"").append(name).append("\");");
          p.println();

          p.print("if(p != null && p.length() > 0) v").append(varCount).append(" = p.charAt(0);");
          p.println();
          p.println();

          s.append(sep).append("v").append(varCount);
          varCount++;
        } else if ("java.lang.Integer".equals(type) || "java.lang.Long".equals(type) || "java.lang.Float".equals(type)
            || "java.lang.Double".equals(type) || "java.lang.Boolean".equals(type) || "java.lang.Byte".equals(type)
            || "java.lang.Short".equals(type)) {

          type = type.substring(type.lastIndexOf('.') + 1);

          p.print(type).append(" v").append(varCount).append(" = null;");
          p.println();

          p.print("p = request.getParameter(\"").append(name).append("\");");
          p.println();

          p.print("if(p != null) v").append(varCount).append(" = ").append(type).append(".valueOf(p);");
          p.println();
          p.println();

          s.append(sep).append("v").append(varCount);
          varCount++;
        } else if ("java.lang.Character".equals(type)) {
          p.print("Character v").append(varCount).append(" = null;");
          p.println();

          p.print("p = request.getParameter(\"").append(name).append("\");");
          p.println();

          p.print("if(p != null && p.length() > 0) v").append(varCount).append(" = p.charAt(0);");
          p.println();
          p.println();

          s.append(sep).append("v").append(varCount);
          varCount++;
        } else if (varType.getKind() == TypeKind.ARRAY
            && "java.lang.String".equals(((ArrayType) varType).getComponentType().toString())) {

          s.append(sep).append("request.getParameterValues(\"").append(name).append("\")");

        } else if ("javax.servlet.http.Part".equals(type)) {
          s.append(sep).append("request.getPart(\"").append(name).append("\")");

        } else if (varType.getKind() == TypeKind.ARRAY
            && "javax.servlet.http.Part".equals(((ArrayType) varType).getComponentType().toString())) {

          p.print("javax.servlet.http.Part[] ").append("v").append(varCount).append(" = null;");
          p.println();
          p.println("java.util.Collection<javax.servlet.http.Part> __parts = request.getParts();");

          p.print("if(__parts != null) v").append(varCount).append(
              " = __parts.toArray(new javax.servlet.http.Part[0]);");
          p.println();
          p.println();

          s.append(sep).append("v").append(varCount);
          varCount++;
        } else {
          //只注入带有@Model的类,其他的都注入null
          if (varType.getKind() == TypeKind.DECLARED) {
            Element element = ((DeclaredType) varType).asElement();

            if (element.getAnnotation(Model.class) != null) {
              String invokerModelVarName;

              //防止循环setXXX
              //如A.setXXX(B b), B.setYYY(A a)
              //出现这种情况时,B直接用A的引用
              int index = modelElementStack.indexOf(element);
              if (index != -1) {
                invokerModelVarName = modelVarNameStack.get(index);
              } else {
                invokerModelVarName = "v" + varCount;
                varCount++;

                modelElementStack.push(element);
                modelVarNameStack.push(invokerModelVarName);

                try {
                  new ModelElementVisitor(p, name, invokerModelVarName, processingEnv).visit(element);
                } finally {
                  modelElementStack.pop();
                  modelVarNameStack.pop();
                }
              }
              s.append(sep).append(invokerModelVarName);
            } else {
              s.append(sep).append("null");
            }
            //TODO 支持Model数组
            //} else if (varType.getKind() == TypeKind.ARRAY
            //    && ((ArrayType) varType).getComponentType().getKind() == TypeKind.DECLARED) {
            //  Element element = ((DeclaredType) ((ArrayType) varType).getComponentType()).asElement();
          } else {
            s.append(sep).append("null");
          }
        }

        sep = ",";
      }
      p.print(invoker).append(".").append(e.getSimpleName()).append("(").append(s).append(");");
      p.println();
    }
  }

  private static Stack<Element> modelElementStack = new Stack<Element>();
  private static Stack<String> modelVarNameStack = new Stack<String>();
}
TOP

Related Classes of com.sun.tools.javac.processing.ControllerProcessor$ModelElementVisitor

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.