| File: SubComponent.cs | Web Access |
| Project: src\test\Microsoft.ML.TestFramework\Microsoft.ML.TestFramework.csproj (Microsoft.ML.TestFramework) |
// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. using System; using System.Text; using Microsoft.ML.CommandLine; using Microsoft.ML.Internal.Utilities; using Microsoft.ML.Runtime; namespace Microsoft.ML.TestFramework { using Conditional = System.Diagnostics.ConditionalAttribute; /// <summary> /// This class is used to represent the command line encoding of a component plus its /// settings. Typically, these settings will be parsed at a later time. Parsing at /// the parent level merely gathers the settings into an instance of SubComponent. /// </summary> [Serializable] public class SubComponent : IEquatable<SubComponent> { private static readonly string[] _empty = new string[0]; private string _kind; private string[] _settings; /// <summary> /// The type/kind of sub-component. This string will never be null, but may be empty. /// </summary> public string Kind { get { return _kind; } set { _kind = value ?? ""; } } /// <summary> /// The settings strings for the sub-component. This array will never be null, but may be empty. /// </summary> public string[] Settings { get { return _settings; } set { _settings = value ?? _empty; } } public string SubComponentSettings { get { return CmdParser.CombineSettings(_settings); } set { _settings = string.IsNullOrEmpty(value) ? _empty : new string[] { value }; } } /// <summary> /// It's generally better to use the IsGood() extension method. It handles null testing /// and empty testing. /// </summary> public bool IsEmpty { get { AssertValid(); return _kind.Length == 0 && _settings.Length == 0; } } public SubComponent() { _kind = ""; _settings = _empty; AssertValid(); } public SubComponent(string kind) { _kind = kind ?? ""; _settings = _empty; AssertValid(); } /// <summary> /// This assumes ownership of the settings array. /// </summary> public SubComponent(string kind, params string[] settings) { _kind = kind ?? ""; if (settings == null || settings.Length == 1 && string.IsNullOrEmpty(settings[0])) settings = _empty; _settings = settings; AssertValid(); } public SubComponent(string kind, string settings) { _kind = kind ?? ""; if (string.IsNullOrEmpty(settings)) _settings = _empty; else _settings = new string[] { settings }; AssertValid(); } [Conditional("DEBUG")] private void AssertValid() { Contracts.AssertValue(_kind); Contracts.AssertValue(_settings); } public bool Equals(SubComponent other) { if (other == null) return false; if (_kind != other._kind) return false; if (_settings.Length != other._settings.Length) return false; for (int i = 0; i < _settings.Length; i++) { if (_settings[i] != other._settings[i]) return false; } return true; } public override string ToString() { if (IsEmpty) return "{}"; if (_settings.Length == 0) return _kind; string str = CmdParser.CombineSettings(_settings); StringBuilder sb = new StringBuilder(); CmdQuoter.QuoteValue(str, sb, true); return _kind + sb.ToString(); } public override bool Equals(object obj) { SubComponent other = obj as SubComponent; if (other == null) return false; return Equals(other); } public override int GetHashCode() { int hash = Kind.GetHashCode(); for (int i = 0; i < Settings.Length; i++) hash = CombineHash(hash, Settings[i].GetHashCode()); return hash; } private static uint CombineHash(uint u1, uint u2) { return ((u1 << 7) | (u1 >> 25)) ^ u2; } private static int CombineHash(int n1, int n2) { return (int)CombineHash((uint)n1, (uint)n2); } private static void ParseCore(string str, out string kind, out string args) { kind = args = null; if (string.IsNullOrWhiteSpace(str)) return; str = str.Trim(); int ich = str.IndexOf('{'); if (ich < 0) { kind = str; return; } if (ich == 0 || str[str.Length - 1] != '}') throw Contracts.Except("Invalid SubComponent string: mismatched braces, or empty component name."); kind = str.Substring(0, ich); args = CmdLexer.UnquoteValue(str.Substring(ich)); } internal static SubComponent Parse(string str) { string kind; string args; ParseCore(str, out kind, out args); Contracts.AssertValueOrNull(kind); Contracts.AssertValueOrNull(args); return new SubComponent(kind, args); } internal static SubComponent<TRes, TSig> Parse<TRes, TSig>(string str) where TRes : class { string kind; string args; ParseCore(str, out kind, out args); Contracts.AssertValueOrNull(kind); Contracts.AssertValueOrNull(args); return new SubComponent<TRes, TSig>(kind, args); } public static SubComponent Create(Type type) { Contracts.Check(type != null && typeof(SubComponent).IsAssignableFrom(type)); return (SubComponent)Activator.CreateInstance(type); } public static SubComponent Clone(SubComponent src, Type type = null) { if (src == null) return null; var dst = Create(type ?? src.GetType()); dst._kind = src._kind; if (Utils.Size(src._settings) == 0) dst._settings = _empty; else dst._settings = (string[])src._settings.Clone(); return dst; } } [Serializable] public class SubComponent<TRes, TSig> : SubComponent where TRes : class { public SubComponent() : base() { } public SubComponent(string kind) : base(kind) { } public SubComponent(string kind, params string[] settings) : base(kind, settings) { } public TRes CreateInstance(IHostEnvironment env, params object[] extra) { string options = CmdParser.CombineSettings(Settings); TRes result; if (ComponentCatalog.TryCreateInstance<TRes, TSig>(env, out result, Kind, options, extra)) return result; throw Contracts.Except("Unknown loadable class: {0}", Kind).MarkSensitive(MessageSensitivity.None); } } public static class SubComponentExtensions { public static bool IsGood(this SubComponent src) { return src != null && !src.IsEmpty; } } }