Skip to content

Commit

Permalink
gherkin parser import tool; upgrade to gh 1.0.24; fix language issues
Browse files Browse the repository at this point in the history
  • Loading branch information
gasparnagy committed May 5, 2010
1 parent 50ef09b commit 5f87e75
Show file tree
Hide file tree
Showing 41 changed files with 1,270 additions and 203 deletions.
2 changes: 1 addition & 1 deletion Generator/TechTalk.SpecFlow.Generator.csproj
Expand Up @@ -12,7 +12,7 @@
<AssemblyName>TechTalk.SpecFlow.Generator</AssemblyName>
<TargetFrameworkVersion>v3.5</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
<SignAssembly>false</SignAssembly>
<SignAssembly>true</SignAssembly>
<AssemblyOriginatorKeyFile>..\specflow.snk</AssemblyOriginatorKeyFile>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
Expand Down
71 changes: 71 additions & 0 deletions Installer/ImportGherkinParser/ImportGherkinParser.csproj
@@ -0,0 +1,71 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="3.5" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProductVersion>9.0.30729</ProductVersion>
<SchemaVersion>2.0</SchemaVersion>
<ProjectGuid>{0D3D2616-F53C-46A2-ADEB-273D140C1267}</ProjectGuid>
<OutputType>Exe</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>ImportGherkinParser</RootNamespace>
<AssemblyName>ImportGherkinParser</AssemblyName>
<TargetFrameworkVersion>v3.5</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<ItemGroup>
<Reference Include="ILMerge, Version=2.10.310.0, Culture=neutral, PublicKeyToken=736440c9b414ea16, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\..\lib\ilmerge\ILMerge.exe</HintPath>
</Reference>
<Reference Include="System" />
<Reference Include="System.Core">
<RequiredTargetFramework>3.5</RequiredTargetFramework>
</Reference>
<Reference Include="System.Xml.Linq">
<RequiredTargetFramework>3.5</RequiredTargetFramework>
</Reference>
<Reference Include="System.Data.DataSetExtensions">
<RequiredTargetFramework>3.5</RequiredTargetFramework>
</Reference>
<Reference Include="System.Data" />
<Reference Include="System.Xml" />
</ItemGroup>
<ItemGroup>
<Compile Include="..\..\lib\nconsoler\NConsoler.cs">
<Link>NConsoler\NConsoler.cs</Link>
</Compile>
<Compile Include="Program.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
</ItemGroup>
<ItemGroup>
<None Include="import.cmd">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets.
<Target Name="BeforeBuild">
</Target>
<Target Name="AfterBuild">
</Target>
-->
</Project>
272 changes: 272 additions & 0 deletions Installer/ImportGherkinParser/Program.cs
@@ -0,0 +1,272 @@
using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Text.RegularExpressions;
using System.Xml.Linq;
using ILMerging;
using NConsoler;

