示例#1
0
        /// <summary>
        /// Gets the assembly's version.
        /// </summary>
        /// <param name="assembly">The assembly to get the version from</param>
        /// <returns>The assembly version</returns>
        public static Version?GetVersion(Assembly assembly)
        {
            Conditions.RequireReference(assembly, nameof(assembly));
            Version?result = assembly.GetName(false).Version;

            return(result);
        }
示例#2
0
        /// <summary>
        /// Creates a new XmlSchemaSet from the XElements containing the schemas using the specified handler.
        /// </summary>
        /// <param name="schemaElements">The XML elements containing the schemas.</param>
        /// <param name="handler">The handler used to process errors and warnings.  This can be null,
        /// which means an XmlSchemaException will be thrown if an error occurs.</param>
        /// <returns>The new XmlSchemaSet instance.</returns>
        public static XmlSchemaSet CreateSchemaSet(IEnumerable <XElement> schemaElements, ValidationEventHandler?handler)
        {
            Conditions.RequireReference(schemaElements, nameof(schemaElements));

            // Unfortunately, XmlSchema is NOT thread-safe by itself because adding it to an XmlSchemaSet
            // will modify it.  To use it thread-safely, we have to create the XmlSchemaSet too, and even then
            // the resulting set is only thread-safe if it's used from a validating reader (which our Validate does).
            // http://blogs.msdn.com/b/marcelolr/archive/2009/03/16/xmlschema-and-xmlschemaset-thread-safety.aspx
            XmlSchemaSet result = new();

            foreach (XElement schemaElement in schemaElements)
            {
                using (XmlReader reader = schemaElement.CreateReader())
                {
                    XmlSchema?schema = XmlSchema.Read(reader, handler);
                    if (schema != null)
                    {
                        result.Add(schema);
                    }
                }
            }

            // Call this up front so it doesn't have to be implicitly called later.  Any time validation is done,
            // the schema must be compiled first.  Doing it here means later threads won't have to bother.
            result.Compile();
            return(result);
        }
示例#3
0
        /// <summary>
        /// Enumerates all of the exceptions in an exception chain including handling multiple
        /// inner exceptions for <see cref="AggregateException"/>.
        /// </summary>
        /// <param name="ex">The root exception to start from.  <paramref name="action"/> will be called for this too.</param>
        /// <param name="action">The action to invoke for the root exception and each inner exception.  This is passed the
        /// exception and its 0-based depth (where 0 is the root exception).</param>
        public static void ForEach(Exception ex, Action <Exception, int> action)
        {
            Conditions.RequireReference(ex, nameof(ex));
            Conditions.RequireReference(action, nameof(action));

            ForEach(ex, 0, null, (exception, depth, outer) => action(exception, depth));
        }
示例#4
0
        /// <summary>
        /// Tokenize the input string using the specified separator (e.g., comma), delimiter (e.g., double quote),
        /// and trimming options and add the output tokens to the specified collection.
        /// </summary>
        /// <param name="text">The text to tokenize.</param>
        /// <param name="separator">The token separator character.  Typically, a comma.</param>
        /// <param name="delimiter">The token quote character.  Typically, a double quote.
        /// This can be used to enclose tokens that contain the separator character.
        /// To use this character inside a token, use it two consecutive times.
        /// </param>
        /// <param name="trimTokens">Whether the resulting tokens should have whitespace trimmed off.</param>
        /// <param name="tokens">The collection to add the parsed tokens to.</param>
        /// <returns>True if all of the tokens were properly delimited (or were not delimited).
        /// False if the final token was open-delimited but never closed (which usually indicates a partial record).</returns>
        public static bool SplitIntoTokens(string text, char separator, char?delimiter, bool trimTokens, ICollection <string> tokens)
        {
            Conditions.RequireReference(tokens, nameof(tokens));
            bool result = SplitIntoTokens(text, separator, delimiter, token => tokens.Add(trimTokens ? token.Trim() : token));

            return(result);
        }
示例#5
0
        /// <summary>
        /// Enumerates all of the exceptions in an exception chain including handling multiple
        /// inner exceptions for <see cref="AggregateException"/>.
        /// </summary>
        /// <param name="ex">The root exception to start from.  <paramref name="action"/> will be called for this too.</param>
        /// <param name="action">The action to invoke for the root exception and each inner exception.  This is passed the
        /// exception, its 0-based depth (where 0 is the root exception), and the outer (i.e., parent) exception.</param>
        public static void ForEach(Exception ex, Action <Exception, int, Exception?> action)
        {
            Conditions.RequireReference(ex, nameof(ex));
            Conditions.RequireReference(action, nameof(action));

            ForEach(ex, 0, null, action);
        }
