generateMethodOverride method

Future<List<String>> generateMethodOverride(
  1. MethodElement methodElement
)

Implementation

Future<List<String>> generateMethodOverride(MethodElement methodElement) async {
  TypeInfo returnType = typeMap.fromDartType(methodElement.returnType, context: typeArgumentsMap());
  List<String> lines = [];

  if (elementInjectionType(methodElement) == '@SubtypeFactory') {
    if ((methodElement.firstFragment.formalParameters[0].name != 'className') ||
        (methodElement.firstFragment.formalParameters[0].element.type.getDisplayString() != 'String')) {
      throw Exception('SubtypeFactory first argument needs to be named className and be of type String');
    }
    List<String> argsDef = [];
    List<String> argsInv = [];
    Map<String, TypeInfo> arguments = {};
    for (var p in methodElement.firstFragment.formalParameters) {
      TypeInfo argType = typeMap.fromDartType(p.element.type, context: typeArgumentsMap());
      argsDef.add('${argType.uniqueName} ${p.name!}');
      argsInv.add(p.name!);
      arguments[p.name!] = argType;
    }
    String factoryInv = 'createSubtypeOf${returnType.flatName}${argsInv.length}(${argsInv.join(',')})';
    String factoryDef = 'createSubtypeOf${returnType.flatName}${argsInv.length}(${argsDef.join(',')})';

    if (!typeMap.subtypeFactories.containsKey(factoryDef)) {
      typeMap.subtypeFactories[factoryDef] = SubtypeFactoryInfo(returnType, arguments);
    }
    lines.add('return \$om.$factoryInv;');
  } else if (elementInjectionType(methodElement) == '@Factory') {
    var best = typeMap.getBestCandidate(returnType);
    lines.add('//${returnType.fullName}');
    lines.add('return ${best.generateCreator()};');
  } else if (elementInjectionType(methodElement) == '@Compile') {
    lines.add('//compiled method');
    //TODO: add original method if not abstract ??
    List<CompiledFieldMethodPart> calledParts = [];
    for (var methodPartElement in allMethods()) {
      if (methodPartElement.name!.startsWith('_${methodElement.name!}')) {
        if (elementInjectionType(methodPartElement) == '@CompilePart') {
          String part = await methodPartElement.getSourceCode(typeMap.step);
          lines.add(part);
        } else if (elementInjectionType(methodPartElement) == '@CompileFieldsOfType') {
          //var fieldType = typeMap.fromDartType(methodPartElement.parameters.last.type, context:typeArgumentsMap());
          if (!typeMap.compiledMethodsByType.containsKey(methodElement)) {
            typeMap.compiledMethodsByType[methodElement] = {};
            lines.add('//dbg: ${methodElement.name!}');
          }

          //iterated trough fields with parent first to generate parent bits first and then subclasses bits.
          for (var field in allFields(parentFirst: true)) {
            var compiledPart = await CompiledFieldMethodPart.addFieldMethodPart(this, methodElement, methodPartElement, field, 'dis');
            if (compiledPart != null && !calledParts.contains(compiledPart)) {
              lines.add(compiledPart.getCallExpression('this'));
              calledParts.add(compiledPart);
            }
          }
          for (var plugin in plugins) {
            for (var field in plugin.allFields()) {
              var compiledPart = await CompiledFieldMethodPart.addFieldMethodPart(this, methodElement, methodPartElement, field, 'dis.decorated');
              if (compiledPart != null && !calledParts.contains(compiledPart)) {
                lines.add(compiledPart.getCallExpression('this.${plugin.varName}'));
                calledParts.add(compiledPart);
              }
            }
          }
        }
      }
    }
    //lines.add(methodElement.computeNode().body.toSource());
  } else {
    //check if method has any plugins
    Map<MethodElement, TypeInfo> beforePlugins = {};
    Map<MethodElement, TypeInfo> afterPlugins = {};
    for (var p in plugins) {
      p.allMethods().forEach((pluginMethodElement) {
        if (elementInjectionType(pluginMethodElement) == '@MethodPlugin') {
          if (pluginMethodElement.name == "before${methodElement.name!.substring(0, 1).toUpperCase()}${methodElement.name!.substring(1)}") {
            beforePlugins[pluginMethodElement] = p;
          } else if (pluginMethodElement.name == "after${methodElement.name!.substring(0, 1).toUpperCase()}${methodElement.name!.substring(1)}") {
            afterPlugins[pluginMethodElement] = p;
          }
        }
      });
    }

    String paramsStr = methodElement.firstFragment.formalParameters.map((mp) => mp.name).join(",");
    String beforeArgsStr = '';
    if (beforePlugins.isNotEmpty) {
      lines.add('List<dynamic> args = [$paramsStr];');
      int i = 0;
      beforeArgsStr = methodElement.firstFragment.formalParameters.map((mp) => "args[${i++}]").join(',');
    }
    for (var pluginMethod in beforePlugins.keys) {
      String pluginName = beforePlugins[pluginMethod]!.varName;
      lines.add('args = ${pluginMethod.firstFragment.isAsynchronous ? 'await' : ''} $pluginName.${pluginMethod.name}($beforeArgsStr);');
    }
    if (beforePlugins.isNotEmpty) {
      int i = 0;
      for (var mp in methodElement.firstFragment.formalParameters) {
        lines.add('${mp.name} = args[$i];');
        i++;
      }
    }
    bool isVoid = (methodElement.returnType is VoidType) || (methodElement.returnType.getDisplayString() == 'Future<void>');
    if (beforePlugins.length + afterPlugins.length > 0) {
      lines.add('${!isVoid ? 'var ret = ' : ''}${methodElement.firstFragment.isAsynchronous ? 'await ' : ''}super.${methodElement.name}($paramsStr);');
    }
    for (var pluginMethod in afterPlugins.keys) {
      String pluginName = afterPlugins[pluginMethod]!.varName;
      //add params to after plugin, TODO: validate if names match original method params
      List<String> params = pluginMethod.firstFragment.formalParameters.map((mp) => mp.name!).toList();
      if (!isVoid) {
        params.first = 'ret';
      }
      lines.add(
        '${!isVoid ? 'ret = ' : ''}${pluginMethod.firstFragment.isAsynchronous ? 'await ' : ''}$pluginName.${pluginMethod.name}(${params.join(',')});',
      );
    }
    if (!isVoid && (beforePlugins.length + afterPlugins.length > 0)) {
      lines.add('return ret;');
    }
  }
  return lines;
}