Package juzu.impl.plugin.controller.metamodel

Source Code of juzu.impl.plugin.controller.metamodel.ControllerMetaModelPlugin

/*
* Copyright 2013 eXo Platform SAS
*
* 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 juzu.impl.plugin.controller.metamodel;

import juzu.Action;
import juzu.Application;
import juzu.Consumes;
import juzu.Resource;
import juzu.View;
import juzu.impl.common.Name;
import juzu.impl.plugin.application.metamodel.ApplicationMetaModel;
import juzu.impl.plugin.application.metamodel.ApplicationMetaModelPlugin;
import juzu.impl.metamodel.AnnotationKey;
import juzu.impl.metamodel.AnnotationState;
import juzu.impl.compiler.ProcessingException;
import juzu.impl.compiler.ElementHandle;
import juzu.impl.compiler.ProcessingContext;
import juzu.impl.plugin.module.metamodel.ModuleMetaModel;
import juzu.impl.request.BeanParameter;
import juzu.impl.request.ContextualParameter;
import juzu.impl.request.ControlParameter;
import juzu.impl.request.ControllerHandler;
import juzu.impl.request.PhaseParameter;
import juzu.impl.plugin.controller.descriptor.ControllerDescriptor;
import juzu.impl.metamodel.MetaModelEvent;
import juzu.impl.metamodel.MetaModelObject;
import juzu.impl.request.Request;
import juzu.impl.common.Cardinality;
import juzu.impl.common.JSON;
import juzu.impl.common.Tools;
import juzu.impl.value.ValueType;
import juzu.processor.MainProcessor;
import juzu.request.Phase;

import javax.annotation.Generated;
import javax.lang.model.element.AnnotationMirror;
import javax.lang.model.element.Element;
import javax.lang.model.element.ElementKind;
import javax.lang.model.element.Modifier;
import javax.lang.model.element.PackageElement;
import javax.lang.model.element.TypeElement;
import javax.lang.model.type.DeclaredType;
import javax.lang.model.type.TypeKind;
import javax.lang.model.type.TypeMirror;
import javax.tools.JavaFileObject;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Writer;
import java.net.URL;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;

/** @author <a href="mailto:julien.viet@exoplatform.com">Julien Viet</a> */
public class ControllerMetaModelPlugin extends ApplicationMetaModelPlugin {

  /** . */
  private static final String METHOD_DESCRIPTOR = ControllerHandler.class.getSimpleName();

  /** . */
  private static final String CONTROLLER_DESCRIPTOR = ControllerDescriptor.class.getSimpleName();

  /** . */
  private static final String PARAMETER = ControlParameter.class.getSimpleName();

  /** . */
  private static final String PHASE_PARAMETER = PhaseParameter.class.getSimpleName();

  /** . */
  private static final String CONTEXTUAL_PARAMETER = ContextualParameter.class.getSimpleName();

  /** . */
  private static final String BEAN_PARAMETER = BeanParameter.class.getSimpleName();

  /** . */
  private static final String PHASE = Phase.class.getSimpleName();

  /** . */
  private static final String TOOLS = Tools.class.getSimpleName();

  /** . */
  public static final String CARDINALITY = Cardinality.class.getSimpleName();

  /** . */
  private HashSet<ControllerMetaModel> written = new HashSet<ControllerMetaModel>();

  /** . */
  private static final String SERVICES = "META-INF/services/" + ValueType.class.getName();

  /** . */
  final HashSet<ElementHandle.Type> valueTypes = new HashSet<ElementHandle.Type>();

  public ControllerMetaModelPlugin() {
    super("controller");
  }

  public Set<Class<? extends java.lang.annotation.Annotation>> init(ProcessingContext env) {
    return Tools.<Class<? extends java.lang.annotation.Annotation>>set(View.class, Action.class, Consumes.class, Resource.class);
  }

