private bool CheckGenericTypeForMatch(Type typeToFormat, MorestachioFormatterModel formatTemplateElement) { var typeToFormatGenerics = typeToFormat.GetTypeInfo().GetGenericArguments(); //explicit check for array support if (typeToFormat.HasElementType) { var elementType = typeToFormat.GetElementType(); typeToFormatGenerics = typeToFormatGenerics.Concat(new[] { elementType }).ToArray(); } //the type check has maybe failed because of generic parameter. Check if both the formatter and the typ have generic arguments var formatterGenerics = formatTemplateElement.InputType.GetTypeInfo().GetGenericArguments(); if (typeToFormatGenerics.Length <= 0 || formatterGenerics.Length <= 0 || typeToFormatGenerics.Length != formatterGenerics.Length) { return(false); } return(true); }
/// <inheritdoc /> public virtual MorestachioFormatterModel Add(MethodInfo method, MorestachioFormatterAttribute morestachioFormatterAttribute) { morestachioFormatterAttribute.ValidateFormatter(method); var arguments = morestachioFormatterAttribute.GetParameters(method); var name = morestachioFormatterAttribute.GetFormatterName(method);; var morestachioFormatterModel = new MorestachioFormatterModel(name, morestachioFormatterAttribute.Description, arguments.FirstOrDefault(e => e.IsSourceObject)?.ParameterType ?? typeof(object), morestachioFormatterAttribute.OutputType ?? method.ReturnType, method.GetCustomAttributes <MorestachioFormatterInputAttribute>() .Select(e => new InputDescription(e.Description, e.OutputType, e.Example)).ToArray(), morestachioFormatterAttribute.ReturnHint, method, new MultiFormatterInfoCollection(arguments), !morestachioFormatterAttribute.IsSourceObjectAware, morestachioFormatterAttribute.LinkFunctionTarget); name = name ?? "{NULL}"; if (!Formatters.TryGetValue(name, out var formatters)) { formatters = new List <MorestachioFormatterModel>(); Formatters[name] = formatters; } formatters.Add(morestachioFormatterModel); return(morestachioFormatterModel); }
/// <summary> /// Composes the values into a Dictionary for each formatter. If returns null, the formatter will not be called. /// </summary> public virtual FormatterComposingResult ComposeValues( [NotNull] MorestachioFormatterModel formatter, [CanBeNull] object sourceObject, [NotNull] MethodInfo method, [NotNull] params KeyValuePair <string, object>[] templateArguments) { Log(() => $"Compose values for object '{sourceObject}' with formatter '{formatter.InputType}' targets '{formatter.Function.Name}'"); var values = new Dictionary <MultiFormatterInfo, object>(); var matched = new Dictionary <MultiFormatterInfo, Tuple <string, object> >(); var templateArgumentsQueue = templateArguments.Select(e => Tuple.Create(e.Key, e.Value)).ToList(); var argumentIndex = 0; foreach (var parameter in formatter.MetaData.Where(e => !e.IsRestObject)) { argumentIndex++; Log(() => $"Match parameter '{parameter.ParameterType}' [{parameter.Name}]"); object givenValue; //set ether the source object or the value from the given arguments if (parameter.IsSourceObject) { Log(() => "Is Source object"); givenValue = sourceObject; } else { //match by index or name Log(() => "Try Match by Name"); //match by name var match = templateArgumentsQueue.FirstOrDefault(e => !string.IsNullOrWhiteSpace(e.Item1) && e.Item1.Equals(parameter.Name)); if (match == null) { Log(() => "Try Match by Index"); //match by index var index = 0; match = templateArgumentsQueue.FirstOrDefault(g => index++ == parameter.Index); } if (match == null) { if (parameter.IsOptional) { givenValue = null; match = new Tuple <string, object>(parameter.Name, null); } else { Log(() => $"Skip: Could not match the parameter at index '{parameter.Index}' nether by name nor by index"); return(default);
/// <summary> /// Executes the specified formatter. /// </summary> /// <param name="formatter">The formatter.</param> /// <param name="sourceObject">The source object.</param> /// <param name="templateArguments">The template arguments.</param> /// <returns></returns> public virtual async Task <object> Execute(MorestachioFormatterModel formatter, object sourceObject, params KeyValuePair <string, object>[] templateArguments) { var values = ComposeValues(formatter, sourceObject, formatter.Function, templateArguments); if (values.Equals(new FormatterComposingResult())) { Log(() => "Skip: Execute skip as Compose Values returned an invalid value"); return(FormatterFlow.Skip); } Log(() => "Execute"); var taskAlike = values.MethodInfo.Invoke(formatter.FunctionTarget, values.Arguments.Select(e => e.Value).ToArray()); return(await taskAlike.UnpackFormatterTask()); }
/// <summary> /// Composes the values into a Dictionary for each formatter. If returns null, the formatter will not be called. /// </summary> public virtual FormatterComposingResult ComposeValues([NotNull] MorestachioFormatterModel formatter, [CanBeNull] object sourceObject, [NotNull] MethodInfo method, [NotNull] ServiceCollection services, [NotNull] params KeyValuePair <string, object>[] templateArguments) { Log(() => $"Compose values for object '{sourceObject}' with formatter '{formatter.InputType}' targets '{formatter.Function.Name}'"); var values = new Dictionary <MultiFormatterInfo, object>(); var matched = new Dictionary <MultiFormatterInfo, Tuple <string, object> >(); var templateArgumentsQueue = templateArguments.Select(e => Tuple.Create(e.Key, e.Value)).ToList(); var argumentIndex = 0; foreach (var parameter in formatter.MetaData.Where(e => !e.IsRestObject)) { argumentIndex++; Log(() => $"Match parameter '{parameter.ParameterType}' [{parameter.Name}]"); object givenValue; //set ether the source object or the value from the given arguments if (parameter.IsSourceObject) { Log(() => "Is Source object"); givenValue = sourceObject; } else if (parameter.IsInjected) { //match by index or name Log(() => "Get the injected service"); if (services.GetService(parameter.ParameterType, out var service)) { if (service is Delegate factory) { service = factory.DynamicInvoke(); } givenValue = service; if (!parameter.ParameterType.IsInstanceOfType(givenValue)) { Log(() => $"Expected service of type '{parameter.ParameterType}' but got '{givenValue}'"); return(default);
/// <summary> /// Composes the values into a Dictionary for each formatter. If returns null, the formatter will not be called. /// </summary> public virtual PrepareFormatterComposingResult PrepareComposeValues(MorestachioFormatterModel formatter, Type sourceType, MethodInfo method, ServiceCollection services, FormatterArgumentType[] templateArguments, ParserOptions parserOptions) { Log(parserOptions, () => $"Compose values for object '{sourceType}' with formatter '{formatter.InputType}' targets '{formatter.Function.Name}'"); var matched = new Dictionary <MultiFormatterInfo, FormatterArgumentMap>(); for (var i = 0; i < formatter.MetaData.NonParamsArguments.Count; i++) { var parameter = formatter.MetaData.NonParamsArguments[i]; Log(parserOptions, () => $"Match parameter '{parameter.ParameterType}' [{parameter.Name}]"); //set ether the source object or the value from the given arguments FormatterArgumentType match; if (parameter.IsSourceObject) { Log(parserOptions, () => "Is Source object"); matched[parameter] = new FormatterArgumentMap(i, null) { ObtainValue = (source, args) => source }; match = new FormatterArgumentType(i, (string)null, sourceType); } else { if (parameter.IsInjected) { //match by index or name Log(parserOptions, () => "Get the injected service"); if (services.TryGetService(parameter.ParameterType, out var service)) { if (!parameter.ParameterType.IsInstanceOfType(service)) { Log(parserOptions, () => $"Expected service of type '{parameter.ParameterType}' but got '{service}'"); return(default);
/// <summary> /// Adds all formatter that are decorated with the <see cref="MorestachioFormatterAttribute"/> /// </summary> /// <param name="type">The type.</param> public void AddFromType(Type type) { foreach (var method in type.GetMethods(BindingFlags.Static | BindingFlags.Public)) { var hasFormatterAttr = method.GetCustomAttributes <MorestachioFormatterAttribute>(); foreach (var morestachioFormatterAttribute in hasFormatterAttr) { if (morestachioFormatterAttribute == null) { continue; } var morestachioFormatterModel = new MorestachioFormatterModel(morestachioFormatterAttribute.Name, morestachioFormatterAttribute.Description, method.GetParameters().FirstOrDefault()?.ParameterType, morestachioFormatterAttribute.OutputType ?? method.ReturnType, method.GetCustomAttributes <MorestachioFormatterInputAttribute>().Select(e => new InputDescription(e.Description, e.OutputType, e.Example)).ToArray(), morestachioFormatterAttribute.ReturnHint, method); GlobalFormatterModels.Add(morestachioFormatterModel); } } }