Package ch.softappeal.yass.js

Source Code of ch.softappeal.yass.js.ModelGenerator$ServiceDesc

package ch.softappeal.yass.js;

import ch.softappeal.yass.Version;
import ch.softappeal.yass.core.remote.ContractId;
import ch.softappeal.yass.core.remote.MethodMapper;
import ch.softappeal.yass.serialize.fast.AbstractFastSerializer;
import ch.softappeal.yass.serialize.fast.ClassTypeHandler;
import ch.softappeal.yass.serialize.fast.JsFastSerializer;
import ch.softappeal.yass.serialize.fast.TypeDesc;
import ch.softappeal.yass.serialize.fast.TypeHandler;
import ch.softappeal.yass.util.Check;
import ch.softappeal.yass.util.Nullable;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

public final class ModelGenerator extends Generator { // $todo: review

  private final String rootPackage;
  private final Map<Integer, TypeHandler> id2typeHandler;
  private final Set<Class<?>> visitedClasses = new HashSet<>();
  private final Set<String> visitedPackages = new HashSet<>();
  private final List<String> types = new ArrayList<>();
  private final MethodMapper.Factory methodMapperFactory;

  private void generatePackage(final String aPackage) {
    if (visitedPackages.add(aPackage)) {
      generatePackage(aPackage.substring(0, aPackage.lastIndexOf('.')));
      tabsln("contract.%s = {};", aPackage.substring(rootPackage.length()));
      println();
    }
  }

  private void checkType(final Class<?> type) {
    if (!type.getCanonicalName().startsWith(rootPackage)) {
      throw new RuntimeException('\'' + type.getCanonicalName() + "' has wrong root package");
    }
    generatePackage(type.getPackage().getName());
  }

  private String jsType(final Class<?> type) {
    return "contract." + type.getCanonicalName().substring(rootPackage.length());
  }

  private void generateEnum(final Class<? extends Enum<?>> type) {
    final String jsType = jsType(type);
    tabsln("%s = yass.enumConstructor();", jsType);
    for (final Enum<?> e : type.getEnumConstants()) {
      tabsln("%s.%s = new %s(%s, \"%s\");", jsType, e.name(), jsType, e.ordinal(), e.name());
    }
    tabsln("yass.enumDesc(%s, %s);", getId(type), jsType);
    types.add(jsType);
    println();
  }

  private void generateClass(final Class<?> type) {
    if (!visitedClasses.add(type)) {
      return;
    }
    checkType(type);
    Class<?> s = type.getSuperclass();
    if ((s == Object.class) || (s == Exception.class) || (s == RuntimeException.class) || (s == Error.class) || (s == Throwable.class)) {
      s = null;
    } else {
      generateClass(s);
    }
    final List<Field> fields = AbstractFastSerializer.ownFields(type);
    Collections.sort(fields, new Comparator<Field>() {
      @Override public int compare(final Field f1, final Field f2) {
        return f1.getName().compareTo(f2.getName());
      }
    });
    final String jsType = jsType(type);
    tabsln("%s = function () {", jsType);
    inc();
    for (final Field field : fields) {
      if (s != null) {
        tabsln("%s.call(this);", jsType(s));
      }
      tabsln("this.%s = null;", field.getName());
    }
    dec();
    tabsln("};");
    tabsln("yass.inherits(%s, %s);", jsType, (s == null) ? "yass.Class" : jsType(s));
    final Integer id = getId(type);
    if (id != null) {
      tabsln("yass.classDesc(%s, %s);", id, jsType);
      types.add(jsType);
    }
    println();
  }

  private void generateFields(final ClassTypeHandler typeHandler) {
    for (final ClassTypeHandler.FieldDesc fieldDesc : typeHandler.fieldDescs()) {
      final TypeHandler fieldHandler = fieldDesc.handler.typeHandler();
      final String typeDescOwner;
      if (TypeDesc.LIST.handler == fieldHandler) {
        typeDescOwner = "yass.LIST";
      } else if (JsFastSerializer.BOOLEAN_TYPEDESC.handler == fieldHandler) {
        typeDescOwner = "yass.BOOLEAN";
      } else if (JsFastSerializer.INTEGER_TYPEDESC.handler == fieldHandler) {
        typeDescOwner = "yass.INTEGER";
      } else if (JsFastSerializer.STRING_TYPEDESC.handler == fieldHandler) {
        typeDescOwner = "yass.STRING";
      } else if (fieldHandler == null) {
        typeDescOwner = "null";
      } else {
        typeDescOwner = jsType(fieldHandler.type);
      }
      tabsln(
        "yass.classField(%s, %s, \"%s\", %s);",
        jsType(typeHandler.type), fieldDesc.id, fieldDesc.handler.field.getName(), typeDescOwner
      );
    }
    println();
  }

  private static final class ServiceDesc {
    final String name;
    final ContractId<?> contractId;
    ServiceDesc(final String name, final ContractId<?> contractId) {
      this.name = name;
      this.contractId = contractId;
    }
  }

  private static List<ServiceDesc> getServiceDescs(final Class<?> services) throws Exception {
    final List<ServiceDesc> serviceDescs = new ArrayList<>();
    for (final Field field : services.getFields()) {
      if (Modifier.isStatic(field.getModifiers()) && (field.getType() == ContractId.class)) {
        serviceDescs.add(new ServiceDesc(field.getName(), (ContractId<?>)field.get(null)));
      }
    }
    Collections.sort(serviceDescs, new Comparator<ServiceDesc>() {
      @Override public int compare(final ServiceDesc serviceDesc1, final ServiceDesc serviceDesc2) {
        return ((Integer)serviceDesc1.contractId.id).compareTo((Integer)serviceDesc2.contractId.id);
      }
    });
    return serviceDescs;
  }

