diff --git a/.nuget/NuGet.Config b/.nuget/NuGet.Config
new file mode 100644
index 0000000..67f8ea0
--- /dev/null
+++ b/.nuget/NuGet.Config
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.nuget/NuGet.exe b/.nuget/NuGet.exe
new file mode 100644
index 0000000..8dd7e45
Binary files /dev/null and b/.nuget/NuGet.exe differ
diff --git a/.nuget/NuGet.targets b/.nuget/NuGet.targets
new file mode 100644
index 0000000..3f8c37b
--- /dev/null
+++ b/.nuget/NuGet.targets
@@ -0,0 +1,144 @@
+
+
+
+ $(MSBuildProjectDirectory)\..\
+
+
+ false
+
+
+ false
+
+
+ true
+
+
+ false
+
+
+
+
+
+
+
+
+
+
+ $([System.IO.Path]::Combine($(SolutionDir), ".nuget"))
+
+
+
+
+ $(SolutionDir).nuget
+
+
+
+ $(MSBuildProjectDirectory)\packages.$(MSBuildProjectName.Replace(' ', '_')).config
+ $(MSBuildProjectDirectory)\packages.$(MSBuildProjectName).config
+
+
+
+ $(MSBuildProjectDirectory)\packages.config
+ $(PackagesProjectConfig)
+
+
+
+
+ $(NuGetToolsPath)\NuGet.exe
+ @(PackageSource)
+
+ "$(NuGetExePath)"
+ mono --runtime=v4.0.30319 "$(NuGetExePath)"
+
+ $(TargetDir.Trim('\\'))
+
+ -RequireConsent
+ -NonInteractive
+
+ "$(SolutionDir) "
+ "$(SolutionDir)"
+
+
+ $(NuGetCommand) install "$(PackagesConfig)" -source "$(PackageSources)" $(NonInteractiveSwitch) $(RequireConsentSwitch) -solutionDir $(PaddedSolutionDir)
+ $(NuGetCommand) pack "$(ProjectPath)" -Properties "Configuration=$(Configuration);Platform=$(Platform)" $(NonInteractiveSwitch) -OutputDirectory "$(PackageOutputDir)" -symbols
+
+
+
+ RestorePackages;
+ $(BuildDependsOn);
+
+
+
+
+ $(BuildDependsOn);
+ BuildPackage;
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Intertech.Validation.Test/Intertech.Validation.Test.csproj b/Intertech.Validation.Test/Intertech.Validation.Test.csproj
new file mode 100644
index 0000000..5e177a6
--- /dev/null
+++ b/Intertech.Validation.Test/Intertech.Validation.Test.csproj
@@ -0,0 +1,109 @@
+
+
+
+ Debug
+ AnyCPU
+ {B788342E-D9C5-44D2-99A1-3D97CD73DF09}
+ Library
+ Properties
+ Intertech.Validation.Test
+ Intertech.Validation.Test
+ v4.5
+ 512
+ {3AC096D0-A1C2-E12C-1390-A8335801FDAB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}
+ 10.0
+ $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)
+ $(ProgramFiles)\Common Files\microsoft shared\VSTT\$(VisualStudioVersion)\UITestExtensionPackages
+ False
+ UnitTest
+ ..\
+ true
+
+
+ true
+ full
+ false
+ bin\Debug\
+ DEBUG;TRACE
+ prompt
+ 4
+
+
+ pdbonly
+ true
+ bin\Release\
+ TRACE
+ prompt
+ 4
+
+
+
+ False
+ ..\packages\Newtonsoft.Json.6.0.7\lib\net45\Newtonsoft.Json.dll
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {991aa9c3-7192-400b-abc2-66d3d98ec859}
+ Intertech.Validation
+
+
+
+
+
+
+
+
+
+ False
+
+
+ False
+
+
+ False
+
+
+ False
+
+
+
+
+
+
+
+
+
+ This project references NuGet package(s) that are missing on this computer. Enable NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.
+
+
+
+
+
\ No newline at end of file
diff --git a/Intertech.Validation.Test/Properties/AssemblyInfo.cs b/Intertech.Validation.Test/Properties/AssemblyInfo.cs
new file mode 100644
index 0000000..ff38ed0
--- /dev/null
+++ b/Intertech.Validation.Test/Properties/AssemblyInfo.cs
@@ -0,0 +1,36 @@
+using System.Reflection;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+
+// General Information about an assembly is controlled through the following
+// set of attributes. Change these attribute values to modify the information
+// associated with an assembly.
+[assembly: AssemblyTitle("Intertech.Validation.Test")]
+[assembly: AssemblyDescription("")]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCompany("Microsoft")]
+[assembly: AssemblyProduct("Intertech.Validation.Test")]
+[assembly: AssemblyCopyright("Copyright © Microsoft 2014")]
+[assembly: AssemblyTrademark("")]
+[assembly: AssemblyCulture("")]
+
+// Setting ComVisible to false makes the types in this assembly not visible
+// to COM components. If you need to access a type in this assembly from
+// COM, set the ComVisible attribute to true on that type.
+[assembly: ComVisible(false)]
+
+// The following GUID is for the ID of the typelib if this project is exposed to COM
+[assembly: Guid("c8f12ddc-2d61-4901-bd67-1bbcf8d4df05")]
+
+// Version information for an assembly consists of the following four values:
+//
+// Major Version
+// Minor Version
+// Build Number
+// Revision
+//
+// You can specify all the values or you can default the Build and Revision Numbers
+// by using the '*' as shown below:
+// [assembly: AssemblyVersion("1.0.*")]
+[assembly: AssemblyVersion("1.0.0.0")]
+[assembly: AssemblyFileVersion("1.0.0.0")]
diff --git a/Intertech.Validation.Test/TestDTO/Constants.cs b/Intertech.Validation.Test/TestDTO/Constants.cs
new file mode 100644
index 0000000..14ff17b
--- /dev/null
+++ b/Intertech.Validation.Test/TestDTO/Constants.cs
@@ -0,0 +1,21 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Intertech.Validation.Test.TestDTO
+{
+ public class ErrorMessages
+ {
+ public const string MinLength = "Length minimum violated";
+ public const string MaxLength = "Length maximum violated";
+ public const string Required = "Length required, yo";
+ public const string Email = "Length should be email";
+ public const string Phone = "Length should be phone";
+ public const string Url = "Length should be Url";
+ public const string Regex = "Length Regex failed";
+ public const string CreditCard = "Visa not a CreditCard";
+ public const string VisaLength = "Visa has invalid length";
+ }
+}
diff --git a/Intertech.Validation.Test/TestDTO/NoValidations.cs b/Intertech.Validation.Test/TestDTO/NoValidations.cs
new file mode 100644
index 0000000..1b76f9b
--- /dev/null
+++ b/Intertech.Validation.Test/TestDTO/NoValidations.cs
@@ -0,0 +1,13 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Intertech.Validation.Test.TestDTO
+{
+ public class NoValidations
+ {
+ public string Notvalidated { get; set; }
+ }
+}
diff --git a/Intertech.Validation.Test/TestDTO/ValidationTest.cs b/Intertech.Validation.Test/TestDTO/ValidationTest.cs
new file mode 100644
index 0000000..fb07357
--- /dev/null
+++ b/Intertech.Validation.Test/TestDTO/ValidationTest.cs
@@ -0,0 +1,62 @@
+using Intertech.Validation.Constants;
+using System;
+using System.Collections.Generic;
+using System.ComponentModel.DataAnnotations;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Intertech.Validation.Test.TestDTO
+{
+ public class ValidationTest
+ {
+ [Required]
+ [MinLength(3)]
+ public string Name { get; set; }
+
+ [CreditCard]
+ public string CreditCard { get; set; }
+
+ [EmailAddress]
+ public string Email { get; set; }
+
+ [MaxLength(40)]
+ public string Street { get; set; }
+
+ [Phone]
+ public string Phone { get; set; }
+
+ [Range(1, 100)]
+ public int FavoriteNumber { get; set; }
+
+ [RegularExpression(RegexConstants.Integer)]
+ public string IntegerString { get; set; }
+
+ [StringLength(30, MinimumLength = 2)]
+ public string NickName { get; set; }
+
+ [Url]
+ public string Website { get; set; }
+
+ [Required(ErrorMessage = ErrorMessages.Required)]
+ [MinLength(5, ErrorMessage = ErrorMessages.MinLength)]
+ [MaxLength(25, ErrorMessage = ErrorMessages.MaxLength)]
+ public string Length { get; set; }
+
+ [CreditCard(ErrorMessage = ErrorMessages.CreditCard)]
+ [StringLength(30, MinimumLength = 12, ErrorMessage = ErrorMessages.VisaLength)]
+ public string Visa { get; set; }
+
+ [Url(ErrorMessage = ErrorMessages.Url)]
+ public string Url { get; set; }
+
+ [EmailAddress(ErrorMessage = ErrorMessages.Email)]
+ public string Email2 { get; set; }
+
+ [Phone(ErrorMessage = ErrorMessages.Phone)]
+ public string Phone2 { get; set; }
+
+ [RegularExpression(RegexConstants.Decimal, ErrorMessage = ErrorMessages.Regex)]
+ public string DecimalString { get; set; }
+ }
+}
diff --git a/Intertech.Validation.Test/ValidationHelperTests.cs b/Intertech.Validation.Test/ValidationHelperTests.cs
new file mode 100644
index 0000000..d7f091f
--- /dev/null
+++ b/Intertech.Validation.Test/ValidationHelperTests.cs
@@ -0,0 +1,118 @@
+using System;
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+using System.Text;
+using Newtonsoft.Json.Linq;
+using Intertech.Validation.Constants;
+using Intertech.Validation.Test.TestDTO;
+using System.IO;
+
+namespace Intertech.Validation.Test
+{
+ [TestClass]
+ public class ValidationHelperTests
+ {
+ private object _validations;
+ private object _emptyValidations;
+
+ [TestInitialize]
+ public void Init()
+ {
+ var vals = new StringBuilder("{ validations: { model: { ");
+ vals.Append("Name: { \"ng-minlength\": 3, \"ng-minlength-msg\": \"" + string.Format(DataAnnotationConstants.DefaultMinLengthErrorMsg, "Name", "3") + "\", \"required\": true, \"required-msg\": \"" + string.Format(DataAnnotationConstants.DefaultRequiredErrorMsg, "Name") + "\" }");
+ vals.Append(", CreditCard: { \"ng-pattern\": \"/" + RegexConstants.GetRegularExpressionForJson(RegexConstants.CreditCard) + "/\", \"ng-pattern-msg\": \"" + string.Format(DataAnnotationConstants.DefaultCreditCardErrorMsg, "CreditCard") + "\" }");
+ vals.Append(", Email: { \"ng-pattern\": \"/" + RegexConstants.GetRegularExpressionForJson(RegexConstants.Email) + "/\", \"ng-pattern-msg\": \"" + string.Format(DataAnnotationConstants.DefaultEmailErrorMsg, "Email") + "\" }");
+ vals.Append(", Email2: { \"ng-pattern\": \"/" + RegexConstants.GetRegularExpressionForJson(RegexConstants.Email) + "/\", \"ng-pattern-msg\": \"" + ErrorMessages.Email + "\" }");
+ vals.Append(", Street: { \"ng-maxlength\": 40, \"ng-maxlength-msg\": \"" + string.Format(DataAnnotationConstants.DefaultMaxLengthErrorMsg, "Street", "40") + "\" }");
+ vals.Append(", Phone: { \"ng-pattern\": \"/" + RegexConstants.GetRegularExpressionForJson(RegexConstants.Phone) + "/\", \"ng-pattern-msg\": \"" + string.Format(DataAnnotationConstants.DefaultPhoneErrorMsg, "Phone") + "\" }");
+ vals.Append(", Phone2: { \"ng-pattern\": \"/" + RegexConstants.GetRegularExpressionForJson(RegexConstants.Phone) + "/\", \"ng-pattern-msg\": \"" + ErrorMessages.Phone + "\" }");
+ vals.Append(", FavoriteNumber: { \"min\": 1, \"min-msg\": \"" + string.Format(DataAnnotationConstants.DefaultRangeErrorMsg, "FavoriteNumber", "1", "100") + "\", \"max\": 100, \"max-msg\": \"" + string.Format(DataAnnotationConstants.DefaultRangeErrorMsg, "FavoriteNumber", "1", "100") + "\" }");
+ vals.Append(", IntegerString: { \"ng-pattern\": \"/" + RegexConstants.GetRegularExpressionForJson(RegexConstants.Integer) + "/\", \"ng-pattern-msg\": \"" + string.Format(DataAnnotationConstants.DefaultRegexErrorMsg, "IntegerString") + "\" }");
+ vals.Append(", DecimalString: { \"ng-pattern\": \"/" + RegexConstants.GetRegularExpressionForJson(RegexConstants.Decimal) + "/\", \"ng-pattern-msg\": \"" + ErrorMessages.Regex + "\" }");
+ vals.Append(", NickName: { \"ng-maxlength\": 30, \"ng-maxlength-msg\": \"" + string.Format(DataAnnotationConstants.DefaultMaxLengthErrorMsg, "NickName", "30") + "\", \"ng-minlength\": 2, \"ng-minlength-msg\": \"" + string.Format(DataAnnotationConstants.DefaultMinLengthErrorMsg, "NickName", "2") + "\" }");
+ vals.Append(", Website: { \"ng-pattern\": \"/" + RegexConstants.GetRegularExpressionForJson(RegexConstants.Url) + "/\", \"ng-pattern-msg\": \"" + string.Format(DataAnnotationConstants.DefaultUrlErrorMsg, "Website") + "\" }");
+ vals.Append(", Length: { \"ng-minlength\": 5, \"ng-minlength-msg\": \"" + ErrorMessages.MinLength + "\", \"ng-maxlength\": 25, \"ng-maxlength-msg\": \"" + ErrorMessages.MaxLength + "\", \"required\": true, \"required-msg\": \"" + ErrorMessages.Required + "\" }");
+ vals.Append(", Visa: { \"ng-pattern\": \"/" + RegexConstants.GetRegularExpressionForJson(RegexConstants.CreditCard) + "/\", \"ng-pattern-msg\": \"" + ErrorMessages.CreditCard + "\", \"ng-maxlength\": 30, \"ng-maxlength-msg\": \"" + ErrorMessages.VisaLength + "\", \"ng-minlength\": 12, \"ng-minlength-msg\": \"" + ErrorMessages.VisaLength + "\" }");
+ vals.Append(", Url: { \"ng-pattern\": \"/" + RegexConstants.GetRegularExpressionForJson(RegexConstants.Url) + "/\", \"ng-pattern-msg\": \"" + ErrorMessages.Url + "\" }");
+ vals.Append(" }");
+ vals.Append("} }");
+ _validations = JObject.Parse(vals.ToString());
+
+ _emptyValidations = JObject.Parse("{ validations: { model: { } } }");
+ }
+
+ private void AssertJsonEqual(object expected, object actual)
+ {
+ Assert.IsTrue(JObject.DeepEquals(expected as JObject, actual as JObject));
+ }
+
+ [TestMethod]
+ public void ValidationHelper_Constructor_Test()
+ {
+ // Assemble
+
+ // Act
+ var valHelper = new ValidationHelper();
+
+ // Assert
+ var converters = valHelper.Converters;
+ Assert.IsNotNull(converters);
+ Assert.IsTrue(converters.Count == 10);
+ }
+
+ [TestMethod]
+ public void ValidationHelper_GetValidations_Success_Test()
+ {
+ // Assemble
+ var valHelper = new ValidationHelper();
+
+ // Act
+ var vals = valHelper.GetValidations("TestDTO.ValidationTest", "model", null, "Intertech.Validation.Test");
+
+ // Assert
+ Assert.IsNotNull(vals);
+ AssertJsonEqual(_validations, vals);
+ }
+
+ [TestMethod]
+ public void ValidationHelper_GetValidations_Empty_Test()
+ {
+ // Assemble
+ var valHelper = new ValidationHelper();
+
+ // Act
+ var vals = valHelper.GetValidations("TestDTO.NoValidations", "model", null, "Intertech.Validation.Test");
+
+ // Assert
+ Assert.IsNotNull(vals);
+ AssertJsonEqual(_emptyValidations, vals);
+ }
+
+ [TestMethod]
+ [ExpectedException(typeof(Exception), "DTO 'blah' not found.")]
+ public void ValidationHelper_GetValidations_DTONotFound_Test()
+ {
+ // Assemble
+ var valHelper = new ValidationHelper();
+
+ // Act
+ var vals = valHelper.GetValidations("blah", "model", null, "Intertech.Validation.Test");
+
+ // Assert
+ Assert.IsNull(vals);
+ }
+
+ [TestMethod]
+ [ExpectedException(typeof(FileNotFoundException))]
+ public void ValidationHelper_GetValidations_AssemblyNotFound_Test()
+ {
+ // Assemble
+ var valHelper = new ValidationHelper();
+
+ // Act
+ var vals = valHelper.GetValidations("TestDTO.ValidationTest", "model", null, "Blah.Validation.Test");
+
+ // Assert
+ Assert.IsNull(vals);
+ }
+ }
+}
diff --git a/Intertech.Validation.Test/packages.config b/Intertech.Validation.Test/packages.config
new file mode 100644
index 0000000..f827ca2
--- /dev/null
+++ b/Intertech.Validation.Test/packages.config
@@ -0,0 +1,4 @@
+
+
+
+
\ No newline at end of file
diff --git a/Intertech.Validation/Constants/DataAnnotationConstants.cs b/Intertech.Validation/Constants/DataAnnotationConstants.cs
new file mode 100644
index 0000000..27197ba
--- /dev/null
+++ b/Intertech.Validation/Constants/DataAnnotationConstants.cs
@@ -0,0 +1,24 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Intertech.Validation.Constants
+{
+ public class DataAnnotationConstants
+ {
+ public const string Display = "Display";
+ public const string MinimumLength = "MinimumLength";
+ public const string ErrorMessage = "ErrorMessage";
+ public const string DefaultEmailErrorMsg = "{0} is an invalid email address.";
+ public const string DefaultPhoneErrorMsg = "{0} is an invalid phone number.";
+ public const string DefaultCreditCardErrorMsg = "{0} is an invalid credit card number.";
+ public const string DefaultRegexErrorMsg = "{0} is invalid.";
+ public const string DefaultUrlErrorMsg = "{0} is an invalid URL.";
+ public const string DefaultRequiredErrorMsg = "{0} is required.";
+ public const string DefaultMinLengthErrorMsg = "{0} cannot be less than {1} characters.";
+ public const string DefaultMaxLengthErrorMsg = "{0} cannot be more than {1} characters.";
+ public const string DefaultRangeErrorMsg = "{0} must be between {1} and {2}.";
+ }
+}
diff --git a/Intertech.Validation/Constants/RegexConstants.cs b/Intertech.Validation/Constants/RegexConstants.cs
new file mode 100644
index 0000000..ee01cc1
--- /dev/null
+++ b/Intertech.Validation/Constants/RegexConstants.cs
@@ -0,0 +1,30 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Text.RegularExpressions;
+using System.Threading.Tasks;
+
+namespace Intertech.Validation.Constants
+{
+ public class RegexConstants
+ {
+ public const string Email = @"^[0-9a-zA-Z]+([0-9a-zA-Z]*[-._+])*[0-9a-zA-Z]+@[0-9a-zA-Z]+([-.][0-9a-zA-Z]+)*([0-9a-zA-Z]*[.])[a-zA-Z]{2,6}$";
+ public const string CreditCard = @"^(?:4[0-9]{12}(?:[0-9]{3})?|5[1-5][0-9]{14}|3[47][0-9]{13}|3(?:0[0-5]|[68][0-9])[0-9]{11}|6(?:011|5[0-9]{2})[0-9]{12}|(?:2131|1800|35\d{3})\d{11})$";
+ public const string Phone = @"^(?:\d{3}|\(\d{3}\))([-\/\.])\d{3}\1\d{4}$";
+ public const string Url = @"^(https?|ftp):\/\/(((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:)*@)?(((\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5]))|((([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.)+(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.?)(:\d*)?)(\/((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)+(\/(([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)*)*)?)?(\?((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)|[\uE000-\uF8FF]|\/|\?)*)?(\#((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)|\/|\?)*)?$";
+ public const string Decimal = @"^([0-9]*|\d*\.\d{1}?\d*)$";
+ public const string Integer = @"^[0-9]*$";
+
+ public static string GetRegularExpressionForJson(string regex)
+ {
+ var resultString = Regex.Replace(regex,
+ @"(?
+ /// Does the given attribute match the type T passed in?
+ ///
+ ///
+ ///
+ ///
+ protected bool IsMatch(CustomAttributeData attr)
+ {
+ if (attr == null) return false;
+
+ return String.Compare(attr.AttributeType.FullName, typeof(T).FullName) == 0;
+ }
+
+ ///
+ /// Prepend a comma on the jsonString based on isFirstAttr.
+ ///
+ ///
+ ///
+ protected void PrependComma(StringBuilder jsonString, bool isFirstAttr)
+ {
+ if (!isFirstAttr)
+ jsonString.Append(", ");
+ }
+
+ ///
+ /// Get a constructor argument at the given index from the given attribute.
+ ///
+ ///
+ ///
+ ///
+ protected string GetConstructorArgumentValue(CustomAttributeData attr, int constructorIndex)
+ {
+ string value = null;
+
+ if (attr.ConstructorArguments != null && attr.ConstructorArguments.Count > constructorIndex)
+ {
+ value = attr.ConstructorArguments[constructorIndex].Value.ToString();
+ }
+
+ return value;
+ }
+
+ ///
+ /// Get named argument value from the given attribute.
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ protected string GetNamedArgumentValue(string propertyName, CustomAttributeData attr, string nameOfArgument, bool usePropertyNameIfNull = true)
+ {
+ string value = null;
+
+ if (attr.NamedArguments != null && attr.NamedArguments.Count > 0)
+ {
+ var namedArg = attr.NamedArguments.FirstOrDefault(na => na.MemberName == nameOfArgument);
+ if (namedArg != null && namedArg.TypedValue != null && namedArg.TypedValue.Value != null)
+ {
+ value = namedArg.TypedValue.Value.ToString();
+ }
+ }
+
+ if (string.IsNullOrWhiteSpace(value) && usePropertyNameIfNull)
+ {
+ value = propertyName;
+ }
+
+ return value;
+ }
+
+ protected void SetRegularExpressionAAValidation(string propertyName, CustomAttributeData attr, StringBuilder jsonString, bool isFirstAttr,
+ string regex, string defaultMsgFormat)
+ {
+ PrependComma(jsonString, isFirstAttr);
+
+ jsonString.Append("'ng-pattern': \"/" + RegexConstants.GetRegularExpressionForJson(regex) + "/\"");
+
+ var displayName = GetNamedArgumentValue(propertyName, attr, DataAnnotationConstants.Display);
+ if (!string.IsNullOrWhiteSpace(displayName))
+ {
+ var msg = GetNamedArgumentValue(propertyName, attr, DataAnnotationConstants.ErrorMessage, false);
+ if (string.IsNullOrWhiteSpace(msg))
+ {
+ msg = string.Format(defaultMsgFormat, displayName);
+ }
+ jsonString.Append(", 'ng-pattern-msg': \"" + msg + "\"");
+ }
+ }
+
+ protected void SetMaxLengthAAValidation(string propertyName, CustomAttributeData attr, StringBuilder jsonString, bool isFirstAttr,
+ string length)
+ {
+ PrependComma(jsonString, isFirstAttr);
+
+ jsonString.Append("'ng-maxlength': " + length);
+
+ var displayName = GetNamedArgumentValue(propertyName, attr, DataAnnotationConstants.Display);
+ if (!string.IsNullOrWhiteSpace(displayName))
+ {
+ var msg = GetNamedArgumentValue(propertyName, attr, DataAnnotationConstants.ErrorMessage, false);
+ if (string.IsNullOrWhiteSpace(msg))
+ {
+ msg = string.Format(DataAnnotationConstants.DefaultMaxLengthErrorMsg, displayName, length);
+ }
+ jsonString.Append(", 'ng-maxlength-msg': \"" + msg + "\"");
+ }
+ }
+
+ protected void SetMinLengthAAValidation(string propertyName, CustomAttributeData attr, StringBuilder jsonString, bool isFirstAttr,
+ string length)
+ {
+ PrependComma(jsonString, isFirstAttr);
+
+ jsonString.Append("'ng-minlength': " + length);
+
+ var displayName = GetNamedArgumentValue(propertyName, attr, DataAnnotationConstants.Display);
+ if (!string.IsNullOrWhiteSpace(displayName))
+ {
+ var msg = GetNamedArgumentValue(propertyName, attr, DataAnnotationConstants.ErrorMessage, false);
+ if (string.IsNullOrWhiteSpace(msg))
+ {
+ msg = string.Format(DataAnnotationConstants.DefaultMinLengthErrorMsg, displayName, length);
+ }
+ jsonString.Append(", 'ng-minlength-msg': \"" + msg + "\"");
+ }
+ }
+ }
+}
diff --git a/Intertech.Validation/Converters/CreditCardConverter.cs b/Intertech.Validation/Converters/CreditCardConverter.cs
new file mode 100644
index 0000000..cbc0f9e
--- /dev/null
+++ b/Intertech.Validation/Converters/CreditCardConverter.cs
@@ -0,0 +1,25 @@
+using System;
+using System.Collections.Generic;
+using System.ComponentModel.DataAnnotations;
+using System.Linq;
+using System.Reflection;
+using System.Text;
+using System.Threading.Tasks;
+using Intertech.Validation.Constants;
+
+namespace Intertech.Validation.Converters
+{
+ public class CreditCardConverter : BaseValidationConverter, IValidationConverter
+ {
+ public bool IsAttributeMatch(CustomAttributeData attr)
+ {
+ return IsMatch(attr);
+ }
+
+ public void Convert(string propertyName, CustomAttributeData attr, StringBuilder jsonString, bool isFirstAttr)
+ {
+ SetRegularExpressionAAValidation(propertyName, attr, jsonString, isFirstAttr,
+ RegexConstants.CreditCard, DataAnnotationConstants.DefaultCreditCardErrorMsg);
+ }
+ }
+}
diff --git a/Intertech.Validation/Converters/EmailAddressConverter.cs b/Intertech.Validation/Converters/EmailAddressConverter.cs
new file mode 100644
index 0000000..72e2608
--- /dev/null
+++ b/Intertech.Validation/Converters/EmailAddressConverter.cs
@@ -0,0 +1,25 @@
+using Intertech.Validation.Constants;
+using System;
+using System.Collections.Generic;
+using System.ComponentModel.DataAnnotations;
+using System.Linq;
+using System.Reflection;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Intertech.Validation.Converters
+{
+ public class EmailAddressConverter : BaseValidationConverter, IValidationConverter
+ {
+ public bool IsAttributeMatch(CustomAttributeData attr)
+ {
+ return IsMatch(attr);
+ }
+
+ public void Convert(string propertyName, CustomAttributeData attr, StringBuilder jsonString, bool isFirstAttr)
+ {
+ SetRegularExpressionAAValidation(propertyName, attr, jsonString, isFirstAttr,
+ RegexConstants.Email, DataAnnotationConstants.DefaultEmailErrorMsg);
+ }
+ }
+}
diff --git a/Intertech.Validation/Converters/IValidationConverter.cs b/Intertech.Validation/Converters/IValidationConverter.cs
new file mode 100644
index 0000000..d3319f5
--- /dev/null
+++ b/Intertech.Validation/Converters/IValidationConverter.cs
@@ -0,0 +1,16 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Reflection;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Intertech.Validation.Converters
+{
+ public interface IValidationConverter
+ {
+ bool IsAttributeMatch(CustomAttributeData attr);
+
+ void Convert(string propertyName, CustomAttributeData attr, StringBuilder jsonString, bool isFirstAttr);
+ }
+}
diff --git a/Intertech.Validation/Converters/MaxLengthConverter.cs b/Intertech.Validation/Converters/MaxLengthConverter.cs
new file mode 100644
index 0000000..0492ce9
--- /dev/null
+++ b/Intertech.Validation/Converters/MaxLengthConverter.cs
@@ -0,0 +1,27 @@
+using System;
+using System.Collections.Generic;
+using System.ComponentModel.DataAnnotations;
+using System.Linq;
+using System.Reflection;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Intertech.Validation.Converters
+{
+ public class MaxLengthConverter : BaseValidationConverter, IValidationConverter
+ {
+ public bool IsAttributeMatch(CustomAttributeData attr)
+ {
+ return IsMatch(attr);
+ }
+
+ public void Convert(string propertyName, CustomAttributeData attr, StringBuilder jsonString, bool isFirstAttr)
+ {
+ var length = GetConstructorArgumentValue(attr, 0);
+ if (!string.IsNullOrWhiteSpace(length))
+ {
+ SetMaxLengthAAValidation(propertyName, attr, jsonString, isFirstAttr, length);
+ }
+ }
+ }
+}
diff --git a/Intertech.Validation/Converters/MinLengthConverter.cs b/Intertech.Validation/Converters/MinLengthConverter.cs
new file mode 100644
index 0000000..2d112a1
--- /dev/null
+++ b/Intertech.Validation/Converters/MinLengthConverter.cs
@@ -0,0 +1,27 @@
+using System;
+using System.Collections.Generic;
+using System.ComponentModel.DataAnnotations;
+using System.Linq;
+using System.Reflection;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Intertech.Validation.Converters
+{
+ public class MinLengthConverter : BaseValidationConverter, IValidationConverter
+ {
+ public bool IsAttributeMatch(CustomAttributeData attr)
+ {
+ return IsMatch(attr);
+ }
+
+ public void Convert(string propertyName, CustomAttributeData attr, StringBuilder jsonString, bool isFirstAttr)
+ {
+ var length = GetConstructorArgumentValue(attr, 0);
+ if (!string.IsNullOrWhiteSpace(length))
+ {
+ SetMinLengthAAValidation(propertyName, attr, jsonString, isFirstAttr, length);
+ }
+ }
+ }
+}
diff --git a/Intertech.Validation/Converters/PhoneConverter.cs b/Intertech.Validation/Converters/PhoneConverter.cs
new file mode 100644
index 0000000..0f32fa2
--- /dev/null
+++ b/Intertech.Validation/Converters/PhoneConverter.cs
@@ -0,0 +1,25 @@
+using Intertech.Validation.Constants;
+using System;
+using System.Collections.Generic;
+using System.ComponentModel.DataAnnotations;
+using System.Linq;
+using System.Reflection;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Intertech.Validation.Converters
+{
+ public class PhoneConverter : BaseValidationConverter, IValidationConverter
+ {
+ public bool IsAttributeMatch(CustomAttributeData attr)
+ {
+ return IsMatch(attr);
+ }
+
+ public void Convert(string propertyName, CustomAttributeData attr, StringBuilder jsonString, bool isFirstAttr)
+ {
+ SetRegularExpressionAAValidation(propertyName, attr, jsonString, isFirstAttr,
+ RegexConstants.Phone, DataAnnotationConstants.DefaultPhoneErrorMsg);
+ }
+ }
+}
diff --git a/Intertech.Validation/Converters/RangeConverter.cs b/Intertech.Validation/Converters/RangeConverter.cs
new file mode 100644
index 0000000..997e1b2
--- /dev/null
+++ b/Intertech.Validation/Converters/RangeConverter.cs
@@ -0,0 +1,45 @@
+using Intertech.Validation.Constants;
+using System;
+using System.Collections.Generic;
+using System.ComponentModel.DataAnnotations;
+using System.Linq;
+using System.Reflection;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Intertech.Validation.Converters
+{
+ public class RangeConverter : BaseValidationConverter, IValidationConverter
+ {
+ public bool IsAttributeMatch(CustomAttributeData attr)
+ {
+ return IsMatch(attr);
+ }
+
+ public void Convert(string propertyName, CustomAttributeData attr, StringBuilder jsonString, bool isFirstAttr)
+ {
+ PrependComma(jsonString, isFirstAttr);
+
+ var minimum = GetConstructorArgumentValue(attr, 0);
+ var maximum = GetConstructorArgumentValue(attr, 1);
+ if (!string.IsNullOrWhiteSpace(minimum) && !string.IsNullOrWhiteSpace(maximum))
+ {
+ jsonString.Append("'min': " + minimum);
+
+ var displayName = base.GetNamedArgumentValue(propertyName, attr, DataAnnotationConstants.Display);
+ if (!string.IsNullOrWhiteSpace(displayName))
+ {
+ var msg = GetNamedArgumentValue(propertyName, attr, DataAnnotationConstants.ErrorMessage, false);
+ if (string.IsNullOrWhiteSpace(msg))
+ {
+ msg = string.Format(DataAnnotationConstants.DefaultRangeErrorMsg, displayName, minimum, maximum);
+ }
+ jsonString.Append(", 'min-msg': \"" + msg + "\"");
+
+ jsonString.Append(", 'max': " + maximum);
+ jsonString.Append(", 'max-msg': \"" + msg + "\"");
+ }
+ }
+ }
+ }
+}
diff --git a/Intertech.Validation/Converters/RegularExpressionConverter.cs b/Intertech.Validation/Converters/RegularExpressionConverter.cs
new file mode 100644
index 0000000..efa95a1
--- /dev/null
+++ b/Intertech.Validation/Converters/RegularExpressionConverter.cs
@@ -0,0 +1,29 @@
+using Intertech.Validation.Constants;
+using System;
+using System.Collections.Generic;
+using System.ComponentModel.DataAnnotations;
+using System.Linq;
+using System.Reflection;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Intertech.Validation.Converters
+{
+ public class RegularExpressionConverter : BaseValidationConverter, IValidationConverter
+ {
+ public bool IsAttributeMatch(CustomAttributeData attr)
+ {
+ return IsMatch(attr);
+ }
+
+ public void Convert(string propertyName, CustomAttributeData attr, StringBuilder jsonString, bool isFirstAttr)
+ {
+ var pattern = GetConstructorArgumentValue(attr, 0);
+ if (!string.IsNullOrWhiteSpace(pattern))
+ {
+ SetRegularExpressionAAValidation(propertyName, attr, jsonString, isFirstAttr,
+ pattern, DataAnnotationConstants.DefaultRegexErrorMsg);
+ }
+ }
+ }
+}
diff --git a/Intertech.Validation/Converters/RequiredConverter.cs b/Intertech.Validation/Converters/RequiredConverter.cs
new file mode 100644
index 0000000..8ae5641
--- /dev/null
+++ b/Intertech.Validation/Converters/RequiredConverter.cs
@@ -0,0 +1,37 @@
+using Intertech.Validation.Constants;
+using System;
+using System.Collections.Generic;
+using System.ComponentModel.DataAnnotations;
+using System.Linq;
+using System.Reflection;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Intertech.Validation.Converters
+{
+ public class RequiredConverter : BaseValidationConverter, IValidationConverter
+ {
+ public bool IsAttributeMatch(CustomAttributeData attr)
+ {
+ return IsMatch(attr);
+ }
+
+ public void Convert(string propertyName, CustomAttributeData attr, StringBuilder jsonString, bool isFirstAttr)
+ {
+ PrependComma(jsonString, isFirstAttr);
+
+ jsonString.Append("required: true");
+
+ var displayName = GetNamedArgumentValue(propertyName, attr, DataAnnotationConstants.Display);
+ if (!string.IsNullOrWhiteSpace(displayName))
+ {
+ var msg = GetNamedArgumentValue(propertyName, attr, DataAnnotationConstants.ErrorMessage, false);
+ if (string.IsNullOrWhiteSpace(msg))
+ {
+ msg = string.Format(DataAnnotationConstants.DefaultRequiredErrorMsg, displayName);
+ }
+ jsonString.Append(", 'required-msg': \"" + msg + "\"");
+ }
+ }
+ }
+}
diff --git a/Intertech.Validation/Converters/StringLengthConverter.cs b/Intertech.Validation/Converters/StringLengthConverter.cs
new file mode 100644
index 0000000..3251f7e
--- /dev/null
+++ b/Intertech.Validation/Converters/StringLengthConverter.cs
@@ -0,0 +1,34 @@
+using Intertech.Validation.Constants;
+using System;
+using System.Collections.Generic;
+using System.ComponentModel.DataAnnotations;
+using System.Linq;
+using System.Reflection;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Intertech.Validation.Converters
+{
+ public class StringLengthConverter : BaseValidationConverter, IValidationConverter
+ {
+ public bool IsAttributeMatch(CustomAttributeData attr)
+ {
+ return IsMatch(attr);
+ }
+
+ public void Convert(string propertyName, CustomAttributeData attr, StringBuilder jsonString, bool isFirstAttr)
+ {
+ var maxLength = GetConstructorArgumentValue(attr, 0);
+ if (!string.IsNullOrWhiteSpace(maxLength))
+ {
+ SetMaxLengthAAValidation(propertyName, attr, jsonString, isFirstAttr, maxLength);
+ }
+
+ var minLength = base.GetNamedArgumentValue(propertyName, attr, DataAnnotationConstants.MinimumLength, false);
+ if (!string.IsNullOrWhiteSpace(minLength))
+ {
+ SetMinLengthAAValidation(propertyName, attr, jsonString, false, minLength);
+ }
+ }
+ }
+}
diff --git a/Intertech.Validation/Converters/UrlConverter.cs b/Intertech.Validation/Converters/UrlConverter.cs
new file mode 100644
index 0000000..a7adfe0
--- /dev/null
+++ b/Intertech.Validation/Converters/UrlConverter.cs
@@ -0,0 +1,25 @@
+using Intertech.Validation.Constants;
+using System;
+using System.Collections.Generic;
+using System.ComponentModel.DataAnnotations;
+using System.Linq;
+using System.Reflection;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Intertech.Validation.Converters
+{
+ public class UrlConverter : BaseValidationConverter, IValidationConverter
+ {
+ public bool IsAttributeMatch(CustomAttributeData attr)
+ {
+ return IsMatch(attr);
+ }
+
+ public void Convert(string propertyName, CustomAttributeData attr, StringBuilder jsonString, bool isFirstAttr)
+ {
+ SetRegularExpressionAAValidation(propertyName, attr, jsonString, isFirstAttr,
+ RegexConstants.Url, DataAnnotationConstants.DefaultUrlErrorMsg);
+ }
+ }
+}
diff --git a/Intertech.Validation/Intertech.Validation.csproj b/Intertech.Validation/Intertech.Validation.csproj
new file mode 100644
index 0000000..fd3fb36
--- /dev/null
+++ b/Intertech.Validation/Intertech.Validation.csproj
@@ -0,0 +1,90 @@
+
+
+
+
+ Debug
+ AnyCPU
+ {991AA9C3-7192-400B-ABC2-66D3D98EC859}
+ Library
+ Properties
+ Intertech.Validation
+ Intertech.Validation.AA
+ v4.5
+ 512
+ SAK
+ SAK
+ SAK
+ SAK
+ ..\
+ true
+
+
+ true
+ full
+ false
+ bin\Debug\
+ DEBUG;TRACE
+ prompt
+ 4
+
+
+ pdbonly
+ true
+ bin\Release\
+ TRACE
+ prompt
+ 4
+
+
+
+ False
+ ..\packages\Newtonsoft.Json.6.0.4\lib\net45\Newtonsoft.Json.dll
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ This project references NuGet package(s) that are missing on this computer. Enable NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.
+
+
+
+
+
\ No newline at end of file
diff --git a/Intertech.Validation/Properties/AssemblyInfo.cs b/Intertech.Validation/Properties/AssemblyInfo.cs
new file mode 100644
index 0000000..b8fd20d
--- /dev/null
+++ b/Intertech.Validation/Properties/AssemblyInfo.cs
@@ -0,0 +1,36 @@
+using System.Reflection;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+
+// General Information about an assembly is controlled through the following
+// set of attributes. Change these attribute values to modify the information
+// associated with an assembly.
+[assembly: AssemblyTitle("Intertech.Validation")]
+[assembly: AssemblyDescription("")]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCompany("")]
+[assembly: AssemblyProduct("Intertech.Validation")]
+[assembly: AssemblyCopyright("Copyright © 2014")]
+[assembly: AssemblyTrademark("")]
+[assembly: AssemblyCulture("")]
+
+// Setting ComVisible to false makes the types in this assembly not visible
+// to COM components. If you need to access a type in this assembly from
+// COM, set the ComVisible attribute to true on that type.
+[assembly: ComVisible(false)]
+
+// The following GUID is for the ID of the typelib if this project is exposed to COM
+[assembly: Guid("4862500b-a5ff-48e6-8df2-d25cb07b5649")]
+
+// Version information for an assembly consists of the following four values:
+//
+// Major Version
+// Minor Version
+// Build Number
+// Revision
+//
+// You can specify all the values or you can default the Build and Revision Numbers
+// by using the '*' as shown below:
+// [assembly: AssemblyVersion("1.0.*")]
+[assembly: AssemblyVersion("1.0.0.0")]
+[assembly: AssemblyFileVersion("1.0.0.0")]
diff --git a/Intertech.Validation/ValidationHelper.cs b/Intertech.Validation/ValidationHelper.cs
new file mode 100644
index 0000000..0d2d66f
--- /dev/null
+++ b/Intertech.Validation/ValidationHelper.cs
@@ -0,0 +1,193 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using System.Reflection;
+using Newtonsoft.Json;
+using Newtonsoft.Json.Linq;
+using Intertech.Validation.Converters;
+
+namespace Intertech.Validation
+{
+ public class ValidationHelper
+ {
+ private static List _converters;
+ public List Converters { get { return _converters; } }
+
+ ///
+ /// Constructor that initializes the list of IValidationConverters.
+ ///
+ public ValidationHelper()
+ {
+ if (_converters == null)
+ {
+ var types = GetAllValidationConverters();
+ if (types != null)
+ {
+ _converters = new List();
+
+ foreach (var t in types)
+ {
+ var constructorInfo = t.GetConstructor(System.Type.EmptyTypes);
+ if (constructorInfo != null)
+ {
+ var vcObj = constructorInfo.Invoke(null);
+ if (vcObj != null)
+ {
+ _converters.Add(vcObj as IValidationConverter);
+ }
+ }
+ }
+ }
+ }
+ }
+
+ ///
+ /// Get the validations for dtoObjectName and return the json object.
+ ///
+ ///
+ ///
+ /// Names of assemblies to check
+ ///
+ public object GetValidations(string dtoObjectName, string jsonObjectName, string alternateNamespace, params string[] assemblyNames)
+ {
+ var jsonString = new StringBuilder("{ validations: {");
+
+ GetValidationsForDto(dtoObjectName, jsonObjectName, jsonString, false, alternateNamespace, assemblyNames);
+
+ jsonString.Append("} }");
+
+ return JObject.Parse(jsonString.ToString());
+ }
+
+ #region Private Methods
+
+ ///
+ /// Get all validation converters in this assembly.
+ ///
+ ///
+ private IEnumerable GetAllValidationConverters()
+ {
+ var valAssembly = typeof(IValidationConverter).Assembly;
+
+ var registrations =
+ from type in valAssembly.GetExportedTypes()
+ where type.Namespace == "Intertech.Validation.Converters"
+ where type.GetInterfaces().Any(t => t == typeof(IValidationConverter))
+ select type;
+
+ return registrations.AsEnumerable();
+ }
+
+ private void GetValidationsForDto(string dtoObjectName, string jsonObjectName, StringBuilder jsonString, bool isContainedDto, string alternateNamespace, params string[] assemblyNames)
+ {
+ if (isContainedDto)
+ {
+ jsonString.Append(", ");
+ }
+
+ jsonString.Append(jsonObjectName + ": { ");
+
+ var dtoClass = GetDtoType(dtoObjectName, alternateNamespace, assemblyNames);
+ if (dtoClass == null)
+ {
+ var message = string.Format("DTO '{0}' not found.", dtoObjectName);
+ throw new Exception(message);
+ }
+
+ var properties = dtoClass.GetProperties();
+ if (properties != null)
+ {
+ var isFirstProp = true;
+
+ foreach (var prop in properties)
+ {
+ if (prop.CustomAttributes != null && prop.CustomAttributes.Count() > 0)
+ {
+ var attrStr = new StringBuilder();
+ var isFirstAttr = true;
+
+ foreach (var attr in prop.CustomAttributes)
+ {
+ var converter = _converters.FirstOrDefault(vc => vc.IsAttributeMatch(attr));
+ if (converter != null)
+ {
+ converter.Convert(prop.Name, attr, attrStr, isFirstAttr);
+ isFirstAttr = false;
+ }
+ }
+
+ if (!isFirstAttr)
+ {
+ var sep = isFirstProp ? string.Empty : ",";
+ jsonString.Append(sep + prop.Name + ": { ");
+ jsonString.Append(attrStr.ToString());
+ jsonString.Append("}");
+
+ isFirstProp = false;
+ }
+ }
+ }
+ }
+
+ jsonString.Append("}");
+ }
+
+ ///
+ /// Get the Type for the given DTO name.
+ ///
+ ///
+ ///
+ private Type GetDtoType(string dtoObjectName, string alternateNamespace, params string[] assemblyNames)
+ {
+ Type type = null;
+
+ AppDomain.CurrentDomain.ReflectionOnlyAssemblyResolve -= CurrentDomain_ReflectionOnlyAssemblyResolve;
+ AppDomain.CurrentDomain.ReflectionOnlyAssemblyResolve += CurrentDomain_ReflectionOnlyAssemblyResolve;
+
+ foreach (var asmName in assemblyNames)
+ {
+ var assembly = Assembly.ReflectionOnlyLoad(asmName);
+ type = GetTypeFromAssembly(assembly, dtoObjectName, alternateNamespace);
+ }
+
+ return type;
+ }
+
+ Assembly CurrentDomain_ReflectionOnlyAssemblyResolve(object sender, ResolveEventArgs args)
+ {
+ return Assembly.ReflectionOnlyLoad(args.Name);
+ }
+
+ private Type GetTypeFromAssembly(Assembly assembly, string dtoObjectName, string alternateNamespace)
+ {
+ string typeName;
+ Type type = null;
+
+ if (assembly != null)
+ {
+ var asmName = assembly.FullName.Substring(0, assembly.FullName.IndexOf(','));
+ typeName = string.Format("{0}.{1}", asmName, dtoObjectName);
+ type = assembly.GetType(typeName);
+
+ if (type == null)
+ {
+ if (!string.IsNullOrWhiteSpace(alternateNamespace))
+ {
+ typeName = string.Format("{0}.{1}", alternateNamespace, dtoObjectName);
+ }
+ else
+ {
+ typeName = dtoObjectName;
+ }
+ type = assembly.GetType(typeName);
+ }
+ }
+
+ return type;
+ }
+
+ #endregion Private Methods
+ }
+}
diff --git a/Intertech.Validation/packages.config b/Intertech.Validation/packages.config
new file mode 100644
index 0000000..992a6e6
--- /dev/null
+++ b/Intertech.Validation/packages.config
@@ -0,0 +1,4 @@
+
+
+
+
\ No newline at end of file
diff --git a/README.md b/README.md
index 8d53813..8a47dee 100644
--- a/README.md
+++ b/README.md
@@ -1,4 +1,6 @@
-aa-validation-from-server
-=========================
+#Validation from Server for Angular Agility
-Angular Agility form validation that is retrieved from Web API and DTO classes
+Angular Agility form validation that is retrieved from Web API and DTO classes
+in the form of the [JSON object that AA expects](https://github.com/AngularAgility/AngularAgility/wiki/External-Form-Configuration).
+
+[NuGet Package](https://www.nuget.org/packages/Intertech.Validation.AA/)
\ No newline at end of file
diff --git a/aa-validation-from-server.sln b/aa-validation-from-server.sln
new file mode 100644
index 0000000..ffec3bc
--- /dev/null
+++ b/aa-validation-from-server.sln
@@ -0,0 +1,35 @@
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio 2013
+VisualStudioVersion = 12.0.30110.0
+MinimumVisualStudioVersion = 10.0.40219.1
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Intertech.Validation", "Intertech.Validation\Intertech.Validation.csproj", "{991AA9C3-7192-400B-ABC2-66D3D98EC859}"
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".nuget", ".nuget", "{53189F7F-6C26-4A6E-B42F-4B1ABC8F1426}"
+ ProjectSection(SolutionItems) = preProject
+ .nuget\NuGet.Config = .nuget\NuGet.Config
+ .nuget\NuGet.exe = .nuget\NuGet.exe
+ .nuget\NuGet.targets = .nuget\NuGet.targets
+ EndProjectSection
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Intertech.Validation.Test", "Intertech.Validation.Test\Intertech.Validation.Test.csproj", "{B788342E-D9C5-44D2-99A1-3D97CD73DF09}"
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|Any CPU = Debug|Any CPU
+ Release|Any CPU = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {991AA9C3-7192-400B-ABC2-66D3D98EC859}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {991AA9C3-7192-400B-ABC2-66D3D98EC859}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {991AA9C3-7192-400B-ABC2-66D3D98EC859}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {991AA9C3-7192-400B-ABC2-66D3D98EC859}.Release|Any CPU.Build.0 = Release|Any CPU
+ {B788342E-D9C5-44D2-99A1-3D97CD73DF09}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {B788342E-D9C5-44D2-99A1-3D97CD73DF09}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {B788342E-D9C5-44D2-99A1-3D97CD73DF09}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {B788342E-D9C5-44D2-99A1-3D97CD73DF09}.Release|Any CPU.Build.0 = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+EndGlobal