示例#6
0
        /// <summary>
        /// Validates the XML using the given schema set.
        /// </summary>
        /// <param name="xml">The XML to validate.</param>
        /// <param name="schemas">The schemas to use for validation.</param>
        /// <returns>A list of validation errors and warnings.</returns>
        public static IList <ValidationEventArgs> Validate(this XElement xml, XmlSchemaSet schemas)
        {
            Conditions.RequireReference(xml, nameof(xml));
            Conditions.RequireReference(schemas, "schema");

            List <ValidationEventArgs> result = new();

            XmlReaderSettings settings = new()
            {
                Schemas        = schemas,
                ValidationType = ValidationType.Schema,
            };

            settings.ValidationEventHandler += (s, e) => result.Add(e);

            using (XmlReader xmlReader = xml.CreateReader())
                using (XmlReader validatingReader = XmlReader.Create(xmlReader, settings))
                {
                    while (validatingReader.Read())
                    {
                        // We have to read through every node to complete validation.
                    }
                }

            return(result);
        }
示例#7
0
        /// <summary>
        /// Adds a <paramref name="source"/> collection into a <paramref name="target"/> collection.
        /// </summary>
        /// <typeparam name="T">The type of item to add.</typeparam>
        /// <param name="target">The collection to add the items to.</param>
        /// <param name="source">The collection to get the items from.</param>
        /// <remarks>
        /// The adding behavior depends on the <paramref name="target"/> collection's <see cref="ICollection{T}.Add"/>
        /// implemenation.  For example, if <paramref name="target"/> is a generic List, then this could add duplicates.
        /// However, if <paramref name="target"/> is a generic HashSet, then this won't add duplicates.
        /// </remarks>
        public static void AddRange <T>(this ICollection <T> target, IEnumerable <T> source)
        {
            Conditions.RequireReference(target, nameof(target));
            Conditions.RequireReference(source, nameof(source));

            foreach (T item in source)
            {
                target.Add(item);
            }
        }
示例#8
0
        /// <summary>
        /// Gets a read-only set.
        /// </summary>
        /// <param name="value">The set to wrap or return.  This must be non-null.</param>
        /// <returns>If the original <paramref name="value"/> is already read-only, this method
        /// returns it as is.  If it is not read-only, this method returns a read-only wrapper around it.</returns>
        public static ISet <T> AsReadOnly <T>(this ISet <T> value)
        {
            Conditions.RequireReference(value, nameof(value));

            ISet <T> result = value;

            if (result != null && !result.IsReadOnly)
            {
                result = new ReadOnlySet <T>(value);
            }

            return(result !);
        }
示例#9
0
        /// <summary>
        /// Gets whether the assembly was built with a debug configuration.
        /// </summary>
        /// <param name="assembly">The assembly to check.</param>
        /// <returns>True if the <see cref="AssemblyConfigurationAttribute"/> is present
        /// and the configuration string contains "Debug". False otherwise.</returns>
        public static bool IsDebugBuild(Assembly assembly)
        {
            Conditions.RequireReference(assembly, nameof(assembly));

            bool result = false;

            if (assembly != null)
            {
                var configuration = (AssemblyConfigurationAttribute?)assembly.GetCustomAttribute(typeof(AssemblyConfigurationAttribute));
                result = configuration?.Configuration?.Contains("Debug") ?? false;
            }

            return(result);
        }
示例#10
0
        /// <summary>
        /// Gets a read-only dictionary.
        /// </summary>
        /// <param name="dictionary">The dictionary to wrap or return.  This must be non-null.</param>
        /// <returns>If the original <paramref name="dictionary"/> is already read-only, this method
        /// returns it as is.  If it is not read-only, this method returns a read-only wrapper around it.</returns>
        public static IDictionary <TKey, TValue> AsReadOnly <TKey, TValue>(this IDictionary <TKey, TValue> dictionary)
            where TKey : notnull
        {
            Conditions.RequireReference(dictionary, nameof(dictionary));

            IDictionary <TKey, TValue> result = dictionary;

            if (result != null && !result.IsReadOnly)
            {
                result = new ReadOnlyDictionary <TKey, TValue>(dictionary);
            }

            return(result !);
        }
