aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRob Mensching <rob@firegiant.com>2021-05-11 07:37:18 -0700
committerRob Mensching <rob@firegiant.com>2021-05-11 07:37:18 -0700
commit985b2683b2717c83a27295b36e09b126083238f9 (patch)
tree622804d279e0ac58366dee62888e6a317a2b9d41
parent3f583916719eeef598d10a5d4e14ef14f008243b (diff)
parent6a24996a2e831cfe402398af65b31fb1ecd575a9 (diff)
downloadwix-985b2683b2717c83a27295b36e09b126083238f9.tar.gz
wix-985b2683b2717c83a27295b36e09b126083238f9.tar.bz2
wix-985b2683b2717c83a27295b36e09b126083238f9.zip
Merge WixBuildTools
-rw-r--r--src/internal/CSharp.Build.props13
-rw-r--r--src/internal/Cpp.Build.props104
-rw-r--r--src/internal/Directory.Build.props29
-rw-r--r--src/internal/Directory.Build.targets56
-rw-r--r--src/internal/MessagesToMessages/MessagesToMessages.csproj8
-rw-r--r--src/internal/MessagesToMessages/Program.cs261
-rw-r--r--src/internal/MessagesToMessages/Properties/launchSettings.json9
-rw-r--r--src/internal/MessagesToMessages/SimpleJson.cs2127
-rw-r--r--src/internal/README.md2
-rw-r--r--src/internal/TablesAndTuples/ColumnDefinitionEnums.cs56
-rw-r--r--src/internal/TablesAndTuples/Program.cs528
-rw-r--r--src/internal/TablesAndTuples/SimpleJson.cs2127
-rw-r--r--src/internal/TablesAndTuples/TablesAndTuples.csproj8
-rw-r--r--src/internal/TablesAndTuples/WixColumnDefinition.cs296
-rw-r--r--src/internal/TablesAndTuples/WixTableDefinition.cs169
-rw-r--r--src/internal/WixBuildTools.MsgGen/AssemblyInfo.cs8
-rw-r--r--src/internal/WixBuildTools.MsgGen/GenerateMessageFiles.cs250
-rw-r--r--src/internal/WixBuildTools.MsgGen/MsgGen.cs261
-rw-r--r--src/internal/WixBuildTools.MsgGen/WixBuildTools.MsgGen.csproj27
-rw-r--r--src/internal/WixBuildTools.MsgGen/Xsd/messages.xsd101
-rw-r--r--src/internal/WixBuildTools.MsgGen/build/WixBuildTools.MsgGen.targets102
-rw-r--r--src/internal/WixBuildTools.MsgGen/buildCrossTargeting/WixBuildTools.MsgGen.targets6
-rw-r--r--src/internal/WixBuildTools.TestSupport.Native/AssemblyInfo.cpp17
-rw-r--r--src/internal/WixBuildTools.TestSupport.Native/NativeAssert.h85
-rw-r--r--src/internal/WixBuildTools.TestSupport.Native/WixBuildTools.TestSupport.Native.nuspec26
-rw-r--r--src/internal/WixBuildTools.TestSupport.Native/WixBuildTools.TestSupport.Native.vcxproj85
-rw-r--r--src/internal/WixBuildTools.TestSupport.Native/WixBuildTools.TestSupport.Native.vcxproj.filters33
-rw-r--r--src/internal/WixBuildTools.TestSupport.Native/build/WixBuildTools.TestSupport.Native.props29
-rw-r--r--src/internal/WixBuildTools.TestSupport.Native/build/WixBuildTools.TestSupport.Native.targets19
-rw-r--r--src/internal/WixBuildTools.TestSupport.Native/packages.config17
-rw-r--r--src/internal/WixBuildTools.TestSupport.Native/precomp.cpp3
-rw-r--r--src/internal/WixBuildTools.TestSupport.Native/precomp.h11
-rw-r--r--src/internal/WixBuildTools.TestSupport/Builder.cs70
-rw-r--r--src/internal/WixBuildTools.TestSupport/DisposableFileSystem.cs93
-rw-r--r--src/internal/WixBuildTools.TestSupport/DotnetRunner.cs57
-rw-r--r--src/internal/WixBuildTools.TestSupport/ExternalExecutable.cs88
-rw-r--r--src/internal/WixBuildTools.TestSupport/ExternalExecutableResult.cs17
-rw-r--r--src/internal/WixBuildTools.TestSupport/FakeBuildEngine.cs33
-rw-r--r--src/internal/WixBuildTools.TestSupport/MsbuildRunner.cs168
-rw-r--r--src/internal/WixBuildTools.TestSupport/MsbuildRunnerResult.cs19
-rw-r--r--src/internal/WixBuildTools.TestSupport/Pushd.cs46
-rw-r--r--src/internal/WixBuildTools.TestSupport/Query.cs172
-rw-r--r--src/internal/WixBuildTools.TestSupport/RobocopyRunner.cs16
-rw-r--r--src/internal/WixBuildTools.TestSupport/SucceededException.cs18
-rw-r--r--src/internal/WixBuildTools.TestSupport/TestData.cs16
-rw-r--r--src/internal/WixBuildTools.TestSupport/TestDataFolderFileSystem.cs42
-rw-r--r--src/internal/WixBuildTools.TestSupport/VswhereRunner.cs41
-rw-r--r--src/internal/WixBuildTools.TestSupport/WixAssert.cs47
-rw-r--r--src/internal/WixBuildTools.TestSupport/WixBuildTools.TestSupport.csproj31
-rw-r--r--src/internal/WixBuildTools.XsdGen/AssemblyInfo.cs9
-rw-r--r--src/internal/WixBuildTools.XsdGen/CodeDomInterfaces.cs96
-rw-r--r--src/internal/WixBuildTools.XsdGen/CodeDomReader.cs159
-rw-r--r--src/internal/WixBuildTools.XsdGen/ElementCollection.cs642
-rw-r--r--src/internal/WixBuildTools.XsdGen/StronglyTypedClasses.cs1498
-rw-r--r--src/internal/WixBuildTools.XsdGen/WixBuildTools.XsdGen.csproj28
-rw-r--r--src/internal/WixBuildTools.XsdGen/XsdGen.cs124
-rw-r--r--src/internal/WixBuildTools.XsdGen/build/WixBuildTools.XsdGen.targets67
-rw-r--r--src/internal/WixBuildTools.XsdGen/buildCrossTargeting/WixBuildTools.XsdGen.targets6
-rw-r--r--src/internal/WixBuildTools.sln81
-rw-r--r--src/internal/WixBuildTools.v3.ncrunchsolution6
-rw-r--r--src/internal/appveyor.cmd13
-rw-r--r--src/internal/appveyor.yml42
-rw-r--r--src/internal/nuget.config8
63 files changed, 10626 insertions, 0 deletions
diff --git a/src/internal/CSharp.Build.props b/src/internal/CSharp.Build.props
new file mode 100644
index 00000000..81d24ad1
--- /dev/null
+++ b/src/internal/CSharp.Build.props
@@ -0,0 +1,13 @@
1<!-- Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. -->
2<!--
3 Do NOT modify this file. Update the canonical version in Home\repo-template\src\CSharp.Build.props
4 then update all of the repos.
5-->
6<Project>
7 <PropertyGroup>
8 <CheckForOverflowUnderflow>true</CheckForOverflowUnderflow>
9 <SignAssembly>true</SignAssembly>
10 <AssemblyOriginatorKeyFile>$([System.IO.Path]::GetFullPath($(MSBuildThisFileDirectory)wix.snk))</AssemblyOriginatorKeyFile>
11 <NBGV_EmitThisAssemblyClass>false</NBGV_EmitThisAssemblyClass>
12 </PropertyGroup>
13</Project>
diff --git a/src/internal/Cpp.Build.props b/src/internal/Cpp.Build.props
new file mode 100644
index 00000000..44a042c7
--- /dev/null
+++ b/src/internal/Cpp.Build.props
@@ -0,0 +1,104 @@
1<?xml version="1.0" encoding="utf-8"?>
2<!-- Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. -->
3
4<Project>
5 <PropertyGroup>
6 <Platform Condition=" '$(Platform)' == '' OR '$(Platform)' == 'AnyCPU' ">Win32</Platform>
7 <IntDir>$(BaseIntermediateOutputPath)$(Configuration)\$(Platform)\</IntDir>
8 <OutDir>$(OutputPath)$(Platform)\</OutDir>
9 </PropertyGroup>
10
11 <PropertyGroup Condition="'$(WindowsTargetPlatformVersion)'=='' AND '$(VisualStudioVersion)'>='15.0'">
12 <WindowsTargetPlatformVersion>$([Microsoft.Build.Utilities.ToolLocationHelper]::GetLatestSDKTargetPlatformVersion('Windows', '10.0'))</WindowsTargetPlatformVersion>
13 </PropertyGroup>
14
15 <ItemDefinitionGroup>
16 <ClCompile>
17 <DisableSpecificWarnings>$(DisableSpecificCompilerWarnings)</DisableSpecificWarnings>
18 <WarningLevel>Level4</WarningLevel>
19 <AdditionalIncludeDirectories>$(ProjectDir)inc;$(MSBuildProjectDirectory);$(IntDir);$(SqlCESdkIncludePath);$(ProjectAdditionalIncludeDirectories);%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
20 <PreprocessorDefinitions>WIN32;_WINDOWS;_WIN32_MSI=500;_WIN32_WINNT=0x0501;$(ArmPreprocessorDefinitions);$(UnicodePreprocessorDefinitions);_CRT_STDIO_LEGACY_WIDE_SPECIFIERS;_WINSOCK_DEPRECATED_NO_WARNINGS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
21 <PrecompiledHeader>Use</PrecompiledHeader>
22 <PrecompiledHeaderFile>precomp.h</PrecompiledHeaderFile>
23 <CallingConvention Condition="'$(Platform)'=='Win32'">StdCall</CallingConvention>
24 <TreatWarningAsError>true</TreatWarningAsError>
25 <ExceptionHandling>false</ExceptionHandling>
26 <AdditionalOptions>-YlprecompDefine</AdditionalOptions>
27 <AdditionalOptions Condition=" $(PlatformToolset.StartsWith('v14')) ">/Zc:threadSafeInit- %(AdditionalOptions)</AdditionalOptions>
28 <MultiProcessorCompilation Condition=" $(NUMBER_OF_PROCESSORS) &gt; 4 ">true</MultiProcessorCompilation>
29 </ClCompile>
30 <ResourceCompile>
31 <PreprocessorDefinitions>$(ArmPreprocessorDefinitions);%(PreprocessorDefinitions)</PreprocessorDefinitions>
32 <AdditionalIncludeDirectories>$(ProjectAdditionalResourceIncludeDirectories);%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
33 </ResourceCompile>
34 <Lib>
35 <AdditionalLibraryDirectories>$(OutDir);$(AdditionalMultiTargetLibraryPath);$(ProjectAdditionalLibraryDirectories);%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
36 </Lib>
37 <Link>
38 <SubSystem>$(ProjectSubSystem)</SubSystem>
39 <ModuleDefinitionFile>$(ProjectModuleDefinitionFile)</ModuleDefinitionFile>
40 <NoEntryPoint>$(ResourceOnlyDll)</NoEntryPoint>
41 <GenerateDebugInformation>true</GenerateDebugInformation>
42 <AdditionalDependencies>$(ProjectAdditionalLinkLibraries);advapi32.lib;comdlg32.lib;user32.lib;oleaut32.lib;gdi32.lib;shell32.lib;ole32.lib;version.lib;%(AdditionalDependencies)</AdditionalDependencies>
43 <AdditionalLibraryDirectories>$(OutDir);$(AdditionalMultiTargetLibraryPath);$(ArmLibraryDirectories);$(ProjectAdditionalLinkLibraryDirectories);%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
44 <AdditionalOptions Condition=" $(PlatformToolset.StartsWith('v14')) ">/IGNORE:4099 %(AdditionalOptions)</AdditionalOptions>
45 </Link>
46 </ItemDefinitionGroup>
47
48 <ItemDefinitionGroup Condition=" '$(Platform)'=='Win32' and '$(PlatformToolset)'!='v100'">
49 <ClCompile>
50 <EnableEnhancedInstructionSet>NoExtensions</EnableEnhancedInstructionSet>
51 </ClCompile>
52 </ItemDefinitionGroup>
53 <ItemDefinitionGroup Condition=" '$(Platform)'=='arm' ">
54 <ClCompile>
55 <CallingConvention>CDecl</CallingConvention>
56 </ClCompile>
57 </ItemDefinitionGroup>
58 <ItemDefinitionGroup Condition=" '$(ConfigurationType)'=='StaticLibrary' ">
59 <ClCompile>
60 <DebugInformationFormat>OldStyle</DebugInformationFormat>
61 <OmitDefaultLibName>true</OmitDefaultLibName>
62 <IgnoreAllDefaultLibraries>true</IgnoreAllDefaultLibraries>
63 </ClCompile>
64 </ItemDefinitionGroup>
65 <ItemDefinitionGroup Condition=" '$(Configuration)'=='Debug' ">
66 <ClCompile>
67 <Optimization>Disabled</Optimization>
68 <BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks>
69 <PreprocessorDefinitions>_DEBUG;DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
70 <RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
71 </ClCompile>
72 </ItemDefinitionGroup>
73 <ItemDefinitionGroup Condition=" '$(Configuration)'=='Debug' and '$(CLRSupport)'=='true' ">
74 <ClCompile>
75 <BasicRuntimeChecks></BasicRuntimeChecks>
76 <RuntimeLibrary>MultiThreadedDebugDll</RuntimeLibrary>
77 </ClCompile>
78 </ItemDefinitionGroup>
79 <ItemDefinitionGroup Condition=" '$(Configuration)'=='Release' ">
80 <ClCompile>
81 <Optimization>MinSpace</Optimization>
82 <PreprocessorDefinitions>NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
83 <FunctionLevelLinking>true</FunctionLevelLinking>
84 <IntrinsicFunctions>true</IntrinsicFunctions>
85 <RuntimeLibrary>MultiThreaded</RuntimeLibrary>
86 </ClCompile>
87 <Link>
88 <EnableCOMDATFolding>true</EnableCOMDATFolding>
89 <OptimizeReferences>true</OptimizeReferences>
90 </Link>
91 </ItemDefinitionGroup>
92 <ItemDefinitionGroup Condition=" '$(Configuration)'=='Release' and '$(CLRSupport)'=='true' ">
93 <ClCompile>
94 <BasicRuntimeChecks></BasicRuntimeChecks>
95 <RuntimeLibrary>MultiThreadedDll</RuntimeLibrary>
96 </ClCompile>
97 </ItemDefinitionGroup>
98 <ItemDefinitionGroup Condition=" '$(CLRSupport)'=='true' ">
99 <Link>
100 <KeyFile>$(LinkKeyFile)</KeyFile>
101 <DelaySign>$(LinkDelaySign)</DelaySign>
102 </Link>
103 </ItemDefinitionGroup>
104</Project>
diff --git a/src/internal/Directory.Build.props b/src/internal/Directory.Build.props
new file mode 100644
index 00000000..f83cc154
--- /dev/null
+++ b/src/internal/Directory.Build.props
@@ -0,0 +1,29 @@
1<?xml version="1.0" encoding="utf-8"?>
2<!-- Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. -->
3<!--
4 Do NOT modify this file. Update the canonical version in Home\repo-template\src\Directory.Build.props
5 then update all of the repos.
6-->
7<Project>
8 <PropertyGroup>
9 <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
10 <EnableSourceLink Condition=" '$(NCrunch)' == '1' ">false</EnableSourceLink>
11 <MSBuildWarningsAsMessages>MSB3246</MSBuildWarningsAsMessages>
12
13 <ProjectName Condition=" '$(ProjectName)' == '' ">$(MSBuildProjectName)</ProjectName>
14 <BaseOutputPath>$([System.IO.Path]::GetFullPath($(MSBuildThisFileDirectory)..\build\))</BaseOutputPath>
15 <BaseIntermediateOutputPath>$(BaseOutputPath)obj\$(ProjectName)\</BaseIntermediateOutputPath>
16 <OutputPath>$(BaseOutputPath)$(Configuration)\</OutputPath>
17
18 <Authors>WiX Toolset Team</Authors>
19 <Company>WiX Toolset</Company>
20 <Copyright>Copyright (c) .NET Foundation and contributors. All rights reserved.</Copyright>
21 <PackageLicenseExpression>MS-RL</PackageLicenseExpression>
22 <Product>WiX Toolset</Product>
23 </PropertyGroup>
24
25 <Import Project="CSharp.Build.props" Condition=" '$(MSBuildProjectExtension)'=='.csproj' and Exists('CSharp.Build.props') " />
26 <Import Project="Cpp.Build.props" Condition=" Exists('Cpp.Build.props') And '$(MSBuildProjectExtension)'=='.vcxproj' " />
27 <Import Project="Wix.Build.props" Condition=" Exists('Wix.Build.props') And '$(MSBuildProjectExtension)'=='.wixproj' " />
28 <Import Project="Custom.Build.props" Condition=" Exists('Custom.Build.props') " />
29</Project>
diff --git a/src/internal/Directory.Build.targets b/src/internal/Directory.Build.targets
new file mode 100644
index 00000000..cb988931
--- /dev/null
+++ b/src/internal/Directory.Build.targets
@@ -0,0 +1,56 @@
1<?xml version="1.0" encoding="utf-8"?>
2<!-- Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. -->
3<!--
4 Do NOT modify this file. Update the canonical version in Home\repo-template\src\Directory.Build.targets
5 then update all of the repos.
6-->
7<!--
8 Replace PackageReferences with ProjectReferences when the projects can be found in .sln.
9 See the original here: https://github.com/dotnet/sdk/issues/1151#issuecomment-385133284
10-->
11<Project>
12 <PropertyGroup>
13 <CreateDocumentation Condition=" '$(CreateDocumentationFile)'!='true' ">false</CreateDocumentation>
14 <DocumentationFile Condition=" '$(CreateDocumentationFile)'=='true' ">$(OutputPath)\$(AssemblyName).xml</DocumentationFile>
15 </PropertyGroup>
16
17 <PropertyGroup>
18 <ReplacePackageReferences>true</ReplacePackageReferences>
19 <TheSolutionPath Condition=" '$(NCrunch)'=='' ">$(SolutionPath)</TheSolutionPath>
20 <TheSolutionPath Condition=" '$(NCrunch)'=='1' ">$(NCrunchOriginalSolutionPath)</TheSolutionPath>
21 </PropertyGroup>
22
23 <Choose>
24 <When Condition="$(ReplacePackageReferences) AND '$(TheSolutionPath)' != '' AND '$(TheSolutionPath)' != '*undefined*' AND Exists('$(TheSolutionPath)')">
25
26 <PropertyGroup>
27 <SolutionFileContent>$([System.IO.File]::ReadAllText($(TheSolutionPath)))</SolutionFileContent>
28 <SmartSolutionDir>$([System.IO.Path]::GetDirectoryName( $(TheSolutionPath) ))</SmartSolutionDir>
29 <RegexPattern>(?&lt;="[PackageName]", ")(.*)(?=", ")</RegexPattern>
30 </PropertyGroup>
31
32 <ItemGroup>
33 <!-- Keep the identity of the PackageReference -->
34 <SmartPackageReference Include="@(PackageReference)">
35 <PackageName>%(Identity)</PackageName>
36 <InSolution>$(SolutionFileContent.Contains('\%(Identity).csproj'))</InSolution>
37 </SmartPackageReference>
38
39 <!-- Filter them by mapping them to another ItemGroup using the WithMetadataValue item function -->
40 <PackageInSolution Include="@(SmartPackageReference->WithMetadataValue('InSolution', True))">
41 <Pattern>$(RegexPattern.Replace('[PackageName]','%(PackageName)') )</Pattern>
42 <SmartPath>$([System.Text.RegularExpressions.Regex]::Match('$(SolutionFileContent)', '%(Pattern)'))</SmartPath>
43 </PackageInSolution>
44
45 <ProjectReference Include="@(PackageInSolution->'$(SmartSolutionDir)\%(SmartPath)' )"/>
46
47 <!-- Remove the package references that are now referenced as projects -->
48 <PackageReference Remove="@(PackageInSolution->'%(PackageName)' )"/>
49 </ItemGroup>
50
51 </When>
52 </Choose>
53
54 <Import Project="Wix.Build.targets" Condition=" Exists('Wix.Build.targets') And '$(MSBuildProjectExtension)'=='.wixproj' " />
55 <Import Project="Custom.Build.targets" Condition=" Exists('Custom.Build.targets') " />
56</Project>
diff --git a/src/internal/MessagesToMessages/MessagesToMessages.csproj b/src/internal/MessagesToMessages/MessagesToMessages.csproj
new file mode 100644
index 00000000..ce1697ae
--- /dev/null
+++ b/src/internal/MessagesToMessages/MessagesToMessages.csproj
@@ -0,0 +1,8 @@
1<Project Sdk="Microsoft.NET.Sdk">
2
3 <PropertyGroup>
4 <OutputType>Exe</OutputType>
5 <TargetFramework>netcoreapp2.0</TargetFramework>
6 </PropertyGroup>
7
8</Project>
diff --git a/src/internal/MessagesToMessages/Program.cs b/src/internal/MessagesToMessages/Program.cs
new file mode 100644
index 00000000..2d5a3777
--- /dev/null
+++ b/src/internal/MessagesToMessages/Program.cs
@@ -0,0 +1,261 @@
1using System;
2using System.Collections.Generic;
3using System.IO;
4using System.Linq;
5using System.Text;
6using System.Xml.Linq;
7
8namespace MessagesToMessages
9{
10 class Program
11 {
12 static readonly XNamespace ns = "http://schemas.microsoft.com/genmsgs/2004/07/messages";
13 static readonly XName ClassDefinition = ns + "Class";
14 static readonly XName MessageDefinition = ns + "Message";
15 static readonly XName InstanceDefinition = ns + "Instance";
16 static readonly XName ParameterDefinition = ns + "Parameter";
17 static readonly XName Id = "Id";
18 static readonly XName Level = "Level";
19 static readonly XName Number = "Number";
20 static readonly XName SourceLineNumbers = "SourceLineNumbers";
21 static readonly XName Type = "Type";
22 static readonly XName Name = "Name";
23
24 static void Main(string[] args)
25 {
26 if (args.Length == 0)
27 {
28 return;
29 }
30 else if (args.Length < 2)
31 {
32 Console.WriteLine("Need to specify output folder as well.");
33 }
34 else if (!Directory.Exists(args[1]))
35 {
36 Console.WriteLine("Output folder does not exist: {0}", args[1]);
37 }
38
39 var messages = ReadXml(Path.GetFullPath(args[0]));
40
41 foreach (var m in messages.GroupBy(m => m.Level))
42 {
43 var className = m.First().ClassName;
44 var result = GenerateCs(className, m.Key, m);
45
46 var path = Path.Combine(args[1], className + ".cs");
47 File.WriteAllText(path, result);
48 }
49 }
50
51 private static IEnumerable<Message> ReadXml(string inputPath)
52 {
53 var doc = XDocument.Load(inputPath);
54
55 foreach (var xClass in doc.Root.Descendants(ClassDefinition))
56 {
57 var name = xClass.Attribute(Name)?.Value;
58 var level = xClass.Attribute(Level)?.Value;
59
60 if (String.IsNullOrEmpty(name))
61 {
62 name = level + "Messages";
63 }
64 if (String.IsNullOrEmpty(level))
65 {
66 if (name.EndsWith("Errors", StringComparison.InvariantCultureIgnoreCase))
67 {
68 level = "Error";
69 }
70 else if (name.EndsWith("Verboses", StringComparison.InvariantCultureIgnoreCase))
71 {
72 level = "Verbose";
73 }
74 else if (name.EndsWith("Warnings", StringComparison.InvariantCultureIgnoreCase))
75 {
76 level = "Warning";
77 }
78 }
79
80 var unique = new HashSet<string>();
81 var lastNumber = 0;
82 foreach (var xMessage in xClass.Elements(MessageDefinition))
83 {
84 var id = xMessage.Attribute(Id).Value;
85
86 if (!unique.Add(id))
87 {
88 Console.WriteLine("Duplicated message: {0}", id);
89 }
90
91 if (!Int32.TryParse(xMessage.Attribute(Number)?.Value, out var number))
92 {
93 number = lastNumber;
94 }
95 lastNumber = number + 1;
96
97 var sln = xMessage.Attribute(SourceLineNumbers)?.Value != "no";
98
99 var suffix = 0;
100 foreach (var xInstance in xMessage.Elements(InstanceDefinition))
101 {
102 var value = xInstance.Value.Trim();
103
104 var parameters = xInstance.Elements(ParameterDefinition).Select(ReadParameter).ToList();
105
106 yield return new Message { Id = id, ClassName = name, Level = level, Number = number, Parameters = parameters, SourceLineNumbers = sln, Value = value, Suffix = suffix == 0 ? String.Empty : suffix.ToString() };
107
108 ++suffix;
109 }
110 }
111 }
112 }
113
114 private static Parameter ReadParameter(XElement element)
115 {
116 var name = element.Attribute(Name)?.Value;
117 var type = element.Attribute(Type)?.Value ?? "string";
118
119 if (type.StartsWith("System."))
120 {
121 type = type.Substring(7);
122 }
123
124 switch (type)
125 {
126 case "String":
127 type = "string";
128 break;
129
130 case "Int32":
131 type = "int";
132 break;
133
134 case "Int64":
135 type = "long";
136 break;
137 }
138
139 return new Parameter { Name = name, Type = type };
140 }
141
142 private static string GenerateCs(string className, string level, IEnumerable<Message> messages)
143 {
144 var header = String.Join(Environment.NewLine,
145 "// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.",
146 "",
147 "namespace WixToolset.Data",
148 "{",
149 " using System;",
150 " using System.Resources;",
151 "",
152 " public static class {0}",
153 " {");
154
155 var messageFormat = String.Join(Environment.NewLine,
156 " public static Message {0}({1})",
157 " {{",
158 " return Message({4}, Ids.{0}, \"{3}\"{2});",
159 " }}",
160 "");
161
162
163 var endMessagesFormat = String.Join(Environment.NewLine,
164 " private static Message Message(SourceLineNumber sourceLineNumber, Ids id, string format, params object[] args)",
165 " {{",
166 " return new Message(sourceLineNumber, MessageLevel.{0}, (int)id, format, args);",
167 " }}",
168 "",
169 " private static Message Message(SourceLineNumber sourceLineNumber, Ids id, ResourceManager resourceManager, string resourceName, params object[] args)",
170 " {{",
171 " return new Message(sourceLineNumber, MessageLevel.{0}, (int)id, resourceManager, resourceName, args);",
172 " }}",
173 "",
174 " public enum Ids",
175 " {{");
176
177 var idEnumFormat =
178 " {0} = {1},";
179 var footer = String.Join(Environment.NewLine,
180 " }",
181 " }",
182 "}",
183 "");
184
185 var sb = new StringBuilder();
186
187 sb.AppendLine(header.Replace("{0}", className));
188
189 foreach (var message in messages.OrderBy(m => m.Id))
190 {
191 var paramsWithTypes = String.Join(", ", message.Parameters.Select(p => $"{p.Type} {p.Name}"));
192 var paramsWithoutTypes = String.Join(", ", message.Parameters.Select(p => p.Name));
193
194 if (message.SourceLineNumbers)
195 {
196 paramsWithTypes = String.IsNullOrEmpty(paramsWithTypes)
197 ? "SourceLineNumber sourceLineNumbers"
198 : "SourceLineNumber sourceLineNumbers, " + paramsWithTypes;
199 }
200
201 if (!String.IsNullOrEmpty(paramsWithoutTypes))
202 {
203 paramsWithoutTypes = ", " + paramsWithoutTypes;
204 }
205
206 sb.AppendFormat(messageFormat, message.Id, paramsWithTypes, paramsWithoutTypes, ToCSharpString(message.Value), message.SourceLineNumbers ? "sourceLineNumbers" : "null");
207
208 sb.AppendLine();
209 }
210
211 sb.AppendFormat(endMessagesFormat, level);
212 sb.AppendLine();
213
214 var unique = new HashSet<int>();
215 foreach (var message in messages.OrderBy(m => m.Number))
216 {
217 if (unique.Add(message.Number))
218 {
219 sb.AppendFormat(idEnumFormat, message.Id, message.Number);
220 sb.AppendLine();
221 }
222 }
223
224 sb.Append(footer);
225
226 return sb.ToString();
227 }
228
229 private static string ToCSharpString(string value)
230 {
231 return value.Replace("\\", "\\\\").Replace("\"", "\\\"").Replace("\r", "\\r").Replace("\n", "\\n").Replace("\t", "\\t");
232 }
233
234 private class Message
235 {
236 public string Id { get; set; }
237
238 public string Suffix { get; set; }
239
240 public string ClassName { get; set; }
241
242 public string Level { get; set; }
243
244 public int Number { get; set; }
245
246 public bool SourceLineNumbers { get; set; }
247
248 public string Value { get; set; }
249
250 public IEnumerable<Parameter> Parameters { get; set; }
251 }
252
253
254 private class Parameter
255 {
256 public string Name { get; set; }
257
258 public string Type { get; set; }
259 }
260 }
261}
diff --git a/src/internal/MessagesToMessages/Properties/launchSettings.json b/src/internal/MessagesToMessages/Properties/launchSettings.json
new file mode 100644
index 00000000..dc7570f6
--- /dev/null
+++ b/src/internal/MessagesToMessages/Properties/launchSettings.json
@@ -0,0 +1,9 @@
1{
2 "profiles": {
3 "TablesAndSymbols": {
4 "commandName": "Project",
5 "commandLineArgs": "E:\\src\\wixtoolset\\Core\\src\\WixToolset.Core\\Data\\messages.xml E:\\src\\wixtoolset\\Data\\src\\WixToolset.Data",
6 "workingDirectory": "E:\\src\\wixtoolset\\Core\\src\\WixToolset.Core\\"
7 }
8 }
9} \ No newline at end of file
diff --git a/src/internal/MessagesToMessages/SimpleJson.cs b/src/internal/MessagesToMessages/SimpleJson.cs
new file mode 100644
index 00000000..3d956f6e
--- /dev/null
+++ b/src/internal/MessagesToMessages/SimpleJson.cs
@@ -0,0 +1,2127 @@
1//-----------------------------------------------------------------------
2// <copyright file="SimpleJson.cs" company="The Outercurve Foundation">
3// Copyright (c) 2011, The Outercurve Foundation.
4//
5// Licensed under the MIT License (the "License");
6// you may not use this file except in compliance with the License.
7// You may obtain a copy of the License at
8// http://www.opensource.org/licenses/mit-license.php
9//
10// Unless required by applicable law or agreed to in writing, software
11// distributed under the License is distributed on an "AS IS" BASIS,
12// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13// See the License for the specific language governing permissions and
14// limitations under the License.
15// </copyright>
16// <author>Nathan Totten (ntotten.com), Jim Zimmerman (jimzimmerman.com) and Prabir Shrestha (prabir.me)</author>
17// <website>https://github.com/facebook-csharp-sdk/simple-json</website>
18//-----------------------------------------------------------------------
19
20// VERSION:
21
22// NOTE: uncomment the following line to make SimpleJson class internal.
23#define SIMPLE_JSON_INTERNAL
24
25// NOTE: uncomment the following line to make JsonArray and JsonObject class internal.
26#define SIMPLE_JSON_OBJARRAYINTERNAL
27
28// NOTE: uncomment the following line to enable dynamic support.
29//#define SIMPLE_JSON_DYNAMIC
30
31// NOTE: uncomment the following line to enable DataContract support.
32//#define SIMPLE_JSON_DATACONTRACT
33
34// NOTE: uncomment the following line to enable IReadOnlyCollection<T> and IReadOnlyList<T> support.
35//#define SIMPLE_JSON_READONLY_COLLECTIONS
36
37// NOTE: uncomment the following line to disable linq expressions/compiled lambda (better performance) instead of method.invoke().
38// define if you are using .net framework <= 3.0 or < WP7.5
39//#define SIMPLE_JSON_NO_LINQ_EXPRESSION
40
41// NOTE: uncomment the following line if you are compiling under Window Metro style application/library.
42// usually already defined in properties
43//#define NETFX_CORE;
44
45// If you are targetting WinStore, WP8 and NET4.5+ PCL make sure to #define SIMPLE_JSON_TYPEINFO;
46
47// original json parsing code from http://techblog.procurios.nl/k/618/news/view/14605/14863/How-do-I-write-my-own-parser-for-JSON.html
48
49#if NETFX_CORE
50#define SIMPLE_JSON_TYPEINFO
51#endif
52
53using System;
54using System.CodeDom.Compiler;
55using System.Collections;
56using System.Collections.Generic;
57#if !SIMPLE_JSON_NO_LINQ_EXPRESSION
58using System.Linq.Expressions;
59#endif
60using System.ComponentModel;
61using System.Diagnostics.CodeAnalysis;
62#if SIMPLE_JSON_DYNAMIC
63using System.Dynamic;
64#endif
65using System.Globalization;
66using System.Reflection;
67using System.Runtime.Serialization;
68using System.Text;
69using SimpleJson.Reflection;
70
71// ReSharper disable LoopCanBeConvertedToQuery
72// ReSharper disable RedundantExplicitArrayCreation
73// ReSharper disable SuggestUseVarKeywordEvident
74namespace SimpleJson
75{
76 /// <summary>
77 /// Represents the json array.
78 /// </summary>
79 [GeneratedCode("simple-json", "1.0.0")]
80 [EditorBrowsable(EditorBrowsableState.Never)]
81 [SuppressMessage("Microsoft.Naming", "CA1710:IdentifiersShouldHaveCorrectSuffix")]
82#if SIMPLE_JSON_OBJARRAYINTERNAL
83 internal
84#else
85 public
86#endif
87 class JsonArray : List<object>
88 {
89 /// <summary>
90 /// Initializes a new instance of the <see cref="JsonArray"/> class.
91 /// </summary>
92 public JsonArray() { }
93
94 /// <summary>
95 /// Initializes a new instance of the <see cref="JsonArray"/> class.
96 /// </summary>
97 /// <param name="capacity">The capacity of the json array.</param>
98 public JsonArray(int capacity) : base(capacity) { }
99
100 /// <summary>
101 /// The json representation of the array.
102 /// </summary>
103 /// <returns>The json representation of the array.</returns>
104 public override string ToString()
105 {
106 return SimpleJson.SerializeObject(this) ?? string.Empty;
107 }
108 }
109
110 /// <summary>
111 /// Represents the json object.
112 /// </summary>
113 [GeneratedCode("simple-json", "1.0.0")]
114 [EditorBrowsable(EditorBrowsableState.Never)]
115 [SuppressMessage("Microsoft.Naming", "CA1710:IdentifiersShouldHaveCorrectSuffix")]
116#if SIMPLE_JSON_OBJARRAYINTERNAL
117 internal
118#else
119 public
120#endif
121 class JsonObject :
122#if SIMPLE_JSON_DYNAMIC
123 DynamicObject,
124#endif
125 IDictionary<string, object>
126 {
127 /// <summary>
128 /// The internal member dictionary.
129 /// </summary>
130 private readonly Dictionary<string, object> _members;
131
132 /// <summary>
133 /// Initializes a new instance of <see cref="JsonObject"/>.
134 /// </summary>
135 public JsonObject()
136 {
137 _members = new Dictionary<string, object>();
138 }
139
140 /// <summary>
141 /// Initializes a new instance of <see cref="JsonObject"/>.
142 /// </summary>
143 /// <param name="comparer">The <see cref="T:System.Collections.Generic.IEqualityComparer`1"/> implementation to use when comparing keys, or null to use the default <see cref="T:System.Collections.Generic.EqualityComparer`1"/> for the type of the key.</param>
144 public JsonObject(IEqualityComparer<string> comparer)
145 {
146 _members = new Dictionary<string, object>(comparer);
147 }
148
149 /// <summary>
150 /// Gets the <see cref="System.Object"/> at the specified index.
151 /// </summary>
152 /// <value></value>
153 public object this[int index]
154 {
155 get { return GetAtIndex(_members, index); }
156 }
157
158 internal static object GetAtIndex(IDictionary<string, object> obj, int index)
159 {
160 if (obj == null)
161 throw new ArgumentNullException("obj");
162 if (index >= obj.Count)
163 throw new ArgumentOutOfRangeException("index");
164 int i = 0;
165 foreach (KeyValuePair<string, object> o in obj)
166 if (i++ == index) return o.Value;
167 return null;
168 }
169
170 /// <summary>
171 /// Adds the specified key.
172 /// </summary>
173 /// <param name="key">The key.</param>
174 /// <param name="value">The value.</param>
175 public void Add(string key, object value)
176 {
177 _members.Add(key, value);
178 }
179
180 /// <summary>
181 /// Determines whether the specified key contains key.
182 /// </summary>
183 /// <param name="key">The key.</param>
184 /// <returns>
185 /// <c>true</c> if the specified key contains key; otherwise, <c>false</c>.
186 /// </returns>
187 public bool ContainsKey(string key)
188 {
189 return _members.ContainsKey(key);
190 }
191
192 /// <summary>
193 /// Gets the keys.
194 /// </summary>
195 /// <value>The keys.</value>
196 public ICollection<string> Keys
197 {
198 get { return _members.Keys; }
199 }
200
201 /// <summary>
202 /// Removes the specified key.
203 /// </summary>
204 /// <param name="key">The key.</param>
205 /// <returns></returns>
206 public bool Remove(string key)
207 {
208 return _members.Remove(key);
209 }
210
211 /// <summary>
212 /// Tries the get value.
213 /// </summary>
214 /// <param name="key">The key.</param>
215 /// <param name="value">The value.</param>
216 /// <returns></returns>
217 public bool TryGetValue(string key, out object value)
218 {
219 return _members.TryGetValue(key, out value);
220 }
221
222 /// <summary>
223 /// Gets the values.
224 /// </summary>
225 /// <value>The values.</value>
226 public ICollection<object> Values
227 {
228 get { return _members.Values; }
229 }
230
231 /// <summary>
232 /// Gets or sets the <see cref="System.Object"/> with the specified key.
233 /// </summary>
234 /// <value></value>
235 public object this[string key]
236 {
237 get { return _members[key]; }
238 set { _members[key] = value; }
239 }
240
241 /// <summary>
242 /// Adds the specified item.
243 /// </summary>
244 /// <param name="item">The item.</param>
245 public void Add(KeyValuePair<string, object> item)
246 {
247 _members.Add(item.Key, item.Value);
248 }
249
250 /// <summary>
251 /// Clears this instance.
252 /// </summary>
253 public void Clear()
254 {
255 _members.Clear();
256 }
257
258 /// <summary>
259 /// Determines whether [contains] [the specified item].
260 /// </summary>
261 /// <param name="item">The item.</param>
262 /// <returns>
263 /// <c>true</c> if [contains] [the specified item]; otherwise, <c>false</c>.
264 /// </returns>
265 public bool Contains(KeyValuePair<string, object> item)
266 {
267 return _members.ContainsKey(item.Key) && _members[item.Key] == item.Value;
268 }
269
270 /// <summary>
271 /// Copies to.
272 /// </summary>
273 /// <param name="array">The array.</param>
274 /// <param name="arrayIndex">Index of the array.</param>
275 public void CopyTo(KeyValuePair<string, object>[] array, int arrayIndex)
276 {
277 if (array == null) throw new ArgumentNullException("array");
278 int num = Count;
279 foreach (KeyValuePair<string, object> kvp in this)
280 {
281 array[arrayIndex++] = kvp;
282 if (--num <= 0)
283 return;
284 }
285 }
286
287 /// <summary>
288 /// Gets the count.
289 /// </summary>
290 /// <value>The count.</value>
291 public int Count
292 {
293 get { return _members.Count; }
294 }
295
296 /// <summary>
297 /// Gets a value indicating whether this instance is read only.
298 /// </summary>
299 /// <value>
300 /// <c>true</c> if this instance is read only; otherwise, <c>false</c>.
301 /// </value>
302 public bool IsReadOnly
303 {
304 get { return false; }
305 }
306
307 /// <summary>
308 /// Removes the specified item.
309 /// </summary>
310 /// <param name="item">The item.</param>
311 /// <returns></returns>
312 public bool Remove(KeyValuePair<string, object> item)
313 {
314 return _members.Remove(item.Key);
315 }
316
317 /// <summary>
318 /// Gets the enumerator.
319 /// </summary>
320 /// <returns></returns>
321 public IEnumerator<KeyValuePair<string, object>> GetEnumerator()
322 {
323 return _members.GetEnumerator();
324 }
325
326 /// <summary>
327 /// Returns an enumerator that iterates through a collection.
328 /// </summary>
329 /// <returns>
330 /// An <see cref="T:System.Collections.IEnumerator"/> object that can be used to iterate through the collection.
331 /// </returns>
332 IEnumerator IEnumerable.GetEnumerator()
333 {
334 return _members.GetEnumerator();
335 }
336
337 /// <summary>
338 /// Returns a json <see cref="T:System.String"/> that represents the current <see cref="T:System.Object"/>.
339 /// </summary>
340 /// <returns>
341 /// A json <see cref="T:System.String"/> that represents the current <see cref="T:System.Object"/>.
342 /// </returns>
343 public override string ToString()
344 {
345 return SimpleJson.SerializeObject(this);
346 }
347
348#if SIMPLE_JSON_DYNAMIC
349 /// <summary>
350 /// Provides implementation for type conversion operations. Classes derived from the <see cref="T:System.Dynamic.DynamicObject"/> class can override this method to specify dynamic behavior for operations that convert an object from one type to another.
351 /// </summary>
352 /// <param name="binder">Provides information about the conversion operation. The binder.Type property provides the type to which the object must be converted. For example, for the statement (String)sampleObject in C# (CType(sampleObject, Type) in Visual Basic), where sampleObject is an instance of the class derived from the <see cref="T:System.Dynamic.DynamicObject"/> class, binder.Type returns the <see cref="T:System.String"/> type. The binder.Explicit property provides information about the kind of conversion that occurs. It returns true for explicit conversion and false for implicit conversion.</param>
353 /// <param name="result">The result of the type conversion operation.</param>
354 /// <returns>
355 /// Alwasy returns true.
356 /// </returns>
357 public override bool TryConvert(ConvertBinder binder, out object result)
358 {
359 // <pex>
360 if (binder == null)
361 throw new ArgumentNullException("binder");
362 // </pex>
363 Type targetType = binder.Type;
364
365 if ((targetType == typeof(IEnumerable)) ||
366 (targetType == typeof(IEnumerable<KeyValuePair<string, object>>)) ||
367 (targetType == typeof(IDictionary<string, object>)) ||
368 (targetType == typeof(IDictionary)))
369 {
370 result = this;
371 return true;
372 }
373
374 return base.TryConvert(binder, out result);
375 }
376
377 /// <summary>
378 /// Provides the implementation for operations that delete an object member. This method is not intended for use in C# or Visual Basic.
379 /// </summary>
380 /// <param name="binder">Provides information about the deletion.</param>
381 /// <returns>
382 /// Alwasy returns true.
383 /// </returns>
384 public override bool TryDeleteMember(DeleteMemberBinder binder)
385 {
386 // <pex>
387 if (binder == null)
388 throw new ArgumentNullException("binder");
389 // </pex>
390 return _members.Remove(binder.Name);
391 }
392
393 /// <summary>
394 /// Provides the implementation for operations that get a value by index. Classes derived from the <see cref="T:System.Dynamic.DynamicObject"/> class can override this method to specify dynamic behavior for indexing operations.
395 /// </summary>
396 /// <param name="binder">Provides information about the operation.</param>
397 /// <param name="indexes">The indexes that are used in the operation. For example, for the sampleObject[3] operation in C# (sampleObject(3) in Visual Basic), where sampleObject is derived from the DynamicObject class, <paramref name="indexes"/> is equal to 3.</param>
398 /// <param name="result">The result of the index operation.</param>
399 /// <returns>
400 /// Alwasy returns true.
401 /// </returns>
402 public override bool TryGetIndex(GetIndexBinder binder, object[] indexes, out object result)
403 {
404 if (indexes == null) throw new ArgumentNullException("indexes");
405 if (indexes.Length == 1)
406 {
407 result = ((IDictionary<string, object>)this)[(string)indexes[0]];
408 return true;
409 }
410 result = null;
411 return true;
412 }
413
414 /// <summary>
415 /// Provides the implementation for operations that get member values. Classes derived from the <see cref="T:System.Dynamic.DynamicObject"/> class can override this method to specify dynamic behavior for operations such as getting a value for a property.
416 /// </summary>
417 /// <param name="binder">Provides information about the object that called the dynamic operation. The binder.Name property provides the name of the member on which the dynamic operation is performed. For example, for the Console.WriteLine(sampleObject.SampleProperty) statement, where sampleObject is an instance of the class derived from the <see cref="T:System.Dynamic.DynamicObject"/> class, binder.Name returns "SampleProperty". The binder.IgnoreCase property specifies whether the member name is case-sensitive.</param>
418 /// <param name="result">The result of the get operation. For example, if the method is called for a property, you can assign the property value to <paramref name="result"/>.</param>
419 /// <returns>
420 /// Alwasy returns true.
421 /// </returns>
422 public override bool TryGetMember(GetMemberBinder binder, out object result)
423 {
424 object value;
425 if (_members.TryGetValue(binder.Name, out value))
426 {
427 result = value;
428 return true;
429 }
430 result = null;
431 return true;
432 }
433
434 /// <summary>
435 /// Provides the implementation for operations that set a value by index. Classes derived from the <see cref="T:System.Dynamic.DynamicObject"/> class can override this method to specify dynamic behavior for operations that access objects by a specified index.
436 /// </summary>
437 /// <param name="binder">Provides information about the operation.</param>
438 /// <param name="indexes">The indexes that are used in the operation. For example, for the sampleObject[3] = 10 operation in C# (sampleObject(3) = 10 in Visual Basic), where sampleObject is derived from the <see cref="T:System.Dynamic.DynamicObject"/> class, <paramref name="indexes"/> is equal to 3.</param>
439 /// <param name="value">The value to set to the object that has the specified index. For example, for the sampleObject[3] = 10 operation in C# (sampleObject(3) = 10 in Visual Basic), where sampleObject is derived from the <see cref="T:System.Dynamic.DynamicObject"/> class, <paramref name="value"/> is equal to 10.</param>
440 /// <returns>
441 /// true if the operation is successful; otherwise, false. If this method returns false, the run-time binder of the language determines the behavior. (In most cases, a language-specific run-time exception is thrown.
442 /// </returns>
443 public override bool TrySetIndex(SetIndexBinder binder, object[] indexes, object value)
444 {
445 if (indexes == null) throw new ArgumentNullException("indexes");
446 if (indexes.Length == 1)
447 {
448 ((IDictionary<string, object>)this)[(string)indexes[0]] = value;
449 return true;
450 }
451 return base.TrySetIndex(binder, indexes, value);
452 }
453
454 /// <summary>
455 /// Provides the implementation for operations that set member values. Classes derived from the <see cref="T:System.Dynamic.DynamicObject"/> class can override this method to specify dynamic behavior for operations such as setting a value for a property.
456 /// </summary>
457 /// <param name="binder">Provides information about the object that called the dynamic operation. The binder.Name property provides the name of the member to which the value is being assigned. For example, for the statement sampleObject.SampleProperty = "Test", where sampleObject is an instance of the class derived from the <see cref="T:System.Dynamic.DynamicObject"/> class, binder.Name returns "SampleProperty". The binder.IgnoreCase property specifies whether the member name is case-sensitive.</param>
458 /// <param name="value">The value to set to the member. For example, for sampleObject.SampleProperty = "Test", where sampleObject is an instance of the class derived from the <see cref="T:System.Dynamic.DynamicObject"/> class, the <paramref name="value"/> is "Test".</param>
459 /// <returns>
460 /// true if the operation is successful; otherwise, false. If this method returns false, the run-time binder of the language determines the behavior. (In most cases, a language-specific run-time exception is thrown.)
461 /// </returns>
462 public override bool TrySetMember(SetMemberBinder binder, object value)
463 {
464 // <pex>
465 if (binder == null)
466 throw new ArgumentNullException("binder");
467 // </pex>
468 _members[binder.Name] = value;
469 return true;
470 }
471
472 /// <summary>
473 /// Returns the enumeration of all dynamic member names.
474 /// </summary>
475 /// <returns>
476 /// A sequence that contains dynamic member names.
477 /// </returns>
478 public override IEnumerable<string> GetDynamicMemberNames()
479 {
480 foreach (var key in Keys)
481 yield return key;
482 }
483#endif
484 }
485}
486
487namespace SimpleJson
488{
489 /// <summary>
490 /// This class encodes and decodes JSON strings.
491 /// Spec. details, see http://www.json.org/
492 ///
493 /// JSON uses Arrays and Objects. These correspond here to the datatypes JsonArray(IList&lt;object>) and JsonObject(IDictionary&lt;string,object>).
494 /// All numbers are parsed to doubles.
495 /// </summary>
496 [GeneratedCode("simple-json", "1.0.0")]
497#if SIMPLE_JSON_INTERNAL
498 internal
499#else
500 public
501#endif
502 static class SimpleJson
503 {
504 private const int TOKEN_NONE = 0;
505 private const int TOKEN_CURLY_OPEN = 1;
506 private const int TOKEN_CURLY_CLOSE = 2;
507 private const int TOKEN_SQUARED_OPEN = 3;
508 private const int TOKEN_SQUARED_CLOSE = 4;
509 private const int TOKEN_COLON = 5;
510 private const int TOKEN_COMMA = 6;
511 private const int TOKEN_STRING = 7;
512 private const int TOKEN_NUMBER = 8;
513 private const int TOKEN_TRUE = 9;
514 private const int TOKEN_FALSE = 10;
515 private const int TOKEN_NULL = 11;
516 private const int BUILDER_CAPACITY = 2000;
517
518 private static readonly char[] EscapeTable;
519 private static readonly char[] EscapeCharacters = new char[] { '"', '\\', '\b', '\f', '\n', '\r', '\t' };
520 private static readonly string EscapeCharactersString = new string(EscapeCharacters);
521
522 static SimpleJson()
523 {
524 EscapeTable = new char[93];
525 EscapeTable['"'] = '"';
526 EscapeTable['\\'] = '\\';
527 EscapeTable['\b'] = 'b';
528 EscapeTable['\f'] = 'f';
529 EscapeTable['\n'] = 'n';
530 EscapeTable['\r'] = 'r';
531 EscapeTable['\t'] = 't';
532 }
533
534 /// <summary>
535 /// Parses the string json into a value
536 /// </summary>
537 /// <param name="json">A JSON string.</param>
538 /// <returns>An IList&lt;object>, a IDictionary&lt;string,object>, a double, a string, null, true, or false</returns>
539 public static object DeserializeObject(string json)
540 {
541 object obj;
542 if (TryDeserializeObject(json, out obj))
543 return obj;
544 throw new SerializationException("Invalid JSON string");
545 }
546
547 /// <summary>
548 /// Try parsing the json string into a value.
549 /// </summary>
550 /// <param name="json">
551 /// A JSON string.
552 /// </param>
553 /// <param name="obj">
554 /// The object.
555 /// </param>
556 /// <returns>
557 /// Returns true if successfull otherwise false.
558 /// </returns>
559 [SuppressMessage("Microsoft.Design", "CA1007:UseGenericsWhereAppropriate", Justification = "Need to support .NET 2")]
560 public static bool TryDeserializeObject(string json, out object obj)
561 {
562 bool success = true;
563 if (json != null)
564 {
565 char[] charArray = json.ToCharArray();
566 int index = 0;
567 obj = ParseValue(charArray, ref index, ref success);
568 }
569 else
570 obj = null;
571
572 return success;
573 }
574
575 public static object DeserializeObject(string json, Type type, IJsonSerializerStrategy jsonSerializerStrategy)
576 {
577 object jsonObject = DeserializeObject(json);
578 return type == null || jsonObject != null && ReflectionUtils.IsAssignableFrom(jsonObject.GetType(), type)
579 ? jsonObject
580 : (jsonSerializerStrategy ?? CurrentJsonSerializerStrategy).DeserializeObject(jsonObject, type);
581 }
582
583 public static object DeserializeObject(string json, Type type)
584 {
585 return DeserializeObject(json, type, null);
586 }
587
588 public static T DeserializeObject<T>(string json, IJsonSerializerStrategy jsonSerializerStrategy)
589 {
590 return (T)DeserializeObject(json, typeof(T), jsonSerializerStrategy);
591 }
592
593 public static T DeserializeObject<T>(string json)
594 {
595 return (T)DeserializeObject(json, typeof(T), null);
596 }
597
598 /// <summary>
599 /// Converts a IDictionary&lt;string,object> / IList&lt;object> object into a JSON string
600 /// </summary>
601 /// <param name="json">A IDictionary&lt;string,object> / IList&lt;object></param>
602 /// <param name="jsonSerializerStrategy">Serializer strategy to use</param>
603 /// <returns>A JSON encoded string, or null if object 'json' is not serializable</returns>
604 public static string SerializeObject(object json, IJsonSerializerStrategy jsonSerializerStrategy)
605 {
606 StringBuilder builder = new StringBuilder(BUILDER_CAPACITY);
607 bool success = SerializeValue(jsonSerializerStrategy, json, builder);
608 return (success ? builder.ToString() : null);
609 }
610
611 public static string SerializeObject(object json)
612 {
613 return SerializeObject(json, CurrentJsonSerializerStrategy);
614 }
615
616 public static string EscapeToJavascriptString(string jsonString)
617 {
618 if (string.IsNullOrEmpty(jsonString))
619 return jsonString;
620
621 StringBuilder sb = new StringBuilder();
622 char c;
623
624 for (int i = 0; i < jsonString.Length;)
625 {
626 c = jsonString[i++];
627
628 if (c == '\\')
629 {
630 int remainingLength = jsonString.Length - i;
631 if (remainingLength >= 2)
632 {
633 char lookahead = jsonString[i];
634 if (lookahead == '\\')
635 {
636 sb.Append('\\');
637 ++i;
638 }
639 else if (lookahead == '"')
640 {
641 sb.Append("\"");
642 ++i;
643 }
644 else if (lookahead == 't')
645 {
646 sb.Append('\t');
647 ++i;
648 }
649 else if (lookahead == 'b')
650 {
651 sb.Append('\b');
652 ++i;
653 }
654 else if (lookahead == 'n')
655 {
656 sb.Append('\n');
657 ++i;
658 }
659 else if (lookahead == 'r')
660 {
661 sb.Append('\r');
662 ++i;
663 }
664 }
665 }
666 else
667 {
668 sb.Append(c);
669 }
670 }
671 return sb.ToString();
672 }
673
674 static IDictionary<string, object> ParseObject(char[] json, ref int index, ref bool success)
675 {
676 IDictionary<string, object> table = new JsonObject();
677 int token;
678
679 // {
680 NextToken(json, ref index);
681
682 bool done = false;
683 while (!done)
684 {
685 token = LookAhead(json, index);
686 if (token == TOKEN_NONE)
687 {
688 success = false;
689 return null;
690 }
691 else if (token == TOKEN_COMMA)
692 NextToken(json, ref index);
693 else if (token == TOKEN_CURLY_CLOSE)
694 {
695 NextToken(json, ref index);
696 return table;
697 }
698 else
699 {
700 // name
701 string name = ParseString(json, ref index, ref success);
702 if (!success)
703 {
704 success = false;
705 return null;
706 }
707 // :
708 token = NextToken(json, ref index);
709 if (token != TOKEN_COLON)
710 {
711 success = false;
712 return null;
713 }
714 // value
715 object value = ParseValue(json, ref index, ref success);
716 if (!success)
717 {
718 success = false;
719 return null;
720 }
721 table[name] = value;
722 }
723 }
724 return table;
725 }
726
727 static JsonArray ParseArray(char[] json, ref int index, ref bool success)
728 {
729 JsonArray array = new JsonArray();
730
731 // [
732 NextToken(json, ref index);
733
734 bool done = false;
735 while (!done)
736 {
737 int token = LookAhead(json, index);
738 if (token == TOKEN_NONE)
739 {
740 success = false;
741 return null;
742 }
743 else if (token == TOKEN_COMMA)
744 NextToken(json, ref index);
745 else if (token == TOKEN_SQUARED_CLOSE)
746 {
747 NextToken(json, ref index);
748 break;
749 }
750 else
751 {
752 object value = ParseValue(json, ref index, ref success);
753 if (!success)
754 return null;
755 array.Add(value);
756 }
757 }
758 return array;
759 }
760
761 static object ParseValue(char[] json, ref int index, ref bool success)
762 {
763 switch (LookAhead(json, index))
764 {
765 case TOKEN_STRING:
766 return ParseString(json, ref index, ref success);
767 case TOKEN_NUMBER:
768 return ParseNumber(json, ref index, ref success);
769 case TOKEN_CURLY_OPEN:
770 return ParseObject(json, ref index, ref success);
771 case TOKEN_SQUARED_OPEN:
772 return ParseArray(json, ref index, ref success);
773 case TOKEN_TRUE:
774 NextToken(json, ref index);
775 return true;
776 case TOKEN_FALSE:
777 NextToken(json, ref index);
778 return false;
779 case TOKEN_NULL:
780 NextToken(json, ref index);
781 return null;
782 case TOKEN_NONE:
783 break;
784 }
785 success = false;
786 return null;
787 }
788
789 static string ParseString(char[] json, ref int index, ref bool success)
790 {
791 StringBuilder s = new StringBuilder(BUILDER_CAPACITY);
792 char c;
793
794 EatWhitespace(json, ref index);
795
796 // "
797 c = json[index++];
798 bool complete = false;
799 while (!complete)
800 {
801 if (index == json.Length)
802 break;
803
804 c = json[index++];
805 if (c == '"')
806 {
807 complete = true;
808 break;
809 }
810 else if (c == '\\')
811 {
812 if (index == json.Length)
813 break;
814 c = json[index++];
815 if (c == '"')
816 s.Append('"');
817 else if (c == '\\')
818 s.Append('\\');
819 else if (c == '/')
820 s.Append('/');
821 else if (c == 'b')
822 s.Append('\b');
823 else if (c == 'f')
824 s.Append('\f');
825 else if (c == 'n')
826 s.Append('\n');
827 else if (c == 'r')
828 s.Append('\r');
829 else if (c == 't')
830 s.Append('\t');
831 else if (c == 'u')
832 {
833 int remainingLength = json.Length - index;
834 if (remainingLength >= 4)
835 {
836 // parse the 32 bit hex into an integer codepoint
837 uint codePoint;
838 if (!(success = UInt32.TryParse(new string(json, index, 4), NumberStyles.HexNumber, CultureInfo.InvariantCulture, out codePoint)))
839 return "";
840
841 // convert the integer codepoint to a unicode char and add to string
842 if (0xD800 <= codePoint && codePoint <= 0xDBFF) // if high surrogate
843 {
844 index += 4; // skip 4 chars
845 remainingLength = json.Length - index;
846 if (remainingLength >= 6)
847 {
848 uint lowCodePoint;
849 if (new string(json, index, 2) == "\\u" && UInt32.TryParse(new string(json, index + 2, 4), NumberStyles.HexNumber, CultureInfo.InvariantCulture, out lowCodePoint))
850 {
851 if (0xDC00 <= lowCodePoint && lowCodePoint <= 0xDFFF) // if low surrogate
852 {
853 s.Append((char)codePoint);
854 s.Append((char)lowCodePoint);
855 index += 6; // skip 6 chars
856 continue;
857 }
858 }
859 }
860 success = false; // invalid surrogate pair
861 return "";
862 }
863 s.Append(ConvertFromUtf32((int)codePoint));
864 // skip 4 chars
865 index += 4;
866 }
867 else
868 break;
869 }
870 }
871 else
872 s.Append(c);
873 }
874 if (!complete)
875 {
876 success = false;
877 return null;
878 }
879 return s.ToString();
880 }
881
882 private static string ConvertFromUtf32(int utf32)
883 {
884 // http://www.java2s.com/Open-Source/CSharp/2.6.4-mono-.net-core/System/System/Char.cs.htm
885 if (utf32 < 0 || utf32 > 0x10FFFF)
886 throw new ArgumentOutOfRangeException("utf32", "The argument must be from 0 to 0x10FFFF.");
887 if (0xD800 <= utf32 && utf32 <= 0xDFFF)
888 throw new ArgumentOutOfRangeException("utf32", "The argument must not be in surrogate pair range.");
889 if (utf32 < 0x10000)
890 return new string((char)utf32, 1);
891 utf32 -= 0x10000;
892 return new string(new char[] { (char)((utf32 >> 10) + 0xD800), (char)(utf32 % 0x0400 + 0xDC00) });
893 }
894
895 static object ParseNumber(char[] json, ref int index, ref bool success)
896 {
897 EatWhitespace(json, ref index);
898 int lastIndex = GetLastIndexOfNumber(json, index);
899 int charLength = (lastIndex - index) + 1;
900 object returnNumber;
901 string str = new string(json, index, charLength);
902 if (str.IndexOf(".", StringComparison.OrdinalIgnoreCase) != -1 || str.IndexOf("e", StringComparison.OrdinalIgnoreCase) != -1)
903 {
904 double number;
905 success = double.TryParse(new string(json, index, charLength), NumberStyles.Any, CultureInfo.InvariantCulture, out number);
906 returnNumber = number;
907 }
908 else
909 {
910 long number;
911 success = long.TryParse(new string(json, index, charLength), NumberStyles.Any, CultureInfo.InvariantCulture, out number);
912 returnNumber = number;
913 }
914 index = lastIndex + 1;
915 return returnNumber;
916 }
917
918 static int GetLastIndexOfNumber(char[] json, int index)
919 {
920 int lastIndex;
921 for (lastIndex = index; lastIndex < json.Length; lastIndex++)
922 if ("0123456789+-.eE".IndexOf(json[lastIndex]) == -1) break;
923 return lastIndex - 1;
924 }
925
926 static void EatWhitespace(char[] json, ref int index)
927 {
928 for (; index < json.Length; index++)
929 if (" \t\n\r\b\f".IndexOf(json[index]) == -1) break;
930 }
931
932 static int LookAhead(char[] json, int index)
933 {
934 int saveIndex = index;
935 return NextToken(json, ref saveIndex);
936 }
937
938 [SuppressMessage("Microsoft.Maintainability", "CA1502:AvoidExcessiveComplexity")]
939 static int NextToken(char[] json, ref int index)
940 {
941 EatWhitespace(json, ref index);
942 if (index == json.Length)
943 return TOKEN_NONE;
944 char c = json[index];
945 index++;
946 switch (c)
947 {
948 case '{':
949 return TOKEN_CURLY_OPEN;
950 case '}':
951 return TOKEN_CURLY_CLOSE;
952 case '[':
953 return TOKEN_SQUARED_OPEN;
954 case ']':
955 return TOKEN_SQUARED_CLOSE;
956 case ',':
957 return TOKEN_COMMA;
958 case '"':
959 return TOKEN_STRING;
960 case '0':
961 case '1':
962 case '2':
963 case '3':
964 case '4':
965 case '5':
966 case '6':
967 case '7':
968 case '8':
969 case '9':
970 case '-':
971 return TOKEN_NUMBER;
972 case ':':
973 return TOKEN_COLON;
974 }
975 index--;
976 int remainingLength = json.Length - index;
977 // false
978 if (remainingLength >= 5)
979 {
980 if (json[index] == 'f' && json[index + 1] == 'a' && json[index + 2] == 'l' && json[index + 3] == 's' && json[index + 4] == 'e')
981 {
982 index += 5;
983 return TOKEN_FALSE;
984 }
985 }
986 // true
987 if (remainingLength >= 4)
988 {
989 if (json[index] == 't' && json[index + 1] == 'r' && json[index + 2] == 'u' && json[index + 3] == 'e')
990 {
991 index += 4;
992 return TOKEN_TRUE;
993 }
994 }
995 // null
996 if (remainingLength >= 4)
997 {
998 if (json[index] == 'n' && json[index + 1] == 'u' && json[index + 2] == 'l' && json[index + 3] == 'l')
999 {
1000 index += 4;
1001 return TOKEN_NULL;
1002 }
1003 }
1004 return TOKEN_NONE;
1005 }
1006
1007 static bool SerializeValue(IJsonSerializerStrategy jsonSerializerStrategy, object value, StringBuilder builder)
1008 {
1009 bool success = true;
1010 string stringValue = value as string;
1011 if (stringValue != null)
1012 success = SerializeString(stringValue, builder);
1013 else
1014 {
1015 IDictionary<string, object> dict = value as IDictionary<string, object>;
1016 if (dict != null)
1017 {
1018 success = SerializeObject(jsonSerializerStrategy, dict.Keys, dict.Values, builder);
1019 }
1020 else
1021 {
1022 IDictionary<string, string> stringDictionary = value as IDictionary<string, string>;
1023 if (stringDictionary != null)
1024 {
1025 success = SerializeObject(jsonSerializerStrategy, stringDictionary.Keys, stringDictionary.Values, builder);
1026 }
1027 else
1028 {
1029 IEnumerable enumerableValue = value as IEnumerable;
1030 if (enumerableValue != null)
1031 success = SerializeArray(jsonSerializerStrategy, enumerableValue, builder);
1032 else if (IsNumeric(value))
1033 success = SerializeNumber(value, builder);
1034 else if (value is bool)
1035 builder.Append((bool)value ? "true" : "false");
1036 else if (value == null)
1037 builder.Append("null");
1038 else
1039 {
1040 object serializedObject;
1041 success = jsonSerializerStrategy.TrySerializeNonPrimitiveObject(value, out serializedObject);
1042 if (success)
1043 SerializeValue(jsonSerializerStrategy, serializedObject, builder);
1044 }
1045 }
1046 }
1047 }
1048 return success;
1049 }
1050
1051 static bool SerializeObject(IJsonSerializerStrategy jsonSerializerStrategy, IEnumerable keys, IEnumerable values, StringBuilder builder)
1052 {
1053 builder.Append("{");
1054 IEnumerator ke = keys.GetEnumerator();
1055 IEnumerator ve = values.GetEnumerator();
1056 bool first = true;
1057 while (ke.MoveNext() && ve.MoveNext())
1058 {
1059 object key = ke.Current;
1060 object value = ve.Current;
1061 if (!first)
1062 builder.Append(",");
1063 string stringKey = key as string;
1064 if (stringKey != null)
1065 SerializeString(stringKey, builder);
1066 else
1067 if (!SerializeValue(jsonSerializerStrategy, value, builder)) return false;
1068 builder.Append(":");
1069 if (!SerializeValue(jsonSerializerStrategy, value, builder))
1070 return false;
1071 first = false;
1072 }
1073 builder.Append("}");
1074 return true;
1075 }
1076
1077 static bool SerializeArray(IJsonSerializerStrategy jsonSerializerStrategy, IEnumerable anArray, StringBuilder builder)
1078 {
1079 builder.Append("[");
1080 bool first = true;
1081 foreach (object value in anArray)
1082 {
1083 if (!first)
1084 builder.Append(",");
1085 if (!SerializeValue(jsonSerializerStrategy, value, builder))
1086 return false;
1087 first = false;
1088 }
1089 builder.Append("]");
1090 return true;
1091 }
1092
1093 static bool SerializeString(string aString, StringBuilder builder)
1094 {
1095 // Happy path if there's nothing to be escaped. IndexOfAny is highly optimized (and unmanaged)
1096 if (aString.IndexOfAny(EscapeCharacters) == -1)
1097 {
1098 builder.Append('"');
1099 builder.Append(aString);
1100 builder.Append('"');
1101
1102 return true;
1103 }
1104
1105 builder.Append('"');
1106 int safeCharacterCount = 0;
1107 char[] charArray = aString.ToCharArray();
1108
1109 for (int i = 0; i < charArray.Length; i++)
1110 {
1111 char c = charArray[i];
1112
1113 // Non ascii characters are fine, buffer them up and send them to the builder
1114 // in larger chunks if possible. The escape table is a 1:1 translation table
1115 // with \0 [default(char)] denoting a safe character.
1116 if (c >= EscapeTable.Length || EscapeTable[c] == default(char))
1117 {
1118 safeCharacterCount++;
1119 }
1120 else
1121 {
1122 if (safeCharacterCount > 0)
1123 {
1124 builder.Append(charArray, i - safeCharacterCount, safeCharacterCount);
1125 safeCharacterCount = 0;
1126 }
1127
1128 builder.Append('\\');
1129 builder.Append(EscapeTable[c]);
1130 }
1131 }
1132
1133 if (safeCharacterCount > 0)
1134 {
1135 builder.Append(charArray, charArray.Length - safeCharacterCount, safeCharacterCount);
1136 }
1137
1138 builder.Append('"');
1139 return true;
1140 }
1141
1142 static bool SerializeNumber(object number, StringBuilder builder)
1143 {
1144 if (number is long)
1145 builder.Append(((long)number).ToString(CultureInfo.InvariantCulture));
1146 else if (number is ulong)
1147 builder.Append(((ulong)number).ToString(CultureInfo.InvariantCulture));
1148 else if (number is int)
1149 builder.Append(((int)number).ToString(CultureInfo.InvariantCulture));
1150 else if (number is uint)
1151 builder.Append(((uint)number).ToString(CultureInfo.InvariantCulture));
1152 else if (number is decimal)
1153 builder.Append(((decimal)number).ToString(CultureInfo.InvariantCulture));
1154 else if (number is float)
1155 builder.Append(((float)number).ToString(CultureInfo.InvariantCulture));
1156 else
1157 builder.Append(Convert.ToDouble(number, CultureInfo.InvariantCulture).ToString("r", CultureInfo.InvariantCulture));
1158 return true;
1159 }
1160
1161 /// <summary>
1162 /// Determines if a given object is numeric in any way
1163 /// (can be integer, double, null, etc).
1164 /// </summary>
1165 static bool IsNumeric(object value)
1166 {
1167 if (value is sbyte) return true;
1168 if (value is byte) return true;
1169 if (value is short) return true;
1170 if (value is ushort) return true;
1171 if (value is int) return true;
1172 if (value is uint) return true;
1173 if (value is long) return true;
1174 if (value is ulong) return true;
1175 if (value is float) return true;
1176 if (value is double) return true;
1177 if (value is decimal) return true;
1178 return false;
1179 }
1180
1181 private static IJsonSerializerStrategy _currentJsonSerializerStrategy;
1182 public static IJsonSerializerStrategy CurrentJsonSerializerStrategy
1183 {
1184 get
1185 {
1186 return _currentJsonSerializerStrategy ??
1187 (_currentJsonSerializerStrategy =
1188#if SIMPLE_JSON_DATACONTRACT
1189 DataContractJsonSerializerStrategy
1190#else
1191 PocoJsonSerializerStrategy
1192#endif
1193);
1194 }
1195 set
1196 {
1197 _currentJsonSerializerStrategy = value;
1198 }
1199 }
1200
1201 private static PocoJsonSerializerStrategy _pocoJsonSerializerStrategy;
1202 [EditorBrowsable(EditorBrowsableState.Advanced)]
1203 public static PocoJsonSerializerStrategy PocoJsonSerializerStrategy
1204 {
1205 get
1206 {
1207 return _pocoJsonSerializerStrategy ?? (_pocoJsonSerializerStrategy = new PocoJsonSerializerStrategy());
1208 }
1209 }
1210
1211#if SIMPLE_JSON_DATACONTRACT
1212
1213 private static DataContractJsonSerializerStrategy _dataContractJsonSerializerStrategy;
1214 [System.ComponentModel.EditorBrowsable(EditorBrowsableState.Advanced)]
1215 public static DataContractJsonSerializerStrategy DataContractJsonSerializerStrategy
1216 {
1217 get
1218 {
1219 return _dataContractJsonSerializerStrategy ?? (_dataContractJsonSerializerStrategy = new DataContractJsonSerializerStrategy());
1220 }
1221 }
1222
1223#endif
1224 }
1225
1226 [GeneratedCode("simple-json", "1.0.0")]
1227#if SIMPLE_JSON_INTERNAL
1228 internal
1229#else
1230 public
1231#endif
1232 interface IJsonSerializerStrategy
1233 {
1234 [SuppressMessage("Microsoft.Design", "CA1007:UseGenericsWhereAppropriate", Justification = "Need to support .NET 2")]
1235 bool TrySerializeNonPrimitiveObject(object input, out object output);
1236 object DeserializeObject(object value, Type type);
1237 }
1238
1239 [GeneratedCode("simple-json", "1.0.0")]
1240#if SIMPLE_JSON_INTERNAL
1241 internal
1242#else
1243 public
1244#endif
1245 class PocoJsonSerializerStrategy : IJsonSerializerStrategy
1246 {
1247 internal IDictionary<Type, ReflectionUtils.ConstructorDelegate> ConstructorCache;
1248 internal IDictionary<Type, IDictionary<string, ReflectionUtils.GetDelegate>> GetCache;
1249 internal IDictionary<Type, IDictionary<string, KeyValuePair<Type, ReflectionUtils.SetDelegate>>> SetCache;
1250
1251 internal static readonly Type[] EmptyTypes = new Type[0];
1252 internal static readonly Type[] ArrayConstructorParameterTypes = new Type[] { typeof(int) };
1253
1254 private static readonly string[] Iso8601Format = new string[]
1255 {
1256 @"yyyy-MM-dd\THH:mm:ss.FFFFFFF\Z",
1257 @"yyyy-MM-dd\THH:mm:ss\Z",
1258 @"yyyy-MM-dd\THH:mm:ssK"
1259 };
1260
1261 public PocoJsonSerializerStrategy()
1262 {
1263 ConstructorCache = new ReflectionUtils.ThreadSafeDictionary<Type, ReflectionUtils.ConstructorDelegate>(ContructorDelegateFactory);
1264 GetCache = new ReflectionUtils.ThreadSafeDictionary<Type, IDictionary<string, ReflectionUtils.GetDelegate>>(GetterValueFactory);
1265 SetCache = new ReflectionUtils.ThreadSafeDictionary<Type, IDictionary<string, KeyValuePair<Type, ReflectionUtils.SetDelegate>>>(SetterValueFactory);
1266 }
1267
1268 protected virtual string MapClrMemberNameToJsonFieldName(string clrPropertyName)
1269 {
1270 return clrPropertyName;
1271 }
1272
1273 internal virtual ReflectionUtils.ConstructorDelegate ContructorDelegateFactory(Type key)
1274 {
1275 return ReflectionUtils.GetContructor(key, key.IsArray ? ArrayConstructorParameterTypes : EmptyTypes);
1276 }
1277
1278 internal virtual IDictionary<string, ReflectionUtils.GetDelegate> GetterValueFactory(Type type)
1279 {
1280 IDictionary<string, ReflectionUtils.GetDelegate> result = new Dictionary<string, ReflectionUtils.GetDelegate>();
1281 foreach (PropertyInfo propertyInfo in ReflectionUtils.GetProperties(type))
1282 {
1283 if (propertyInfo.CanRead)
1284 {
1285 MethodInfo getMethod = ReflectionUtils.GetGetterMethodInfo(propertyInfo);
1286 if (getMethod.IsStatic || !getMethod.IsPublic)
1287 continue;
1288 result[MapClrMemberNameToJsonFieldName(propertyInfo.Name)] = ReflectionUtils.GetGetMethod(propertyInfo);
1289 }
1290 }
1291 foreach (FieldInfo fieldInfo in ReflectionUtils.GetFields(type))
1292 {
1293 if (fieldInfo.IsStatic || !fieldInfo.IsPublic)
1294 continue;
1295 result[MapClrMemberNameToJsonFieldName(fieldInfo.Name)] = ReflectionUtils.GetGetMethod(fieldInfo);
1296 }
1297 return result;
1298 }
1299
1300 internal virtual IDictionary<string, KeyValuePair<Type, ReflectionUtils.SetDelegate>> SetterValueFactory(Type type)
1301 {
1302 IDictionary<string, KeyValuePair<Type, ReflectionUtils.SetDelegate>> result = new Dictionary<string, KeyValuePair<Type, ReflectionUtils.SetDelegate>>();
1303 foreach (PropertyInfo propertyInfo in ReflectionUtils.GetProperties(type))
1304 {
1305 if (propertyInfo.CanWrite)
1306 {
1307 MethodInfo setMethod = ReflectionUtils.GetSetterMethodInfo(propertyInfo);
1308 if (setMethod.IsStatic || !setMethod.IsPublic)
1309 continue;
1310 result[MapClrMemberNameToJsonFieldName(propertyInfo.Name)] = new KeyValuePair<Type, ReflectionUtils.SetDelegate>(propertyInfo.PropertyType, ReflectionUtils.GetSetMethod(propertyInfo));
1311 }
1312 }
1313 foreach (FieldInfo fieldInfo in ReflectionUtils.GetFields(type))
1314 {
1315 if (fieldInfo.IsInitOnly || fieldInfo.IsStatic || !fieldInfo.IsPublic)
1316 continue;
1317 result[MapClrMemberNameToJsonFieldName(fieldInfo.Name)] = new KeyValuePair<Type, ReflectionUtils.SetDelegate>(fieldInfo.FieldType, ReflectionUtils.GetSetMethod(fieldInfo));
1318 }
1319 return result;
1320 }
1321
1322 public virtual bool TrySerializeNonPrimitiveObject(object input, out object output)
1323 {
1324 return TrySerializeKnownTypes(input, out output) || TrySerializeUnknownTypes(input, out output);
1325 }
1326
1327 [SuppressMessage("Microsoft.Maintainability", "CA1502:AvoidExcessiveComplexity")]
1328 public virtual object DeserializeObject(object value, Type type)
1329 {
1330 if (type == null) throw new ArgumentNullException("type");
1331 string str = value as string;
1332
1333 if (type == typeof(Guid) && string.IsNullOrEmpty(str))
1334 return default(Guid);
1335
1336 if (value == null)
1337 return null;
1338
1339 object obj = null;
1340
1341 if (str != null)
1342 {
1343 if (str.Length != 0) // We know it can't be null now.
1344 {
1345 if (type == typeof(DateTime) || (ReflectionUtils.IsNullableType(type) && Nullable.GetUnderlyingType(type) == typeof(DateTime)))
1346 return DateTime.ParseExact(str, Iso8601Format, CultureInfo.InvariantCulture, DateTimeStyles.AssumeUniversal | DateTimeStyles.AdjustToUniversal);
1347 if (type == typeof(DateTimeOffset) || (ReflectionUtils.IsNullableType(type) && Nullable.GetUnderlyingType(type) == typeof(DateTimeOffset)))
1348 return DateTimeOffset.ParseExact(str, Iso8601Format, CultureInfo.InvariantCulture, DateTimeStyles.AssumeUniversal | DateTimeStyles.AdjustToUniversal);
1349 if (type == typeof(Guid) || (ReflectionUtils.IsNullableType(type) && Nullable.GetUnderlyingType(type) == typeof(Guid)))
1350 return new Guid(str);
1351 if (type == typeof(Uri))
1352 {
1353 bool isValid = Uri.IsWellFormedUriString(str, UriKind.RelativeOrAbsolute);
1354
1355 Uri result;
1356 if (isValid && Uri.TryCreate(str, UriKind.RelativeOrAbsolute, out result))
1357 return result;
1358
1359 return null;
1360 }
1361
1362 if (type == typeof(string))
1363 return str;
1364
1365 return Convert.ChangeType(str, type, CultureInfo.InvariantCulture);
1366 }
1367 else
1368 {
1369 if (type == typeof(Guid))
1370 obj = default(Guid);
1371 else if (ReflectionUtils.IsNullableType(type) && Nullable.GetUnderlyingType(type) == typeof(Guid))
1372 obj = null;
1373 else
1374 obj = str;
1375 }
1376 // Empty string case
1377 if (!ReflectionUtils.IsNullableType(type) && Nullable.GetUnderlyingType(type) == typeof(Guid))
1378 return str;
1379 }
1380 else if (value is bool)
1381 return value;
1382
1383 bool valueIsLong = value is long;
1384 bool valueIsDouble = value is double;
1385 if ((valueIsLong && type == typeof(long)) || (valueIsDouble && type == typeof(double)))
1386 return value;
1387 if ((valueIsDouble && type != typeof(double)) || (valueIsLong && type != typeof(long)))
1388 {
1389 obj = type == typeof(int) || type == typeof(long) || type == typeof(double) || type == typeof(float) || type == typeof(bool) || type == typeof(decimal) || type == typeof(byte) || type == typeof(short)
1390 ? Convert.ChangeType(value, type, CultureInfo.InvariantCulture)
1391 : value;
1392 }
1393 else
1394 {
1395 IDictionary<string, object> objects = value as IDictionary<string, object>;
1396 if (objects != null)
1397 {
1398 IDictionary<string, object> jsonObject = objects;
1399
1400 if (ReflectionUtils.IsTypeDictionary(type))
1401 {
1402 // if dictionary then
1403 Type[] types = ReflectionUtils.GetGenericTypeArguments(type);
1404 Type keyType = types[0];
1405 Type valueType = types[1];
1406
1407 Type genericType = typeof(Dictionary<,>).MakeGenericType(keyType, valueType);
1408
1409 IDictionary dict = (IDictionary)ConstructorCache[genericType]();
1410
1411 foreach (KeyValuePair<string, object> kvp in jsonObject)
1412 dict.Add(kvp.Key, DeserializeObject(kvp.Value, valueType));
1413
1414 obj = dict;
1415 }
1416 else
1417 {
1418 if (type == typeof(object))
1419 obj = value;
1420 else
1421 {
1422 obj = ConstructorCache[type]();
1423 foreach (KeyValuePair<string, KeyValuePair<Type, ReflectionUtils.SetDelegate>> setter in SetCache[type])
1424 {
1425 object jsonValue;
1426 if (jsonObject.TryGetValue(setter.Key, out jsonValue))
1427 {
1428 jsonValue = DeserializeObject(jsonValue, setter.Value.Key);
1429 setter.Value.Value(obj, jsonValue);
1430 }
1431 }
1432 }
1433 }
1434 }
1435 else
1436 {
1437 IList<object> valueAsList = value as IList<object>;
1438 if (valueAsList != null)
1439 {
1440 IList<object> jsonObject = valueAsList;
1441 IList list = null;
1442
1443 if (type.IsArray)
1444 {
1445 list = (IList)ConstructorCache[type](jsonObject.Count);
1446 int i = 0;
1447 foreach (object o in jsonObject)
1448 list[i++] = DeserializeObject(o, type.GetElementType());
1449 }
1450 else if (ReflectionUtils.IsTypeGenericeCollectionInterface(type) || ReflectionUtils.IsAssignableFrom(typeof(IList), type))
1451 {
1452 Type innerType = ReflectionUtils.GetGenericListElementType(type);
1453 list = (IList)(ConstructorCache[type] ?? ConstructorCache[typeof(List<>).MakeGenericType(innerType)])(jsonObject.Count);
1454 foreach (object o in jsonObject)
1455 list.Add(DeserializeObject(o, innerType));
1456 }
1457 obj = list;
1458 }
1459 }
1460 return obj;
1461 }
1462 if (ReflectionUtils.IsNullableType(type))
1463 return ReflectionUtils.ToNullableType(obj, type);
1464 return obj;
1465 }
1466
1467 protected virtual object SerializeEnum(Enum p)
1468 {
1469 return Convert.ToDouble(p, CultureInfo.InvariantCulture);
1470 }
1471
1472 [SuppressMessage("Microsoft.Design", "CA1007:UseGenericsWhereAppropriate", Justification = "Need to support .NET 2")]
1473 protected virtual bool TrySerializeKnownTypes(object input, out object output)
1474 {
1475 bool returnValue = true;
1476 if (input is DateTime)
1477 output = ((DateTime)input).ToUniversalTime().ToString(Iso8601Format[0], CultureInfo.InvariantCulture);
1478 else if (input is DateTimeOffset)
1479 output = ((DateTimeOffset)input).ToUniversalTime().ToString(Iso8601Format[0], CultureInfo.InvariantCulture);
1480 else if (input is Guid)
1481 output = ((Guid)input).ToString("D");
1482 else if (input is Uri)
1483 output = input.ToString();
1484 else
1485 {
1486 Enum inputEnum = input as Enum;
1487 if (inputEnum != null)
1488 output = SerializeEnum(inputEnum);
1489 else
1490 {
1491 returnValue = false;
1492 output = null;
1493 }
1494 }
1495 return returnValue;
1496 }
1497 [SuppressMessage("Microsoft.Design", "CA1007:UseGenericsWhereAppropriate", Justification = "Need to support .NET 2")]
1498 protected virtual bool TrySerializeUnknownTypes(object input, out object output)
1499 {
1500 if (input == null) throw new ArgumentNullException("input");
1501 output = null;
1502 Type type = input.GetType();
1503 if (type.FullName == null)
1504 return false;
1505 IDictionary<string, object> obj = new JsonObject();
1506 IDictionary<string, ReflectionUtils.GetDelegate> getters = GetCache[type];
1507 foreach (KeyValuePair<string, ReflectionUtils.GetDelegate> getter in getters)
1508 {
1509 if (getter.Value != null)
1510 obj.Add(MapClrMemberNameToJsonFieldName(getter.Key), getter.Value(input));
1511 }
1512 output = obj;
1513 return true;
1514 }
1515 }
1516
1517#if SIMPLE_JSON_DATACONTRACT
1518 [GeneratedCode("simple-json", "1.0.0")]
1519#if SIMPLE_JSON_INTERNAL
1520 internal
1521#else
1522 public
1523#endif
1524 class DataContractJsonSerializerStrategy : PocoJsonSerializerStrategy
1525 {
1526 public DataContractJsonSerializerStrategy()
1527 {
1528 GetCache = new ReflectionUtils.ThreadSafeDictionary<Type, IDictionary<string, ReflectionUtils.GetDelegate>>(GetterValueFactory);
1529 SetCache = new ReflectionUtils.ThreadSafeDictionary<Type, IDictionary<string, KeyValuePair<Type, ReflectionUtils.SetDelegate>>>(SetterValueFactory);
1530 }
1531
1532 internal override IDictionary<string, ReflectionUtils.GetDelegate> GetterValueFactory(Type type)
1533 {
1534 bool hasDataContract = ReflectionUtils.GetAttribute(type, typeof(DataContractAttribute)) != null;
1535 if (!hasDataContract)
1536 return base.GetterValueFactory(type);
1537 string jsonKey;
1538 IDictionary<string, ReflectionUtils.GetDelegate> result = new Dictionary<string, ReflectionUtils.GetDelegate>();
1539 foreach (PropertyInfo propertyInfo in ReflectionUtils.GetProperties(type))
1540 {
1541 if (propertyInfo.CanRead)
1542 {
1543 MethodInfo getMethod = ReflectionUtils.GetGetterMethodInfo(propertyInfo);
1544 if (!getMethod.IsStatic && CanAdd(propertyInfo, out jsonKey))
1545 result[jsonKey] = ReflectionUtils.GetGetMethod(propertyInfo);
1546 }
1547 }
1548 foreach (FieldInfo fieldInfo in ReflectionUtils.GetFields(type))
1549 {
1550 if (!fieldInfo.IsStatic && CanAdd(fieldInfo, out jsonKey))
1551 result[jsonKey] = ReflectionUtils.GetGetMethod(fieldInfo);
1552 }
1553 return result;
1554 }
1555
1556 internal override IDictionary<string, KeyValuePair<Type, ReflectionUtils.SetDelegate>> SetterValueFactory(Type type)
1557 {
1558 bool hasDataContract = ReflectionUtils.GetAttribute(type, typeof(DataContractAttribute)) != null;
1559 if (!hasDataContract)
1560 return base.SetterValueFactory(type);
1561 string jsonKey;
1562 IDictionary<string, KeyValuePair<Type, ReflectionUtils.SetDelegate>> result = new Dictionary<string, KeyValuePair<Type, ReflectionUtils.SetDelegate>>();
1563 foreach (PropertyInfo propertyInfo in ReflectionUtils.GetProperties(type))
1564 {
1565 if (propertyInfo.CanWrite)
1566 {
1567 MethodInfo setMethod = ReflectionUtils.GetSetterMethodInfo(propertyInfo);
1568 if (!setMethod.IsStatic && CanAdd(propertyInfo, out jsonKey))
1569 result[jsonKey] = new KeyValuePair<Type, ReflectionUtils.SetDelegate>(propertyInfo.PropertyType, ReflectionUtils.GetSetMethod(propertyInfo));
1570 }
1571 }
1572 foreach (FieldInfo fieldInfo in ReflectionUtils.GetFields(type))
1573 {
1574 if (!fieldInfo.IsInitOnly && !fieldInfo.IsStatic && CanAdd(fieldInfo, out jsonKey))
1575 result[jsonKey] = new KeyValuePair<Type, ReflectionUtils.SetDelegate>(fieldInfo.FieldType, ReflectionUtils.GetSetMethod(fieldInfo));
1576 }
1577 // todo implement sorting for DATACONTRACT.
1578 return result;
1579 }
1580
1581 private static bool CanAdd(MemberInfo info, out string jsonKey)
1582 {
1583 jsonKey = null;
1584 if (ReflectionUtils.GetAttribute(info, typeof(IgnoreDataMemberAttribute)) != null)
1585 return false;
1586 DataMemberAttribute dataMemberAttribute = (DataMemberAttribute)ReflectionUtils.GetAttribute(info, typeof(DataMemberAttribute));
1587 if (dataMemberAttribute == null)
1588 return false;
1589 jsonKey = string.IsNullOrEmpty(dataMemberAttribute.Name) ? info.Name : dataMemberAttribute.Name;
1590 return true;
1591 }
1592 }
1593
1594#endif
1595
1596 namespace Reflection
1597 {
1598 // This class is meant to be copied into other libraries. So we want to exclude it from Code Analysis rules
1599 // that might be in place in the target project.
1600 [GeneratedCode("reflection-utils", "1.0.0")]
1601#if SIMPLE_JSON_REFLECTION_UTILS_PUBLIC
1602 public
1603#else
1604 internal
1605#endif
1606 class ReflectionUtils
1607 {
1608 private static readonly object[] EmptyObjects = new object[] { };
1609
1610 public delegate object GetDelegate(object source);
1611 public delegate void SetDelegate(object source, object value);
1612 public delegate object ConstructorDelegate(params object[] args);
1613
1614 public delegate TValue ThreadSafeDictionaryValueFactory<TKey, TValue>(TKey key);
1615
1616#if SIMPLE_JSON_TYPEINFO
1617 public static TypeInfo GetTypeInfo(Type type)
1618 {
1619 return type.GetTypeInfo();
1620 }
1621#else
1622 public static Type GetTypeInfo(Type type)
1623 {
1624 return type;
1625 }
1626#endif
1627
1628 public static Attribute GetAttribute(MemberInfo info, Type type)
1629 {
1630#if SIMPLE_JSON_TYPEINFO
1631 if (info == null || type == null || !info.IsDefined(type))
1632 return null;
1633 return info.GetCustomAttribute(type);
1634#else
1635 if (info == null || type == null || !Attribute.IsDefined(info, type))
1636 return null;
1637 return Attribute.GetCustomAttribute(info, type);
1638#endif
1639 }
1640
1641 public static Type GetGenericListElementType(Type type)
1642 {
1643 IEnumerable<Type> interfaces;
1644#if SIMPLE_JSON_TYPEINFO
1645 interfaces = type.GetTypeInfo().ImplementedInterfaces;
1646#else
1647 interfaces = type.GetInterfaces();
1648#endif
1649 foreach (Type implementedInterface in interfaces)
1650 {
1651 if (IsTypeGeneric(implementedInterface) &&
1652 implementedInterface.GetGenericTypeDefinition() == typeof(IList<>))
1653 {
1654 return GetGenericTypeArguments(implementedInterface)[0];
1655 }
1656 }
1657 return GetGenericTypeArguments(type)[0];
1658 }
1659
1660 public static Attribute GetAttribute(Type objectType, Type attributeType)
1661 {
1662
1663#if SIMPLE_JSON_TYPEINFO
1664 if (objectType == null || attributeType == null || !objectType.GetTypeInfo().IsDefined(attributeType))
1665 return null;
1666 return objectType.GetTypeInfo().GetCustomAttribute(attributeType);
1667#else
1668 if (objectType == null || attributeType == null || !Attribute.IsDefined(objectType, attributeType))
1669 return null;
1670 return Attribute.GetCustomAttribute(objectType, attributeType);
1671#endif
1672 }
1673
1674 public static Type[] GetGenericTypeArguments(Type type)
1675 {
1676#if SIMPLE_JSON_TYPEINFO
1677 return type.GetTypeInfo().GenericTypeArguments;
1678#else
1679 return type.GetGenericArguments();
1680#endif
1681 }
1682
1683 public static bool IsTypeGeneric(Type type)
1684 {
1685 return GetTypeInfo(type).IsGenericType;
1686 }
1687
1688 public static bool IsTypeGenericeCollectionInterface(Type type)
1689 {
1690 if (!IsTypeGeneric(type))
1691 return false;
1692
1693 Type genericDefinition = type.GetGenericTypeDefinition();
1694
1695 return (genericDefinition == typeof(IList<>)
1696 || genericDefinition == typeof(ICollection<>)
1697 || genericDefinition == typeof(IEnumerable<>)
1698#if SIMPLE_JSON_READONLY_COLLECTIONS
1699 || genericDefinition == typeof(IReadOnlyCollection<>)
1700 || genericDefinition == typeof(IReadOnlyList<>)
1701#endif
1702 );
1703 }
1704
1705 public static bool IsAssignableFrom(Type type1, Type type2)
1706 {
1707 return GetTypeInfo(type1).IsAssignableFrom(GetTypeInfo(type2));
1708 }
1709
1710 public static bool IsTypeDictionary(Type type)
1711 {
1712#if SIMPLE_JSON_TYPEINFO
1713 if (typeof(IDictionary<,>).GetTypeInfo().IsAssignableFrom(type.GetTypeInfo()))
1714 return true;
1715#else
1716 if (typeof(System.Collections.IDictionary).IsAssignableFrom(type))
1717 return true;
1718#endif
1719 if (!GetTypeInfo(type).IsGenericType)
1720 return false;
1721
1722 Type genericDefinition = type.GetGenericTypeDefinition();
1723 return genericDefinition == typeof(IDictionary<,>);
1724 }
1725
1726 public static bool IsNullableType(Type type)
1727 {
1728 return GetTypeInfo(type).IsGenericType && type.GetGenericTypeDefinition() == typeof(Nullable<>);
1729 }
1730
1731 public static object ToNullableType(object obj, Type nullableType)
1732 {
1733 return obj == null ? null : Convert.ChangeType(obj, Nullable.GetUnderlyingType(nullableType), CultureInfo.InvariantCulture);
1734 }
1735
1736 public static bool IsValueType(Type type)
1737 {
1738 return GetTypeInfo(type).IsValueType;
1739 }
1740
1741 public static IEnumerable<ConstructorInfo> GetConstructors(Type type)
1742 {
1743#if SIMPLE_JSON_TYPEINFO
1744 return type.GetTypeInfo().DeclaredConstructors;
1745#else
1746 return type.GetConstructors();
1747#endif
1748 }
1749
1750 public static ConstructorInfo GetConstructorInfo(Type type, params Type[] argsType)
1751 {
1752 IEnumerable<ConstructorInfo> constructorInfos = GetConstructors(type);
1753 int i;
1754 bool matches;
1755 foreach (ConstructorInfo constructorInfo in constructorInfos)
1756 {
1757 ParameterInfo[] parameters = constructorInfo.GetParameters();
1758 if (argsType.Length != parameters.Length)
1759 continue;
1760
1761 i = 0;
1762 matches = true;
1763 foreach (ParameterInfo parameterInfo in constructorInfo.GetParameters())
1764 {
1765 if (parameterInfo.ParameterType != argsType[i])
1766 {
1767 matches = false;
1768 break;
1769 }
1770 }
1771
1772 if (matches)
1773 return constructorInfo;
1774 }
1775
1776 return null;
1777 }
1778
1779 public static IEnumerable<PropertyInfo> GetProperties(Type type)
1780 {
1781#if SIMPLE_JSON_TYPEINFO
1782 return type.GetRuntimeProperties();
1783#else
1784 return type.GetProperties(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static);
1785#endif
1786 }
1787
1788 public static IEnumerable<FieldInfo> GetFields(Type type)
1789 {
1790#if SIMPLE_JSON_TYPEINFO
1791 return type.GetRuntimeFields();
1792#else
1793 return type.GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static);
1794#endif
1795 }
1796
1797 public static MethodInfo GetGetterMethodInfo(PropertyInfo propertyInfo)
1798 {
1799#if SIMPLE_JSON_TYPEINFO
1800 return propertyInfo.GetMethod;
1801#else
1802 return propertyInfo.GetGetMethod(true);
1803#endif
1804 }
1805
1806 public static MethodInfo GetSetterMethodInfo(PropertyInfo propertyInfo)
1807 {
1808#if SIMPLE_JSON_TYPEINFO
1809 return propertyInfo.SetMethod;
1810#else
1811 return propertyInfo.GetSetMethod(true);
1812#endif
1813 }
1814
1815 public static ConstructorDelegate GetContructor(ConstructorInfo constructorInfo)
1816 {
1817#if SIMPLE_JSON_NO_LINQ_EXPRESSION
1818 return GetConstructorByReflection(constructorInfo);
1819#else
1820 return GetConstructorByExpression(constructorInfo);
1821#endif
1822 }
1823
1824 public static ConstructorDelegate GetContructor(Type type, params Type[] argsType)
1825 {
1826#if SIMPLE_JSON_NO_LINQ_EXPRESSION
1827 return GetConstructorByReflection(type, argsType);
1828#else
1829 return GetConstructorByExpression(type, argsType);
1830#endif
1831 }
1832
1833 public static ConstructorDelegate GetConstructorByReflection(ConstructorInfo constructorInfo)
1834 {
1835 return delegate (object[] args) { return constructorInfo.Invoke(args); };
1836 }
1837
1838 public static ConstructorDelegate GetConstructorByReflection(Type type, params Type[] argsType)
1839 {
1840 ConstructorInfo constructorInfo = GetConstructorInfo(type, argsType);
1841 return constructorInfo == null ? null : GetConstructorByReflection(constructorInfo);
1842 }
1843
1844#if !SIMPLE_JSON_NO_LINQ_EXPRESSION
1845
1846 public static ConstructorDelegate GetConstructorByExpression(ConstructorInfo constructorInfo)
1847 {
1848 ParameterInfo[] paramsInfo = constructorInfo.GetParameters();
1849 ParameterExpression param = Expression.Parameter(typeof(object[]), "args");
1850 Expression[] argsExp = new Expression[paramsInfo.Length];
1851 for (int i = 0; i < paramsInfo.Length; i++)
1852 {
1853 Expression index = Expression.Constant(i);
1854 Type paramType = paramsInfo[i].ParameterType;
1855 Expression paramAccessorExp = Expression.ArrayIndex(param, index);
1856 Expression paramCastExp = Expression.Convert(paramAccessorExp, paramType);
1857 argsExp[i] = paramCastExp;
1858 }
1859 NewExpression newExp = Expression.New(constructorInfo, argsExp);
1860 Expression<Func<object[], object>> lambda = Expression.Lambda<Func<object[], object>>(newExp, param);
1861 Func<object[], object> compiledLambda = lambda.Compile();
1862 return delegate (object[] args) { return compiledLambda(args); };
1863 }
1864
1865 public static ConstructorDelegate GetConstructorByExpression(Type type, params Type[] argsType)
1866 {
1867 ConstructorInfo constructorInfo = GetConstructorInfo(type, argsType);
1868 return constructorInfo == null ? null : GetConstructorByExpression(constructorInfo);
1869 }
1870
1871#endif
1872
1873 public static GetDelegate GetGetMethod(PropertyInfo propertyInfo)
1874 {
1875#if SIMPLE_JSON_NO_LINQ_EXPRESSION
1876 return GetGetMethodByReflection(propertyInfo);
1877#else
1878 return GetGetMethodByExpression(propertyInfo);
1879#endif
1880 }
1881
1882 public static GetDelegate GetGetMethod(FieldInfo fieldInfo)
1883 {
1884#if SIMPLE_JSON_NO_LINQ_EXPRESSION
1885 return GetGetMethodByReflection(fieldInfo);
1886#else
1887 return GetGetMethodByExpression(fieldInfo);
1888#endif
1889 }
1890
1891 public static GetDelegate GetGetMethodByReflection(PropertyInfo propertyInfo)
1892 {
1893 MethodInfo methodInfo = GetGetterMethodInfo(propertyInfo);
1894 return delegate (object source) { return methodInfo.Invoke(source, EmptyObjects); };
1895 }
1896
1897 public static GetDelegate GetGetMethodByReflection(FieldInfo fieldInfo)
1898 {
1899 return delegate (object source) { return fieldInfo.GetValue(source); };
1900 }
1901
1902#if !SIMPLE_JSON_NO_LINQ_EXPRESSION
1903
1904 public static GetDelegate GetGetMethodByExpression(PropertyInfo propertyInfo)
1905 {
1906 MethodInfo getMethodInfo = GetGetterMethodInfo(propertyInfo);
1907 ParameterExpression instance = Expression.Parameter(typeof(object), "instance");
1908 UnaryExpression instanceCast = (!IsValueType(propertyInfo.DeclaringType)) ? Expression.TypeAs(instance, propertyInfo.DeclaringType) : Expression.Convert(instance, propertyInfo.DeclaringType);
1909 Func<object, object> compiled = Expression.Lambda<Func<object, object>>(Expression.TypeAs(Expression.Call(instanceCast, getMethodInfo), typeof(object)), instance).Compile();
1910 return delegate (object source) { return compiled(source); };
1911 }
1912
1913 public static GetDelegate GetGetMethodByExpression(FieldInfo fieldInfo)
1914 {
1915 ParameterExpression instance = Expression.Parameter(typeof(object), "instance");
1916 MemberExpression member = Expression.Field(Expression.Convert(instance, fieldInfo.DeclaringType), fieldInfo);
1917 GetDelegate compiled = Expression.Lambda<GetDelegate>(Expression.Convert(member, typeof(object)), instance).Compile();
1918 return delegate (object source) { return compiled(source); };
1919 }
1920
1921#endif
1922
1923 public static SetDelegate GetSetMethod(PropertyInfo propertyInfo)
1924 {
1925#if SIMPLE_JSON_NO_LINQ_EXPRESSION
1926 return GetSetMethodByReflection(propertyInfo);
1927#else
1928 return GetSetMethodByExpression(propertyInfo);
1929#endif
1930 }
1931
1932 public static SetDelegate GetSetMethod(FieldInfo fieldInfo)
1933 {
1934#if SIMPLE_JSON_NO_LINQ_EXPRESSION
1935 return GetSetMethodByReflection(fieldInfo);
1936#else
1937 return GetSetMethodByExpression(fieldInfo);
1938#endif
1939 }
1940
1941 public static SetDelegate GetSetMethodByReflection(PropertyInfo propertyInfo)
1942 {
1943 MethodInfo methodInfo = GetSetterMethodInfo(propertyInfo);
1944 return delegate (object source, object value) { methodInfo.Invoke(source, new object[] { value }); };
1945 }
1946
1947 public static SetDelegate GetSetMethodByReflection(FieldInfo fieldInfo)
1948 {
1949 return delegate (object source, object value) { fieldInfo.SetValue(source, value); };
1950 }
1951
1952#if !SIMPLE_JSON_NO_LINQ_EXPRESSION
1953
1954 public static SetDelegate GetSetMethodByExpression(PropertyInfo propertyInfo)
1955 {
1956 MethodInfo setMethodInfo = GetSetterMethodInfo(propertyInfo);
1957 ParameterExpression instance = Expression.Parameter(typeof(object), "instance");
1958 ParameterExpression value = Expression.Parameter(typeof(object), "value");
1959 UnaryExpression instanceCast = (!IsValueType(propertyInfo.DeclaringType)) ? Expression.TypeAs(instance, propertyInfo.DeclaringType) : Expression.Convert(instance, propertyInfo.DeclaringType);
1960 UnaryExpression valueCast = (!IsValueType(propertyInfo.PropertyType)) ? Expression.TypeAs(value, propertyInfo.PropertyType) : Expression.Convert(value, propertyInfo.PropertyType);
1961 Action<object, object> compiled = Expression.Lambda<Action<object, object>>(Expression.Call(instanceCast, setMethodInfo, valueCast), new ParameterExpression[] { instance, value }).Compile();
1962 return delegate (object source, object val) { compiled(source, val); };
1963 }
1964
1965 public static SetDelegate GetSetMethodByExpression(FieldInfo fieldInfo)
1966 {
1967 ParameterExpression instance = Expression.Parameter(typeof(object), "instance");
1968 ParameterExpression value = Expression.Parameter(typeof(object), "value");
1969 Action<object, object> compiled = Expression.Lambda<Action<object, object>>(
1970 Assign(Expression.Field(Expression.Convert(instance, fieldInfo.DeclaringType), fieldInfo), Expression.Convert(value, fieldInfo.FieldType)), instance, value).Compile();
1971 return delegate (object source, object val) { compiled(source, val); };
1972 }
1973
1974 public static BinaryExpression Assign(Expression left, Expression right)
1975 {
1976#if SIMPLE_JSON_TYPEINFO
1977 return Expression.Assign(left, right);
1978#else
1979 MethodInfo assign = typeof(Assigner<>).MakeGenericType(left.Type).GetMethod("Assign");
1980 BinaryExpression assignExpr = Expression.Add(left, right, assign);
1981 return assignExpr;
1982#endif
1983 }
1984
1985 private static class Assigner<T>
1986 {
1987 public static T Assign(ref T left, T right)
1988 {
1989 return (left = right);
1990 }
1991 }
1992
1993#endif
1994
1995 public sealed class ThreadSafeDictionary<TKey, TValue> : IDictionary<TKey, TValue>
1996 {
1997 private readonly object _lock = new object();
1998 private readonly ThreadSafeDictionaryValueFactory<TKey, TValue> _valueFactory;
1999 private Dictionary<TKey, TValue> _dictionary;
2000
2001 public ThreadSafeDictionary(ThreadSafeDictionaryValueFactory<TKey, TValue> valueFactory)
2002 {
2003 _valueFactory = valueFactory;
2004 }
2005
2006 private TValue Get(TKey key)
2007 {
2008 if (_dictionary == null)
2009 return AddValue(key);
2010 TValue value;
2011 if (!_dictionary.TryGetValue(key, out value))
2012 return AddValue(key);
2013 return value;
2014 }
2015
2016 private TValue AddValue(TKey key)
2017 {
2018 TValue value = _valueFactory(key);
2019 lock (_lock)
2020 {
2021 if (_dictionary == null)
2022 {
2023 _dictionary = new Dictionary<TKey, TValue>();
2024 _dictionary[key] = value;
2025 }
2026 else
2027 {
2028 TValue val;
2029 if (_dictionary.TryGetValue(key, out val))
2030 return val;
2031 Dictionary<TKey, TValue> dict = new Dictionary<TKey, TValue>(_dictionary);
2032 dict[key] = value;
2033 _dictionary = dict;
2034 }
2035 }
2036 return value;
2037 }
2038
2039 public void Add(TKey key, TValue value)
2040 {
2041 throw new NotImplementedException();
2042 }
2043
2044 public bool ContainsKey(TKey key)
2045 {
2046 return _dictionary.ContainsKey(key);
2047 }
2048
2049 public ICollection<TKey> Keys
2050 {
2051 get { return _dictionary.Keys; }
2052 }
2053
2054 public bool Remove(TKey key)
2055 {
2056 throw new NotImplementedException();
2057 }
2058
2059 public bool TryGetValue(TKey key, out TValue value)
2060 {
2061 value = this[key];
2062 return true;
2063 }
2064
2065 public ICollection<TValue> Values
2066 {
2067 get { return _dictionary.Values; }
2068 }
2069
2070 public TValue this[TKey key]
2071 {
2072 get { return Get(key); }
2073 set { throw new NotImplementedException(); }
2074 }
2075
2076 public void Add(KeyValuePair<TKey, TValue> item)
2077 {
2078 throw new NotImplementedException();
2079 }
2080
2081 public void Clear()
2082 {
2083 throw new NotImplementedException();
2084 }
2085
2086 public bool Contains(KeyValuePair<TKey, TValue> item)
2087 {
2088 throw new NotImplementedException();
2089 }
2090
2091 public void CopyTo(KeyValuePair<TKey, TValue>[] array, int arrayIndex)
2092 {
2093 throw new NotImplementedException();
2094 }
2095
2096 public int Count
2097 {
2098 get { return _dictionary.Count; }
2099 }
2100
2101 public bool IsReadOnly
2102 {
2103 get { throw new NotImplementedException(); }
2104 }
2105
2106 public bool Remove(KeyValuePair<TKey, TValue> item)
2107 {
2108 throw new NotImplementedException();
2109 }
2110
2111 public IEnumerator<KeyValuePair<TKey, TValue>> GetEnumerator()
2112 {
2113 return _dictionary.GetEnumerator();
2114 }
2115
2116 System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
2117 {
2118 return _dictionary.GetEnumerator();
2119 }
2120 }
2121
2122 }
2123 }
2124}
2125// ReSharper restore LoopCanBeConvertedToQuery
2126// ReSharper restore RedundantExplicitArrayCreation
2127// ReSharper restore SuggestUseVarKeywordEvident \ No newline at end of file
diff --git a/src/internal/README.md b/src/internal/README.md
new file mode 100644
index 00000000..da42c09e
--- /dev/null
+++ b/src/internal/README.md
@@ -0,0 +1,2 @@
1# WixBuildTools
2Internal shared props/targets/tools used to build the WiX Toolset
diff --git a/src/internal/TablesAndTuples/ColumnDefinitionEnums.cs b/src/internal/TablesAndTuples/ColumnDefinitionEnums.cs
new file mode 100644
index 00000000..1499500e
--- /dev/null
+++ b/src/internal/TablesAndTuples/ColumnDefinitionEnums.cs
@@ -0,0 +1,56 @@
1namespace TablesAndSymbols
2{
3 public enum ColumnCategory
4 {
5 Unknown,
6 Text,
7 UpperCase,
8 LowerCase,
9 Integer,
10 DoubleInteger,
11 TimeDate,
12 Identifier,
13 Property,
14 Filename,
15 WildCardFilename,
16 Path,
17 Paths,
18 AnyPath,
19 DefaultDir,
20 RegPath,
21 Formatted,
22 Template,
23 Condition,
24 Guid,
25 Version,
26 Language,
27 Binary,
28 CustomSource,
29 Cabinet,
30 Shortcut,
31 FormattedSDDLText,
32 }
33
34 public enum ColumnModularizeType
35 {
36 None,
37 Column,
38 Icon,
39 CompanionFile,
40 Condition,
41 ControlEventArgument,
42 ControlText,
43 Property,
44 SemicolonDelimited,
45 }
46
47 public enum ColumnType
48 {
49 Unknown,
50 String,
51 Localized,
52 Number,
53 Object,
54 Preserved,
55 }
56}
diff --git a/src/internal/TablesAndTuples/Program.cs b/src/internal/TablesAndTuples/Program.cs
new file mode 100644
index 00000000..634acf9d
--- /dev/null
+++ b/src/internal/TablesAndTuples/Program.cs
@@ -0,0 +1,528 @@
1using System;
2using System.Collections.Generic;
3using System.IO;
4using System.Linq;
5using System.Text;
6using System.Text.RegularExpressions;
7using System.Xml.Linq;
8using SimpleJson;
9
10namespace TablesAndSymbols
11{
12 class Program
13 {
14 static void Main(string[] args)
15 {
16 if (args.Length == 0)
17 {
18 return;
19 }
20 else if (Path.GetExtension(args[0]) == ".xml")
21 {
22 if (args.Length < 2)
23 {
24 Console.WriteLine("Need to specify output json file as well.");
25 return;
26 }
27 if (Path.GetExtension(args[1]) != ".json")
28 {
29 Console.WriteLine("Output needs to be .json");
30 return;
31 }
32
33 string prefix = null;
34 if (args.Length > 2)
35 {
36 prefix = args[2];
37 }
38
39 var csFile = Path.Combine(Path.GetDirectoryName(args[1]), String.Concat(prefix ?? "WindowsInstaller", "TableDefinitions.cs"));
40
41 ReadXmlWriteJson(Path.GetFullPath(args[0]), Path.GetFullPath(args[1]), Path.GetFullPath(csFile), prefix);
42 }
43 else if (Path.GetExtension(args[0]) == ".json")
44 {
45 string prefix = null;
46 if (args.Length < 2)
47 {
48 Console.WriteLine("Need to specify output folder.");
49 return;
50 }
51 else if (args.Length > 2)
52 {
53 prefix = args[2];
54 }
55
56 ReadJsonWriteCs(Path.GetFullPath(args[0]), Path.GetFullPath(args[1]), prefix);
57 }
58 }
59
60 private static void ReadXmlWriteJson(string inputPath, string outputPath, string csOutputPath, string prefix)
61 {
62 var tableDefinitions = ReadXmlWriteCs(inputPath, csOutputPath, prefix);
63
64 var array = new JsonArray();
65
66 foreach (var tableDefinition in tableDefinitions)
67 {
68 if (tableDefinition.Symbolless)
69 {
70 continue;
71 }
72 var symbolType = tableDefinition.SymbolDefinitionName;
73
74 var fields = new JsonArray();
75 var firstField = true;
76
77 foreach (var columnDefinition in tableDefinition.Columns)
78 {
79 if (firstField)
80 {
81 firstField = false;
82 if (tableDefinition.SymbolIdIsPrimaryKey)
83 {
84 continue;
85 }
86 }
87
88 var fieldName = columnDefinition.Name;
89 fieldName = Regex.Replace(fieldName, "^([^_]+)_([^_]*)$", x =>
90 {
91 return $"{x.Groups[2].Value}{x.Groups[1].Value}Ref";
92 });
93 var type = columnDefinition.Type.ToString().ToLower();
94
95 if (type == "localized")
96 {
97 type = "string";
98 }
99 else if (type == "object")
100 {
101 type = "path";
102 }
103 else if (columnDefinition.Type == ColumnType.Number && columnDefinition.Length == 2 &&
104 columnDefinition.MinValue == 0 && columnDefinition.MaxValue == 1)
105 {
106 type = "bool";
107 }
108
109 if (columnDefinition.Type == ColumnType.Number && columnDefinition.Nullable)
110 {
111 type += "?";
112 }
113
114 var field = new JsonObject
115 {
116 { fieldName, type }
117 };
118
119 fields.Add(field);
120 }
121
122 var obj = new JsonObject
123 {
124 { symbolType, fields }
125 };
126 array.Add(obj);
127 }
128
129 array.Sort(CompareSymbolDefinitions);
130
131 var strat = new PocoJsonSerializerStrategy();
132 var json = SimpleJson.SimpleJson.SerializeObject(array, strat);
133
134 Directory.CreateDirectory(Path.GetDirectoryName(outputPath));
135 File.WriteAllText(outputPath, json);
136 }
137
138 private static List<WixTableDefinition> ReadXmlWriteCs(string inputPath, string outputPath, string prefix)
139 {
140 var tableDefinitions = WixTableDefinition.LoadCollection(inputPath);
141 var text = GenerateCsTableDefinitionsFileText(prefix, tableDefinitions);
142 Console.WriteLine("Writing: {0}", outputPath);
143 File.WriteAllText(outputPath, text);
144 return tableDefinitions;
145 }
146
147 private static void ReadJsonWriteCs(string inputPath, string outputFolder, string prefix)
148 {
149 var json = File.ReadAllText(inputPath);
150 var symbols = SimpleJson.SimpleJson.DeserializeObject(json) as JsonArray;
151
152 var symbolNames = new List<string>();
153
154 foreach (var symbolDefinition in symbols.Cast<JsonObject>())
155 {
156 var symbolName = symbolDefinition.Keys.Single();
157 var fields = symbolDefinition.Values.Single() as JsonArray;
158
159 var list = GetFields(fields).ToList();
160
161 symbolNames.Add(symbolName);
162
163 var text = GenerateSymbolFileText(prefix, symbolName, list);
164
165 var pathSymbol = Path.Combine(outputFolder, symbolName + "Symbol.cs");
166 Console.WriteLine("Writing: {0}", pathSymbol);
167 File.WriteAllText(pathSymbol, text);
168 }
169
170 var content = SymbolNamesFileContent(prefix, symbolNames);
171 var pathNames = Path.Combine(outputFolder, String.Concat(prefix, "SymbolDefinitions.cs"));
172 Console.WriteLine("Writing: {0}", pathNames);
173 File.WriteAllText(pathNames, content);
174 }
175
176 private static IEnumerable<(string Name, string Type, string ClrType, string AsFunction)> GetFields(JsonArray fields)
177 {
178 foreach (var field in fields.Cast<JsonObject>())
179 {
180 var fieldName = field.Keys.Single();
181 var fieldType = field.Values.Single() as string;
182
183 var clrType = ConvertToClrType(fieldType);
184 fieldType = ConvertToFieldType(fieldType);
185
186 var asFunction = $"As{(clrType.Contains("?") ? "Nullable" : "")}{fieldType}()";
187
188 yield return (Name: fieldName, Type: fieldType, ClrType: clrType, AsFunction: asFunction);
189 }
190 }
191
192 private static string GenerateCsTableDefinitionsFileText(string prefix, List<WixTableDefinition> tableDefinitions)
193 {
194 var ns = prefix ?? "Data";
195
196 var startClassDef = String.Join(Environment.NewLine,
197 "// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.",
198 "",
199 "namespace WixToolset.{1}",
200 "{",
201 " using WixToolset.Data.WindowsInstaller;",
202 "",
203 " public static class {2}TableDefinitions",
204 " {");
205 var startTableDef = String.Join(Environment.NewLine,
206 " public static readonly TableDefinition {1} = new TableDefinition(",
207 " \"{2}\",",
208 " {3},",
209 " new[]",
210 " {");
211 var columnDef =
212 " new ColumnDefinition(\"{1}\", ColumnType.{2}, {3}, primaryKey: {4}, nullable: {5}, ColumnCategory.{6}";
213 var endColumnsDef = String.Join(Environment.NewLine,
214 " },");
215 var unrealDef =
216 " unreal: true,";
217 var endTableDef = String.Join(Environment.NewLine,
218 " symbolIdIsPrimaryKey: {1}",
219 " );",
220 "");
221 var startAllTablesDef = String.Join(Environment.NewLine,
222 " public static readonly TableDefinition[] All = new[]",
223 " {");
224 var allTableDef =
225 " {1},";
226 var endAllTablesDef =
227 " };";
228 var endClassDef = String.Join(Environment.NewLine,
229 " }",
230 "}");
231
232 var sb = new StringBuilder();
233
234 sb.AppendLine(startClassDef.Replace("{1}", ns).Replace("{2}", prefix));
235 foreach (var tableDefinition in tableDefinitions)
236 {
237 var symbolDefinition = tableDefinition.Symbolless ? "null" : $"{prefix}SymbolDefinitions.{tableDefinition.SymbolDefinitionName}";
238 sb.AppendLine(startTableDef.Replace("{1}", tableDefinition.VariableName).Replace("{2}", tableDefinition.Name).Replace("{3}", symbolDefinition));
239 foreach (var columnDefinition in tableDefinition.Columns)
240 {
241 sb.Append(columnDef.Replace("{1}", columnDefinition.Name).Replace("{2}", columnDefinition.Type.ToString()).Replace("{3}", columnDefinition.Length.ToString())
242 .Replace("{4}", columnDefinition.PrimaryKey.ToString().ToLower()).Replace("{5}", columnDefinition.Nullable.ToString().ToLower()).Replace("{6}", columnDefinition.Category.ToString()));
243 if (columnDefinition.MinValue.HasValue)
244 {
245 sb.AppendFormat(", minValue: {0}", columnDefinition.MinValue.Value);
246 }
247 if (columnDefinition.MaxValue.HasValue)
248 {
249 sb.AppendFormat(", maxValue: {0}", columnDefinition.MaxValue.Value);
250 }
251 if (!String.IsNullOrEmpty(columnDefinition.KeyTable))
252 {
253 sb.AppendFormat(", keyTable: \"{0}\"", columnDefinition.KeyTable);
254 }
255 if (columnDefinition.KeyColumn.HasValue)
256 {
257 sb.AppendFormat(", keyColumn: {0}", columnDefinition.KeyColumn.Value);
258 }
259 if (!String.IsNullOrEmpty(columnDefinition.Possibilities))
260 {
261 sb.AppendFormat(", possibilities: \"{0}\"", columnDefinition.Possibilities);
262 }
263 if (!String.IsNullOrEmpty(columnDefinition.Description))
264 {
265 sb.AppendFormat(", description: \"{0}\"", columnDefinition.Description.Replace("\\", "\\\\").Replace("\"", "\\\""));
266 }
267 if (columnDefinition.ModularizeType.HasValue && columnDefinition.ModularizeType.Value != ColumnModularizeType.None)
268 {
269 sb.AppendFormat(", modularizeType: ColumnModularizeType.{0}", columnDefinition.ModularizeType.ToString());
270 }
271 if (columnDefinition.ForceLocalizable)
272 {
273 sb.Append(", forceLocalizable: true");
274 }
275 if (columnDefinition.UseCData)
276 {
277 sb.Append(", useCData: true");
278 }
279 if (columnDefinition.Unreal)
280 {
281 sb.Append(", unreal: true");
282 }
283 sb.AppendLine("),");
284 }
285 sb.AppendLine(endColumnsDef);
286 if (tableDefinition.Unreal)
287 {
288 sb.AppendLine(unrealDef);
289 }
290 sb.AppendLine(endTableDef.Replace("{1}", tableDefinition.SymbolIdIsPrimaryKey.ToString().ToLower()));
291 }
292 sb.AppendLine(startAllTablesDef);
293 foreach (var tableDefinition in tableDefinitions)
294 {
295 sb.AppendLine(allTableDef.Replace("{1}", tableDefinition.VariableName));
296 }
297 sb.AppendLine(endAllTablesDef);
298 sb.AppendLine(endClassDef);
299
300 return sb.ToString();
301 }
302
303 private static string GenerateSymbolFileText(string prefix, string symbolName, List<(string Name, string Type, string ClrType, string AsFunction)> symbolFields)
304 {
305 var ns = prefix ?? "Data";
306 var toString = String.IsNullOrEmpty(prefix) ? null : ".ToString()";
307
308 var startFileDef = String.Join(Environment.NewLine,
309 "// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.",
310 "",
311 "namespace WixToolset.{2}",
312 "{");
313 var usingDataDef =
314 " using WixToolset.Data;";
315 var startSymbolDef = String.Join(Environment.NewLine,
316 " using WixToolset.{2}.Symbols;",
317 "",
318 " public static partial class {3}SymbolDefinitions",
319 " {",
320 " public static readonly IntermediateSymbolDefinition {1} = new IntermediateSymbolDefinition(",
321 " {3}SymbolDefinitionType.{1}{4},",
322 " new{5}[]",
323 " {");
324 var fieldDef =
325 " new IntermediateFieldDefinition(nameof({1}SymbolFields.{2}), IntermediateFieldType.{3}),";
326 var endSymbolDef = String.Join(Environment.NewLine,
327 " },",
328 " typeof({1}Symbol));",
329 " }",
330 "}",
331 "",
332 "namespace WixToolset.{2}.Symbols",
333 "{");
334 var startEnumDef = String.Join(Environment.NewLine,
335 " public enum {1}SymbolFields",
336 " {");
337 var fieldEnum =
338 " {2},";
339 var startSymbol = String.Join(Environment.NewLine,
340 " }",
341 "",
342 " public class {1}Symbol : IntermediateSymbol",
343 " {",
344 " public {1}Symbol() : base({3}SymbolDefinitions.{1}, null, null)",
345 " {",
346 " }",
347 "",
348 " public {1}Symbol(SourceLineNumber sourceLineNumber, Identifier id = null) : base({3}SymbolDefinitions.{1}, sourceLineNumber, id)",
349 " {",
350 " }",
351 "",
352 " public IntermediateField this[{1}SymbolFields index] => this.Fields[(int)index];");
353 var fieldProp = String.Join(Environment.NewLine,
354 "",
355 " public {4} {2}",
356 " {",
357 " get => {6}this.Fields[(int){1}SymbolFields.{2}]{5};",
358 " set => this.Set((int){1}SymbolFields.{2}, value);",
359 " }");
360 var endSymbol = String.Join(Environment.NewLine,
361 " }",
362 "}");
363
364 var sb = new StringBuilder();
365
366 sb.AppendLine(startFileDef.Replace("{2}", ns));
367 if (ns != "Data")
368 {
369 sb.AppendLine(usingDataDef);
370 }
371 sb.AppendLine(startSymbolDef.Replace("{1}", symbolName).Replace("{2}", ns).Replace("{3}", prefix).Replace("{4}", toString).Replace("{5}", symbolFields.Any() ? null : " IntermediateFieldDefinition"));
372 foreach (var field in symbolFields)
373 {
374 sb.AppendLine(fieldDef.Replace("{1}", symbolName).Replace("{2}", field.Name).Replace("{3}", field.Type));
375 }
376 sb.AppendLine(endSymbolDef.Replace("{1}", symbolName).Replace("{2}", ns).Replace("{3}", prefix));
377 if (ns != "Data")
378 {
379 sb.AppendLine(usingDataDef);
380 sb.AppendLine();
381 }
382 sb.AppendLine(startEnumDef.Replace("{1}", symbolName));
383 foreach (var field in symbolFields)
384 {
385 sb.AppendLine(fieldEnum.Replace("{1}", symbolName).Replace("{2}", field.Name));
386 }
387 sb.AppendLine(startSymbol.Replace("{1}", symbolName).Replace("{2}", ns).Replace("{3}", prefix));
388 foreach (var field in symbolFields)
389 {
390 var useCast = ns == "Data" && field.AsFunction != "AsPath()";
391 var cast = useCast ? $"({field.ClrType})" : null;
392 var asFunction = useCast ? null : $".{field.AsFunction}";
393 sb.AppendLine(fieldProp.Replace("{1}", symbolName).Replace("{2}", field.Name).Replace("{3}", field.Type).Replace("{4}", field.ClrType).Replace("{5}", asFunction).Replace("{6}", cast));
394 }
395 sb.Append(endSymbol);
396
397 return sb.ToString();
398 }
399
400 private static string SymbolNamesFileContent(string prefix, List<string> symbolNames)
401 {
402 var ns = prefix ?? "Data";
403
404 var header = String.Join(Environment.NewLine,
405 "// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.",
406 "",
407 "namespace WixToolset.{2}",
408 "{",
409 " using System;",
410 " using WixToolset.Data;",
411 "",
412 " public enum {3}SymbolDefinitionType",
413 " {");
414 var namesFormat =
415 " {1},";
416 var midpoint = String.Join(Environment.NewLine,
417 " }",
418 "",
419 " public static partial class {3}SymbolDefinitions",
420 " {",
421 " public static readonly Version Version = new Version(\"4.0.0\");",
422 "",
423 " public static IntermediateSymbolDefinition ByName(string name)",
424 " {",
425 " if (!Enum.TryParse(name, out {3}SymbolDefinitionType type))",
426 " {",
427 " return null;",
428 " }",
429 "",
430 " return ByType(type);",
431 " }",
432 "",
433 " public static IntermediateSymbolDefinition ByType({3}SymbolDefinitionType type)",
434 " {",
435 " switch (type)",
436 " {");
437
438 var caseFormat = String.Join(Environment.NewLine,
439 " case {3}SymbolDefinitionType.{1}:",
440 " return {3}SymbolDefinitions.{1};",
441 "");
442
443 var footer = String.Join(Environment.NewLine,
444 " default:",
445 " throw new ArgumentOutOfRangeException(nameof(type));",
446 " }",
447 " }",
448 " }",
449 "}");
450
451 var sb = new StringBuilder();
452
453 sb.AppendLine(header.Replace("{2}", ns).Replace("{3}", prefix));
454 foreach (var symbolName in symbolNames)
455 {
456 sb.AppendLine(namesFormat.Replace("{1}", symbolName).Replace("{2}", ns).Replace("{3}", prefix));
457 }
458 sb.AppendLine(midpoint.Replace("{2}", ns).Replace("{3}", prefix));
459 foreach (var symbolName in symbolNames)
460 {
461 sb.AppendLine(caseFormat.Replace("{1}", symbolName).Replace("{2}", ns).Replace("{3}", prefix));
462 }
463 sb.AppendLine(footer);
464
465 return sb.ToString();
466 }
467
468 private static string ConvertToFieldType(string fieldType)
469 {
470 switch (fieldType.ToLowerInvariant())
471 {
472 case "bool":
473 return "Bool";
474 case "bool?":
475 return "Number";
476
477 case "string":
478 case "preserved":
479 return "String";
480
481 case "number":
482 case "number?":
483 return "Number";
484
485 case "path":
486 return "Path";
487 }
488
489 throw new ArgumentException(fieldType);
490 }
491
492 private static string ConvertToClrType(string fieldType)
493 {
494 switch (fieldType.ToLowerInvariant())
495 {
496 case "bool":
497 return "bool";
498 case "bool?":
499 return "bool?";
500
501 case "string":
502 case "preserved":
503 return "string";
504
505 case "number":
506 return "int";
507 case "number?":
508 return "int?";
509
510 case "path":
511 return "IntermediateFieldPathValue";
512 }
513
514 throw new ArgumentException(fieldType);
515 }
516
517 private static int CompareSymbolDefinitions(object x, object y)
518 {
519 var first = (JsonObject)x;
520 var second = (JsonObject)y;
521
522 var firstType = first.Keys.Single();
523 var secondType = second.Keys.Single();
524
525 return firstType.CompareTo(secondType);
526 }
527 }
528}
diff --git a/src/internal/TablesAndTuples/SimpleJson.cs b/src/internal/TablesAndTuples/SimpleJson.cs
new file mode 100644
index 00000000..3d956f6e
--- /dev/null
+++ b/src/internal/TablesAndTuples/SimpleJson.cs
@@ -0,0 +1,2127 @@
1//-----------------------------------------------------------------------
2// <copyright file="SimpleJson.cs" company="The Outercurve Foundation">
3// Copyright (c) 2011, The Outercurve Foundation.
4//
5// Licensed under the MIT License (the "License");
6// you may not use this file except in compliance with the License.
7// You may obtain a copy of the License at
8// http://www.opensource.org/licenses/mit-license.php
9//
10// Unless required by applicable law or agreed to in writing, software
11// distributed under the License is distributed on an "AS IS" BASIS,
12// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13// See the License for the specific language governing permissions and
14// limitations under the License.
15// </copyright>
16// <author>Nathan Totten (ntotten.com), Jim Zimmerman (jimzimmerman.com) and Prabir Shrestha (prabir.me)</author>
17// <website>https://github.com/facebook-csharp-sdk/simple-json</website>
18//-----------------------------------------------------------------------
19
20// VERSION:
21
22// NOTE: uncomment the following line to make SimpleJson class internal.
23#define SIMPLE_JSON_INTERNAL
24
25// NOTE: uncomment the following line to make JsonArray and JsonObject class internal.
26#define SIMPLE_JSON_OBJARRAYINTERNAL
27
28// NOTE: uncomment the following line to enable dynamic support.
29//#define SIMPLE_JSON_DYNAMIC
30
31// NOTE: uncomment the following line to enable DataContract support.
32//#define SIMPLE_JSON_DATACONTRACT
33
34// NOTE: uncomment the following line to enable IReadOnlyCollection<T> and IReadOnlyList<T> support.
35//#define SIMPLE_JSON_READONLY_COLLECTIONS
36
37// NOTE: uncomment the following line to disable linq expressions/compiled lambda (better performance) instead of method.invoke().
38// define if you are using .net framework <= 3.0 or < WP7.5
39//#define SIMPLE_JSON_NO_LINQ_EXPRESSION
40
41// NOTE: uncomment the following line if you are compiling under Window Metro style application/library.
42// usually already defined in properties
43//#define NETFX_CORE;
44
45// If you are targetting WinStore, WP8 and NET4.5+ PCL make sure to #define SIMPLE_JSON_TYPEINFO;
46
47// original json parsing code from http://techblog.procurios.nl/k/618/news/view/14605/14863/How-do-I-write-my-own-parser-for-JSON.html
48
49#if NETFX_CORE
50#define SIMPLE_JSON_TYPEINFO
51#endif
52
53using System;
54using System.CodeDom.Compiler;
55using System.Collections;
56using System.Collections.Generic;
57#if !SIMPLE_JSON_NO_LINQ_EXPRESSION
58using System.Linq.Expressions;
59#endif
60using System.ComponentModel;
61using System.Diagnostics.CodeAnalysis;
62#if SIMPLE_JSON_DYNAMIC
63using System.Dynamic;
64#endif
65using System.Globalization;
66using System.Reflection;
67using System.Runtime.Serialization;
68using System.Text;
69using SimpleJson.Reflection;
70
71// ReSharper disable LoopCanBeConvertedToQuery
72// ReSharper disable RedundantExplicitArrayCreation
73// ReSharper disable SuggestUseVarKeywordEvident
74namespace SimpleJson
75{
76 /// <summary>
77 /// Represents the json array.
78 /// </summary>
79 [GeneratedCode("simple-json", "1.0.0")]
80 [EditorBrowsable(EditorBrowsableState.Never)]
81 [SuppressMessage("Microsoft.Naming", "CA1710:IdentifiersShouldHaveCorrectSuffix")]
82#if SIMPLE_JSON_OBJARRAYINTERNAL
83 internal
84#else
85 public
86#endif
87 class JsonArray : List<object>
88 {
89 /// <summary>
90 /// Initializes a new instance of the <see cref="JsonArray"/> class.
91 /// </summary>
92 public JsonArray() { }
93
94 /// <summary>
95 /// Initializes a new instance of the <see cref="JsonArray"/> class.
96 /// </summary>
97 /// <param name="capacity">The capacity of the json array.</param>
98 public JsonArray(int capacity) : base(capacity) { }
99
100 /// <summary>
101 /// The json representation of the array.
102 /// </summary>
103 /// <returns>The json representation of the array.</returns>
104 public override string ToString()
105 {
106 return SimpleJson.SerializeObject(this) ?? string.Empty;
107 }
108 }
109
110 /// <summary>
111 /// Represents the json object.
112 /// </summary>
113 [GeneratedCode("simple-json", "1.0.0")]
114 [EditorBrowsable(EditorBrowsableState.Never)]
115 [SuppressMessage("Microsoft.Naming", "CA1710:IdentifiersShouldHaveCorrectSuffix")]
116#if SIMPLE_JSON_OBJARRAYINTERNAL
117 internal
118#else
119 public
120#endif
121 class JsonObject :
122#if SIMPLE_JSON_DYNAMIC
123 DynamicObject,
124#endif
125 IDictionary<string, object>
126 {
127 /// <summary>
128 /// The internal member dictionary.
129 /// </summary>
130 private readonly Dictionary<string, object> _members;
131
132 /// <summary>
133 /// Initializes a new instance of <see cref="JsonObject"/>.
134 /// </summary>
135 public JsonObject()
136 {
137 _members = new Dictionary<string, object>();
138 }
139
140 /// <summary>
141 /// Initializes a new instance of <see cref="JsonObject"/>.
142 /// </summary>
143 /// <param name="comparer">The <see cref="T:System.Collections.Generic.IEqualityComparer`1"/> implementation to use when comparing keys, or null to use the default <see cref="T:System.Collections.Generic.EqualityComparer`1"/> for the type of the key.</param>
144 public JsonObject(IEqualityComparer<string> comparer)
145 {
146 _members = new Dictionary<string, object>(comparer);
147 }
148
149 /// <summary>
150 /// Gets the <see cref="System.Object"/> at the specified index.
151 /// </summary>
152 /// <value></value>
153 public object this[int index]
154 {
155 get { return GetAtIndex(_members, index); }
156 }
157
158 internal static object GetAtIndex(IDictionary<string, object> obj, int index)
159 {
160 if (obj == null)
161 throw new ArgumentNullException("obj");
162 if (index >= obj.Count)
163 throw new ArgumentOutOfRangeException("index");
164 int i = 0;
165 foreach (KeyValuePair<string, object> o in obj)
166 if (i++ == index) return o.Value;
167 return null;
168 }
169
170 /// <summary>
171 /// Adds the specified key.
172 /// </summary>
173 /// <param name="key">The key.</param>
174 /// <param name="value">The value.</param>
175 public void Add(string key, object value)
176 {
177 _members.Add(key, value);
178 }
179
180 /// <summary>
181 /// Determines whether the specified key contains key.
182 /// </summary>
183 /// <param name="key">The key.</param>
184 /// <returns>
185 /// <c>true</c> if the specified key contains key; otherwise, <c>false</c>.
186 /// </returns>
187 public bool ContainsKey(string key)
188 {
189 return _members.ContainsKey(key);
190 }
191
192 /// <summary>
193 /// Gets the keys.
194 /// </summary>
195 /// <value>The keys.</value>
196 public ICollection<string> Keys
197 {
198 get { return _members.Keys; }
199 }
200
201 /// <summary>
202 /// Removes the specified key.
203 /// </summary>
204 /// <param name="key">The key.</param>
205 /// <returns></returns>
206 public bool Remove(string key)
207 {
208 return _members.Remove(key);
209 }
210
211 /// <summary>
212 /// Tries the get value.
213 /// </summary>
214 /// <param name="key">The key.</param>
215 /// <param name="value">The value.</param>
216 /// <returns></returns>
217 public bool TryGetValue(string key, out object value)
218 {
219 return _members.TryGetValue(key, out value);
220 }
221
222 /// <summary>
223 /// Gets the values.
224 /// </summary>
225 /// <value>The values.</value>
226 public ICollection<object> Values
227 {
228 get { return _members.Values; }
229 }
230
231 /// <summary>
232 /// Gets or sets the <see cref="System.Object"/> with the specified key.
233 /// </summary>
234 /// <value></value>
235 public object this[string key]
236 {
237 get { return _members[key]; }
238 set { _members[key] = value; }
239 }
240
241 /// <summary>
242 /// Adds the specified item.
243 /// </summary>
244 /// <param name="item">The item.</param>
245 public void Add(KeyValuePair<string, object> item)
246 {
247 _members.Add(item.Key, item.Value);
248 }
249
250 /// <summary>
251 /// Clears this instance.
252 /// </summary>
253 public void Clear()
254 {
255 _members.Clear();
256 }
257
258 /// <summary>
259 /// Determines whether [contains] [the specified item].
260 /// </summary>
261 /// <param name="item">The item.</param>
262 /// <returns>
263 /// <c>true</c> if [contains] [the specified item]; otherwise, <c>false</c>.
264 /// </returns>
265 public bool Contains(KeyValuePair<string, object> item)
266 {
267 return _members.ContainsKey(item.Key) && _members[item.Key] == item.Value;
268 }
269
270 /// <summary>
271 /// Copies to.
272 /// </summary>
273 /// <param name="array">The array.</param>
274 /// <param name="arrayIndex">Index of the array.</param>
275 public void CopyTo(KeyValuePair<string, object>[] array, int arrayIndex)
276 {
277 if (array == null) throw new ArgumentNullException("array");
278 int num = Count;
279 foreach (KeyValuePair<string, object> kvp in this)
280 {
281 array[arrayIndex++] = kvp;
282 if (--num <= 0)
283 return;
284 }
285 }
286
287 /// <summary>
288 /// Gets the count.
289 /// </summary>
290 /// <value>The count.</value>
291 public int Count
292 {
293 get { return _members.Count; }
294 }
295
296 /// <summary>
297 /// Gets a value indicating whether this instance is read only.
298 /// </summary>
299 /// <value>
300 /// <c>true</c> if this instance is read only; otherwise, <c>false</c>.
301 /// </value>
302 public bool IsReadOnly
303 {
304 get { return false; }
305 }
306
307 /// <summary>
308 /// Removes the specified item.
309 /// </summary>
310 /// <param name="item">The item.</param>
311 /// <returns></returns>
312 public bool Remove(KeyValuePair<string, object> item)
313 {
314 return _members.Remove(item.Key);
315 }
316
317 /// <summary>
318 /// Gets the enumerator.
319 /// </summary>
320 /// <returns></returns>
321 public IEnumerator<KeyValuePair<string, object>> GetEnumerator()
322 {
323 return _members.GetEnumerator();
324 }
325
326 /// <summary>
327 /// Returns an enumerator that iterates through a collection.
328 /// </summary>
329 /// <returns>
330 /// An <see cref="T:System.Collections.IEnumerator"/> object that can be used to iterate through the collection.
331 /// </returns>
332 IEnumerator IEnumerable.GetEnumerator()
333 {
334 return _members.GetEnumerator();
335 }
336
337 /// <summary>
338 /// Returns a json <see cref="T:System.String"/> that represents the current <see cref="T:System.Object"/>.
339 /// </summary>
340 /// <returns>
341 /// A json <see cref="T:System.String"/> that represents the current <see cref="T:System.Object"/>.
342 /// </returns>
343 public override string ToString()
344 {
345 return SimpleJson.SerializeObject(this);
346 }
347
348#if SIMPLE_JSON_DYNAMIC
349 /// <summary>
350 /// Provides implementation for type conversion operations. Classes derived from the <see cref="T:System.Dynamic.DynamicObject"/> class can override this method to specify dynamic behavior for operations that convert an object from one type to another.
351 /// </summary>
352 /// <param name="binder">Provides information about the conversion operation. The binder.Type property provides the type to which the object must be converted. For example, for the statement (String)sampleObject in C# (CType(sampleObject, Type) in Visual Basic), where sampleObject is an instance of the class derived from the <see cref="T:System.Dynamic.DynamicObject"/> class, binder.Type returns the <see cref="T:System.String"/> type. The binder.Explicit property provides information about the kind of conversion that occurs. It returns true for explicit conversion and false for implicit conversion.</param>
353 /// <param name="result">The result of the type conversion operation.</param>
354 /// <returns>
355 /// Alwasy returns true.
356 /// </returns>
357 public override bool TryConvert(ConvertBinder binder, out object result)
358 {
359 // <pex>
360 if (binder == null)
361 throw new ArgumentNullException("binder");
362 // </pex>
363 Type targetType = binder.Type;
364
365 if ((targetType == typeof(IEnumerable)) ||
366 (targetType == typeof(IEnumerable<KeyValuePair<string, object>>)) ||
367 (targetType == typeof(IDictionary<string, object>)) ||
368 (targetType == typeof(IDictionary)))
369 {
370 result = this;
371 return true;
372 }
373
374 return base.TryConvert(binder, out result);
375 }
376
377 /// <summary>
378 /// Provides the implementation for operations that delete an object member. This method is not intended for use in C# or Visual Basic.
379 /// </summary>
380 /// <param name="binder">Provides information about the deletion.</param>
381 /// <returns>
382 /// Alwasy returns true.
383 /// </returns>
384 public override bool TryDeleteMember(DeleteMemberBinder binder)
385 {
386 // <pex>
387 if (binder == null)
388 throw new ArgumentNullException("binder");
389 // </pex>
390 return _members.Remove(binder.Name);
391 }
392
393 /// <summary>
394 /// Provides the implementation for operations that get a value by index. Classes derived from the <see cref="T:System.Dynamic.DynamicObject"/> class can override this method to specify dynamic behavior for indexing operations.
395 /// </summary>
396 /// <param name="binder">Provides information about the operation.</param>
397 /// <param name="indexes">The indexes that are used in the operation. For example, for the sampleObject[3] operation in C# (sampleObject(3) in Visual Basic), where sampleObject is derived from the DynamicObject class, <paramref name="indexes"/> is equal to 3.</param>
398 /// <param name="result">The result of the index operation.</param>
399 /// <returns>
400 /// Alwasy returns true.
401 /// </returns>
402 public override bool TryGetIndex(GetIndexBinder binder, object[] indexes, out object result)
403 {
404 if (indexes == null) throw new ArgumentNullException("indexes");
405 if (indexes.Length == 1)
406 {
407 result = ((IDictionary<string, object>)this)[(string)indexes[0]];
408 return true;
409 }
410 result = null;
411 return true;
412 }
413
414 /// <summary>
415 /// Provides the implementation for operations that get member values. Classes derived from the <see cref="T:System.Dynamic.DynamicObject"/> class can override this method to specify dynamic behavior for operations such as getting a value for a property.
416 /// </summary>
417 /// <param name="binder">Provides information about the object that called the dynamic operation. The binder.Name property provides the name of the member on which the dynamic operation is performed. For example, for the Console.WriteLine(sampleObject.SampleProperty) statement, where sampleObject is an instance of the class derived from the <see cref="T:System.Dynamic.DynamicObject"/> class, binder.Name returns "SampleProperty". The binder.IgnoreCase property specifies whether the member name is case-sensitive.</param>
418 /// <param name="result">The result of the get operation. For example, if the method is called for a property, you can assign the property value to <paramref name="result"/>.</param>
419 /// <returns>
420 /// Alwasy returns true.
421 /// </returns>
422 public override bool TryGetMember(GetMemberBinder binder, out object result)
423 {
424 object value;
425 if (_members.TryGetValue(binder.Name, out value))
426 {
427 result = value;
428 return true;
429 }
430 result = null;
431 return true;
432 }
433
434 /// <summary>
435 /// Provides the implementation for operations that set a value by index. Classes derived from the <see cref="T:System.Dynamic.DynamicObject"/> class can override this method to specify dynamic behavior for operations that access objects by a specified index.
436 /// </summary>
437 /// <param name="binder">Provides information about the operation.</param>
438 /// <param name="indexes">The indexes that are used in the operation. For example, for the sampleObject[3] = 10 operation in C# (sampleObject(3) = 10 in Visual Basic), where sampleObject is derived from the <see cref="T:System.Dynamic.DynamicObject"/> class, <paramref name="indexes"/> is equal to 3.</param>
439 /// <param name="value">The value to set to the object that has the specified index. For example, for the sampleObject[3] = 10 operation in C# (sampleObject(3) = 10 in Visual Basic), where sampleObject is derived from the <see cref="T:System.Dynamic.DynamicObject"/> class, <paramref name="value"/> is equal to 10.</param>
440 /// <returns>
441 /// true if the operation is successful; otherwise, false. If this method returns false, the run-time binder of the language determines the behavior. (In most cases, a language-specific run-time exception is thrown.
442 /// </returns>
443 public override bool TrySetIndex(SetIndexBinder binder, object[] indexes, object value)
444 {
445 if (indexes == null) throw new ArgumentNullException("indexes");
446 if (indexes.Length == 1)
447 {
448 ((IDictionary<string, object>)this)[(string)indexes[0]] = value;
449 return true;
450 }
451 return base.TrySetIndex(binder, indexes, value);
452 }
453
454 /// <summary>
455 /// Provides the implementation for operations that set member values. Classes derived from the <see cref="T:System.Dynamic.DynamicObject"/> class can override this method to specify dynamic behavior for operations such as setting a value for a property.
456 /// </summary>
457 /// <param name="binder">Provides information about the object that called the dynamic operation. The binder.Name property provides the name of the member to which the value is being assigned. For example, for the statement sampleObject.SampleProperty = "Test", where sampleObject is an instance of the class derived from the <see cref="T:System.Dynamic.DynamicObject"/> class, binder.Name returns "SampleProperty". The binder.IgnoreCase property specifies whether the member name is case-sensitive.</param>
458 /// <param name="value">The value to set to the member. For example, for sampleObject.SampleProperty = "Test", where sampleObject is an instance of the class derived from the <see cref="T:System.Dynamic.DynamicObject"/> class, the <paramref name="value"/> is "Test".</param>
459 /// <returns>
460 /// true if the operation is successful; otherwise, false. If this method returns false, the run-time binder of the language determines the behavior. (In most cases, a language-specific run-time exception is thrown.)
461 /// </returns>
462 public override bool TrySetMember(SetMemberBinder binder, object value)
463 {
464 // <pex>
465 if (binder == null)
466 throw new ArgumentNullException("binder");
467 // </pex>
468 _members[binder.Name] = value;
469 return true;
470 }
471
472 /// <summary>
473 /// Returns the enumeration of all dynamic member names.
474 /// </summary>
475 /// <returns>
476 /// A sequence that contains dynamic member names.
477 /// </returns>
478 public override IEnumerable<string> GetDynamicMemberNames()
479 {
480 foreach (var key in Keys)
481 yield return key;
482 }
483#endif
484 }
485}
486
487namespace SimpleJson
488{
489 /// <summary>
490 /// This class encodes and decodes JSON strings.
491 /// Spec. details, see http://www.json.org/
492 ///
493 /// JSON uses Arrays and Objects. These correspond here to the datatypes JsonArray(IList&lt;object>) and JsonObject(IDictionary&lt;string,object>).
494 /// All numbers are parsed to doubles.
495 /// </summary>
496 [GeneratedCode("simple-json", "1.0.0")]
497#if SIMPLE_JSON_INTERNAL
498 internal
499#else
500 public
501#endif
502 static class SimpleJson
503 {
504 private const int TOKEN_NONE = 0;
505 private const int TOKEN_CURLY_OPEN = 1;
506 private const int TOKEN_CURLY_CLOSE = 2;
507 private const int TOKEN_SQUARED_OPEN = 3;
508 private const int TOKEN_SQUARED_CLOSE = 4;
509 private const int TOKEN_COLON = 5;
510 private const int TOKEN_COMMA = 6;
511 private const int TOKEN_STRING = 7;
512 private const int TOKEN_NUMBER = 8;
513 private const int TOKEN_TRUE = 9;
514 private const int TOKEN_FALSE = 10;
515 private const int TOKEN_NULL = 11;
516 private const int BUILDER_CAPACITY = 2000;
517
518 private static readonly char[] EscapeTable;
519 private static readonly char[] EscapeCharacters = new char[] { '"', '\\', '\b', '\f', '\n', '\r', '\t' };
520 private static readonly string EscapeCharactersString = new string(EscapeCharacters);
521
522 static SimpleJson()
523 {
524 EscapeTable = new char[93];
525 EscapeTable['"'] = '"';
526 EscapeTable['\\'] = '\\';
527 EscapeTable['\b'] = 'b';
528 EscapeTable['\f'] = 'f';
529 EscapeTable['\n'] = 'n';
530 EscapeTable['\r'] = 'r';
531 EscapeTable['\t'] = 't';
532 }
533
534 /// <summary>
535 /// Parses the string json into a value
536 /// </summary>
537 /// <param name="json">A JSON string.</param>
538 /// <returns>An IList&lt;object>, a IDictionary&lt;string,object>, a double, a string, null, true, or false</returns>
539 public static object DeserializeObject(string json)
540 {
541 object obj;
542 if (TryDeserializeObject(json, out obj))
543 return obj;
544 throw new SerializationException("Invalid JSON string");
545 }
546
547 /// <summary>
548 /// Try parsing the json string into a value.
549 /// </summary>
550 /// <param name="json">
551 /// A JSON string.
552 /// </param>
553 /// <param name="obj">
554 /// The object.
555 /// </param>
556 /// <returns>
557 /// Returns true if successfull otherwise false.
558 /// </returns>
559 [SuppressMessage("Microsoft.Design", "CA1007:UseGenericsWhereAppropriate", Justification = "Need to support .NET 2")]
560 public static bool TryDeserializeObject(string json, out object obj)
561 {
562 bool success = true;
563 if (json != null)
564 {
565 char[] charArray = json.ToCharArray();
566 int index = 0;
567 obj = ParseValue(charArray, ref index, ref success);
568 }
569 else
570 obj = null;
571
572 return success;
573 }
574
575 public static object DeserializeObject(string json, Type type, IJsonSerializerStrategy jsonSerializerStrategy)
576 {
577 object jsonObject = DeserializeObject(json);
578 return type == null || jsonObject != null && ReflectionUtils.IsAssignableFrom(jsonObject.GetType(), type)
579 ? jsonObject
580 : (jsonSerializerStrategy ?? CurrentJsonSerializerStrategy).DeserializeObject(jsonObject, type);
581 }
582
583 public static object DeserializeObject(string json, Type type)
584 {
585 return DeserializeObject(json, type, null);
586 }
587
588 public static T DeserializeObject<T>(string json, IJsonSerializerStrategy jsonSerializerStrategy)
589 {
590 return (T)DeserializeObject(json, typeof(T), jsonSerializerStrategy);
591 }
592
593 public static T DeserializeObject<T>(string json)
594 {
595 return (T)DeserializeObject(json, typeof(T), null);
596 }
597
598 /// <summary>
599 /// Converts a IDictionary&lt;string,object> / IList&lt;object> object into a JSON string
600 /// </summary>
601 /// <param name="json">A IDictionary&lt;string,object> / IList&lt;object></param>
602 /// <param name="jsonSerializerStrategy">Serializer strategy to use</param>
603 /// <returns>A JSON encoded string, or null if object 'json' is not serializable</returns>
604 public static string SerializeObject(object json, IJsonSerializerStrategy jsonSerializerStrategy)
605 {
606 StringBuilder builder = new StringBuilder(BUILDER_CAPACITY);
607 bool success = SerializeValue(jsonSerializerStrategy, json, builder);
608 return (success ? builder.ToString() : null);
609 }
610
611 public static string SerializeObject(object json)
612 {
613 return SerializeObject(json, CurrentJsonSerializerStrategy);
614 }
615
616 public static string EscapeToJavascriptString(string jsonString)
617 {
618 if (string.IsNullOrEmpty(jsonString))
619 return jsonString;
620
621 StringBuilder sb = new StringBuilder();
622 char c;
623
624 for (int i = 0; i < jsonString.Length;)
625 {
626 c = jsonString[i++];
627
628 if (c == '\\')
629 {
630 int remainingLength = jsonString.Length - i;
631 if (remainingLength >= 2)
632 {
633 char lookahead = jsonString[i];
634 if (lookahead == '\\')
635 {
636 sb.Append('\\');
637 ++i;
638 }
639 else if (lookahead == '"')
640 {
641 sb.Append("\"");
642 ++i;
643 }
644 else if (lookahead == 't')
645 {
646 sb.Append('\t');
647 ++i;
648 }
649 else if (lookahead == 'b')
650 {
651 sb.Append('\b');
652 ++i;
653 }
654 else if (lookahead == 'n')
655 {
656 sb.Append('\n');
657 ++i;
658 }
659 else if (lookahead == 'r')
660 {
661 sb.Append('\r');
662 ++i;
663 }
664 }
665 }
666 else
667 {
668 sb.Append(c);
669 }
670 }
671 return sb.ToString();
672 }
673
674 static IDictionary<string, object> ParseObject(char[] json, ref int index, ref bool success)
675 {
676 IDictionary<string, object> table = new JsonObject();
677 int token;
678
679 // {
680 NextToken(json, ref index);
681
682 bool done = false;
683 while (!done)
684 {
685 token = LookAhead(json, index);
686 if (token == TOKEN_NONE)
687 {
688 success = false;
689 return null;
690 }
691 else if (token == TOKEN_COMMA)
692 NextToken(json, ref index);
693 else if (token == TOKEN_CURLY_CLOSE)
694 {
695 NextToken(json, ref index);
696 return table;
697 }
698 else
699 {
700 // name
701 string name = ParseString(json, ref index, ref success);
702 if (!success)
703 {
704 success = false;
705 return null;
706 }
707 // :
708 token = NextToken(json, ref index);
709 if (token != TOKEN_COLON)
710 {
711 success = false;
712 return null;
713 }
714 // value
715 object value = ParseValue(json, ref index, ref success);
716 if (!success)
717 {
718 success = false;
719 return null;
720 }
721 table[name] = value;
722 }
723 }
724 return table;
725 }
726
727 static JsonArray ParseArray(char[] json, ref int index, ref bool success)
728 {
729 JsonArray array = new JsonArray();
730
731 // [
732 NextToken(json, ref index);
733
734 bool done = false;
735 while (!done)
736 {
737 int token = LookAhead(json, index);
738 if (token == TOKEN_NONE)
739 {
740 success = false;
741 return null;
742 }
743 else if (token == TOKEN_COMMA)
744 NextToken(json, ref index);
745 else if (token == TOKEN_SQUARED_CLOSE)
746 {
747 NextToken(json, ref index);
748 break;
749 }
750 else
751 {
752 object value = ParseValue(json, ref index, ref success);
753 if (!success)
754 return null;
755 array.Add(value);
756 }
757 }
758 return array;
759 }
760
761 static object ParseValue(char[] json, ref int index, ref bool success)
762 {
763 switch (LookAhead(json, index))
764 {
765 case TOKEN_STRING:
766 return ParseString(json, ref index, ref success);
767 case TOKEN_NUMBER:
768 return ParseNumber(json, ref index, ref success);
769 case TOKEN_CURLY_OPEN:
770 return ParseObject(json, ref index, ref success);
771 case TOKEN_SQUARED_OPEN:
772 return ParseArray(json, ref index, ref success);
773 case TOKEN_TRUE:
774 NextToken(json, ref index);
775 return true;
776 case TOKEN_FALSE:
777 NextToken(json, ref index);
778 return false;
779 case TOKEN_NULL:
780 NextToken(json, ref index);
781 return null;
782 case TOKEN_NONE:
783 break;
784 }
785 success = false;
786 return null;
787 }
788
789 static string ParseString(char[] json, ref int index, ref bool success)
790 {
791 StringBuilder s = new StringBuilder(BUILDER_CAPACITY);
792 char c;
793
794 EatWhitespace(json, ref index);
795
796 // "
797 c = json[index++];
798 bool complete = false;
799 while (!complete)
800 {
801 if (index == json.Length)
802 break;
803
804 c = json[index++];
805 if (c == '"')
806 {
807 complete = true;
808 break;
809 }
810 else if (c == '\\')
811 {
812 if (index == json.Length)
813 break;
814 c = json[index++];
815 if (c == '"')
816 s.Append('"');
817 else if (c == '\\')
818 s.Append('\\');
819 else if (c == '/')
820 s.Append('/');
821 else if (c == 'b')
822 s.Append('\b');
823 else if (c == 'f')
824 s.Append('\f');
825 else if (c == 'n')
826 s.Append('\n');
827 else if (c == 'r')
828 s.Append('\r');
829 else if (c == 't')
830 s.Append('\t');
831 else if (c == 'u')
832 {
833 int remainingLength = json.Length - index;
834 if (remainingLength >= 4)
835 {
836 // parse the 32 bit hex into an integer codepoint
837 uint codePoint;
838 if (!(success = UInt32.TryParse(new string(json, index, 4), NumberStyles.HexNumber, CultureInfo.InvariantCulture, out codePoint)))
839 return "";
840
841 // convert the integer codepoint to a unicode char and add to string
842 if (0xD800 <= codePoint && codePoint <= 0xDBFF) // if high surrogate
843 {
844 index += 4; // skip 4 chars
845 remainingLength = json.Length - index;
846 if (remainingLength >= 6)
847 {
848 uint lowCodePoint;
849 if (new string(json, index, 2) == "\\u" && UInt32.TryParse(new string(json, index + 2, 4), NumberStyles.HexNumber, CultureInfo.InvariantCulture, out lowCodePoint))
850 {
851 if (0xDC00 <= lowCodePoint && lowCodePoint <= 0xDFFF) // if low surrogate
852 {
853 s.Append((char)codePoint);
854 s.Append((char)lowCodePoint);
855 index += 6; // skip 6 chars
856 continue;
857 }
858 }
859 }
860 success = false; // invalid surrogate pair
861 return "";
862 }
863 s.Append(ConvertFromUtf32((int)codePoint));
864 // skip 4 chars
865 index += 4;
866 }
867 else
868 break;
869 }
870 }
871 else
872 s.Append(c);
873 }
874 if (!complete)
875 {
876 success = false;
877 return null;
878 }
879 return s.ToString();
880 }
881
882 private static string ConvertFromUtf32(int utf32)
883 {
884 // http://www.java2s.com/Open-Source/CSharp/2.6.4-mono-.net-core/System/System/Char.cs.htm
885 if (utf32 < 0 || utf32 > 0x10FFFF)
886 throw new ArgumentOutOfRangeException("utf32", "The argument must be from 0 to 0x10FFFF.");
887 if (0xD800 <= utf32 && utf32 <= 0xDFFF)
888 throw new ArgumentOutOfRangeException("utf32", "The argument must not be in surrogate pair range.");
889 if (utf32 < 0x10000)
890 return new string((char)utf32, 1);
891 utf32 -= 0x10000;
892 return new string(new char[] { (char)((utf32 >> 10) + 0xD800), (char)(utf32 % 0x0400 + 0xDC00) });
893 }
894
895 static object ParseNumber(char[] json, ref int index, ref bool success)
896 {
897 EatWhitespace(json, ref index);
898 int lastIndex = GetLastIndexOfNumber(json, index);
899 int charLength = (lastIndex - index) + 1;
900 object returnNumber;
901 string str = new string(json, index, charLength);
902 if (str.IndexOf(".", StringComparison.OrdinalIgnoreCase) != -1 || str.IndexOf("e", StringComparison.OrdinalIgnoreCase) != -1)
903 {
904 double number;
905 success = double.TryParse(new string(json, index, charLength), NumberStyles.Any, CultureInfo.InvariantCulture, out number);
906 returnNumber = number;
907 }
908 else
909 {
910 long number;
911 success = long.TryParse(new string(json, index, charLength), NumberStyles.Any, CultureInfo.InvariantCulture, out number);
912 returnNumber = number;
913 }
914 index = lastIndex + 1;
915 return returnNumber;
916 }
917
918 static int GetLastIndexOfNumber(char[] json, int index)
919 {
920 int lastIndex;
921 for (lastIndex = index; lastIndex < json.Length; lastIndex++)
922 if ("0123456789+-.eE".IndexOf(json[lastIndex]) == -1) break;
923 return lastIndex - 1;
924 }
925
926 static void EatWhitespace(char[] json, ref int index)
927 {
928 for (; index < json.Length; index++)
929 if (" \t\n\r\b\f".IndexOf(json[index]) == -1) break;
930 }
931
932 static int LookAhead(char[] json, int index)
933 {
934 int saveIndex = index;
935 return NextToken(json, ref saveIndex);
936 }
937
938 [SuppressMessage("Microsoft.Maintainability", "CA1502:AvoidExcessiveComplexity")]
939 static int NextToken(char[] json, ref int index)
940 {
941 EatWhitespace(json, ref index);
942 if (index == json.Length)
943 return TOKEN_NONE;
944 char c = json[index];
945 index++;
946 switch (c)
947 {
948 case '{':
949 return TOKEN_CURLY_OPEN;
950 case '}':
951 return TOKEN_CURLY_CLOSE;
952 case '[':
953 return TOKEN_SQUARED_OPEN;
954 case ']':
955 return TOKEN_SQUARED_CLOSE;
956 case ',':
957 return TOKEN_COMMA;
958 case '"':
959 return TOKEN_STRING;
960 case '0':
961 case '1':
962 case '2':
963 case '3':
964 case '4':
965 case '5':
966 case '6':
967 case '7':
968 case '8':
969 case '9':
970 case '-':
971 return TOKEN_NUMBER;
972 case ':':
973 return TOKEN_COLON;
974 }
975 index--;
976 int remainingLength = json.Length - index;
977 // false
978 if (remainingLength >= 5)
979 {
980 if (json[index] == 'f' && json[index + 1] == 'a' && json[index + 2] == 'l' && json[index + 3] == 's' && json[index + 4] == 'e')
981 {
982 index += 5;
983 return TOKEN_FALSE;
984 }
985 }
986 // true
987 if (remainingLength >= 4)
988 {
989 if (json[index] == 't' && json[index + 1] == 'r' && json[index + 2] == 'u' && json[index + 3] == 'e')
990 {
991 index += 4;
992 return TOKEN_TRUE;
993 }
994 }
995 // null
996 if (remainingLength >= 4)
997 {
998 if (json[index] == 'n' && json[index + 1] == 'u' && json[index + 2] == 'l' && json[index + 3] == 'l')
999 {
1000 index += 4;
1001 return TOKEN_NULL;
1002 }
1003 }
1004 return TOKEN_NONE;
1005 }
1006
1007 static bool SerializeValue(IJsonSerializerStrategy jsonSerializerStrategy, object value, StringBuilder builder)
1008 {
1009 bool success = true;
1010 string stringValue = value as string;
1011 if (stringValue != null)
1012 success = SerializeString(stringValue, builder);
1013 else
1014 {
1015 IDictionary<string, object> dict = value as IDictionary<string, object>;
1016 if (dict != null)
1017 {
1018 success = SerializeObject(jsonSerializerStrategy, dict.Keys, dict.Values, builder);
1019 }
1020 else
1021 {
1022 IDictionary<string, string> stringDictionary = value as IDictionary<string, string>;
1023 if (stringDictionary != null)
1024 {
1025 success = SerializeObject(jsonSerializerStrategy, stringDictionary.Keys, stringDictionary.Values, builder);
1026 }
1027 else
1028 {
1029 IEnumerable enumerableValue = value as IEnumerable;
1030 if (enumerableValue != null)
1031 success = SerializeArray(jsonSerializerStrategy, enumerableValue, builder);
1032 else if (IsNumeric(value))
1033 success = SerializeNumber(value, builder);
1034 else if (value is bool)
1035 builder.Append((bool)value ? "true" : "false");
1036 else if (value == null)
1037 builder.Append("null");
1038 else
1039 {
1040 object serializedObject;
1041 success = jsonSerializerStrategy.TrySerializeNonPrimitiveObject(value, out serializedObject);
1042 if (success)
1043 SerializeValue(jsonSerializerStrategy, serializedObject, builder);
1044 }
1045 }
1046 }
1047 }
1048 return success;
1049 }
1050
1051 static bool SerializeObject(IJsonSerializerStrategy jsonSerializerStrategy, IEnumerable keys, IEnumerable values, StringBuilder builder)
1052 {
1053 builder.Append("{");
1054 IEnumerator ke = keys.GetEnumerator();
1055 IEnumerator ve = values.GetEnumerator();
1056 bool first = true;
1057 while (ke.MoveNext() && ve.MoveNext())
1058 {
1059 object key = ke.Current;
1060 object value = ve.Current;
1061 if (!first)
1062 builder.Append(",");
1063 string stringKey = key as string;
1064 if (stringKey != null)
1065 SerializeString(stringKey, builder);
1066 else
1067 if (!SerializeValue(jsonSerializerStrategy, value, builder)) return false;
1068 builder.Append(":");
1069 if (!SerializeValue(jsonSerializerStrategy, value, builder))
1070 return false;
1071 first = false;
1072 }
1073 builder.Append("}");
1074 return true;
1075 }
1076
1077 static bool SerializeArray(IJsonSerializerStrategy jsonSerializerStrategy, IEnumerable anArray, StringBuilder builder)
1078 {
1079 builder.Append("[");
1080 bool first = true;
1081 foreach (object value in anArray)
1082 {
1083 if (!first)
1084 builder.Append(",");
1085 if (!SerializeValue(jsonSerializerStrategy, value, builder))
1086 return false;
1087 first = false;
1088 }
1089 builder.Append("]");
1090 return true;
1091 }
1092
1093 static bool SerializeString(string aString, StringBuilder builder)
1094 {
1095 // Happy path if there's nothing to be escaped. IndexOfAny is highly optimized (and unmanaged)
1096 if (aString.IndexOfAny(EscapeCharacters) == -1)
1097 {
1098 builder.Append('"');
1099 builder.Append(aString);
1100 builder.Append('"');
1101
1102 return true;
1103 }
1104
1105 builder.Append('"');
1106 int safeCharacterCount = 0;
1107 char[] charArray = aString.ToCharArray();
1108
1109 for (int i = 0; i < charArray.Length; i++)
1110 {
1111 char c = charArray[i];
1112
1113 // Non ascii characters are fine, buffer them up and send them to the builder
1114 // in larger chunks if possible. The escape table is a 1:1 translation table
1115 // with \0 [default(char)] denoting a safe character.
1116 if (c >= EscapeTable.Length || EscapeTable[c] == default(char))
1117 {
1118 safeCharacterCount++;
1119 }
1120 else
1121 {
1122 if (safeCharacterCount > 0)
1123 {
1124 builder.Append(charArray, i - safeCharacterCount, safeCharacterCount);
1125 safeCharacterCount = 0;
1126 }
1127
1128 builder.Append('\\');
1129 builder.Append(EscapeTable[c]);
1130 }
1131 }
1132
1133 if (safeCharacterCount > 0)
1134 {
1135 builder.Append(charArray, charArray.Length - safeCharacterCount, safeCharacterCount);
1136 }
1137
1138 builder.Append('"');
1139 return true;
1140 }
1141
1142 static bool SerializeNumber(object number, StringBuilder builder)
1143 {
1144 if (number is long)
1145 builder.Append(((long)number).ToString(CultureInfo.InvariantCulture));
1146 else if (number is ulong)
1147 builder.Append(((ulong)number).ToString(CultureInfo.InvariantCulture));
1148 else if (number is int)
1149 builder.Append(((int)number).ToString(CultureInfo.InvariantCulture));
1150 else if (number is uint)
1151 builder.Append(((uint)number).ToString(CultureInfo.InvariantCulture));
1152 else if (number is decimal)
1153 builder.Append(((decimal)number).ToString(CultureInfo.InvariantCulture));
1154 else if (number is float)
1155 builder.Append(((float)number).ToString(CultureInfo.InvariantCulture));
1156 else
1157 builder.Append(Convert.ToDouble(number, CultureInfo.InvariantCulture).ToString("r", CultureInfo.InvariantCulture));
1158 return true;
1159 }
1160
1161 /// <summary>
1162 /// Determines if a given object is numeric in any way
1163 /// (can be integer, double, null, etc).
1164 /// </summary>
1165 static bool IsNumeric(object value)
1166 {
1167 if (value is sbyte) return true;
1168 if (value is byte) return true;
1169 if (value is short) return true;
1170 if (value is ushort) return true;
1171 if (value is int) return true;
1172 if (value is uint) return true;
1173 if (value is long) return true;
1174 if (value is ulong) return true;
1175 if (value is float) return true;
1176 if (value is double) return true;
1177 if (value is decimal) return true;
1178 return false;
1179 }
1180
1181 private static IJsonSerializerStrategy _currentJsonSerializerStrategy;
1182 public static IJsonSerializerStrategy CurrentJsonSerializerStrategy
1183 {
1184 get
1185 {
1186 return _currentJsonSerializerStrategy ??
1187 (_currentJsonSerializerStrategy =
1188#if SIMPLE_JSON_DATACONTRACT
1189 DataContractJsonSerializerStrategy
1190#else
1191 PocoJsonSerializerStrategy
1192#endif
1193);
1194 }
1195 set
1196 {
1197 _currentJsonSerializerStrategy = value;
1198 }
1199 }
1200
1201 private static PocoJsonSerializerStrategy _pocoJsonSerializerStrategy;
1202 [EditorBrowsable(EditorBrowsableState.Advanced)]
1203 public static PocoJsonSerializerStrategy PocoJsonSerializerStrategy
1204 {
1205 get
1206 {
1207 return _pocoJsonSerializerStrategy ?? (_pocoJsonSerializerStrategy = new PocoJsonSerializerStrategy());
1208 }
1209 }
1210
1211#if SIMPLE_JSON_DATACONTRACT
1212
1213 private static DataContractJsonSerializerStrategy _dataContractJsonSerializerStrategy;
1214 [System.ComponentModel.EditorBrowsable(EditorBrowsableState.Advanced)]
1215 public static DataContractJsonSerializerStrategy DataContractJsonSerializerStrategy
1216 {
1217 get
1218 {
1219 return _dataContractJsonSerializerStrategy ?? (_dataContractJsonSerializerStrategy = new DataContractJsonSerializerStrategy());
1220 }
1221 }
1222
1223#endif
1224 }
1225
1226 [GeneratedCode("simple-json", "1.0.0")]
1227#if SIMPLE_JSON_INTERNAL
1228 internal
1229#else
1230 public
1231#endif
1232 interface IJsonSerializerStrategy
1233 {
1234 [SuppressMessage("Microsoft.Design", "CA1007:UseGenericsWhereAppropriate", Justification = "Need to support .NET 2")]
1235 bool TrySerializeNonPrimitiveObject(object input, out object output);
1236 object DeserializeObject(object value, Type type);
1237 }
1238
1239 [GeneratedCode("simple-json", "1.0.0")]
1240#if SIMPLE_JSON_INTERNAL
1241 internal
1242#else
1243 public
1244#endif
1245 class PocoJsonSerializerStrategy : IJsonSerializerStrategy
1246 {
1247 internal IDictionary<Type, ReflectionUtils.ConstructorDelegate> ConstructorCache;
1248 internal IDictionary<Type, IDictionary<string, ReflectionUtils.GetDelegate>> GetCache;
1249 internal IDictionary<Type, IDictionary<string, KeyValuePair<Type, ReflectionUtils.SetDelegate>>> SetCache;
1250
1251 internal static readonly Type[] EmptyTypes = new Type[0];
1252 internal static readonly Type[] ArrayConstructorParameterTypes = new Type[] { typeof(int) };
1253
1254 private static readonly string[] Iso8601Format = new string[]
1255 {
1256 @"yyyy-MM-dd\THH:mm:ss.FFFFFFF\Z",
1257 @"yyyy-MM-dd\THH:mm:ss\Z",
1258 @"yyyy-MM-dd\THH:mm:ssK"
1259 };
1260
1261 public PocoJsonSerializerStrategy()
1262 {
1263 ConstructorCache = new ReflectionUtils.ThreadSafeDictionary<Type, ReflectionUtils.ConstructorDelegate>(ContructorDelegateFactory);
1264 GetCache = new ReflectionUtils.ThreadSafeDictionary<Type, IDictionary<string, ReflectionUtils.GetDelegate>>(GetterValueFactory);
1265 SetCache = new ReflectionUtils.ThreadSafeDictionary<Type, IDictionary<string, KeyValuePair<Type, ReflectionUtils.SetDelegate>>>(SetterValueFactory);
1266 }
1267
1268 protected virtual string MapClrMemberNameToJsonFieldName(string clrPropertyName)
1269 {
1270 return clrPropertyName;
1271 }
1272
1273 internal virtual ReflectionUtils.ConstructorDelegate ContructorDelegateFactory(Type key)
1274 {
1275 return ReflectionUtils.GetContructor(key, key.IsArray ? ArrayConstructorParameterTypes : EmptyTypes);
1276 }
1277
1278 internal virtual IDictionary<string, ReflectionUtils.GetDelegate> GetterValueFactory(Type type)
1279 {
1280 IDictionary<string, ReflectionUtils.GetDelegate> result = new Dictionary<string, ReflectionUtils.GetDelegate>();
1281 foreach (PropertyInfo propertyInfo in ReflectionUtils.GetProperties(type))
1282 {
1283 if (propertyInfo.CanRead)
1284 {
1285 MethodInfo getMethod = ReflectionUtils.GetGetterMethodInfo(propertyInfo);
1286 if (getMethod.IsStatic || !getMethod.IsPublic)
1287 continue;
1288 result[MapClrMemberNameToJsonFieldName(propertyInfo.Name)] = ReflectionUtils.GetGetMethod(propertyInfo);
1289 }
1290 }
1291 foreach (FieldInfo fieldInfo in ReflectionUtils.GetFields(type))
1292 {
1293 if (fieldInfo.IsStatic || !fieldInfo.IsPublic)
1294 continue;
1295 result[MapClrMemberNameToJsonFieldName(fieldInfo.Name)] = ReflectionUtils.GetGetMethod(fieldInfo);
1296 }
1297 return result;
1298 }
1299
1300 internal virtual IDictionary<string, KeyValuePair<Type, ReflectionUtils.SetDelegate>> SetterValueFactory(Type type)
1301 {
1302 IDictionary<string, KeyValuePair<Type, ReflectionUtils.SetDelegate>> result = new Dictionary<string, KeyValuePair<Type, ReflectionUtils.SetDelegate>>();
1303 foreach (PropertyInfo propertyInfo in ReflectionUtils.GetProperties(type))
1304 {
1305 if (propertyInfo.CanWrite)
1306 {
1307 MethodInfo setMethod = ReflectionUtils.GetSetterMethodInfo(propertyInfo);
1308 if (setMethod.IsStatic || !setMethod.IsPublic)
1309 continue;
1310 result[MapClrMemberNameToJsonFieldName(propertyInfo.Name)] = new KeyValuePair<Type, ReflectionUtils.SetDelegate>(propertyInfo.PropertyType, ReflectionUtils.GetSetMethod(propertyInfo));
1311 }
1312 }
1313 foreach (FieldInfo fieldInfo in ReflectionUtils.GetFields(type))
1314 {
1315 if (fieldInfo.IsInitOnly || fieldInfo.IsStatic || !fieldInfo.IsPublic)
1316 continue;
1317 result[MapClrMemberNameToJsonFieldName(fieldInfo.Name)] = new KeyValuePair<Type, ReflectionUtils.SetDelegate>(fieldInfo.FieldType, ReflectionUtils.GetSetMethod(fieldInfo));
1318 }
1319 return result;
1320 }
1321
1322 public virtual bool TrySerializeNonPrimitiveObject(object input, out object output)
1323 {
1324 return TrySerializeKnownTypes(input, out output) || TrySerializeUnknownTypes(input, out output);
1325 }
1326
1327 [SuppressMessage("Microsoft.Maintainability", "CA1502:AvoidExcessiveComplexity")]
1328 public virtual object DeserializeObject(object value, Type type)
1329 {
1330 if (type == null) throw new ArgumentNullException("type");
1331 string str = value as string;
1332
1333 if (type == typeof(Guid) && string.IsNullOrEmpty(str))
1334 return default(Guid);
1335
1336 if (value == null)
1337 return null;
1338
1339 object obj = null;
1340
1341 if (str != null)
1342 {
1343 if (str.Length != 0) // We know it can't be null now.
1344 {
1345 if (type == typeof(DateTime) || (ReflectionUtils.IsNullableType(type) && Nullable.GetUnderlyingType(type) == typeof(DateTime)))
1346 return DateTime.ParseExact(str, Iso8601Format, CultureInfo.InvariantCulture, DateTimeStyles.AssumeUniversal | DateTimeStyles.AdjustToUniversal);
1347 if (type == typeof(DateTimeOffset) || (ReflectionUtils.IsNullableType(type) && Nullable.GetUnderlyingType(type) == typeof(DateTimeOffset)))
1348 return DateTimeOffset.ParseExact(str, Iso8601Format, CultureInfo.InvariantCulture, DateTimeStyles.AssumeUniversal | DateTimeStyles.AdjustToUniversal);
1349 if (type == typeof(Guid) || (ReflectionUtils.IsNullableType(type) && Nullable.GetUnderlyingType(type) == typeof(Guid)))
1350 return new Guid(str);
1351 if (type == typeof(Uri))
1352 {
1353 bool isValid = Uri.IsWellFormedUriString(str, UriKind.RelativeOrAbsolute);
1354
1355 Uri result;
1356 if (isValid && Uri.TryCreate(str, UriKind.RelativeOrAbsolute, out result))
1357 return result;
1358
1359 return null;
1360 }
1361
1362 if (type == typeof(string))
1363 return str;
1364
1365 return Convert.ChangeType(str, type, CultureInfo.InvariantCulture);
1366 }
1367 else
1368 {
1369 if (type == typeof(Guid))
1370 obj = default(Guid);
1371 else if (ReflectionUtils.IsNullableType(type) && Nullable.GetUnderlyingType(type) == typeof(Guid))
1372 obj = null;
1373 else
1374 obj = str;
1375 }
1376 // Empty string case
1377 if (!ReflectionUtils.IsNullableType(type) && Nullable.GetUnderlyingType(type) == typeof(Guid))
1378 return str;
1379 }
1380 else if (value is bool)
1381 return value;
1382
1383 bool valueIsLong = value is long;
1384 bool valueIsDouble = value is double;
1385 if ((valueIsLong && type == typeof(long)) || (valueIsDouble && type == typeof(double)))
1386 return value;
1387 if ((valueIsDouble && type != typeof(double)) || (valueIsLong && type != typeof(long)))
1388 {
1389 obj = type == typeof(int) || type == typeof(long) || type == typeof(double) || type == typeof(float) || type == typeof(bool) || type == typeof(decimal) || type == typeof(byte) || type == typeof(short)
1390 ? Convert.ChangeType(value, type, CultureInfo.InvariantCulture)
1391 : value;
1392 }
1393 else
1394 {
1395 IDictionary<string, object> objects = value as IDictionary<string, object>;
1396 if (objects != null)
1397 {
1398 IDictionary<string, object> jsonObject = objects;
1399
1400 if (ReflectionUtils.IsTypeDictionary(type))
1401 {
1402 // if dictionary then
1403 Type[] types = ReflectionUtils.GetGenericTypeArguments(type);
1404 Type keyType = types[0];
1405 Type valueType = types[1];
1406
1407 Type genericType = typeof(Dictionary<,>).MakeGenericType(keyType, valueType);
1408
1409 IDictionary dict = (IDictionary)ConstructorCache[genericType]();
1410
1411 foreach (KeyValuePair<string, object> kvp in jsonObject)
1412 dict.Add(kvp.Key, DeserializeObject(kvp.Value, valueType));
1413
1414 obj = dict;
1415 }
1416 else
1417 {
1418 if (type == typeof(object))
1419 obj = value;
1420 else
1421 {
1422 obj = ConstructorCache[type]();
1423 foreach (KeyValuePair<string, KeyValuePair<Type, ReflectionUtils.SetDelegate>> setter in SetCache[type])
1424 {
1425 object jsonValue;
1426 if (jsonObject.TryGetValue(setter.Key, out jsonValue))
1427 {
1428 jsonValue = DeserializeObject(jsonValue, setter.Value.Key);
1429 setter.Value.Value(obj, jsonValue);
1430 }
1431 }
1432 }
1433 }
1434 }
1435 else
1436 {
1437 IList<object> valueAsList = value as IList<object>;
1438 if (valueAsList != null)
1439 {
1440 IList<object> jsonObject = valueAsList;
1441 IList list = null;
1442
1443 if (type.IsArray)
1444 {
1445 list = (IList)ConstructorCache[type](jsonObject.Count);
1446 int i = 0;
1447 foreach (object o in jsonObject)
1448 list[i++] = DeserializeObject(o, type.GetElementType());
1449 }
1450 else if (ReflectionUtils.IsTypeGenericeCollectionInterface(type) || ReflectionUtils.IsAssignableFrom(typeof(IList), type))
1451 {
1452 Type innerType = ReflectionUtils.GetGenericListElementType(type);
1453 list = (IList)(ConstructorCache[type] ?? ConstructorCache[typeof(List<>).MakeGenericType(innerType)])(jsonObject.Count);
1454 foreach (object o in jsonObject)
1455 list.Add(DeserializeObject(o, innerType));
1456 }
1457 obj = list;
1458 }
1459 }
1460 return obj;
1461 }
1462 if (ReflectionUtils.IsNullableType(type))
1463 return ReflectionUtils.ToNullableType(obj, type);
1464 return obj;
1465 }
1466
1467 protected virtual object SerializeEnum(Enum p)
1468 {
1469 return Convert.ToDouble(p, CultureInfo.InvariantCulture);
1470 }
1471
1472 [SuppressMessage("Microsoft.Design", "CA1007:UseGenericsWhereAppropriate", Justification = "Need to support .NET 2")]
1473 protected virtual bool TrySerializeKnownTypes(object input, out object output)
1474 {
1475 bool returnValue = true;
1476 if (input is DateTime)
1477 output = ((DateTime)input).ToUniversalTime().ToString(Iso8601Format[0], CultureInfo.InvariantCulture);
1478 else if (input is DateTimeOffset)
1479 output = ((DateTimeOffset)input).ToUniversalTime().ToString(Iso8601Format[0], CultureInfo.InvariantCulture);
1480 else if (input is Guid)
1481 output = ((Guid)input).ToString("D");
1482 else if (input is Uri)
1483 output = input.ToString();
1484 else
1485 {
1486 Enum inputEnum = input as Enum;
1487 if (inputEnum != null)
1488 output = SerializeEnum(inputEnum);
1489 else
1490 {
1491 returnValue = false;
1492 output = null;
1493 }
1494 }
1495 return returnValue;
1496 }
1497 [SuppressMessage("Microsoft.Design", "CA1007:UseGenericsWhereAppropriate", Justification = "Need to support .NET 2")]
1498 protected virtual bool TrySerializeUnknownTypes(object input, out object output)
1499 {
1500 if (input == null) throw new ArgumentNullException("input");
1501 output = null;
1502 Type type = input.GetType();
1503 if (type.FullName == null)
1504 return false;
1505 IDictionary<string, object> obj = new JsonObject();
1506 IDictionary<string, ReflectionUtils.GetDelegate> getters = GetCache[type];
1507 foreach (KeyValuePair<string, ReflectionUtils.GetDelegate> getter in getters)
1508 {
1509 if (getter.Value != null)
1510 obj.Add(MapClrMemberNameToJsonFieldName(getter.Key), getter.Value(input));
1511 }
1512 output = obj;
1513 return true;
1514 }
1515 }
1516
1517#if SIMPLE_JSON_DATACONTRACT
1518 [GeneratedCode("simple-json", "1.0.0")]
1519#if SIMPLE_JSON_INTERNAL
1520 internal
1521#else
1522 public
1523#endif
1524 class DataContractJsonSerializerStrategy : PocoJsonSerializerStrategy
1525 {
1526 public DataContractJsonSerializerStrategy()
1527 {
1528 GetCache = new ReflectionUtils.ThreadSafeDictionary<Type, IDictionary<string, ReflectionUtils.GetDelegate>>(GetterValueFactory);
1529 SetCache = new ReflectionUtils.ThreadSafeDictionary<Type, IDictionary<string, KeyValuePair<Type, ReflectionUtils.SetDelegate>>>(SetterValueFactory);
1530 }
1531
1532 internal override IDictionary<string, ReflectionUtils.GetDelegate> GetterValueFactory(Type type)
1533 {
1534 bool hasDataContract = ReflectionUtils.GetAttribute(type, typeof(DataContractAttribute)) != null;
1535 if (!hasDataContract)
1536 return base.GetterValueFactory(type);
1537 string jsonKey;
1538 IDictionary<string, ReflectionUtils.GetDelegate> result = new Dictionary<string, ReflectionUtils.GetDelegate>();
1539 foreach (PropertyInfo propertyInfo in ReflectionUtils.GetProperties(type))
1540 {
1541 if (propertyInfo.CanRead)
1542 {
1543 MethodInfo getMethod = ReflectionUtils.GetGetterMethodInfo(propertyInfo);
1544 if (!getMethod.IsStatic && CanAdd(propertyInfo, out jsonKey))
1545 result[jsonKey] = ReflectionUtils.GetGetMethod(propertyInfo);
1546 }
1547 }
1548 foreach (FieldInfo fieldInfo in ReflectionUtils.GetFields(type))
1549 {
1550 if (!fieldInfo.IsStatic && CanAdd(fieldInfo, out jsonKey))
1551 result[jsonKey] = ReflectionUtils.GetGetMethod(fieldInfo);
1552 }
1553 return result;
1554 }
1555
1556 internal override IDictionary<string, KeyValuePair<Type, ReflectionUtils.SetDelegate>> SetterValueFactory(Type type)
1557 {
1558 bool hasDataContract = ReflectionUtils.GetAttribute(type, typeof(DataContractAttribute)) != null;
1559 if (!hasDataContract)
1560 return base.SetterValueFactory(type);
1561 string jsonKey;
1562 IDictionary<string, KeyValuePair<Type, ReflectionUtils.SetDelegate>> result = new Dictionary<string, KeyValuePair<Type, ReflectionUtils.SetDelegate>>();
1563 foreach (PropertyInfo propertyInfo in ReflectionUtils.GetProperties(type))
1564 {
1565 if (propertyInfo.CanWrite)
1566 {
1567 MethodInfo setMethod = ReflectionUtils.GetSetterMethodInfo(propertyInfo);
1568 if (!setMethod.IsStatic && CanAdd(propertyInfo, out jsonKey))
1569 result[jsonKey] = new KeyValuePair<Type, ReflectionUtils.SetDelegate>(propertyInfo.PropertyType, ReflectionUtils.GetSetMethod(propertyInfo));
1570 }
1571 }
1572 foreach (FieldInfo fieldInfo in ReflectionUtils.GetFields(type))
1573 {
1574 if (!fieldInfo.IsInitOnly && !fieldInfo.IsStatic && CanAdd(fieldInfo, out jsonKey))
1575 result[jsonKey] = new KeyValuePair<Type, ReflectionUtils.SetDelegate>(fieldInfo.FieldType, ReflectionUtils.GetSetMethod(fieldInfo));
1576 }
1577 // todo implement sorting for DATACONTRACT.
1578 return result;
1579 }
1580
1581 private static bool CanAdd(MemberInfo info, out string jsonKey)
1582 {
1583 jsonKey = null;
1584 if (ReflectionUtils.GetAttribute(info, typeof(IgnoreDataMemberAttribute)) != null)
1585 return false;
1586 DataMemberAttribute dataMemberAttribute = (DataMemberAttribute)ReflectionUtils.GetAttribute(info, typeof(DataMemberAttribute));
1587 if (dataMemberAttribute == null)
1588 return false;
1589 jsonKey = string.IsNullOrEmpty(dataMemberAttribute.Name) ? info.Name : dataMemberAttribute.Name;
1590 return true;
1591 }
1592 }
1593
1594#endif
1595
1596 namespace Reflection
1597 {
1598 // This class is meant to be copied into other libraries. So we want to exclude it from Code Analysis rules
1599 // that might be in place in the target project.
1600 [GeneratedCode("reflection-utils", "1.0.0")]
1601#if SIMPLE_JSON_REFLECTION_UTILS_PUBLIC
1602 public
1603#else
1604 internal
1605#endif
1606 class ReflectionUtils
1607 {
1608 private static readonly object[] EmptyObjects = new object[] { };
1609
1610 public delegate object GetDelegate(object source);
1611 public delegate void SetDelegate(object source, object value);
1612 public delegate object ConstructorDelegate(params object[] args);
1613
1614 public delegate TValue ThreadSafeDictionaryValueFactory<TKey, TValue>(TKey key);
1615
1616#if SIMPLE_JSON_TYPEINFO
1617 public static TypeInfo GetTypeInfo(Type type)
1618 {
1619 return type.GetTypeInfo();
1620 }
1621#else
1622 public static Type GetTypeInfo(Type type)
1623 {
1624 return type;
1625 }
1626#endif
1627
1628 public static Attribute GetAttribute(MemberInfo info, Type type)
1629 {
1630#if SIMPLE_JSON_TYPEINFO
1631 if (info == null || type == null || !info.IsDefined(type))
1632 return null;
1633 return info.GetCustomAttribute(type);
1634#else
1635 if (info == null || type == null || !Attribute.IsDefined(info, type))
1636 return null;
1637 return Attribute.GetCustomAttribute(info, type);
1638#endif
1639 }
1640
1641 public static Type GetGenericListElementType(Type type)
1642 {
1643 IEnumerable<Type> interfaces;
1644#if SIMPLE_JSON_TYPEINFO
1645 interfaces = type.GetTypeInfo().ImplementedInterfaces;
1646#else
1647 interfaces = type.GetInterfaces();
1648#endif
1649 foreach (Type implementedInterface in interfaces)
1650 {
1651 if (IsTypeGeneric(implementedInterface) &&
1652 implementedInterface.GetGenericTypeDefinition() == typeof(IList<>))
1653 {
1654 return GetGenericTypeArguments(implementedInterface)[0];
1655 }
1656 }
1657 return GetGenericTypeArguments(type)[0];
1658 }
1659
1660 public static Attribute GetAttribute(Type objectType, Type attributeType)
1661 {
1662
1663#if SIMPLE_JSON_TYPEINFO
1664 if (objectType == null || attributeType == null || !objectType.GetTypeInfo().IsDefined(attributeType))
1665 return null;
1666 return objectType.GetTypeInfo().GetCustomAttribute(attributeType);
1667#else
1668 if (objectType == null || attributeType == null || !Attribute.IsDefined(objectType, attributeType))
1669 return null;
1670 return Attribute.GetCustomAttribute(objectType, attributeType);
1671#endif
1672 }
1673
1674 public static Type[] GetGenericTypeArguments(Type type)
1675 {
1676#if SIMPLE_JSON_TYPEINFO
1677 return type.GetTypeInfo().GenericTypeArguments;
1678#else
1679 return type.GetGenericArguments();
1680#endif
1681 }
1682
1683 public static bool IsTypeGeneric(Type type)
1684 {
1685 return GetTypeInfo(type).IsGenericType;
1686 }
1687
1688 public static bool IsTypeGenericeCollectionInterface(Type type)
1689 {
1690 if (!IsTypeGeneric(type))
1691 return false;
1692
1693 Type genericDefinition = type.GetGenericTypeDefinition();
1694
1695 return (genericDefinition == typeof(IList<>)
1696 || genericDefinition == typeof(ICollection<>)
1697 || genericDefinition == typeof(IEnumerable<>)
1698#if SIMPLE_JSON_READONLY_COLLECTIONS
1699 || genericDefinition == typeof(IReadOnlyCollection<>)
1700 || genericDefinition == typeof(IReadOnlyList<>)
1701#endif
1702 );
1703 }
1704
1705 public static bool IsAssignableFrom(Type type1, Type type2)
1706 {
1707 return GetTypeInfo(type1).IsAssignableFrom(GetTypeInfo(type2));
1708 }
1709
1710 public static bool IsTypeDictionary(Type type)
1711 {
1712#if SIMPLE_JSON_TYPEINFO
1713 if (typeof(IDictionary<,>).GetTypeInfo().IsAssignableFrom(type.GetTypeInfo()))
1714 return true;
1715#else
1716 if (typeof(System.Collections.IDictionary).IsAssignableFrom(type))
1717 return true;
1718#endif
1719 if (!GetTypeInfo(type).IsGenericType)
1720 return false;
1721
1722 Type genericDefinition = type.GetGenericTypeDefinition();
1723 return genericDefinition == typeof(IDictionary<,>);
1724 }
1725
1726 public static bool IsNullableType(Type type)
1727 {
1728 return GetTypeInfo(type).IsGenericType && type.GetGenericTypeDefinition() == typeof(Nullable<>);
1729 }
1730
1731 public static object ToNullableType(object obj, Type nullableType)
1732 {
1733 return obj == null ? null : Convert.ChangeType(obj, Nullable.GetUnderlyingType(nullableType), CultureInfo.InvariantCulture);
1734 }
1735
1736 public static bool IsValueType(Type type)
1737 {
1738 return GetTypeInfo(type).IsValueType;
1739 }
1740
1741 public static IEnumerable<ConstructorInfo> GetConstructors(Type type)
1742 {
1743#if SIMPLE_JSON_TYPEINFO
1744 return type.GetTypeInfo().DeclaredConstructors;
1745#else
1746 return type.GetConstructors();
1747#endif
1748 }
1749
1750 public static ConstructorInfo GetConstructorInfo(Type type, params Type[] argsType)
1751 {
1752 IEnumerable<ConstructorInfo> constructorInfos = GetConstructors(type);
1753 int i;
1754 bool matches;
1755 foreach (ConstructorInfo constructorInfo in constructorInfos)
1756 {
1757 ParameterInfo[] parameters = constructorInfo.GetParameters();
1758 if (argsType.Length != parameters.Length)
1759 continue;
1760
1761 i = 0;
1762 matches = true;
1763 foreach (ParameterInfo parameterInfo in constructorInfo.GetParameters())
1764 {
1765 if (parameterInfo.ParameterType != argsType[i])
1766 {
1767 matches = false;
1768 break;
1769 }
1770 }
1771
1772 if (matches)
1773 return constructorInfo;
1774 }
1775
1776 return null;
1777 }
1778
1779 public static IEnumerable<PropertyInfo> GetProperties(Type type)
1780 {
1781#if SIMPLE_JSON_TYPEINFO
1782 return type.GetRuntimeProperties();
1783#else
1784 return type.GetProperties(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static);
1785#endif
1786 }
1787
1788 public static IEnumerable<FieldInfo> GetFields(Type type)
1789 {
1790#if SIMPLE_JSON_TYPEINFO
1791 return type.GetRuntimeFields();
1792#else
1793 return type.GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static);
1794#endif
1795 }
1796
1797 public static MethodInfo GetGetterMethodInfo(PropertyInfo propertyInfo)
1798 {
1799#if SIMPLE_JSON_TYPEINFO
1800 return propertyInfo.GetMethod;
1801#else
1802 return propertyInfo.GetGetMethod(true);
1803#endif
1804 }
1805
1806 public static MethodInfo GetSetterMethodInfo(PropertyInfo propertyInfo)
1807 {
1808#if SIMPLE_JSON_TYPEINFO
1809 return propertyInfo.SetMethod;
1810#else
1811 return propertyInfo.GetSetMethod(true);
1812#endif
1813 }
1814
1815 public static ConstructorDelegate GetContructor(ConstructorInfo constructorInfo)
1816 {
1817#if SIMPLE_JSON_NO_LINQ_EXPRESSION
1818 return GetConstructorByReflection(constructorInfo);
1819#else
1820 return GetConstructorByExpression(constructorInfo);
1821#endif
1822 }
1823
1824 public static ConstructorDelegate GetContructor(Type type, params Type[] argsType)
1825 {
1826#if SIMPLE_JSON_NO_LINQ_EXPRESSION
1827 return GetConstructorByReflection(type, argsType);
1828#else
1829 return GetConstructorByExpression(type, argsType);
1830#endif
1831 }
1832
1833 public static ConstructorDelegate GetConstructorByReflection(ConstructorInfo constructorInfo)
1834 {
1835 return delegate (object[] args) { return constructorInfo.Invoke(args); };
1836 }
1837
1838 public static ConstructorDelegate GetConstructorByReflection(Type type, params Type[] argsType)
1839 {
1840 ConstructorInfo constructorInfo = GetConstructorInfo(type, argsType);
1841 return constructorInfo == null ? null : GetConstructorByReflection(constructorInfo);
1842 }
1843
1844#if !SIMPLE_JSON_NO_LINQ_EXPRESSION
1845
1846 public static ConstructorDelegate GetConstructorByExpression(ConstructorInfo constructorInfo)
1847 {
1848 ParameterInfo[] paramsInfo = constructorInfo.GetParameters();
1849 ParameterExpression param = Expression.Parameter(typeof(object[]), "args");
1850 Expression[] argsExp = new Expression[paramsInfo.Length];
1851 for (int i = 0; i < paramsInfo.Length; i++)
1852 {
1853 Expression index = Expression.Constant(i);
1854 Type paramType = paramsInfo[i].ParameterType;
1855 Expression paramAccessorExp = Expression.ArrayIndex(param, index);
1856 Expression paramCastExp = Expression.Convert(paramAccessorExp, paramType);
1857 argsExp[i] = paramCastExp;
1858 }
1859 NewExpression newExp = Expression.New(constructorInfo, argsExp);
1860 Expression<Func<object[], object>> lambda = Expression.Lambda<Func<object[], object>>(newExp, param);
1861 Func<object[], object> compiledLambda = lambda.Compile();
1862 return delegate (object[] args) { return compiledLambda(args); };
1863 }
1864
1865 public static ConstructorDelegate GetConstructorByExpression(Type type, params Type[] argsType)
1866 {
1867 ConstructorInfo constructorInfo = GetConstructorInfo(type, argsType);
1868 return constructorInfo == null ? null : GetConstructorByExpression(constructorInfo);
1869 }
1870
1871#endif
1872
1873 public static GetDelegate GetGetMethod(PropertyInfo propertyInfo)
1874 {
1875#if SIMPLE_JSON_NO_LINQ_EXPRESSION
1876 return GetGetMethodByReflection(propertyInfo);
1877#else
1878 return GetGetMethodByExpression(propertyInfo);
1879#endif
1880 }
1881
1882 public static GetDelegate GetGetMethod(FieldInfo fieldInfo)
1883 {
1884#if SIMPLE_JSON_NO_LINQ_EXPRESSION
1885 return GetGetMethodByReflection(fieldInfo);
1886#else
1887 return GetGetMethodByExpression(fieldInfo);
1888#endif
1889 }
1890
1891 public static GetDelegate GetGetMethodByReflection(PropertyInfo propertyInfo)
1892 {
1893 MethodInfo methodInfo = GetGetterMethodInfo(propertyInfo);
1894 return delegate (object source) { return methodInfo.Invoke(source, EmptyObjects); };
1895 }
1896
1897 public static GetDelegate GetGetMethodByReflection(FieldInfo fieldInfo)
1898 {
1899 return delegate (object source) { return fieldInfo.GetValue(source); };
1900 }
1901
1902#if !SIMPLE_JSON_NO_LINQ_EXPRESSION
1903
1904 public static GetDelegate GetGetMethodByExpression(PropertyInfo propertyInfo)
1905 {
1906 MethodInfo getMethodInfo = GetGetterMethodInfo(propertyInfo);
1907 ParameterExpression instance = Expression.Parameter(typeof(object), "instance");
1908 UnaryExpression instanceCast = (!IsValueType(propertyInfo.DeclaringType)) ? Expression.TypeAs(instance, propertyInfo.DeclaringType) : Expression.Convert(instance, propertyInfo.DeclaringType);
1909 Func<object, object> compiled = Expression.Lambda<Func<object, object>>(Expression.TypeAs(Expression.Call(instanceCast, getMethodInfo), typeof(object)), instance).Compile();
1910 return delegate (object source) { return compiled(source); };
1911 }
1912
1913 public static GetDelegate GetGetMethodByExpression(FieldInfo fieldInfo)
1914 {
1915 ParameterExpression instance = Expression.Parameter(typeof(object), "instance");
1916 MemberExpression member = Expression.Field(Expression.Convert(instance, fieldInfo.DeclaringType), fieldInfo);
1917 GetDelegate compiled = Expression.Lambda<GetDelegate>(Expression.Convert(member, typeof(object)), instance).Compile();
1918 return delegate (object source) { return compiled(source); };
1919 }
1920
1921#endif
1922
1923 public static SetDelegate GetSetMethod(PropertyInfo propertyInfo)
1924 {
1925#if SIMPLE_JSON_NO_LINQ_EXPRESSION
1926 return GetSetMethodByReflection(propertyInfo);
1927#else
1928 return GetSetMethodByExpression(propertyInfo);
1929#endif
1930 }
1931
1932 public static SetDelegate GetSetMethod(FieldInfo fieldInfo)
1933 {
1934#if SIMPLE_JSON_NO_LINQ_EXPRESSION
1935 return GetSetMethodByReflection(fieldInfo);
1936#else
1937 return GetSetMethodByExpression(fieldInfo);
1938#endif
1939 }
1940
1941 public static SetDelegate GetSetMethodByReflection(PropertyInfo propertyInfo)
1942 {
1943 MethodInfo methodInfo = GetSetterMethodInfo(propertyInfo);
1944 return delegate (object source, object value) { methodInfo.Invoke(source, new object[] { value }); };
1945 }
1946
1947 public static SetDelegate GetSetMethodByReflection(FieldInfo fieldInfo)
1948 {
1949 return delegate (object source, object value) { fieldInfo.SetValue(source, value); };
1950 }
1951
1952#if !SIMPLE_JSON_NO_LINQ_EXPRESSION
1953
1954 public static SetDelegate GetSetMethodByExpression(PropertyInfo propertyInfo)
1955 {
1956 MethodInfo setMethodInfo = GetSetterMethodInfo(propertyInfo);
1957 ParameterExpression instance = Expression.Parameter(typeof(object), "instance");
1958 ParameterExpression value = Expression.Parameter(typeof(object), "value");
1959 UnaryExpression instanceCast = (!IsValueType(propertyInfo.DeclaringType)) ? Expression.TypeAs(instance, propertyInfo.DeclaringType) : Expression.Convert(instance, propertyInfo.DeclaringType);
1960 UnaryExpression valueCast = (!IsValueType(propertyInfo.PropertyType)) ? Expression.TypeAs(value, propertyInfo.PropertyType) : Expression.Convert(value, propertyInfo.PropertyType);
1961 Action<object, object> compiled = Expression.Lambda<Action<object, object>>(Expression.Call(instanceCast, setMethodInfo, valueCast), new ParameterExpression[] { instance, value }).Compile();
1962 return delegate (object source, object val) { compiled(source, val); };
1963 }
1964
1965 public static SetDelegate GetSetMethodByExpression(FieldInfo fieldInfo)
1966 {
1967 ParameterExpression instance = Expression.Parameter(typeof(object), "instance");
1968 ParameterExpression value = Expression.Parameter(typeof(object), "value");
1969 Action<object, object> compiled = Expression.Lambda<Action<object, object>>(
1970 Assign(Expression.Field(Expression.Convert(instance, fieldInfo.DeclaringType), fieldInfo), Expression.Convert(value, fieldInfo.FieldType)), instance, value).Compile();
1971 return delegate (object source, object val) { compiled(source, val); };
1972 }
1973
1974 public static BinaryExpression Assign(Expression left, Expression right)
1975 {
1976#if SIMPLE_JSON_TYPEINFO
1977 return Expression.Assign(left, right);
1978#else
1979 MethodInfo assign = typeof(Assigner<>).MakeGenericType(left.Type).GetMethod("Assign");
1980 BinaryExpression assignExpr = Expression.Add(left, right, assign);
1981 return assignExpr;
1982#endif
1983 }
1984
1985 private static class Assigner<T>
1986 {
1987 public static T Assign(ref T left, T right)
1988 {
1989 return (left = right);
1990 }
1991 }
1992
1993#endif
1994
1995 public sealed class ThreadSafeDictionary<TKey, TValue> : IDictionary<TKey, TValue>
1996 {
1997 private readonly object _lock = new object();
1998 private readonly ThreadSafeDictionaryValueFactory<TKey, TValue> _valueFactory;
1999 private Dictionary<TKey, TValue> _dictionary;
2000
2001 public ThreadSafeDictionary(ThreadSafeDictionaryValueFactory<TKey, TValue> valueFactory)
2002 {
2003 _valueFactory = valueFactory;
2004 }
2005
2006 private TValue Get(TKey key)
2007 {
2008 if (_dictionary == null)
2009 return AddValue(key);
2010 TValue value;
2011 if (!_dictionary.TryGetValue(key, out value))
2012 return AddValue(key);
2013 return value;
2014 }
2015
2016 private TValue AddValue(TKey key)
2017 {
2018 TValue value = _valueFactory(key);
2019 lock (_lock)
2020 {
2021 if (_dictionary == null)
2022 {
2023 _dictionary = new Dictionary<TKey, TValue>();
2024 _dictionary[key] = value;
2025 }
2026 else
2027 {
2028 TValue val;
2029 if (_dictionary.TryGetValue(key, out val))
2030 return val;
2031 Dictionary<TKey, TValue> dict = new Dictionary<TKey, TValue>(_dictionary);
2032 dict[key] = value;
2033 _dictionary = dict;
2034 }
2035 }
2036 return value;
2037 }
2038
2039 public void Add(TKey key, TValue value)
2040 {
2041 throw new NotImplementedException();
2042 }
2043
2044 public bool ContainsKey(TKey key)
2045 {
2046 return _dictionary.ContainsKey(key);
2047 }
2048
2049 public ICollection<TKey> Keys
2050 {
2051 get { return _dictionary.Keys; }
2052 }
2053
2054 public bool Remove(TKey key)
2055 {
2056 throw new NotImplementedException();
2057 }
2058
2059 public bool TryGetValue(TKey key, out TValue value)
2060 {
2061 value = this[key];
2062 return true;
2063 }
2064
2065 public ICollection<TValue> Values
2066 {
2067 get { return _dictionary.Values; }
2068 }
2069
2070 public TValue this[TKey key]
2071 {
2072 get { return Get(key); }
2073 set { throw new NotImplementedException(); }
2074 }
2075
2076 public void Add(KeyValuePair<TKey, TValue> item)
2077 {
2078 throw new NotImplementedException();
2079 }
2080
2081 public void Clear()
2082 {
2083 throw new NotImplementedException();
2084 }
2085
2086 public bool Contains(KeyValuePair<TKey, TValue> item)
2087 {
2088 throw new NotImplementedException();
2089 }
2090
2091 public void CopyTo(KeyValuePair<TKey, TValue>[] array, int arrayIndex)
2092 {
2093 throw new NotImplementedException();
2094 }
2095
2096 public int Count
2097 {
2098 get { return _dictionary.Count; }
2099 }
2100
2101 public bool IsReadOnly
2102 {
2103 get { throw new NotImplementedException(); }
2104 }
2105
2106 public bool Remove(KeyValuePair<TKey, TValue> item)
2107 {
2108 throw new NotImplementedException();
2109 }
2110
2111 public IEnumerator<KeyValuePair<TKey, TValue>> GetEnumerator()
2112 {
2113 return _dictionary.GetEnumerator();
2114 }
2115
2116 System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
2117 {
2118 return _dictionary.GetEnumerator();
2119 }
2120 }
2121
2122 }
2123 }
2124}
2125// ReSharper restore LoopCanBeConvertedToQuery
2126// ReSharper restore RedundantExplicitArrayCreation
2127// ReSharper restore SuggestUseVarKeywordEvident \ No newline at end of file
diff --git a/src/internal/TablesAndTuples/TablesAndTuples.csproj b/src/internal/TablesAndTuples/TablesAndTuples.csproj
new file mode 100644
index 00000000..ce1697ae
--- /dev/null
+++ b/src/internal/TablesAndTuples/TablesAndTuples.csproj
@@ -0,0 +1,8 @@
1<Project Sdk="Microsoft.NET.Sdk">
2
3 <PropertyGroup>
4 <OutputType>Exe</OutputType>
5 <TargetFramework>netcoreapp2.0</TargetFramework>
6 </PropertyGroup>
7
8</Project>
diff --git a/src/internal/TablesAndTuples/WixColumnDefinition.cs b/src/internal/TablesAndTuples/WixColumnDefinition.cs
new file mode 100644
index 00000000..2d60c9dc
--- /dev/null
+++ b/src/internal/TablesAndTuples/WixColumnDefinition.cs
@@ -0,0 +1,296 @@
1using System;
2using System.Xml;
3
4namespace TablesAndSymbols
5{
6 class WixColumnDefinition
7 {
8 public WixColumnDefinition(string name, ColumnType type, int length, bool primaryKey, bool nullable, ColumnCategory category, long? minValue = null, long? maxValue = null, string keyTable = null, int? keyColumn = null, string possibilities = null, string description = null, ColumnModularizeType? modularizeType = null, bool forceLocalizable = false, bool useCData = false, bool unreal = false)
9 {
10 this.Name = name;
11 this.Type = type;
12 this.Length = length;
13 this.PrimaryKey = primaryKey;
14 this.Nullable = nullable;
15 this.ModularizeType = modularizeType;
16 this.ForceLocalizable = forceLocalizable;
17 this.MinValue = minValue;
18 this.MaxValue = maxValue;
19 this.KeyTable = keyTable;
20 this.KeyColumn = keyColumn;
21 this.Category = category;
22 this.Possibilities = possibilities;
23 this.Description = description;
24 this.UseCData = useCData;
25 this.Unreal = unreal;
26 }
27
28 public string Name { get; }
29 public ColumnType Type { get; }
30 public int Length { get; }
31 public bool PrimaryKey { get; }
32 public bool Nullable { get; }
33 public ColumnModularizeType? ModularizeType { get; }
34 public bool ForceLocalizable { get; }
35 public long? MinValue { get; }
36 public long? MaxValue { get; }
37 public string KeyTable { get; }
38 public int? KeyColumn { get; }
39 public ColumnCategory Category { get; }
40 public string Possibilities { get; }
41 public string Description { get; }
42 public bool UseCData { get; }
43 public bool Unreal { get; }
44
45 internal static WixColumnDefinition Read(XmlReader reader)
46 {
47 if (!reader.LocalName.Equals("columnDefinition"))
48 {
49 throw new XmlException();
50 }
51
52 ColumnCategory category = ColumnCategory.Unknown;
53 string description = null;
54 bool empty = reader.IsEmptyElement;
55 int? keyColumn = null;
56 string keyTable = null;
57 int length = -1;
58 bool localizable = false;
59 long? maxValue = null;
60 long? minValue = null;
61 var modularize = ColumnModularizeType.None;
62 string name = null;
63 bool nullable = false;
64 string possibilities = null;
65 bool primaryKey = false;
66 var type = ColumnType.Unknown;
67 bool useCData = false;
68 bool unreal = false;
69
70 // parse the attributes
71 while (reader.MoveToNextAttribute())
72 {
73 switch (reader.LocalName)
74 {
75 case "category":
76 switch (reader.Value)
77 {
78 case "anyPath":
79 category = ColumnCategory.AnyPath;
80 break;
81 case "binary":
82 category = ColumnCategory.Binary;
83 break;
84 case "cabinet":
85 category = ColumnCategory.Cabinet;
86 break;
87 case "condition":
88 category = ColumnCategory.Condition;
89 break;
90 case "customSource":
91 category = ColumnCategory.CustomSource;
92 break;
93 case "defaultDir":
94 category = ColumnCategory.DefaultDir;
95 break;
96 case "doubleInteger":
97 category = ColumnCategory.DoubleInteger;
98 break;
99 case "filename":
100 category = ColumnCategory.Filename;
101 break;
102 case "formatted":
103 category = ColumnCategory.Formatted;
104 break;
105 case "formattedSddl":
106 category = ColumnCategory.FormattedSDDLText;
107 break;
108 case "guid":
109 category = ColumnCategory.Guid;
110 break;
111 case "identifier":
112 category = ColumnCategory.Identifier;
113 break;
114 case "integer":
115 category = ColumnCategory.Integer;
116 break;
117 case "language":
118 category = ColumnCategory.Language;
119 break;
120 case "lowerCase":
121 category = ColumnCategory.LowerCase;
122 break;
123 case "path":
124 category = ColumnCategory.Path;
125 break;
126 case "paths":
127 category = ColumnCategory.Paths;
128 break;
129 case "property":
130 category = ColumnCategory.Property;
131 break;
132 case "regPath":
133 category = ColumnCategory.RegPath;
134 break;
135 case "shortcut":
136 category = ColumnCategory.Shortcut;
137 break;
138 case "template":
139 category = ColumnCategory.Template;
140 break;
141 case "text":
142 category = ColumnCategory.Text;
143 break;
144 case "timeDate":
145 category = ColumnCategory.TimeDate;
146 break;
147 case "upperCase":
148 category = ColumnCategory.UpperCase;
149 break;
150 case "version":
151 category = ColumnCategory.Version;
152 break;
153 case "wildCardFilename":
154 category = ColumnCategory.WildCardFilename;
155 break;
156 default:
157 throw new InvalidOperationException();
158 }
159 break;
160 case "description":
161 description = reader.Value;
162 break;
163 case "keyColumn":
164 keyColumn = Convert.ToInt32(reader.Value, 10);
165 break;
166 case "keyTable":
167 keyTable = reader.Value;
168 break;
169 case "length":
170 length = Convert.ToInt32(reader.Value, 10);
171 break;
172 case "localizable":
173 localizable = reader.Value.Equals("yes");
174 break;
175 case "maxValue":
176 maxValue = Convert.ToInt32(reader.Value, 10);
177 break;
178 case "minValue":
179 minValue = Convert.ToInt32(reader.Value, 10);
180 break;
181 case "modularize":
182 switch (reader.Value)
183 {
184 case "column":
185 modularize = ColumnModularizeType.Column;
186 break;
187 case "companionFile":
188 modularize = ColumnModularizeType.CompanionFile;
189 break;
190 case "condition":
191 modularize = ColumnModularizeType.Condition;
192 break;
193 case "controlEventArgument":
194 modularize = ColumnModularizeType.ControlEventArgument;
195 break;
196 case "controlText":
197 modularize = ColumnModularizeType.ControlText;
198 break;
199 case "icon":
200 modularize = ColumnModularizeType.Icon;
201 break;
202 case "none":
203 modularize = ColumnModularizeType.None;
204 break;
205 case "property":
206 modularize = ColumnModularizeType.Property;
207 break;
208 case "semicolonDelimited":
209 modularize = ColumnModularizeType.SemicolonDelimited;
210 break;
211 default:
212 throw new XmlException();
213 }
214 break;
215 case "name":
216 switch (reader.Value)
217 {
218 case "CREATE":
219 case "DELETE":
220 case "DROP":
221 case "INSERT":
222 throw new XmlException();
223 default:
224 name = reader.Value;
225 break;
226 }
227 break;
228 case "nullable":
229 nullable = reader.Value.Equals("yes");
230 break;
231 case "primaryKey":
232 primaryKey = reader.Value.Equals("yes");
233 break;
234 case "set":
235 possibilities = reader.Value;
236 break;
237 case "type":
238 switch (reader.Value)
239 {
240 case "localized":
241 type = ColumnType.Localized;
242 break;
243 case "number":
244 type = ColumnType.Number;
245 break;
246 case "object":
247 type = ColumnType.Object;
248 break;
249 case "string":
250 type = ColumnType.String;
251 break;
252 case "preserved":
253 type = ColumnType.Preserved;
254 break;
255 default:
256 throw new XmlException();
257 }
258 break;
259 case "useCData":
260 useCData = reader.Value.Equals("yes");
261 break;
262 case "unreal":
263 unreal = reader.Value.Equals("yes");
264 break;
265 }
266 }
267
268 // parse the child elements (there should be none)
269 if (!empty)
270 {
271 bool done = false;
272
273 while (!done && reader.Read())
274 {
275 switch (reader.NodeType)
276 {
277 case XmlNodeType.Element:
278 throw new XmlException();
279 case XmlNodeType.EndElement:
280 done = true;
281 break;
282 }
283 }
284
285 if (!done)
286 {
287 throw new XmlException();
288 }
289 }
290
291 WixColumnDefinition columnDefinition = new WixColumnDefinition(name, type, length, primaryKey, nullable, category, minValue, maxValue, keyTable, keyColumn, possibilities, description, modularize, localizable, useCData, unreal);
292
293 return columnDefinition;
294 }
295 }
296}
diff --git a/src/internal/TablesAndTuples/WixTableDefinition.cs b/src/internal/TablesAndTuples/WixTableDefinition.cs
new file mode 100644
index 00000000..61dcbb0a
--- /dev/null
+++ b/src/internal/TablesAndTuples/WixTableDefinition.cs
@@ -0,0 +1,169 @@
1using System.Collections.Generic;
2using System.Linq;
3using System.Xml;
4
5namespace TablesAndSymbols
6{
7 class WixTableDefinition
8 {
9 public WixTableDefinition(string name, IEnumerable<WixColumnDefinition> columns, bool unreal, bool symbolless, string symbolDefinitionName, bool? symbolIdIsPrimaryKey)
10 {
11 this.Name = name;
12 this.VariableName = name.Replace("_", "");
13 this.Unreal = unreal;
14 this.Columns = columns?.ToArray();
15 this.Symbolless = symbolless;
16 this.SymbolDefinitionName = symbolless ? null : symbolDefinitionName ?? this.VariableName;
17 this.SymbolIdIsPrimaryKey = symbolIdIsPrimaryKey ?? DeriveSymbolIdIsPrimaryKey(this.Columns);
18 }
19
20 public string Name { get; }
21
22 public string VariableName { get; }
23
24 public string SymbolDefinitionName { get; }
25
26 public bool Unreal { get; }
27
28 public WixColumnDefinition[] Columns { get; }
29
30 public bool SymbolIdIsPrimaryKey { get; }
31
32 public bool Symbolless { get; }
33
34 static WixTableDefinition Read(XmlReader reader)
35 {
36 var empty = reader.IsEmptyElement;
37 string name = null;
38 string symbolDefinitionName = null;
39 var unreal = false;
40 bool? symbolIdIsPrimaryKey = null;
41 var symbolless = false;
42
43 while (reader.MoveToNextAttribute())
44 {
45 switch (reader.LocalName)
46 {
47 case "name":
48 name = reader.Value;
49 break;
50 case "symbolDefinitionName":
51 symbolDefinitionName = reader.Value;
52 break;
53 case "symbolIdIsPrimaryKey":
54 symbolIdIsPrimaryKey = reader.Value.Equals("yes");
55 break;
56 case "symbolless":
57 symbolless = reader.Value.Equals("yes");
58 break;
59 case "unreal":
60 unreal = reader.Value.Equals("yes");
61 break;
62 }
63 }
64
65 if (null == name)
66 {
67 throw new XmlException();
68 }
69
70 var columns = new List<WixColumnDefinition>();
71
72 // parse the child elements
73 if (!empty)
74 {
75 var done = false;
76
77 while (!done && reader.Read())
78 {
79 switch (reader.NodeType)
80 {
81 case XmlNodeType.Element:
82 switch (reader.LocalName)
83 {
84 case "columnDefinition":
85 var columnDefinition = WixColumnDefinition.Read(reader);
86 columns.Add(columnDefinition);
87 break;
88 default:
89 throw new XmlException();
90 }
91 break;
92 case XmlNodeType.EndElement:
93 done = true;
94 break;
95 }
96 }
97
98 if (!done)
99 {
100 throw new XmlException();
101 }
102 }
103
104 return new WixTableDefinition(name, columns.ToArray(), unreal, symbolless, symbolDefinitionName, symbolIdIsPrimaryKey);
105 }
106
107 static bool DeriveSymbolIdIsPrimaryKey(WixColumnDefinition[] columns)
108 {
109 return columns[0].PrimaryKey &&
110 columns[0].Type == ColumnType.String &&
111 columns[0].Category == ColumnCategory.Identifier &&
112 !columns[0].Name.EndsWith("_") &&
113 (columns.Length == 1 || !columns.Skip(1).Any(t => t.PrimaryKey));
114 }
115
116 public static List<WixTableDefinition> LoadCollection(string inputPath)
117 {
118 using (var reader = XmlReader.Create(inputPath))
119 {
120 reader.MoveToContent();
121
122 if ("tableDefinitions" != reader.LocalName)
123 {
124 throw new XmlException();
125 }
126
127 var empty = reader.IsEmptyElement;
128 var tableDefinitions = new List<WixTableDefinition>();
129
130 while (reader.MoveToNextAttribute())
131 {
132 }
133
134 // parse the child elements
135 if (!empty)
136 {
137 var done = false;
138
139 while (!done && reader.Read())
140 {
141 switch (reader.NodeType)
142 {
143 case XmlNodeType.Element:
144 switch (reader.LocalName)
145 {
146 case "tableDefinition":
147 tableDefinitions.Add(WixTableDefinition.Read(reader));
148 break;
149 default:
150 throw new XmlException();
151 }
152 break;
153 case XmlNodeType.EndElement:
154 done = true;
155 break;
156 }
157 }
158
159 if (!done)
160 {
161 throw new XmlException();
162 }
163 }
164
165 return tableDefinitions;
166 }
167 }
168 }
169}
diff --git a/src/internal/WixBuildTools.MsgGen/AssemblyInfo.cs b/src/internal/WixBuildTools.MsgGen/AssemblyInfo.cs
new file mode 100644
index 00000000..378adbf0
--- /dev/null
+++ b/src/internal/WixBuildTools.MsgGen/AssemblyInfo.cs
@@ -0,0 +1,8 @@
1// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
2
3using System.Reflection;
4
5// General Information about an assembly is controlled through the following
6// set of attributes. Change these attribute values to modify the information
7// associated with an assembly.
8[assembly: AssemblyCulture("")]
diff --git a/src/internal/WixBuildTools.MsgGen/GenerateMessageFiles.cs b/src/internal/WixBuildTools.MsgGen/GenerateMessageFiles.cs
new file mode 100644
index 00000000..6f51dbf9
--- /dev/null
+++ b/src/internal/WixBuildTools.MsgGen/GenerateMessageFiles.cs
@@ -0,0 +1,250 @@
1// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
2
3namespace WixBuildTools.MsgGen
4{
5 using System;
6 using System.CodeDom;
7 using System.Collections;
8 using System.Globalization;
9 using System.Reflection;
10 using System.Resources;
11 using System.Xml;
12
13 /// <summary>
14 /// Message files generation class.
15 /// </summary>
16 public class GenerateMessageFiles
17 {
18 /// <summary>
19 /// Generate the message files.
20 /// </summary>
21 /// <param name="messagesDoc">Input Xml document containing message definitions.</param>
22 /// <param name="codeCompileUnit">CodeDom container.</param>
23 /// <param name="resourceWriter">Writer for default resource file.</param>
24 public static void Generate(XmlDocument messagesDoc, CodeCompileUnit codeCompileUnit, ResourceWriter resourceWriter)
25 {
26 Hashtable usedNumbers = new Hashtable();
27
28 if (null == messagesDoc)
29 {
30 throw new ArgumentNullException("messagesDoc");
31 }
32
33 if (null == codeCompileUnit)
34 {
35 throw new ArgumentNullException("codeCompileUnit");
36 }
37
38 if (null == resourceWriter)
39 {
40 throw new ArgumentNullException("resourceWriter");
41 }
42
43 string namespaceAttr = messagesDoc.DocumentElement.GetAttribute("Namespace");
44 string resourcesAttr = messagesDoc.DocumentElement.GetAttribute("Resources");
45
46 // namespace
47 CodeNamespace messagesNamespace = new CodeNamespace(namespaceAttr);
48 codeCompileUnit.Namespaces.Add(messagesNamespace);
49
50 // imports
51 messagesNamespace.Imports.Add(new CodeNamespaceImport("System"));
52 messagesNamespace.Imports.Add(new CodeNamespaceImport("System.Reflection"));
53 messagesNamespace.Imports.Add(new CodeNamespaceImport("System.Resources"));
54 if (namespaceAttr != "WixToolset.Data")
55 {
56 messagesNamespace.Imports.Add(new CodeNamespaceImport("WixToolset.Data"));
57 }
58
59 foreach (XmlElement classElement in messagesDoc.DocumentElement.ChildNodes)
60 {
61 string className = classElement.GetAttribute("Name");
62 string baseContainerName = classElement.GetAttribute("BaseContainerName");
63 string containerName = classElement.GetAttribute("ContainerName");
64 string messageLevel = classElement.GetAttribute("Level");
65
66 // message container class
67 messagesNamespace.Types.Add(CreateContainer(namespaceAttr, baseContainerName, containerName, messageLevel, resourcesAttr));
68
69 // class
70 CodeTypeDeclaration messagesClass = new CodeTypeDeclaration(className);
71 messagesClass.TypeAttributes = TypeAttributes.Class | TypeAttributes.Public | TypeAttributes.Sealed;
72 messagesNamespace.Types.Add(messagesClass);
73
74 // private constructor (needed since all methods in this class are static)
75 CodeConstructor constructor = new CodeConstructor();
76 constructor.Attributes = MemberAttributes.Private;
77 constructor.ReturnType = null;
78 messagesClass.Members.Add(constructor);
79
80 // messages
81 foreach (XmlElement messageElement in classElement.ChildNodes)
82 {
83 int number;
84 string id = messageElement.GetAttribute("Id");
85 string numberString = messageElement.GetAttribute("Number");
86 bool sourceLineNumbers = true;
87
88 // determine the message number (and ensure it was set properly)
89 if (0 < numberString.Length)
90 {
91 number = Convert.ToInt32(numberString, CultureInfo.InvariantCulture);
92 }
93 else
94 {
95 throw new ApplicationException(String.Format("Message number must be assigned for {0} '{1}'.", containerName, id));
96 }
97
98 // check for message number collisions
99 if (usedNumbers.Contains(number))
100 {
101 throw new ApplicationException(String.Format("Collision detected between two or more messages with number '{0}'.", number));
102 }
103
104 usedNumbers.Add(number, null);
105
106 if ("no" == messageElement.GetAttribute("SourceLineNumbers"))
107 {
108 sourceLineNumbers = false;
109 }
110
111 int instanceCount = 0;
112 foreach (XmlElement instanceElement in messageElement.ChildNodes)
113 {
114 string formatString = instanceElement.InnerText.Trim();
115 string resourceName = String.Concat(className, "_", id, "_", (++instanceCount).ToString());
116
117 // create a resource
118 resourceWriter.AddResource(resourceName, formatString);
119
120 // create method
121 CodeMemberMethod method = new CodeMemberMethod();
122 method.ReturnType = new CodeTypeReference(baseContainerName);
123 method.Attributes = MemberAttributes.Public | MemberAttributes.Static;
124 messagesClass.Members.Add(method);
125
126 // method name
127 method.Name = id;
128
129 // return statement
130 CodeMethodReturnStatement stmt = new CodeMethodReturnStatement();
131 method.Statements.Add(stmt);
132
133 // return statement expression
134 CodeObjectCreateExpression expr = new CodeObjectCreateExpression();
135 stmt.Expression = expr;
136
137 // new struct
138 expr.CreateType = new CodeTypeReference(containerName);
139
140 // optionally have sourceLineNumbers as the first parameter
141 if (sourceLineNumbers)
142 {
143 // sourceLineNumbers parameter
144 expr.Parameters.Add(new CodeArgumentReferenceExpression("sourceLineNumbers"));
145 }
146 else
147 {
148 expr.Parameters.Add(new CodePrimitiveExpression(null));
149 }
150
151 // message number parameter
152 expr.Parameters.Add(new CodePrimitiveExpression(number));
153
154 // resource name parameter
155 expr.Parameters.Add(new CodePrimitiveExpression(resourceName));
156
157 // optionally have sourceLineNumbers as the first parameter
158 if (sourceLineNumbers)
159 {
160 method.Parameters.Add(new CodeParameterDeclarationExpression("SourceLineNumber", "sourceLineNumbers"));
161 }
162
163 foreach (XmlNode parameterNode in instanceElement.ChildNodes)
164 {
165 XmlElement parameterElement;
166
167 if (null != (parameterElement = parameterNode as XmlElement))
168 {
169 string type = parameterElement.GetAttribute("Type");
170 string name = parameterElement.GetAttribute("Name");
171
172 // method parameter
173 method.Parameters.Add(new CodeParameterDeclarationExpression(type, name));
174
175 // String.Format parameter
176 expr.Parameters.Add(new CodeArgumentReferenceExpression(name));
177 }
178 }
179 }
180 }
181 }
182 }
183
184 /// <summary>
185 /// Create message container class.
186 /// </summary>
187 /// <param name="namespaceName">Namespace to use for resources stream.</param>
188 /// <param name="baseContainerName">Name of the base message container class.</param>
189 /// <param name="containerName">Name of the message container class.</param>
190 /// <param name="messageLevel">Message level of for the message.</param>
191 /// <param name="resourcesName">Name of the resources stream (will get namespace prepended).</param>
192 /// <returns>Message container class CodeDom object.</returns>
193 private static CodeTypeDeclaration CreateContainer(string namespaceName, string baseContainerName, string containerName, string messageLevel, string resourcesName)
194 {
195 CodeTypeDeclaration messageContainer = new CodeTypeDeclaration();
196
197 messageContainer.Name = containerName;
198 messageContainer.BaseTypes.Add(new CodeTypeReference(baseContainerName));
199
200 // constructor
201 CodeConstructor constructor = new CodeConstructor();
202 constructor.Attributes = MemberAttributes.Public;
203 constructor.ReturnType = null;
204 messageContainer.Members.Add(constructor);
205
206 CodeMemberField resourceManager = new CodeMemberField();
207 resourceManager.Attributes = MemberAttributes.Private | MemberAttributes.Static;
208 resourceManager.Name = "resourceManager";
209 resourceManager.Type = new CodeTypeReference("ResourceManager");
210 resourceManager.InitExpression = new CodeObjectCreateExpression("ResourceManager", new CodeSnippetExpression(String.Format("\"{0}.{1}\"", namespaceName, resourcesName)), new CodeSnippetExpression("Assembly.GetExecutingAssembly()"));
211 messageContainer.Members.Add(resourceManager);
212
213 // constructor parameters
214 constructor.Parameters.Add(new CodeParameterDeclarationExpression("SourceLineNumber", "sourceLineNumbers"));
215 constructor.Parameters.Add(new CodeParameterDeclarationExpression(typeof(int), "id"));
216 constructor.Parameters.Add(new CodeParameterDeclarationExpression(typeof(string), "resourceName"));
217 CodeParameterDeclarationExpression messageArgsParam = new CodeParameterDeclarationExpression("params object[]", "messageArgs");
218 constructor.Parameters.Add(messageArgsParam);
219
220 constructor.BaseConstructorArgs.Add(new CodeArgumentReferenceExpression("sourceLineNumbers"));
221 constructor.BaseConstructorArgs.Add(new CodeArgumentReferenceExpression("id"));
222 constructor.BaseConstructorArgs.Add(new CodeArgumentReferenceExpression("resourceName"));
223 constructor.BaseConstructorArgs.Add(new CodeArgumentReferenceExpression("messageArgs"));
224
225 // assign base.Level if messageLevel is specified
226 if (!String.IsNullOrEmpty(messageLevel))
227 {
228 CodePropertyReferenceExpression levelReference = new CodePropertyReferenceExpression(new CodeBaseReferenceExpression(), "Level");
229 CodeFieldReferenceExpression messageLevelField = new CodeFieldReferenceExpression(new CodeTypeReferenceExpression("MessageLevel"), messageLevel);
230 constructor.Statements.Add(new CodeAssignStatement(levelReference, messageLevelField));
231 }
232
233 // Assign base.ResourceManager property
234 CodePropertyReferenceExpression baseResourceManagerReference = new CodePropertyReferenceExpression(new CodeBaseReferenceExpression(), "ResourceManager");
235 CodeFieldReferenceExpression resourceManagerField = new CodeFieldReferenceExpression(null, "resourceManager");
236 constructor.Statements.Add(new CodeAssignStatement(baseResourceManagerReference, resourceManagerField));
237
238 //CodeMemberProperty resourceManagerProperty = new CodeMemberProperty();
239 //resourceManagerProperty.Attributes = MemberAttributes.Public | MemberAttributes.Override;
240 //resourceManagerProperty.Name = "ResourceManager";
241 //resourceManagerProperty.Type = new CodeTypeReference("ResourceManager");
242 //CodeFieldReferenceExpression resourceManagerReference = new CodeFieldReferenceExpression();
243 //resourceManagerReference.FieldName = "resourceManager";
244 //resourceManagerProperty.GetStatements.Add(new CodeMethodReturnStatement(resourceManagerReference));
245 //messageContainer.Members.Add(resourceManagerProperty);
246
247 return messageContainer;
248 }
249 }
250}
diff --git a/src/internal/WixBuildTools.MsgGen/MsgGen.cs b/src/internal/WixBuildTools.MsgGen/MsgGen.cs
new file mode 100644
index 00000000..ff4a4a90
--- /dev/null
+++ b/src/internal/WixBuildTools.MsgGen/MsgGen.cs
@@ -0,0 +1,261 @@
1// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
2
3namespace WixBuildTools.MsgGen
4{
5 using Microsoft.CSharp;
6 using System;
7 using System.CodeDom;
8 using System.CodeDom.Compiler;
9 using System.Collections;
10 using System.IO;
11 using System.Reflection;
12 using System.Resources;
13 using System.Runtime.InteropServices;
14 using System.Xml;
15 using System.Xml.Schema;
16
17 /// <summary>
18 /// The main entry point for MsgGen.
19 /// </summary>
20 public class MsgGen
21 {
22 /// <summary>
23 /// The main entry point for MsgGen.
24 /// </summary>
25 /// <param name="args">Commandline arguments for the application.</param>
26 /// <returns>Returns the application error code.</returns>
27 [STAThread]
28 public static int Main(string[] args)
29 {
30 try
31 {
32 MsgGenMain msgGen = new MsgGenMain(args);
33 }
34 catch (Exception e)
35 {
36 Console.WriteLine("MsgGen.exe : fatal error MSGG0000: {0}\r\n\r\nStack Trace:\r\n{1}", e.Message, e.StackTrace);
37 if (e is NullReferenceException || e is SEHException)
38 {
39 throw;
40 }
41 return 2;
42 }
43
44 return 0;
45 }
46
47 /// <summary>
48 /// Main class for MsgGen.
49 /// </summary>
50 private class MsgGenMain
51 {
52 private bool showLogo;
53 private bool showHelp;
54
55 private string sourceFile;
56 private string destClassFile;
57 private string destResourcesFile;
58
59 /// <summary>
60 /// Main method for the MsgGen application within the MsgGenMain class.
61 /// </summary>
62 /// <param name="args">Commandline arguments to the application.</param>
63 public MsgGenMain(string[] args)
64 {
65 this.showLogo = true;
66 this.showHelp = false;
67
68 this.sourceFile = null;
69 this.destClassFile = null;
70 this.destResourcesFile = null;
71
72 // parse the command line
73 this.ParseCommandLine(args);
74
75 if (null == this.sourceFile || null == this.destClassFile)
76 {
77 this.showHelp = true;
78 }
79 if (null == this.destResourcesFile)
80 {
81 this.destResourcesFile = Path.ChangeExtension(this.destClassFile, ".resources");
82 }
83
84 // get the assemblies
85 Assembly msgGenAssembly = Assembly.GetExecutingAssembly();
86
87 if (this.showLogo)
88 {
89 Console.WriteLine("Microsoft (R) Message Generation Tool version {0}", msgGenAssembly.GetName().Version.ToString());
90 Console.WriteLine("Copyright (C) Microsoft Corporation 2004. All rights reserved.");
91 Console.WriteLine();
92 }
93 if (this.showHelp)
94 {
95 Console.WriteLine(" usage: MsgGen.exe [-?] [-nologo] sourceFile destClassFile [destResourcesFile]");
96 Console.WriteLine();
97 Console.WriteLine(" -? this help information");
98 Console.WriteLine();
99 Console.WriteLine("For more information see: http://wix.sourceforge.net");
100 return; // exit
101 }
102
103 // load the schema
104 XmlReader reader = null;
105 XmlSchemaCollection schemaCollection = null;
106 try
107 {
108 reader = new XmlTextReader(msgGenAssembly.GetManifestResourceStream("WixBuildTools.MsgGen.Xsd.messages.xsd"));
109 schemaCollection = new XmlSchemaCollection();
110 schemaCollection.Add("http://schemas.microsoft.com/genmsgs/2004/07/messages", reader);
111 }
112 finally
113 {
114 reader.Close();
115 }
116
117 // load the source file and process it
118 using (StreamReader sr = new StreamReader(this.sourceFile))
119 {
120 XmlParserContext context = new XmlParserContext(null, null, null, XmlSpace.None);
121 XmlValidatingReader validatingReader = new XmlValidatingReader(sr.BaseStream, XmlNodeType.Document, context);
122 validatingReader.Schemas.Add(schemaCollection);
123
124 XmlDocument errorsDoc = new XmlDocument();
125 errorsDoc.Load(validatingReader);
126
127 CodeCompileUnit codeCompileUnit = new CodeCompileUnit();
128
129 using (ResourceWriter resourceWriter = new ResourceWriter(this.destResourcesFile))
130 {
131 GenerateMessageFiles.Generate(errorsDoc, codeCompileUnit, resourceWriter);
132
133 GenerateCSharpCode(codeCompileUnit, this.destClassFile);
134 }
135 }
136 }
137
138 /// <summary>
139 /// Generate the actual C# code.
140 /// </summary>
141 /// <param name="codeCompileUnit">The code DOM.</param>
142 /// <param name="destClassFile">Destination C# source file.</param>
143 public static void GenerateCSharpCode(CodeCompileUnit codeCompileUnit, string destClassFile)
144 {
145 // generate the code with the C# code provider
146 CSharpCodeProvider provider = new CSharpCodeProvider();
147
148 // obtain an ICodeGenerator from the CodeDomProvider class
149 ICodeGenerator gen = provider.CreateGenerator();
150
151 // create a TextWriter to a StreamWriter to the output file
152 using (StreamWriter sw = new StreamWriter(destClassFile))
153 {
154 using (IndentedTextWriter tw = new IndentedTextWriter(sw, " "))
155 {
156 CodeGeneratorOptions options = new CodeGeneratorOptions();
157
158 // code generation options
159 options.BlankLinesBetweenMembers = true;
160 options.BracingStyle = "C";
161
162 // generate source code using the code generator
163 gen.GenerateCodeFromCompileUnit(codeCompileUnit, tw, options);
164 }
165 }
166 }
167
168 /// <summary>
169 /// Parse the commandline arguments.
170 /// </summary>
171 /// <param name="args">Commandline arguments.</param>
172 private void ParseCommandLine(string[] args)
173 {
174 for (int i = 0; i < args.Length; ++i)
175 {
176 string arg = args[i];
177 if (null == arg || "" == arg) // skip blank arguments
178 {
179 continue;
180 }
181
182 if ('-' == arg[0] || '/' == arg[0])
183 {
184 string parameter = arg.Substring(1);
185 if ("nologo" == parameter)
186 {
187 this.showLogo = false;
188 }
189 else if ("?" == parameter || "help" == parameter)
190 {
191 this.showHelp = true;
192 }
193 }
194 else if ('@' == arg[0])
195 {
196 using (StreamReader reader = new StreamReader(arg.Substring(1)))
197 {
198 string line;
199 ArrayList newArgs = new ArrayList();
200
201 while (null != (line = reader.ReadLine()))
202 {
203 string newArg = "";
204 bool betweenQuotes = false;
205 for (int j = 0; j < line.Length; ++j)
206 {
207 // skip whitespace
208 if (!betweenQuotes && (' ' == line[j] || '\t' == line[j]))
209 {
210 if ("" != newArg)
211 {
212 newArgs.Add(newArg);
213 newArg = null;
214 }
215
216 continue;
217 }
218
219 // if we're escaping a quote
220 if ('\\' == line[j] && '"' == line[j])
221 {
222 ++j;
223 }
224 else if ('"' == line[j]) // if we've hit a new quote
225 {
226 betweenQuotes = !betweenQuotes;
227 continue;
228 }
229
230 newArg = String.Concat(newArg, line[j]);
231 }
232 if ("" != newArg)
233 {
234 newArgs.Add(newArg);
235 }
236 }
237 string[] ar = (string[])newArgs.ToArray(typeof(string));
238 this.ParseCommandLine(ar);
239 }
240 }
241 else if (null == this.sourceFile)
242 {
243 this.sourceFile = arg;
244 }
245 else if (null == this.destClassFile)
246 {
247 this.destClassFile = arg;
248 }
249 else if (null == this.destResourcesFile)
250 {
251 this.destResourcesFile = arg;
252 }
253 else
254 {
255 throw new ArgumentException(String.Format("Unknown argument '{0}'.", arg));
256 }
257 }
258 }
259 }
260 }
261}
diff --git a/src/internal/WixBuildTools.MsgGen/WixBuildTools.MsgGen.csproj b/src/internal/WixBuildTools.MsgGen/WixBuildTools.MsgGen.csproj
new file mode 100644
index 00000000..954ffa59
--- /dev/null
+++ b/src/internal/WixBuildTools.MsgGen/WixBuildTools.MsgGen.csproj
@@ -0,0 +1,27 @@
1<Project Sdk="Microsoft.NET.Sdk">
2
3 <PropertyGroup>
4 <OutputType>Exe</OutputType>
5 <TargetFramework>net461</TargetFramework>
6 <IsTool>true</IsTool>
7 <SuppressDependenciesWhenPacking>true</SuppressDependenciesWhenPacking>
8 <DebugType>embedded</DebugType>
9 <PublishRepositoryUrl>true</PublishRepositoryUrl>
10 <NoWarn>CS0618</NoWarn>
11 </PropertyGroup>
12
13 <ItemGroup>
14 <Content Include="build\WixBuildTools.MsgGen.targets" PackagePath="build\" />
15 <Content Include="buildCrossTargeting\WixBuildTools.MsgGen.targets" PackagePath="buildCrossTargeting\" />
16 </ItemGroup>
17
18 <ItemGroup>
19 <EmbeddedResource Include="Xsd\messages.xsd" />
20 </ItemGroup>
21
22 <ItemGroup>
23 <PackageReference Include="Microsoft.SourceLink.GitHub" Version="1.0.0" PrivateAssets="All" />
24 <PackageReference Include="Nerdbank.GitVersioning" Version="3.3.37" PrivateAssets="All" />
25 </ItemGroup>
26
27</Project>
diff --git a/src/internal/WixBuildTools.MsgGen/Xsd/messages.xsd b/src/internal/WixBuildTools.MsgGen/Xsd/messages.xsd
new file mode 100644
index 00000000..fd086502
--- /dev/null
+++ b/src/internal/WixBuildTools.MsgGen/Xsd/messages.xsd
@@ -0,0 +1,101 @@
1<?xml version="1.0" encoding="utf-8"?>
2<!-- Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. -->
3
4
5<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
6 targetNamespace="http://schemas.microsoft.com/genmsgs/2004/07/messages"
7 xmlns="http://schemas.microsoft.com/genmsgs/2004/07/messages">
8 <xs:annotation>
9 <xs:documentation>
10 Schema for describing any kind of messages.
11 </xs:documentation>
12 </xs:annotation>
13
14 <xs:element name="Messages">
15 <xs:complexType>
16 <xs:sequence maxOccurs="unbounded">
17 <xs:element ref="Class"/>
18 </xs:sequence>
19 <xs:attribute name="Namespace" type="xs:string" use="required">
20 <xs:annotation><xs:documentation>Namespace of the generated class.</xs:documentation></xs:annotation>
21 </xs:attribute>
22 <xs:attribute name="Resources" type="xs:string" use="required">
23 <xs:annotation><xs:documentation>Resources stream for messages. Will get namespace prepended to it.</xs:documentation></xs:annotation>
24 </xs:attribute>
25 </xs:complexType>
26 </xs:element>
27
28 <xs:element name="Class">
29 <xs:complexType>
30 <xs:sequence minOccurs="0" maxOccurs="unbounded">
31 <xs:element ref="Message"/>
32 </xs:sequence>
33 <xs:attribute name="Name" type="xs:string" use="required">
34 <xs:annotation><xs:documentation>Name of the generated class.</xs:documentation></xs:annotation>
35 </xs:attribute>
36 <xs:attribute name="ContainerName" type="xs:string" use="required">
37 <xs:annotation><xs:documentation>Name of the generated container class.</xs:documentation></xs:annotation>
38 </xs:attribute>
39 <xs:attribute name="BaseContainerName" type="xs:string" use="required">
40 <xs:annotation><xs:documentation>Name of the base container class.</xs:documentation></xs:annotation>
41 </xs:attribute>
42 <xs:attribute name="Level" type="MessageLevelType">
43 <xs:annotation><xs:documentation>Optional message level for this container class and derivative classes.</xs:documentation></xs:annotation>
44 </xs:attribute>
45 </xs:complexType>
46 </xs:element>
47
48 <xs:element name="Message">
49 <xs:complexType>
50 <xs:sequence maxOccurs="unbounded">
51 <xs:element ref="Instance"/>
52 </xs:sequence>
53 <xs:attribute name="Id" type="xs:string" use="required">
54 <xs:annotation><xs:documentation>Name of the message type.</xs:documentation></xs:annotation>
55 </xs:attribute>
56 <xs:attribute name="Number" type="xs:integer" use="required">
57 <xs:annotation><xs:documentation>Override the number for this message type.</xs:documentation></xs:annotation>
58 </xs:attribute>
59 <xs:attribute name="SourceLineNumbers" type="YesNoType">
60 <xs:annotation><xs:documentation>Associate SourceLineNumbers with this message. The default value is "yes".</xs:documentation></xs:annotation>
61 </xs:attribute>
62 </xs:complexType>
63 </xs:element>
64
65 <xs:element name="Instance">
66 <xs:complexType mixed="true">
67 <xs:sequence minOccurs="0" maxOccurs="unbounded">
68 <xs:element ref="Parameter"/>
69 </xs:sequence>
70 </xs:complexType>
71 </xs:element>
72
73 <xs:element name="Parameter">
74 <xs:complexType>
75 <xs:attribute name="Type" type="xs:string" use="required">
76 <xs:annotation><xs:documentation>Type of the parameter.</xs:documentation></xs:annotation>
77 </xs:attribute>
78 <xs:attribute name="Name" type="xs:string" use="required">
79 <xs:annotation><xs:documentation>Name of the parameter.</xs:documentation></xs:annotation>
80 </xs:attribute>
81 </xs:complexType>
82 </xs:element>
83
84 <xs:simpleType name="YesNoType">
85 <xs:annotation><xs:documentation>Values of this type will either be "yes" or "no".</xs:documentation></xs:annotation>
86 <xs:restriction base="xs:NMTOKEN">
87 <xs:enumeration value="no"/>
88 <xs:enumeration value="yes"/>
89 </xs:restriction>
90 </xs:simpleType>
91
92 <xs:simpleType name="MessageLevelType">
93 <xs:annotation><xs:documentation>The message level for this message which corresponds to the WixToolset.MessageLevel enumeration.</xs:documentation></xs:annotation>
94 <xs:restriction base="xs:NMTOKEN">
95 <xs:enumeration value="Verbose"/>
96 <xs:enumeration value="Information"/>
97 <xs:enumeration value="Warning"/>
98 <xs:enumeration value="Error"/>
99 </xs:restriction>
100 </xs:simpleType>
101</xs:schema>
diff --git a/src/internal/WixBuildTools.MsgGen/build/WixBuildTools.MsgGen.targets b/src/internal/WixBuildTools.MsgGen/build/WixBuildTools.MsgGen.targets
new file mode 100644
index 00000000..dfa7bcbe
--- /dev/null
+++ b/src/internal/WixBuildTools.MsgGen/build/WixBuildTools.MsgGen.targets
@@ -0,0 +1,102 @@
1<?xml version="1.0" encoding="utf-8"?>
2<!-- Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. -->
3
4<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003" ToolsVersion="4.0">
5 <PropertyGroup>
6 <MSBuildAllProjects>$(MSBuildAllProjects);$(MSBuildThisFileFullPath)</MSBuildAllProjects>
7 </PropertyGroup>
8
9 <ItemGroup>
10 <!--Provide support for setting type (BuildAction) from VS-->
11 <AvailableItemName Include="MsgGenSource" />
12 </ItemGroup>
13
14 <PropertyGroup>
15 <MsgGenPath Condition=" '$(MsgGenPath)'=='' ">$(MSBuildThisFileDirectory)..\tools\</MsgGenPath>
16 </PropertyGroup>
17
18 <!--
19 ================================================================================================
20 MsgGen
21
22 Generates a .cs class file and a .resx file from an XML file.
23
24 [IN]
25 @(MsgGenSource) - The items to run through the MsgGen tool.
26
27 [OUT]
28 $(IntermediateOutputPath)%(Filename).cs - The generated .cs files to include in the compilation.
29 $(IntermediateOutputPath)%(MsgGenSource.ResourcesLogicalName) - The generated .resources file to embed in the assembly.
30 ================================================================================================
31 -->
32 <PropertyGroup>
33 <MsgGenDependsOn>
34 PrepareMsgGen
35 </MsgGenDependsOn>
36 <PrepareResourcesDependsOn>
37 MsgGen;
38 $(PrepareResourcesDependsOn)
39 </PrepareResourcesDependsOn>
40 </PropertyGroup>
41 <Target
42 Name="MsgGen"
43 BeforeTargets="PrepareResources"
44 DependsOnTargets="$(MsgGenDependsOn)"
45 Condition=" '@(MsgGenSource)' != '' "
46 Inputs="@(MsgGenSource)"
47 Outputs="$(IntermediateOutputPath)%(MsgGenSource.Filename).cs;
48 $(IntermediateOutputPath)%(MsgGenSource.ResourcesLogicalName)">
49
50 <Exec Command="&quot;$(MsgGenPath)WixBuildTools.MsgGen.exe&quot; -nologo &quot;%(MsgGenSource.FullPath)&quot; &quot;$(MsgGenCsFile)&quot; &quot;$(MsgGenResourcesFile)&quot;"
51 Outputs="$(MsgGenCsFile);$(MsgGenResourcesFile)" />
52
53 <ItemGroup>
54 <!-- This will tell MSBuild to clean up the .cs and .resources file during a Clean build -->
55 <FileWrites Include="$(MsgGenCsFile);$(MsgGenResourcesFile)" />
56 </ItemGroup>
57 </Target>
58
59 <!--
60 ================================================================================================
61 PrepareMsgGen
62
63 Creates properties and Include items for MsgGen. This must be separate from the MsgGen target
64 to workaround an MSBuild bug: AdditionalMetadata is ignored when the target is up-to-date.
65
66 ================================================================================================
67 -->
68 <Target
69 Name="PrepareMsgGen"
70 Condition=" '@(MsgGenSource)' != '' ">
71
72 <CreateProperty Value="$(IntermediateOutputPath)%(MsgGenSource.Filename).cs">
73 <Output TaskParameter="Value" PropertyName="MsgGenCsFile" />
74 </CreateProperty>
75
76 <CreateProperty
77 Value="$(IntermediateOutputPath)%(MsgGenSource.ResourcesLogicalName)"
78 Condition=" '%(MsgGenSource.ResourcesLogicalName)' != '' ">
79
80 <Output TaskParameter="Value" PropertyName="MsgGenResourcesFile" />
81 </CreateProperty>
82
83 <!-- Add the generated .cs file to the list of source files to compile -->
84 <CreateItem
85 Include="$(MsgGenCsFile)"
86 AdditionalMetadata="Link=%(MsgGenCsFile.Filename)%(MsgGenCsFile.Extension)">
87
88 <Output TaskParameter="Include" ItemName="Compile" />
89 </CreateItem>
90
91 <!-- Add the generated .resources file to the list of resources to embed -->
92 <CreateItem
93 Include="$(MsgGenResourcesFile)"
94 AdditionalMetadata="Link=%(MsgGenResourcesFile.Filename)%(MsgGenResourcesFile.Extension);
95 LogicalName=%(MsgGenSource.ResourcesLogicalName)"
96 Condition=" '$(MsgGenResourcesFile)' != '' ">
97
98 <Output TaskParameter="Include" ItemName="EmbeddedResource" />
99 </CreateItem>
100 </Target>
101
102</Project>
diff --git a/src/internal/WixBuildTools.MsgGen/buildCrossTargeting/WixBuildTools.MsgGen.targets b/src/internal/WixBuildTools.MsgGen/buildCrossTargeting/WixBuildTools.MsgGen.targets
new file mode 100644
index 00000000..a3985af5
--- /dev/null
+++ b/src/internal/WixBuildTools.MsgGen/buildCrossTargeting/WixBuildTools.MsgGen.targets
@@ -0,0 +1,6 @@
1<?xml version="1.0" encoding="utf-8"?>
2<!-- Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. -->
3
4<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003" ToolsVersion="4.0">
5 <Import Project="..\build\WixBuildTools.MsgGen.targets" />
6</Project>
diff --git a/src/internal/WixBuildTools.TestSupport.Native/AssemblyInfo.cpp b/src/internal/WixBuildTools.TestSupport.Native/AssemblyInfo.cpp
new file mode 100644
index 00000000..23a48993
--- /dev/null
+++ b/src/internal/WixBuildTools.TestSupport.Native/AssemblyInfo.cpp
@@ -0,0 +1,17 @@
1// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
2
3#include "precomp.h"
4
5using namespace System::Reflection;
6using namespace System::Runtime::CompilerServices;
7using namespace System::Runtime::InteropServices;
8
9//
10// General Information about an assembly is controlled through the following
11// set of attributes. Change these attribute values to modify the information
12// associated with an assembly.
13//
14[assembly: AssemblyTitleAttribute("WixBuildTools.TestSupport.Native")];
15[assembly: AssemblyDescriptionAttribute("")];
16[assembly: AssemblyCultureAttribute("")];
17[assembly: ComVisible(false)];
diff --git a/src/internal/WixBuildTools.TestSupport.Native/NativeAssert.h b/src/internal/WixBuildTools.TestSupport.Native/NativeAssert.h
new file mode 100644
index 00000000..34af4f34
--- /dev/null
+++ b/src/internal/WixBuildTools.TestSupport.Native/NativeAssert.h
@@ -0,0 +1,85 @@
1#pragma once
2// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
3
4
5namespace WixBuildTools {
6namespace TestSupport {
7
8 using namespace System;
9 using namespace System::Collections::Generic;
10 using namespace System::Linq;
11 using namespace Xunit;
12
13 public ref class NativeAssert : WixAssert
14 {
15 public:
16 static void NotNull(LPCWSTR wz)
17 {
18 if (!wz)
19 {
20 Assert::NotNull(nullptr);
21 }
22 }
23
24 // For some reason, naming these NotStringEqual methods "NotEqual" breaks Intellisense in files that call any overload of the NotEqual method.
25 static void NotStringEqual(LPCWSTR expected, LPCWSTR actual)
26 {
27 NativeAssert::NotStringEqual(expected, actual, FALSE);
28 }
29
30 static void NotStringEqual(LPCWSTR expected, LPCWSTR actual, BOOL ignoreCase)
31 {
32 IEqualityComparer<String^>^ comparer = ignoreCase ? StringComparer::InvariantCultureIgnoreCase : StringComparer::InvariantCulture;
33 Assert::NotEqual(NativeAssert::LPWSTRToString(expected), NativeAssert::LPWSTRToString(actual), comparer);
34 }
35
36 // For some reason, naming these StringEqual methods "Equal" breaks Intellisense in files that call any overload of the Equal method.
37 static void StringEqual(LPCWSTR expected, LPCWSTR actual)
38 {
39 NativeAssert::StringEqual(expected, actual, FALSE);
40 }
41
42 static void StringEqual(LPCWSTR expected, LPCWSTR actual, BOOL ignoreCase)
43 {
44 IEqualityComparer<String^>^ comparer = ignoreCase ? StringComparer::InvariantCultureIgnoreCase : StringComparer::InvariantCulture;
45 Assert::Equal(NativeAssert::LPWSTRToString(expected), NativeAssert::LPWSTRToString(actual), comparer);
46 }
47
48 static void Succeeded(HRESULT hr, LPCSTR zFormat, LPCSTR zArg, ... array<LPCSTR>^ zArgs)
49 {
50 array<Object^>^ formatArgs = gcnew array<Object^, 1>(zArgs->Length + 1);
51 formatArgs[0] = NativeAssert::LPSTRToString(zArg);
52 for (int i = 0; i < zArgs->Length; ++i)
53 {
54 formatArgs[i + 1] = NativeAssert::LPSTRToString(zArgs[i]);
55 }
56 WixAssert::Succeeded(hr, gcnew String(zFormat), formatArgs);
57 }
58
59 static void Succeeded(HRESULT hr, LPCSTR zFormat, ... array<LPCWSTR>^ wzArgs)
60 {
61 array<Object^>^ formatArgs = gcnew array<Object^, 1>(wzArgs->Length);
62 for (int i = 0; i < wzArgs->Length; ++i)
63 {
64 formatArgs[i] = NativeAssert::LPWSTRToString(wzArgs[i]);
65 }
66 WixAssert::Succeeded(hr, gcnew String(zFormat), formatArgs);
67 }
68
69 static void ValidReturnCode(HRESULT hr, ... array<HRESULT>^ validReturnCodes)
70 {
71 Assert::Contains(hr, (IEnumerable<HRESULT>^)validReturnCodes);
72 }
73
74 private:
75 static String^ LPSTRToString(LPCSTR z)
76 {
77 return z ? gcnew String(z) : nullptr;
78 }
79 static String^ LPWSTRToString(LPCWSTR wz)
80 {
81 return wz ? gcnew String(wz) : nullptr;
82 }
83 };
84}
85}
diff --git a/src/internal/WixBuildTools.TestSupport.Native/WixBuildTools.TestSupport.Native.nuspec b/src/internal/WixBuildTools.TestSupport.Native/WixBuildTools.TestSupport.Native.nuspec
new file mode 100644
index 00000000..2852826b
--- /dev/null
+++ b/src/internal/WixBuildTools.TestSupport.Native/WixBuildTools.TestSupport.Native.nuspec
@@ -0,0 +1,26 @@
1<?xml version="1.0"?>
2<package xmlns="http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd">
3 <metadata minClientVersion="4.0">
4 <id>$id$</id>
5 <version>$version$</version>
6 <authors>$authors$</authors>
7 <owners>$authors$</owners>
8 <license type="expression">MS-RL</license>
9 <projectUrl>https://github.com/wixtoolset/WixBuildTools</projectUrl>
10 <requireLicenseAcceptance>false</requireLicenseAcceptance>
11 <description>$description$</description>
12 <copyright>$copyright$</copyright>
13 <repository type="$repositorytype$" url="$repositoryurl$" commit="$repositorycommit$" />
14 <dependencies>
15 <group targetFramework=".NETFramework4.7.2" />
16 </dependencies>
17 </metadata>
18
19 <files>
20 <file src="build\$id$.props" target="build" />
21 <file src="build\$id$.targets" target="build" />
22
23 <file src="$outputpath$$id$.dll" target="lib\net472" />
24 <file src="$outputpath$$id$.pdb" target="lib\net472" />
25 </files>
26</package>
diff --git a/src/internal/WixBuildTools.TestSupport.Native/WixBuildTools.TestSupport.Native.vcxproj b/src/internal/WixBuildTools.TestSupport.Native/WixBuildTools.TestSupport.Native.vcxproj
new file mode 100644
index 00000000..aefdb4fb
--- /dev/null
+++ b/src/internal/WixBuildTools.TestSupport.Native/WixBuildTools.TestSupport.Native.vcxproj
@@ -0,0 +1,85 @@
1<?xml version="1.0" encoding="utf-8"?>
2<!-- Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. -->
3
4
5<Project DefaultTargets="Build" ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
6 <Import Project="build\WixBuildTools.TestSupport.Native.props" />
7 <Import Project="..\..\packages\Microsoft.SourceLink.GitHub.1.0.0\build\Microsoft.SourceLink.GitHub.props" Condition="Exists('..\..\packages\Microsoft.SourceLink.GitHub.1.0.0\build\Microsoft.SourceLink.GitHub.props')" />
8 <Import Project="..\..\packages\Microsoft.SourceLink.Common.1.0.0\build\Microsoft.SourceLink.Common.props" Condition="Exists('..\..\packages\Microsoft.SourceLink.Common.1.0.0\build\Microsoft.SourceLink.Common.props')" />
9 <Import Project="..\..\packages\Microsoft.Build.Tasks.Git.1.0.0\build\Microsoft.Build.Tasks.Git.props" Condition="Exists('..\..\packages\Microsoft.Build.Tasks.Git.1.0.0\build\Microsoft.Build.Tasks.Git.props')" />
10 <ItemGroup Label="ProjectConfigurations">
11 <ProjectConfiguration Include="Debug|Win32">
12 <Configuration>Debug</Configuration>
13 <Platform>Win32</Platform>
14 </ProjectConfiguration>
15 <ProjectConfiguration Include="Release|Win32">
16 <Configuration>Release</Configuration>
17 <Platform>Win32</Platform>
18 </ProjectConfiguration>
19 </ItemGroup>
20 <PropertyGroup Label="Globals">
21 <ProjectTypes>{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}</ProjectTypes>
22 <ProjectGuid>{95BABD97-FBDB-453A-AF8A-FA031A07B599}</ProjectGuid>
23 <RootNamespace>WixBuildTools::TestSupport</RootNamespace>
24 <Keyword>ManagedCProj</Keyword>
25 <ConfigurationType>DynamicLibrary</ConfigurationType>
26 <CharacterSet>Unicode</CharacterSet>
27 <CLRSupport>true</CLRSupport>
28 <Description>WixBuildTools C++/CLI Test Support</Description>
29 </PropertyGroup>
30 <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
31 <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
32 <ImportGroup Label="Shared">
33 <Import Project="..\..\packages\Microsoft.Build.Tasks.Git.1.0.0\build\Microsoft.Build.Tasks.Git.targets" Condition="Exists('..\..\packages\Microsoft.Build.Tasks.Git.1.0.0\build\Microsoft.Build.Tasks.Git.targets')" />
34 <Import Project="..\..\packages\Microsoft.SourceLink.Common.1.0.0\build\Microsoft.SourceLink.Common.targets" Condition="Exists('..\..\packages\Microsoft.SourceLink.Common.1.0.0\build\Microsoft.SourceLink.Common.targets')" />
35 <Import Project="..\..\packages\Microsoft.SourceLink.GitHub.1.0.0\build\Microsoft.SourceLink.GitHub.targets" Condition="Exists('..\..\packages\Microsoft.SourceLink.GitHub.1.0.0\build\Microsoft.SourceLink.GitHub.targets')" />
36 <Import Project="..\..\packages\Nerdbank.GitVersioning.3.3.37\build\Nerdbank.GitVersioning.targets" Condition="Exists('..\..\packages\Nerdbank.GitVersioning.3.3.37\build\Nerdbank.GitVersioning.targets')" />
37 </ImportGroup>
38 <PropertyGroup>
39 <ProjectAdditionalIncludeDirectories>
40 </ProjectAdditionalIncludeDirectories>
41 <ProjectAdditionalLinkLibraries>
42 </ProjectAdditionalLinkLibraries>
43 </PropertyGroup>
44 <ItemGroup>
45 <ClCompile Include="AssemblyInfo.cpp" />
46 <ClCompile Include="precomp.cpp">
47 <PrecompiledHeader>Create</PrecompiledHeader>
48 <!-- Warnings from NativeAssert.h from referencing netstandard dlls -->
49 <DisableSpecificWarnings>4564;4691</DisableSpecificWarnings>
50 </ClCompile>
51 </ItemGroup>
52 <ItemGroup>
53 <ClInclude Include="precomp.h" />
54 <ClInclude Include="NativeAssert.h" />
55 </ItemGroup>
56 <ItemGroup>
57 <None Include="packages.config" />
58 </ItemGroup>
59 <ItemGroup>
60 <Reference Include="System" />
61 <Reference Include="System.Core" />
62 </ItemGroup>
63 <ItemGroup>
64 <ProjectReference Include="..\WixBuildTools.TestSupport\WixBuildTools.TestSupport.csproj">
65 <Project>{6C57EF2C-979A-4106-A9E5-FE342810619A}</Project>
66 </ProjectReference>
67 </ItemGroup>
68 <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
69 <Import Project="build\WixBuildTools.TestSupport.Native.targets" />
70 <Target Name="PackNativeNuget" DependsOnTargets="Build">
71 <Exec Command='nuget pack $(MSBuildThisFileName).nuspec -OutputDirectory "$(OutputPath).." -Properties Id=$(MSBuildThisFileName);Version="$(BuildVersionSimple)";Authors="$(Authors)";Copyright="$(Copyright)";Description="$(Description)";Title="$(Title);RepositoryCommit=$(SourceRevisionId);RepositoryType=$(RepositoryType);RepositoryUrl=$(PrivateRepositoryUrl);OutputPath=$(OutputPath)' />
72 </Target>
73 <Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
74 <PropertyGroup>
75 <ErrorText>This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
76 </PropertyGroup>
77 <Error Condition="!Exists('..\..\packages\Microsoft.Build.Tasks.Git.1.0.0\build\Microsoft.Build.Tasks.Git.props')" Text="$([System.String]::Format('$(ErrorText)', '..\..\packages\Microsoft.Build.Tasks.Git.1.0.0\build\Microsoft.Build.Tasks.Git.props'))" />
78 <Error Condition="!Exists('..\..\packages\Microsoft.Build.Tasks.Git.1.0.0\build\Microsoft.Build.Tasks.Git.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\packages\Microsoft.Build.Tasks.Git.1.0.0\build\Microsoft.Build.Tasks.Git.targets'))" />
79 <Error Condition="!Exists('..\..\packages\Microsoft.SourceLink.Common.1.0.0\build\Microsoft.SourceLink.Common.props')" Text="$([System.String]::Format('$(ErrorText)', '..\..\packages\Microsoft.SourceLink.Common.1.0.0\build\Microsoft.SourceLink.Common.props'))" />
80 <Error Condition="!Exists('..\..\packages\Microsoft.SourceLink.Common.1.0.0\build\Microsoft.SourceLink.Common.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\packages\Microsoft.SourceLink.Common.1.0.0\build\Microsoft.SourceLink.Common.targets'))" />
81 <Error Condition="!Exists('..\..\packages\Microsoft.SourceLink.GitHub.1.0.0\build\Microsoft.SourceLink.GitHub.props')" Text="$([System.String]::Format('$(ErrorText)', '..\..\packages\Microsoft.SourceLink.GitHub.1.0.0\build\Microsoft.SourceLink.GitHub.props'))" />
82 <Error Condition="!Exists('..\..\packages\Microsoft.SourceLink.GitHub.1.0.0\build\Microsoft.SourceLink.GitHub.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\packages\Microsoft.SourceLink.GitHub.1.0.0\build\Microsoft.SourceLink.GitHub.targets'))" />
83 <Error Condition="!Exists('..\..\packages\Nerdbank.GitVersioning.3.3.37\build\Nerdbank.GitVersioning.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\packages\Nerdbank.GitVersioning.3.3.37\build\Nerdbank.GitVersioning.targets'))" />
84 </Target>
85</Project> \ No newline at end of file
diff --git a/src/internal/WixBuildTools.TestSupport.Native/WixBuildTools.TestSupport.Native.vcxproj.filters b/src/internal/WixBuildTools.TestSupport.Native/WixBuildTools.TestSupport.Native.vcxproj.filters
new file mode 100644
index 00000000..34c1380f
--- /dev/null
+++ b/src/internal/WixBuildTools.TestSupport.Native/WixBuildTools.TestSupport.Native.vcxproj.filters
@@ -0,0 +1,33 @@
1<?xml version="1.0" encoding="utf-8"?>
2<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
3 <ItemGroup>
4 <Filter Include="Source Files">
5 <UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
6 <Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
7 </Filter>
8 <Filter Include="Header Files">
9 <UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>
10 <Extensions>h;hh;hpp;hxx;hm;inl;inc;xsd</Extensions>
11 </Filter>
12 <Filter Include="Resource Files">
13 <UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier>
14 <Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions>
15 </Filter>
16 </ItemGroup>
17 <ItemGroup>
18 <ClInclude Include="precomp.h">
19 <Filter>Header Files</Filter>
20 </ClInclude>
21 <ClInclude Include="NativeAssert.h">
22 <Filter>Header Files</Filter>
23 </ClInclude>
24 </ItemGroup>
25 <ItemGroup>
26 <ClCompile Include="AssemblyInfo.cpp">
27 <Filter>Source Files</Filter>
28 </ClCompile>
29 <ClCompile Include="precomp.cpp">
30 <Filter>Source Files</Filter>
31 </ClCompile>
32 </ItemGroup>
33</Project> \ No newline at end of file
diff --git a/src/internal/WixBuildTools.TestSupport.Native/build/WixBuildTools.TestSupport.Native.props b/src/internal/WixBuildTools.TestSupport.Native/build/WixBuildTools.TestSupport.Native.props
new file mode 100644
index 00000000..4a7a0035
--- /dev/null
+++ b/src/internal/WixBuildTools.TestSupport.Native/build/WixBuildTools.TestSupport.Native.props
@@ -0,0 +1,29 @@
1<?xml version="1.0" encoding="utf-8"?>
2<!-- Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. -->
3
4
5<Project DefaultTargets="Build" ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
6 <PropertyGroup>
7 <RepoRootDir Condition=" '$(RepoRootDir)' == '' ">$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildProjectDirectory), version.json))</RepoRootDir>
8 </PropertyGroup>
9 <Import Project="$(RepoRootDir)\packages\xunit.core.2.4.1\build\xunit.core.props" Condition="Exists('$(RepoRootDir)\packages\xunit.core.2.4.1\build\xunit.core.props')" />
10 <Import Project="$(RepoRootDir)\packages\xunit.runner.visualstudio.2.4.1\build\net20\xunit.runner.visualstudio.props" Condition="Exists('$(RepoRootDir)\packages\xunit.runner.visualstudio.2.4.1\build\net20\xunit.runner.visualstudio.props')" />
11 <PropertyGroup>
12 <PlatformToolset>v142</PlatformToolset>
13 <TargetFrameworkVersion>v4.7.2</TargetFrameworkVersion>
14 </PropertyGroup>
15 <ItemGroup>
16 <Reference Include="xunit.abstractions">
17 <HintPath>$(RepoRootDir)\packages\xunit.abstractions.2.0.3\lib\netstandard2.0\xunit.abstractions.dll</HintPath>
18 </Reference>
19 <Reference Include="xunit.assert">
20 <HintPath>$(RepoRootDir)\packages\xunit.assert.2.4.1\lib\netstandard1.1\xunit.assert.dll</HintPath>
21 </Reference>
22 <Reference Include="xunit.core">
23 <HintPath>$(RepoRootDir)\packages\xunit.extensibility.core.2.4.1\lib\netstandard1.1\xunit.core.dll</HintPath>
24 </Reference>
25 <Reference Include="xunit.execution.desktop">
26 <HintPath>$(RepoRootDir)\packages\xunit.extensibility.execution.2.4.1\lib\net452\xunit.execution.desktop.dll</HintPath>
27 </Reference>
28 </ItemGroup>
29</Project> \ No newline at end of file
diff --git a/src/internal/WixBuildTools.TestSupport.Native/build/WixBuildTools.TestSupport.Native.targets b/src/internal/WixBuildTools.TestSupport.Native/build/WixBuildTools.TestSupport.Native.targets
new file mode 100644
index 00000000..77e72e95
--- /dev/null
+++ b/src/internal/WixBuildTools.TestSupport.Native/build/WixBuildTools.TestSupport.Native.targets
@@ -0,0 +1,19 @@
1<?xml version="1.0" encoding="utf-8"?>
2<!-- Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. -->
3
4
5<Project DefaultTargets="Build" ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
6 <Import Project="$(RepoRootDir)\packages\xunit.core.2.4.1\build\xunit.core.targets" Condition="Exists('$(RepoRootDir)\packages\xunit.core.2.4.1\build\xunit.core.targets')" />
7 <Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
8 <PropertyGroup>
9 <ErrorText>This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
10 </PropertyGroup>
11 <Error Condition="!Exists('$(RepoRootDir)\packages\xunit.core.2.4.1\build\xunit.core.props')" Text="$([System.String]::Format('$(ErrorText)', '$(RepoRootDir)\packages\xunit.core.2.4.1\build\xunit.core.props'))" />
12 <Error Condition="!Exists('$(RepoRootDir)\packages\xunit.core.2.4.1\build\xunit.core.targets')" Text="$([System.String]::Format('$(ErrorText)', '$(RepoRootDir)\packages\xunit.core.2.4.1\build\xunit.core.targets'))" />
13 <Error Condition="!Exists('$(RepoRootDir)\packages\xunit.runner.visualstudio.2.4.1\build\net20\xunit.runner.visualstudio.props')" Text="$([System.String]::Format('$(ErrorText)', '$(RepoRootDir)\packages\xunit.runner.visualstudio.2.4.1\build\net20\xunit.runner.visualstudio.props'))" />
14 </Target>
15 <UsingTask AssemblyFile="$(RepoRootDir)\packages\xunit.runner.msbuild.2.4.1\build\net452\xunit.runner.msbuild.net452.dll" TaskName="Xunit.Runner.MSBuild.xunit" />
16 <Target Name="Test" DependsOnTargets="Build">
17 <xunit Assemblies="$(TargetPath)" />
18 </Target>
19</Project> \ No newline at end of file
diff --git a/src/internal/WixBuildTools.TestSupport.Native/packages.config b/src/internal/WixBuildTools.TestSupport.Native/packages.config
new file mode 100644
index 00000000..917d7a63
--- /dev/null
+++ b/src/internal/WixBuildTools.TestSupport.Native/packages.config
@@ -0,0 +1,17 @@
1<?xml version="1.0" encoding="utf-8"?>
2<!-- Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. -->
3
4
5<packages>
6 <package id="Microsoft.Build.Tasks.Git" version="1.0.0" targetFramework="native" developmentDependency="true" />
7 <package id="Microsoft.SourceLink.Common" version="1.0.0" targetFramework="native" developmentDependency="true" />
8 <package id="Microsoft.SourceLink.GitHub" version="1.0.0" targetFramework="native" developmentDependency="true" />
9 <package id="Nerdbank.GitVersioning" version="3.3.37" targetFramework="native" developmentDependency="true" />
10 <package id="xunit.abstractions" version="2.0.3" />
11 <package id="xunit.assert" version="2.4.1" />
12 <package id="xunit.core" version="2.4.1" />
13 <package id="xunit.extensibility.core" version="2.4.1" />
14 <package id="xunit.extensibility.execution" version="2.4.1" />
15 <package id="xunit.runner.msbuild" version="2.4.1" />
16 <package id="xunit.runner.visualstudio" version="2.4.1" />
17</packages> \ No newline at end of file
diff --git a/src/internal/WixBuildTools.TestSupport.Native/precomp.cpp b/src/internal/WixBuildTools.TestSupport.Native/precomp.cpp
new file mode 100644
index 00000000..37664a1c
--- /dev/null
+++ b/src/internal/WixBuildTools.TestSupport.Native/precomp.cpp
@@ -0,0 +1,3 @@
1// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
2
3#include "precomp.h"
diff --git a/src/internal/WixBuildTools.TestSupport.Native/precomp.h b/src/internal/WixBuildTools.TestSupport.Native/precomp.h
new file mode 100644
index 00000000..f54b55be
--- /dev/null
+++ b/src/internal/WixBuildTools.TestSupport.Native/precomp.h
@@ -0,0 +1,11 @@
1#pragma once
2// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
3
4
5#include <windows.h>
6#include <strsafe.h>
7
8#include "NativeAssert.h"
9
10#pragma managed
11#include <vcclr.h>
diff --git a/src/internal/WixBuildTools.TestSupport/Builder.cs b/src/internal/WixBuildTools.TestSupport/Builder.cs
new file mode 100644
index 00000000..ef0de8c9
--- /dev/null
+++ b/src/internal/WixBuildTools.TestSupport/Builder.cs
@@ -0,0 +1,70 @@
1// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
2
3namespace WixBuildTools.TestSupport
4{
5 using System;
6 using System.Collections.Generic;
7 using System.IO;
8
9 public class Builder
10 {
11 public Builder(string sourceFolder, Type extensionType = null, string[] bindPaths = null, string outputFile = null)
12 {
13 this.SourceFolder = sourceFolder;
14 this.ExtensionType = extensionType;
15 this.BindPaths = bindPaths;
16 this.OutputFile = outputFile ?? "test.msi";
17 }
18
19 public string[] BindPaths { get; set; }
20
21 public Type ExtensionType { get; set; }
22
23 public string OutputFile { get; set; }
24
25 public string SourceFolder { get; }
26
27 public string[] BuildAndQuery(Action<string[]> buildFunc, params string[] tables)
28 {
29 var sourceFiles = Directory.GetFiles(this.SourceFolder, "*.wxs");
30 var wxlFiles = Directory.GetFiles(this.SourceFolder, "*.wxl");
31
32 using (var fs = new DisposableFileSystem())
33 {
34 var intermediateFolder = fs.GetFolder();
35 var outputPath = Path.Combine(intermediateFolder, "bin", this.OutputFile);
36
37 var args = new List<string>
38 {
39 "build",
40 "-o", outputPath,
41 "-intermediateFolder", intermediateFolder,
42 };
43
44 if (this.ExtensionType != null)
45 {
46 args.Add("-ext");
47 args.Add(Path.GetFullPath(new Uri(this.ExtensionType.Assembly.CodeBase).LocalPath));
48 }
49
50 args.AddRange(sourceFiles);
51
52 foreach (var wxlFile in wxlFiles)
53 {
54 args.Add("-loc");
55 args.Add(wxlFile);
56 }
57
58 foreach (var bindPath in this.BindPaths)
59 {
60 args.Add("-bindpath");
61 args.Add(bindPath);
62 }
63
64 buildFunc(args.ToArray());
65
66 return Query.QueryDatabase(outputPath, tables);
67 }
68 }
69 }
70}
diff --git a/src/internal/WixBuildTools.TestSupport/DisposableFileSystem.cs b/src/internal/WixBuildTools.TestSupport/DisposableFileSystem.cs
new file mode 100644
index 00000000..f096db72
--- /dev/null
+++ b/src/internal/WixBuildTools.TestSupport/DisposableFileSystem.cs
@@ -0,0 +1,93 @@
1// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
2
3namespace WixBuildTools.TestSupport
4{
5 using System;
6 using System.Collections.Generic;
7 using System.IO;
8
9 public class DisposableFileSystem : IDisposable
10 {
11 protected bool Disposed { get; private set; }
12
13 private List<string> CleanupPaths { get; } = new List<string>();
14
15 public bool Keep { get; }
16
17 public DisposableFileSystem(bool keep = false)
18 {
19 this.Keep = keep;
20 }
21
22 protected string GetFile(bool create = false)
23 {
24 var path = Path.GetTempFileName();
25
26 if (!create)
27 {
28 File.Delete(path);
29 }
30
31 this.CleanupPaths.Add(path);
32
33 return path;
34 }
35
36 public string GetFolder(bool create = false)
37 {
38 var path = Path.Combine(Path.GetTempPath(), Path.GetRandomFileName());
39
40 if (create)
41 {
42 Directory.CreateDirectory(path);
43 }
44
45 this.CleanupPaths.Add(path);
46
47 return path;
48 }
49
50
51 #region // IDisposable
52
53 public void Dispose()
54 {
55 this.Dispose(true);
56 GC.SuppressFinalize(this);
57 }
58
59 protected virtual void Dispose(bool disposing)
60 {
61 if (this.Disposed)
62 {
63 return;
64 }
65
66 if (disposing && !this.Keep)
67 {
68 foreach (var path in this.CleanupPaths)
69 {
70 try
71 {
72 if (File.Exists(path))
73 {
74 File.Delete(path);
75 }
76 else if (Directory.Exists(path))
77 {
78 Directory.Delete(path, true);
79 }
80 }
81 catch
82 {
83 // Best effort delete, so ignore any failures.
84 }
85 }
86 }
87
88 this.Disposed = true;
89 }
90
91 #endregion
92 }
93}
diff --git a/src/internal/WixBuildTools.TestSupport/DotnetRunner.cs b/src/internal/WixBuildTools.TestSupport/DotnetRunner.cs
new file mode 100644
index 00000000..82391178
--- /dev/null
+++ b/src/internal/WixBuildTools.TestSupport/DotnetRunner.cs
@@ -0,0 +1,57 @@
1// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
2
3namespace WixBuildTools.TestSupport
4{
5 using System;
6 using System.Collections.Generic;
7 using System.IO;
8
9 public class DotnetRunner : ExternalExecutable
10 {
11 private static readonly object InitLock = new object();
12 private static bool Initialized;
13 private static DotnetRunner Instance;
14
15 public static ExternalExecutableResult Execute(string command, string[] arguments = null) =>
16 InitAndExecute(command, arguments);
17
18 private static ExternalExecutableResult InitAndExecute(string command, string[] arguments)
19 {
20 lock (InitLock)
21 {
22 if (!Initialized)
23 {
24 Initialized = true;
25 var dotnetPath = Environment.GetEnvironmentVariable("DOTNET_HOST_PATH");
26 if (String.IsNullOrEmpty(dotnetPath) || !File.Exists(dotnetPath))
27 {
28 dotnetPath = "dotnet";
29 }
30
31 Instance = new DotnetRunner(dotnetPath);
32 }
33 }
34
35 return Instance.ExecuteCore(command, arguments);
36 }
37
38 private DotnetRunner(string exePath) : base(exePath) { }
39
40 private ExternalExecutableResult ExecuteCore(string command, string[] arguments)
41 {
42 var total = new List<string>
43 {
44 command,
45 };
46
47 if (arguments != null)
48 {
49 total.AddRange(arguments);
50 }
51
52 var args = CombineArguments(total);
53 var mergeErrorIntoOutput = true;
54 return this.Run(args, mergeErrorIntoOutput);
55 }
56 }
57}
diff --git a/src/internal/WixBuildTools.TestSupport/ExternalExecutable.cs b/src/internal/WixBuildTools.TestSupport/ExternalExecutable.cs
new file mode 100644
index 00000000..eb07aa13
--- /dev/null
+++ b/src/internal/WixBuildTools.TestSupport/ExternalExecutable.cs
@@ -0,0 +1,88 @@
1// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
2
3namespace WixBuildTools.TestSupport
4{
5 using System.Collections.Concurrent;
6 using System.Collections.Generic;
7 using System.Diagnostics;
8 using System.IO;
9 using System.Text;
10
11 public abstract class ExternalExecutable
12 {
13 private readonly string exePath;
14
15 protected ExternalExecutable(string exePath)
16 {
17 this.exePath = exePath;
18 }
19
20 protected ExternalExecutableResult Run(string args, bool mergeErrorIntoOutput = false, string workingDirectory = null)
21 {
22 var startInfo = new ProcessStartInfo(this.exePath, args)
23 {
24 CreateNoWindow = true,
25 RedirectStandardError = true,
26 RedirectStandardOutput = true,
27 UseShellExecute = false,
28 WorkingDirectory = workingDirectory ?? Path.GetDirectoryName(this.exePath),
29 };
30
31 using (var process = Process.Start(startInfo))
32 {
33 // This implementation of merging the streams does not guarantee that lines are retrieved in the same order that they were written.
34 // If the process is simultaneously writing to both streams, this is impossible to do anyway.
35 var standardOutput = new ConcurrentQueue<string>();
36 var standardError = mergeErrorIntoOutput ? standardOutput : new ConcurrentQueue<string>();
37
38 process.ErrorDataReceived += (s, e) => { if (e.Data != null) { standardError.Enqueue(e.Data); } };
39 process.OutputDataReceived += (s, e) => { if (e.Data != null) { standardOutput.Enqueue(e.Data); } };
40
41 process.BeginErrorReadLine();
42 process.BeginOutputReadLine();
43
44 process.WaitForExit();
45
46 return new ExternalExecutableResult
47 {
48 ExitCode = process.ExitCode,
49 StandardError = mergeErrorIntoOutput ? null : standardError.ToArray(),
50 StandardOutput = standardOutput.ToArray(),
51 StartInfo = startInfo,
52 };
53 }
54 }
55
56 // This is internal because it assumes backslashes aren't used as escape characters and there aren't any double quotes.
57 internal static string CombineArguments(IEnumerable<string> arguments)
58 {
59 if (arguments == null)
60 {
61 return null;
62 }
63
64 var sb = new StringBuilder();
65
66 foreach (var arg in arguments)
67 {
68 if (sb.Length > 0)
69 {
70 sb.Append(' ');
71 }
72
73 if (arg.IndexOf(' ') > -1)
74 {
75 sb.Append("\"");
76 sb.Append(arg);
77 sb.Append("\"");
78 }
79 else
80 {
81 sb.Append(arg);
82 }
83 }
84
85 return sb.ToString();
86 }
87 }
88}
diff --git a/src/internal/WixBuildTools.TestSupport/ExternalExecutableResult.cs b/src/internal/WixBuildTools.TestSupport/ExternalExecutableResult.cs
new file mode 100644
index 00000000..19b5183b
--- /dev/null
+++ b/src/internal/WixBuildTools.TestSupport/ExternalExecutableResult.cs
@@ -0,0 +1,17 @@
1// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
2
3namespace WixBuildTools.TestSupport
4{
5 using System.Diagnostics;
6
7 public class ExternalExecutableResult
8 {
9 public int ExitCode { get; set; }
10
11 public string[] StandardError { get; set; }
12
13 public string[] StandardOutput { get; set; }
14
15 public ProcessStartInfo StartInfo { get; set; }
16 }
17}
diff --git a/src/internal/WixBuildTools.TestSupport/FakeBuildEngine.cs b/src/internal/WixBuildTools.TestSupport/FakeBuildEngine.cs
new file mode 100644
index 00000000..20545970
--- /dev/null
+++ b/src/internal/WixBuildTools.TestSupport/FakeBuildEngine.cs
@@ -0,0 +1,33 @@
1// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
2
3namespace WixBuildTools.TestSupport
4{
5 using System.Collections;
6 using System.Text;
7 using Microsoft.Build.Framework;
8
9 public class FakeBuildEngine : IBuildEngine
10 {
11 private readonly StringBuilder output = new StringBuilder();
12
13 public int ColumnNumberOfTaskNode => 0;
14
15 public bool ContinueOnError => false;
16
17 public int LineNumberOfTaskNode => 0;
18
19 public string ProjectFileOfTaskNode => "fake_wix.targets";
20
21 public string Output => this.output.ToString();
22
23 public bool BuildProjectFile(string projectFileName, string[] targetNames, IDictionary globalProperties, IDictionary targetOutputs) => throw new System.NotImplementedException();
24
25 public void LogCustomEvent(CustomBuildEventArgs e) => this.output.AppendLine(e.Message);
26
27 public void LogErrorEvent(BuildErrorEventArgs e) => this.output.AppendLine(e.Message);
28
29 public void LogMessageEvent(BuildMessageEventArgs e) => this.output.AppendLine(e.Message);
30
31 public void LogWarningEvent(BuildWarningEventArgs e) => this.output.AppendLine(e.Message);
32 }
33}
diff --git a/src/internal/WixBuildTools.TestSupport/MsbuildRunner.cs b/src/internal/WixBuildTools.TestSupport/MsbuildRunner.cs
new file mode 100644
index 00000000..35e53de6
--- /dev/null
+++ b/src/internal/WixBuildTools.TestSupport/MsbuildRunner.cs
@@ -0,0 +1,168 @@
1// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
2
3namespace WixBuildTools.TestSupport
4{
5 using System;
6 using System.Collections.Generic;
7 using System.IO;
8
9 public class MsbuildRunner : ExternalExecutable
10 {
11 private static readonly string VswhereFindArguments = "-property installationPath";
12 private static readonly string Msbuild15RelativePath = @"MSBuild\15.0\Bin\MSBuild.exe";
13 private static readonly string Msbuild15RelativePath64 = @"MSBuild\15.0\Bin\amd64\MSBuild.exe";
14 private static readonly string MsbuildCurrentRelativePath = @"MSBuild\Current\Bin\MSBuild.exe";
15 private static readonly string MsbuildCurrentRelativePath64 = @"MSBuild\Current\Bin\amd64\MSBuild.exe";
16
17 private static readonly object InitLock = new object();
18
19 private static bool Initialized;
20 private static MsbuildRunner Msbuild15Runner;
21 private static MsbuildRunner Msbuild15Runner64;
22 private static MsbuildRunner MsbuildCurrentRunner;
23 private static MsbuildRunner MsbuildCurrentRunner64;
24
25 public static MsbuildRunnerResult Execute(string projectPath, string[] arguments = null, bool x64 = false) =>
26 InitAndExecute(String.Empty, projectPath, arguments, x64);
27
28 public static MsbuildRunnerResult ExecuteWithMsbuild15(string projectPath, string[] arguments = null, bool x64 = false) =>
29 InitAndExecute("15", projectPath, arguments, x64);
30
31 public static MsbuildRunnerResult ExecuteWithMsbuildCurrent(string projectPath, string[] arguments = null, bool x64 = false) =>
32 InitAndExecute("Current", projectPath, arguments, x64);
33
34 private static MsbuildRunnerResult InitAndExecute(string msbuildVersion, string projectPath, string[] arguments, bool x64)
35 {
36 lock (InitLock)
37 {
38 if (!Initialized)
39 {
40 Initialized = true;
41 var vswhereResult = VswhereRunner.Execute(VswhereFindArguments, true);
42 if (vswhereResult.ExitCode != 0)
43 {
44 throw new InvalidOperationException($"Failed to execute vswhere.exe, exit code: {vswhereResult.ExitCode}. Output:\r\n{String.Join("\r\n", vswhereResult.StandardOutput)}");
45 }
46
47 string msbuild15Path = null;
48 string msbuild15Path64 = null;
49 string msbuildCurrentPath = null;
50 string msbuildCurrentPath64 = null;
51
52 foreach (var installPath in vswhereResult.StandardOutput)
53 {
54 if (msbuildCurrentPath == null)
55 {
56 var path = Path.Combine(installPath, MsbuildCurrentRelativePath);
57 if (File.Exists(path))
58 {
59 msbuildCurrentPath = path;
60 }
61 }
62
63 if (msbuildCurrentPath64 == null)
64 {
65 var path = Path.Combine(installPath, MsbuildCurrentRelativePath64);
66 if (File.Exists(path))
67 {
68 msbuildCurrentPath64 = path;
69 }
70 }
71
72 if (msbuild15Path == null)
73 {
74 var path = Path.Combine(installPath, Msbuild15RelativePath);
75 if (File.Exists(path))
76 {
77 msbuild15Path = path;
78 }
79 }
80
81 if (msbuild15Path64 == null)
82 {
83 var path = Path.Combine(installPath, Msbuild15RelativePath64);
84 if (File.Exists(path))
85 {
86 msbuild15Path64 = path;
87 }
88 }
89 }
90
91 if (msbuildCurrentPath != null)
92 {
93 MsbuildCurrentRunner = new MsbuildRunner(msbuildCurrentPath);
94 }
95
96 if (msbuildCurrentPath64 != null)
97 {
98 MsbuildCurrentRunner64 = new MsbuildRunner(msbuildCurrentPath64);
99 }
100
101 if (msbuild15Path != null)
102 {
103 Msbuild15Runner = new MsbuildRunner(msbuild15Path);
104 }
105
106 if (msbuild15Path64 != null)
107 {
108 Msbuild15Runner64 = new MsbuildRunner(msbuild15Path64);
109 }
110 }
111 }
112
113 MsbuildRunner runner;
114 switch (msbuildVersion)
115 {
116 case "15":
117 {
118 runner = x64 ? Msbuild15Runner64 : Msbuild15Runner;
119 break;
120 }
121 case "Current":
122 {
123 runner = x64 ? MsbuildCurrentRunner64 : MsbuildCurrentRunner;
124 break;
125 }
126 default:
127 {
128 runner = x64 ? MsbuildCurrentRunner64 ?? Msbuild15Runner64
129 : MsbuildCurrentRunner ?? Msbuild15Runner;
130 break;
131 }
132 }
133
134 if (runner == null)
135 {
136 throw new InvalidOperationException($"Failed to find an installed{(x64 ? " 64-bit" : String.Empty)} MSBuild{msbuildVersion}");
137 }
138
139 return runner.ExecuteCore(projectPath, arguments);
140 }
141
142 private MsbuildRunner(string exePath) : base(exePath) { }
143
144 private MsbuildRunnerResult ExecuteCore(string projectPath, string[] arguments)
145 {
146 var total = new List<string>
147 {
148 projectPath,
149 };
150
151 if (arguments != null)
152 {
153 total.AddRange(arguments);
154 }
155
156 var args = CombineArguments(total);
157 var mergeErrorIntoOutput = true;
158 var workingFolder = Path.GetDirectoryName(projectPath);
159 var result = this.Run(args, mergeErrorIntoOutput, workingFolder);
160
161 return new MsbuildRunnerResult
162 {
163 ExitCode = result.ExitCode,
164 Output = result.StandardOutput,
165 };
166 }
167 }
168}
diff --git a/src/internal/WixBuildTools.TestSupport/MsbuildRunnerResult.cs b/src/internal/WixBuildTools.TestSupport/MsbuildRunnerResult.cs
new file mode 100644
index 00000000..5610987e
--- /dev/null
+++ b/src/internal/WixBuildTools.TestSupport/MsbuildRunnerResult.cs
@@ -0,0 +1,19 @@
1// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
2
3namespace WixBuildTools.TestSupport
4{
5 using System;
6 using Xunit;
7
8 public class MsbuildRunnerResult
9 {
10 public int ExitCode { get; set; }
11
12 public string[] Output { get; set; }
13
14 public void AssertSuccess()
15 {
16 Assert.True(0 == this.ExitCode, $"MSBuild failed unexpectedly. Output:\r\n{String.Join("\r\n", this.Output)}");
17 }
18 }
19}
diff --git a/src/internal/WixBuildTools.TestSupport/Pushd.cs b/src/internal/WixBuildTools.TestSupport/Pushd.cs
new file mode 100644
index 00000000..d0545215
--- /dev/null
+++ b/src/internal/WixBuildTools.TestSupport/Pushd.cs
@@ -0,0 +1,46 @@
1// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
2
3namespace WixBuildTools.TestSupport
4{
5 using System;
6 using System.IO;
7
8 public class Pushd : IDisposable
9 {
10 protected bool Disposed { get; private set; }
11
12 public Pushd(string path)
13 {
14 this.PreviousDirectory = Directory.GetCurrentDirectory();
15
16 Directory.SetCurrentDirectory(path);
17 }
18
19 public string PreviousDirectory { get; }
20
21 #region // IDisposable
22
23 public void Dispose()
24 {
25 this.Dispose(true);
26 GC.SuppressFinalize(this);
27 }
28
29 protected virtual void Dispose(bool disposing)
30 {
31 if (this.Disposed)
32 {
33 return;
34 }
35
36 if (disposing)
37 {
38 Directory.SetCurrentDirectory(this.PreviousDirectory);
39 }
40
41 this.Disposed = true;
42 }
43
44 #endregion
45 }
46}
diff --git a/src/internal/WixBuildTools.TestSupport/Query.cs b/src/internal/WixBuildTools.TestSupport/Query.cs
new file mode 100644
index 00000000..101a8890
--- /dev/null
+++ b/src/internal/WixBuildTools.TestSupport/Query.cs
@@ -0,0 +1,172 @@
1// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
2
3namespace WixBuildTools.TestSupport
4{
5 using System;
6 using System.Collections.Generic;
7 using System.IO;
8 using System.Linq;
9 using System.Text;
10 using WixToolset.Dtf.Compression.Cab;
11 using WixToolset.Dtf.WindowsInstaller;
12
13 public class Query
14 {
15 public static string[] QueryDatabase(string path, string[] tables)
16 {
17 var results = new List<string>();
18 var resultsByTable = QueryDatabaseByTable(path, tables);
19 var sortedTables = tables.ToList();
20 sortedTables.Sort();
21 foreach (var tableName in sortedTables)
22 {
23 var rows = resultsByTable[tableName];
24 rows?.ForEach(r => results.Add($"{tableName}:{r}"));
25 }
26 return results.ToArray();
27 }
28
29 /// <summary>
30 /// Returns rows from requested tables formatted to facilitate testing.
31 /// If the table did not exist in the database, its list will be null.
32 /// </summary>
33 /// <param name="path"></param>
34 /// <param name="tables"></param>
35 /// <returns></returns>
36 public static Dictionary<string, List<string>> QueryDatabaseByTable(string path, string[] tables)
37 {
38 var results = new Dictionary<string, List<string>>();
39
40 if (tables?.Length > 0)
41 {
42 var sb = new StringBuilder();
43 using (var db = new Database(path))
44 {
45 foreach (var table in tables)
46 {
47 if (table == "_SummaryInformation")
48 {
49 var entries = new List<string>();
50 results.Add(table, entries);
51
52 entries.Add($"Title\t{db.SummaryInfo.Title}");
53 entries.Add($"Subject\t{db.SummaryInfo.Subject}");
54 entries.Add($"Author\t{db.SummaryInfo.Author}");
55 entries.Add($"Keywords\t{db.SummaryInfo.Keywords}");
56 entries.Add($"Comments\t{db.SummaryInfo.Comments}");
57 entries.Add($"Template\t{db.SummaryInfo.Template}");
58 entries.Add($"CodePage\t{db.SummaryInfo.CodePage}");
59 entries.Add($"PageCount\t{db.SummaryInfo.PageCount}");
60 entries.Add($"WordCount\t{db.SummaryInfo.WordCount}");
61 entries.Add($"CharacterCount\t{db.SummaryInfo.CharacterCount}");
62 entries.Add($"Security\t{db.SummaryInfo.Security}");
63
64 continue;
65 }
66
67 if (!db.IsTablePersistent(table))
68 {
69 results.Add(table, null);
70 continue;
71 }
72
73 var rows = new List<string>();
74 results.Add(table, rows);
75
76 using (var view = db.OpenView("SELECT * FROM `{0}`", table))
77 {
78 view.Execute();
79
80 Record record;
81 while ((record = view.Fetch()) != null)
82 {
83 sb.Clear();
84
85 using (record)
86 {
87 for (var i = 0; i < record.FieldCount; ++i)
88 {
89 if (i > 0)
90 {
91 sb.Append("\t");
92 }
93
94 sb.Append(record[i + 1]?.ToString());
95 }
96 }
97
98 rows.Add(sb.ToString());
99 }
100 }
101 rows.Sort();
102 }
103 }
104 }
105
106 return results;
107 }
108
109 public static CabFileInfo[] GetCabinetFiles(string path)
110 {
111 var cab = new CabInfo(path);
112
113 var result = cab.GetFiles();
114
115 return result.Select(c => c).ToArray();
116 }
117
118 public static void ExtractStream(string path, string streamName, string outputPath)
119 {
120 Directory.CreateDirectory(Path.GetDirectoryName(outputPath));
121
122 using (var db = new Database(path))
123 using (var view = db.OpenView("SELECT `Data` FROM `_Streams` WHERE `Name` = '{0}'", streamName))
124 {
125 view.Execute();
126
127 using (var record = view.Fetch())
128 {
129 record.GetStream(1, outputPath);
130 }
131 }
132 }
133
134 public static void ExtractSubStorage(string path, string subStorageName, string outputPath)
135 {
136 Directory.CreateDirectory(Path.GetDirectoryName(outputPath));
137
138 using (var db = new Database(path))
139 using (var view = db.OpenView("SELECT `Name`, `Data` FROM `_Storages` WHERE `Name` = '{0}'", subStorageName))
140 {
141 view.Execute();
142
143 using (var record = view.Fetch())
144 {
145 var name = record.GetString(1);
146 record.GetStream(2, outputPath);
147 }
148 }
149 }
150
151 public static string[] GetSubStorageNames(string path)
152 {
153 var result = new List<string>();
154
155 using (var db = new Database(path))
156 using (var view = db.OpenView("SELECT `Name` FROM `_Storages`"))
157 {
158 view.Execute();
159
160 Record record;
161 while ((record = view.Fetch()) != null)
162 {
163 var name = record.GetString(1);
164 result.Add(name);
165 }
166 }
167
168 result.Sort();
169 return result.ToArray();
170 }
171 }
172}
diff --git a/src/internal/WixBuildTools.TestSupport/RobocopyRunner.cs b/src/internal/WixBuildTools.TestSupport/RobocopyRunner.cs
new file mode 100644
index 00000000..49d53351
--- /dev/null
+++ b/src/internal/WixBuildTools.TestSupport/RobocopyRunner.cs
@@ -0,0 +1,16 @@
1// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
2
3namespace WixBuildTools.TestSupport
4{
5 public class RobocopyRunner : ExternalExecutable
6 {
7 private static readonly RobocopyRunner Instance = new RobocopyRunner();
8
9 private RobocopyRunner() : base("robocopy") { }
10
11 public static ExternalExecutableResult Execute(string args)
12 {
13 return Instance.Run(args);
14 }
15 }
16}
diff --git a/src/internal/WixBuildTools.TestSupport/SucceededException.cs b/src/internal/WixBuildTools.TestSupport/SucceededException.cs
new file mode 100644
index 00000000..00b31d68
--- /dev/null
+++ b/src/internal/WixBuildTools.TestSupport/SucceededException.cs
@@ -0,0 +1,18 @@
1// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
2
3namespace WixBuildTools.TestSupport
4{
5 using System;
6 using Xunit.Sdk;
7
8 public class SucceededException : XunitException
9 {
10 public SucceededException(int hr, string userMessage)
11 : base(String.Format("WixAssert.Succeeded() Failure\r\n" +
12 "HRESULT: 0x{0:X8}\r\n" +
13 "Message: {1}",
14 hr, userMessage))
15 {
16 }
17 }
18}
diff --git a/src/internal/WixBuildTools.TestSupport/TestData.cs b/src/internal/WixBuildTools.TestSupport/TestData.cs
new file mode 100644
index 00000000..8587330d
--- /dev/null
+++ b/src/internal/WixBuildTools.TestSupport/TestData.cs
@@ -0,0 +1,16 @@
1// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
2
3namespace WixBuildTools.TestSupport
4{
5 using System;
6 using System.IO;
7
8 public class TestData
9 {
10 public static string Get(params string[] paths)
11 {
12 var localPath = Path.GetDirectoryName(new Uri(System.Reflection.Assembly.GetCallingAssembly().CodeBase).LocalPath);
13 return Path.Combine(localPath, Path.Combine(paths));
14 }
15 }
16}
diff --git a/src/internal/WixBuildTools.TestSupport/TestDataFolderFileSystem.cs b/src/internal/WixBuildTools.TestSupport/TestDataFolderFileSystem.cs
new file mode 100644
index 00000000..8d670bf0
--- /dev/null
+++ b/src/internal/WixBuildTools.TestSupport/TestDataFolderFileSystem.cs
@@ -0,0 +1,42 @@
1// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
2
3namespace WixBuildTools.TestSupport
4{
5 using System;
6
7 /// <summary>
8 /// This class builds on top of DisposableFileSystem
9 /// to make it easy to write a test that needs a whole folder of test data copied to a temp location
10 /// that will automatically be cleaned up at the end of the test.
11 /// </summary>
12 public class TestDataFolderFileSystem : IDisposable
13 {
14 private DisposableFileSystem fileSystem;
15
16 public string BaseFolder { get; private set; }
17
18 public void Dispose()
19 {
20 this.fileSystem?.Dispose();
21 }
22
23 public void Initialize(string sourceDirectoryPath)
24 {
25 if (this.fileSystem != null)
26 {
27 throw new InvalidOperationException();
28 }
29 this.fileSystem = new DisposableFileSystem();
30
31 this.BaseFolder = this.fileSystem.GetFolder();
32
33 RobocopyFolder(sourceDirectoryPath, this.BaseFolder);
34 }
35
36 private static ExternalExecutableResult RobocopyFolder(string sourceFolderPath, string destinationFolderPath)
37 {
38 var args = $"\"{sourceFolderPath}\" \"{destinationFolderPath}\" /E /R:1 /W:1";
39 return RobocopyRunner.Execute(args);
40 }
41 }
42}
diff --git a/src/internal/WixBuildTools.TestSupport/VswhereRunner.cs b/src/internal/WixBuildTools.TestSupport/VswhereRunner.cs
new file mode 100644
index 00000000..0197e125
--- /dev/null
+++ b/src/internal/WixBuildTools.TestSupport/VswhereRunner.cs
@@ -0,0 +1,41 @@
1// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
2
3namespace WixBuildTools.TestSupport
4{
5 using System;
6 using System.IO;
7
8 public class VswhereRunner : ExternalExecutable
9 {
10 private static readonly string VswhereRelativePath = @"Microsoft Visual Studio\Installer\vswhere.exe";
11
12 private static readonly object InitLock = new object();
13 private static bool Initialized;
14 private static VswhereRunner Instance;
15
16 public static ExternalExecutableResult Execute(string args, bool mergeErrorIntoOutput = false) =>
17 InitAndExecute(args, mergeErrorIntoOutput);
18
19 private static ExternalExecutableResult InitAndExecute(string args, bool mergeErrorIntoOutput)
20 {
21 lock (InitLock)
22 {
23 if (!Initialized)
24 {
25 Initialized = true;
26 var vswherePath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ProgramFilesX86), VswhereRelativePath);
27 if (!File.Exists(vswherePath))
28 {
29 throw new InvalidOperationException($"Failed to find vswhere at: {vswherePath}");
30 }
31
32 Instance = new VswhereRunner(vswherePath);
33 }
34 }
35
36 return Instance.Run(args, mergeErrorIntoOutput);
37 }
38
39 private VswhereRunner(string exePath) : base(exePath) { }
40 }
41}
diff --git a/src/internal/WixBuildTools.TestSupport/WixAssert.cs b/src/internal/WixBuildTools.TestSupport/WixAssert.cs
new file mode 100644
index 00000000..5638a787
--- /dev/null
+++ b/src/internal/WixBuildTools.TestSupport/WixAssert.cs
@@ -0,0 +1,47 @@
1// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
2
3namespace WixBuildTools.TestSupport
4{
5 using System;
6 using System.Linq;
7 using System.Xml.Linq;
8 using Xunit;
9
10 public class WixAssert : Assert
11 {
12 public static void CompareLineByLine(string[] expectedLines, string[] actualLines)
13 {
14 for (var i = 0; i < expectedLines.Length; ++i)
15 {
16 Assert.True(actualLines.Length > i, $"{i}: expectedLines longer than actualLines");
17 Assert.Equal($"{i}: {expectedLines[i]}", $"{i}: {actualLines[i]}");
18 }
19
20 Assert.True(expectedLines.Length == actualLines.Length, "actualLines longer than expectedLines");
21 }
22
23 public static void CompareXml(XContainer xExpected, XContainer xActual)
24 {
25 var expecteds = xExpected.Descendants().Select(x => $"{x.Name.LocalName}:{String.Join(",", x.Attributes().OrderBy(a => a.Name.LocalName).Select(a => $"{a.Name.LocalName}={a.Value}"))}");
26 var actuals = xActual.Descendants().Select(x => $"{x.Name.LocalName}:{String.Join(",", x.Attributes().OrderBy(a => a.Name.LocalName).Select(a => $"{a.Name.LocalName}={a.Value}"))}");
27
28 CompareLineByLine(expecteds.OrderBy(s => s).ToArray(), actuals.OrderBy(s => s).ToArray());
29 }
30
31 public static void CompareXml(string expectedPath, string actualPath)
32 {
33 var expectedDoc = XDocument.Load(expectedPath, LoadOptions.PreserveWhitespace | LoadOptions.SetBaseUri | LoadOptions.SetLineInfo);
34 var actualDoc = XDocument.Load(actualPath, LoadOptions.PreserveWhitespace | LoadOptions.SetBaseUri | LoadOptions.SetLineInfo);
35
36 CompareXml(expectedDoc, actualDoc);
37 }
38
39 public static void Succeeded(int hr, string format, params object[] formatArgs)
40 {
41 if (0 > hr)
42 {
43 throw new SucceededException(hr, String.Format(format, formatArgs));
44 }
45 }
46 }
47}
diff --git a/src/internal/WixBuildTools.TestSupport/WixBuildTools.TestSupport.csproj b/src/internal/WixBuildTools.TestSupport/WixBuildTools.TestSupport.csproj
new file mode 100644
index 00000000..f59e5eca
--- /dev/null
+++ b/src/internal/WixBuildTools.TestSupport/WixBuildTools.TestSupport.csproj
@@ -0,0 +1,31 @@
1<?xml version="1.0" encoding="utf-8"?>
2<!-- Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. -->
3
4<Project Sdk="Microsoft.NET.Sdk">
5
6 <PropertyGroup>
7 <TargetFrameworks>netstandard2.0;net461;net472</TargetFrameworks>
8 <IsPackable>true</IsPackable>
9 <DebugType>embedded</DebugType>
10 <PublishRepositoryUrl>true</PublishRepositoryUrl>
11 <CreateDocumentationFile>true</CreateDocumentationFile>
12 <NoWarn>CS1591</NoWarn>
13 </PropertyGroup>
14
15 <ItemGroup>
16 <PackageReference Include="Microsoft.Build.Tasks.Core" Version="14.3" />
17 <PackageReference Include="WixToolset.Dtf.WindowsInstaller" Version="4.0.*" />
18 <PackageReference Include="WixToolset.Dtf.Compression" Version="4.0.*" />
19 <PackageReference Include="WixToolset.Dtf.Compression.Cab" Version="4.0.*" />
20 </ItemGroup>
21
22 <ItemGroup>
23 <PackageReference Include="Microsoft.SourceLink.GitHub" Version="1.0.0" PrivateAssets="All" />
24 <PackageReference Include="Nerdbank.GitVersioning" Version="3.3.37" PrivateAssets="All" />
25 </ItemGroup>
26
27 <ItemGroup>
28 <PackageReference Include="xunit.assert" Version="2.4.1" />
29 </ItemGroup>
30
31</Project>
diff --git a/src/internal/WixBuildTools.XsdGen/AssemblyInfo.cs b/src/internal/WixBuildTools.XsdGen/AssemblyInfo.cs
new file mode 100644
index 00000000..b3740b2a
--- /dev/null
+++ b/src/internal/WixBuildTools.XsdGen/AssemblyInfo.cs
@@ -0,0 +1,9 @@
1// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
2
3using System;
4using System.Reflection;
5using System.Runtime.InteropServices;
6
7[assembly: AssemblyCulture("")]
8[assembly: CLSCompliant(true)]
9[assembly: ComVisible(false)]
diff --git a/src/internal/WixBuildTools.XsdGen/CodeDomInterfaces.cs b/src/internal/WixBuildTools.XsdGen/CodeDomInterfaces.cs
new file mode 100644
index 00000000..850839d4
--- /dev/null
+++ b/src/internal/WixBuildTools.XsdGen/CodeDomInterfaces.cs
@@ -0,0 +1,96 @@
1// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
2
3namespace WixToolset.Serialize
4{
5 using System;
6 using System.Collections;
7 using System.Xml;
8
9 /// <summary>
10 /// Interface for generated schema elements.
11 /// </summary>
12 public interface ISchemaElement
13 {
14 /// <summary>
15 /// Gets and sets the parent of this element. May be null.
16 /// </summary>
17 /// <value>An ISchemaElement that has this element as a child.</value>
18 ISchemaElement ParentElement
19 {
20 get;
21 set;
22 }
23
24 /// <summary>
25 /// Outputs xml representing this element, including the associated attributes
26 /// and any nested elements.
27 /// </summary>
28 /// <param name="writer">XmlTextWriter to be used when outputting the element.</param>
29 void OutputXml(XmlWriter writer);
30 }
31
32 /// <summary>
33 /// Interface for generated schema elements. Implemented by elements that have child
34 /// elements.
35 /// </summary>
36 public interface IParentElement
37 {
38 /// <summary>
39 /// Gets an enumerable collection of the children of this element.
40 /// </summary>
41 /// <value>An enumerable collection of the children of this element.</value>
42 IEnumerable Children
43 {
44 get;
45 }
46
47 /// <summary>
48 /// Gets an enumerable collection of the children of this element, filtered
49 /// by the passed in type.
50 /// </summary>
51 /// <param name="childType">The type of children to retrieve.</param>
52 IEnumerable this[Type childType]
53 {
54 get;
55 }
56
57 /// <summary>
58 /// Adds a child to this element.
59 /// </summary>
60 /// <param name="child">Child to add.</param>
61 void AddChild(ISchemaElement child);
62
63 /// <summary>
64 /// Removes a child from this element.
65 /// </summary>
66 /// <param name="child">Child to remove.</param>
67 void RemoveChild(ISchemaElement child);
68 }
69
70 /// <summary>
71 /// Interface for generated schema elements. Implemented by classes with attributes.
72 /// </summary>
73 public interface ISetAttributes
74 {
75 /// <summary>
76 /// Sets the attribute with the given name to the given value. The value here is
77 /// a string, and is converted to the strongly-typed version inside this method.
78 /// </summary>
79 /// <param name="name">The name of the attribute to set.</param>
80 /// <param name="value">The value to assign to the attribute.</param>
81 void SetAttribute(string name, string value);
82 }
83
84 /// <summary>
85 /// Interface for generated schema elements. Implemented by classes with children.
86 /// </summary>
87 public interface ICreateChildren
88 {
89 /// <summary>
90 /// Creates an instance of the child with the passed in name.
91 /// </summary>
92 /// <param name="childName">String matching the element name of the child when represented in XML.</param>
93 /// <returns>An instance of that child.</returns>
94 ISchemaElement CreateChild(string childName);
95 }
96}
diff --git a/src/internal/WixBuildTools.XsdGen/CodeDomReader.cs b/src/internal/WixBuildTools.XsdGen/CodeDomReader.cs
new file mode 100644
index 00000000..5198f264
--- /dev/null
+++ b/src/internal/WixBuildTools.XsdGen/CodeDomReader.cs
@@ -0,0 +1,159 @@
1// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
2
3namespace WixToolset.Serialize
4{
5 using System;
6 using System.Collections;
7 using System.Reflection;
8 using System.Xml;
9
10 /// <summary>
11 /// Class used for reading XML files in to the CodeDom.
12 /// </summary>
13 public class CodeDomReader
14 {
15 private Assembly[] assemblies;
16
17 /// <summary>
18 /// Creates a new CodeDomReader, using the current assembly.
19 /// </summary>
20 public CodeDomReader()
21 {
22 this.assemblies = new Assembly[] { Assembly.GetExecutingAssembly() };
23 }
24
25 /// <summary>
26 /// Creates a new CodeDomReader, and takes in a list of assemblies in which to
27 /// look for elements.
28 /// </summary>
29 /// <param name="assemblies">Assemblies in which to look for types that correspond
30 /// to elements.</param>
31 public CodeDomReader(Assembly[] assemblies)
32 {
33 this.assemblies = assemblies;
34 }
35
36 /// <summary>
37 /// Loads an XML file into a strongly-typed code dom.
38 /// </summary>
39 /// <param name="filePath">File to load into the code dom.</param>
40 /// <returns>The strongly-typed object at the root of the tree.</returns>
41 public ISchemaElement Load(string filePath)
42 {
43 XmlDocument document = new XmlDocument();
44 document.Load(filePath);
45 ISchemaElement schemaElement = null;
46
47 foreach (XmlNode node in document.ChildNodes)
48 {
49 XmlElement element = node as XmlElement;
50 if (element != null)
51 {
52 if (schemaElement != null)
53 {
54 throw new InvalidOperationException("Multiple root elements found in file.");
55 }
56
57 schemaElement = this.CreateObjectFromElement(element);
58 this.ParseObjectFromElement(schemaElement, element);
59 }
60 }
61 return schemaElement;
62 }
63
64 /// <summary>
65 /// Parses an ISchemaElement from the XmlElement.
66 /// </summary>
67 /// <param name="schemaElement">ISchemaElement to fill in.</param>
68 /// <param name="element">XmlElement to parse from.</param>
69 private void ParseObjectFromElement(ISchemaElement schemaElement, XmlElement element)
70 {
71 foreach (XmlAttribute attribute in element.Attributes)
72 {
73 this.SetAttributeOnObject(schemaElement, attribute.LocalName, attribute.Value);
74 }
75
76 foreach (XmlNode node in element.ChildNodes)
77 {
78 XmlElement childElement = node as XmlElement;
79 if (childElement != null)
80 {
81 ISchemaElement childSchemaElement = null;
82 ICreateChildren createChildren = schemaElement as ICreateChildren;
83 if (createChildren == null)
84 {
85 throw new InvalidOperationException("ISchemaElement with name " + element.LocalName + " does not implement ICreateChildren.");
86 }
87 else
88 {
89 childSchemaElement = createChildren.CreateChild(childElement.LocalName);
90 }
91
92 if (childSchemaElement == null)
93 {
94 childSchemaElement = this.CreateObjectFromElement(childElement);
95 if (childSchemaElement == null)
96 {
97 throw new InvalidOperationException("XmlElement with name " + childElement.LocalName + " does not have a corresponding ISchemaElement.");
98 }
99 }
100
101 this.ParseObjectFromElement(childSchemaElement, childElement);
102 IParentElement parentElement = (IParentElement)schemaElement;
103 parentElement.AddChild(childSchemaElement);
104 }
105 else
106 {
107 XmlText childText = node as XmlText;
108 if (childText != null)
109 {
110 this.SetAttributeOnObject(schemaElement, "Content", childText.Value);
111 }
112 }
113 }
114 }
115
116 /// <summary>
117 /// Sets an attribute on an ISchemaElement.
118 /// </summary>
119 /// <param name="schemaElement">Schema element to set attribute on.</param>
120 /// <param name="name">Name of the attribute to set.</param>
121 /// <param name="value">Value to set on the attribute.</param>
122 private void SetAttributeOnObject(ISchemaElement schemaElement, string name, string value)
123 {
124 ISetAttributes setAttributes = schemaElement as ISetAttributes;
125 if (setAttributes == null)
126 {
127 throw new InvalidOperationException("ISchemaElement with name "
128 + schemaElement.GetType().FullName.ToString()
129 + " does not implement ISetAttributes.");
130 }
131 else
132 {
133 setAttributes.SetAttribute(name, value);
134 }
135 }
136
137 /// <summary>
138 /// Creates an object from an XML element by digging through the assembly list.
139 /// </summary>
140 /// <param name="element">XML Element to create an ISchemaElement from.</param>
141 /// <returns>A constructed ISchemaElement.</returns>
142 private ISchemaElement CreateObjectFromElement(XmlElement element)
143 {
144 ISchemaElement schemaElement = null;
145 foreach (Assembly assembly in this.assemblies)
146 {
147 foreach (Type type in assembly.GetTypes())
148 {
149 if (type.FullName.EndsWith(element.LocalName)
150 && typeof(ISchemaElement).IsAssignableFrom(type))
151 {
152 schemaElement = (ISchemaElement)Activator.CreateInstance(type);
153 }
154 }
155 }
156 return schemaElement;
157 }
158 }
159}
diff --git a/src/internal/WixBuildTools.XsdGen/ElementCollection.cs b/src/internal/WixBuildTools.XsdGen/ElementCollection.cs
new file mode 100644
index 00000000..3f0bff16
--- /dev/null
+++ b/src/internal/WixBuildTools.XsdGen/ElementCollection.cs
@@ -0,0 +1,642 @@
1// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
2
3namespace WixToolset.Serialize
4{
5 using System;
6 using System.Collections;
7 using System.Globalization;
8
9 /// <summary>
10 /// Collection used in the CodeDOM for the children of a given element. Provides type-checking
11 /// on the allowed children to ensure that only allowed types are added.
12 /// </summary>
13 public class ElementCollection : ICollection, IEnumerable
14 {
15 private CollectionType collectionType;
16 private int minimum = 1;
17 private int maximum = 1;
18 private int totalContainedItems;
19 private int containersUsed;
20 private ArrayList items;
21
22 /// <summary>
23 /// Creates a new element collection.
24 /// </summary>
25 /// <param name="collectionType">Type of the collection to create.</param>
26 public ElementCollection(CollectionType collectionType)
27 {
28 this.collectionType = collectionType;
29 this.items = new ArrayList();
30 }
31
32 /// <summary>
33 /// Creates a new element collection.
34 /// </summary>
35 /// <param name="collectionType">Type of the collection to create.</param>
36 /// <param name="minimum">When used with a type 'Choice', specifies a minimum number of allowed children.</param>
37 /// <param name="maximum">When used with a type 'Choice', specifies a maximum number of allowed children.</param>
38 public ElementCollection(CollectionType collectionType, int minimum, int maximum) : this(collectionType)
39 {
40 this.minimum = minimum;
41 this.maximum = maximum;
42 }
43
44 /// <summary>
45 /// Enum representing types of XML collections.
46 /// </summary>
47 public enum CollectionType
48 {
49 /// <summary>
50 /// A choice type, corresponding to the XSD choice element.
51 /// </summary>
52 Choice,
53
54 /// <summary>
55 /// A sequence type, corresponding to the XSD sequence element.
56 /// </summary>
57 Sequence
58 }
59
60 /// <summary>
61 /// Gets the type of collection.
62 /// </summary>
63 /// <value>The type of collection.</value>
64 public CollectionType Type
65 {
66 get { return this.collectionType; }
67 }
68
69 /// <summary>
70 /// Gets the count of child elements in this collection (counts ISchemaElements, not nested collections).
71 /// </summary>
72 /// <value>The count of child elements in this collection (counts ISchemaElements, not nested collections).</value>
73 public int Count
74 {
75 get { return this.totalContainedItems; }
76 }
77
78 /// <summary>
79 /// Gets the flag specifying whether this collection is synchronized. Always returns false.
80 /// </summary>
81 /// <value>The flag specifying whether this collection is synchronized. Always returns false.</value>
82 public bool IsSynchronized
83 {
84 get { return false; }
85 }
86
87 /// <summary>
88 /// Gets an object external callers can synchronize on.
89 /// </summary>
90 /// <value>An object external callers can synchronize on.</value>
91 public object SyncRoot
92 {
93 get { return this; }
94 }
95
96 /// <summary>
97 /// Adds a child element to this collection.
98 /// </summary>
99 /// <param name="element">The element to add.</param>
100 /// <exception cref="ArgumentException">Thrown if the child is not of an allowed type.</exception>
101 public void AddElement(ISchemaElement element)
102 {
103 foreach (object obj in this.items)
104 {
105 bool containerUsed;
106
107 CollectionItem collectionItem = obj as CollectionItem;
108 if (collectionItem != null)
109 {
110 containerUsed = collectionItem.Elements.Count != 0;
111 if (collectionItem.ElementType.IsAssignableFrom(element.GetType()))
112 {
113 collectionItem.AddElement(element);
114
115 if (!containerUsed)
116 {
117 this.containersUsed++;
118 }
119
120 this.totalContainedItems++;
121 return;
122 }
123
124 continue;
125 }
126
127 ElementCollection collection = obj as ElementCollection;
128 if (collection != null)
129 {
130 containerUsed = collection.Count != 0;
131
132 try
133 {
134 collection.AddElement(element);
135
136 if (!containerUsed)
137 {
138 this.containersUsed++;
139 }
140
141 this.totalContainedItems++;
142 return;
143 }
144 catch (ArgumentException)
145 {
146 // Eat the exception and keep looking. We'll throw our own if we can't find its home.
147 }
148
149 continue;
150 }
151 }
152
153 throw new ArgumentException(String.Format(
154 CultureInfo.InvariantCulture,
155 "Element of type {0} is not valid for this collection.",
156 element.GetType().Name));
157 }
158
159 /// <summary>
160 /// Removes a child element from this collection.
161 /// </summary>
162 /// <param name="element">The element to remove.</param>
163 /// <exception cref="ArgumentException">Thrown if the element is not of an allowed type.</exception>
164 public void RemoveElement(ISchemaElement element)
165 {
166 foreach (object obj in this.items)
167 {
168 CollectionItem collectionItem = obj as CollectionItem;
169 if (collectionItem != null)
170 {
171 if (collectionItem.ElementType.IsAssignableFrom(element.GetType()))
172 {
173 if (collectionItem.Elements.Count == 0)
174 {
175 return;
176 }
177
178 collectionItem.RemoveElement(element);
179
180 if (collectionItem.Elements.Count == 0)
181 {
182 this.containersUsed--;
183 }
184
185 this.totalContainedItems--;
186 return;
187 }
188
189 continue;
190 }
191
192 ElementCollection collection = obj as ElementCollection;
193 if (collection != null)
194 {
195 if (collection.Count == 0)
196 {
197 continue;
198 }
199
200 try
201 {
202 collection.RemoveElement(element);
203
204 if (collection.Count == 0)
205 {
206 this.containersUsed--;
207 }
208
209 this.totalContainedItems--;
210 return;
211 }
212 catch (ArgumentException)
213 {
214 // Eat the exception and keep looking. We'll throw our own if we can't find its home.
215 }
216
217 continue;
218 }
219 }
220
221 throw new ArgumentException(String.Format(
222 CultureInfo.InvariantCulture,
223 "Element of type {0} is not valid for this collection.",
224 element.GetType().Name));
225 }
226
227 /// <summary>
228 /// Copies this collection to an array.
229 /// </summary>
230 /// <param name="array">Array to copy to.</param>
231 /// <param name="index">Offset into the array.</param>
232 public void CopyTo(Array array, int index)
233 {
234 int item = 0;
235 foreach (ISchemaElement element in this)
236 {
237 array.SetValue(element, (long)(item + index));
238 item++;
239 }
240 }
241
242 /// <summary>
243 /// Creates an enumerator for walking the elements in this collection.
244 /// </summary>
245 /// <returns>A newly created enumerator.</returns>
246 public IEnumerator GetEnumerator()
247 {
248 return new ElementCollectionEnumerator(this);
249 }
250
251 /// <summary>
252 /// Gets an enumerable collection of children of a given type.
253 /// </summary>
254 /// <param name="childType">Type of children to get.</param>
255 /// <returns>A collection of children.</returns>
256 /// <exception cref="ArgumentException">Thrown if the type isn't a valid child type.</exception>
257 public IEnumerable Filter(Type childType)
258 {
259 foreach (object container in this.items)
260 {
261 CollectionItem collectionItem = container as CollectionItem;
262 if (collectionItem != null)
263 {
264 if (collectionItem.ElementType.IsAssignableFrom(childType))
265 {
266 return collectionItem.Elements;
267 }
268
269 continue;
270 }
271
272 ElementCollection elementCollection = container as ElementCollection;
273 if (elementCollection != null)
274 {
275 IEnumerable nestedFilter = elementCollection.Filter(childType);
276 if (nestedFilter != null)
277 {
278 return nestedFilter;
279 }
280
281 continue;
282 }
283 }
284
285 throw new ArgumentException(String.Format(
286 CultureInfo.InvariantCulture,
287 "Type {0} is not valid for this collection.",
288 childType.Name));
289 }
290
291 /// <summary>
292 /// Adds a type to this collection.
293 /// </summary>
294 /// <param name="collectionItem">CollectionItem representing the type to add.</param>
295 public void AddItem(CollectionItem collectionItem)
296 {
297 this.items.Add(collectionItem);
298 }
299
300 /// <summary>
301 /// Adds a nested collection to this collection.
302 /// </summary>
303 /// <param name="collection">ElementCollection to add.</param>
304 public void AddCollection(ElementCollection collection)
305 {
306 this.items.Add(collection);
307 }
308
309 /// <summary>
310 /// Class used to represent a given type in the child collection of an element. Abstract,
311 /// has subclasses for choice and sequence (which can do cardinality checks).
312 /// </summary>
313 public abstract class CollectionItem
314 {
315 private Type elementType;
316 private ArrayList elements;
317
318 /// <summary>
319 /// Creates a new CollectionItem for the given element type.
320 /// </summary>
321 /// <param name="elementType">Type of the element for this collection item.</param>
322 public CollectionItem(Type elementType)
323 {
324 this.elementType = elementType;
325 this.elements = new ArrayList();
326 }
327
328 /// <summary>
329 /// Gets the type of this collection's items.
330 /// </summary>
331 public Type ElementType
332 {
333 get { return this.elementType; }
334 }
335
336 /// <summary>
337 /// Gets the elements of this collection.
338 /// </summary>
339 public ArrayList Elements
340 {
341 get { return this.elements; }
342 }
343
344 /// <summary>
345 /// Adds an element to this collection. Must be of an assignable type to the collection's
346 /// type.
347 /// </summary>
348 /// <param name="element">The element to add.</param>
349 /// <exception cref="ArgumentException">Thrown if the type isn't assignable to the collection's type.</exception>
350 public void AddElement(ISchemaElement element)
351 {
352 if (!this.elementType.IsAssignableFrom(element.GetType()))
353 {
354 throw new ArgumentException(
355 String.Format(
356 CultureInfo.InvariantCulture,
357 "Element must be a subclass of {0}, but was of type {1}.",
358 this.elementType.Name,
359 element.GetType().Name),
360 "element");
361 }
362
363 this.elements.Add(element);
364 }
365
366 /// <summary>
367 /// Removes an element from this collection.
368 /// </summary>
369 /// <param name="element">The element to remove.</param>
370 /// <exception cref="ArgumentException">Thrown if the element's type isn't assignable to the collection's type.</exception>
371 public void RemoveElement(ISchemaElement element)
372 {
373 if (!this.elementType.IsAssignableFrom(element.GetType()))
374 {
375 throw new ArgumentException(
376 String.Format(
377 CultureInfo.InvariantCulture,
378 "Element must be a subclass of {0}, but was of type {1}.",
379 this.elementType.Name,
380 element.GetType().Name),
381 "element");
382 }
383
384 this.elements.Remove(element);
385 }
386 }
387
388 /// <summary>
389 /// Class representing a choice item. Doesn't do cardinality checks.
390 /// </summary>
391 public class ChoiceItem : CollectionItem
392 {
393 /// <summary>
394 /// Creates a new choice item.
395 /// </summary>
396 /// <param name="elementType">Type of the created item.</param>
397 public ChoiceItem(Type elementType) : base(elementType)
398 {
399 }
400 }
401
402 /// <summary>
403 /// Class representing a sequence item. Can do cardinality checks, if required.
404 /// </summary>
405 public class SequenceItem : CollectionItem
406 {
407 private int minimum = 1;
408 private int maximum = 1;
409
410 /// <summary>
411 /// Creates a new sequence item.
412 /// </summary>
413 /// <param name="elementType">Type of the created item.</param>
414 public SequenceItem(Type elementType) : base(elementType)
415 {
416 }
417
418 /// <summary>
419 /// Creates a new sequence item with the specified minimum and maximum.
420 /// </summary>
421 /// <param name="elementType">Type of the created item.</param>
422 /// <param name="minimum">Minimum number of elements.</param>
423 /// <param name="maximum">Maximum number of elements.</param>
424 public SequenceItem(Type elementType, int minimum, int maximum) : base(elementType)
425 {
426 this.minimum = minimum;
427 this.maximum = maximum;
428 }
429 }
430
431 /// <summary>
432 /// Enumerator for the ElementCollection.
433 /// </summary>
434 private class ElementCollectionEnumerator : IEnumerator
435 {
436 private ElementCollection collection;
437 private Stack collectionStack;
438
439 /// <summary>
440 /// Creates a new ElementCollectionEnumerator.
441 /// </summary>
442 /// <param name="collection">The collection to create an enumerator for.</param>
443 public ElementCollectionEnumerator(ElementCollection collection)
444 {
445 this.collection = collection;
446 }
447
448 /// <summary>
449 /// Gets the current object from the enumerator.
450 /// </summary>
451 public object Current
452 {
453 get
454 {
455 if (this.collectionStack != null && this.collectionStack.Count > 0)
456 {
457 CollectionSymbol symbol = (CollectionSymbol)this.collectionStack.Peek();
458 object container = symbol.Collection.items[symbol.ContainerIndex];
459
460 CollectionItem collectionItem = container as CollectionItem;
461 if (collectionItem != null)
462 {
463 return collectionItem.Elements[symbol.ItemIndex];
464 }
465
466 throw new InvalidOperationException(String.Format(
467 CultureInfo.InvariantCulture,
468 "Element of type {0} found in enumerator. Must be ChoiceItem or SequenceItem.",
469 container.GetType().Name));
470 }
471
472 return null;
473 }
474 }
475
476 /// <summary>
477 /// Resets the enumerator to the beginning.
478 /// </summary>
479 public void Reset()
480 {
481 if (this.collectionStack != null)
482 {
483 this.collectionStack.Clear();
484 this.collectionStack = null;
485 }
486 }
487
488 /// <summary>
489 /// Moves the enumerator to the next item.
490 /// </summary>
491 /// <returns>True if there is a next item, false otherwise.</returns>
492 public bool MoveNext()
493 {
494 if (this.collectionStack == null)
495 {
496 if (this.collection.Count == 0)
497 {
498 return false;
499 }
500
501 this.collectionStack = new Stack();
502 this.collectionStack.Push(new CollectionSymbol(this.collection));
503 }
504
505 CollectionSymbol symbol = (CollectionSymbol)this.collectionStack.Peek();
506
507 if (this.FindNext(symbol))
508 {
509 return true;
510 }
511
512 this.collectionStack.Pop();
513 if (this.collectionStack.Count == 0)
514 {
515 return false;
516 }
517
518 return this.MoveNext();
519 }
520
521 /// <summary>
522 /// Pushes a collection onto the stack.
523 /// </summary>
524 /// <param name="collection">The collection to push.</param>
525 private void PushCollection(ElementCollection collection)
526 {
527 if (collection.Count <= 0)
528 {
529 throw new ArgumentException(String.Format(
530 CultureInfo.InvariantCulture,
531 "Collection has {0} elements. Must have at least one.",
532 collection.Count));
533 }
534
535 CollectionSymbol symbol = new CollectionSymbol(collection);
536 this.collectionStack.Push(symbol);
537 this.FindNext(symbol);
538 }
539
540 /// <summary>
541 /// Finds the next item from a given symbol.
542 /// </summary>
543 /// <param name="symbol">The symbol to start looking from.</param>
544 /// <returns>True if a next element is found, false otherwise.</returns>
545 private bool FindNext(CollectionSymbol symbol)
546 {
547 object container = symbol.Collection.items[symbol.ContainerIndex];
548
549 CollectionItem collectionItem = container as CollectionItem;
550 if (collectionItem != null)
551 {
552 if (symbol.ItemIndex + 1 < collectionItem.Elements.Count)
553 {
554 symbol.ItemIndex++;
555 return true;
556 }
557 }
558
559 ElementCollection elementCollection = container as ElementCollection;
560 if (elementCollection != null && elementCollection.Count > 0 && symbol.ItemIndex == -1)
561 {
562 symbol.ItemIndex++;
563 this.PushCollection(elementCollection);
564 return true;
565 }
566
567 symbol.ItemIndex = 0;
568
569 for (int i = symbol.ContainerIndex + 1; i < symbol.Collection.items.Count; ++i)
570 {
571 object nestedContainer = symbol.Collection.items[i];
572
573 CollectionItem nestedCollectionItem = nestedContainer as CollectionItem;
574 if (nestedCollectionItem != null)
575 {
576 if (nestedCollectionItem.Elements.Count > 0)
577 {
578 symbol.ContainerIndex = i;
579 return true;
580 }
581 }
582
583 ElementCollection nestedElementCollection = nestedContainer as ElementCollection;
584 if (nestedElementCollection != null && nestedElementCollection.Count > 0)
585 {
586 symbol.ContainerIndex = i;
587 this.PushCollection(nestedElementCollection);
588 return true;
589 }
590 }
591
592 return false;
593 }
594
595 /// <summary>
596 /// Class representing a single point in the collection. Consists of an ElementCollection,
597 /// a container index, and an index into the container.
598 /// </summary>
599 private class CollectionSymbol
600 {
601 private ElementCollection collection;
602 private int containerIndex;
603 private int itemIndex = -1;
604
605 /// <summary>
606 /// Creates a new CollectionSymbol.
607 /// </summary>
608 /// <param name="collection">The collection for the symbol.</param>
609 public CollectionSymbol(ElementCollection collection)
610 {
611 this.collection = collection;
612 }
613
614 /// <summary>
615 /// Gets the collection for the symbol.
616 /// </summary>
617 public ElementCollection Collection
618 {
619 get { return this.collection; }
620 }
621
622 /// <summary>
623 /// Gets and sets the index of the container in the collection.
624 /// </summary>
625 public int ContainerIndex
626 {
627 get { return this.containerIndex; }
628 set { this.containerIndex = value; }
629 }
630
631 /// <summary>
632 /// Gets and sets the index of the item in the container.
633 /// </summary>
634 public int ItemIndex
635 {
636 get { return this.itemIndex; }
637 set { this.itemIndex = value; }
638 }
639 }
640 }
641 }
642}
diff --git a/src/internal/WixBuildTools.XsdGen/StronglyTypedClasses.cs b/src/internal/WixBuildTools.XsdGen/StronglyTypedClasses.cs
new file mode 100644
index 00000000..4a41f8a9
--- /dev/null
+++ b/src/internal/WixBuildTools.XsdGen/StronglyTypedClasses.cs
@@ -0,0 +1,1498 @@
1// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
2
3namespace WixToolset.Tools
4{
5 using System;
6 using System.CodeDom;
7 using System.Collections;
8 using System.Collections.Generic;
9 using System.Collections.Specialized;
10 using System.IO;
11 using System.Reflection;
12 using System.Text;
13 using System.Text.RegularExpressions;
14 using System.Xml;
15 using System.Xml.Schema;
16
17 /// <summary>
18 /// Type containing static Generate method, which fills in a compile unit from a
19 /// given schema.
20 /// </summary>
21 internal class StronglyTypedClasses
22 {
23 private static string outputXmlComment = "Processes this element and all child elements into an XmlWriter.";
24 private static Hashtable simpleTypeNamesToClrTypeNames;
25 private static Dictionary<string, EnumDeclaration> typeNamesToEnumDeclarations;
26 private static Dictionary<EnumDeclaration, CodeTypeDeclaration> enumsToParseMethodClasses;
27 private static Regex multiUppercaseNameRegex = new Regex("[A-Z][A-Z][A-Z]", RegexOptions.Compiled);
28 private static Dictionary<string, XmlSchemaAttributeGroup> refToAttributeGroups;
29 private static CodeTypeDeclaration enumHelperClass;
30
31 /// <summary>
32 /// Private constructor for static class.
33 /// </summary>
34 private StronglyTypedClasses()
35 {
36 }
37
38 /// <summary>
39 /// Generates strongly typed serialization classes for the given schema document
40 /// under the given namespace and generates a code compile unit.
41 /// </summary>
42 /// <param name="xmlSchema">Schema document to generate classes for.</param>
43 /// <param name="generateNamespace">Namespace to be used for the generated code.</param>
44 /// <param name="commonNamespace">Namespace in which to find common classes and interfaces,
45 /// like ISchemaElement.</param>
46 /// <returns>A fully populated CodeCompileUnit, which can be serialized in the language of choice.</returns>
47 public static CodeCompileUnit Generate(XmlSchema xmlSchema, string generateNamespace, string commonNamespace)
48 {
49 if (xmlSchema == null)
50 {
51 throw new ArgumentNullException("xmlSchema");
52 }
53 if (generateNamespace == null)
54 {
55 throw new ArgumentNullException("generateNamespace");
56 }
57
58 simpleTypeNamesToClrTypeNames = new Hashtable();
59 typeNamesToEnumDeclarations = new Dictionary<string, EnumDeclaration>();
60 refToAttributeGroups = new Dictionary<string, XmlSchemaAttributeGroup>();
61 enumsToParseMethodClasses = new Dictionary<EnumDeclaration, CodeTypeDeclaration>();
62
63 CodeCompileUnit codeCompileUnit = new CodeCompileUnit();
64 CodeNamespace codeNamespace = new CodeNamespace(generateNamespace);
65 codeCompileUnit.Namespaces.Add(codeNamespace);
66 codeNamespace.Imports.Add(new CodeNamespaceImport("System"));
67 codeNamespace.Imports.Add(new CodeNamespaceImport("System.CodeDom.Compiler")); // for GeneratedCodeAttribute
68 codeNamespace.Imports.Add(new CodeNamespaceImport("System.Collections"));
69 codeNamespace.Imports.Add(new CodeNamespaceImport("System.Diagnostics.CodeAnalysis"));
70 codeNamespace.Imports.Add(new CodeNamespaceImport("System.Globalization"));
71 codeNamespace.Imports.Add(new CodeNamespaceImport("System.Xml"));
72 if (commonNamespace != null)
73 {
74 codeNamespace.Imports.Add(new CodeNamespaceImport(commonNamespace));
75 }
76
77 // NOTE: This hash table serves double duty so be sure to have the XSD
78 // type name mapped to the CLR type name *and* the CLR type name
79 // mapped to the same CLR type name. Look at long and bool for
80 // examples below (and before you ask, no I don't know why DateTime
81 // just works).
82 simpleTypeNamesToClrTypeNames.Add("dateTime", "DateTime");
83 simpleTypeNamesToClrTypeNames.Add("integer", "int");
84 simpleTypeNamesToClrTypeNames.Add("int", "int");
85 simpleTypeNamesToClrTypeNames.Add("NMTOKEN", "string");
86 simpleTypeNamesToClrTypeNames.Add("string", "string");
87 simpleTypeNamesToClrTypeNames.Add("nonNegativeInteger", "long");
88 simpleTypeNamesToClrTypeNames.Add("long", "long");
89 simpleTypeNamesToClrTypeNames.Add("boolean", "bool");
90 simpleTypeNamesToClrTypeNames.Add("bool", "bool");
91
92 xmlSchema.Compile(null);
93
94 foreach (XmlSchemaAttributeGroup schemaAttributeGroup in xmlSchema.AttributeGroups.Values)
95 {
96 refToAttributeGroups.Add(schemaAttributeGroup.Name, schemaAttributeGroup);
97 }
98
99 foreach (XmlSchemaObject schemaObject in xmlSchema.SchemaTypes.Values)
100 {
101 XmlSchemaSimpleType schemaSimpleType = schemaObject as XmlSchemaSimpleType;
102 if (schemaSimpleType != null)
103 {
104 ProcessSimpleType(schemaSimpleType, codeNamespace);
105 }
106 }
107
108 foreach (XmlSchemaObject schemaObject in xmlSchema.SchemaTypes.Values)
109 {
110 XmlSchemaComplexType schemaComplexType = schemaObject as XmlSchemaComplexType;
111 if (schemaComplexType != null)
112 {
113 ProcessComplexType(schemaComplexType, codeNamespace);
114 }
115 }
116
117 foreach (XmlSchemaObject schemaObject in xmlSchema.Elements.Values)
118 {
119 XmlSchemaElement schemaElement = schemaObject as XmlSchemaElement;
120 if (schemaElement != null)
121 {
122 ProcessElement(schemaElement, codeNamespace);
123 }
124 }
125
126 return codeCompileUnit;
127 }
128
129 /// <summary>
130 /// Processes an XmlSchemaElement into corresponding types.
131 /// </summary>
132 /// <param name="schemaElement">XmlSchemaElement to be processed.</param>
133 /// <param name="codeNamespace">CodeNamespace to be used when outputting code.</param>
134 private static void ProcessElement(XmlSchemaElement schemaElement, CodeNamespace codeNamespace)
135 {
136 string elementType = schemaElement.SchemaTypeName.Name;
137 string elementNamespace = schemaElement.QualifiedName.Namespace;
138 string elementDocumentation = GetDocumentation(schemaElement.Annotation);
139
140 if ((elementType == null || elementType.Length == 0) && schemaElement.SchemaType != null)
141 {
142 ProcessComplexType(schemaElement.Name, elementNamespace, (XmlSchemaComplexType)schemaElement.SchemaType, elementDocumentation, codeNamespace);
143 }
144 else
145 {
146 if (elementType == null || elementType.Length == 0)
147 {
148 elementType = "string";
149 }
150
151 CodeTypeDeclaration typeDeclaration = new CodeTypeDeclaration(schemaElement.Name);
152 typeDeclaration.CustomAttributes.Add(GetGeneratedCodeAttribute());
153 typeDeclaration.Attributes = MemberAttributes.Public;
154 typeDeclaration.IsClass = true;
155
156 if (elementDocumentation != null)
157 {
158 GenerateSummaryComment(typeDeclaration.Comments, elementDocumentation);
159 }
160
161 CodeMemberMethod outputXmlMethod = new CodeMemberMethod();
162 outputXmlMethod.Attributes = MemberAttributes.Public;
163 outputXmlMethod.ImplementationTypes.Add("ISchemaElement");
164 outputXmlMethod.Name = "OutputXml";
165 outputXmlMethod.Parameters.Add(new CodeParameterDeclarationExpression("XmlWriter", "writer"));
166 outputXmlMethod.Statements.Add(GetArgumentNullCheckStatement("writer", false));
167 outputXmlMethod.Statements.Add(new CodeMethodInvokeExpression(new CodeVariableReferenceExpression("writer"), "WriteStartElement", new CodeSnippetExpression(String.Concat("\"", schemaElement.Name, "\"")), new CodeSnippetExpression(String.Concat("\"", elementNamespace, "\""))));
168 GenerateSummaryComment(outputXmlMethod.Comments, outputXmlComment);
169
170 if (simpleTypeNamesToClrTypeNames.ContainsKey(elementType))
171 {
172 CodeMemberField parentField = new CodeMemberField("ISchemaElement", "parentElement");
173 typeDeclaration.Members.Add(parentField);
174
175 CodeMemberProperty parentProperty = new CodeMemberProperty();
176 parentProperty.Attributes = MemberAttributes.Public;
177 parentProperty.ImplementationTypes.Add("ISchemaElement");
178 parentProperty.Name = "ParentElement";
179 parentProperty.Type = new CodeTypeReference("ISchemaElement");
180 parentProperty.GetStatements.Add(new CodeMethodReturnStatement(new CodeFieldReferenceExpression(new CodeThisReferenceExpression(), "parentElement")));
181 parentProperty.SetStatements.Add(new CodeAssignStatement(new CodeFieldReferenceExpression(new CodeThisReferenceExpression(), "parentElement"), new CodeVariableReferenceExpression("value")));
182 typeDeclaration.Members.Add(parentProperty);
183
184 CodeMemberMethod setAttributeMethod = new CodeMemberMethod();
185 setAttributeMethod.Attributes = MemberAttributes.Public;
186 setAttributeMethod.ImplementationTypes.Add("ISetAttributes");
187 setAttributeMethod.Name = "SetAttribute";
188 setAttributeMethod.Parameters.Add(new CodeParameterDeclarationExpression(typeof(string), "name"));
189 setAttributeMethod.Parameters.Add(new CodeParameterDeclarationExpression(typeof(string), "value"));
190 setAttributeMethod.PrivateImplementationType = new CodeTypeReference("ISetAttributes");
191 setAttributeMethod.CustomAttributes.Add(GetCodeAnalysisSuppressionAttribute("Microsoft.Design", "CA1033:InterfaceMethodsShouldBeCallableByChildTypes"));
192 setAttributeMethod.Statements.Add(GetArgumentNullCheckStatement("name", true));
193
194 GenerateFieldAndProperty("Content", (string)simpleTypeNamesToClrTypeNames[elementType], typeDeclaration, outputXmlMethod, setAttributeMethod, null, elementDocumentation, true, false);
195
196 typeDeclaration.Members.Add(setAttributeMethod);
197 typeDeclaration.BaseTypes.Add(new CodeTypeReference("ISetAttributes"));
198 }
199 else
200 {
201 typeDeclaration.BaseTypes.Add(elementType);
202 outputXmlMethod.Statements.Add(new CodeMethodInvokeExpression(new CodeBaseReferenceExpression(), "OutputXml", new CodeVariableReferenceExpression("writer")));
203 outputXmlMethod.Attributes |= MemberAttributes.Override;
204 }
205
206 outputXmlMethod.Statements.Add(new CodeMethodInvokeExpression(new CodeVariableReferenceExpression("writer"), "WriteEndElement"));
207
208 typeDeclaration.BaseTypes.Add(new CodeTypeReference("ISchemaElement"));
209 typeDeclaration.Members.Add(outputXmlMethod);
210 codeNamespace.Types.Add(typeDeclaration);
211 }
212 }
213
214 /// <summary>
215 /// Processes an XmlSchemaComplexType into corresponding types.
216 /// </summary>
217 /// <param name="complexType">XmlSchemaComplexType to be processed.</param>
218 /// <param name="codeNamespace">CodeNamespace to be used when outputting code.</param>
219 private static void ProcessComplexType(XmlSchemaComplexType complexType, CodeNamespace codeNamespace)
220 {
221 CodeMemberMethod outputXmlMethod = new CodeMemberMethod();
222 outputXmlMethod.Attributes = MemberAttributes.Public;
223 outputXmlMethod.ImplementationTypes.Add("ISchemaElement");
224 outputXmlMethod.Name = "OutputXml";
225 outputXmlMethod.Parameters.Add(new CodeParameterDeclarationExpression("XmlWriter", "writer"));
226 outputXmlMethod.Statements.Add(GetArgumentNullCheckStatement("writer", false));
227 GenerateSummaryComment(outputXmlMethod.Comments, outputXmlComment);
228
229 CodeMemberMethod setAttributeMethod = new CodeMemberMethod();
230 setAttributeMethod.Attributes = MemberAttributes.Public;
231 setAttributeMethod.ImplementationTypes.Add("ISetAttributes");
232 setAttributeMethod.Name = "SetAttribute";
233 setAttributeMethod.Parameters.Add(new CodeParameterDeclarationExpression(typeof(string), "name"));
234 setAttributeMethod.Parameters.Add(new CodeParameterDeclarationExpression(typeof(string), "value"));
235 setAttributeMethod.PrivateImplementationType = new CodeTypeReference("ISetAttributes");
236 setAttributeMethod.CustomAttributes.Add(GetCodeAnalysisSuppressionAttribute("Microsoft.Design", "CA1033:InterfaceMethodsShouldBeCallableByChildTypes"));
237 setAttributeMethod.Statements.Add(GetArgumentNullCheckStatement("name", true));
238
239 string documentation = GetDocumentation(complexType.Annotation);
240
241 ProcessSimpleContent(complexType.Name, (XmlSchemaSimpleContentExtension)complexType.ContentModel.Content, documentation, codeNamespace, outputXmlMethod, setAttributeMethod, true);
242 }
243
244 /// <summary>
245 /// Processes an XmlSchemaComplexType into corresponding types.
246 /// </summary>
247 /// <param name="typeName">Name to use for the type being output.</param>
248 /// <param name="elementNamespace">Namespace of the xml element.</param>
249 /// <param name="complexType">XmlSchemaComplexType to be processed.</param>
250 /// <param name="documentation">Documentation for the element.</param>
251 /// <param name="codeNamespace">CodeNamespace to be used when outputting code.</param>
252 private static void ProcessComplexType(string typeName, string elementNamespace, XmlSchemaComplexType complexType, string documentation, CodeNamespace codeNamespace)
253 {
254 CodeMemberMethod outputXmlMethod = new CodeMemberMethod();
255 outputXmlMethod.Attributes = MemberAttributes.Public;
256 outputXmlMethod.ImplementationTypes.Add("ISchemaElement");
257 outputXmlMethod.Name = "OutputXml";
258 outputXmlMethod.Parameters.Add(new CodeParameterDeclarationExpression("XmlWriter", "writer"));
259 outputXmlMethod.Statements.Add(GetArgumentNullCheckStatement("writer", false));
260 outputXmlMethod.Statements.Add(new CodeMethodInvokeExpression(new CodeVariableReferenceExpression("writer"), "WriteStartElement", new CodeSnippetExpression(String.Concat("\"", typeName, "\"")), new CodeSnippetExpression(String.Concat("\"", elementNamespace, "\""))));
261 GenerateSummaryComment(outputXmlMethod.Comments, outputXmlComment);
262
263 CodeMemberMethod setAttributeMethod = new CodeMemberMethod();
264 setAttributeMethod.Attributes = MemberAttributes.Public;
265 setAttributeMethod.ImplementationTypes.Add("ISetAttributes");
266 setAttributeMethod.Name = "SetAttribute";
267 setAttributeMethod.Parameters.Add(new CodeParameterDeclarationExpression(typeof(string), "name"));
268 setAttributeMethod.Parameters.Add(new CodeParameterDeclarationExpression(typeof(string), "value"));
269 setAttributeMethod.PrivateImplementationType = new CodeTypeReference("ISetAttributes");
270 setAttributeMethod.CustomAttributes.Add(GetCodeAnalysisSuppressionAttribute("Microsoft.Design", "CA1033:InterfaceMethodsShouldBeCallableByChildTypes"));
271 setAttributeMethod.Statements.Add(GetArgumentNullCheckStatement("name", true));
272
273 if (complexType.ContentModel == null)
274 {
275 CodeTypeDeclaration typeDeclaration = new CodeTypeDeclaration(typeName);
276 typeDeclaration.CustomAttributes.Add(GetGeneratedCodeAttribute());
277 typeDeclaration.Attributes = MemberAttributes.Public;
278 typeDeclaration.IsClass = true;
279 CodeIterationStatement childEnumStatement = null;
280
281 if (documentation != null)
282 {
283 GenerateSummaryComment(typeDeclaration.Comments, documentation);
284 }
285
286 if (complexType.Particle != null)
287 {
288 CodeMemberField childrenField = new CodeMemberField("ElementCollection", "children");
289 typeDeclaration.Members.Add(childrenField);
290
291 CodeMemberProperty childrenProperty = new CodeMemberProperty();
292 childrenProperty.Attributes = MemberAttributes.Public;
293 childrenProperty.ImplementationTypes.Add("IParentElement");
294 childrenProperty.Name = "Children";
295 childrenProperty.Type = new CodeTypeReference("IEnumerable");
296 childrenProperty.GetStatements.Add(new CodeMethodReturnStatement(new CodeFieldReferenceExpression(new CodeThisReferenceExpression(), "children")));
297 typeDeclaration.Members.Add(childrenProperty);
298
299 CodeMemberProperty filterChildrenProperty = new CodeMemberProperty();
300 filterChildrenProperty.Attributes = MemberAttributes.Public;
301 filterChildrenProperty.ImplementationTypes.Add("IParentElement");
302 filterChildrenProperty.Name = "Item";
303 filterChildrenProperty.Parameters.Add(new CodeParameterDeclarationExpression(typeof(Type), "childType"));
304 filterChildrenProperty.Type = new CodeTypeReference("IEnumerable");
305 filterChildrenProperty.GetStatements.Add(new CodeMethodReturnStatement(new CodeMethodInvokeExpression(new CodeFieldReferenceExpression(new CodeThisReferenceExpression(), "children"), "Filter", new CodeVariableReferenceExpression("childType"))));
306 filterChildrenProperty.CustomAttributes.Add(GetCodeAnalysisSuppressionAttribute("Microsoft.Design", "CA1043:UseIntegralOrStringArgumentForIndexers"));
307 typeDeclaration.Members.Add(filterChildrenProperty);
308
309 CodeMemberMethod addChildMethod = new CodeMemberMethod();
310 addChildMethod.Attributes = MemberAttributes.Public;
311 addChildMethod.ImplementationTypes.Add("IParentElement");
312 addChildMethod.Name = "AddChild";
313 addChildMethod.Parameters.Add(new CodeParameterDeclarationExpression("ISchemaElement", "child"));
314 addChildMethod.Statements.Add(GetArgumentNullCheckStatement("child", false));
315 CodeExpressionStatement addChildStatement = new CodeExpressionStatement(new CodeMethodInvokeExpression(new CodeFieldReferenceExpression(new CodeThisReferenceExpression(), "children"), "AddElement", new CodeVariableReferenceExpression("child")));
316 addChildMethod.Statements.Add(addChildStatement);
317 CodeAssignStatement setParentStatement = new CodeAssignStatement(new CodePropertyReferenceExpression(new CodeVariableReferenceExpression("child"), "ParentElement"), new CodeThisReferenceExpression());
318 addChildMethod.Statements.Add(setParentStatement);
319 typeDeclaration.Members.Add(addChildMethod);
320
321 CodeMemberMethod removeChildMethod = new CodeMemberMethod();
322 removeChildMethod.Attributes = MemberAttributes.Public;
323 removeChildMethod.ImplementationTypes.Add("IParentElement");
324 removeChildMethod.Name = "RemoveChild";
325 removeChildMethod.Parameters.Add(new CodeParameterDeclarationExpression("ISchemaElement", "child"));
326 removeChildMethod.Statements.Add(GetArgumentNullCheckStatement("child", false));
327 CodeExpressionStatement removeChildStatement = new CodeExpressionStatement(new CodeMethodInvokeExpression(new CodeFieldReferenceExpression(new CodeThisReferenceExpression(), "children"), "RemoveElement", new CodeVariableReferenceExpression("child")));
328 removeChildMethod.Statements.Add(removeChildStatement);
329 CodeAssignStatement nullParentStatement = new CodeAssignStatement(new CodePropertyReferenceExpression(new CodeVariableReferenceExpression("child"), "ParentElement"), new CodePrimitiveExpression(null));
330 removeChildMethod.Statements.Add(nullParentStatement);
331 typeDeclaration.Members.Add(removeChildMethod);
332
333 CodeMemberMethod createChildMethod = new CodeMemberMethod();
334 createChildMethod.Attributes = MemberAttributes.Public;
335 createChildMethod.ImplementationTypes.Add("ICreateChildren");
336 createChildMethod.Name = "CreateChild";
337 createChildMethod.Parameters.Add(new CodeParameterDeclarationExpression(typeof(string), "childName"));
338 createChildMethod.PrivateImplementationType = new CodeTypeReference("ICreateChildren");
339 createChildMethod.CustomAttributes.Add(GetCodeAnalysisSuppressionAttribute("Microsoft.Design", "CA1033:InterfaceMethodsShouldBeCallableByChildTypes"));
340 createChildMethod.ReturnType = new CodeTypeReference("ISchemaElement");
341 createChildMethod.Statements.Add(GetArgumentNullCheckStatement("childName", true));
342 createChildMethod.Statements.Add(new CodeVariableDeclarationStatement("ISchemaElement", "childValue", new CodePrimitiveExpression(null)));
343
344 CodeConstructor typeConstructor = new CodeConstructor();
345 typeConstructor.Attributes = MemberAttributes.Public;
346
347 CodeVariableReferenceExpression collectionVariable = null;
348
349 XmlSchemaChoice schemaChoice = complexType.Particle as XmlSchemaChoice;
350 if (schemaChoice != null)
351 {
352 collectionVariable = ProcessSchemaGroup(schemaChoice, typeConstructor, createChildMethod);
353 }
354 else
355 {
356 XmlSchemaSequence schemaSequence = complexType.Particle as XmlSchemaSequence;
357 if (schemaSequence != null)
358 {
359 collectionVariable = ProcessSchemaGroup(schemaSequence, typeConstructor, createChildMethod);
360 }
361 }
362
363 typeConstructor.Statements.Add(new CodeAssignStatement(new CodeFieldReferenceExpression(new CodeThisReferenceExpression(), "children"), collectionVariable));
364 typeDeclaration.Members.Add(typeConstructor);
365
366 CodeConditionStatement childNameNotFound = new CodeConditionStatement();
367 childNameNotFound.Condition = new CodeBinaryOperatorExpression(new CodePrimitiveExpression(null), CodeBinaryOperatorType.ValueEquality, new CodeVariableReferenceExpression("childValue"));
368 childNameNotFound.TrueStatements.Add(new CodeThrowExceptionStatement(new CodeObjectCreateExpression("InvalidOperationException", new CodeMethodInvokeExpression(new CodeTypeReferenceExpression("String"), "Concat", new CodeVariableReferenceExpression("childName"), new CodeSnippetExpression("\" is not a valid child name.\"")))));
369 createChildMethod.Statements.Add(childNameNotFound);
370
371 if (createChildMethod.Statements.Count > 8)
372 {
373 createChildMethod.CustomAttributes.Add(GetCodeAnalysisSuppressionAttribute("Microsoft.Maintainability", "CA1502:AvoidExcessiveComplexity"));
374 }
375
376 createChildMethod.Statements.Add(new CodeMethodReturnStatement(new CodeVariableReferenceExpression("childValue")));
377 typeDeclaration.Members.Add(createChildMethod);
378
379 childEnumStatement = new CodeIterationStatement();
380 childEnumStatement.InitStatement = new CodeVariableDeclarationStatement("IEnumerator", "enumerator", new CodeMethodInvokeExpression(new CodeFieldReferenceExpression(new CodeThisReferenceExpression(), "children"), "GetEnumerator"));
381 childEnumStatement.TestExpression = new CodeMethodInvokeExpression(new CodeVariableReferenceExpression("enumerator"), "MoveNext");
382 childEnumStatement.Statements.Add(new CodeVariableDeclarationStatement("ISchemaElement", "childElement", new CodeCastExpression("ISchemaElement", new CodePropertyReferenceExpression(new CodeVariableReferenceExpression("enumerator"), "Current"))));
383 childEnumStatement.Statements.Add(new CodeMethodInvokeExpression(new CodeVariableReferenceExpression("childElement"), "OutputXml", new CodeVariableReferenceExpression("writer")));
384 childEnumStatement.IncrementStatement = new CodeExpressionStatement(new CodeSnippetExpression(""));
385
386 typeDeclaration.BaseTypes.Add(new CodeTypeReference("IParentElement"));
387 typeDeclaration.BaseTypes.Add(new CodeTypeReference("ICreateChildren"));
388 }
389
390 // TODO: Handle xs:anyAttribute.
391 ProcessAttributes(complexType.Attributes, typeDeclaration, outputXmlMethod, setAttributeMethod);
392
393 if (childEnumStatement != null)
394 {
395 outputXmlMethod.Statements.Add(childEnumStatement);
396 }
397
398 typeDeclaration.BaseTypes.Add(new CodeTypeReference("ISchemaElement"));
399 typeDeclaration.BaseTypes.Add(new CodeTypeReference("ISetAttributes"));
400
401 CodeMemberField parentField = new CodeMemberField("ISchemaElement", "parentElement");
402 typeDeclaration.Members.Add(parentField);
403
404 CodeMemberProperty parentProperty = new CodeMemberProperty();
405 parentProperty.Attributes = MemberAttributes.Public;
406 parentProperty.ImplementationTypes.Add("ISchemaElement");
407 parentProperty.Name = "ParentElement";
408 parentProperty.Type = new CodeTypeReference("ISchemaElement");
409 parentProperty.GetStatements.Add(new CodeMethodReturnStatement(new CodeFieldReferenceExpression(new CodeThisReferenceExpression(), "parentElement")));
410 parentProperty.SetStatements.Add(new CodeAssignStatement(new CodeFieldReferenceExpression(new CodeThisReferenceExpression(), "parentElement"), new CodeVariableReferenceExpression("value")));
411 typeDeclaration.Members.Add(parentProperty);
412
413 if (outputXmlMethod.Statements.Count > 8)
414 {
415 outputXmlMethod.CustomAttributes.Add(GetCodeAnalysisSuppressionAttribute("Microsoft.Maintainability", "CA1502:AvoidExcessiveComplexity"));
416 }
417
418 if (setAttributeMethod.Statements.Count > 8)
419 {
420 setAttributeMethod.CustomAttributes.Add(GetCodeAnalysisSuppressionAttribute("Microsoft.Maintainability", "CA1502:AvoidExcessiveComplexity"));
421 }
422
423 typeDeclaration.Members.Add(outputXmlMethod);
424 typeDeclaration.Members.Add(setAttributeMethod);
425 codeNamespace.Types.Add(typeDeclaration);
426 }
427 else
428 {
429 ProcessSimpleContent(typeName, (XmlSchemaSimpleContentExtension)complexType.ContentModel.Content, documentation, codeNamespace, outputXmlMethod, setAttributeMethod, false);
430 }
431
432 outputXmlMethod.Statements.Add(new CodeMethodInvokeExpression(new CodeVariableReferenceExpression("writer"), "WriteEndElement"));
433 }
434
435 /// <summary>
436 /// Processes a collection of attributes, generating the required fields and properties.
437 /// </summary>
438 /// <param name="attributes">List of attribute or attributeGroupRef elements being processed.</param>
439 /// <param name="typeDeclaration">CodeTypeDeclaration to be used when outputting code.</param>
440 /// <param name="outputXmlMethod">Member method for the OutputXml method.</param>
441 /// <param name="setAttributeMethod">Member method for the SetAttribute method.</param>
442 private static void ProcessAttributes(XmlSchemaObjectCollection attributes, CodeTypeDeclaration typeDeclaration, CodeMemberMethod outputXmlMethod, CodeMemberMethod setAttributeMethod)
443 {
444 foreach (XmlSchemaObject schemaObject in attributes)
445 {
446 XmlSchemaAttribute schemaAttribute = schemaObject as XmlSchemaAttribute;
447 if (schemaAttribute != null)
448 {
449 ProcessAttribute(schemaAttribute, typeDeclaration, outputXmlMethod, setAttributeMethod);
450 }
451 else
452 {
453 XmlSchemaAttributeGroupRef schemaAttributeGroupRef = schemaObject as XmlSchemaAttributeGroupRef;
454 if (schemaAttributeGroupRef != null)
455 {
456 XmlSchemaAttributeGroup schemaAttributeGroup = refToAttributeGroups[schemaAttributeGroupRef.RefName.Name];
457 // recurse!
458 ProcessAttributes(schemaAttributeGroup.Attributes, typeDeclaration, outputXmlMethod, setAttributeMethod);
459 }
460 }
461 }
462 }
463
464 /// <summary>
465 /// Processes an XmlSchemaGroupBase element.
466 /// </summary>
467 /// <param name="schemaGroup">Element group to process.</param>
468 /// <param name="constructor">Constructor to which statements should be added.</param>
469 /// <param name="createChildMethod">Method used for creating children on read-in.</param>
470 /// <returns>A reference to the local variable containing the collection.</returns>
471 private static CodeVariableReferenceExpression ProcessSchemaGroup(XmlSchemaGroupBase schemaGroup, CodeConstructor constructor, CodeMemberMethod createChildMethod)
472 {
473 return ProcessSchemaGroup(schemaGroup, constructor, createChildMethod, 0);
474 }
475
476 /// <summary>
477 /// Processes an XmlSchemaGroupBase element.
478 /// </summary>
479 /// <param name="schemaGroup">Element group to process.</param>
480 /// <param name="constructor">Constructor to which statements should be added.</param>
481 /// <param name="createChildMethod">Method used for creating children on read-in.</param>
482 /// <param name="depth">Depth to which this collection is nested.</param>
483 /// <returns>A reference to the local variable containing the collection.</returns>
484 private static CodeVariableReferenceExpression ProcessSchemaGroup(XmlSchemaGroupBase schemaGroup, CodeConstructor constructor, CodeMemberMethod createChildMethod, int depth)
485 {
486 string collectionName = String.Format("childCollection{0}", depth);
487 CodeVariableReferenceExpression collectionVariableReference = new CodeVariableReferenceExpression(collectionName);
488 CodeVariableDeclarationStatement collectionStatement = new CodeVariableDeclarationStatement("ElementCollection", collectionName);
489 if (schemaGroup is XmlSchemaChoice)
490 {
491 collectionStatement.InitExpression = new CodeObjectCreateExpression("ElementCollection", new CodePropertyReferenceExpression(new CodeTypeReferenceExpression("ElementCollection.CollectionType"), "Choice"));
492 }
493 else
494 {
495 collectionStatement.InitExpression = new CodeObjectCreateExpression("ElementCollection", new CodePropertyReferenceExpression(new CodeTypeReferenceExpression("ElementCollection.CollectionType"), "Sequence"));
496 }
497 constructor.Statements.Add(collectionStatement);
498
499 foreach (XmlSchemaObject obj in schemaGroup.Items)
500 {
501 XmlSchemaElement schemaElement = obj as XmlSchemaElement;
502 if (schemaElement != null)
503 {
504 if (schemaGroup is XmlSchemaChoice)
505 {
506 CodeMethodInvokeExpression addItemInvoke = new CodeMethodInvokeExpression(collectionVariableReference, "AddItem", new CodeObjectCreateExpression("ElementCollection.ChoiceItem", new CodeTypeOfExpression(schemaElement.RefName.Name)));
507 constructor.Statements.Add(addItemInvoke);
508 }
509 else
510 {
511 CodeMethodInvokeExpression addItemInvoke = new CodeMethodInvokeExpression(collectionVariableReference, "AddItem", new CodeObjectCreateExpression("ElementCollection.SequenceItem", new CodeTypeOfExpression(schemaElement.RefName.Name)));
512 constructor.Statements.Add(addItemInvoke);
513 }
514
515 CodeConditionStatement createChildIf = new CodeConditionStatement();
516 createChildIf.Condition = new CodeBinaryOperatorExpression(new CodeSnippetExpression(String.Concat("\"", schemaElement.RefName.Name, "\"")), CodeBinaryOperatorType.ValueEquality, new CodeVariableReferenceExpression("childName"));
517 createChildIf.TrueStatements.Add(new CodeAssignStatement(new CodeVariableReferenceExpression("childValue"), new CodeObjectCreateExpression(schemaElement.RefName.Name)));
518 createChildMethod.Statements.Add(createChildIf);
519
520 continue;
521 }
522
523 XmlSchemaAny schemaAny = obj as XmlSchemaAny;
524 if (schemaAny != null)
525 {
526 if (schemaGroup is XmlSchemaChoice)
527 {
528 CodeMethodInvokeExpression addItemInvoke = new CodeMethodInvokeExpression(collectionVariableReference, "AddItem", new CodeObjectCreateExpression("ElementCollection.ChoiceItem", new CodeTypeOfExpression("ISchemaElement")));
529 constructor.Statements.Add(addItemInvoke);
530 }
531 else
532 {
533 CodeMethodInvokeExpression addItemInvoke = new CodeMethodInvokeExpression(collectionVariableReference, "AddItem", new CodeObjectCreateExpression("ElementCollection.SequenceItem", new CodeTypeOfExpression("ISchemaElement"), new CodeSnippetExpression("0"), new CodeSnippetExpression("-1")));
534 constructor.Statements.Add(addItemInvoke);
535 }
536
537 continue;
538 }
539
540 XmlSchemaGroupBase schemaGroupBase = obj as XmlSchemaGroupBase;
541 if (schemaGroupBase != null)
542 {
543 CodeVariableReferenceExpression nestedCollectionReference = ProcessSchemaGroup(schemaGroupBase, constructor, createChildMethod, depth + 1);
544 CodeMethodInvokeExpression addCollectionInvoke = new CodeMethodInvokeExpression(collectionVariableReference, "AddCollection", nestedCollectionReference);
545 constructor.Statements.Add(addCollectionInvoke);
546
547 continue;
548 }
549 }
550
551 return collectionVariableReference;
552 }
553
554 /// <summary>
555 /// Processes an XmlSchemaSimpleContentExtension into corresponding types.
556 /// </summary>
557 /// <param name="typeName">Name of the type being generated.</param>
558 /// <param name="simpleContent">XmlSchemaSimpleContentExtension being processed.</param>
559 /// <param name="documentation">Documentation for the simple content.</param>
560 /// <param name="codeNamespace">CodeNamespace to be used when outputting code.</param>
561 /// <param name="outputXmlMethod">Method to use when outputting Xml.</param>
562 /// <param name="setAttributeMethod">Method to use when setting an attribute.</param>
563 /// <param name="abstractClass">If true, generate an abstract class.</param>
564 private static void ProcessSimpleContent(string typeName, XmlSchemaSimpleContentExtension simpleContent, string documentation, CodeNamespace codeNamespace, CodeMemberMethod outputXmlMethod, CodeMemberMethod setAttributeMethod, bool abstractClass)
565 {
566 CodeTypeDeclaration typeDeclaration = new CodeTypeDeclaration(typeName);
567 typeDeclaration.CustomAttributes.Add(GetGeneratedCodeAttribute());
568 typeDeclaration.Attributes = MemberAttributes.Public;
569 typeDeclaration.IsClass = true;
570
571 if (documentation != null)
572 {
573 GenerateSummaryComment(typeDeclaration.Comments, documentation);
574 }
575
576 if (abstractClass)
577 {
578 typeDeclaration.TypeAttributes = System.Reflection.TypeAttributes.Abstract | System.Reflection.TypeAttributes.Public;
579 }
580
581 // TODO: Handle xs:anyAttribute here.
582 foreach (XmlSchemaAttribute schemaAttribute in simpleContent.Attributes)
583 {
584 ProcessAttribute(schemaAttribute, typeDeclaration, outputXmlMethod, setAttributeMethod);
585 }
586
587 // This needs to come last, so that the generation code generates the inner content after the attributes.
588 string contentDocumentation = GetDocumentation(simpleContent.Annotation);
589 GenerateFieldAndProperty("Content", (string)simpleTypeNamesToClrTypeNames[simpleContent.BaseTypeName.Name], typeDeclaration, outputXmlMethod, setAttributeMethod, null, contentDocumentation, true, false);
590
591 typeDeclaration.BaseTypes.Add(new CodeTypeReference("ISchemaElement"));
592 typeDeclaration.BaseTypes.Add(new CodeTypeReference("ISetAttributes"));
593
594 CodeMemberField parentField = new CodeMemberField("ISchemaElement", "parentElement");
595 typeDeclaration.Members.Add(parentField);
596
597 CodeMemberProperty parentProperty = new CodeMemberProperty();
598 parentProperty.Attributes = MemberAttributes.Public;
599 parentProperty.ImplementationTypes.Add("ISchemaElement");
600 parentProperty.Name = "ParentElement";
601 parentProperty.Type = new CodeTypeReference("ISchemaElement");
602 parentProperty.GetStatements.Add(new CodeMethodReturnStatement(new CodeFieldReferenceExpression(new CodeThisReferenceExpression(), "parentElement")));
603 parentProperty.SetStatements.Add(new CodeAssignStatement(new CodeFieldReferenceExpression(new CodeThisReferenceExpression(), "parentElement"), new CodeVariableReferenceExpression("value")));
604 typeDeclaration.Members.Add(parentProperty);
605
606 if (outputXmlMethod.Statements.Count > 8)
607 {
608 outputXmlMethod.CustomAttributes.Add(GetCodeAnalysisSuppressionAttribute("Microsoft.Maintainability", "CA1502:AvoidExcessiveComplexity"));
609 }
610
611 if (setAttributeMethod.Statements.Count > 8)
612 {
613 setAttributeMethod.CustomAttributes.Add(GetCodeAnalysisSuppressionAttribute("Microsoft.Maintainability", "CA1502:AvoidExcessiveComplexity"));
614 }
615
616 typeDeclaration.Members.Add(outputXmlMethod);
617 typeDeclaration.Members.Add(setAttributeMethod);
618 codeNamespace.Types.Add(typeDeclaration);
619 }
620
621 /// <summary>
622 /// Processes an attribute, generating the required field and property. Potentially generates
623 /// an enum for an attribute restriction.
624 /// </summary>
625 /// <param name="attribute">Attribute element being processed.</param>
626 /// <param name="typeDeclaration">CodeTypeDeclaration to be used when outputting code.</param>
627 /// <param name="outputXmlMethod">Member method for the OutputXml method.</param>
628 /// <param name="setAttributeMethod">Member method for the SetAttribute method.</param>
629 private static void ProcessAttribute(XmlSchemaAttribute attribute, CodeTypeDeclaration typeDeclaration, CodeMemberMethod outputXmlMethod, CodeMemberMethod setAttributeMethod)
630 {
631 string attributeName = attribute.QualifiedName.Name;
632 string rawAttributeType = attribute.AttributeSchemaType.QualifiedName.Name;
633 string attributeType = null;
634 EnumDeclaration enumDeclaration = null;
635 if (rawAttributeType == null || rawAttributeType.Length == 0)
636 {
637 ProcessSimpleType(attributeName, attribute.AttributeSchemaType, true, out enumDeclaration, out attributeType);
638
639 if (enumDeclaration != null)
640 {
641 typeDeclaration.Members.Add(enumDeclaration.TypeDeclaration);
642 AddEnumHelperMethods(enumDeclaration, typeDeclaration);
643 }
644 }
645 else
646 {
647 attributeType = (string)simpleTypeNamesToClrTypeNames[rawAttributeType];
648 }
649
650 string documentation = GetDocumentation(attribute.Annotation);
651
652 // TODO: Handle required fields.
653 GenerateFieldAndProperty(attributeName, attributeType, typeDeclaration, outputXmlMethod, setAttributeMethod, enumDeclaration, documentation, false, false);
654 }
655
656 /// <summary>
657 /// Gets the first sentence of a documentation element and returns it as a string.
658 /// </summary>
659 /// <param name="annotation">The annotation in which to look for a documentation element.</param>
660 /// <returns>The string representing the first sentence, or null if none found.</returns>
661 private static string GetDocumentation(XmlSchemaAnnotation annotation)
662 {
663 string documentation = null;
664
665 if (annotation != null && annotation.Items != null)
666 {
667 foreach (XmlSchemaObject obj in annotation.Items)
668 {
669 XmlSchemaDocumentation schemaDocumentation = obj as XmlSchemaDocumentation;
670 if (schemaDocumentation != null)
671 {
672 if (schemaDocumentation.Markup.Length > 0)
673 {
674 XmlText text = schemaDocumentation.Markup[0] as XmlText;
675 if (text != null)
676 {
677 documentation = text.Value;
678 }
679 }
680 break;
681 }
682 }
683 }
684
685 if (documentation != null)
686 {
687 documentation = documentation.Trim();
688 }
689 return documentation;
690 }
691
692 /// <summary>
693 /// Makes a valid enum value out of the passed in value. May remove spaces, add 'Item' to the
694 /// start if it begins with an integer, or strip out punctuation.
695 /// </summary>
696 /// <param name="enumValue">Enum value to be processed.</param>
697 /// <returns>Enum value with invalid characters removed.</returns>
698 private static string MakeEnumValue(string enumValue)
699 {
700 if (Char.IsDigit(enumValue[0]))
701 {
702 enumValue = String.Concat("Item", enumValue);
703 }
704
705 StringBuilder newValue = new StringBuilder();
706 for (int i = 0; i < enumValue.Length; ++i)
707 {
708 if (!Char.IsPunctuation(enumValue[i]) && !Char.IsSymbol(enumValue[i]) && !Char.IsWhiteSpace(enumValue[i]))
709 {
710 newValue.Append(enumValue[i]);
711 }
712 }
713
714 return newValue.ToString();
715 }
716
717 /// <summary>
718 /// Generates the private field and public property for a piece of data.
719 /// </summary>
720 /// <param name="propertyName">Name of the property being generated.</param>
721 /// <param name="typeName">Name of the type for the property.</param>
722 /// <param name="typeDeclaration">Type declaration into which the field and property should be placed.</param>
723 /// <param name="outputXmlMethod">Member method for the OutputXml method.</param>
724 /// <param name="setAttributeMethod">Member method for the SetAttribute method.</param>
725 /// <param name="enumDeclaration">EnumDeclaration, which is null unless called from a locally defined enum attribute.</param>
726 /// <param name="documentation">Comment string to be placed on the property.</param>
727 /// <param name="nestedContent">If true, the field will be placed in nested content when outputting to XML.</param>
728 /// <param name="requiredField">If true, the generated serialization code will throw if the field is not set.</param>
729 private static void GenerateFieldAndProperty(string propertyName, string typeName, CodeTypeDeclaration typeDeclaration, CodeMemberMethod outputXmlMethod, CodeMemberMethod setAttributeMethod, EnumDeclaration enumDeclaration, string documentation, bool nestedContent, bool requiredField)
730 {
731 string fieldName = String.Concat(propertyName.Substring(0, 1).ToLower(), propertyName.Substring(1), "Field");
732 string fieldNameSet = String.Concat(fieldName, "Set");
733 Type type = GetClrTypeByXmlName(typeName);
734 CodeMemberField fieldMember;
735 if (type == null)
736 {
737 fieldMember = new CodeMemberField(typeName, fieldName);
738 }
739 else
740 {
741 fieldMember = new CodeMemberField(type, fieldName);
742 }
743 fieldMember.Attributes = MemberAttributes.Private;
744 typeDeclaration.Members.Add(fieldMember);
745 typeDeclaration.Members.Add(new CodeMemberField(typeof(bool), fieldNameSet));
746
747 CodeMemberProperty propertyMember = new CodeMemberProperty();
748 propertyMember.Attributes = MemberAttributes.Public | MemberAttributes.Final;
749 if (documentation != null)
750 {
751 GenerateSummaryComment(propertyMember.Comments, documentation);
752 }
753 propertyMember.Name = propertyName;
754 if (type == null)
755 {
756 propertyMember.Type = new CodeTypeReference(typeName);
757 }
758 else
759 {
760 propertyMember.Type = new CodeTypeReference(type);
761 }
762
763 if (propertyMember.Name.StartsWith("src"))
764 {
765 propertyMember.CustomAttributes.Add(GetCodeAnalysisSuppressionAttribute("Microsoft.Naming", "CA1709:IdentifiersShouldBeCasedCorrectly"));
766 }
767 else if (StronglyTypedClasses.multiUppercaseNameRegex.Match(propertyMember.Name).Success)
768 {
769 propertyMember.CustomAttributes.Add(GetCodeAnalysisSuppressionAttribute("Microsoft.Naming", "CA1705:LongAcronymsShouldBePascalCased"));
770 }
771
772 CodeMethodReturnStatement returnStatement = new CodeMethodReturnStatement();
773 returnStatement.Expression = new CodeFieldReferenceExpression(new CodeThisReferenceExpression(), fieldName);
774 propertyMember.GetStatements.Add(returnStatement);
775
776 CodeAssignStatement assignmentStatement = new CodeAssignStatement();
777 propertyMember.SetStatements.Add(new CodeAssignStatement(new CodeFieldReferenceExpression(new CodeThisReferenceExpression(), fieldNameSet), new CodePrimitiveExpression(true)));
778 assignmentStatement.Left = new CodeFieldReferenceExpression(new CodeThisReferenceExpression(), fieldName);
779 assignmentStatement.Right = new CodePropertySetValueReferenceExpression();
780 propertyMember.SetStatements.Add(assignmentStatement);
781
782 CodeConditionStatement fieldSetStatement = new CodeConditionStatement();
783 fieldSetStatement.Condition = new CodeFieldReferenceExpression(new CodeThisReferenceExpression(), fieldNameSet);
784
785 CodeAssignStatement fieldSetAttrStatement = new CodeAssignStatement();
786 fieldSetAttrStatement.Left = new CodeFieldReferenceExpression(new CodeThisReferenceExpression(), fieldNameSet);
787 fieldSetAttrStatement.Right = new CodePrimitiveExpression(true);
788
789 CodeConditionStatement attributeNameMatchStatement = new CodeConditionStatement();
790 attributeNameMatchStatement.Condition = new CodeBinaryOperatorExpression(new CodeSnippetExpression(String.Concat("\"", propertyName, "\"")), CodeBinaryOperatorType.IdentityEquality, new CodeVariableReferenceExpression("name"));
791
792 string clrTypeName = (string)simpleTypeNamesToClrTypeNames[typeName];
793 switch (clrTypeName)
794 {
795 case "string":
796 if (nestedContent)
797 {
798 fieldSetStatement.TrueStatements.Add(new CodeMethodInvokeExpression(new CodeVariableReferenceExpression("writer"), "WriteString", new CodeFieldReferenceExpression(new CodeThisReferenceExpression(), fieldName)));
799 attributeNameMatchStatement.TrueStatements.Add(new CodeAssignStatement(new CodeFieldReferenceExpression(new CodeThisReferenceExpression(), fieldName), new CodeVariableReferenceExpression("value")));
800 }
801 else
802 {
803 fieldSetStatement.TrueStatements.Add(new CodeMethodInvokeExpression(new CodeVariableReferenceExpression("writer"), "WriteAttributeString", new CodeSnippetExpression(String.Concat("\"", propertyName, "\"")), new CodeFieldReferenceExpression(new CodeThisReferenceExpression(), fieldName)));
804 attributeNameMatchStatement.TrueStatements.Add(new CodeAssignStatement(new CodeFieldReferenceExpression(new CodeThisReferenceExpression(), fieldName), new CodeVariableReferenceExpression("value")));
805 }
806 break;
807 case "bool":
808 if (nestedContent)
809 {
810 fieldSetStatement.TrueStatements.Add(new CodeMethodInvokeExpression(new CodeVariableReferenceExpression("writer"), "WriteString", new CodeMethodInvokeExpression(new CodeFieldReferenceExpression(new CodeThisReferenceExpression(), fieldName), "ToString", new CodePropertyReferenceExpression(new CodeTypeReferenceExpression("CultureInfo"), "InvariantCulture"))));
811 }
812 else
813 {
814 fieldSetStatement.TrueStatements.Add(new CodeMethodInvokeExpression(new CodeVariableReferenceExpression("writer"), "WriteAttributeString", new CodeSnippetExpression(String.Concat("\"", propertyName, "\"")), new CodeMethodInvokeExpression(new CodeFieldReferenceExpression(new CodeThisReferenceExpression(), fieldName), "ToString", new CodePropertyReferenceExpression(new CodeTypeReferenceExpression("CultureInfo"), "InvariantCulture"))));
815 }
816 attributeNameMatchStatement.TrueStatements.Add(new CodeAssignStatement(new CodeFieldReferenceExpression(new CodeThisReferenceExpression(), fieldName), new CodeMethodInvokeExpression(new CodeTypeReferenceExpression("Convert"), "ToBoolean", new CodeVariableReferenceExpression("value"), new CodePropertyReferenceExpression(new CodeTypeReferenceExpression("CultureInfo"), "InvariantCulture"))));
817 break;
818 case "int":
819 case "long":
820 if (nestedContent)
821 {
822 fieldSetStatement.TrueStatements.Add(new CodeMethodInvokeExpression(new CodeVariableReferenceExpression("writer"), "WriteString", new CodeMethodInvokeExpression(new CodeFieldReferenceExpression(new CodeThisReferenceExpression(), fieldName), "ToString", new CodePropertyReferenceExpression(new CodeTypeReferenceExpression("CultureInfo"), "InvariantCulture"))));
823 }
824 else
825 {
826 fieldSetStatement.TrueStatements.Add(new CodeMethodInvokeExpression(new CodeVariableReferenceExpression("writer"), "WriteAttributeString", new CodeSnippetExpression(String.Concat("\"", propertyName, "\"")), new CodeMethodInvokeExpression(new CodeFieldReferenceExpression(new CodeThisReferenceExpression(), fieldName), "ToString", new CodePropertyReferenceExpression(new CodeTypeReferenceExpression("CultureInfo"), "InvariantCulture"))));
827 }
828 attributeNameMatchStatement.TrueStatements.Add(new CodeAssignStatement(new CodeFieldReferenceExpression(new CodeThisReferenceExpression(), fieldName), new CodeMethodInvokeExpression(new CodeTypeReferenceExpression("Convert"), "ToInt32", new CodeVariableReferenceExpression("value"), new CodePropertyReferenceExpression(new CodeTypeReferenceExpression("CultureInfo"), "InvariantCulture"))));
829 break;
830 default:
831 if (typeName == "DateTime")
832 {
833 if (nestedContent)
834 {
835 fieldSetStatement.TrueStatements.Add(new CodeMethodInvokeExpression(new CodeVariableReferenceExpression("writer"), "WriteString", new CodeMethodInvokeExpression(new CodeFieldReferenceExpression(new CodeThisReferenceExpression(), fieldName), "ToString", new CodePropertyReferenceExpression(new CodeTypeReferenceExpression("CultureInfo"), "InvariantCulture"))));
836 }
837 else
838 {
839 fieldSetStatement.TrueStatements.Add(new CodeMethodInvokeExpression(new CodeVariableReferenceExpression("writer"), "WriteAttributeString", new CodeSnippetExpression(String.Concat("\"", propertyName, "\"")), new CodeMethodInvokeExpression(new CodeFieldReferenceExpression(new CodeThisReferenceExpression(), fieldName), "ToString", new CodePrimitiveExpression("yyyy-MM-ddTHH:mm:ss"), new CodePropertyReferenceExpression(new CodePropertyReferenceExpression(new CodeTypeReferenceExpression("CultureInfo"), "InvariantCulture"), "DateTimeFormat"))));
840 }
841 attributeNameMatchStatement.TrueStatements.Add(new CodeAssignStatement(new CodeFieldReferenceExpression(new CodeThisReferenceExpression(), fieldName), new CodeMethodInvokeExpression(new CodeTypeReferenceExpression("Convert"), "ToDateTime", new CodeVariableReferenceExpression("value"), new CodePropertyReferenceExpression(new CodeTypeReferenceExpression("CultureInfo"), "InvariantCulture"))));
842 break;
843 }
844
845 if (enumDeclaration == null)
846 {
847 GenerateOutputForEnum(fieldSetStatement, attributeNameMatchStatement, typeNamesToEnumDeclarations[typeName], fieldName, propertyName);
848 }
849 else
850 {
851 GenerateOutputForEnum(fieldSetStatement, attributeNameMatchStatement, enumDeclaration, fieldName, propertyName);
852 }
853 break;
854 }
855
856 attributeNameMatchStatement.TrueStatements.Add(fieldSetAttrStatement);
857
858 // TODO: Add throw to falseStatements if required field not set.
859 outputXmlMethod.Statements.Add(fieldSetStatement);
860 setAttributeMethod.Statements.Add(attributeNameMatchStatement);
861
862 typeDeclaration.Members.Add(propertyMember);
863 }
864
865 /// <summary>
866 /// Generates output for an enum type. Will generate a switch statement for normal enums, and if statements
867 /// for a flags enum.
868 /// </summary>
869 /// <param name="fieldSetStatement">If statement to add statements to.</param>
870 /// <param name="attributeNameMatchStatement">If statement to add statements to.</param>
871 /// <param name="enumDeclaration">Enum declaration for this field. Could be locally defined enum or global.</param>
872 /// <param name="fieldName">Name of the private field.</param>
873 /// <param name="propertyName">Name of the property (and XML attribute).</param>
874 private static void GenerateOutputForEnum(CodeConditionStatement fieldSetStatement, CodeConditionStatement attributeNameMatchStatement, EnumDeclaration enumDeclaration, string fieldName, string propertyName)
875 {
876 CodeTypeDeclaration enumParent = enumsToParseMethodClasses[enumDeclaration];
877
878 if (enumDeclaration.Flags)
879 {
880 CodeVariableDeclarationStatement outputValueVariable = new CodeVariableDeclarationStatement(typeof(string), "outputValue", new CodeSnippetExpression("\"\""));
881 fieldSetStatement.TrueStatements.Add(outputValueVariable);
882
883 foreach (string key in enumDeclaration.Values)
884 {
885 CodeConditionStatement enumIfStatement = new CodeConditionStatement();
886 enumIfStatement.Condition = new CodeBinaryOperatorExpression(new CodeBinaryOperatorExpression(new CodeFieldReferenceExpression(new CodeThisReferenceExpression(), fieldName), CodeBinaryOperatorType.BitwiseAnd, new CodePropertyReferenceExpression(new CodeSnippetExpression(enumDeclaration.Name), MakeEnumValue(key))), CodeBinaryOperatorType.IdentityInequality, new CodeSnippetExpression("0"));
887 CodeConditionStatement lengthIfStatement = new CodeConditionStatement();
888 lengthIfStatement.Condition = new CodeBinaryOperatorExpression(new CodePropertyReferenceExpression(new CodeVariableReferenceExpression("outputValue"), "Length"), CodeBinaryOperatorType.IdentityInequality, new CodeSnippetExpression("0"));
889 lengthIfStatement.TrueStatements.Add(new CodeAssignStatement(new CodeVariableReferenceExpression("outputValue"), new CodeBinaryOperatorExpression(new CodeVariableReferenceExpression("outputValue"), CodeBinaryOperatorType.Add, new CodeSnippetExpression("\" \""))));
890 enumIfStatement.TrueStatements.Add(lengthIfStatement);
891 enumIfStatement.TrueStatements.Add(new CodeAssignStatement(new CodeVariableReferenceExpression("outputValue"), new CodeBinaryOperatorExpression(new CodeVariableReferenceExpression("outputValue"), CodeBinaryOperatorType.Add, new CodeSnippetExpression(String.Concat("\"", key, "\"")))));
892 fieldSetStatement.TrueStatements.Add(enumIfStatement);
893 }
894
895 attributeNameMatchStatement.TrueStatements.Add(new CodeMethodInvokeExpression(
896 new CodeTypeReferenceExpression(enumParent.Name),
897 String.Concat("TryParse", enumDeclaration.Name),
898 new CodeVariableReferenceExpression("value"),
899 new CodeDirectionExpression(FieldDirection.Out, new CodeFieldReferenceExpression(new CodeThisReferenceExpression(), fieldName))));
900
901 fieldSetStatement.TrueStatements.Add(new CodeMethodInvokeExpression(new CodeVariableReferenceExpression("writer"), "WriteAttributeString", new CodeSnippetExpression(String.Concat("\"", propertyName, "\"")), new CodeSnippetExpression(String.Concat("outputValue"))));
902 }
903 else
904 {
905 foreach (string key in enumDeclaration.Values)
906 {
907 CodeConditionStatement enumOutStatement = new CodeConditionStatement();
908 enumOutStatement.Condition = new CodeBinaryOperatorExpression(new CodeFieldReferenceExpression(new CodeThisReferenceExpression(), fieldName), CodeBinaryOperatorType.ValueEquality, new CodePropertyReferenceExpression(new CodeSnippetExpression(enumDeclaration.Name), MakeEnumValue(key)));
909 enumOutStatement.TrueStatements.Add(new CodeMethodInvokeExpression(new CodeVariableReferenceExpression("writer"), "WriteAttributeString", new CodeSnippetExpression(String.Concat("\"", propertyName, "\"")), new CodeSnippetExpression(String.Concat("\"", key, "\""))));
910 fieldSetStatement.TrueStatements.Add(enumOutStatement);
911 }
912
913 attributeNameMatchStatement.TrueStatements.Add(new CodeAssignStatement(
914 new CodeFieldReferenceExpression(new CodeThisReferenceExpression(), fieldName),
915 new CodeMethodInvokeExpression(new CodeTypeReferenceExpression(enumParent.Name),
916 String.Concat("Parse", enumDeclaration.Name),
917 new CodeVariableReferenceExpression("value"))));
918 }
919 }
920
921 /// <summary>
922 /// Generates a summary comment.
923 /// </summary>
924 /// <param name="comments">Comments collection to add the comments to.</param>
925 /// <param name="content">Content of the comment.</param>
926 private static void GenerateSummaryComment(CodeCommentStatementCollection comments, string content)
927 {
928 using (StringWriter sw = new StringWriter())
929 {
930 XmlTextWriter writer = null;
931
932 // create the comment as xml to ensure proper escaping of special xml characters
933 try
934 {
935 writer = new XmlTextWriter(sw);
936 writer.Indentation = 0;
937
938 writer.WriteStartElement("summary");
939 writer.WriteString(Environment.NewLine);
940
941 string nextComment;
942 int newlineIndex = content.IndexOf(Environment.NewLine);
943 int offset = 0;
944 while (newlineIndex != -1)
945 {
946 nextComment = content.Substring(offset, newlineIndex - offset).Trim();
947 writer.WriteString(nextComment);
948 writer.WriteString(Environment.NewLine);
949 offset = newlineIndex + Environment.NewLine.Length;
950 newlineIndex = content.IndexOf(Environment.NewLine, offset);
951 }
952 nextComment = content.Substring(offset).Trim();
953 writer.WriteString(nextComment);
954 writer.WriteString(Environment.NewLine);
955
956 writer.WriteEndElement();
957 }
958 finally
959 {
960 if (null != writer)
961 {
962 writer.Close();
963 }
964 }
965
966 // create the comment statements (one per line of xml)
967 using (StringReader sr = new StringReader(sw.ToString()))
968 {
969 string line;
970
971 while (null != (line = sr.ReadLine()))
972 {
973 comments.Add(new CodeCommentStatement(line, true));
974 }
975 }
976 }
977 }
978
979 /// <summary>
980 /// Gets the CLR type for simple XML type.
981 /// </summary>
982 /// <param name="typeName">Plain text name of type.</param>
983 /// <returns>Type corresponding to parameter.</returns>
984 private static Type GetClrTypeByXmlName(string typeName)
985 {
986 switch (typeName)
987 {
988 case "bool":
989 return typeof(bool);
990 case "int":
991 return typeof(int);
992 case "long":
993 return typeof(long);
994 case "string":
995 return typeof(string);
996 default:
997 return null;
998 }
999 }
1000
1001 /// <summary>
1002 /// Processes an XmlSchemaSimpleType into corresponding types.
1003 /// </summary>
1004 /// <param name="simpleType">XmlSchemaSimpleType to be processed.</param>
1005 /// <param name="codeNamespace">CodeNamespace to be used when outputting code.</param>
1006 private static void ProcessSimpleType(XmlSchemaSimpleType simpleType, CodeNamespace codeNamespace)
1007 {
1008 EnumDeclaration enumDeclaration;
1009 string simpleTypeName = simpleType.Name;
1010 string baseTypeName;
1011
1012 ProcessSimpleType(simpleTypeName, simpleType, false, out enumDeclaration, out baseTypeName);
1013
1014 simpleTypeNamesToClrTypeNames.Add(simpleTypeName, baseTypeName);
1015
1016 if (enumDeclaration != null)
1017 {
1018 codeNamespace.Types.Add(enumDeclaration.TypeDeclaration);
1019 typeNamesToEnumDeclarations.Add(simpleTypeName, enumDeclaration);
1020 AddEnumHelperMethods(enumDeclaration, codeNamespace);
1021 }
1022 }
1023
1024 /// <summary>
1025 /// Processes an XmlSchemaSimpleType into corresponding code.
1026 /// </summary>
1027 /// <param name="simpleTypeName">Name for the type.</param>
1028 /// <param name="simpleType">XmlSchemaSimpleType to be processed.</param>
1029 /// <param name="codeNamespace">CodeNamespace to be used when outputting code for global types.</param>
1030 /// <param name="parentTypeDeclaration">CodeTypeDeclaration to be used when outputting code for nested types.</param>
1031 /// <param name="outputXmlMethod">Member method for the OutputXml method for nested types.</param>
1032 /// <param name="setAttributeMethod">Member method for the SetAttribute method for nested types.</param>
1033 private static void ProcessSimpleType(string simpleTypeName, XmlSchemaSimpleType simpleType, bool nestedType, out EnumDeclaration enumDeclaration, out string baseTypeName)
1034 {
1035 enumDeclaration = null;
1036 baseTypeName = null;
1037
1038 // XSD supports simpleTypes derived by union, list, or restriction; restrictions can have any
1039 // combination of pattern, enumeration, length, and more; lists can contain any other simpleType.
1040 // XsdGen, in contrast, only supports a limited set of values...
1041 // Unions are weakly supported by just using the first member type
1042 // restrictions must either be all enumeration or a single pattern, a list must be of a
1043 // single simpleType which itself is only a restriction of enumeration.
1044 if (simpleType.Content is XmlSchemaSimpleTypeUnion)
1045 {
1046 XmlSchemaSimpleTypeUnion union = simpleType.Content as XmlSchemaSimpleTypeUnion;
1047 if (union.MemberTypes.Length > 0)
1048 {
1049 baseTypeName = union.MemberTypes[0].Name;
1050 return;
1051 }
1052 else
1053 {
1054 baseTypeName = "string";
1055 return;
1056 }
1057 }
1058
1059 bool listType = false; // XSD lists become [Flag]enums in C#...
1060 XmlSchemaSimpleTypeList simpleTypeList = simpleType.Content as XmlSchemaSimpleTypeList;
1061 XmlSchemaSimpleTypeRestriction simpleTypeRestriction = simpleType.Content as XmlSchemaSimpleTypeRestriction;
1062
1063 if (simpleTypeList != null)
1064 {
1065 baseTypeName = simpleTypeList.ItemTypeName.Name;
1066
1067 if (String.IsNullOrEmpty(baseTypeName))
1068 {
1069 simpleTypeRestriction = simpleTypeList.ItemType.Content as XmlSchemaSimpleTypeRestriction;
1070 if (simpleTypeRestriction == null)
1071 {
1072 string appName = typeof(XsdGen).Assembly.GetName().Name;
1073 throw new NotImplementedException(string.Format("{0} does not support a <list> that does not contain a <simpleType>/<restriction>.", appName));
1074 }
1075
1076 listType = true;
1077 }
1078 else
1079 {
1080 // We expect to find an existing enum already declared!
1081 EnumDeclaration existingEnumDeclaration = typeNamesToEnumDeclarations[baseTypeName];
1082 // TODO: do we need to further alter the Flags setter code because of the helper stuff?
1083 // As far as I can tell, this code is never exercised by our existing XSDs!
1084 existingEnumDeclaration.SetFlags();
1085 }
1086 }
1087
1088 if (simpleTypeRestriction == null)
1089 {
1090 string appName = typeof(XsdGen).Assembly.GetName().Name;
1091 throw new NotImplementedException(string.Format("{0} does not understand this simpleType!", appName));
1092 }
1093
1094 bool foundPattern = false;
1095 foreach (XmlSchemaFacet facet in simpleTypeRestriction.Facets)
1096 {
1097 XmlSchemaEnumerationFacet enumFacet = facet as XmlSchemaEnumerationFacet;
1098 XmlSchemaPatternFacet patternFacet = facet as XmlSchemaPatternFacet;
1099
1100 if (enumFacet != null)
1101 {
1102 if (foundPattern)
1103 {
1104 string appName = typeof(XsdGen).Assembly.GetName().Name;
1105 throw new NotImplementedException(string.Format("{0} does not support restrictions containing both <pattern> and <enumeration>.", appName));
1106 }
1107
1108 if (enumDeclaration == null)
1109 {
1110 // For nested types, the simple name comes from the attribute name, with "Type" appended
1111 // to prevent name collision with the attribute member itself.
1112 if (nestedType)
1113 {
1114 simpleTypeName = String.Concat(simpleTypeName, "Type");
1115 }
1116 baseTypeName = simpleTypeName;
1117
1118 string typeDocumentation = GetDocumentation(simpleType.Annotation);
1119 enumDeclaration = new EnumDeclaration(simpleTypeName, typeDocumentation);
1120 }
1121
1122 string documentation = GetDocumentation(enumFacet.Annotation);
1123 enumDeclaration.AddValue(enumFacet.Value, documentation);
1124 }
1125
1126 if (patternFacet != null)
1127 {
1128 if (enumDeclaration != null)
1129 {
1130 string appName = typeof(XsdGen).Assembly.GetName().Name;
1131 throw new NotImplementedException(string.Format("{0} does not support restrictions containing both <pattern> and <enumeration>.", appName));
1132 }
1133
1134 if (foundPattern)
1135 {
1136 string appName = typeof(XsdGen).Assembly.GetName().Name;
1137 throw new NotImplementedException(string.Format("{0} does not support restrictions multiple <pattern> elements.", appName));
1138 }
1139
1140 foundPattern = true;
1141 }
1142 }
1143
1144 if (enumDeclaration != null && listType)
1145 {
1146 enumDeclaration.SetFlags();
1147 }
1148
1149 if (String.IsNullOrEmpty(baseTypeName))
1150 {
1151 baseTypeName = (string)simpleTypeNamesToClrTypeNames[simpleTypeRestriction.BaseTypeName.Name];
1152 }
1153 }
1154
1155 /// <summary>
1156 /// Creates an attribute declaration indicating generated code including the tool name and version.
1157 /// </summary>
1158 /// <returns>GeneratedCodeAttribute declearation.</returns>
1159 private static CodeAttributeDeclaration GetGeneratedCodeAttribute()
1160 {
1161 AssemblyName generatorAssemblyName = typeof(XsdGen).Assembly.GetName();
1162 return new CodeAttributeDeclaration("GeneratedCode",
1163 new CodeAttributeArgument(new CodePrimitiveExpression(generatorAssemblyName.Name)),
1164 new CodeAttributeArgument(new CodePrimitiveExpression(generatorAssemblyName.Version.ToString())));
1165 }
1166
1167 /// <summary>
1168 /// Creates a code statement to throw an exception if an argument is null.
1169 /// </summary>
1170 /// <param name="argumentName">Name of the argument to check.</param>
1171 /// <param name="nullOrEmpty">True to check for null-or-empty instead of just null</param>
1172 /// <returns>Code condition statement.</returns>
1173 private static CodeConditionStatement GetArgumentNullCheckStatement(string argumentName, bool nullOrEmpty)
1174 {
1175 CodeConditionStatement conditionStatement = new CodeConditionStatement();
1176 if (nullOrEmpty)
1177 {
1178 conditionStatement.Condition = new CodeMethodInvokeExpression(new CodeMethodReferenceExpression(new CodeTypeReferenceExpression("String"), "IsNullOrEmpty"), new CodeVariableReferenceExpression(argumentName));
1179 }
1180 else
1181 {
1182 conditionStatement.Condition = new CodeBinaryOperatorExpression(new CodePrimitiveExpression(null), CodeBinaryOperatorType.ValueEquality, new CodeVariableReferenceExpression(argumentName));
1183 }
1184
1185 conditionStatement.TrueStatements.Add(new CodeThrowExceptionStatement(new CodeObjectCreateExpression("ArgumentNullException", new CodeSnippetExpression(String.Concat("\"", argumentName, "\"")))));
1186 return conditionStatement;
1187 }
1188
1189 /// <summary>
1190 /// Creates an attribute declaration to suppress a particular code-analysis message.
1191 /// </summary>
1192 /// <param name="category">Code analysis category, such as "Microsoft.Design"</param>
1193 /// <param name="checkId">Code analysis ID number.</param>
1194 /// <returns>SuppressMessageAttribute declaration.</returns>
1195 private static CodeAttributeDeclaration GetCodeAnalysisSuppressionAttribute(string category, string checkId)
1196 {
1197 return new CodeAttributeDeclaration("SuppressMessage",
1198 new CodeAttributeArgument(new CodePrimitiveExpression(category)),
1199 new CodeAttributeArgument(new CodePrimitiveExpression(checkId)));
1200 }
1201
1202 /// <summary>
1203 /// Class representing an enum declaration.
1204 /// </summary>
1205 internal class EnumDeclaration
1206 {
1207 private string enumTypeName;
1208 private CodeTypeDeclaration declaration;
1209 private bool flags;
1210 private StringCollection enumValues;
1211
1212 /// <summary>
1213 /// Creates a new enum declaration with the given name.
1214 /// </summary>
1215 /// <param name="enumTypeName">Name of the type for the enum.</param>
1216 /// <param name="documentation">Documentation for the enum type.</param>
1217 public EnumDeclaration(string enumTypeName, string documentation)
1218 {
1219 this.enumTypeName = enumTypeName;
1220
1221 this.declaration = new CodeTypeDeclaration(enumTypeName);
1222 this.declaration.CustomAttributes.Add(GetGeneratedCodeAttribute());
1223 this.declaration.Attributes = MemberAttributes.Public;
1224 this.declaration.IsEnum = true;
1225
1226 if (documentation != null)
1227 {
1228 GenerateSummaryComment(this.declaration.Comments, documentation);
1229 }
1230
1231 this.enumValues = new StringCollection();
1232 }
1233
1234 public CodeTypeDeclaration TypeDeclaration
1235 {
1236 get { return this.declaration; }
1237 }
1238
1239 /// <summary>
1240 /// Gets the enumeration values.
1241 /// </summary>
1242 /// <value>The enumeration values.</value>
1243 public ICollection Values
1244 {
1245 get { return this.enumValues; }
1246 }
1247
1248 /// <summary>
1249 /// Gets the enumeration name.
1250 /// </summary>
1251 /// <value>The enumeration name.</value>
1252 public string Name
1253 {
1254 get { return this.enumTypeName; }
1255 }
1256
1257 /// <summary>
1258 /// Gets the enumeration flags property.
1259 /// </summary>
1260 /// <value>Whether the enumeration is a [Flags] type.</value>
1261 public bool Flags
1262 {
1263 get { return this.flags; }
1264 }
1265
1266 /// <summary>
1267 /// Sets the [Flags] property on the enumeration. Once set, this cannot be undone.
1268 /// </summary>
1269 public void SetFlags()
1270 {
1271 if (this.flags)
1272 {
1273 return;
1274 }
1275
1276 this.flags = true;
1277
1278 this.declaration.CustomAttributes.Add(new CodeAttributeDeclaration("Flags"));
1279 SwitchToNoneValue();
1280
1281 int enumValue = 0;
1282 foreach (CodeMemberField enumField in this.declaration.Members)
1283 {
1284 enumField.InitExpression = new CodeSnippetExpression(enumValue.ToString());
1285 if (enumValue == 0)
1286 {
1287 enumValue = 1;
1288 }
1289 else
1290 {
1291 enumValue *= 2;
1292 }
1293 }
1294 }
1295
1296 private void InjectIllegalAndNotSetValues()
1297 {
1298 CodeMemberField memberIllegal = new CodeMemberField(typeof(int), "IllegalValue");
1299 CodeMemberField memberNotSet = new CodeMemberField(typeof(int), "NotSet");
1300
1301 memberIllegal.InitExpression = new CodeFieldReferenceExpression(new CodeTypeReferenceExpression(typeof(int)), "MaxValue");
1302 // Using "-1" for "NotSet" ensure that the next value is zero, which is consistent
1303 // with older (3.0) behavior.
1304 memberNotSet.InitExpression = new CodePrimitiveExpression(-1);
1305
1306 this.declaration.Members.Insert(0, memberIllegal);
1307 this.declaration.Members.Insert(1, memberNotSet);
1308 }
1309
1310 private void SwitchToNoneValue()
1311 {
1312 if (this.enumValues.Count > 0)
1313 {
1314 // Remove the "IllegalValue" and "NotSet" values first.
1315 this.declaration.Members.RemoveAt(0);
1316 this.declaration.Members.RemoveAt(0);
1317
1318 CodeMemberField memberNone = new CodeMemberField(typeof(int), "None");
1319 memberNone.InitExpression = new CodePrimitiveExpression(0);
1320
1321 this.declaration.Members.Insert(0, memberNone);
1322 }
1323 }
1324
1325 /// <summary>
1326 /// Add a value to the enumeration.
1327 /// </summary>
1328 /// <param name="enumValue">The value to add.</param>
1329 public void AddValue(string enumValue, string documentation)
1330 {
1331 if (this.enumValues.Count == 0)
1332 {
1333 InjectIllegalAndNotSetValues();
1334 }
1335
1336 this.enumValues.Add(enumValue);
1337 CodeMemberField memberField = new CodeMemberField(typeof(int), MakeEnumValue(enumValue));
1338 //memberField.Attributes
1339 this.declaration.Members.Add(memberField);
1340 if (documentation != null)
1341 {
1342 GenerateSummaryComment(memberField.Comments, documentation);
1343 }
1344 }
1345 }
1346
1347 private static void AddEnumHelperMethods(EnumDeclaration enumDeclaration, CodeNamespace codeNamespace)
1348 {
1349 if (enumHelperClass == null)
1350 {
1351 enumHelperClass = new CodeTypeDeclaration("Enums");
1352 enumHelperClass.CustomAttributes.Add(GetGeneratedCodeAttribute());
1353 // The static and final attributes don't seem to get applied, but we'd prefer if they were.
1354 enumHelperClass.Attributes = MemberAttributes.Public | MemberAttributes.Static | MemberAttributes.Final;
1355 codeNamespace.Types.Add(enumHelperClass);
1356 }
1357
1358 AddEnumHelperMethods(enumDeclaration, enumHelperClass);
1359 }
1360
1361 private static void AddEnumHelperMethods(EnumDeclaration enumDeclaration, CodeTypeDeclaration parentType)
1362 {
1363 CodeTypeReference stringType = new CodeTypeReference(typeof(string));
1364 CodeTypeReference boolType = new CodeTypeReference(typeof(bool));
1365 CodeTypeReference enumType = new CodeTypeReference(typeof(Enum));
1366 CodeTypeReference newEnumType = new CodeTypeReference(enumDeclaration.Name);
1367
1368 CodePrimitiveExpression falseValue = new CodePrimitiveExpression(false);
1369 CodePrimitiveExpression trueValue = new CodePrimitiveExpression(true);
1370 CodeMethodReturnStatement returnFalse = new CodeMethodReturnStatement(falseValue);
1371 CodeMethodReturnStatement returnTrue = new CodeMethodReturnStatement(trueValue);
1372
1373 string parseMethodName = String.Concat("Parse", enumDeclaration.Name);
1374 string tryParseMethodName = String.Concat("TryParse", enumDeclaration.Name);
1375
1376 CodeFieldReferenceExpression defaultEnumValue = null;
1377 CodeFieldReferenceExpression illegalEnumValue = null;
1378 bool addParse = true;
1379 if (enumDeclaration.Flags)
1380 {
1381 defaultEnumValue = new CodeFieldReferenceExpression(new CodeTypeReferenceExpression(newEnumType), "None");
1382 illegalEnumValue = defaultEnumValue;
1383 // Because there's no "IllegalValue" for [Flags] enums, we can't create the Parse()
1384 // method. We can still create the TryParse() method, though!
1385 addParse = false;
1386 }
1387 else
1388 {
1389 defaultEnumValue = new CodeFieldReferenceExpression(new CodeTypeReferenceExpression(newEnumType), "NotSet");
1390 illegalEnumValue = new CodeFieldReferenceExpression(new CodeTypeReferenceExpression(newEnumType), "IllegalValue");
1391 }
1392
1393 if (addParse)
1394 {
1395 CodeMemberMethod parseNewEnum = new CodeMemberMethod();
1396 GenerateSummaryComment(parseNewEnum.Comments, String.Format("Parses a {0} from a string.", enumDeclaration.Name));
1397 parseNewEnum.Attributes = MemberAttributes.Public | MemberAttributes.Static;
1398 parseNewEnum.Name = parseMethodName;
1399 parseNewEnum.ReturnType = newEnumType;
1400 parseNewEnum.Parameters.Add(new CodeParameterDeclarationExpression(stringType, "value"));
1401
1402 parseNewEnum.Statements.Add(new CodeVariableDeclarationStatement(newEnumType, "parsedValue"));
1403
1404 // Just delegate to the TryParse version...
1405 parseNewEnum.Statements.Add(new CodeMethodInvokeExpression(
1406 new CodeMethodReferenceExpression(new CodeTypeReferenceExpression(parentType.Name), tryParseMethodName),
1407 new CodeArgumentReferenceExpression("value"),
1408 new CodeDirectionExpression(FieldDirection.Out, new CodeVariableReferenceExpression("parsedValue"))));
1409
1410 parseNewEnum.Statements.Add(new CodeMethodReturnStatement(new CodeVariableReferenceExpression("parsedValue")));
1411 parentType.Members.Add(parseNewEnum);
1412 }
1413
1414 CodeMemberMethod tryParseNewEnum = new CodeMemberMethod();
1415 GenerateSummaryComment(tryParseNewEnum.Comments, String.Format("Tries to parse a {0} from a string.", enumDeclaration.Name));
1416 tryParseNewEnum.Attributes = MemberAttributes.Public | MemberAttributes.Static;
1417 tryParseNewEnum.Name = tryParseMethodName;
1418 tryParseNewEnum.ReturnType = boolType;
1419 CodeParameterDeclarationExpression valueDeclaration = new CodeParameterDeclarationExpression(stringType, "value");
1420 CodeParameterDeclarationExpression parsedValueDeclaration = new CodeParameterDeclarationExpression(newEnumType, "parsedValue");
1421 parsedValueDeclaration.Direction = FieldDirection.Out;
1422 tryParseNewEnum.Parameters.Add(valueDeclaration);
1423 tryParseNewEnum.Parameters.Add(parsedValueDeclaration);
1424
1425 CodeArgumentReferenceExpression value = new CodeArgumentReferenceExpression(valueDeclaration.Name);
1426 CodeArgumentReferenceExpression parsedValue = new CodeArgumentReferenceExpression(parsedValueDeclaration.Name);
1427
1428 tryParseNewEnum.Statements.Add(new CodeAssignStatement(parsedValue, defaultEnumValue));
1429
1430 tryParseNewEnum.Statements.Add(new CodeConditionStatement(
1431 new CodeMethodInvokeExpression(new CodeMethodReferenceExpression(new CodeTypeReferenceExpression(stringType), "IsNullOrEmpty"), value),
1432 returnFalse));
1433
1434 // The structure is similar, but distinct, for regular and flag-style enums. In particular,
1435 // for a flags-style enum we have to be able to parse multiple values, separated by
1436 // spaces, and each value is bitwise-OR'd together.
1437 CodeStatementCollection nestedIfParent = tryParseNewEnum.Statements;
1438 CodeExpression valueToTest = value;
1439
1440 // For Flags-style enums, we need to loop over the space-separated values...
1441 if (enumDeclaration.Flags)
1442 {
1443 CodeVariableDeclarationStatement split = new CodeVariableDeclarationStatement(typeof(string[]), "splitValue",
1444 new CodeMethodInvokeExpression(value, "Split",
1445 new CodeMethodInvokeExpression(new CodePrimitiveExpression(" \t\r\n"), "ToCharArray"),
1446 new CodeFieldReferenceExpression(new CodeTypeReferenceExpression(typeof(StringSplitOptions)), "RemoveEmptyEntries")));
1447 tryParseNewEnum.Statements.Add(split);
1448
1449 CodeIterationStatement flagLoop = new CodeIterationStatement(
1450 new CodeVariableDeclarationStatement(typeof(IEnumerator), "enumerator",
1451 new CodeMethodInvokeExpression(new CodeVariableReferenceExpression(split.Name), "GetEnumerator")),
1452 new CodeMethodInvokeExpression(new CodeVariableReferenceExpression("enumerator"), "MoveNext"),
1453 new CodeSnippetStatement(""));
1454 tryParseNewEnum.Statements.Add(flagLoop);
1455
1456 CodeVariableDeclarationStatement currentValue = new CodeVariableDeclarationStatement(typeof(string), "currentValue",
1457 new CodeCastExpression(stringType,
1458 new CodePropertyReferenceExpression(new CodeVariableReferenceExpression("enumerator"), "Current")));
1459 flagLoop.Statements.Add(currentValue);
1460 valueToTest = new CodeVariableReferenceExpression(currentValue.Name);
1461
1462 nestedIfParent = flagLoop.Statements;
1463 }
1464
1465 // We can't just Enum.Parse, because some values are also keywords (like 'string', 'int', 'default'),
1466 // and these get generated as '@'-prefixed values. Instead, we 'switch' on the value and do it manually.
1467 // Actually, we if/else, because CodeDom doesn't support 'switch'! Also, we nest the successive 'if's
1468 // in order to short-circuit the parsing as soon as there's a match.
1469 foreach (string enumValue in enumDeclaration.Values)
1470 {
1471 CodeFieldReferenceExpression enumValueReference = new CodeFieldReferenceExpression(new CodeTypeReferenceExpression(newEnumType), MakeEnumValue(enumValue));
1472 CodeConditionStatement ifStatement = new CodeConditionStatement(
1473 new CodeBinaryOperatorExpression(new CodePrimitiveExpression(enumValue), CodeBinaryOperatorType.ValueEquality, valueToTest));
1474 if (enumDeclaration.Flags)
1475 {
1476 ifStatement.TrueStatements.Add(new CodeAssignStatement(parsedValue,
1477 new CodeBinaryOperatorExpression(parsedValue, CodeBinaryOperatorType.BitwiseOr, enumValueReference)));
1478 }
1479 else
1480 {
1481 ifStatement.TrueStatements.Add(new CodeAssignStatement(parsedValue, enumValueReference));
1482 }
1483 nestedIfParent.Add(ifStatement);
1484 nestedIfParent = ifStatement.FalseStatements;
1485 }
1486
1487 // Finally, if we didn't find a match, it's illegal (or none, for flags)!
1488 nestedIfParent.Add(new CodeAssignStatement(parsedValue, illegalEnumValue));
1489 nestedIfParent.Add(returnFalse);
1490
1491 tryParseNewEnum.Statements.Add(returnTrue);
1492
1493 parentType.Members.Add(tryParseNewEnum);
1494
1495 enumsToParseMethodClasses.Add(enumDeclaration, parentType);
1496 }
1497 }
1498}
diff --git a/src/internal/WixBuildTools.XsdGen/WixBuildTools.XsdGen.csproj b/src/internal/WixBuildTools.XsdGen/WixBuildTools.XsdGen.csproj
new file mode 100644
index 00000000..2e3a0382
--- /dev/null
+++ b/src/internal/WixBuildTools.XsdGen/WixBuildTools.XsdGen.csproj
@@ -0,0 +1,28 @@
1<Project Sdk="Microsoft.NET.Sdk">
2
3 <PropertyGroup>
4 <OutputType>Exe</OutputType>
5 <!-- <TargetFrameworks>netcoreapp2.0;net461</TargetFrameworks> -->
6 <TargetFrameworks>net461</TargetFrameworks>
7 <IsTool>true</IsTool>
8 <IncludeBuildOutput>false</IncludeBuildOutput>
9 <SuppressDependenciesWhenPacking>true</SuppressDependenciesWhenPacking>
10 <DebugType>embedded</DebugType>
11 <PublishRepositoryUrl>true</PublishRepositoryUrl>
12 <NoWarn>CS0618</NoWarn>
13 </PropertyGroup>
14
15 <ItemGroup>
16 <Content Include="build\WixBuildTools.XsdGen.targets" PackagePath="build\" />
17 <Content Include="buildCrossTargeting\WixBuildTools.XsdGen.targets" PackagePath="buildCrossTargeting\" />
18
19 <Content Include="$(OutputPath)net461\$(TargetFileName)" PackagePath="tools\full" />
20 <!-- <Content Include="$(OutputPath)netcoreapp2.0\$(TargetFileName)" PackagePath="tools\core" /> -->
21 </ItemGroup>
22
23 <ItemGroup>
24 <PackageReference Include="Microsoft.SourceLink.GitHub" Version="1.0.0" PrivateAssets="All" />
25 <PackageReference Include="Nerdbank.GitVersioning" Version="3.3.37" PrivateAssets="All" />
26 </ItemGroup>
27
28</Project>
diff --git a/src/internal/WixBuildTools.XsdGen/XsdGen.cs b/src/internal/WixBuildTools.XsdGen/XsdGen.cs
new file mode 100644
index 00000000..a1374df3
--- /dev/null
+++ b/src/internal/WixBuildTools.XsdGen/XsdGen.cs
@@ -0,0 +1,124 @@
1// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
2
3namespace WixToolset.Tools
4{
5 using System;
6 using System.CodeDom;
7 using System.CodeDom.Compiler;
8 using System.Collections;
9 using System.IO;
10 using System.Xml;
11 using System.Xml.Schema;
12 using Microsoft.CSharp;
13 using WixToolset;
14
15 /// <summary>
16 /// Generates a strongly-typed C# class from an XML schema (XSD).
17 /// </summary>
18 public class XsdGen
19 {
20 private string xsdFile;
21 private string outFile;
22 private string outputNamespace;
23 private string commonNamespace;
24 private bool showHelp;
25
26 /// <summary>
27 /// Constructor for the XsdGen class.
28 /// </summary>
29 /// <param name="args">Command-line arguments passed to the program.</param>
30 private XsdGen(string[] args)
31 {
32 this.ParseCommandlineArgs(args);
33
34 // show usage information
35 if (this.showHelp)
36 {
37 Console.WriteLine("usage: XsdGen.exe <schema>.xsd <outputFile> <namespace> [<commonNamespace>]");
38 return;
39 }
40
41 // ensure that the schema file exists
42 if (!File.Exists(this.xsdFile))
43 {
44 throw new ApplicationException(String.Format("Schema file does not exist: '{0}'.", this.xsdFile));
45 }
46
47 XmlSchema document = null;
48 using (StreamReader xsdFileReader = new StreamReader(this.xsdFile))
49 {
50 document = XmlSchema.Read(xsdFileReader, new ValidationEventHandler(this.ValidationHandler));
51 }
52
53 CodeCompileUnit codeCompileUnit = StronglyTypedClasses.Generate(document, this.outputNamespace, this.commonNamespace);
54
55 using (CSharpCodeProvider codeProvider = new CSharpCodeProvider())
56 {
57 ICodeGenerator generator = codeProvider.CreateGenerator();
58
59 CodeGeneratorOptions options = new CodeGeneratorOptions();
60 options.BlankLinesBetweenMembers = true;
61 options.BracingStyle = "C";
62 options.IndentString = " ";
63
64 using (StreamWriter csharpFileWriter = new StreamWriter(this.outFile))
65 {
66 generator.GenerateCodeFromCompileUnit(codeCompileUnit, csharpFileWriter, options);
67 }
68 }
69 }
70
71 /// <summary>
72 /// The main entry point for the application.
73 /// </summary>
74 /// <param name="args">The command line arguments.</param>
75 /// <returns>The error code.</returns>
76 [STAThread]
77 public static int Main(string[] args)
78 {
79 try
80 {
81 XsdGen xsdGen = new XsdGen(args);
82 }
83 catch (Exception e)
84 {
85 Console.WriteLine("XsdGen.exe : fatal error MSF0000: {0}\r\n\r\nStack Trace:\r\n{1}", e.Message, e.StackTrace);
86 return 1;
87 }
88
89 return 0;
90 }
91
92 /// <summary>
93 /// Validation event handler.
94 /// </summary>
95 /// <param name="sender">Sender for the event.</param>
96 /// <param name="e">Event args.</param>
97 public void ValidationHandler(object sender, ValidationEventArgs e)
98 {
99 }
100
101 /// <summary>
102 /// Parse the command line arguments.
103 /// </summary>
104 /// <param name="args">Command-line arguments.</param>
105 private void ParseCommandlineArgs(string[] args)
106 {
107 if (3 > args.Length)
108 {
109 this.showHelp = true;
110 }
111 else
112 {
113 this.xsdFile = args[0];
114 this.outFile = args[1];
115 this.outputNamespace = args[2];
116
117 if (args.Length >= 4)
118 {
119 this.commonNamespace = args[3];
120 }
121 }
122 }
123 }
124}
diff --git a/src/internal/WixBuildTools.XsdGen/build/WixBuildTools.XsdGen.targets b/src/internal/WixBuildTools.XsdGen/build/WixBuildTools.XsdGen.targets
new file mode 100644
index 00000000..ca1b89f6
--- /dev/null
+++ b/src/internal/WixBuildTools.XsdGen/build/WixBuildTools.XsdGen.targets
@@ -0,0 +1,67 @@
1<?xml version="1.0" encoding="utf-8"?>
2<!-- Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. -->
3
4<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003" ToolsVersion="4.0">
5 <PropertyGroup>
6 <MSBuildAllProjects>$(MSBuildAllProjects);$(MSBuildThisFileFullPath)</MSBuildAllProjects>
7 </PropertyGroup>
8
9 <ItemGroup>
10 <!--Provide support for setting type (BuildAction) from VS-->
11 <AvailableItemName Include="XsdGenSource" />
12 </ItemGroup>
13
14 <PropertyGroup>
15 <!-- <XsdGenPath Condition=" '$(XsdGenPath)'=='' and '$(MSBuildRuntimeType)'=='Core' ">$(MSBuildThisFileDirectory)..\tools\core\</XsdGenPath> -->
16 <XsdGenPath Condition=" '$(XsdGenPath)'=='' ">$(MSBuildThisFileDirectory)..\tools\full\</XsdGenPath>
17 </PropertyGroup>
18
19 <!--
20 ================================================================================================
21 XsdGen
22
23 Generates a .cs class file from an .xsd file.
24
25 [IN]
26 @(XsdGenSource) - The items to run through the XsdGen tool.
27
28 [OUT]
29 $(IntermediateOutputPath)%(Filename).cs - The generated .cs files to include in the compilation.
30 ================================================================================================
31 -->
32 <PropertyGroup>
33 <XsdGenDependsOn>
34 </XsdGenDependsOn>
35 <PrepareResourcesDependsOn>
36 XsdGen;
37 $(PrepareResourcesDependsOn)
38 </PrepareResourcesDependsOn>
39 </PropertyGroup>
40 <Target
41 Name="XsdGen"
42 BeforeTargets="PrepareResources"
43 DependsOnTargets="$(XsdGenDependsOn)"
44 Condition=" '@(XsdGenSource)' != '' "
45 Inputs="$(MSBuildAllProjects);@(XsdGenSource)"
46 Outputs="$(IntermediateOutputPath)%(XsdGenSource.Filename).cs">
47
48 <PropertyGroup>
49 <XsdGenCsFile>$(IntermediateOutputPath)%(XsdGenSource.Filename).cs</XsdGenCsFile>
50 <XsdGenCommonNamespace>%(XsdGenSource.CommonNamespace)</XsdGenCommonNamespace>
51 </PropertyGroup>
52
53 <Exec Command="&quot;$(XsdGenPath)WixBuildTools.XsdGen.exe&quot; &quot;%(XsdGenSource.FullPath)&quot; &quot;$(XsdGenCsFile)&quot; %(XsdGenSource.Namespace) $(XsdGenCommonNamespace)"
54 Outputs="$(XsdGenCsFile)" />
55
56 <ItemGroup>
57 <!-- This will tell MSBuild to clean up the .cs file during a Clean build -->
58 <FileWrites Include="$(XsdGenCsFile)" />
59
60 <!-- Add the generated .cs file to the list of source files to compile -->
61 <Compile Include="$(XsdGenCsFile)">
62 <Link>%(XsdGenCsFile.Filename)%(XsdGenCsFile.Extension)</Link>
63 </Compile>
64 </ItemGroup>
65 </Target>
66
67</Project>
diff --git a/src/internal/WixBuildTools.XsdGen/buildCrossTargeting/WixBuildTools.XsdGen.targets b/src/internal/WixBuildTools.XsdGen/buildCrossTargeting/WixBuildTools.XsdGen.targets
new file mode 100644
index 00000000..58692095
--- /dev/null
+++ b/src/internal/WixBuildTools.XsdGen/buildCrossTargeting/WixBuildTools.XsdGen.targets
@@ -0,0 +1,6 @@
1<?xml version="1.0" encoding="utf-8"?>
2<!-- Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. -->
3
4<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003" ToolsVersion="4.0">
5 <Import Project="..\build\WixBuildTools.XsdGen.targets" />
6</Project>
diff --git a/src/internal/WixBuildTools.sln b/src/internal/WixBuildTools.sln
new file mode 100644
index 00000000..689832fb
--- /dev/null
+++ b/src/internal/WixBuildTools.sln
@@ -0,0 +1,81 @@
1Microsoft Visual Studio Solution File, Format Version 12.00
2# Visual Studio 15
3VisualStudioVersion = 15.0.27130.2003
4MinimumVisualStudioVersion = 15.0.26124.0
5Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WixBuildTools.XsdGen", "src\WixBuildTools.XsdGen\WixBuildTools.XsdGen.csproj", "{E89E52C9-A4A1-4174-A1B1-3B72975E6ED6}"
6EndProject
7Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WixBuildTools.MsgGen", "src\WixBuildTools.MsgGen\WixBuildTools.MsgGen.csproj", "{DB6EF6F3-51B1-4214-9A14-D501C23F6FA4}"
8EndProject
9Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WixBuildTools.TestSupport", "src\WixBuildTools.TestSupport\WixBuildTools.TestSupport.csproj", "{6C57EF2C-979A-4106-A9E5-FE342810619A}"
10EndProject
11Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "WixBuildTools.TestSupport.Native", "src\WixBuildTools.TestSupport.Native\WixBuildTools.TestSupport.Native.vcxproj", "{95BABD97-FBDB-453A-AF8A-FA031A07B599}"
12EndProject
13Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{F6660B22-092F-4BC5-A303-E6F696C31E1B}"
14 ProjectSection(SolutionItems) = preProject
15 .editorconfig = .editorconfig
16 EndProjectSection
17EndProject
18Global
19 GlobalSection(SolutionConfigurationPlatforms) = preSolution
20 Debug|Any CPU = Debug|Any CPU
21 Debug|x64 = Debug|x64
22 Debug|x86 = Debug|x86
23 Release|Any CPU = Release|Any CPU
24 Release|x64 = Release|x64
25 Release|x86 = Release|x86
26 EndGlobalSection
27 GlobalSection(ProjectConfigurationPlatforms) = postSolution
28 {E89E52C9-A4A1-4174-A1B1-3B72975E6ED6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
29 {E89E52C9-A4A1-4174-A1B1-3B72975E6ED6}.Debug|Any CPU.Build.0 = Debug|Any CPU
30 {E89E52C9-A4A1-4174-A1B1-3B72975E6ED6}.Debug|x64.ActiveCfg = Debug|Any CPU
31 {E89E52C9-A4A1-4174-A1B1-3B72975E6ED6}.Debug|x64.Build.0 = Debug|Any CPU
32 {E89E52C9-A4A1-4174-A1B1-3B72975E6ED6}.Debug|x86.ActiveCfg = Debug|Any CPU
33 {E89E52C9-A4A1-4174-A1B1-3B72975E6ED6}.Debug|x86.Build.0 = Debug|Any CPU
34 {E89E52C9-A4A1-4174-A1B1-3B72975E6ED6}.Release|Any CPU.ActiveCfg = Release|Any CPU
35 {E89E52C9-A4A1-4174-A1B1-3B72975E6ED6}.Release|Any CPU.Build.0 = Release|Any CPU
36 {E89E52C9-A4A1-4174-A1B1-3B72975E6ED6}.Release|x64.ActiveCfg = Release|Any CPU
37 {E89E52C9-A4A1-4174-A1B1-3B72975E6ED6}.Release|x64.Build.0 = Release|Any CPU
38 {E89E52C9-A4A1-4174-A1B1-3B72975E6ED6}.Release|x86.ActiveCfg = Release|Any CPU
39 {E89E52C9-A4A1-4174-A1B1-3B72975E6ED6}.Release|x86.Build.0 = Release|Any CPU
40 {DB6EF6F3-51B1-4214-9A14-D501C23F6FA4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
41 {DB6EF6F3-51B1-4214-9A14-D501C23F6FA4}.Debug|Any CPU.Build.0 = Debug|Any CPU
42 {DB6EF6F3-51B1-4214-9A14-D501C23F6FA4}.Debug|x64.ActiveCfg = Debug|Any CPU
43 {DB6EF6F3-51B1-4214-9A14-D501C23F6FA4}.Debug|x64.Build.0 = Debug|Any CPU
44 {DB6EF6F3-51B1-4214-9A14-D501C23F6FA4}.Debug|x86.ActiveCfg = Debug|Any CPU
45 {DB6EF6F3-51B1-4214-9A14-D501C23F6FA4}.Debug|x86.Build.0 = Debug|Any CPU
46 {DB6EF6F3-51B1-4214-9A14-D501C23F6FA4}.Release|Any CPU.ActiveCfg = Release|Any CPU
47 {DB6EF6F3-51B1-4214-9A14-D501C23F6FA4}.Release|Any CPU.Build.0 = Release|Any CPU
48 {DB6EF6F3-51B1-4214-9A14-D501C23F6FA4}.Release|x64.ActiveCfg = Release|Any CPU
49 {DB6EF6F3-51B1-4214-9A14-D501C23F6FA4}.Release|x64.Build.0 = Release|Any CPU
50 {DB6EF6F3-51B1-4214-9A14-D501C23F6FA4}.Release|x86.ActiveCfg = Release|Any CPU
51 {DB6EF6F3-51B1-4214-9A14-D501C23F6FA4}.Release|x86.Build.0 = Release|Any CPU
52 {6C57EF2C-979A-4106-A9E5-FE342810619A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
53 {6C57EF2C-979A-4106-A9E5-FE342810619A}.Debug|Any CPU.Build.0 = Debug|Any CPU
54 {6C57EF2C-979A-4106-A9E5-FE342810619A}.Debug|x64.ActiveCfg = Debug|Any CPU
55 {6C57EF2C-979A-4106-A9E5-FE342810619A}.Debug|x64.Build.0 = Debug|Any CPU
56 {6C57EF2C-979A-4106-A9E5-FE342810619A}.Debug|x86.ActiveCfg = Debug|Any CPU
57 {6C57EF2C-979A-4106-A9E5-FE342810619A}.Debug|x86.Build.0 = Debug|Any CPU
58 {6C57EF2C-979A-4106-A9E5-FE342810619A}.Release|Any CPU.ActiveCfg = Release|Any CPU
59 {6C57EF2C-979A-4106-A9E5-FE342810619A}.Release|Any CPU.Build.0 = Release|Any CPU
60 {6C57EF2C-979A-4106-A9E5-FE342810619A}.Release|x64.ActiveCfg = Release|Any CPU
61 {6C57EF2C-979A-4106-A9E5-FE342810619A}.Release|x64.Build.0 = Release|Any CPU
62 {6C57EF2C-979A-4106-A9E5-FE342810619A}.Release|x86.ActiveCfg = Release|Any CPU
63 {6C57EF2C-979A-4106-A9E5-FE342810619A}.Release|x86.Build.0 = Release|Any CPU
64 {95BABD97-FBDB-453A-AF8A-FA031A07B599}.Debug|Any CPU.ActiveCfg = Debug|Win32
65 {95BABD97-FBDB-453A-AF8A-FA031A07B599}.Debug|Any CPU.Build.0 = Debug|Win32
66 {95BABD97-FBDB-453A-AF8A-FA031A07B599}.Debug|x64.ActiveCfg = Debug|Win32
67 {95BABD97-FBDB-453A-AF8A-FA031A07B599}.Debug|x86.ActiveCfg = Debug|Win32
68 {95BABD97-FBDB-453A-AF8A-FA031A07B599}.Debug|x86.Build.0 = Debug|Win32
69 {95BABD97-FBDB-453A-AF8A-FA031A07B599}.Release|Any CPU.ActiveCfg = Release|Win32
70 {95BABD97-FBDB-453A-AF8A-FA031A07B599}.Release|Any CPU.Build.0 = Release|Win32
71 {95BABD97-FBDB-453A-AF8A-FA031A07B599}.Release|x64.ActiveCfg = Release|Win32
72 {95BABD97-FBDB-453A-AF8A-FA031A07B599}.Release|x86.ActiveCfg = Release|Win32
73 {95BABD97-FBDB-453A-AF8A-FA031A07B599}.Release|x86.Build.0 = Release|Win32
74 EndGlobalSection
75 GlobalSection(SolutionProperties) = preSolution
76 HideSolutionNode = FALSE
77 EndGlobalSection
78 GlobalSection(ExtensibilityGlobals) = postSolution
79 SolutionGuid = {83E9E075-B440-471A-9C37-9D84BA0AE3E0}
80 EndGlobalSection
81EndGlobal
diff --git a/src/internal/WixBuildTools.v3.ncrunchsolution b/src/internal/WixBuildTools.v3.ncrunchsolution
new file mode 100644
index 00000000..10420ac9
--- /dev/null
+++ b/src/internal/WixBuildTools.v3.ncrunchsolution
@@ -0,0 +1,6 @@
1<SolutionConfiguration>
2 <Settings>
3 <AllowParallelTestExecution>True</AllowParallelTestExecution>
4 <SolutionConfigured>True</SolutionConfigured>
5 </Settings>
6</SolutionConfiguration> \ No newline at end of file
diff --git a/src/internal/appveyor.cmd b/src/internal/appveyor.cmd
new file mode 100644
index 00000000..9fa5e330
--- /dev/null
+++ b/src/internal/appveyor.cmd
@@ -0,0 +1,13 @@
1@setlocal
2@pushd %~dp0
3
4nuget restore || exit /b
5
6dotnet pack -c Release src\WixBuildTools.MsgGen\WixBuildTools.MsgGen.csproj || exit /b
7dotnet pack -c Release src\WixBuildTools.TestSupport\WixBuildTools.TestSupport.csproj || exit /b
8dotnet pack -c Release src\WixBuildTools.XsdGen\WixBuildTools.XsdGen.csproj || exit /b
9
10msbuild -p:Configuration=Release -t:PackNativeNuget src\WixBuildTools.TestSupport.Native\WixBuildTools.TestSupport.Native.vcxproj || exit /b
11
12@popd
13@endlocal \ No newline at end of file
diff --git a/src/internal/appveyor.yml b/src/internal/appveyor.yml
new file mode 100644
index 00000000..522e5af3
--- /dev/null
+++ b/src/internal/appveyor.yml
@@ -0,0 +1,42 @@
1# Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
2#
3# Do NOT modify this file. Update the canonical version in Home\repo-template\src\appveyor.yml
4# then update all of the repos.
5
6branches:
7 only:
8 - master
9 - develop
10
11image: Visual Studio 2019
12
13version: 0.0.0.{build}
14configuration: Release
15
16environment:
17 DOTNET_SKIP_FIRST_TIME_EXPERIENCE: true
18 DOTNET_CLI_TELEMETRY_OPTOUT: 1
19 NUGET_XMLDOC_MODE: skip
20
21build_script:
22 - appveyor.cmd
23
24pull_requests:
25 do_not_increment_build_number: true
26
27nuget:
28 disable_publish_on_pr: true
29
30skip_branch_with_pr: true
31skip_tags: true
32
33artifacts:
34- path: build\Release\**\*.nupkg
35 name: nuget
36- path: build\Release\**\*.msi
37 name: msi
38
39notifications:
40- provider: Slack
41 incoming_webhook:
42 secure: p5xuu+4x2JHfwGDMDe5KcG1k7gZxqYc4jWVwvyNZv5cvkubPD2waJs5yXMAXZNN7Z63/3PWHb7q4KoY/99AjauYa1nZ4c5qYqRPFRBKTHfA=
diff --git a/src/internal/nuget.config b/src/internal/nuget.config
new file mode 100644
index 00000000..fcbac27a
--- /dev/null
+++ b/src/internal/nuget.config
@@ -0,0 +1,8 @@
1<?xml version="1.0" encoding="utf-8"?>
2<configuration>
3 <packageSources>
4 <clear />
5 <add key="wixtoolset-dtf" value="https://ci.appveyor.com/nuget/wixtoolset-dtf" />
6 <add key="api.nuget.org" value="https://api.nuget.org/v3/index.json" protocolVersion="3" />
7 </packageSources>
8</configuration> \ No newline at end of file