From b614e32774962e33c5d3c4a567c119389de448ad Mon Sep 17 00:00:00 2001 From: Kyle Heon Date: Thu, 21 Nov 2013 09:43:55 -0500 Subject: [PATCH 1/3] Added a new Extensions.Test project to more easily validate changes to this core library. Tweaked the AsNamespace method(s) to pascal case each piece of a namespace while preserving any other casing already inherited. Also changed AsNamespace to allow for a custom illegal character replacement value (such as nothing at all to create namespaces like .UserDefined. instead of .User_Defined.). Adjusted the Helpers.tt to make no space the default. Modifications to the Capitalize extension method were made to support this. --- CodeGeneration.sln | 13 ++- Extensions.Test/Extensions.Test.csproj | 90 +++++++++++++++++++ Extensions.Test/InflectorBehavior.cs | 23 +++++ Extensions.Test/Properties/AssemblyInfo.cs | 36 ++++++++ Extensions.Test/StringExtensionsBehavior.cs | 47 ++++++++++ Extensions/Inflector.cs | 12 +++ Extensions/StringExtensions.cs | 21 +++-- .../Code Generation Templates/Helpers.tt | 4 +- Sitecore.Master/Sitecore.Master.scproj | 12 +-- 9 files changed, 241 insertions(+), 17 deletions(-) create mode 100644 Extensions.Test/Extensions.Test.csproj create mode 100644 Extensions.Test/InflectorBehavior.cs create mode 100644 Extensions.Test/Properties/AssemblyInfo.cs create mode 100644 Extensions.Test/StringExtensionsBehavior.cs diff --git a/CodeGeneration.sln b/CodeGeneration.sln index 675e78a..a46232c 100644 --- a/CodeGeneration.sln +++ b/CodeGeneration.sln @@ -17,11 +17,15 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Libraries", "Libraries", "{ EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Files", "Solution Files", "{3E32806A-8707-4D41-9126-57717DC4C1EE}" ProjectSection(SolutionItems) = preProject - .gitignore = .gitignore .gitattributes = .gitattributes + .gitignore = .gitignore README.md = README.md EndProjectSection EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Extensions.Test", "Extensions.Test\Extensions.Test.csproj", "{64EAD5E4-5816-4A94-AF47-7D9A640D8B55}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Tests", "Tests", "{587DABEF-E1F1-4A53-90E4-C330A3AC4917}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -42,8 +46,15 @@ Global {D29742AE-DCD9-4A34-AD67-E3F39FE4443C}.Debug|Any CPU.Build.0 = Debug|Any CPU {D29742AE-DCD9-4A34-AD67-E3F39FE4443C}.Release|Any CPU.ActiveCfg = Release|Any CPU {D29742AE-DCD9-4A34-AD67-E3F39FE4443C}.Release|Any CPU.Build.0 = Release|Any CPU + {64EAD5E4-5816-4A94-AF47-7D9A640D8B55}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {64EAD5E4-5816-4A94-AF47-7D9A640D8B55}.Debug|Any CPU.Build.0 = Debug|Any CPU + {64EAD5E4-5816-4A94-AF47-7D9A640D8B55}.Release|Any CPU.ActiveCfg = Release|Any CPU + {64EAD5E4-5816-4A94-AF47-7D9A640D8B55}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {64EAD5E4-5816-4A94-AF47-7D9A640D8B55} = {587DABEF-E1F1-4A53-90E4-C330A3AC4917} + EndGlobalSection EndGlobal diff --git a/Extensions.Test/Extensions.Test.csproj b/Extensions.Test/Extensions.Test.csproj new file mode 100644 index 0000000..6cdb9c3 --- /dev/null +++ b/Extensions.Test/Extensions.Test.csproj @@ -0,0 +1,90 @@ + + + + Debug + AnyCPU + {64EAD5E4-5816-4A94-AF47-7D9A640D8B55} + Library + Properties + Extensions.Test + Extensions.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 + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + + + + + + + + + + + + + + + + + + + + + + {d29742ae-dcd9-4a34-ad67-e3f39fe4443c} + Extensions + + + + + + + False + + + False + + + False + + + False + + + + + + + + \ No newline at end of file diff --git a/Extensions.Test/InflectorBehavior.cs b/Extensions.Test/InflectorBehavior.cs new file mode 100644 index 0000000..66dee21 --- /dev/null +++ b/Extensions.Test/InflectorBehavior.cs @@ -0,0 +1,23 @@ +using System; +using System.Collections; +using HedgehogDevelopment.CodeGeneration.Extensions; +using Microsoft.VisualStudio.TestTools.UnitTesting; + +namespace Extensions.Test +{ + [TestClass] + public class InflectorBehavior + { + [TestMethod] + public void CapitalizeShouldOnlyCapitalizeFirstCharacter() + { + Assert.AreEqual("Pagetemplates", "pageTemplates".Capitalize()); + } + + [TestMethod] + public void CapitalizeShouldPreserveRemainingCharacters() + { + Assert.AreEqual("PageTemplates", "pageTemplates".Capitalize(true)); + } + } +} \ No newline at end of file diff --git a/Extensions.Test/Properties/AssemblyInfo.cs b/Extensions.Test/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..4de8122 --- /dev/null +++ b/Extensions.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("Extensions.Test")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("Extensions.Test")] +[assembly: AssemblyCopyright("Copyright © 2013")] +[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("249ef0fa-7b93-4ed7-857d-b80ecb8d057a")] + +// 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/Extensions.Test/StringExtensionsBehavior.cs b/Extensions.Test/StringExtensionsBehavior.cs new file mode 100644 index 0000000..0e8adb7 --- /dev/null +++ b/Extensions.Test/StringExtensionsBehavior.cs @@ -0,0 +1,47 @@ +using System; +using System.Collections; +using HedgehogDevelopment.CodeGeneration.Extensions; +using Microsoft.VisualStudio.TestTools.UnitTesting; + +namespace Extensions.Test +{ + [TestClass] + public class StringExtensionsBehavior + { + [TestMethod] + public void AsNamespaceShouldStripErroneousPeriods() + { + string[] segments = new string[5] { ".My", "Namespace.", "For", "The...Sample..", "Project." }; + string ns = segments.AsNamespace(); + + Assert.AreEqual("My.Namespace.For.The.Sample.Project", ns); + } + + [TestMethod] + public void AsNamespaceShouldReturnPascalCased() + { + string[] segments = new string[6] { "MyCompany", "MyProject", "sitecore", "templates", "User Defined", "Page Fragments" }; + string ns = segments.AsNamespace(); + + Assert.AreEqual("MyCompany.MyProject.Sitecore.Templates.User_Defined.Page_Fragments", ns); + } + + [TestMethod] + public void AsNamespaceShouldReplaceSpacesWithDefaultUnderscore() + { + string[] segments = new string[6] { "MyCompany", "MyProject", "sitecore", "templates", "User Defined", "Page Fragments" }; + string ns = segments.AsNamespace(); + + Assert.AreEqual("MyCompany.MyProject.Sitecore.Templates.User_Defined.Page_Fragments", ns); + } + + [TestMethod] + public void AsNamespaceShouldReplaceSpacesWithNothing() + { + string[] segments = new string[6] { "MyCompany", "MyProject", "sitecore", "templates", "User Defined", "Page Fragments" }; + string ns = segments.AsNamespace(""); + + Assert.AreEqual("MyCompany.MyProject.Sitecore.Templates.UserDefined.PageFragments", ns); + } + } +} \ No newline at end of file diff --git a/Extensions/Inflector.cs b/Extensions/Inflector.cs index a144117..b28d1f5 100644 --- a/Extensions/Inflector.cs +++ b/Extensions/Inflector.cs @@ -147,6 +147,18 @@ public static string Singularize(this string word) /// capitalized. public static string Capitalize(this string word) { + return Capitalize(word, false); + } + + /// + /// Capitalizes a word. + /// + /// The word to be capitalized. + /// Flag to determine if remaining characters are reset to lower case + /// capitalized. + public static string Capitalize(this string word, bool preserveExisting) + { + if (preserveExisting) return word.Substring(0, 1).ToUpper() + word.Substring(1); return word.Substring(0, 1).ToUpper() + word.Substring(1).ToLower(); } diff --git a/Extensions/StringExtensions.cs b/Extensions/StringExtensions.cs index 537c0ce..49d55cd 100644 --- a/Extensions/StringExtensions.cs +++ b/Extensions/StringExtensions.cs @@ -4,6 +4,7 @@ using System.Globalization; using System.Text; using System.Text.RegularExpressions; +using System.Linq; namespace HedgehogDevelopment.CodeGeneration.Extensions { @@ -86,11 +87,11 @@ public static string AsFieldName(this string word) /// /// The words. /// - public static IEnumerable AsValidWords(this IEnumerable words) + public static IEnumerable AsValidWords(this IEnumerable words, string illegalCharReplacement = "_") { foreach (string word in words) { - yield return AsValidWord(word); + yield return AsValidWord(word, illegalCharReplacement); } } @@ -107,7 +108,7 @@ public static IEnumerable AsValidWords(this IEnumerable words) /// /// The word. /// A valid word for the specified word. - public static string AsValidWord(this string word) + public static string AsValidWord(this string word, string illegalCharReplacement = "_") { string identifier = word; @@ -119,9 +120,9 @@ public static string AsValidWord(this string word) //identifier = RemoveDiacritics(identifier); // C# Identifiers - http://msdn.microsoft.com/en-us/library/aa664670(VS.71).aspx - // replace all illegal chars with an '_' + // replace all illegal chars with the value passed in via illegalCharReplacement, default is "_" System.Text.RegularExpressions.Regex regex = new System.Text.RegularExpressions.Regex(@"[^\p{Ll}\p{Lu}\p{Lt}\p{Lo}\p{Nd}\p{Nl}\p{Mn}\p{Mc}\p{Cf}\p{Pc}\p{Lm}]"); - identifier = regex.Replace(identifier, "_"); + identifier = regex.Replace(identifier, illegalCharReplacement); //The identifier must start with a character or '_' if (! (char.IsLetter(identifier, 0) || identifier[0] == '_')) @@ -152,18 +153,20 @@ public static string AsValidWord(this string word) /// /// The namespace segments. /// A valid string in valid namespace format. - public static string AsNamespace(this IEnumerable words) + public static string AsNamespace(this IEnumerable words, string illegalCharReplacement = "_") { List joinedNamespace = new List(); foreach (string segment in words) { if (segment != null) { - // split apart any strings with a '.' and remove any consecutive multiple '.' - string[] segments = segment.Split(new char[1] { '.' }, StringSplitOptions.RemoveEmptyEntries); + // split apart any strings with a '.' and remove any consecutive multiple '.' and + // ensure that the first character is capitalized while preserving the rest + var segments = segment.Split(new char[1] { '.' }, StringSplitOptions.RemoveEmptyEntries) + .Select(s => s.Capitalize(true)); // being we are making a namespace, make sure the segments are valid - IEnumerable validSegments = segments.AsValidWords(); + IEnumerable validSegments = segments.AsValidWords(illegalCharReplacement); joinedNamespace.AddRange(validSegments); } } diff --git a/Sitecore.Master/Code Generation Templates/Helpers.tt b/Sitecore.Master/Code Generation Templates/Helpers.tt index f6226f9..b4183c9 100644 --- a/Sitecore.Master/Code Generation Templates/Helpers.tt +++ b/Sitecore.Master/Code Generation Templates/Helpers.tt @@ -31,7 +31,7 @@ public static string GetNamespace(string defaultNamespace, SitecoreItem item, bo List namespaceSegments = new List(); namespaceSegments.Add(defaultNamespace); namespaceSegments.Add(item.Namespace); - string @namespace = namespaceSegments.AsNamespace(); // use an extension method in the supporting assembly + string @namespace = namespaceSegments.AsNamespace(""); // use an extension method in the supporting assembly return includeGlobal ? string.Concat("global::", @namespace) : @namespace; } @@ -43,7 +43,7 @@ public static string GetNamespace(string defaultNamespace, SitecoreItem item, bo public static string JoinNamespaces(params string[] namespaces) { // leverage an extension method in the support assembly - return namespaces.AsNamespace(); + return namespaces.AsNamespace(""); } /// diff --git a/Sitecore.Master/Sitecore.Master.scproj b/Sitecore.Master/Sitecore.Master.scproj index 6f54098..2839950 100644 --- a/Sitecore.Master/Sitecore.Master.scproj +++ b/Sitecore.Master/Sitecore.Master.scproj @@ -8,8 +8,10 @@ v2.0 - {3fffc16d-eacd-452f-975e-9f48c4a95c6d}|UI\UI.csproj - /UI.csproj + + + + v2.0 true true @@ -642,9 +644,6 @@ AlwaysUpdate - - - @@ -655,5 +654,8 @@ + + + \ No newline at end of file From e388ff36b88ce6c49625db315ff3b883af55b5ee Mon Sep 17 00:00:00 2001 From: Kyle Heon Date: Wed, 4 Dec 2013 09:36:21 -0500 Subject: [PATCH 2/3] Reworked code gen logic to create namespaces, classes and interfaces without underscores and to pascal case them. This makes for cleaner generated code. Cleaned up the test code as well. --- Extensions.Test/InflectorBehavior.cs | 4 +-- Extensions.Test/StringExtensionsBehavior.cs | 36 ++++++++++++--------- Extensions/StringExtensions.cs | 32 +++++++++++------- 3 files changed, 43 insertions(+), 29 deletions(-) diff --git a/Extensions.Test/InflectorBehavior.cs b/Extensions.Test/InflectorBehavior.cs index 66dee21..bf96bd3 100644 --- a/Extensions.Test/InflectorBehavior.cs +++ b/Extensions.Test/InflectorBehavior.cs @@ -1,6 +1,4 @@ -using System; -using System.Collections; -using HedgehogDevelopment.CodeGeneration.Extensions; +using HedgehogDevelopment.CodeGeneration.Extensions; using Microsoft.VisualStudio.TestTools.UnitTesting; namespace Extensions.Test diff --git a/Extensions.Test/StringExtensionsBehavior.cs b/Extensions.Test/StringExtensionsBehavior.cs index 0e8adb7..fd5a152 100644 --- a/Extensions.Test/StringExtensionsBehavior.cs +++ b/Extensions.Test/StringExtensionsBehavior.cs @@ -1,6 +1,4 @@ -using System; -using System.Collections; -using HedgehogDevelopment.CodeGeneration.Extensions; +using HedgehogDevelopment.CodeGeneration.Extensions; using Microsoft.VisualStudio.TestTools.UnitTesting; namespace Extensions.Test @@ -9,36 +7,44 @@ namespace Extensions.Test public class StringExtensionsBehavior { [TestMethod] - public void AsNamespaceShouldStripErroneousPeriods() + public void AsClassNameShouldNotContainUnderscores() { - string[] segments = new string[5] { ".My", "Namespace.", "For", "The...Sample..", "Project." }; - string ns = segments.AsNamespace(); + Assert.AreEqual("UserDefined", "User Defined".AsClassName()); + Assert.AreEqual("UserDefined", "User_Defined".AsClassName()); + } - Assert.AreEqual("My.Namespace.For.The.Sample.Project", ns); + [TestMethod] + public void AsInterfaceShouldNotContainUnderscores() + { + Assert.AreEqual("IBasePage", "Base Page".AsInterfaceName()); + Assert.AreEqual("IBasePage", "Base_Page".AsInterfaceName()); + Assert.AreEqual("IBasePage", "IBasePage".AsInterfaceName()); } [TestMethod] - public void AsNamespaceShouldReturnPascalCased() + public void AsNamespaceShouldStripErroneousPeriods() { - string[] segments = new string[6] { "MyCompany", "MyProject", "sitecore", "templates", "User Defined", "Page Fragments" }; + var segments = new string[5] {".My", "Namespace.", "For", "The...Sample..", "Project."}; string ns = segments.AsNamespace(); - Assert.AreEqual("MyCompany.MyProject.Sitecore.Templates.User_Defined.Page_Fragments", ns); + Assert.AreEqual("My.Namespace.For.The.Sample.Project", ns); } [TestMethod] - public void AsNamespaceShouldReplaceSpacesWithDefaultUnderscore() + public void AsNamespaceShouldReturnPascalCased() { - string[] segments = new string[6] { "MyCompany", "MyProject", "sitecore", "templates", "User Defined", "Page Fragments" }; + var segments = new string[6] + {"MyCompany", "MyProject", "sitecore", "templates", "User Defined", "Page Fragments"}; string ns = segments.AsNamespace(); - Assert.AreEqual("MyCompany.MyProject.Sitecore.Templates.User_Defined.Page_Fragments", ns); + Assert.AreEqual("MyCompany.MyProject.Sitecore.Templates.UserDefined.PageFragments", ns); } [TestMethod] - public void AsNamespaceShouldReplaceSpacesWithNothing() + public void AsNamespaceShouldNotContainUnderscores() { - string[] segments = new string[6] { "MyCompany", "MyProject", "sitecore", "templates", "User Defined", "Page Fragments" }; + var segments = new string[6] + {"MyCompany", "MyProject", "sitecore", "templates", "User Defined", "Page Fragments"}; string ns = segments.AsNamespace(""); Assert.AreEqual("MyCompany.MyProject.Sitecore.Templates.UserDefined.PageFragments", ns); diff --git a/Extensions/StringExtensions.cs b/Extensions/StringExtensions.cs index 49d55cd..a25d37a 100644 --- a/Extensions/StringExtensions.cs +++ b/Extensions/StringExtensions.cs @@ -55,13 +55,13 @@ public static string AsInterfaceName(this string word) { interfaceWord = string.Concat("I", interfaceWord); } - return interfaceWord; + return interfaceWord.RemoveUnderscores(); } public static string AsClassName(this string word) { // TitleCase the word - return GetFormattedWord(word, TitleCase); + return GetFormattedWord(word, TitleCase).RemoveUnderscores(); } public static string AsPropertyName(this string word, bool pluralize = false) @@ -87,7 +87,7 @@ public static string AsFieldName(this string word) /// /// The words. /// - public static IEnumerable AsValidWords(this IEnumerable words, string illegalCharReplacement = "_") + public static IEnumerable AsValidWords(this IEnumerable words, string illegalCharReplacement = "") { foreach (string word in words) { @@ -108,21 +108,19 @@ public static IEnumerable AsValidWords(this IEnumerable words, s /// /// The word. /// A valid word for the specified word. - public static string AsValidWord(this string word, string illegalCharReplacement = "_") + public static string AsValidWord(this string word, string illegalCharReplacement = "") { string identifier = word; - if (identifier == "*") - { - identifier = "Wildcard"; - } + if (identifier == "*") identifier = "Wildcard"; + if (String.IsNullOrEmpty(illegalCharReplacement)) identifier = identifier.Replace("_", ""); //identifier = RemoveDiacritics(identifier); // C# Identifiers - http://msdn.microsoft.com/en-us/library/aa664670(VS.71).aspx - // replace all illegal chars with the value passed in via illegalCharReplacement, default is "_" + // replace all illegal chars with the value passed in via illegalCharReplacement, default is "" System.Text.RegularExpressions.Regex regex = new System.Text.RegularExpressions.Regex(@"[^\p{Ll}\p{Lu}\p{Lt}\p{Lo}\p{Nd}\p{Nl}\p{Mn}\p{Mc}\p{Cf}\p{Pc}\p{Lm}]"); - identifier = regex.Replace(identifier, illegalCharReplacement); + identifier = regex.Replace(identifier, illegalCharReplacement); //The identifier must start with a character or '_' if (! (char.IsLetter(identifier, 0) || identifier[0] == '_')) @@ -171,7 +169,7 @@ public static string AsNamespace(this IEnumerable words, string illegalC } } string ns = string.Join(".", joinedNamespace.ToArray()); - return ns; + return ns.RemoveUnderscores(); } /// @@ -194,6 +192,18 @@ private static string RemoveDiacritics(string s) return stringBuilder.ToString(); } + /// + /// Replaces any underscores with an empty string, for instance User_Defined becomes UserDefined + /// + /// The string to replace underscores on. + /// The string without any underscores. + public static string RemoveUnderscores(this string word) + { + return !String.IsNullOrEmpty(word) + ? word.Replace("_", "") + : word; + } + /// /// Tests whether the word conflicts with reserved or language keywords, and if so, attempts to return a /// valid word that does not conflict. Usually the returned word is only slightly modified to differentiate From 69606aaba991dff99dbcd61590bf57770f3792e1 Mon Sep 17 00:00:00 2001 From: Kyle Heon Date: Wed, 4 Dec 2013 13:09:59 -0500 Subject: [PATCH 3/3] Added additional xcopy to drop extensions dll in the Microsoft Visual Studio 11.0 "PublicAssemblies" folder as well as the 10. 0 which was needed for my system to pick up this library. --- Extensions/Extensions.csproj | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Extensions/Extensions.csproj b/Extensions/Extensions.csproj index 70e9241..effdfcc 100644 --- a/Extensions/Extensions.csproj +++ b/Extensions/Extensions.csproj @@ -53,7 +53,8 @@ - xcopy "$(TargetDir)*" "%25ProgramFiles%25\Microsoft Visual Studio 10.0\Common7\IDE\PublicAssemblies" /i /d /y /e + xcopy "$(TargetDir)*" "%25ProgramFiles%25\Microsoft Visual Studio 10.0\Common7\IDE\PublicAssemblies" /i /d /y /e +xcopy "$(TargetDir)*" "%25ProgramFiles%25\Microsoft Visual Studio 11.0\Common7\IDE\PublicAssemblies" /i /d /y /e