Click here to Skip to main content
15,895,256 members
Articles / Programming Languages / C# 4.0

Units of Measure Validator for C#

Rate me:
Please Sign up or sign in to vote.
4.66/5 (28 votes)
2 Jul 2012CPOL7 min read 81.4K   2.4K   41  
This library provides a MSBuild task for compile time validation of units of measurement within C# code.
using System;
using System.Collections.Generic;
using System.Linq;
using ICSharpCode.NRefactory.TypeSystem;

namespace HDLibrary.UnitsOfMeasure.Validator
{
    class ExternalUnitDescriptionProvider : ValiditionTask, IUnitDescriptionProvider
    {
        public ExternalUnitDescriptionProvider(ILog log, IUnitInference unitInference, IUnitParser unitParser, IEnumerable<IAssembly> assemblies)
            : base(log)
        {
            this.unitInference = unitInference;
            this.unitParser = unitParser;
            foreach (IAssembly assembly in assemblies)
            {
                foreach (IAttribute attribute in assembly.AssemblyAttributes)
                {
                    if (attribute.AttributeType.FullName == "HDLibrary.UnitsOfMeasure.UnitDescriptionAttribute")
                    {
                        unitDescriptions.Add(new UnitDescription(attribute, unitParser, Log));
                    }
                }
            }
        }

        private readonly List<UnitDescription> unitDescriptions = new List<UnitDescription>();
        private readonly IUnitInference unitInference;
        private readonly IUnitParser unitParser;

        class UnitDescription
        {
            private static string GetNamedArgumentValue(IAttribute attribute, string property)
            {
                foreach (var namedArgument in attribute.NamedArguments)
                {
                    if (namedArgument.Key.Name == property)
                        return namedArgument.Value.ConstantValue as string;
                }
                return null;
            }

            private static IUnresolvedUnit ParseUnresolvedUnitString(string str, IUnitParser unitParser, DomRegion region, ILog log)
            {
                if (string.IsNullOrEmpty(str))
                    return null;

                if (str.Contains("[") || str.Contains("{") || str.Contains("@"))
                    return new DynamicUnit(str, region);
                else
                {
                    Unit u = unitParser.TryParse(str);
                    if (u == null)
                    {
                        log.LogError(region, Language.HDUnitsOfMeasureValidator.CouldNotParseUnitStringX0, str);
                        return null;
                    }
                    else
                        return new StaticUnit(u);
                }
            }

            public UnitDescription(IAttribute attribute, IUnitParser unitParser, ILog log)
            {
                if (unitParser == null)
                    throw new ArgumentNullException("unitParser");

                MemberName = attribute.PositionalArguments[0].ConstantValue as string;

                string overloading = GetNamedArgumentValue(attribute, "Overloading");
                if (overloading != null)
                    parameterTypes = overloading.Split(',');

                string resultUnit = GetNamedArgumentValue(attribute, "ResultUnit");
                ResultUnit = ParseUnresolvedUnitString(resultUnit, unitParser, attribute.Region, log);

                string parameterUnits = GetNamedArgumentValue(attribute, "ParameterUnits");
                if (parameterUnits != null)
                {
                    string[] parts = parameterUnits.Split(',');
                    ParameterUnits = new IUnresolvedUnit[parts.Length];

                    for (int index = 0; index < parts.Length; index++)
                        ParameterUnits[index] = ParseUnresolvedUnitString(parts[index].Trim(), unitParser, attribute.Region, log);
                }
                else
                    ParameterUnits = new IUnresolvedUnit[0];
            }

            public bool CanApplyTo(IMember member)
            {
                if (member.FullName != MemberName)
                    return false;

                var pm = member as IParameterizedMember;
                if (pm != null)
                {
                    if (parameterTypes != null)
                    {
                        if (parameterTypes.Length != pm.Parameters.Count)
                            return false;

                        for (int i = 0; i < parameterTypes.Length; i++)
                        {
                            if (parameterTypes[i] != pm.Parameters[i].Type.ToString())
                                return false;
                        }
                    }

                    if (ParameterUnits == null || pm.Parameters.Count != ParameterUnits.Length)
                        return false;
                }
                else if (ParameterUnits.Length != 0)
                    return false;

                return true;
            }

            private readonly string[] parameterTypes;
            public string MemberName { get; private set; }

            public IUnresolvedUnit ResultUnit { get; private set; }
            public IUnresolvedUnit[] ParameterUnits { get; private set; }
        }

        public IUnitDescription GetUnitDescription(IMember member)
        {
            foreach (var unitDescription in unitDescriptions)
            {
                if (unitDescription.CanApplyTo(member))
                {
                    if (unitDescription.ParameterUnits.Length == 0)
                        return new SimpleUnitDescription(Log, unitDescription.ResultUnit, unitInference, unitParser);
                    else
                        return new ParameterizedUnitDescription(Log, unitInference, unitParser, member as IParameterizedMember, unitDescription.ResultUnit, unitDescription.ParameterUnits);
                }
            }

            return null;
        }
    }
}

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Student
Germany Germany
Presently I am a student of computer science at the Karlsruhe Institute of Technology in Germany.

Comments and Discussions