  @Override
  public void postActivate(ModuleMetaModel applications) {
    valueTypes.clear();
    for (ValueType<?> valueType : ValueType.DEFAULT) {
      for (Class<?> type : valueType.getTypes()) {
        valueTypes.add(ElementHandle.Type.create(Name.create(type)));
      }
    }
    Enumeration<URL> services;
    try {
      services = MainProcessor.class.getClassLoader().getResources(SERVICES);
    }
    catch (IOException e) {
      services = null;
    }
    if (services != null) {
      while (services.hasMoreElements()) {
        URL url = services.nextElement();
        try {
          InputStream in = url.openStream();
          BufferedReader reader = new BufferedReader(new InputStreamReader(in, Tools.UTF_8));
          while (true) {
            String line = reader.readLine();
            if (line != null) {
              String providerName = line.trim();
              TypeElement provider = applications.getProcessingContext().getTypeElement(providerName);
              if (provider.getKind() == ElementKind.CLASS) {
                List<? extends TypeMirror> superTypes = applications.getProcessingContext().directSupertypes((TypeMirror)provider.asType());
                if (superTypes.size() > 0) {
                  TypeMirror superType = superTypes.get(0);
                  if (superType.getKind() == TypeKind.DECLARED) {
                    DeclaredType declaredTypeSuper = (DeclaredType)superType;
                    TypeElement declaredSuperElement = (TypeElement)declaredTypeSuper.asElement();
                    if (ValueType.class.getName().equals(declaredSuperElement.getQualifiedName().toString())) {
                      TypeMirror argument = declaredTypeSuper.getTypeArguments().get(0);
                      if (argument instanceof DeclaredType) {
                        DeclaredType declaredArgumentType = (DeclaredType)argument;
                        Element declaredArgumentElement = declaredArgumentType.asElement();
                        if (declaredArgumentElement instanceof TypeElement) {
                          // Here we are
                          ElementHandle.Type valueType = ElementHandle.Type.create((TypeElement) declaredArgumentElement);
                          valueTypes.add(valueType);
                        }
                      }
                    } else {
                      // ?
                    }
                  } else {
                    // ?
                  }
                } else {
                  // This is likely an interface
                }
              }
            } else {
              break;
            }
          }
        }
        catch (IOException e) {
          // ?
        }
      }
    }
  }

  @Override
  public void init(ApplicationMetaModel application) {
    ControllersMetaModel controllers = new ControllersMetaModel(this);
    PackageElement pkg = application.model.processingContext.get(application.getHandle());
    AnnotationMirror annotation = Tools.getAnnotation(pkg, Application.class.getName());
    AnnotationState values = AnnotationState.create(annotation);
    Boolean escapeXML = (Boolean)values.get("escapeXML");
    ElementHandle.Type defaultControllerElt = (ElementHandle.Type)values.get("defaultController");
    ElementHandle.Type errorControllerElt = (ElementHandle.Type)values.get("errorController");
    controllers.escapeXML = escapeXML;
    controllers.defaultController = defaultControllerElt != null ? defaultControllerElt.getName() : null;
    controllers.errorController = errorControllerElt != null ? errorControllerElt.getName() : null;
    application.addChild(ControllersMetaModel.KEY, controllers);
  }

  @Override
  public void processAnnotationAdded(ApplicationMetaModel application, AnnotationKey key, AnnotationState added) {
    ElementHandle.Method methodHandle = (ElementHandle.Method)key.getElement();
    ElementHandle.Type controllerHandle = methodHandle.getType();
    ControllersMetaModel controllers = application.getChild(ControllersMetaModel.KEY);
    ControllerMetaModel controller = controllers.get(controllerHandle);
    if (controller == null) {
      controllers.add(controller = new ControllerMetaModel(controllerHandle));
    }
    controller.addMethod(application.model, key, added);
  }

  @Override
  public void processAnnotationRemoved(ApplicationMetaModel metaModel, AnnotationKey key, AnnotationState removed) {
    ElementHandle.Method methodHandle = (ElementHandle.Method)key.getElement();
    ElementHandle.Type controllerHandle = ElementHandle.Type.create(methodHandle.getTypeName());
    ControllersMetaModel controllers = metaModel.getChild(ControllersMetaModel.KEY);
    ControllerMetaModel controller = controllers.get(controllerHandle);
    if (controller != null) {
      controller.removeMethod(methodHandle);
      if (controller.getHandlers().isEmpty()) {
        controller.remove();
      }
    }
  }

  @Override
  public void postProcessAnnotations(ApplicationMetaModel application) {
    for (ControllerMetaModel controller : application.getChild(ControllersMetaModel.KEY)) {
      if (controller.modified) {
        controller.modified = false;
        controller.queue(MetaModelEvent.createUpdated(controller));
      }
    }
  }

  @Override
  public void processEvent(ApplicationMetaModel application, MetaModelEvent event) {
    MetaModelObject obj = event.getObject();
    if (obj instanceof ControllerMetaModel) {
      switch (event.getType()) {
        case MetaModelEvent.BEFORE_REMOVE:
          break;
        case MetaModelEvent.UPDATED:
        case MetaModelEvent.AFTER_ADD:
          ControllerMetaModel controller = (ControllerMetaModel)obj;
          written.add(controller);
          break;
      }
    }
  }


  @Override
  public JSON getDescriptor(ApplicationMetaModel application) {
    ControllersMetaModel ac = application.getChild(ControllersMetaModel.KEY);

    // Build routes configuration
    ArrayList<String> controllers = new ArrayList<String>();
    for (ControllerMetaModel controller : ac) {
      controllers.add(controller.getHandle().getName() + "_");
    }

    //
    JSON config = new JSON();
    config.set("default", ac.defaultController != null ? ac.defaultController.toString() : null);
    config.set("error", ac.errorController != null ? ac.errorController.toString() : null);
    config.set("escapeXML", ac.escapeXML);
    config.map("controllers", controllers);

    //
    return config;
  }