namespace ImportGherkinParser
{
class Program
{
static void Main(string[] args)
{
Console.WriteLine("Gherkin parser import tool:");
Console.WriteLine(" + adds signing for the assembly");
Console.WriteLine(" + generates Language.xml from the parser");
Console.WriteLine();

Consolery.Run(typeof(Program), args);
return;
}

static private readonly Regex parserVersionRe = new Regex(@"(?<version>[\d\.]+)");

[Action("Imports official Gherkin parser to SpecFlow")]
public static void ImportGherkinParser(
[Required(Description = "Original gherkin parser, like gherkin-1.0.21.dll")] string gherkinParser,
[Optional("specflow.snk", "key")] string keyFile,
[Optional("Gherkin.dll", "out")] string outputFile,
[Optional("Languages.xml", "lngout")] string languagesOutputFile
)
{
string gherkinParserFullPath = Path.GetFullPath(gherkinParser);
Version parserVersion = GetParserVersion(gherkinParserFullPath);
if (parserVersion == null)
return;

string keyFullPath = Path.GetFullPath(keyFile);
string outputFileFullPath = Path.GetFullPath(outputFile);
string languagesOutputFileFullPath = Path.GetFullPath(languagesOutputFile);

CreateSignedParser(gherkinParserFullPath, parserVersion, keyFullPath, outputFileFullPath);

Assembly parserAssembly = LoadParser(outputFileFullPath);
GenerateLanguageDescriptions(parserAssembly, languagesOutputFileFullPath);
}

private static Version GetParserVersion(string gherkinParserFullPath)
{
var match = parserVersionRe.Match(Path.GetFileNameWithoutExtension(gherkinParserFullPath));
if (!match.Success)
{
Console.WriteLine("> Unable to detect parser version");
return null;
}
var version = new Version(match.Groups["version"].Value);
version = new Version(version.Major, version.Minor, version.Build, version.Revision < 0 ? 0 : version.Revision);
return version;
}

private static Assembly LoadParser(string outputFileFullPath)
{
Console.WriteLine("Loading imported parser from {0}", outputFileFullPath);
return Assembly.LoadFrom(outputFileFullPath);
}

private static void CreateSignedParser(string gherkinParserFullPath, Version version, string keyFullPath, string outputFileFullPath)
{
Console.WriteLine("Generating signed parser...");

ILMerge ilMerge = new ILMerge();
ilMerge.KeyFile = keyFullPath;
ilMerge.Version = version;
ilMerge.SetInputAssemblies(new[] { gherkinParserFullPath });

ilMerge.OutputFile = outputFileFullPath;
ilMerge.TargetKind = ILMerge.Kind.Dll;

ilMerge.Log = true;

ilMerge.Merge();

Console.WriteLine();
}

private static readonly Dictionary<string, string> languageTranslations =
new Dictionary<string, string>()
{
{"se", "sv"},
};

private class KeywordTranslation
{
public string Keyword;
public string Translation;
}

class LanguageInfo
{
public string Code; // this is the gherkin code of the language can be either a CultureInfo or a specialized culture info
public string CultureInfo; // this is the most specific .NET CultureInfo name that matches to code
public string CompatibleGherkinCode; // only provided if the code was not consistent to the .NET naming (se vs. sv)
public string DefaultSpecificCulture; // only provided is CultureInfo is a neutral language
public string EnglishName;

public readonly List<KeywordTranslation> KeywordTranslations = new List<KeywordTranslation>();
}

//ikvm__gherkin!I18nKeywords_eo.properties
static private readonly Regex i18NResourceRe = new Regex(@"I18nKeywords_(?<lang>.+)\.properties");
private static void GenerateLanguageDescriptions(Assembly parserAssembly, string languagesOutputFile)
{
List<LanguageInfo> languages = CollectSupportedLanguages(parserAssembly);
CollectKeywordTranslations(parserAssembly, languages);

//write languages.xml file
XDocument document = new XDocument();
var rootElement = new XElement(XName.Get("SpecFlowLanguages", ""));
document.Add(rootElement);

foreach (var language in languages)
{
var langElement = new XElement(XName.Get("Language", ""));
langElement.SetAttributeValue(XName.Get("code", ""), language.Code);
langElement.SetAttributeValue(XName.Get("cultureInfo", ""), language.CultureInfo);
if (language.CompatibleGherkinCode != null)
langElement.SetAttributeValue(XName.Get("compatibleGherkinCode", ""), language.CompatibleGherkinCode);
if (language.DefaultSpecificCulture != null)
langElement.SetAttributeValue(XName.Get("defaultSpecificCulture", ""), language.DefaultSpecificCulture);
if (language.EnglishName != null)
langElement.SetAttributeValue(XName.Get("englishName", ""), language.EnglishName);

foreach (var keywordTranslation in language.KeywordTranslations)
{
var keywordElement = new XElement(XName.Get(keywordTranslation.Keyword, ""));
keywordElement.SetValue(keywordTranslation.Translation);
langElement.Add(keywordElement);
}

rootElement.Add(langElement);
}

document.Save(languagesOutputFile);
}

private static void CollectKeywordTranslations(Assembly parserAssembly, List<LanguageInfo> languages)
{
Type i18NType = parserAssembly.GetType("gherkin.I18n", true);
MethodInfo keywordMethod = i18NType.GetMethod("keywords");
if (keywordMethod == null)
throw new InvalidOperationException("keywords method not found");

string[] keywords = new[] { "Feature", "Background", "Scenario", "ScenarioOutline", "Examples", "Given", "When", "Then", "And", "But" };

foreach (var language in languages)
{
var i18N = Activator.CreateInstance(i18NType, language.CompatibleGherkinCode ?? language.Code);

foreach (var keyword in keywords)
{
CollectKeywordTranslations(keyword, language.KeywordTranslations, i18N, keywordMethod);
}
}
}

private static void CollectKeywordTranslations(string keyword, List<KeywordTranslation> keywordTranslations, object i18N, MethodInfo keywordMethod)
{
string gherkinKeyword = keyword == "ScenarioOutline" ? "scenario_outline" : keyword.ToLower();
var keywordList = keywordMethod.Invoke(i18N, new object[] { gherkinKeyword });
object[] keywordListArray = (object[])keywordList.GetType().GetMethod("toArray", new Type[0]).Invoke(keywordList, null);
foreach (string translation in keywordListArray)
{
if (!translation.Trim().StartsWith("*"))
keywordTranslations.Add(new KeywordTranslation { Keyword = keyword, Translation = translation.Trim() });
}
}

private static List<LanguageInfo> CollectSupportedLanguages(Assembly parserAssembly)
{
var languages = new List<LanguageInfo>();
var resourceNames = parserAssembly.GetManifestResourceNames();
foreach (var resourceName in resourceNames)
{
var match = i18NResourceRe.Match(resourceName);
if (!match.Success)
continue;

LanguageInfo languageInfo = new LanguageInfo();
languageInfo.Code = match.Groups["lang"].Value.Replace("_", "-");
if (languageTranslations.ContainsKey(languageInfo.Code))
{
languageInfo.CompatibleGherkinCode = languageInfo.Code;
languageInfo.Code = languageTranslations[languageInfo.Code];
}

CultureInfo cultureInfo = GetCultureInfo(languageInfo.Code);
if (cultureInfo == null)
continue;

languageInfo.CultureInfo = cultureInfo.Name;
languageInfo.EnglishName = cultureInfo.EnglishName;
if (cultureInfo.IsNeutralCulture)
{
CultureInfo defaultSpecificCulture = GetDefaultSpecificCulture(languageInfo.Code);
if (defaultSpecificCulture != null)
languageInfo.DefaultSpecificCulture = defaultSpecificCulture.Name;
}
languages.Add(languageInfo);
}
return languages;
}

private static CultureInfo GetCultureInfo(string code)
{
try
{
return CultureInfo.GetCultureInfo(code);
}
catch (Exception)
{
string[] langParts = code.Split('-');
try
{
var result = CultureInfo.GetCultureInfo(langParts[0]);
Console.WriteLine("Possibly unusable language: {0}", code);
return result;
}
catch (Exception)
{
Console.WriteLine("invlid language: {0}", code);
return null;
}
}
}

private static readonly Dictionary<string, string> wellKnownSpecificCultures =
new Dictionary<string, string>()
{
{"en", "en-US"},
{"no", "nb-NO"},
{"sv", "sv-SE"},
{"uz", "uz-Cyrl-UZ"},
{"sr-Cyrl", "sr-Cyrl-RS"},
{"sr-Latn", "sr-Latn-RS"},
};

private static CultureInfo GetDefaultSpecificCulture(string cultureName)
{
if (wellKnownSpecificCultures.ContainsKey(cultureName))
return CultureInfo.GetCultureInfo(wellKnownSpecificCultures[cultureName]);

string guessedName = cultureName + "-" + cultureName.ToUpper();
var result = CultureInfo.GetCultures(CultureTypes.SpecificCultures).Where(c => c.Name.Equals(guessedName)).FirstOrDefault();
if (result != null)
return result;

var cultures = CultureInfo.GetCultures(CultureTypes.SpecificCultures).Where(c => c.Parent.Name.Equals(cultureName));
result = cultures.FirstOrDefault();
if (cultures.Count() > 1)
{
Console.WriteLine("Multiple possible specific cultures: {0}", string.Join(", ", cultures.Select(c => c.Name).ToArray()));
Console.WriteLine("Default specific culture for {0} is {1}", cultureName, result);
}
return result;
}
}
}
36 changes: 36 additions & 0 deletions Installer/ImportGherkinParser/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("ImportGherkinParser")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("Microsoft")]
[assembly: AssemblyProduct("ImportGherkinParser")]
[assembly: AssemblyCopyright("Copyright © Microsoft 2010")]
[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("2c786e92-a5ea-471d-bcc6-bd0c47fc0c9e")]

// 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")]
2 changes: 2 additions & 0 deletions Installer/ImportGherkinParser/import.cmd
@@ -0,0 +1,2 @@

ImportGherkinParser.exe ..\..\..\..\lib\gherkin\%1 /key:..\..\..\..\specflow.snk /lngout:..\..\..\..\Languages.xml /out:..\..\..\..\lib\gherkin\Gherkin.dll

0 comments on commit 5f87e75

Please sign in to comment.