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;
}