  /** . */
  private static final HashMap<Phase, String> DISPATCH_TYPE = new HashMap<Phase, String>();

  static
  {
    DISPATCH_TYPE.put(Phase.ACTION, Tools.getName(Phase.Action.Dispatch.class));
    DISPATCH_TYPE.put(Phase.VIEW, Tools.getName(Phase.View.Dispatch.class));
    DISPATCH_TYPE.put(Phase.RESOURCE, Tools.getName(Phase.Resource.Dispatch.class));
  }

  @Override
  public void postProcessEvents(ApplicationMetaModel application) {

    // Validate abstract
    for (ControllerMetaModel controller : application.getChild(ControllersMetaModel.KEY)) {
      TypeElement controllerElt = application.getProcessingContext().get(controller.getHandle());
      if (controllerElt.getModifiers().contains(Modifier.ABSTRACT)) {
        throw ControllerMetaModel.CONTROLLER_IS_ABSTRACT.failure(controllerElt, controller.handle.getName());
      }
    }

    // Check everything is OK here
//    for (ControllerMetaModel controller : application.getChild(ControllersMetaModel.KEY)) {
//      for (MethodMetaModel method : controller.getMethods()) {
//        ExecutableElement executableElt = application.model.processingContext.get(method.handle);
//        Iterator<? extends VariableElement> i = executableElt.getParameters().iterator();
//        for (ParameterMetaModel parameter : method.parameters) {
//          VariableElement ve = i.next();
//          if (parameter instanceof InvocationParameterMetaModel) {
//            InvocationParameterMetaModel invocationParameter = (InvocationParameterMetaModel)parameter;
//            TypeElement te = application.model.processingContext.get(invocationParameter.getType());
//            if (!te.toString().equals("java.lang.String") && te.getAnnotation(Mapped.class) == null) {
//              // We should find out who was compiled the bean or the type containing a ref to the class
//              throw ControllerMetaModel.CONTROLLER_METHOD_PARAMETER_NOT_RESOLVED.failure(ve, ve.getSimpleName());
//            }
//          }
//        }
//      }
//    }

    // Emit controllers
    for (Iterator<ControllerMetaModel> i = written.iterator();i.hasNext();) {
      ControllerMetaModel controller = i.next();
      i.remove();
      emitController(application.model.processingContext, controller);
    }
  }

