getBestConstructorsFor method

List<ConstructorReflection<O>> getBestConstructorsFor({
  1. Iterable<String> requiredParameters = const <String>[],
  2. Iterable<String> optionalParameters = const <String>[],
  3. Iterable<String> nullableParameters = const <String>[],
  4. Iterable<String> presentParameters = const <String>[],
  5. bool allowEmptyConstructors = true,
  6. bool allowOptionalOnlyConstructors = true,
  7. bool jsonName = false,
})

Returns a List of the best ConstructorReflection for requiredParameters, optionalParameters, nullableParameters and presentParameters.

Implementation

List<ConstructorReflection<O>> getBestConstructorsFor(
    {Iterable<String> requiredParameters = const <String>[],
    Iterable<String> optionalParameters = const <String>[],
    Iterable<String> nullableParameters = const <String>[],
    Iterable<String> presentParameters = const <String>[],
    bool allowEmptyConstructors = true,
    bool allowOptionalOnlyConstructors = true,
    bool jsonName = false}) {
  if (nullableParameters is! List && nullableParameters is! Set) {
    nullableParameters = nullableParameters.toList(growable: false);
  }

  var constructors = allConstructors().toList();
  if (constructors.isEmpty) return <ConstructorReflection<O>>[];

  if (!allowEmptyConstructors) {
    var emptyConstructors =
        constructors.where((c) => c.parametersLength == 0).toList();

    if (emptyConstructors.isNotEmpty) {
      constructors =
          constructors.where((c) => !emptyConstructors.contains(c)).toList();
    }

    if (constructors.isEmpty) return <ConstructorReflection<O>>[];
  }

  if (!allowOptionalOnlyConstructors) {
    var optionalOnlyConstructors = constructors
        .where((c) => (c.normalParameters.isEmpty &&
            c.optionalParameters.none((c) => c.required) &&
            c.namedParameters.values.none((c) => c.required)))
        .toList();

    if (optionalOnlyConstructors.isNotEmpty) {
      constructors = constructors
          .where((c) => !optionalOnlyConstructors.contains(c))
          .toList();
    }

    if (constructors.isEmpty) return <ConstructorReflection<O>>[];
  }

  var presentParametersResolved = presentParameters.toSet();

  String paramNameResolver(ParameterReflection p, String name) {
    var f = field(p.name);
    var alias = f?.jsonFieldAliasAnnotations.alias;
    return alias ?? name;
  }

  var paramNameResolverJson = jsonName ? paramNameResolver : null;

  var invalidConstructors = constructors.where((c) {
    var paramsRequired = c
        .parametersNamesWhere((p) => p.required && !p.nullable,
            jsonName: jsonName, nameResolver: paramNameResolverJson)
        .toList();
    return _elementsInCount(
            presentParameters, paramsRequired, _nameNormalizer) <
        paramsRequired.length;
  }).toList();

  if (invalidConstructors.isNotEmpty) {
    constructors =
        constructors.where((c) => !invalidConstructors.contains(c)).toList();

    if (constructors.isEmpty) return <ConstructorReflection<O>>[];
  }

  if (requiredParameters.isNotEmpty) {
    var constructorsWithRequired = constructors.where((c) {
      var paramsAll = c
          .parametersNamesWhere((p) => true,
              jsonName: jsonName, nameResolver: paramNameResolverJson)
          .toList();
      return _elementsInCount<String>(
              requiredParameters, paramsAll, _nameNormalizer) ==
          requiredParameters.length;
    }).toList();

    constructors = constructorsWithRequired;
    if (constructors.isEmpty) return <ConstructorReflection<O>>[];

    presentParametersResolved.addAll(requiredParameters);
  }

  if (nullableParameters.isNotEmpty) {
    var constructorsWithNullables = constructors.where((c) {
      var paramsNullable = c
          .parametersNamesWhere((p) => p.nullable || !p.required,
              jsonName: jsonName, nameResolver: paramNameResolverJson)
          .toList();
      return _elementsInCount(
              nullableParameters, paramsNullable, _nameNormalizer) ==
          nullableParameters.length;
    }).toList();

    constructors = constructorsWithNullables;
    if (constructors.isEmpty) return <ConstructorReflection<O>>[];

    presentParametersResolved.addAll(nullableParameters);
  }

  presentParametersResolved.addAll(optionalParameters);

  constructors = constructors.where((c) {
    var paramsRequired = c
        .parametersNamesWhere((p) => p.required,
            jsonName: jsonName, nameResolver: paramNameResolverJson)
        .toList();
    return _elementsInCount<String>(
            presentParametersResolved, paramsRequired, _nameNormalizer) ==
        paramsRequired.length;
  }).toList();

  if (constructors.length <= 1) {
    return constructors;
  }

  var constructorsInfo = Map.fromEntries(constructors.map((c) {
    var requiredCount =
        c.getParametersByNames(requiredParameters, jsonName: jsonName).length;
    var optionalCount =
        c.getParametersByNames(optionalParameters, jsonName: jsonName).length;
    return MapEntry(c, [requiredCount, optionalCount]);
  }));

  constructors.sort((c1, c2) {
    var i1 = constructorsInfo[c1]!;
    var i2 = constructorsInfo[c2]!;

    var req1 = i1[0];
    var req2 = i2[0];

    var cmp = req2.compareTo(req1);
    if (cmp == 0) {
      var opt1 = i1[1];
      var opt2 = i2[1];
      cmp = opt2.compareTo(opt1);
    }
    return cmp;
  });

  return constructors;
}