示例#11
0
        /// <summary>
        /// Gets the assembly's copyright information.
        /// </summary>
        /// <param name="assembly">The assembly to get the copyright from.</param>
        /// <returns>User-friendly copyright information.</returns>
        public static string?GetCopyright(Assembly assembly)
        {
            Conditions.RequireReference(assembly, nameof(assembly));

            string?result = null;

            var copyrights = (AssemblyCopyrightAttribute[])assembly.GetCustomAttributes(typeof(AssemblyCopyrightAttribute), true);

            if (copyrights.Length > 0)
            {
                result = copyrights[0].Copyright;
            }

            return(result);
        }
示例#12
0
        /// <summary>
        /// Tokenize the input string using the specified separator (e.g., comma), delimiter (e.g., double quote),
        /// and trimming options and add the output tokens to the specified collection.
        /// </summary>
        /// <param name="text">The text to tokenize.</param>
        /// <param name="separator">The token separator character.  Typically, a comma.</param>
        /// <param name="delimiter">The token quote character.  Typically, a double quote.
        /// This can be used to enclose tokens that contain the separator character.
        /// To use this character inside a token, use it two consecutive times.
        /// </param>
        /// <param name="addToken">The action to invoke when a token has been matched and needs to be output.</param>
        /// <returns>True if all of the tokens were properly delimited (or were not delimited).
        /// False if the final token was open-delimited but never closed (which usually indicates a partial record).</returns>
        public static bool SplitIntoTokens(string text, char separator, char?delimiter, Action <string> addToken)
        {
            Conditions.RequireReference(addToken, nameof(addToken));

            bool result;

            if (delimiter == null)
            {
                SplitIntoTokensWithoutDelimiter(text, separator, addToken);
                result = true;
            }
            else
            {
                result = SplitIntoTokensWithDelimiter(text, separator, delimiter.Value, addToken);
            }

            return(result);
        }
示例#13
0
        /// <summary>
        /// Gets the specified attribute's value or a default value if the attribute isn't present.
        /// </summary>
        /// <param name="element">The element to get the attribute from.</param>
        /// <param name="name">The name of the attribute to read.</param>
        /// <param name="defaultValue">The value to return if the attribute isn't found.</param>
        /// <param name="useDefaultIfEmptyValue">If the attribute is present but with an empty value,
        /// then this parameter determines whether the <paramref name="defaultValue"/> should be
        /// returned (if true) or the actual, empty attribute value should be returned (if false).
        /// Normally, this should be true, but false is useful if you need to allow the user to explicitly
        /// set an empty attribute value to override a non-empty default value.
        /// </param>
        /// <returns>The value of the attribute, or the default value if the attribute isn't found.</returns>
        public static string?GetAttributeValueN(this XElement element, XName name, string?defaultValue, bool useDefaultIfEmptyValue)
        {
            // It's ok if defaultValue is null.
            Conditions.RequireReference(element, nameof(element));
            Conditions.RequireReference(name, nameof(name));

            string?result = defaultValue;

            XAttribute?attr = element.Attribute(name);

            if (attr != null)
            {
                result = attr.Value;

                if (useDefaultIfEmptyValue && string.IsNullOrEmpty(result))
                {
                    result = defaultValue;
                }
            }

            return(result);
        }
示例#14
0
        /// <summary>
        /// Gets the UTC build timestamp from the assembly.
        /// </summary>
        /// <param name="assembly">The assembly to get the BuildTime metadata from.</param>
        /// <returns>The assembly's build time as a UTC datetime if an <see cref="AssemblyMetadataAttribute"/> is found
        /// with Key="BuildTime" and Value equal to a parsable UTC datetime. Returns null otherwise.</returns>
        public static DateTime?GetBuildTime(Assembly assembly)
        {
            Conditions.RequireReference(assembly, nameof(assembly));

            const DateTimeStyles UseUtc = DateTimeStyles.AdjustToUniversal | DateTimeStyles.AssumeUniversal;
            DateTime?            result = assembly.GetCustomAttributes <AssemblyMetadataAttribute>()
                                          .Where(metadata => string.Equals(metadata.Key, "BuildTime"))
                                          .Select(metadata => DateTime.TryParse(metadata.Value, null, UseUtc, out DateTime value) ? value : (DateTime?)null)
                                          .FirstOrDefault(value => value != null);

            if (result != null)
            {
                result = result.Value.Kind switch
                {
                    DateTimeKind.Unspecified => DateTime.SpecifyKind(result.Value, DateTimeKind.Utc),
                    DateTimeKind.Local => result.Value.ToUniversalTime(),
                    _ => result.Value,
                };
            }

            return(result);
        }