  private void emitController(ProcessingContext env, ControllerMetaModel controller) throws ProcessingException {
    Name fqn = controller.getHandle().getName();
    Element origin = env.get(controller.getHandle());
    Collection<HandlerMetaModel> methods = controller.getHandlers();
    Writer writer = null;
    try {
      JavaFileObject file = env.createSourceFile(fqn + "_", origin);
      writer = file.openWriter();

      //
      writer.append("package ").append(fqn.getParent()).append(";\n");

      // Imports
      writer.append("import ").append(ControllerHandler.class.getCanonicalName()).append(";\n");
      writer.append("import ").append(ControlParameter.class.getCanonicalName()).append(";\n");
      writer.append("import ").append(PhaseParameter.class.getCanonicalName()).append(";\n");
      writer.append("import ").append(ContextualParameter.class.getCanonicalName()).append(";\n");
      writer.append("import ").append(BeanParameter.class.getCanonicalName()).append(";\n");
      writer.append("import ").append(Tools.class.getCanonicalName()).append(";\n");
      writer.append("import ").append(Arrays.class.getCanonicalName()).append(";\n");
      writer.append("import ").append(Phase.class.getCanonicalName()).append(";\n");
      writer.append("import ").append(ControllerDescriptor.class.getCanonicalName()).append(";\n");
      writer.append("import ").append(Generated.class.getCanonicalName()).append(";\n");
      writer.append("import ").append(Cardinality.class.getCanonicalName()).append(";\n");
      writer.append("import ").append(Request.class.getCanonicalName()).append(";\n");

      // Open class
      writer.append("@Generated(value={})\n");
      writer.append("public class ").append(fqn.getIdentifier()).append("_ {\n");

      // Class literal
      writer.append("private static final Class<").append(fqn).append("> TYPE = ").append(fqn).append(".class;\n");

      //
      int index = 0;
      for (HandlerMetaModel method : methods) {

        //
        String methodRef = "method_" + index++;

        // Method constant
        writer.append("private static final ").append(METHOD_DESCRIPTOR).append("<");
        Tools.nameOf(method.getPhase().getClass(), writer);
        writer.append("> ").append(methodRef).append(" = ");
        writer.append("new ").append(METHOD_DESCRIPTOR).append("<");
        Tools.nameOf(method.getPhase().getClass(), writer);
        writer.append(">(");
        if (method.getId() != null) {
          writer.append("\"").append(method.getId()).append("\",");
        }
        else {
          writer.append("null,");
        }
        writer.append(PHASE).append(".").append(method.getPhase().name()).append(",");
        writer.append("TYPE,");
        writer.append(TOOLS).append(".safeGetMethod(TYPE,\"").append(method.getName()).append("\"");
        for (ParameterMetaModel parameter : method.getParameters()) {
          writer.append(",").append(parameter.type).append(".class");
        }
        writer.append(')');
        writer.append(", Arrays.<").append(PARAMETER).append(">asList(");
        for (int i = 0;i < method.getParameters().size();i++) {
          ParameterMetaModel parameter = method.getParameters().get(i);
          if (i > 0) {
            writer.append(',');
          }
          if (parameter instanceof BeanParameterMetaModel) {
            writer.append("new ").
                append(BEAN_PARAMETER).append('(').
                append('"').append(parameter.getName()).append('"').append(',').
                append(parameter.type).append(".class").
                append(')');
          } else if (parameter instanceof PhaseParameterMetaModel) {
            PhaseParameterMetaModel phaseParameter = (PhaseParameterMetaModel)parameter;
            writer.append("new ").
                append(PHASE_PARAMETER).append('(').
                append('"').append(parameter.getName()).append('"').append(',').
                append(parameter.type).append(".class").append(',').
                append(phaseParameter.valueType).append(".class").append(',').
                append(CARDINALITY).append('.').append(phaseParameter.getCardinality().name()).append(',');
            if (phaseParameter.getAlias() != null) {
              writer.append('"').append(phaseParameter.getAlias()).append('"');
            } else {
              writer.append("null");
            }
            writer.append(')');
          } else {
            writer.append("new ").
                append(CONTEXTUAL_PARAMETER).append('(').
                append('"').append(parameter.getName()).append('"').append(',').
                append(parameter.type).append(".class").
                append(')');
          }
        }
        writer.append(')');
        writer.append(");\n");

        //
        String dispatchType = DISPATCH_TYPE.get(method.getPhase());

        // Build list of invocation parameters, i.e phase+bean parameters
        ArrayList<ParameterMetaModel> parameters = new ArrayList<ParameterMetaModel>(method.getParameters().size());
        for (ParameterMetaModel parameter : method.getParameters()) {
          if (parameter instanceof PhaseParameterMetaModel || parameter instanceof BeanParameterMetaModel) {
            parameters.add(parameter);
          }
        }

        // We don't generate dispatch for event phase
        if (method.getPhase() != Phase.EVENT) {
          // Dispatch literal
          writer.append("public static ").append(dispatchType).append(" ").append(method.getName()).append("(");
          for (int i = 0;i < parameters.size();i++) {
            ParameterMetaModel parameter = parameters.get(i);
            if (i > 0) {
              writer.append(',');
            }
            writer.append(parameter.type).append(" ").append(parameter.getName());
          }
          writer.append(") { return Request.create").append(method.getPhase().getClass().getSimpleName()).append("Dispatch(").append(methodRef);
          switch (parameters.size()) {
            case 0:
              break;
            case 1:
              writer.append(",(Object)").append(parameters.get(0).getName());
              break;
            default:
              writer.append(",new Object[]{");
              for (int j = 0;j < parameters.size();j++) {
                if (j > 0) {
                  writer.append(",");
                }
                writer.append(parameters.get(j).getName());
              }
              writer.append('}');
              break;
          }
          writer.append("); }\n");
        }
      }

      //
      writer.append("public static final ").append(CONTROLLER_DESCRIPTOR).append(" DESCRIPTOR = new ").append(CONTROLLER_DESCRIPTOR).append("(");
      writer.append("TYPE,Arrays.<").append(METHOD_DESCRIPTOR).append("<?>>asList(");
      for (int j = 0;j < methods.size();j++) {
        if (j > 0) {
          writer.append(',');
        }
        writer.append("method_").append(Integer.toString(j));
      }
      writer.append(")");
      writer.append(");\n");

      // Close class
      writer.append("}\n");

      //
      env.info("Generated controller companion " + fqn + "_" + " as " + file.toUri());
    }
    catch (IOException e) {
      throw ControllerMetaModel.CANNOT_WRITE_CONTROLLER_COMPANION.failure(e, origin, controller.getHandle().getName());
    }
    finally {
      Tools.safeClose(writer);
    }
  }
}
TOP

Related Classes of juzu.impl.plugin.controller.metamodel.ControllerMetaModelPlugin

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.