// say, we have a type Type<(1) T1, (2) T2> with the method of the following signature:
        // T3[,][] Meth<(3) T3>(T1 o1, Func<T1, T3> o2) that has the following public type points:
        //
        // ReturnValue = T3 (a)[,][]
        // Args = T1 (b), Func<T1 (c), T3 (d)>
        // 
        // to be perfectly fine we need the following typelinks to be built:
        // (3) <-> (a), (1) <-> (b), (1) <-> (c), (3) <-> (d)
        // 
        // after some analysis we build the following mapping:
        // (key <- value)
        // a0 <- t[0]
        // a1[0] <- t[0]
        // a1[1] <- m[0]
        // ret[0][0] <- m[0]

        public static Dictionary<String, String> GetGenericArgsMapping(this MethodInfo method)
        {
            if (method == null)
            {
                return null;
            }
            else
            {
                var pattern = (MethodInfo)method.Module.ResolveMethod(method.MetadataToken);
                var methodGenericArgs = pattern.XGetGenericArguments();
                var typeGenericArgs = pattern.DeclaringType.XGetGenericArguments();

                var mapping = new Dictionary<String, String>();
                mapping.AddElements(AppendMethodMapping(pattern, methodGenericArgs, "m"));
                mapping.AddElements(AppendMethodMapping(pattern, typeGenericArgs, "t"));
                return mapping;
            }
        }
        private static Dictionary<String, String> AppendMethodMapping(MethodInfo method, IEnumerable<Type> types, String prefix)
        {
            var mapping = new Dictionary<String, String>();
            mapping.AddElements(GetGenericArgsMapping(method.ReturnType, types, "ret", prefix));
            for (var i = 0; i < method.GetParameters().Length; ++i)
            {
                var parameterType = method.GetParameters()[i].ParameterType;
                mapping.AddElements(GetGenericArgsMapping(parameterType, types, "a" + i, prefix));
            }

            return mapping;
        }