  private static Set<Class<?>> getInterfaces(final Class<?> services) throws Exception {
    final List<ServiceDesc> serviceDescs = getServiceDescs(services);
    final Set<Class<?>> interfaces = new HashSet<>();
    for (final ServiceDesc serviceDesc : serviceDescs) {
      interfaces.add(serviceDesc.contractId.contract);
    }
    return interfaces;
  }

  private static Method[] getMethods(final Class<?> type) {
    final Method[] methods = type.getMethods();
    Arrays.sort(methods, new Comparator<Method>() {
      @Override public int compare(final Method method1, final Method method2) {
        return method1.getName().compareTo(method2.getName());
      }
    });
    return methods;
  }

  private void generateServices(final Class<?> services) throws Exception {
    tabs("%s = {", jsType(services));
    inc();
    boolean first = true;
    for (final ServiceDesc serviceDesc : getServiceDescs(services)) {
      final Class<?> type = serviceDesc.contractId.contract;
      if (!first) {
        print(",");
      }
      first = false;
      println();
      tabs("%s: yass.contractId(%s, %s_MAPPER)", serviceDesc.name, serviceDesc.contractId.id, jsType(type));
    }
    dec();
    println();
    tabsln("};");
    println();
  }

  private void generateInterface(final Class<?> type) {
    checkType(type);
    final Method[] methods = getMethods(type);
    tabs("%s = {", jsType(type));
    inc();
    final MethodMapper methodMapper = methodMapperFactory.create(type);
    boolean firstMethod = true;
    for (final Method method : methods) {
      if (firstMethod) {
        firstMethod = false;
      } else {
        print(",");
      }
      println();
      tabs("%s: function (", method.getName());
      int param = 0; // $todo: use Parameter.getName() in Java 8
      for (final Class<?> paramType : method.getParameterTypes()) {
        if (param != 0) {
          print(", ");
        }
        print("param%s", param++);
      }
      print(") {}");
      if (methodMapper.mapMethod(method).oneWay) {
        print(" // OneWay");
      }
    }
    println();
    dec();
    tabsln("};");
    println();
    tabs("%s_MAPPER = yass.methodMapper(", jsType(type));
    inc();
    boolean first = true;
    for (final Method method : getMethods(type)) {
      if (!first) {
        print(",");
      }
      first = false;
      println();
      final MethodMapper.Mapping mapping = methodMapper.mapMethod(method);
      tabs("yass.methodMapping(\"%s\", %s, %s)", mapping.method.getName(), mapping.id, mapping.oneWay);
    }
    println();
    dec();
    tabsln(");");
    println();
  }

  @Nullable private Integer getId(final Class<?> type) {
    for (final Map.Entry<Integer, TypeHandler> entry : id2typeHandler.entrySet()) {
      if (entry.getValue().type == type) {
        return entry.getKey();
      }
    }
    return null;
  }

  /**
   * @param methodMapperFactory Note: You must provide a factory that doesn't allow overloading due to JavaScript restrictions!
   */
  @SuppressWarnings("unchecked")
  public ModelGenerator(
    final Package rootPackage, final JsFastSerializer serializer, final MethodMapper.Factory methodMapperFactory,
    final String modelModule, final String modelFile
  ) throws Exception {
    super(modelFile);
    visitedPackages.add(rootPackage.getName());
    this.rootPackage = rootPackage.getName() + '.';
    this.methodMapperFactory = Check.notNull(methodMapperFactory);
    id2typeHandler = serializer.id2typeHandler();
    println("// This file has been generated by yass.");
    println();
    tabsln("var %s = (function (yass) {", Check.notNull(modelModule));
    inc();
    tabsln("'use strict';");
    tabsln("var contract = {};");
    tabsln("contract.YASS_VERSION = '%s';", Version.VALUE);
    println();
    for (final Map.Entry<Integer, TypeHandler> entry : id2typeHandler.entrySet()) {
      final TypeHandler typeHandler = entry.getValue();
      final Class<?> type = typeHandler.type;
      if (type.isEnum()) {
        generateEnum((Class<Enum<?>>)type);
      } else if (typeHandler instanceof ClassTypeHandler) {
        generateClass(type);
      }
    }
    final Class<?> clientServices = Class.forName(this.rootPackage + "ClientServices");
    final Class<?> serverServices = Class.forName(this.rootPackage + "ServerServices");
    final Set<Class<?>> interfacesSet = getInterfaces(clientServices);
    interfacesSet.addAll(getInterfaces(serverServices));
    final List<Class<?>> interfacesList = new ArrayList<>(interfacesSet);
    Collections.sort(interfacesList, new Comparator<Class<?>>() {
      @Override public int compare(final Class<?> type1, final Class<?> type2) {
        return type1.getCanonicalName().compareTo(type2.getCanonicalName());
      }
    });
    for (final Class<?> type : interfacesList) {
      generateInterface(type);
    }
    generateServices(clientServices);
    generateServices(serverServices);
    for (final Map.Entry<Integer, TypeHandler> entry : id2typeHandler.entrySet()) {
      final TypeHandler typeHandler = entry.getValue();
      if (typeHandler instanceof ClassTypeHandler) {
        generateFields((ClassTypeHandler)typeHandler);
      }
    }
    tabs("contract.SERIALIZER = yass.serializer(");
    inc();
    boolean first = true;
    for (final String type : types) {
      if (!first) {
        print(",");
      }
      first = false;
      println();
      tabs(type);
    }
    println();
    dec();
    tabsln(");");
    println();
    tabsln("return contract;");
    dec();
    tabsln("}(yass));");
    close();
  }

}
TOP

Related Classes of ch.softappeal.yass.js.ModelGenerator$ServiceDesc

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.