示例#15
0
        /// <summary>
        /// Strips the opening and closing quotes off of a string if they exist.
        /// </summary>
        /// <param name="text">The text to search.</param>
        /// <param name="openQuote">The opening quote string.</param>
        /// <param name="closeQuote">The closing quote string.</param>
        /// <returns>The text with the quotes removed.</returns>
        public static string StripQuotes(string text, string openQuote, string closeQuote)
        {
            Conditions.RequireReference(text, nameof(text));             // An empty string is ok.
            Conditions.RequireString(openQuote, nameof(openQuote));
            Conditions.RequireString(closeQuote, nameof(closeQuote));

            int startIndex = 0;
            int length     = text.Length;

            if (text.StartsWith(openQuote))
            {
                int quoteLength = openQuote.Length;
                startIndex += quoteLength;
                length     -= quoteLength;
            }

            if (text.EndsWith(closeQuote))
            {
                length -= closeQuote.Length;
            }

            return(text.Substring(startIndex, length));
        }
示例#16
0
        /// <summary>
        /// Ensures that the given text has the specified quotes at the start and end.
        /// </summary>
        /// <param name="text">The text to quote if necessary.</param>
        /// <param name="openQuote">The opening quote mark to use.</param>
        /// <param name="closeQuote">The closing quote mark to use.</param>
        /// <returns>The text enclosed in quotes.</returns>
        public static string EnsureQuotes(string text, string openQuote, string closeQuote)
        {
            Conditions.RequireReference(text, nameof(text));             // An empty string is ok.
            Conditions.RequireString(openQuote, nameof(openQuote));
            Conditions.RequireString(closeQuote, nameof(closeQuote));

            bool needsOpenQuote  = !text.StartsWith(openQuote);
            bool needsCloseQuote = !text.EndsWith(closeQuote);

            if (needsOpenQuote && needsCloseQuote)
            {
                text = string.Concat(openQuote, text, closeQuote);
            }
            else if (needsOpenQuote)
            {
                text = openQuote + text;
            }
            else if (needsCloseQuote)
            {
                text += closeQuote;
            }

            return(text);
        }
示例#17
0
 /// <summary>
 /// Creates a new instance.
 /// </summary>
 /// <remarks>
 /// See the <see cref="Disposer"/> class comments for an example of using an anonymous
 /// dispose method.
 /// </remarks>
 /// <param name="disposeMethod">The method to invoke during disposal.</param>
 public Disposer(Action disposeMethod)
 {
     Conditions.RequireReference(disposeMethod, nameof(disposeMethod));
     this.disposeMethod = disposeMethod;
 }
示例#18
0
 /// <summary>
 /// Creates a new instance that wraps the supplied <paramref name="source"/>.
 /// </summary>
 /// <param name="source">The set to wrap.</param>
 public ReadOnlySet(ISet <T> source)
 {
     Conditions.RequireReference(source, nameof(source));
     this.source = source;
 }
示例#19
0
 /// <summary>
 /// Creates a new instance for the specified settings node.
 /// </summary>
 /// <param name="settings">A settings node.</param>
 public SettingsEventArgs(ISettingsNode settings)
 {
     Conditions.RequireReference(settings, nameof(settings));
     this.settings = settings;
 }
示例#20
0
 /// <summary>
 ///  Creates a new XmlSchemaSet from the XElement containing the schema and stores
 ///  any errors and warnings in the specified collection.
 /// </summary>
 /// <param name="schemaElements">The XML elements containing the schemas.</param>
 /// <param name="errors">The collection to add errors and warnings into.</param>
 /// <returns>The new XmlSchemaSet instance.</returns>
 public static XmlSchemaSet CreateSchemaSet(IEnumerable <XElement> schemaElements, ICollection <ValidationEventArgs> errors)
 {
     Conditions.RequireReference(errors, nameof(errors));
     return(CreateSchemaSet(schemaElements, (s, e) => errors.Add(e)));
 }