Example #3
0
        protected CommandLineConfig(String[] s_args)
        {
            Log = Logger.Get(GetType());
            Log.MinLevel = Level.Info;

            if (s_args.Count() == 1 && s_args[0] == "/?")
            {
                Banners.About();
                throw new ConfigException(String.Empty);
            }
            else
            {
                if (s_args.LastOrDefault() == "/verbose")
                {
                    IsVerbose = true;
                    s_args = s_args.SkipLast().ToArray();

                    Info.WriteLine("Detected the \"/verbose\" switch, entering verbose mode.");
                    Log.MinLevel = Level.Debug;

                    Debug.EnsureBlankLine();
                    Debug.Write("Command line args are: ");
                    if (s_args.IsEmpty()) Debug.WriteLine("empty");
                    else Debug.WriteLine("({0} arg{1})", s_args.Count(), s_args.Count() == 1 ? "" : "s");
                    s_args.ForEach((arg, i) => Debug.WriteLine("{0}: {1}", i + 1, arg));
                }

                Debug.EnsureBlankLine();
                Debug.WriteLine("Pre-parsing arguments...");
                var named_args = new Dictionary<String, String>();
                var shortcut_args = new List<String>();
                foreach (var s_arg in s_args)
                {
                    var m = s_arg.Parse("^-(?<name>.*?):(?<value>.*)$");
                    var name = m != null ? m["name"] : null;
                    var value = m != null ? m["value"] : s_arg;
                    if (m != null) Debug.WriteLine("Parsed \"{0}\" as name/value pair: {1} => \"{2}\".", s_arg, name, value);
                    else Debug.WriteLine("Parsed \"{0}\" as raw value.", s_arg);

                    if (name == null)
                    {
                        if (named_args.IsNotEmpty()) throw new ConfigException("Fatal error: shortcut arguments must be specified before named arguments.");
                        else shortcut_args.Add(value);
                    }
                    else
                    {
                        if (named_args.ContainsKey(name)) throw new ConfigException("Fatal error: duplicate argument \"{0}\".", name);
                        else named_args.Add(name, value);
                    }
                }
                Debug.WriteLine("Pre-parse completed: found {0} named argument{1} and {2} shortcut argument{3}.",
                    named_args.Count(), named_args.Count() == 1 ? "" : "s",
                    shortcut_args.Count(), shortcut_args.Count() == 1 ? "" : "s");

                Debug.EnsureBlankLine();
                Debug.WriteLine("Parsing arguments...");

                var parsed_args = new Dictionary<PropertyInfo, Object>();
                if (named_args.IsNotEmpty())
                {
                    Debug.WriteLine("Parsing named arguments...");
                    parsed_args = ParseArgs(named_args);
                }

                if (shortcut_args.IsNotEmpty())
                {
                    Debug.WriteLine("Parsing shortcut arguments...");

                    Dictionary<PropertyInfo, Object> parsed_shortcut_args = null;
                    var shortcuts = GetType().Attrs<ShortcutAttribute>().OrderBy(shortcut => shortcut.Priority);
                    var bind_errors = new Dictionary<String, String>();
                    foreach (var shortcut in shortcuts)
                    {
                        Debug.WriteLine("Considering shortcut schema \"{0}\"...", shortcut.Schema);
                        var words = shortcut.Schema.SplitWords();
                        if (words.Count() != shortcut_args.Count())
                        {
                            var message = String.Format("argument count mismatch.");
                            bind_errors.Add(shortcut.Schema, message);
                            Debug.WriteLine("Schema \"{0}\" won't work: {1}", shortcut.Schema, message);
                            continue;
                        }

                        try { parsed_shortcut_args = ParseArgs(words.Zip(shortcut_args).ToDictionary(t => t.Item1, t => t.Item2)); }
                        catch (ConfigException cex)
                        {
                            bind_errors.Add(shortcut.Schema, cex.Message);
                            Debug.WriteLine(cex.Message);
                            Debug.WriteLine("Schema \"{0}\" won't work: failed to parse arguments.", shortcut.Schema);

                            continue;
                        }

                        var dupes = Set.Intersect(parsed_args.Keys, parsed_shortcut_args.Keys);
                        if (dupes.IsNotEmpty())
                        {
                            var a = dupes.AssertFirst().Attr<ParamAttribute>();
                            var name = named_args.Keys.Single(k => a.Aliases.Contains(k));
                            Debug.WriteLine("Schema \"{0}\" won't work: shortcut argument duplicates parsed argument \"{1}\".", shortcut.Schema, name);
                            parsed_shortcut_args = null;
                            continue;
                        }
                        else
                        {
                            Debug.WriteLine("Schema \"{0}\" works fine, skipping other schemas (if any).", shortcut.Schema);
                            break;
                        }
                    }

                    if (parsed_shortcut_args == null)
                    {
                        var message = "Fatal error: failed to match the shortcuts.";
                        var bind_summary = bind_errors.Select(kvp => String.Format("Failed to match \"{0}\": {1}", kvp.Key, kvp.Value.Replace("Fatal error: ", String.Empty))).StringJoin(Environment.NewLine);
                        if (!IsVerbose) message = message + Environment.NewLine + bind_summary;
                        throw new ConfigException(message);
                    }
                    else
                    {
                        parsed_args.AddElements(parsed_shortcut_args);
                    }
                }

                Debug.WriteLine("Parse completed.");
                Debug.EnsureBlankLine();
                Debug.WriteLine("Setting configuration parameters...");
                var props = this.GetType().GetProperties(BF.AllInstance).Where(p => p.HasAttr<ParamAttribute>()).OrderBy(p => p.Attr<ParamAttribute>().Priority);
                props.ForEach(p =>
                {
                    if (parsed_args.ContainsKey(p))
                    {
                        var value = parsed_args[p];
                        Debug.WriteLine("Resolved %{0} as {1}.", p.Name, value.ToLog());
                        p.SetValue(this, value, null);
                    }
                    else
                    {
                        var p_default = this.GetType().GetProperty("Default" + p.Name, BF.AllStatic);
                        if (p_default == null) throw new ConfigException("Fatal error: parameter \"{0}\" must be specified.");

                        var value = p_default.GetValue(null, null);
                        Debug.WriteLine("Defaulted %{0} to {1}.", p.Name, value.ToLog());
                        p.SetValue(this, value, null);
                    }
                });
                Debug.WriteLine("Configuration parameters successfully set.");
            }
        }
 public static ReadOnlyDictionary<MethodBase, MethodBase> MapInterfacesToImpls(this Type t, IEnumerable<Type> ifaces)
 {
     var map = new Dictionary<MethodBase, MethodBase>();
     ifaces.ForEach(iface => map.AddElements(t.MapInterfacesToImpls(iface)));
     return map.ToReadOnly();
 }