From 6a24996a2e831cfe402398af65b31fb1ecd575a9 Mon Sep 17 00:00:00 2001 From: Rob Mensching Date: Thu, 22 Apr 2021 16:36:39 -0700 Subject: Move WixBuildTools into internal --- .editorconfig | 37 - README.md | 2 - WixBuildTools.sln | 81 - WixBuildTools.v3.ncrunchsolution | 6 - appveyor.cmd | 13 - appveyor.yml | 42 - nuget.config | 8 - src/.editorconfig | 37 + src/CSharp.Build.props | 13 - src/Cpp.Build.props | 104 - src/Directory.Build.props | 29 - src/Directory.Build.targets | 56 - src/MessagesToMessages/MessagesToMessages.csproj | 8 - src/MessagesToMessages/Program.cs | 261 --- .../Properties/launchSettings.json | 9 - src/MessagesToMessages/SimpleJson.cs | 2127 -------------------- src/TablesAndTuples/ColumnDefinitionEnums.cs | 56 - src/TablesAndTuples/Program.cs | 528 ----- src/TablesAndTuples/SimpleJson.cs | 2127 -------------------- src/TablesAndTuples/TablesAndTuples.csproj | 8 - src/TablesAndTuples/WixColumnDefinition.cs | 296 --- src/TablesAndTuples/WixTableDefinition.cs | 169 -- src/WixBuildTools.MsgGen/AssemblyInfo.cs | 8 - src/WixBuildTools.MsgGen/GenerateMessageFiles.cs | 250 --- src/WixBuildTools.MsgGen/MsgGen.cs | 261 --- .../WixBuildTools.MsgGen.csproj | 27 - src/WixBuildTools.MsgGen/Xsd/messages.xsd | 101 - .../build/WixBuildTools.MsgGen.targets | 102 - .../WixBuildTools.MsgGen.targets | 6 - .../AssemblyInfo.cpp | 17 - .../NativeAssert.h | 85 - .../WixBuildTools.TestSupport.Native.nuspec | 26 - .../WixBuildTools.TestSupport.Native.vcxproj | 85 - ...ixBuildTools.TestSupport.Native.vcxproj.filters | 33 - .../build/WixBuildTools.TestSupport.Native.props | 29 - .../build/WixBuildTools.TestSupport.Native.targets | 19 - .../packages.config | 17 - src/WixBuildTools.TestSupport.Native/precomp.cpp | 3 - src/WixBuildTools.TestSupport.Native/precomp.h | 11 - src/WixBuildTools.TestSupport/Builder.cs | 70 - .../DisposableFileSystem.cs | 93 - src/WixBuildTools.TestSupport/DotnetRunner.cs | 57 - .../ExternalExecutable.cs | 88 - .../ExternalExecutableResult.cs | 17 - src/WixBuildTools.TestSupport/FakeBuildEngine.cs | 33 - src/WixBuildTools.TestSupport/MsbuildRunner.cs | 168 -- .../MsbuildRunnerResult.cs | 19 - src/WixBuildTools.TestSupport/Pushd.cs | 46 - src/WixBuildTools.TestSupport/Query.cs | 172 -- src/WixBuildTools.TestSupport/RobocopyRunner.cs | 16 - .../SucceededException.cs | 18 - src/WixBuildTools.TestSupport/TestData.cs | 16 - .../TestDataFolderFileSystem.cs | 42 - src/WixBuildTools.TestSupport/VswhereRunner.cs | 41 - src/WixBuildTools.TestSupport/WixAssert.cs | 47 - .../WixBuildTools.TestSupport.csproj | 31 - src/WixBuildTools.XsdGen/AssemblyInfo.cs | 9 - src/WixBuildTools.XsdGen/CodeDomInterfaces.cs | 96 - src/WixBuildTools.XsdGen/CodeDomReader.cs | 159 -- src/WixBuildTools.XsdGen/ElementCollection.cs | 642 ------ src/WixBuildTools.XsdGen/StronglyTypedClasses.cs | 1498 -------------- .../WixBuildTools.XsdGen.csproj | 28 - src/WixBuildTools.XsdGen/XsdGen.cs | 124 -- .../build/WixBuildTools.XsdGen.targets | 67 - .../WixBuildTools.XsdGen.targets | 6 - src/internal/CSharp.Build.props | 13 + src/internal/Cpp.Build.props | 104 + src/internal/Directory.Build.props | 29 + src/internal/Directory.Build.targets | 56 + .../MessagesToMessages/MessagesToMessages.csproj | 8 + src/internal/MessagesToMessages/Program.cs | 261 +++ .../Properties/launchSettings.json | 9 + src/internal/MessagesToMessages/SimpleJson.cs | 2127 ++++++++++++++++++++ src/internal/README.md | 2 + .../TablesAndTuples/ColumnDefinitionEnums.cs | 56 + src/internal/TablesAndTuples/Program.cs | 528 +++++ src/internal/TablesAndTuples/SimpleJson.cs | 2127 ++++++++++++++++++++ .../TablesAndTuples/TablesAndTuples.csproj | 8 + .../TablesAndTuples/WixColumnDefinition.cs | 296 +++ src/internal/TablesAndTuples/WixTableDefinition.cs | 169 ++ src/internal/WixBuildTools.MsgGen/AssemblyInfo.cs | 8 + .../WixBuildTools.MsgGen/GenerateMessageFiles.cs | 250 +++ src/internal/WixBuildTools.MsgGen/MsgGen.cs | 261 +++ .../WixBuildTools.MsgGen.csproj | 27 + src/internal/WixBuildTools.MsgGen/Xsd/messages.xsd | 101 + .../build/WixBuildTools.MsgGen.targets | 102 + .../WixBuildTools.MsgGen.targets | 6 + .../AssemblyInfo.cpp | 17 + .../NativeAssert.h | 85 + .../WixBuildTools.TestSupport.Native.nuspec | 26 + .../WixBuildTools.TestSupport.Native.vcxproj | 85 + ...ixBuildTools.TestSupport.Native.vcxproj.filters | 33 + .../build/WixBuildTools.TestSupport.Native.props | 29 + .../build/WixBuildTools.TestSupport.Native.targets | 19 + .../packages.config | 17 + .../WixBuildTools.TestSupport.Native/precomp.cpp | 3 + .../WixBuildTools.TestSupport.Native/precomp.h | 11 + src/internal/WixBuildTools.TestSupport/Builder.cs | 70 + .../DisposableFileSystem.cs | 93 + .../WixBuildTools.TestSupport/DotnetRunner.cs | 57 + .../ExternalExecutable.cs | 88 + .../ExternalExecutableResult.cs | 17 + .../WixBuildTools.TestSupport/FakeBuildEngine.cs | 33 + .../WixBuildTools.TestSupport/MsbuildRunner.cs | 168 ++ .../MsbuildRunnerResult.cs | 19 + src/internal/WixBuildTools.TestSupport/Pushd.cs | 46 + src/internal/WixBuildTools.TestSupport/Query.cs | 172 ++ .../WixBuildTools.TestSupport/RobocopyRunner.cs | 16 + .../SucceededException.cs | 18 + src/internal/WixBuildTools.TestSupport/TestData.cs | 16 + .../TestDataFolderFileSystem.cs | 42 + .../WixBuildTools.TestSupport/VswhereRunner.cs | 41 + .../WixBuildTools.TestSupport/WixAssert.cs | 47 + .../WixBuildTools.TestSupport.csproj | 31 + src/internal/WixBuildTools.XsdGen/AssemblyInfo.cs | 9 + .../WixBuildTools.XsdGen/CodeDomInterfaces.cs | 96 + src/internal/WixBuildTools.XsdGen/CodeDomReader.cs | 159 ++ .../WixBuildTools.XsdGen/ElementCollection.cs | 642 ++++++ .../WixBuildTools.XsdGen/StronglyTypedClasses.cs | 1498 ++++++++++++++ .../WixBuildTools.XsdGen.csproj | 28 + src/internal/WixBuildTools.XsdGen/XsdGen.cs | 124 ++ .../build/WixBuildTools.XsdGen.targets | 67 + .../WixBuildTools.XsdGen.targets | 6 + src/internal/WixBuildTools.sln | 81 + src/internal/WixBuildTools.v3.ncrunchsolution | 6 + src/internal/appveyor.cmd | 13 + src/internal/appveyor.yml | 42 + src/internal/nuget.config | 8 + src/version.json | 11 + version.json | 11 - 130 files changed, 10674 insertions(+), 10674 deletions(-) delete mode 100644 .editorconfig delete mode 100644 README.md delete mode 100644 WixBuildTools.sln delete mode 100644 WixBuildTools.v3.ncrunchsolution delete mode 100644 appveyor.cmd delete mode 100644 appveyor.yml delete mode 100644 nuget.config create mode 100644 src/.editorconfig delete mode 100644 src/CSharp.Build.props delete mode 100644 src/Cpp.Build.props delete mode 100644 src/Directory.Build.props delete mode 100644 src/Directory.Build.targets delete mode 100644 src/MessagesToMessages/MessagesToMessages.csproj delete mode 100644 src/MessagesToMessages/Program.cs delete mode 100644 src/MessagesToMessages/Properties/launchSettings.json delete mode 100644 src/MessagesToMessages/SimpleJson.cs delete mode 100644 src/TablesAndTuples/ColumnDefinitionEnums.cs delete mode 100644 src/TablesAndTuples/Program.cs delete mode 100644 src/TablesAndTuples/SimpleJson.cs delete mode 100644 src/TablesAndTuples/TablesAndTuples.csproj delete mode 100644 src/TablesAndTuples/WixColumnDefinition.cs delete mode 100644 src/TablesAndTuples/WixTableDefinition.cs delete mode 100644 src/WixBuildTools.MsgGen/AssemblyInfo.cs delete mode 100644 src/WixBuildTools.MsgGen/GenerateMessageFiles.cs delete mode 100644 src/WixBuildTools.MsgGen/MsgGen.cs delete mode 100644 src/WixBuildTools.MsgGen/WixBuildTools.MsgGen.csproj delete mode 100644 src/WixBuildTools.MsgGen/Xsd/messages.xsd delete mode 100644 src/WixBuildTools.MsgGen/build/WixBuildTools.MsgGen.targets delete mode 100644 src/WixBuildTools.MsgGen/buildCrossTargeting/WixBuildTools.MsgGen.targets delete mode 100644 src/WixBuildTools.TestSupport.Native/AssemblyInfo.cpp delete mode 100644 src/WixBuildTools.TestSupport.Native/NativeAssert.h delete mode 100644 src/WixBuildTools.TestSupport.Native/WixBuildTools.TestSupport.Native.nuspec delete mode 100644 src/WixBuildTools.TestSupport.Native/WixBuildTools.TestSupport.Native.vcxproj delete mode 100644 src/WixBuildTools.TestSupport.Native/WixBuildTools.TestSupport.Native.vcxproj.filters delete mode 100644 src/WixBuildTools.TestSupport.Native/build/WixBuildTools.TestSupport.Native.props delete mode 100644 src/WixBuildTools.TestSupport.Native/build/WixBuildTools.TestSupport.Native.targets delete mode 100644 src/WixBuildTools.TestSupport.Native/packages.config delete mode 100644 src/WixBuildTools.TestSupport.Native/precomp.cpp delete mode 100644 src/WixBuildTools.TestSupport.Native/precomp.h delete mode 100644 src/WixBuildTools.TestSupport/Builder.cs delete mode 100644 src/WixBuildTools.TestSupport/DisposableFileSystem.cs delete mode 100644 src/WixBuildTools.TestSupport/DotnetRunner.cs delete mode 100644 src/WixBuildTools.TestSupport/ExternalExecutable.cs delete mode 100644 src/WixBuildTools.TestSupport/ExternalExecutableResult.cs delete mode 100644 src/WixBuildTools.TestSupport/FakeBuildEngine.cs delete mode 100644 src/WixBuildTools.TestSupport/MsbuildRunner.cs delete mode 100644 src/WixBuildTools.TestSupport/MsbuildRunnerResult.cs delete mode 100644 src/WixBuildTools.TestSupport/Pushd.cs delete mode 100644 src/WixBuildTools.TestSupport/Query.cs delete mode 100644 src/WixBuildTools.TestSupport/RobocopyRunner.cs delete mode 100644 src/WixBuildTools.TestSupport/SucceededException.cs delete mode 100644 src/WixBuildTools.TestSupport/TestData.cs delete mode 100644 src/WixBuildTools.TestSupport/TestDataFolderFileSystem.cs delete mode 100644 src/WixBuildTools.TestSupport/VswhereRunner.cs delete mode 100644 src/WixBuildTools.TestSupport/WixAssert.cs delete mode 100644 src/WixBuildTools.TestSupport/WixBuildTools.TestSupport.csproj delete mode 100644 src/WixBuildTools.XsdGen/AssemblyInfo.cs delete mode 100644 src/WixBuildTools.XsdGen/CodeDomInterfaces.cs delete mode 100644 src/WixBuildTools.XsdGen/CodeDomReader.cs delete mode 100644 src/WixBuildTools.XsdGen/ElementCollection.cs delete mode 100644 src/WixBuildTools.XsdGen/StronglyTypedClasses.cs delete mode 100644 src/WixBuildTools.XsdGen/WixBuildTools.XsdGen.csproj delete mode 100644 src/WixBuildTools.XsdGen/XsdGen.cs delete mode 100644 src/WixBuildTools.XsdGen/build/WixBuildTools.XsdGen.targets delete mode 100644 src/WixBuildTools.XsdGen/buildCrossTargeting/WixBuildTools.XsdGen.targets create mode 100644 src/internal/CSharp.Build.props create mode 100644 src/internal/Cpp.Build.props create mode 100644 src/internal/Directory.Build.props create mode 100644 src/internal/Directory.Build.targets create mode 100644 src/internal/MessagesToMessages/MessagesToMessages.csproj create mode 100644 src/internal/MessagesToMessages/Program.cs create mode 100644 src/internal/MessagesToMessages/Properties/launchSettings.json create mode 100644 src/internal/MessagesToMessages/SimpleJson.cs create mode 100644 src/internal/README.md create mode 100644 src/internal/TablesAndTuples/ColumnDefinitionEnums.cs create mode 100644 src/internal/TablesAndTuples/Program.cs create mode 100644 src/internal/TablesAndTuples/SimpleJson.cs create mode 100644 src/internal/TablesAndTuples/TablesAndTuples.csproj create mode 100644 src/internal/TablesAndTuples/WixColumnDefinition.cs create mode 100644 src/internal/TablesAndTuples/WixTableDefinition.cs create mode 100644 src/internal/WixBuildTools.MsgGen/AssemblyInfo.cs create mode 100644 src/internal/WixBuildTools.MsgGen/GenerateMessageFiles.cs create mode 100644 src/internal/WixBuildTools.MsgGen/MsgGen.cs create mode 100644 src/internal/WixBuildTools.MsgGen/WixBuildTools.MsgGen.csproj create mode 100644 src/internal/WixBuildTools.MsgGen/Xsd/messages.xsd create mode 100644 src/internal/WixBuildTools.MsgGen/build/WixBuildTools.MsgGen.targets create mode 100644 src/internal/WixBuildTools.MsgGen/buildCrossTargeting/WixBuildTools.MsgGen.targets create mode 100644 src/internal/WixBuildTools.TestSupport.Native/AssemblyInfo.cpp create mode 100644 src/internal/WixBuildTools.TestSupport.Native/NativeAssert.h create mode 100644 src/internal/WixBuildTools.TestSupport.Native/WixBuildTools.TestSupport.Native.nuspec create mode 100644 src/internal/WixBuildTools.TestSupport.Native/WixBuildTools.TestSupport.Native.vcxproj create mode 100644 src/internal/WixBuildTools.TestSupport.Native/WixBuildTools.TestSupport.Native.vcxproj.filters create mode 100644 src/internal/WixBuildTools.TestSupport.Native/build/WixBuildTools.TestSupport.Native.props create mode 100644 src/internal/WixBuildTools.TestSupport.Native/build/WixBuildTools.TestSupport.Native.targets create mode 100644 src/internal/WixBuildTools.TestSupport.Native/packages.config create mode 100644 src/internal/WixBuildTools.TestSupport.Native/precomp.cpp create mode 100644 src/internal/WixBuildTools.TestSupport.Native/precomp.h create mode 100644 src/internal/WixBuildTools.TestSupport/Builder.cs create mode 100644 src/internal/WixBuildTools.TestSupport/DisposableFileSystem.cs create mode 100644 src/internal/WixBuildTools.TestSupport/DotnetRunner.cs create mode 100644 src/internal/WixBuildTools.TestSupport/ExternalExecutable.cs create mode 100644 src/internal/WixBuildTools.TestSupport/ExternalExecutableResult.cs create mode 100644 src/internal/WixBuildTools.TestSupport/FakeBuildEngine.cs create mode 100644 src/internal/WixBuildTools.TestSupport/MsbuildRunner.cs create mode 100644 src/internal/WixBuildTools.TestSupport/MsbuildRunnerResult.cs create mode 100644 src/internal/WixBuildTools.TestSupport/Pushd.cs create mode 100644 src/internal/WixBuildTools.TestSupport/Query.cs create mode 100644 src/internal/WixBuildTools.TestSupport/RobocopyRunner.cs create mode 100644 src/internal/WixBuildTools.TestSupport/SucceededException.cs create mode 100644 src/internal/WixBuildTools.TestSupport/TestData.cs create mode 100644 src/internal/WixBuildTools.TestSupport/TestDataFolderFileSystem.cs create mode 100644 src/internal/WixBuildTools.TestSupport/VswhereRunner.cs create mode 100644 src/internal/WixBuildTools.TestSupport/WixAssert.cs create mode 100644 src/internal/WixBuildTools.TestSupport/WixBuildTools.TestSupport.csproj create mode 100644 src/internal/WixBuildTools.XsdGen/AssemblyInfo.cs create mode 100644 src/internal/WixBuildTools.XsdGen/CodeDomInterfaces.cs create mode 100644 src/internal/WixBuildTools.XsdGen/CodeDomReader.cs create mode 100644 src/internal/WixBuildTools.XsdGen/ElementCollection.cs create mode 100644 src/internal/WixBuildTools.XsdGen/StronglyTypedClasses.cs create mode 100644 src/internal/WixBuildTools.XsdGen/WixBuildTools.XsdGen.csproj create mode 100644 src/internal/WixBuildTools.XsdGen/XsdGen.cs create mode 100644 src/internal/WixBuildTools.XsdGen/build/WixBuildTools.XsdGen.targets create mode 100644 src/internal/WixBuildTools.XsdGen/buildCrossTargeting/WixBuildTools.XsdGen.targets create mode 100644 src/internal/WixBuildTools.sln create mode 100644 src/internal/WixBuildTools.v3.ncrunchsolution create mode 100644 src/internal/appveyor.cmd create mode 100644 src/internal/appveyor.yml create mode 100644 src/internal/nuget.config create mode 100644 src/version.json delete mode 100644 version.json diff --git a/.editorconfig b/.editorconfig deleted file mode 100644 index 1d72e683..00000000 --- a/.editorconfig +++ /dev/null @@ -1,37 +0,0 @@ -# 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. -# -# Do NOT modify this file. Update the canonical version in Home\repo-template\src\.editorconfig -# then update all of the repos. - -root = true - -[*] -charset = utf-8 -indent_style = space -indent_size = 4 -trim_trailing_whitespace = true - -[*.{cs,vb}] -dotnet_sort_system_directives_first = true - -[*.cs] -csharp_indent_case_contents = true : error -csharp_indent_switch_labels = true : error -csharp_new_line_before_open_brace = all -csharp_prefer_braces = true : error -csharp_style_expression_bodied_methods = when_on_single_line : suggestion -csharp_style_expression_bodied_constructors = when_on_single_line : suggestion -csharp_style_expression_bodied_operators = when_on_single_line : suggestion -csharp_style_expression_bodied_properties = when_on_single_line : suggestion -csharp_style_expression_bodied_indexers = when_on_single_line : suggestion -csharp_style_expression_bodied_accessors = when_on_single_line : suggestion -csharp_style_var_elsewhere = true : suggestion -csharp_style_var_for_built_in_types = true : suggestion -csharp_style_var_when_type_is_apparent = true : suggestion -dotnet_style_qualification_for_event = true : error -dotnet_style_qualification_for_field = true : error -dotnet_style_qualification_for_method = true : error -dotnet_style_qualification_for_property = true : error - -[*.targets] -indent_size = 2 diff --git a/README.md b/README.md deleted file mode 100644 index da42c09e..00000000 --- a/README.md +++ /dev/null @@ -1,2 +0,0 @@ -# WixBuildTools -Internal shared props/targets/tools used to build the WiX Toolset diff --git a/WixBuildTools.sln b/WixBuildTools.sln deleted file mode 100644 index 689832fb..00000000 --- a/WixBuildTools.sln +++ /dev/null @@ -1,81 +0,0 @@ -Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio 15 -VisualStudioVersion = 15.0.27130.2003 -MinimumVisualStudioVersion = 15.0.26124.0 -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WixBuildTools.XsdGen", "src\WixBuildTools.XsdGen\WixBuildTools.XsdGen.csproj", "{E89E52C9-A4A1-4174-A1B1-3B72975E6ED6}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WixBuildTools.MsgGen", "src\WixBuildTools.MsgGen\WixBuildTools.MsgGen.csproj", "{DB6EF6F3-51B1-4214-9A14-D501C23F6FA4}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WixBuildTools.TestSupport", "src\WixBuildTools.TestSupport\WixBuildTools.TestSupport.csproj", "{6C57EF2C-979A-4106-A9E5-FE342810619A}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "WixBuildTools.TestSupport.Native", "src\WixBuildTools.TestSupport.Native\WixBuildTools.TestSupport.Native.vcxproj", "{95BABD97-FBDB-453A-AF8A-FA031A07B599}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{F6660B22-092F-4BC5-A303-E6F696C31E1B}" - ProjectSection(SolutionItems) = preProject - .editorconfig = .editorconfig - EndProjectSection -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Any CPU = Debug|Any CPU - Debug|x64 = Debug|x64 - Debug|x86 = Debug|x86 - Release|Any CPU = Release|Any CPU - Release|x64 = Release|x64 - Release|x86 = Release|x86 - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {E89E52C9-A4A1-4174-A1B1-3B72975E6ED6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {E89E52C9-A4A1-4174-A1B1-3B72975E6ED6}.Debug|Any CPU.Build.0 = Debug|Any CPU - {E89E52C9-A4A1-4174-A1B1-3B72975E6ED6}.Debug|x64.ActiveCfg = Debug|Any CPU - {E89E52C9-A4A1-4174-A1B1-3B72975E6ED6}.Debug|x64.Build.0 = Debug|Any CPU - {E89E52C9-A4A1-4174-A1B1-3B72975E6ED6}.Debug|x86.ActiveCfg = Debug|Any CPU - {E89E52C9-A4A1-4174-A1B1-3B72975E6ED6}.Debug|x86.Build.0 = Debug|Any CPU - {E89E52C9-A4A1-4174-A1B1-3B72975E6ED6}.Release|Any CPU.ActiveCfg = Release|Any CPU - {E89E52C9-A4A1-4174-A1B1-3B72975E6ED6}.Release|Any CPU.Build.0 = Release|Any CPU - {E89E52C9-A4A1-4174-A1B1-3B72975E6ED6}.Release|x64.ActiveCfg = Release|Any CPU - {E89E52C9-A4A1-4174-A1B1-3B72975E6ED6}.Release|x64.Build.0 = Release|Any CPU - {E89E52C9-A4A1-4174-A1B1-3B72975E6ED6}.Release|x86.ActiveCfg = Release|Any CPU - {E89E52C9-A4A1-4174-A1B1-3B72975E6ED6}.Release|x86.Build.0 = Release|Any CPU - {DB6EF6F3-51B1-4214-9A14-D501C23F6FA4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {DB6EF6F3-51B1-4214-9A14-D501C23F6FA4}.Debug|Any CPU.Build.0 = Debug|Any CPU - {DB6EF6F3-51B1-4214-9A14-D501C23F6FA4}.Debug|x64.ActiveCfg = Debug|Any CPU - {DB6EF6F3-51B1-4214-9A14-D501C23F6FA4}.Debug|x64.Build.0 = Debug|Any CPU - {DB6EF6F3-51B1-4214-9A14-D501C23F6FA4}.Debug|x86.ActiveCfg = Debug|Any CPU - {DB6EF6F3-51B1-4214-9A14-D501C23F6FA4}.Debug|x86.Build.0 = Debug|Any CPU - {DB6EF6F3-51B1-4214-9A14-D501C23F6FA4}.Release|Any CPU.ActiveCfg = Release|Any CPU - {DB6EF6F3-51B1-4214-9A14-D501C23F6FA4}.Release|Any CPU.Build.0 = Release|Any CPU - {DB6EF6F3-51B1-4214-9A14-D501C23F6FA4}.Release|x64.ActiveCfg = Release|Any CPU - {DB6EF6F3-51B1-4214-9A14-D501C23F6FA4}.Release|x64.Build.0 = Release|Any CPU - {DB6EF6F3-51B1-4214-9A14-D501C23F6FA4}.Release|x86.ActiveCfg = Release|Any CPU - {DB6EF6F3-51B1-4214-9A14-D501C23F6FA4}.Release|x86.Build.0 = Release|Any CPU - {6C57EF2C-979A-4106-A9E5-FE342810619A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {6C57EF2C-979A-4106-A9E5-FE342810619A}.Debug|Any CPU.Build.0 = Debug|Any CPU - {6C57EF2C-979A-4106-A9E5-FE342810619A}.Debug|x64.ActiveCfg = Debug|Any CPU - {6C57EF2C-979A-4106-A9E5-FE342810619A}.Debug|x64.Build.0 = Debug|Any CPU - {6C57EF2C-979A-4106-A9E5-FE342810619A}.Debug|x86.ActiveCfg = Debug|Any CPU - {6C57EF2C-979A-4106-A9E5-FE342810619A}.Debug|x86.Build.0 = Debug|Any CPU - {6C57EF2C-979A-4106-A9E5-FE342810619A}.Release|Any CPU.ActiveCfg = Release|Any CPU - {6C57EF2C-979A-4106-A9E5-FE342810619A}.Release|Any CPU.Build.0 = Release|Any CPU - {6C57EF2C-979A-4106-A9E5-FE342810619A}.Release|x64.ActiveCfg = Release|Any CPU - {6C57EF2C-979A-4106-A9E5-FE342810619A}.Release|x64.Build.0 = Release|Any CPU - {6C57EF2C-979A-4106-A9E5-FE342810619A}.Release|x86.ActiveCfg = Release|Any CPU - {6C57EF2C-979A-4106-A9E5-FE342810619A}.Release|x86.Build.0 = Release|Any CPU - {95BABD97-FBDB-453A-AF8A-FA031A07B599}.Debug|Any CPU.ActiveCfg = Debug|Win32 - {95BABD97-FBDB-453A-AF8A-FA031A07B599}.Debug|Any CPU.Build.0 = Debug|Win32 - {95BABD97-FBDB-453A-AF8A-FA031A07B599}.Debug|x64.ActiveCfg = Debug|Win32 - {95BABD97-FBDB-453A-AF8A-FA031A07B599}.Debug|x86.ActiveCfg = Debug|Win32 - {95BABD97-FBDB-453A-AF8A-FA031A07B599}.Debug|x86.Build.0 = Debug|Win32 - {95BABD97-FBDB-453A-AF8A-FA031A07B599}.Release|Any CPU.ActiveCfg = Release|Win32 - {95BABD97-FBDB-453A-AF8A-FA031A07B599}.Release|Any CPU.Build.0 = Release|Win32 - {95BABD97-FBDB-453A-AF8A-FA031A07B599}.Release|x64.ActiveCfg = Release|Win32 - {95BABD97-FBDB-453A-AF8A-FA031A07B599}.Release|x86.ActiveCfg = Release|Win32 - {95BABD97-FBDB-453A-AF8A-FA031A07B599}.Release|x86.Build.0 = Release|Win32 - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection - GlobalSection(ExtensibilityGlobals) = postSolution - SolutionGuid = {83E9E075-B440-471A-9C37-9D84BA0AE3E0} - EndGlobalSection -EndGlobal diff --git a/WixBuildTools.v3.ncrunchsolution b/WixBuildTools.v3.ncrunchsolution deleted file mode 100644 index 10420ac9..00000000 --- a/WixBuildTools.v3.ncrunchsolution +++ /dev/null @@ -1,6 +0,0 @@ - - - True - True - - \ No newline at end of file diff --git a/appveyor.cmd b/appveyor.cmd deleted file mode 100644 index 9fa5e330..00000000 --- a/appveyor.cmd +++ /dev/null @@ -1,13 +0,0 @@ -@setlocal -@pushd %~dp0 - -nuget restore || exit /b - -dotnet pack -c Release src\WixBuildTools.MsgGen\WixBuildTools.MsgGen.csproj || exit /b -dotnet pack -c Release src\WixBuildTools.TestSupport\WixBuildTools.TestSupport.csproj || exit /b -dotnet pack -c Release src\WixBuildTools.XsdGen\WixBuildTools.XsdGen.csproj || exit /b - -msbuild -p:Configuration=Release -t:PackNativeNuget src\WixBuildTools.TestSupport.Native\WixBuildTools.TestSupport.Native.vcxproj || exit /b - -@popd -@endlocal \ No newline at end of file diff --git a/appveyor.yml b/appveyor.yml deleted file mode 100644 index 522e5af3..00000000 --- a/appveyor.yml +++ /dev/null @@ -1,42 +0,0 @@ -# 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. -# -# Do NOT modify this file. Update the canonical version in Home\repo-template\src\appveyor.yml -# then update all of the repos. - -branches: - only: - - master - - develop - -image: Visual Studio 2019 - -version: 0.0.0.{build} -configuration: Release - -environment: - DOTNET_SKIP_FIRST_TIME_EXPERIENCE: true - DOTNET_CLI_TELEMETRY_OPTOUT: 1 - NUGET_XMLDOC_MODE: skip - -build_script: - - appveyor.cmd - -pull_requests: - do_not_increment_build_number: true - -nuget: - disable_publish_on_pr: true - -skip_branch_with_pr: true -skip_tags: true - -artifacts: -- path: build\Release\**\*.nupkg - name: nuget -- path: build\Release\**\*.msi - name: msi - -notifications: -- provider: Slack - incoming_webhook: - secure: p5xuu+4x2JHfwGDMDe5KcG1k7gZxqYc4jWVwvyNZv5cvkubPD2waJs5yXMAXZNN7Z63/3PWHb7q4KoY/99AjauYa1nZ4c5qYqRPFRBKTHfA= diff --git a/nuget.config b/nuget.config deleted file mode 100644 index fcbac27a..00000000 --- a/nuget.config +++ /dev/null @@ -1,8 +0,0 @@ - - - - - - - - \ No newline at end of file diff --git a/src/.editorconfig b/src/.editorconfig new file mode 100644 index 00000000..1d72e683 --- /dev/null +++ b/src/.editorconfig @@ -0,0 +1,37 @@ +# 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. +# +# Do NOT modify this file. Update the canonical version in Home\repo-template\src\.editorconfig +# then update all of the repos. + +root = true + +[*] +charset = utf-8 +indent_style = space +indent_size = 4 +trim_trailing_whitespace = true + +[*.{cs,vb}] +dotnet_sort_system_directives_first = true + +[*.cs] +csharp_indent_case_contents = true : error +csharp_indent_switch_labels = true : error +csharp_new_line_before_open_brace = all +csharp_prefer_braces = true : error +csharp_style_expression_bodied_methods = when_on_single_line : suggestion +csharp_style_expression_bodied_constructors = when_on_single_line : suggestion +csharp_style_expression_bodied_operators = when_on_single_line : suggestion +csharp_style_expression_bodied_properties = when_on_single_line : suggestion +csharp_style_expression_bodied_indexers = when_on_single_line : suggestion +csharp_style_expression_bodied_accessors = when_on_single_line : suggestion +csharp_style_var_elsewhere = true : suggestion +csharp_style_var_for_built_in_types = true : suggestion +csharp_style_var_when_type_is_apparent = true : suggestion +dotnet_style_qualification_for_event = true : error +dotnet_style_qualification_for_field = true : error +dotnet_style_qualification_for_method = true : error +dotnet_style_qualification_for_property = true : error + +[*.targets] +indent_size = 2 diff --git a/src/CSharp.Build.props b/src/CSharp.Build.props deleted file mode 100644 index 81d24ad1..00000000 --- a/src/CSharp.Build.props +++ /dev/null @@ -1,13 +0,0 @@ - - - - - true - true - $([System.IO.Path]::GetFullPath($(MSBuildThisFileDirectory)wix.snk)) - false - - diff --git a/src/Cpp.Build.props b/src/Cpp.Build.props deleted file mode 100644 index 44a042c7..00000000 --- a/src/Cpp.Build.props +++ /dev/null @@ -1,104 +0,0 @@ - - - - - - Win32 - $(BaseIntermediateOutputPath)$(Configuration)\$(Platform)\ - $(OutputPath)$(Platform)\ - - - - $([Microsoft.Build.Utilities.ToolLocationHelper]::GetLatestSDKTargetPlatformVersion('Windows', '10.0')) - - - - - $(DisableSpecificCompilerWarnings) - Level4 - $(ProjectDir)inc;$(MSBuildProjectDirectory);$(IntDir);$(SqlCESdkIncludePath);$(ProjectAdditionalIncludeDirectories);%(AdditionalIncludeDirectories) - WIN32;_WINDOWS;_WIN32_MSI=500;_WIN32_WINNT=0x0501;$(ArmPreprocessorDefinitions);$(UnicodePreprocessorDefinitions);_CRT_STDIO_LEGACY_WIDE_SPECIFIERS;_WINSOCK_DEPRECATED_NO_WARNINGS;%(PreprocessorDefinitions) - Use - precomp.h - StdCall - true - false - -YlprecompDefine - /Zc:threadSafeInit- %(AdditionalOptions) - true - - - $(ArmPreprocessorDefinitions);%(PreprocessorDefinitions) - $(ProjectAdditionalResourceIncludeDirectories);%(AdditionalIncludeDirectories) - - - $(OutDir);$(AdditionalMultiTargetLibraryPath);$(ProjectAdditionalLibraryDirectories);%(AdditionalLibraryDirectories) - - - $(ProjectSubSystem) - $(ProjectModuleDefinitionFile) - $(ResourceOnlyDll) - true - $(ProjectAdditionalLinkLibraries);advapi32.lib;comdlg32.lib;user32.lib;oleaut32.lib;gdi32.lib;shell32.lib;ole32.lib;version.lib;%(AdditionalDependencies) - $(OutDir);$(AdditionalMultiTargetLibraryPath);$(ArmLibraryDirectories);$(ProjectAdditionalLinkLibraryDirectories);%(AdditionalLibraryDirectories) - /IGNORE:4099 %(AdditionalOptions) - - - - - - NoExtensions - - - - - CDecl - - - - - OldStyle - true - true - - - - - Disabled - EnableFastChecks - _DEBUG;DEBUG;%(PreprocessorDefinitions) - MultiThreadedDebug - - - - - - MultiThreadedDebugDll - - - - - MinSpace - NDEBUG;%(PreprocessorDefinitions) - true - true - MultiThreaded - - - true - true - - - - - - MultiThreadedDll - - - - - $(LinkKeyFile) - $(LinkDelaySign) - - - diff --git a/src/Directory.Build.props b/src/Directory.Build.props deleted file mode 100644 index f83cc154..00000000 --- a/src/Directory.Build.props +++ /dev/null @@ -1,29 +0,0 @@ - - - - - - Debug - false - MSB3246 - - $(MSBuildProjectName) - $([System.IO.Path]::GetFullPath($(MSBuildThisFileDirectory)..\build\)) - $(BaseOutputPath)obj\$(ProjectName)\ - $(BaseOutputPath)$(Configuration)\ - - WiX Toolset Team - WiX Toolset - Copyright (c) .NET Foundation and contributors. All rights reserved. - MS-RL - WiX Toolset - - - - - - - diff --git a/src/Directory.Build.targets b/src/Directory.Build.targets deleted file mode 100644 index cb988931..00000000 --- a/src/Directory.Build.targets +++ /dev/null @@ -1,56 +0,0 @@ - - - - - - - false - $(OutputPath)\$(AssemblyName).xml - - - - true - $(SolutionPath) - $(NCrunchOriginalSolutionPath) - - - - - - - $([System.IO.File]::ReadAllText($(TheSolutionPath))) - $([System.IO.Path]::GetDirectoryName( $(TheSolutionPath) )) - (?<="[PackageName]", ")(.*)(?=", ") - - - - - - %(Identity) - $(SolutionFileContent.Contains('\%(Identity).csproj')) - - - - - $(RegexPattern.Replace('[PackageName]','%(PackageName)') ) - $([System.Text.RegularExpressions.Regex]::Match('$(SolutionFileContent)', '%(Pattern)')) - - - - - - - - - - - - - - diff --git a/src/MessagesToMessages/MessagesToMessages.csproj b/src/MessagesToMessages/MessagesToMessages.csproj deleted file mode 100644 index ce1697ae..00000000 --- a/src/MessagesToMessages/MessagesToMessages.csproj +++ /dev/null @@ -1,8 +0,0 @@ - - - - Exe - netcoreapp2.0 - - - diff --git a/src/MessagesToMessages/Program.cs b/src/MessagesToMessages/Program.cs deleted file mode 100644 index 2d5a3777..00000000 --- a/src/MessagesToMessages/Program.cs +++ /dev/null @@ -1,261 +0,0 @@ -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Text; -using System.Xml.Linq; - -namespace MessagesToMessages -{ - class Program - { - static readonly XNamespace ns = "http://schemas.microsoft.com/genmsgs/2004/07/messages"; - static readonly XName ClassDefinition = ns + "Class"; - static readonly XName MessageDefinition = ns + "Message"; - static readonly XName InstanceDefinition = ns + "Instance"; - static readonly XName ParameterDefinition = ns + "Parameter"; - static readonly XName Id = "Id"; - static readonly XName Level = "Level"; - static readonly XName Number = "Number"; - static readonly XName SourceLineNumbers = "SourceLineNumbers"; - static readonly XName Type = "Type"; - static readonly XName Name = "Name"; - - static void Main(string[] args) - { - if (args.Length == 0) - { - return; - } - else if (args.Length < 2) - { - Console.WriteLine("Need to specify output folder as well."); - } - else if (!Directory.Exists(args[1])) - { - Console.WriteLine("Output folder does not exist: {0}", args[1]); - } - - var messages = ReadXml(Path.GetFullPath(args[0])); - - foreach (var m in messages.GroupBy(m => m.Level)) - { - var className = m.First().ClassName; - var result = GenerateCs(className, m.Key, m); - - var path = Path.Combine(args[1], className + ".cs"); - File.WriteAllText(path, result); - } - } - - private static IEnumerable ReadXml(string inputPath) - { - var doc = XDocument.Load(inputPath); - - foreach (var xClass in doc.Root.Descendants(ClassDefinition)) - { - var name = xClass.Attribute(Name)?.Value; - var level = xClass.Attribute(Level)?.Value; - - if (String.IsNullOrEmpty(name)) - { - name = level + "Messages"; - } - if (String.IsNullOrEmpty(level)) - { - if (name.EndsWith("Errors", StringComparison.InvariantCultureIgnoreCase)) - { - level = "Error"; - } - else if (name.EndsWith("Verboses", StringComparison.InvariantCultureIgnoreCase)) - { - level = "Verbose"; - } - else if (name.EndsWith("Warnings", StringComparison.InvariantCultureIgnoreCase)) - { - level = "Warning"; - } - } - - var unique = new HashSet(); - var lastNumber = 0; - foreach (var xMessage in xClass.Elements(MessageDefinition)) - { - var id = xMessage.Attribute(Id).Value; - - if (!unique.Add(id)) - { - Console.WriteLine("Duplicated message: {0}", id); - } - - if (!Int32.TryParse(xMessage.Attribute(Number)?.Value, out var number)) - { - number = lastNumber; - } - lastNumber = number + 1; - - var sln = xMessage.Attribute(SourceLineNumbers)?.Value != "no"; - - var suffix = 0; - foreach (var xInstance in xMessage.Elements(InstanceDefinition)) - { - var value = xInstance.Value.Trim(); - - var parameters = xInstance.Elements(ParameterDefinition).Select(ReadParameter).ToList(); - - 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() }; - - ++suffix; - } - } - } - } - - private static Parameter ReadParameter(XElement element) - { - var name = element.Attribute(Name)?.Value; - var type = element.Attribute(Type)?.Value ?? "string"; - - if (type.StartsWith("System.")) - { - type = type.Substring(7); - } - - switch (type) - { - case "String": - type = "string"; - break; - - case "Int32": - type = "int"; - break; - - case "Int64": - type = "long"; - break; - } - - return new Parameter { Name = name, Type = type }; - } - - private static string GenerateCs(string className, string level, IEnumerable messages) - { - var header = String.Join(Environment.NewLine, - "// 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.", - "", - "namespace WixToolset.Data", - "{", - " using System;", - " using System.Resources;", - "", - " public static class {0}", - " {"); - - var messageFormat = String.Join(Environment.NewLine, - " public static Message {0}({1})", - " {{", - " return Message({4}, Ids.{0}, \"{3}\"{2});", - " }}", - ""); - - - var endMessagesFormat = String.Join(Environment.NewLine, - " private static Message Message(SourceLineNumber sourceLineNumber, Ids id, string format, params object[] args)", - " {{", - " return new Message(sourceLineNumber, MessageLevel.{0}, (int)id, format, args);", - " }}", - "", - " private static Message Message(SourceLineNumber sourceLineNumber, Ids id, ResourceManager resourceManager, string resourceName, params object[] args)", - " {{", - " return new Message(sourceLineNumber, MessageLevel.{0}, (int)id, resourceManager, resourceName, args);", - " }}", - "", - " public enum Ids", - " {{"); - - var idEnumFormat = - " {0} = {1},"; - var footer = String.Join(Environment.NewLine, - " }", - " }", - "}", - ""); - - var sb = new StringBuilder(); - - sb.AppendLine(header.Replace("{0}", className)); - - foreach (var message in messages.OrderBy(m => m.Id)) - { - var paramsWithTypes = String.Join(", ", message.Parameters.Select(p => $"{p.Type} {p.Name}")); - var paramsWithoutTypes = String.Join(", ", message.Parameters.Select(p => p.Name)); - - if (message.SourceLineNumbers) - { - paramsWithTypes = String.IsNullOrEmpty(paramsWithTypes) - ? "SourceLineNumber sourceLineNumbers" - : "SourceLineNumber sourceLineNumbers, " + paramsWithTypes; - } - - if (!String.IsNullOrEmpty(paramsWithoutTypes)) - { - paramsWithoutTypes = ", " + paramsWithoutTypes; - } - - sb.AppendFormat(messageFormat, message.Id, paramsWithTypes, paramsWithoutTypes, ToCSharpString(message.Value), message.SourceLineNumbers ? "sourceLineNumbers" : "null"); - - sb.AppendLine(); - } - - sb.AppendFormat(endMessagesFormat, level); - sb.AppendLine(); - - var unique = new HashSet(); - foreach (var message in messages.OrderBy(m => m.Number)) - { - if (unique.Add(message.Number)) - { - sb.AppendFormat(idEnumFormat, message.Id, message.Number); - sb.AppendLine(); - } - } - - sb.Append(footer); - - return sb.ToString(); - } - - private static string ToCSharpString(string value) - { - return value.Replace("\\", "\\\\").Replace("\"", "\\\"").Replace("\r", "\\r").Replace("\n", "\\n").Replace("\t", "\\t"); - } - - private class Message - { - public string Id { get; set; } - - public string Suffix { get; set; } - - public string ClassName { get; set; } - - public string Level { get; set; } - - public int Number { get; set; } - - public bool SourceLineNumbers { get; set; } - - public string Value { get; set; } - - public IEnumerable Parameters { get; set; } - } - - - private class Parameter - { - public string Name { get; set; } - - public string Type { get; set; } - } - } -} diff --git a/src/MessagesToMessages/Properties/launchSettings.json b/src/MessagesToMessages/Properties/launchSettings.json deleted file mode 100644 index dc7570f6..00000000 --- a/src/MessagesToMessages/Properties/launchSettings.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "profiles": { - "TablesAndSymbols": { - "commandName": "Project", - "commandLineArgs": "E:\\src\\wixtoolset\\Core\\src\\WixToolset.Core\\Data\\messages.xml E:\\src\\wixtoolset\\Data\\src\\WixToolset.Data", - "workingDirectory": "E:\\src\\wixtoolset\\Core\\src\\WixToolset.Core\\" - } - } -} \ No newline at end of file diff --git a/src/MessagesToMessages/SimpleJson.cs b/src/MessagesToMessages/SimpleJson.cs deleted file mode 100644 index 3d956f6e..00000000 --- a/src/MessagesToMessages/SimpleJson.cs +++ /dev/null @@ -1,2127 +0,0 @@ -//----------------------------------------------------------------------- -// -// Copyright (c) 2011, The Outercurve Foundation. -// -// Licensed under the MIT License (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// http://www.opensource.org/licenses/mit-license.php -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -// Nathan Totten (ntotten.com), Jim Zimmerman (jimzimmerman.com) and Prabir Shrestha (prabir.me) -// https://github.com/facebook-csharp-sdk/simple-json -//----------------------------------------------------------------------- - -// VERSION: - -// NOTE: uncomment the following line to make SimpleJson class internal. -#define SIMPLE_JSON_INTERNAL - -// NOTE: uncomment the following line to make JsonArray and JsonObject class internal. -#define SIMPLE_JSON_OBJARRAYINTERNAL - -// NOTE: uncomment the following line to enable dynamic support. -//#define SIMPLE_JSON_DYNAMIC - -// NOTE: uncomment the following line to enable DataContract support. -//#define SIMPLE_JSON_DATACONTRACT - -// NOTE: uncomment the following line to enable IReadOnlyCollection and IReadOnlyList support. -//#define SIMPLE_JSON_READONLY_COLLECTIONS - -// NOTE: uncomment the following line to disable linq expressions/compiled lambda (better performance) instead of method.invoke(). -// define if you are using .net framework <= 3.0 or < WP7.5 -//#define SIMPLE_JSON_NO_LINQ_EXPRESSION - -// NOTE: uncomment the following line if you are compiling under Window Metro style application/library. -// usually already defined in properties -//#define NETFX_CORE; - -// If you are targetting WinStore, WP8 and NET4.5+ PCL make sure to #define SIMPLE_JSON_TYPEINFO; - -// 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 - -#if NETFX_CORE -#define SIMPLE_JSON_TYPEINFO -#endif - -using System; -using System.CodeDom.Compiler; -using System.Collections; -using System.Collections.Generic; -#if !SIMPLE_JSON_NO_LINQ_EXPRESSION -using System.Linq.Expressions; -#endif -using System.ComponentModel; -using System.Diagnostics.CodeAnalysis; -#if SIMPLE_JSON_DYNAMIC -using System.Dynamic; -#endif -using System.Globalization; -using System.Reflection; -using System.Runtime.Serialization; -using System.Text; -using SimpleJson.Reflection; - -// ReSharper disable LoopCanBeConvertedToQuery -// ReSharper disable RedundantExplicitArrayCreation -// ReSharper disable SuggestUseVarKeywordEvident -namespace SimpleJson -{ - /// - /// Represents the json array. - /// - [GeneratedCode("simple-json", "1.0.0")] - [EditorBrowsable(EditorBrowsableState.Never)] - [SuppressMessage("Microsoft.Naming", "CA1710:IdentifiersShouldHaveCorrectSuffix")] -#if SIMPLE_JSON_OBJARRAYINTERNAL - internal -#else - public -#endif - class JsonArray : List - { - /// - /// Initializes a new instance of the class. - /// - public JsonArray() { } - - /// - /// Initializes a new instance of the class. - /// - /// The capacity of the json array. - public JsonArray(int capacity) : base(capacity) { } - - /// - /// The json representation of the array. - /// - /// The json representation of the array. - public override string ToString() - { - return SimpleJson.SerializeObject(this) ?? string.Empty; - } - } - - /// - /// Represents the json object. - /// - [GeneratedCode("simple-json", "1.0.0")] - [EditorBrowsable(EditorBrowsableState.Never)] - [SuppressMessage("Microsoft.Naming", "CA1710:IdentifiersShouldHaveCorrectSuffix")] -#if SIMPLE_JSON_OBJARRAYINTERNAL - internal -#else - public -#endif - class JsonObject : -#if SIMPLE_JSON_DYNAMIC - DynamicObject, -#endif - IDictionary - { - /// - /// The internal member dictionary. - /// - private readonly Dictionary _members; - - /// - /// Initializes a new instance of . - /// - public JsonObject() - { - _members = new Dictionary(); - } - - /// - /// Initializes a new instance of . - /// - /// The implementation to use when comparing keys, or null to use the default for the type of the key. - public JsonObject(IEqualityComparer comparer) - { - _members = new Dictionary(comparer); - } - - /// - /// Gets the at the specified index. - /// - /// - public object this[int index] - { - get { return GetAtIndex(_members, index); } - } - - internal static object GetAtIndex(IDictionary obj, int index) - { - if (obj == null) - throw new ArgumentNullException("obj"); - if (index >= obj.Count) - throw new ArgumentOutOfRangeException("index"); - int i = 0; - foreach (KeyValuePair o in obj) - if (i++ == index) return o.Value; - return null; - } - - /// - /// Adds the specified key. - /// - /// The key. - /// The value. - public void Add(string key, object value) - { - _members.Add(key, value); - } - - /// - /// Determines whether the specified key contains key. - /// - /// The key. - /// - /// true if the specified key contains key; otherwise, false. - /// - public bool ContainsKey(string key) - { - return _members.ContainsKey(key); - } - - /// - /// Gets the keys. - /// - /// The keys. - public ICollection Keys - { - get { return _members.Keys; } - } - - /// - /// Removes the specified key. - /// - /// The key. - /// - public bool Remove(string key) - { - return _members.Remove(key); - } - - /// - /// Tries the get value. - /// - /// The key. - /// The value. - /// - public bool TryGetValue(string key, out object value) - { - return _members.TryGetValue(key, out value); - } - - /// - /// Gets the values. - /// - /// The values. - public ICollection Values - { - get { return _members.Values; } - } - - /// - /// Gets or sets the with the specified key. - /// - /// - public object this[string key] - { - get { return _members[key]; } - set { _members[key] = value; } - } - - /// - /// Adds the specified item. - /// - /// The item. - public void Add(KeyValuePair item) - { - _members.Add(item.Key, item.Value); - } - - /// - /// Clears this instance. - /// - public void Clear() - { - _members.Clear(); - } - - /// - /// Determines whether [contains] [the specified item]. - /// - /// The item. - /// - /// true if [contains] [the specified item]; otherwise, false. - /// - public bool Contains(KeyValuePair item) - { - return _members.ContainsKey(item.Key) && _members[item.Key] == item.Value; - } - - /// - /// Copies to. - /// - /// The array. - /// Index of the array. - public void CopyTo(KeyValuePair[] array, int arrayIndex) - { - if (array == null) throw new ArgumentNullException("array"); - int num = Count; - foreach (KeyValuePair kvp in this) - { - array[arrayIndex++] = kvp; - if (--num <= 0) - return; - } - } - - /// - /// Gets the count. - /// - /// The count. - public int Count - { - get { return _members.Count; } - } - - /// - /// Gets a value indicating whether this instance is read only. - /// - /// - /// true if this instance is read only; otherwise, false. - /// - public bool IsReadOnly - { - get { return false; } - } - - /// - /// Removes the specified item. - /// - /// The item. - /// - public bool Remove(KeyValuePair item) - { - return _members.Remove(item.Key); - } - - /// - /// Gets the enumerator. - /// - /// - public IEnumerator> GetEnumerator() - { - return _members.GetEnumerator(); - } - - /// - /// Returns an enumerator that iterates through a collection. - /// - /// - /// An object that can be used to iterate through the collection. - /// - IEnumerator IEnumerable.GetEnumerator() - { - return _members.GetEnumerator(); - } - - /// - /// Returns a json that represents the current . - /// - /// - /// A json that represents the current . - /// - public override string ToString() - { - return SimpleJson.SerializeObject(this); - } - -#if SIMPLE_JSON_DYNAMIC - /// - /// Provides implementation for type conversion operations. Classes derived from the class can override this method to specify dynamic behavior for operations that convert an object from one type to another. - /// - /// 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 class, binder.Type returns the 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. - /// The result of the type conversion operation. - /// - /// Alwasy returns true. - /// - public override bool TryConvert(ConvertBinder binder, out object result) - { - // - if (binder == null) - throw new ArgumentNullException("binder"); - // - Type targetType = binder.Type; - - if ((targetType == typeof(IEnumerable)) || - (targetType == typeof(IEnumerable>)) || - (targetType == typeof(IDictionary)) || - (targetType == typeof(IDictionary))) - { - result = this; - return true; - } - - return base.TryConvert(binder, out result); - } - - /// - /// Provides the implementation for operations that delete an object member. This method is not intended for use in C# or Visual Basic. - /// - /// Provides information about the deletion. - /// - /// Alwasy returns true. - /// - public override bool TryDeleteMember(DeleteMemberBinder binder) - { - // - if (binder == null) - throw new ArgumentNullException("binder"); - // - return _members.Remove(binder.Name); - } - - /// - /// Provides the implementation for operations that get a value by index. Classes derived from the class can override this method to specify dynamic behavior for indexing operations. - /// - /// Provides information about the operation. - /// 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, is equal to 3. - /// The result of the index operation. - /// - /// Alwasy returns true. - /// - public override bool TryGetIndex(GetIndexBinder binder, object[] indexes, out object result) - { - if (indexes == null) throw new ArgumentNullException("indexes"); - if (indexes.Length == 1) - { - result = ((IDictionary)this)[(string)indexes[0]]; - return true; - } - result = null; - return true; - } - - /// - /// Provides the implementation for operations that get member values. Classes derived from the class can override this method to specify dynamic behavior for operations such as getting a value for a property. - /// - /// 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 class, binder.Name returns "SampleProperty". The binder.IgnoreCase property specifies whether the member name is case-sensitive. - /// The result of the get operation. For example, if the method is called for a property, you can assign the property value to . - /// - /// Alwasy returns true. - /// - public override bool TryGetMember(GetMemberBinder binder, out object result) - { - object value; - if (_members.TryGetValue(binder.Name, out value)) - { - result = value; - return true; - } - result = null; - return true; - } - - /// - /// Provides the implementation for operations that set a value by index. Classes derived from the class can override this method to specify dynamic behavior for operations that access objects by a specified index. - /// - /// Provides information about the operation. - /// 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 class, is equal to 3. - /// 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 class, is equal to 10. - /// - /// 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. - /// - public override bool TrySetIndex(SetIndexBinder binder, object[] indexes, object value) - { - if (indexes == null) throw new ArgumentNullException("indexes"); - if (indexes.Length == 1) - { - ((IDictionary)this)[(string)indexes[0]] = value; - return true; - } - return base.TrySetIndex(binder, indexes, value); - } - - /// - /// Provides the implementation for operations that set member values. Classes derived from the class can override this method to specify dynamic behavior for operations such as setting a value for a property. - /// - /// 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 class, binder.Name returns "SampleProperty". The binder.IgnoreCase property specifies whether the member name is case-sensitive. - /// The value to set to the member. For example, for sampleObject.SampleProperty = "Test", where sampleObject is an instance of the class derived from the class, the is "Test". - /// - /// 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.) - /// - public override bool TrySetMember(SetMemberBinder binder, object value) - { - // - if (binder == null) - throw new ArgumentNullException("binder"); - // - _members[binder.Name] = value; - return true; - } - - /// - /// Returns the enumeration of all dynamic member names. - /// - /// - /// A sequence that contains dynamic member names. - /// - public override IEnumerable GetDynamicMemberNames() - { - foreach (var key in Keys) - yield return key; - } -#endif - } -} - -namespace SimpleJson -{ - /// - /// This class encodes and decodes JSON strings. - /// Spec. details, see http://www.json.org/ - /// - /// JSON uses Arrays and Objects. These correspond here to the datatypes JsonArray(IList<object>) and JsonObject(IDictionary<string,object>). - /// All numbers are parsed to doubles. - /// - [GeneratedCode("simple-json", "1.0.0")] -#if SIMPLE_JSON_INTERNAL - internal -#else - public -#endif - static class SimpleJson - { - private const int TOKEN_NONE = 0; - private const int TOKEN_CURLY_OPEN = 1; - private const int TOKEN_CURLY_CLOSE = 2; - private const int TOKEN_SQUARED_OPEN = 3; - private const int TOKEN_SQUARED_CLOSE = 4; - private const int TOKEN_COLON = 5; - private const int TOKEN_COMMA = 6; - private const int TOKEN_STRING = 7; - private const int TOKEN_NUMBER = 8; - private const int TOKEN_TRUE = 9; - private const int TOKEN_FALSE = 10; - private const int TOKEN_NULL = 11; - private const int BUILDER_CAPACITY = 2000; - - private static readonly char[] EscapeTable; - private static readonly char[] EscapeCharacters = new char[] { '"', '\\', '\b', '\f', '\n', '\r', '\t' }; - private static readonly string EscapeCharactersString = new string(EscapeCharacters); - - static SimpleJson() - { - EscapeTable = new char[93]; - EscapeTable['"'] = '"'; - EscapeTable['\\'] = '\\'; - EscapeTable['\b'] = 'b'; - EscapeTable['\f'] = 'f'; - EscapeTable['\n'] = 'n'; - EscapeTable['\r'] = 'r'; - EscapeTable['\t'] = 't'; - } - - /// - /// Parses the string json into a value - /// - /// A JSON string. - /// An IList<object>, a IDictionary<string,object>, a double, a string, null, true, or false - public static object DeserializeObject(string json) - { - object obj; - if (TryDeserializeObject(json, out obj)) - return obj; - throw new SerializationException("Invalid JSON string"); - } - - /// - /// Try parsing the json string into a value. - /// - /// - /// A JSON string. - /// - /// - /// The object. - /// - /// - /// Returns true if successfull otherwise false. - /// - [SuppressMessage("Microsoft.Design", "CA1007:UseGenericsWhereAppropriate", Justification = "Need to support .NET 2")] - public static bool TryDeserializeObject(string json, out object obj) - { - bool success = true; - if (json != null) - { - char[] charArray = json.ToCharArray(); - int index = 0; - obj = ParseValue(charArray, ref index, ref success); - } - else - obj = null; - - return success; - } - - public static object DeserializeObject(string json, Type type, IJsonSerializerStrategy jsonSerializerStrategy) - { - object jsonObject = DeserializeObject(json); - return type == null || jsonObject != null && ReflectionUtils.IsAssignableFrom(jsonObject.GetType(), type) - ? jsonObject - : (jsonSerializerStrategy ?? CurrentJsonSerializerStrategy).DeserializeObject(jsonObject, type); - } - - public static object DeserializeObject(string json, Type type) - { - return DeserializeObject(json, type, null); - } - - public static T DeserializeObject(string json, IJsonSerializerStrategy jsonSerializerStrategy) - { - return (T)DeserializeObject(json, typeof(T), jsonSerializerStrategy); - } - - public static T DeserializeObject(string json) - { - return (T)DeserializeObject(json, typeof(T), null); - } - - /// - /// Converts a IDictionary<string,object> / IList<object> object into a JSON string - /// - /// A IDictionary<string,object> / IList<object> - /// Serializer strategy to use - /// A JSON encoded string, or null if object 'json' is not serializable - public static string SerializeObject(object json, IJsonSerializerStrategy jsonSerializerStrategy) - { - StringBuilder builder = new StringBuilder(BUILDER_CAPACITY); - bool success = SerializeValue(jsonSerializerStrategy, json, builder); - return (success ? builder.ToString() : null); - } - - public static string SerializeObject(object json) - { - return SerializeObject(json, CurrentJsonSerializerStrategy); - } - - public static string EscapeToJavascriptString(string jsonString) - { - if (string.IsNullOrEmpty(jsonString)) - return jsonString; - - StringBuilder sb = new StringBuilder(); - char c; - - for (int i = 0; i < jsonString.Length;) - { - c = jsonString[i++]; - - if (c == '\\') - { - int remainingLength = jsonString.Length - i; - if (remainingLength >= 2) - { - char lookahead = jsonString[i]; - if (lookahead == '\\') - { - sb.Append('\\'); - ++i; - } - else if (lookahead == '"') - { - sb.Append("\""); - ++i; - } - else if (lookahead == 't') - { - sb.Append('\t'); - ++i; - } - else if (lookahead == 'b') - { - sb.Append('\b'); - ++i; - } - else if (lookahead == 'n') - { - sb.Append('\n'); - ++i; - } - else if (lookahead == 'r') - { - sb.Append('\r'); - ++i; - } - } - } - else - { - sb.Append(c); - } - } - return sb.ToString(); - } - - static IDictionary ParseObject(char[] json, ref int index, ref bool success) - { - IDictionary table = new JsonObject(); - int token; - - // { - NextToken(json, ref index); - - bool done = false; - while (!done) - { - token = LookAhead(json, index); - if (token == TOKEN_NONE) - { - success = false; - return null; - } - else if (token == TOKEN_COMMA) - NextToken(json, ref index); - else if (token == TOKEN_CURLY_CLOSE) - { - NextToken(json, ref index); - return table; - } - else - { - // name - string name = ParseString(json, ref index, ref success); - if (!success) - { - success = false; - return null; - } - // : - token = NextToken(json, ref index); - if (token != TOKEN_COLON) - { - success = false; - return null; - } - // value - object value = ParseValue(json, ref index, ref success); - if (!success) - { - success = false; - return null; - } - table[name] = value; - } - } - return table; - } - - static JsonArray ParseArray(char[] json, ref int index, ref bool success) - { - JsonArray array = new JsonArray(); - - // [ - NextToken(json, ref index); - - bool done = false; - while (!done) - { - int token = LookAhead(json, index); - if (token == TOKEN_NONE) - { - success = false; - return null; - } - else if (token == TOKEN_COMMA) - NextToken(json, ref index); - else if (token == TOKEN_SQUARED_CLOSE) - { - NextToken(json, ref index); - break; - } - else - { - object value = ParseValue(json, ref index, ref success); - if (!success) - return null; - array.Add(value); - } - } - return array; - } - - static object ParseValue(char[] json, ref int index, ref bool success) - { - switch (LookAhead(json, index)) - { - case TOKEN_STRING: - return ParseString(json, ref index, ref success); - case TOKEN_NUMBER: - return ParseNumber(json, ref index, ref success); - case TOKEN_CURLY_OPEN: - return ParseObject(json, ref index, ref success); - case TOKEN_SQUARED_OPEN: - return ParseArray(json, ref index, ref success); - case TOKEN_TRUE: - NextToken(json, ref index); - return true; - case TOKEN_FALSE: - NextToken(json, ref index); - return false; - case TOKEN_NULL: - NextToken(json, ref index); - return null; - case TOKEN_NONE: - break; - } - success = false; - return null; - } - - static string ParseString(char[] json, ref int index, ref bool success) - { - StringBuilder s = new StringBuilder(BUILDER_CAPACITY); - char c; - - EatWhitespace(json, ref index); - - // " - c = json[index++]; - bool complete = false; - while (!complete) - { - if (index == json.Length) - break; - - c = json[index++]; - if (c == '"') - { - complete = true; - break; - } - else if (c == '\\') - { - if (index == json.Length) - break; - c = json[index++]; - if (c == '"') - s.Append('"'); - else if (c == '\\') - s.Append('\\'); - else if (c == '/') - s.Append('/'); - else if (c == 'b') - s.Append('\b'); - else if (c == 'f') - s.Append('\f'); - else if (c == 'n') - s.Append('\n'); - else if (c == 'r') - s.Append('\r'); - else if (c == 't') - s.Append('\t'); - else if (c == 'u') - { - int remainingLength = json.Length - index; - if (remainingLength >= 4) - { - // parse the 32 bit hex into an integer codepoint - uint codePoint; - if (!(success = UInt32.TryParse(new string(json, index, 4), NumberStyles.HexNumber, CultureInfo.InvariantCulture, out codePoint))) - return ""; - - // convert the integer codepoint to a unicode char and add to string - if (0xD800 <= codePoint && codePoint <= 0xDBFF) // if high surrogate - { - index += 4; // skip 4 chars - remainingLength = json.Length - index; - if (remainingLength >= 6) - { - uint lowCodePoint; - if (new string(json, index, 2) == "\\u" && UInt32.TryParse(new string(json, index + 2, 4), NumberStyles.HexNumber, CultureInfo.InvariantCulture, out lowCodePoint)) - { - if (0xDC00 <= lowCodePoint && lowCodePoint <= 0xDFFF) // if low surrogate - { - s.Append((char)codePoint); - s.Append((char)lowCodePoint); - index += 6; // skip 6 chars - continue; - } - } - } - success = false; // invalid surrogate pair - return ""; - } - s.Append(ConvertFromUtf32((int)codePoint)); - // skip 4 chars - index += 4; - } - else - break; - } - } - else - s.Append(c); - } - if (!complete) - { - success = false; - return null; - } - return s.ToString(); - } - - private static string ConvertFromUtf32(int utf32) - { - // http://www.java2s.com/Open-Source/CSharp/2.6.4-mono-.net-core/System/System/Char.cs.htm - if (utf32 < 0 || utf32 > 0x10FFFF) - throw new ArgumentOutOfRangeException("utf32", "The argument must be from 0 to 0x10FFFF."); - if (0xD800 <= utf32 && utf32 <= 0xDFFF) - throw new ArgumentOutOfRangeException("utf32", "The argument must not be in surrogate pair range."); - if (utf32 < 0x10000) - return new string((char)utf32, 1); - utf32 -= 0x10000; - return new string(new char[] { (char)((utf32 >> 10) + 0xD800), (char)(utf32 % 0x0400 + 0xDC00) }); - } - - static object ParseNumber(char[] json, ref int index, ref bool success) - { - EatWhitespace(json, ref index); - int lastIndex = GetLastIndexOfNumber(json, index); - int charLength = (lastIndex - index) + 1; - object returnNumber; - string str = new string(json, index, charLength); - if (str.IndexOf(".", StringComparison.OrdinalIgnoreCase) != -1 || str.IndexOf("e", StringComparison.OrdinalIgnoreCase) != -1) - { - double number; - success = double.TryParse(new string(json, index, charLength), NumberStyles.Any, CultureInfo.InvariantCulture, out number); - returnNumber = number; - } - else - { - long number; - success = long.TryParse(new string(json, index, charLength), NumberStyles.Any, CultureInfo.InvariantCulture, out number); - returnNumber = number; - } - index = lastIndex + 1; - return returnNumber; - } - - static int GetLastIndexOfNumber(char[] json, int index) - { - int lastIndex; - for (lastIndex = index; lastIndex < json.Length; lastIndex++) - if ("0123456789+-.eE".IndexOf(json[lastIndex]) == -1) break; - return lastIndex - 1; - } - - static void EatWhitespace(char[] json, ref int index) - { - for (; index < json.Length; index++) - if (" \t\n\r\b\f".IndexOf(json[index]) == -1) break; - } - - static int LookAhead(char[] json, int index) - { - int saveIndex = index; - return NextToken(json, ref saveIndex); - } - - [SuppressMessage("Microsoft.Maintainability", "CA1502:AvoidExcessiveComplexity")] - static int NextToken(char[] json, ref int index) - { - EatWhitespace(json, ref index); - if (index == json.Length) - return TOKEN_NONE; - char c = json[index]; - index++; - switch (c) - { - case '{': - return TOKEN_CURLY_OPEN; - case '}': - return TOKEN_CURLY_CLOSE; - case '[': - return TOKEN_SQUARED_OPEN; - case ']': - return TOKEN_SQUARED_CLOSE; - case ',': - return TOKEN_COMMA; - case '"': - return TOKEN_STRING; - case '0': - case '1': - case '2': - case '3': - case '4': - case '5': - case '6': - case '7': - case '8': - case '9': - case '-': - return TOKEN_NUMBER; - case ':': - return TOKEN_COLON; - } - index--; - int remainingLength = json.Length - index; - // false - if (remainingLength >= 5) - { - if (json[index] == 'f' && json[index + 1] == 'a' && json[index + 2] == 'l' && json[index + 3] == 's' && json[index + 4] == 'e') - { - index += 5; - return TOKEN_FALSE; - } - } - // true - if (remainingLength >= 4) - { - if (json[index] == 't' && json[index + 1] == 'r' && json[index + 2] == 'u' && json[index + 3] == 'e') - { - index += 4; - return TOKEN_TRUE; - } - } - // null - if (remainingLength >= 4) - { - if (json[index] == 'n' && json[index + 1] == 'u' && json[index + 2] == 'l' && json[index + 3] == 'l') - { - index += 4; - return TOKEN_NULL; - } - } - return TOKEN_NONE; - } - - static bool SerializeValue(IJsonSerializerStrategy jsonSerializerStrategy, object value, StringBuilder builder) - { - bool success = true; - string stringValue = value as string; - if (stringValue != null) - success = SerializeString(stringValue, builder); - else - { - IDictionary dict = value as IDictionary; - if (dict != null) - { - success = SerializeObject(jsonSerializerStrategy, dict.Keys, dict.Values, builder); - } - else - { - IDictionary stringDictionary = value as IDictionary; - if (stringDictionary != null) - { - success = SerializeObject(jsonSerializerStrategy, stringDictionary.Keys, stringDictionary.Values, builder); - } - else - { - IEnumerable enumerableValue = value as IEnumerable; - if (enumerableValue != null) - success = SerializeArray(jsonSerializerStrategy, enumerableValue, builder); - else if (IsNumeric(value)) - success = SerializeNumber(value, builder); - else if (value is bool) - builder.Append((bool)value ? "true" : "false"); - else if (value == null) - builder.Append("null"); - else - { - object serializedObject; - success = jsonSerializerStrategy.TrySerializeNonPrimitiveObject(value, out serializedObject); - if (success) - SerializeValue(jsonSerializerStrategy, serializedObject, builder); - } - } - } - } - return success; - } - - static bool SerializeObject(IJsonSerializerStrategy jsonSerializerStrategy, IEnumerable keys, IEnumerable values, StringBuilder builder) - { - builder.Append("{"); - IEnumerator ke = keys.GetEnumerator(); - IEnumerator ve = values.GetEnumerator(); - bool first = true; - while (ke.MoveNext() && ve.MoveNext()) - { - object key = ke.Current; - object value = ve.Current; - if (!first) - builder.Append(","); - string stringKey = key as string; - if (stringKey != null) - SerializeString(stringKey, builder); - else - if (!SerializeValue(jsonSerializerStrategy, value, builder)) return false; - builder.Append(":"); - if (!SerializeValue(jsonSerializerStrategy, value, builder)) - return false; - first = false; - } - builder.Append("}"); - return true; - } - - static bool SerializeArray(IJsonSerializerStrategy jsonSerializerStrategy, IEnumerable anArray, StringBuilder builder) - { - builder.Append("["); - bool first = true; - foreach (object value in anArray) - { - if (!first) - builder.Append(","); - if (!SerializeValue(jsonSerializerStrategy, value, builder)) - return false; - first = false; - } - builder.Append("]"); - return true; - } - - static bool SerializeString(string aString, StringBuilder builder) - { - // Happy path if there's nothing to be escaped. IndexOfAny is highly optimized (and unmanaged) - if (aString.IndexOfAny(EscapeCharacters) == -1) - { - builder.Append('"'); - builder.Append(aString); - builder.Append('"'); - - return true; - } - - builder.Append('"'); - int safeCharacterCount = 0; - char[] charArray = aString.ToCharArray(); - - for (int i = 0; i < charArray.Length; i++) - { - char c = charArray[i]; - - // Non ascii characters are fine, buffer them up and send them to the builder - // in larger chunks if possible. The escape table is a 1:1 translation table - // with \0 [default(char)] denoting a safe character. - if (c >= EscapeTable.Length || EscapeTable[c] == default(char)) - { - safeCharacterCount++; - } - else - { - if (safeCharacterCount > 0) - { - builder.Append(charArray, i - safeCharacterCount, safeCharacterCount); - safeCharacterCount = 0; - } - - builder.Append('\\'); - builder.Append(EscapeTable[c]); - } - } - - if (safeCharacterCount > 0) - { - builder.Append(charArray, charArray.Length - safeCharacterCount, safeCharacterCount); - } - - builder.Append('"'); - return true; - } - - static bool SerializeNumber(object number, StringBuilder builder) - { - if (number is long) - builder.Append(((long)number).ToString(CultureInfo.InvariantCulture)); - else if (number is ulong) - builder.Append(((ulong)number).ToString(CultureInfo.InvariantCulture)); - else if (number is int) - builder.Append(((int)number).ToString(CultureInfo.InvariantCulture)); - else if (number is uint) - builder.Append(((uint)number).ToString(CultureInfo.InvariantCulture)); - else if (number is decimal) - builder.Append(((decimal)number).ToString(CultureInfo.InvariantCulture)); - else if (number is float) - builder.Append(((float)number).ToString(CultureInfo.InvariantCulture)); - else - builder.Append(Convert.ToDouble(number, CultureInfo.InvariantCulture).ToString("r", CultureInfo.InvariantCulture)); - return true; - } - - /// - /// Determines if a given object is numeric in any way - /// (can be integer, double, null, etc). - /// - static bool IsNumeric(object value) - { - if (value is sbyte) return true; - if (value is byte) return true; - if (value is short) return true; - if (value is ushort) return true; - if (value is int) return true; - if (value is uint) return true; - if (value is long) return true; - if (value is ulong) return true; - if (value is float) return true; - if (value is double) return true; - if (value is decimal) return true; - return false; - } - - private static IJsonSerializerStrategy _currentJsonSerializerStrategy; - public static IJsonSerializerStrategy CurrentJsonSerializerStrategy - { - get - { - return _currentJsonSerializerStrategy ?? - (_currentJsonSerializerStrategy = -#if SIMPLE_JSON_DATACONTRACT - DataContractJsonSerializerStrategy -#else - PocoJsonSerializerStrategy -#endif -); - } - set - { - _currentJsonSerializerStrategy = value; - } - } - - private static PocoJsonSerializerStrategy _pocoJsonSerializerStrategy; - [EditorBrowsable(EditorBrowsableState.Advanced)] - public static PocoJsonSerializerStrategy PocoJsonSerializerStrategy - { - get - { - return _pocoJsonSerializerStrategy ?? (_pocoJsonSerializerStrategy = new PocoJsonSerializerStrategy()); - } - } - -#if SIMPLE_JSON_DATACONTRACT - - private static DataContractJsonSerializerStrategy _dataContractJsonSerializerStrategy; - [System.ComponentModel.EditorBrowsable(EditorBrowsableState.Advanced)] - public static DataContractJsonSerializerStrategy DataContractJsonSerializerStrategy - { - get - { - return _dataContractJsonSerializerStrategy ?? (_dataContractJsonSerializerStrategy = new DataContractJsonSerializerStrategy()); - } - } - -#endif - } - - [GeneratedCode("simple-json", "1.0.0")] -#if SIMPLE_JSON_INTERNAL - internal -#else - public -#endif - interface IJsonSerializerStrategy - { - [SuppressMessage("Microsoft.Design", "CA1007:UseGenericsWhereAppropriate", Justification = "Need to support .NET 2")] - bool TrySerializeNonPrimitiveObject(object input, out object output); - object DeserializeObject(object value, Type type); - } - - [GeneratedCode("simple-json", "1.0.0")] -#if SIMPLE_JSON_INTERNAL - internal -#else - public -#endif - class PocoJsonSerializerStrategy : IJsonSerializerStrategy - { - internal IDictionary ConstructorCache; - internal IDictionary> GetCache; - internal IDictionary>> SetCache; - - internal static readonly Type[] EmptyTypes = new Type[0]; - internal static readonly Type[] ArrayConstructorParameterTypes = new Type[] { typeof(int) }; - - private static readonly string[] Iso8601Format = new string[] - { - @"yyyy-MM-dd\THH:mm:ss.FFFFFFF\Z", - @"yyyy-MM-dd\THH:mm:ss\Z", - @"yyyy-MM-dd\THH:mm:ssK" - }; - - public PocoJsonSerializerStrategy() - { - ConstructorCache = new ReflectionUtils.ThreadSafeDictionary(ContructorDelegateFactory); - GetCache = new ReflectionUtils.ThreadSafeDictionary>(GetterValueFactory); - SetCache = new ReflectionUtils.ThreadSafeDictionary>>(SetterValueFactory); - } - - protected virtual string MapClrMemberNameToJsonFieldName(string clrPropertyName) - { - return clrPropertyName; - } - - internal virtual ReflectionUtils.ConstructorDelegate ContructorDelegateFactory(Type key) - { - return ReflectionUtils.GetContructor(key, key.IsArray ? ArrayConstructorParameterTypes : EmptyTypes); - } - - internal virtual IDictionary GetterValueFactory(Type type) - { - IDictionary result = new Dictionary(); - foreach (PropertyInfo propertyInfo in ReflectionUtils.GetProperties(type)) - { - if (propertyInfo.CanRead) - { - MethodInfo getMethod = ReflectionUtils.GetGetterMethodInfo(propertyInfo); - if (getMethod.IsStatic || !getMethod.IsPublic) - continue; - result[MapClrMemberNameToJsonFieldName(propertyInfo.Name)] = ReflectionUtils.GetGetMethod(propertyInfo); - } - } - foreach (FieldInfo fieldInfo in ReflectionUtils.GetFields(type)) - { - if (fieldInfo.IsStatic || !fieldInfo.IsPublic) - continue; - result[MapClrMemberNameToJsonFieldName(fieldInfo.Name)] = ReflectionUtils.GetGetMethod(fieldInfo); - } - return result; - } - - internal virtual IDictionary> SetterValueFactory(Type type) - { - IDictionary> result = new Dictionary>(); - foreach (PropertyInfo propertyInfo in ReflectionUtils.GetProperties(type)) - { - if (propertyInfo.CanWrite) - { - MethodInfo setMethod = ReflectionUtils.GetSetterMethodInfo(propertyInfo); - if (setMethod.IsStatic || !setMethod.IsPublic) - continue; - result[MapClrMemberNameToJsonFieldName(propertyInfo.Name)] = new KeyValuePair(propertyInfo.PropertyType, ReflectionUtils.GetSetMethod(propertyInfo)); - } - } - foreach (FieldInfo fieldInfo in ReflectionUtils.GetFields(type)) - { - if (fieldInfo.IsInitOnly || fieldInfo.IsStatic || !fieldInfo.IsPublic) - continue; - result[MapClrMemberNameToJsonFieldName(fieldInfo.Name)] = new KeyValuePair(fieldInfo.FieldType, ReflectionUtils.GetSetMethod(fieldInfo)); - } - return result; - } - - public virtual bool TrySerializeNonPrimitiveObject(object input, out object output) - { - return TrySerializeKnownTypes(input, out output) || TrySerializeUnknownTypes(input, out output); - } - - [SuppressMessage("Microsoft.Maintainability", "CA1502:AvoidExcessiveComplexity")] - public virtual object DeserializeObject(object value, Type type) - { - if (type == null) throw new ArgumentNullException("type"); - string str = value as string; - - if (type == typeof(Guid) && string.IsNullOrEmpty(str)) - return default(Guid); - - if (value == null) - return null; - - object obj = null; - - if (str != null) - { - if (str.Length != 0) // We know it can't be null now. - { - if (type == typeof(DateTime) || (ReflectionUtils.IsNullableType(type) && Nullable.GetUnderlyingType(type) == typeof(DateTime))) - return DateTime.ParseExact(str, Iso8601Format, CultureInfo.InvariantCulture, DateTimeStyles.AssumeUniversal | DateTimeStyles.AdjustToUniversal); - if (type == typeof(DateTimeOffset) || (ReflectionUtils.IsNullableType(type) && Nullable.GetUnderlyingType(type) == typeof(DateTimeOffset))) - return DateTimeOffset.ParseExact(str, Iso8601Format, CultureInfo.InvariantCulture, DateTimeStyles.AssumeUniversal | DateTimeStyles.AdjustToUniversal); - if (type == typeof(Guid) || (ReflectionUtils.IsNullableType(type) && Nullable.GetUnderlyingType(type) == typeof(Guid))) - return new Guid(str); - if (type == typeof(Uri)) - { - bool isValid = Uri.IsWellFormedUriString(str, UriKind.RelativeOrAbsolute); - - Uri result; - if (isValid && Uri.TryCreate(str, UriKind.RelativeOrAbsolute, out result)) - return result; - - return null; - } - - if (type == typeof(string)) - return str; - - return Convert.ChangeType(str, type, CultureInfo.InvariantCulture); - } - else - { - if (type == typeof(Guid)) - obj = default(Guid); - else if (ReflectionUtils.IsNullableType(type) && Nullable.GetUnderlyingType(type) == typeof(Guid)) - obj = null; - else - obj = str; - } - // Empty string case - if (!ReflectionUtils.IsNullableType(type) && Nullable.GetUnderlyingType(type) == typeof(Guid)) - return str; - } - else if (value is bool) - return value; - - bool valueIsLong = value is long; - bool valueIsDouble = value is double; - if ((valueIsLong && type == typeof(long)) || (valueIsDouble && type == typeof(double))) - return value; - if ((valueIsDouble && type != typeof(double)) || (valueIsLong && type != typeof(long))) - { - 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) - ? Convert.ChangeType(value, type, CultureInfo.InvariantCulture) - : value; - } - else - { - IDictionary objects = value as IDictionary; - if (objects != null) - { - IDictionary jsonObject = objects; - - if (ReflectionUtils.IsTypeDictionary(type)) - { - // if dictionary then - Type[] types = ReflectionUtils.GetGenericTypeArguments(type); - Type keyType = types[0]; - Type valueType = types[1]; - - Type genericType = typeof(Dictionary<,>).MakeGenericType(keyType, valueType); - - IDictionary dict = (IDictionary)ConstructorCache[genericType](); - - foreach (KeyValuePair kvp in jsonObject) - dict.Add(kvp.Key, DeserializeObject(kvp.Value, valueType)); - - obj = dict; - } - else - { - if (type == typeof(object)) - obj = value; - else - { - obj = ConstructorCache[type](); - foreach (KeyValuePair> setter in SetCache[type]) - { - object jsonValue; - if (jsonObject.TryGetValue(setter.Key, out jsonValue)) - { - jsonValue = DeserializeObject(jsonValue, setter.Value.Key); - setter.Value.Value(obj, jsonValue); - } - } - } - } - } - else - { - IList valueAsList = value as IList; - if (valueAsList != null) - { - IList jsonObject = valueAsList; - IList list = null; - - if (type.IsArray) - { - list = (IList)ConstructorCache[type](jsonObject.Count); - int i = 0; - foreach (object o in jsonObject) - list[i++] = DeserializeObject(o, type.GetElementType()); - } - else if (ReflectionUtils.IsTypeGenericeCollectionInterface(type) || ReflectionUtils.IsAssignableFrom(typeof(IList), type)) - { - Type innerType = ReflectionUtils.GetGenericListElementType(type); - list = (IList)(ConstructorCache[type] ?? ConstructorCache[typeof(List<>).MakeGenericType(innerType)])(jsonObject.Count); - foreach (object o in jsonObject) - list.Add(DeserializeObject(o, innerType)); - } - obj = list; - } - } - return obj; - } - if (ReflectionUtils.IsNullableType(type)) - return ReflectionUtils.ToNullableType(obj, type); - return obj; - } - - protected virtual object SerializeEnum(Enum p) - { - return Convert.ToDouble(p, CultureInfo.InvariantCulture); - } - - [SuppressMessage("Microsoft.Design", "CA1007:UseGenericsWhereAppropriate", Justification = "Need to support .NET 2")] - protected virtual bool TrySerializeKnownTypes(object input, out object output) - { - bool returnValue = true; - if (input is DateTime) - output = ((DateTime)input).ToUniversalTime().ToString(Iso8601Format[0], CultureInfo.InvariantCulture); - else if (input is DateTimeOffset) - output = ((DateTimeOffset)input).ToUniversalTime().ToString(Iso8601Format[0], CultureInfo.InvariantCulture); - else if (input is Guid) - output = ((Guid)input).ToString("D"); - else if (input is Uri) - output = input.ToString(); - else - { - Enum inputEnum = input as Enum; - if (inputEnum != null) - output = SerializeEnum(inputEnum); - else - { - returnValue = false; - output = null; - } - } - return returnValue; - } - [SuppressMessage("Microsoft.Design", "CA1007:UseGenericsWhereAppropriate", Justification = "Need to support .NET 2")] - protected virtual bool TrySerializeUnknownTypes(object input, out object output) - { - if (input == null) throw new ArgumentNullException("input"); - output = null; - Type type = input.GetType(); - if (type.FullName == null) - return false; - IDictionary obj = new JsonObject(); - IDictionary getters = GetCache[type]; - foreach (KeyValuePair getter in getters) - { - if (getter.Value != null) - obj.Add(MapClrMemberNameToJsonFieldName(getter.Key), getter.Value(input)); - } - output = obj; - return true; - } - } - -#if SIMPLE_JSON_DATACONTRACT - [GeneratedCode("simple-json", "1.0.0")] -#if SIMPLE_JSON_INTERNAL - internal -#else - public -#endif - class DataContractJsonSerializerStrategy : PocoJsonSerializerStrategy - { - public DataContractJsonSerializerStrategy() - { - GetCache = new ReflectionUtils.ThreadSafeDictionary>(GetterValueFactory); - SetCache = new ReflectionUtils.ThreadSafeDictionary>>(SetterValueFactory); - } - - internal override IDictionary GetterValueFactory(Type type) - { - bool hasDataContract = ReflectionUtils.GetAttribute(type, typeof(DataContractAttribute)) != null; - if (!hasDataContract) - return base.GetterValueFactory(type); - string jsonKey; - IDictionary result = new Dictionary(); - foreach (PropertyInfo propertyInfo in ReflectionUtils.GetProperties(type)) - { - if (propertyInfo.CanRead) - { - MethodInfo getMethod = ReflectionUtils.GetGetterMethodInfo(propertyInfo); - if (!getMethod.IsStatic && CanAdd(propertyInfo, out jsonKey)) - result[jsonKey] = ReflectionUtils.GetGetMethod(propertyInfo); - } - } - foreach (FieldInfo fieldInfo in ReflectionUtils.GetFields(type)) - { - if (!fieldInfo.IsStatic && CanAdd(fieldInfo, out jsonKey)) - result[jsonKey] = ReflectionUtils.GetGetMethod(fieldInfo); - } - return result; - } - - internal override IDictionary> SetterValueFactory(Type type) - { - bool hasDataContract = ReflectionUtils.GetAttribute(type, typeof(DataContractAttribute)) != null; - if (!hasDataContract) - return base.SetterValueFactory(type); - string jsonKey; - IDictionary> result = new Dictionary>(); - foreach (PropertyInfo propertyInfo in ReflectionUtils.GetProperties(type)) - { - if (propertyInfo.CanWrite) - { - MethodInfo setMethod = ReflectionUtils.GetSetterMethodInfo(propertyInfo); - if (!setMethod.IsStatic && CanAdd(propertyInfo, out jsonKey)) - result[jsonKey] = new KeyValuePair(propertyInfo.PropertyType, ReflectionUtils.GetSetMethod(propertyInfo)); - } - } - foreach (FieldInfo fieldInfo in ReflectionUtils.GetFields(type)) - { - if (!fieldInfo.IsInitOnly && !fieldInfo.IsStatic && CanAdd(fieldInfo, out jsonKey)) - result[jsonKey] = new KeyValuePair(fieldInfo.FieldType, ReflectionUtils.GetSetMethod(fieldInfo)); - } - // todo implement sorting for DATACONTRACT. - return result; - } - - private static bool CanAdd(MemberInfo info, out string jsonKey) - { - jsonKey = null; - if (ReflectionUtils.GetAttribute(info, typeof(IgnoreDataMemberAttribute)) != null) - return false; - DataMemberAttribute dataMemberAttribute = (DataMemberAttribute)ReflectionUtils.GetAttribute(info, typeof(DataMemberAttribute)); - if (dataMemberAttribute == null) - return false; - jsonKey = string.IsNullOrEmpty(dataMemberAttribute.Name) ? info.Name : dataMemberAttribute.Name; - return true; - } - } - -#endif - - namespace Reflection - { - // This class is meant to be copied into other libraries. So we want to exclude it from Code Analysis rules - // that might be in place in the target project. - [GeneratedCode("reflection-utils", "1.0.0")] -#if SIMPLE_JSON_REFLECTION_UTILS_PUBLIC - public -#else - internal -#endif - class ReflectionUtils - { - private static readonly object[] EmptyObjects = new object[] { }; - - public delegate object GetDelegate(object source); - public delegate void SetDelegate(object source, object value); - public delegate object ConstructorDelegate(params object[] args); - - public delegate TValue ThreadSafeDictionaryValueFactory(TKey key); - -#if SIMPLE_JSON_TYPEINFO - public static TypeInfo GetTypeInfo(Type type) - { - return type.GetTypeInfo(); - } -#else - public static Type GetTypeInfo(Type type) - { - return type; - } -#endif - - public static Attribute GetAttribute(MemberInfo info, Type type) - { -#if SIMPLE_JSON_TYPEINFO - if (info == null || type == null || !info.IsDefined(type)) - return null; - return info.GetCustomAttribute(type); -#else - if (info == null || type == null || !Attribute.IsDefined(info, type)) - return null; - return Attribute.GetCustomAttribute(info, type); -#endif - } - - public static Type GetGenericListElementType(Type type) - { - IEnumerable interfaces; -#if SIMPLE_JSON_TYPEINFO - interfaces = type.GetTypeInfo().ImplementedInterfaces; -#else - interfaces = type.GetInterfaces(); -#endif - foreach (Type implementedInterface in interfaces) - { - if (IsTypeGeneric(implementedInterface) && - implementedInterface.GetGenericTypeDefinition() == typeof(IList<>)) - { - return GetGenericTypeArguments(implementedInterface)[0]; - } - } - return GetGenericTypeArguments(type)[0]; - } - - public static Attribute GetAttribute(Type objectType, Type attributeType) - { - -#if SIMPLE_JSON_TYPEINFO - if (objectType == null || attributeType == null || !objectType.GetTypeInfo().IsDefined(attributeType)) - return null; - return objectType.GetTypeInfo().GetCustomAttribute(attributeType); -#else - if (objectType == null || attributeType == null || !Attribute.IsDefined(objectType, attributeType)) - return null; - return Attribute.GetCustomAttribute(objectType, attributeType); -#endif - } - - public static Type[] GetGenericTypeArguments(Type type) - { -#if SIMPLE_JSON_TYPEINFO - return type.GetTypeInfo().GenericTypeArguments; -#else - return type.GetGenericArguments(); -#endif - } - - public static bool IsTypeGeneric(Type type) - { - return GetTypeInfo(type).IsGenericType; - } - - public static bool IsTypeGenericeCollectionInterface(Type type) - { - if (!IsTypeGeneric(type)) - return false; - - Type genericDefinition = type.GetGenericTypeDefinition(); - - return (genericDefinition == typeof(IList<>) - || genericDefinition == typeof(ICollection<>) - || genericDefinition == typeof(IEnumerable<>) -#if SIMPLE_JSON_READONLY_COLLECTIONS - || genericDefinition == typeof(IReadOnlyCollection<>) - || genericDefinition == typeof(IReadOnlyList<>) -#endif - ); - } - - public static bool IsAssignableFrom(Type type1, Type type2) - { - return GetTypeInfo(type1).IsAssignableFrom(GetTypeInfo(type2)); - } - - public static bool IsTypeDictionary(Type type) - { -#if SIMPLE_JSON_TYPEINFO - if (typeof(IDictionary<,>).GetTypeInfo().IsAssignableFrom(type.GetTypeInfo())) - return true; -#else - if (typeof(System.Collections.IDictionary).IsAssignableFrom(type)) - return true; -#endif - if (!GetTypeInfo(type).IsGenericType) - return false; - - Type genericDefinition = type.GetGenericTypeDefinition(); - return genericDefinition == typeof(IDictionary<,>); - } - - public static bool IsNullableType(Type type) - { - return GetTypeInfo(type).IsGenericType && type.GetGenericTypeDefinition() == typeof(Nullable<>); - } - - public static object ToNullableType(object obj, Type nullableType) - { - return obj == null ? null : Convert.ChangeType(obj, Nullable.GetUnderlyingType(nullableType), CultureInfo.InvariantCulture); - } - - public static bool IsValueType(Type type) - { - return GetTypeInfo(type).IsValueType; - } - - public static IEnumerable GetConstructors(Type type) - { -#if SIMPLE_JSON_TYPEINFO - return type.GetTypeInfo().DeclaredConstructors; -#else - return type.GetConstructors(); -#endif - } - - public static ConstructorInfo GetConstructorInfo(Type type, params Type[] argsType) - { - IEnumerable constructorInfos = GetConstructors(type); - int i; - bool matches; - foreach (ConstructorInfo constructorInfo in constructorInfos) - { - ParameterInfo[] parameters = constructorInfo.GetParameters(); - if (argsType.Length != parameters.Length) - continue; - - i = 0; - matches = true; - foreach (ParameterInfo parameterInfo in constructorInfo.GetParameters()) - { - if (parameterInfo.ParameterType != argsType[i]) - { - matches = false; - break; - } - } - - if (matches) - return constructorInfo; - } - - return null; - } - - public static IEnumerable GetProperties(Type type) - { -#if SIMPLE_JSON_TYPEINFO - return type.GetRuntimeProperties(); -#else - return type.GetProperties(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static); -#endif - } - - public static IEnumerable GetFields(Type type) - { -#if SIMPLE_JSON_TYPEINFO - return type.GetRuntimeFields(); -#else - return type.GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static); -#endif - } - - public static MethodInfo GetGetterMethodInfo(PropertyInfo propertyInfo) - { -#if SIMPLE_JSON_TYPEINFO - return propertyInfo.GetMethod; -#else - return propertyInfo.GetGetMethod(true); -#endif - } - - public static MethodInfo GetSetterMethodInfo(PropertyInfo propertyInfo) - { -#if SIMPLE_JSON_TYPEINFO - return propertyInfo.SetMethod; -#else - return propertyInfo.GetSetMethod(true); -#endif - } - - public static ConstructorDelegate GetContructor(ConstructorInfo constructorInfo) - { -#if SIMPLE_JSON_NO_LINQ_EXPRESSION - return GetConstructorByReflection(constructorInfo); -#else - return GetConstructorByExpression(constructorInfo); -#endif - } - - public static ConstructorDelegate GetContructor(Type type, params Type[] argsType) - { -#if SIMPLE_JSON_NO_LINQ_EXPRESSION - return GetConstructorByReflection(type, argsType); -#else - return GetConstructorByExpression(type, argsType); -#endif - } - - public static ConstructorDelegate GetConstructorByReflection(ConstructorInfo constructorInfo) - { - return delegate (object[] args) { return constructorInfo.Invoke(args); }; - } - - public static ConstructorDelegate GetConstructorByReflection(Type type, params Type[] argsType) - { - ConstructorInfo constructorInfo = GetConstructorInfo(type, argsType); - return constructorInfo == null ? null : GetConstructorByReflection(constructorInfo); - } - -#if !SIMPLE_JSON_NO_LINQ_EXPRESSION - - public static ConstructorDelegate GetConstructorByExpression(ConstructorInfo constructorInfo) - { - ParameterInfo[] paramsInfo = constructorInfo.GetParameters(); - ParameterExpression param = Expression.Parameter(typeof(object[]), "args"); - Expression[] argsExp = new Expression[paramsInfo.Length]; - for (int i = 0; i < paramsInfo.Length; i++) - { - Expression index = Expression.Constant(i); - Type paramType = paramsInfo[i].ParameterType; - Expression paramAccessorExp = Expression.ArrayIndex(param, index); - Expression paramCastExp = Expression.Convert(paramAccessorExp, paramType); - argsExp[i] = paramCastExp; - } - NewExpression newExp = Expression.New(constructorInfo, argsExp); - Expression> lambda = Expression.Lambda>(newExp, param); - Func compiledLambda = lambda.Compile(); - return delegate (object[] args) { return compiledLambda(args); }; - } - - public static ConstructorDelegate GetConstructorByExpression(Type type, params Type[] argsType) - { - ConstructorInfo constructorInfo = GetConstructorInfo(type, argsType); - return constructorInfo == null ? null : GetConstructorByExpression(constructorInfo); - } - -#endif - - public static GetDelegate GetGetMethod(PropertyInfo propertyInfo) - { -#if SIMPLE_JSON_NO_LINQ_EXPRESSION - return GetGetMethodByReflection(propertyInfo); -#else - return GetGetMethodByExpression(propertyInfo); -#endif - } - - public static GetDelegate GetGetMethod(FieldInfo fieldInfo) - { -#if SIMPLE_JSON_NO_LINQ_EXPRESSION - return GetGetMethodByReflection(fieldInfo); -#else - return GetGetMethodByExpression(fieldInfo); -#endif - } - - public static GetDelegate GetGetMethodByReflection(PropertyInfo propertyInfo) - { - MethodInfo methodInfo = GetGetterMethodInfo(propertyInfo); - return delegate (object source) { return methodInfo.Invoke(source, EmptyObjects); }; - } - - public static GetDelegate GetGetMethodByReflection(FieldInfo fieldInfo) - { - return delegate (object source) { return fieldInfo.GetValue(source); }; - } - -#if !SIMPLE_JSON_NO_LINQ_EXPRESSION - - public static GetDelegate GetGetMethodByExpression(PropertyInfo propertyInfo) - { - MethodInfo getMethodInfo = GetGetterMethodInfo(propertyInfo); - ParameterExpression instance = Expression.Parameter(typeof(object), "instance"); - UnaryExpression instanceCast = (!IsValueType(propertyInfo.DeclaringType)) ? Expression.TypeAs(instance, propertyInfo.DeclaringType) : Expression.Convert(instance, propertyInfo.DeclaringType); - Func compiled = Expression.Lambda>(Expression.TypeAs(Expression.Call(instanceCast, getMethodInfo), typeof(object)), instance).Compile(); - return delegate (object source) { return compiled(source); }; - } - - public static GetDelegate GetGetMethodByExpression(FieldInfo fieldInfo) - { - ParameterExpression instance = Expression.Parameter(typeof(object), "instance"); - MemberExpression member = Expression.Field(Expression.Convert(instance, fieldInfo.DeclaringType), fieldInfo); - GetDelegate compiled = Expression.Lambda(Expression.Convert(member, typeof(object)), instance).Compile(); - return delegate (object source) { return compiled(source); }; - } - -#endif - - public static SetDelegate GetSetMethod(PropertyInfo propertyInfo) - { -#if SIMPLE_JSON_NO_LINQ_EXPRESSION - return GetSetMethodByReflection(propertyInfo); -#else - return GetSetMethodByExpression(propertyInfo); -#endif - } - - public static SetDelegate GetSetMethod(FieldInfo fieldInfo) - { -#if SIMPLE_JSON_NO_LINQ_EXPRESSION - return GetSetMethodByReflection(fieldInfo); -#else - return GetSetMethodByExpression(fieldInfo); -#endif - } - - public static SetDelegate GetSetMethodByReflection(PropertyInfo propertyInfo) - { - MethodInfo methodInfo = GetSetterMethodInfo(propertyInfo); - return delegate (object source, object value) { methodInfo.Invoke(source, new object[] { value }); }; - } - - public static SetDelegate GetSetMethodByReflection(FieldInfo fieldInfo) - { - return delegate (object source, object value) { fieldInfo.SetValue(source, value); }; - } - -#if !SIMPLE_JSON_NO_LINQ_EXPRESSION - - public static SetDelegate GetSetMethodByExpression(PropertyInfo propertyInfo) - { - MethodInfo setMethodInfo = GetSetterMethodInfo(propertyInfo); - ParameterExpression instance = Expression.Parameter(typeof(object), "instance"); - ParameterExpression value = Expression.Parameter(typeof(object), "value"); - UnaryExpression instanceCast = (!IsValueType(propertyInfo.DeclaringType)) ? Expression.TypeAs(instance, propertyInfo.DeclaringType) : Expression.Convert(instance, propertyInfo.DeclaringType); - UnaryExpression valueCast = (!IsValueType(propertyInfo.PropertyType)) ? Expression.TypeAs(value, propertyInfo.PropertyType) : Expression.Convert(value, propertyInfo.PropertyType); - Action compiled = Expression.Lambda>(Expression.Call(instanceCast, setMethodInfo, valueCast), new ParameterExpression[] { instance, value }).Compile(); - return delegate (object source, object val) { compiled(source, val); }; - } - - public static SetDelegate GetSetMethodByExpression(FieldInfo fieldInfo) - { - ParameterExpression instance = Expression.Parameter(typeof(object), "instance"); - ParameterExpression value = Expression.Parameter(typeof(object), "value"); - Action compiled = Expression.Lambda>( - Assign(Expression.Field(Expression.Convert(instance, fieldInfo.DeclaringType), fieldInfo), Expression.Convert(value, fieldInfo.FieldType)), instance, value).Compile(); - return delegate (object source, object val) { compiled(source, val); }; - } - - public static BinaryExpression Assign(Expression left, Expression right) - { -#if SIMPLE_JSON_TYPEINFO - return Expression.Assign(left, right); -#else - MethodInfo assign = typeof(Assigner<>).MakeGenericType(left.Type).GetMethod("Assign"); - BinaryExpression assignExpr = Expression.Add(left, right, assign); - return assignExpr; -#endif - } - - private static class Assigner - { - public static T Assign(ref T left, T right) - { - return (left = right); - } - } - -#endif - - public sealed class ThreadSafeDictionary : IDictionary - { - private readonly object _lock = new object(); - private readonly ThreadSafeDictionaryValueFactory _valueFactory; - private Dictionary _dictionary; - - public ThreadSafeDictionary(ThreadSafeDictionaryValueFactory valueFactory) - { - _valueFactory = valueFactory; - } - - private TValue Get(TKey key) - { - if (_dictionary == null) - return AddValue(key); - TValue value; - if (!_dictionary.TryGetValue(key, out value)) - return AddValue(key); - return value; - } - - private TValue AddValue(TKey key) - { - TValue value = _valueFactory(key); - lock (_lock) - { - if (_dictionary == null) - { - _dictionary = new Dictionary(); - _dictionary[key] = value; - } - else - { - TValue val; - if (_dictionary.TryGetValue(key, out val)) - return val; - Dictionary dict = new Dictionary(_dictionary); - dict[key] = value; - _dictionary = dict; - } - } - return value; - } - - public void Add(TKey key, TValue value) - { - throw new NotImplementedException(); - } - - public bool ContainsKey(TKey key) - { - return _dictionary.ContainsKey(key); - } - - public ICollection Keys - { - get { return _dictionary.Keys; } - } - - public bool Remove(TKey key) - { - throw new NotImplementedException(); - } - - public bool TryGetValue(TKey key, out TValue value) - { - value = this[key]; - return true; - } - - public ICollection Values - { - get { return _dictionary.Values; } - } - - public TValue this[TKey key] - { - get { return Get(key); } - set { throw new NotImplementedException(); } - } - - public void Add(KeyValuePair item) - { - throw new NotImplementedException(); - } - - public void Clear() - { - throw new NotImplementedException(); - } - - public bool Contains(KeyValuePair item) - { - throw new NotImplementedException(); - } - - public void CopyTo(KeyValuePair[] array, int arrayIndex) - { - throw new NotImplementedException(); - } - - public int Count - { - get { return _dictionary.Count; } - } - - public bool IsReadOnly - { - get { throw new NotImplementedException(); } - } - - public bool Remove(KeyValuePair item) - { - throw new NotImplementedException(); - } - - public IEnumerator> GetEnumerator() - { - return _dictionary.GetEnumerator(); - } - - System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() - { - return _dictionary.GetEnumerator(); - } - } - - } - } -} -// ReSharper restore LoopCanBeConvertedToQuery -// ReSharper restore RedundantExplicitArrayCreation -// ReSharper restore SuggestUseVarKeywordEvident \ No newline at end of file diff --git a/src/TablesAndTuples/ColumnDefinitionEnums.cs b/src/TablesAndTuples/ColumnDefinitionEnums.cs deleted file mode 100644 index 1499500e..00000000 --- a/src/TablesAndTuples/ColumnDefinitionEnums.cs +++ /dev/null @@ -1,56 +0,0 @@ -namespace TablesAndSymbols -{ - public enum ColumnCategory - { - Unknown, - Text, - UpperCase, - LowerCase, - Integer, - DoubleInteger, - TimeDate, - Identifier, - Property, - Filename, - WildCardFilename, - Path, - Paths, - AnyPath, - DefaultDir, - RegPath, - Formatted, - Template, - Condition, - Guid, - Version, - Language, - Binary, - CustomSource, - Cabinet, - Shortcut, - FormattedSDDLText, - } - - public enum ColumnModularizeType - { - None, - Column, - Icon, - CompanionFile, - Condition, - ControlEventArgument, - ControlText, - Property, - SemicolonDelimited, - } - - public enum ColumnType - { - Unknown, - String, - Localized, - Number, - Object, - Preserved, - } -} diff --git a/src/TablesAndTuples/Program.cs b/src/TablesAndTuples/Program.cs deleted file mode 100644 index 634acf9d..00000000 --- a/src/TablesAndTuples/Program.cs +++ /dev/null @@ -1,528 +0,0 @@ -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Text; -using System.Text.RegularExpressions; -using System.Xml.Linq; -using SimpleJson; - -namespace TablesAndSymbols -{ - class Program - { - static void Main(string[] args) - { - if (args.Length == 0) - { - return; - } - else if (Path.GetExtension(args[0]) == ".xml") - { - if (args.Length < 2) - { - Console.WriteLine("Need to specify output json file as well."); - return; - } - if (Path.GetExtension(args[1]) != ".json") - { - Console.WriteLine("Output needs to be .json"); - return; - } - - string prefix = null; - if (args.Length > 2) - { - prefix = args[2]; - } - - var csFile = Path.Combine(Path.GetDirectoryName(args[1]), String.Concat(prefix ?? "WindowsInstaller", "TableDefinitions.cs")); - - ReadXmlWriteJson(Path.GetFullPath(args[0]), Path.GetFullPath(args[1]), Path.GetFullPath(csFile), prefix); - } - else if (Path.GetExtension(args[0]) == ".json") - { - string prefix = null; - if (args.Length < 2) - { - Console.WriteLine("Need to specify output folder."); - return; - } - else if (args.Length > 2) - { - prefix = args[2]; - } - - ReadJsonWriteCs(Path.GetFullPath(args[0]), Path.GetFullPath(args[1]), prefix); - } - } - - private static void ReadXmlWriteJson(string inputPath, string outputPath, string csOutputPath, string prefix) - { - var tableDefinitions = ReadXmlWriteCs(inputPath, csOutputPath, prefix); - - var array = new JsonArray(); - - foreach (var tableDefinition in tableDefinitions) - { - if (tableDefinition.Symbolless) - { - continue; - } - var symbolType = tableDefinition.SymbolDefinitionName; - - var fields = new JsonArray(); - var firstField = true; - - foreach (var columnDefinition in tableDefinition.Columns) - { - if (firstField) - { - firstField = false; - if (tableDefinition.SymbolIdIsPrimaryKey) - { - continue; - } - } - - var fieldName = columnDefinition.Name; - fieldName = Regex.Replace(fieldName, "^([^_]+)_([^_]*)$", x => - { - return $"{x.Groups[2].Value}{x.Groups[1].Value}Ref"; - }); - var type = columnDefinition.Type.ToString().ToLower(); - - if (type == "localized") - { - type = "string"; - } - else if (type == "object") - { - type = "path"; - } - else if (columnDefinition.Type == ColumnType.Number && columnDefinition.Length == 2 && - columnDefinition.MinValue == 0 && columnDefinition.MaxValue == 1) - { - type = "bool"; - } - - if (columnDefinition.Type == ColumnType.Number && columnDefinition.Nullable) - { - type += "?"; - } - - var field = new JsonObject - { - { fieldName, type } - }; - - fields.Add(field); - } - - var obj = new JsonObject - { - { symbolType, fields } - }; - array.Add(obj); - } - - array.Sort(CompareSymbolDefinitions); - - var strat = new PocoJsonSerializerStrategy(); - var json = SimpleJson.SimpleJson.SerializeObject(array, strat); - - Directory.CreateDirectory(Path.GetDirectoryName(outputPath)); - File.WriteAllText(outputPath, json); - } - - private static List ReadXmlWriteCs(string inputPath, string outputPath, string prefix) - { - var tableDefinitions = WixTableDefinition.LoadCollection(inputPath); - var text = GenerateCsTableDefinitionsFileText(prefix, tableDefinitions); - Console.WriteLine("Writing: {0}", outputPath); - File.WriteAllText(outputPath, text); - return tableDefinitions; - } - - private static void ReadJsonWriteCs(string inputPath, string outputFolder, string prefix) - { - var json = File.ReadAllText(inputPath); - var symbols = SimpleJson.SimpleJson.DeserializeObject(json) as JsonArray; - - var symbolNames = new List(); - - foreach (var symbolDefinition in symbols.Cast()) - { - var symbolName = symbolDefinition.Keys.Single(); - var fields = symbolDefinition.Values.Single() as JsonArray; - - var list = GetFields(fields).ToList(); - - symbolNames.Add(symbolName); - - var text = GenerateSymbolFileText(prefix, symbolName, list); - - var pathSymbol = Path.Combine(outputFolder, symbolName + "Symbol.cs"); - Console.WriteLine("Writing: {0}", pathSymbol); - File.WriteAllText(pathSymbol, text); - } - - var content = SymbolNamesFileContent(prefix, symbolNames); - var pathNames = Path.Combine(outputFolder, String.Concat(prefix, "SymbolDefinitions.cs")); - Console.WriteLine("Writing: {0}", pathNames); - File.WriteAllText(pathNames, content); - } - - private static IEnumerable<(string Name, string Type, string ClrType, string AsFunction)> GetFields(JsonArray fields) - { - foreach (var field in fields.Cast()) - { - var fieldName = field.Keys.Single(); - var fieldType = field.Values.Single() as string; - - var clrType = ConvertToClrType(fieldType); - fieldType = ConvertToFieldType(fieldType); - - var asFunction = $"As{(clrType.Contains("?") ? "Nullable" : "")}{fieldType}()"; - - yield return (Name: fieldName, Type: fieldType, ClrType: clrType, AsFunction: asFunction); - } - } - - private static string GenerateCsTableDefinitionsFileText(string prefix, List tableDefinitions) - { - var ns = prefix ?? "Data"; - - var startClassDef = String.Join(Environment.NewLine, - "// 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.", - "", - "namespace WixToolset.{1}", - "{", - " using WixToolset.Data.WindowsInstaller;", - "", - " public static class {2}TableDefinitions", - " {"); - var startTableDef = String.Join(Environment.NewLine, - " public static readonly TableDefinition {1} = new TableDefinition(", - " \"{2}\",", - " {3},", - " new[]", - " {"); - var columnDef = - " new ColumnDefinition(\"{1}\", ColumnType.{2}, {3}, primaryKey: {4}, nullable: {5}, ColumnCategory.{6}"; - var endColumnsDef = String.Join(Environment.NewLine, - " },"); - var unrealDef = - " unreal: true,"; - var endTableDef = String.Join(Environment.NewLine, - " symbolIdIsPrimaryKey: {1}", - " );", - ""); - var startAllTablesDef = String.Join(Environment.NewLine, - " public static readonly TableDefinition[] All = new[]", - " {"); - var allTableDef = - " {1},"; - var endAllTablesDef = - " };"; - var endClassDef = String.Join(Environment.NewLine, - " }", - "}"); - - var sb = new StringBuilder(); - - sb.AppendLine(startClassDef.Replace("{1}", ns).Replace("{2}", prefix)); - foreach (var tableDefinition in tableDefinitions) - { - var symbolDefinition = tableDefinition.Symbolless ? "null" : $"{prefix}SymbolDefinitions.{tableDefinition.SymbolDefinitionName}"; - sb.AppendLine(startTableDef.Replace("{1}", tableDefinition.VariableName).Replace("{2}", tableDefinition.Name).Replace("{3}", symbolDefinition)); - foreach (var columnDefinition in tableDefinition.Columns) - { - sb.Append(columnDef.Replace("{1}", columnDefinition.Name).Replace("{2}", columnDefinition.Type.ToString()).Replace("{3}", columnDefinition.Length.ToString()) - .Replace("{4}", columnDefinition.PrimaryKey.ToString().ToLower()).Replace("{5}", columnDefinition.Nullable.ToString().ToLower()).Replace("{6}", columnDefinition.Category.ToString())); - if (columnDefinition.MinValue.HasValue) - { - sb.AppendFormat(", minValue: {0}", columnDefinition.MinValue.Value); - } - if (columnDefinition.MaxValue.HasValue) - { - sb.AppendFormat(", maxValue: {0}", columnDefinition.MaxValue.Value); - } - if (!String.IsNullOrEmpty(columnDefinition.KeyTable)) - { - sb.AppendFormat(", keyTable: \"{0}\"", columnDefinition.KeyTable); - } - if (columnDefinition.KeyColumn.HasValue) - { - sb.AppendFormat(", keyColumn: {0}", columnDefinition.KeyColumn.Value); - } - if (!String.IsNullOrEmpty(columnDefinition.Possibilities)) - { - sb.AppendFormat(", possibilities: \"{0}\"", columnDefinition.Possibilities); - } - if (!String.IsNullOrEmpty(columnDefinition.Description)) - { - sb.AppendFormat(", description: \"{0}\"", columnDefinition.Description.Replace("\\", "\\\\").Replace("\"", "\\\"")); - } - if (columnDefinition.ModularizeType.HasValue && columnDefinition.ModularizeType.Value != ColumnModularizeType.None) - { - sb.AppendFormat(", modularizeType: ColumnModularizeType.{0}", columnDefinition.ModularizeType.ToString()); - } - if (columnDefinition.ForceLocalizable) - { - sb.Append(", forceLocalizable: true"); - } - if (columnDefinition.UseCData) - { - sb.Append(", useCData: true"); - } - if (columnDefinition.Unreal) - { - sb.Append(", unreal: true"); - } - sb.AppendLine("),"); - } - sb.AppendLine(endColumnsDef); - if (tableDefinition.Unreal) - { - sb.AppendLine(unrealDef); - } - sb.AppendLine(endTableDef.Replace("{1}", tableDefinition.SymbolIdIsPrimaryKey.ToString().ToLower())); - } - sb.AppendLine(startAllTablesDef); - foreach (var tableDefinition in tableDefinitions) - { - sb.AppendLine(allTableDef.Replace("{1}", tableDefinition.VariableName)); - } - sb.AppendLine(endAllTablesDef); - sb.AppendLine(endClassDef); - - return sb.ToString(); - } - - private static string GenerateSymbolFileText(string prefix, string symbolName, List<(string Name, string Type, string ClrType, string AsFunction)> symbolFields) - { - var ns = prefix ?? "Data"; - var toString = String.IsNullOrEmpty(prefix) ? null : ".ToString()"; - - var startFileDef = String.Join(Environment.NewLine, - "// 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.", - "", - "namespace WixToolset.{2}", - "{"); - var usingDataDef = - " using WixToolset.Data;"; - var startSymbolDef = String.Join(Environment.NewLine, - " using WixToolset.{2}.Symbols;", - "", - " public static partial class {3}SymbolDefinitions", - " {", - " public static readonly IntermediateSymbolDefinition {1} = new IntermediateSymbolDefinition(", - " {3}SymbolDefinitionType.{1}{4},", - " new{5}[]", - " {"); - var fieldDef = - " new IntermediateFieldDefinition(nameof({1}SymbolFields.{2}), IntermediateFieldType.{3}),"; - var endSymbolDef = String.Join(Environment.NewLine, - " },", - " typeof({1}Symbol));", - " }", - "}", - "", - "namespace WixToolset.{2}.Symbols", - "{"); - var startEnumDef = String.Join(Environment.NewLine, - " public enum {1}SymbolFields", - " {"); - var fieldEnum = - " {2},"; - var startSymbol = String.Join(Environment.NewLine, - " }", - "", - " public class {1}Symbol : IntermediateSymbol", - " {", - " public {1}Symbol() : base({3}SymbolDefinitions.{1}, null, null)", - " {", - " }", - "", - " public {1}Symbol(SourceLineNumber sourceLineNumber, Identifier id = null) : base({3}SymbolDefinitions.{1}, sourceLineNumber, id)", - " {", - " }", - "", - " public IntermediateField this[{1}SymbolFields index] => this.Fields[(int)index];"); - var fieldProp = String.Join(Environment.NewLine, - "", - " public {4} {2}", - " {", - " get => {6}this.Fields[(int){1}SymbolFields.{2}]{5};", - " set => this.Set((int){1}SymbolFields.{2}, value);", - " }"); - var endSymbol = String.Join(Environment.NewLine, - " }", - "}"); - - var sb = new StringBuilder(); - - sb.AppendLine(startFileDef.Replace("{2}", ns)); - if (ns != "Data") - { - sb.AppendLine(usingDataDef); - } - sb.AppendLine(startSymbolDef.Replace("{1}", symbolName).Replace("{2}", ns).Replace("{3}", prefix).Replace("{4}", toString).Replace("{5}", symbolFields.Any() ? null : " IntermediateFieldDefinition")); - foreach (var field in symbolFields) - { - sb.AppendLine(fieldDef.Replace("{1}", symbolName).Replace("{2}", field.Name).Replace("{3}", field.Type)); - } - sb.AppendLine(endSymbolDef.Replace("{1}", symbolName).Replace("{2}", ns).Replace("{3}", prefix)); - if (ns != "Data") - { - sb.AppendLine(usingDataDef); - sb.AppendLine(); - } - sb.AppendLine(startEnumDef.Replace("{1}", symbolName)); - foreach (var field in symbolFields) - { - sb.AppendLine(fieldEnum.Replace("{1}", symbolName).Replace("{2}", field.Name)); - } - sb.AppendLine(startSymbol.Replace("{1}", symbolName).Replace("{2}", ns).Replace("{3}", prefix)); - foreach (var field in symbolFields) - { - var useCast = ns == "Data" && field.AsFunction != "AsPath()"; - var cast = useCast ? $"({field.ClrType})" : null; - var asFunction = useCast ? null : $".{field.AsFunction}"; - sb.AppendLine(fieldProp.Replace("{1}", symbolName).Replace("{2}", field.Name).Replace("{3}", field.Type).Replace("{4}", field.ClrType).Replace("{5}", asFunction).Replace("{6}", cast)); - } - sb.Append(endSymbol); - - return sb.ToString(); - } - - private static string SymbolNamesFileContent(string prefix, List symbolNames) - { - var ns = prefix ?? "Data"; - - var header = String.Join(Environment.NewLine, - "// 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.", - "", - "namespace WixToolset.{2}", - "{", - " using System;", - " using WixToolset.Data;", - "", - " public enum {3}SymbolDefinitionType", - " {"); - var namesFormat = - " {1},"; - var midpoint = String.Join(Environment.NewLine, - " }", - "", - " public static partial class {3}SymbolDefinitions", - " {", - " public static readonly Version Version = new Version(\"4.0.0\");", - "", - " public static IntermediateSymbolDefinition ByName(string name)", - " {", - " if (!Enum.TryParse(name, out {3}SymbolDefinitionType type))", - " {", - " return null;", - " }", - "", - " return ByType(type);", - " }", - "", - " public static IntermediateSymbolDefinition ByType({3}SymbolDefinitionType type)", - " {", - " switch (type)", - " {"); - - var caseFormat = String.Join(Environment.NewLine, - " case {3}SymbolDefinitionType.{1}:", - " return {3}SymbolDefinitions.{1};", - ""); - - var footer = String.Join(Environment.NewLine, - " default:", - " throw new ArgumentOutOfRangeException(nameof(type));", - " }", - " }", - " }", - "}"); - - var sb = new StringBuilder(); - - sb.AppendLine(header.Replace("{2}", ns).Replace("{3}", prefix)); - foreach (var symbolName in symbolNames) - { - sb.AppendLine(namesFormat.Replace("{1}", symbolName).Replace("{2}", ns).Replace("{3}", prefix)); - } - sb.AppendLine(midpoint.Replace("{2}", ns).Replace("{3}", prefix)); - foreach (var symbolName in symbolNames) - { - sb.AppendLine(caseFormat.Replace("{1}", symbolName).Replace("{2}", ns).Replace("{3}", prefix)); - } - sb.AppendLine(footer); - - return sb.ToString(); - } - - private static string ConvertToFieldType(string fieldType) - { - switch (fieldType.ToLowerInvariant()) - { - case "bool": - return "Bool"; - case "bool?": - return "Number"; - - case "string": - case "preserved": - return "String"; - - case "number": - case "number?": - return "Number"; - - case "path": - return "Path"; - } - - throw new ArgumentException(fieldType); - } - - private static string ConvertToClrType(string fieldType) - { - switch (fieldType.ToLowerInvariant()) - { - case "bool": - return "bool"; - case "bool?": - return "bool?"; - - case "string": - case "preserved": - return "string"; - - case "number": - return "int"; - case "number?": - return "int?"; - - case "path": - return "IntermediateFieldPathValue"; - } - - throw new ArgumentException(fieldType); - } - - private static int CompareSymbolDefinitions(object x, object y) - { - var first = (JsonObject)x; - var second = (JsonObject)y; - - var firstType = first.Keys.Single(); - var secondType = second.Keys.Single(); - - return firstType.CompareTo(secondType); - } - } -} diff --git a/src/TablesAndTuples/SimpleJson.cs b/src/TablesAndTuples/SimpleJson.cs deleted file mode 100644 index 3d956f6e..00000000 --- a/src/TablesAndTuples/SimpleJson.cs +++ /dev/null @@ -1,2127 +0,0 @@ -//----------------------------------------------------------------------- -// -// Copyright (c) 2011, The Outercurve Foundation. -// -// Licensed under the MIT License (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// http://www.opensource.org/licenses/mit-license.php -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -// Nathan Totten (ntotten.com), Jim Zimmerman (jimzimmerman.com) and Prabir Shrestha (prabir.me) -// https://github.com/facebook-csharp-sdk/simple-json -//----------------------------------------------------------------------- - -// VERSION: - -// NOTE: uncomment the following line to make SimpleJson class internal. -#define SIMPLE_JSON_INTERNAL - -// NOTE: uncomment the following line to make JsonArray and JsonObject class internal. -#define SIMPLE_JSON_OBJARRAYINTERNAL - -// NOTE: uncomment the following line to enable dynamic support. -//#define SIMPLE_JSON_DYNAMIC - -// NOTE: uncomment the following line to enable DataContract support. -//#define SIMPLE_JSON_DATACONTRACT - -// NOTE: uncomment the following line to enable IReadOnlyCollection and IReadOnlyList support. -//#define SIMPLE_JSON_READONLY_COLLECTIONS - -// NOTE: uncomment the following line to disable linq expressions/compiled lambda (better performance) instead of method.invoke(). -// define if you are using .net framework <= 3.0 or < WP7.5 -//#define SIMPLE_JSON_NO_LINQ_EXPRESSION - -// NOTE: uncomment the following line if you are compiling under Window Metro style application/library. -// usually already defined in properties -//#define NETFX_CORE; - -// If you are targetting WinStore, WP8 and NET4.5+ PCL make sure to #define SIMPLE_JSON_TYPEINFO; - -// 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 - -#if NETFX_CORE -#define SIMPLE_JSON_TYPEINFO -#endif - -using System; -using System.CodeDom.Compiler; -using System.Collections; -using System.Collections.Generic; -#if !SIMPLE_JSON_NO_LINQ_EXPRESSION -using System.Linq.Expressions; -#endif -using System.ComponentModel; -using System.Diagnostics.CodeAnalysis; -#if SIMPLE_JSON_DYNAMIC -using System.Dynamic; -#endif -using System.Globalization; -using System.Reflection; -using System.Runtime.Serialization; -using System.Text; -using SimpleJson.Reflection; - -// ReSharper disable LoopCanBeConvertedToQuery -// ReSharper disable RedundantExplicitArrayCreation -// ReSharper disable SuggestUseVarKeywordEvident -namespace SimpleJson -{ - /// - /// Represents the json array. - /// - [GeneratedCode("simple-json", "1.0.0")] - [EditorBrowsable(EditorBrowsableState.Never)] - [SuppressMessage("Microsoft.Naming", "CA1710:IdentifiersShouldHaveCorrectSuffix")] -#if SIMPLE_JSON_OBJARRAYINTERNAL - internal -#else - public -#endif - class JsonArray : List - { - /// - /// Initializes a new instance of the class. - /// - public JsonArray() { } - - /// - /// Initializes a new instance of the class. - /// - /// The capacity of the json array. - public JsonArray(int capacity) : base(capacity) { } - - /// - /// The json representation of the array. - /// - /// The json representation of the array. - public override string ToString() - { - return SimpleJson.SerializeObject(this) ?? string.Empty; - } - } - - /// - /// Represents the json object. - /// - [GeneratedCode("simple-json", "1.0.0")] - [EditorBrowsable(EditorBrowsableState.Never)] - [SuppressMessage("Microsoft.Naming", "CA1710:IdentifiersShouldHaveCorrectSuffix")] -#if SIMPLE_JSON_OBJARRAYINTERNAL - internal -#else - public -#endif - class JsonObject : -#if SIMPLE_JSON_DYNAMIC - DynamicObject, -#endif - IDictionary - { - /// - /// The internal member dictionary. - /// - private readonly Dictionary _members; - - /// - /// Initializes a new instance of . - /// - public JsonObject() - { - _members = new Dictionary(); - } - - /// - /// Initializes a new instance of . - /// - /// The implementation to use when comparing keys, or null to use the default for the type of the key. - public JsonObject(IEqualityComparer comparer) - { - _members = new Dictionary(comparer); - } - - /// - /// Gets the at the specified index. - /// - /// - public object this[int index] - { - get { return GetAtIndex(_members, index); } - } - - internal static object GetAtIndex(IDictionary obj, int index) - { - if (obj == null) - throw new ArgumentNullException("obj"); - if (index >= obj.Count) - throw new ArgumentOutOfRangeException("index"); - int i = 0; - foreach (KeyValuePair o in obj) - if (i++ == index) return o.Value; - return null; - } - - /// - /// Adds the specified key. - /// - /// The key. - /// The value. - public void Add(string key, object value) - { - _members.Add(key, value); - } - - /// - /// Determines whether the specified key contains key. - /// - /// The key. - /// - /// true if the specified key contains key; otherwise, false. - /// - public bool ContainsKey(string key) - { - return _members.ContainsKey(key); - } - - /// - /// Gets the keys. - /// - /// The keys. - public ICollection Keys - { - get { return _members.Keys; } - } - - /// - /// Removes the specified key. - /// - /// The key. - /// - public bool Remove(string key) - { - return _members.Remove(key); - } - - /// - /// Tries the get value. - /// - /// The key. - /// The value. - /// - public bool TryGetValue(string key, out object value) - { - return _members.TryGetValue(key, out value); - } - - /// - /// Gets the values. - /// - /// The values. - public ICollection Values - { - get { return _members.Values; } - } - - /// - /// Gets or sets the with the specified key. - /// - /// - public object this[string key] - { - get { return _members[key]; } - set { _members[key] = value; } - } - - /// - /// Adds the specified item. - /// - /// The item. - public void Add(KeyValuePair item) - { - _members.Add(item.Key, item.Value); - } - - /// - /// Clears this instance. - /// - public void Clear() - { - _members.Clear(); - } - - /// - /// Determines whether [contains] [the specified item]. - /// - /// The item. - /// - /// true if [contains] [the specified item]; otherwise, false. - /// - public bool Contains(KeyValuePair item) - { - return _members.ContainsKey(item.Key) && _members[item.Key] == item.Value; - } - - /// - /// Copies to. - /// - /// The array. - /// Index of the array. - public void CopyTo(KeyValuePair[] array, int arrayIndex) - { - if (array == null) throw new ArgumentNullException("array"); - int num = Count; - foreach (KeyValuePair kvp in this) - { - array[arrayIndex++] = kvp; - if (--num <= 0) - return; - } - } - - /// - /// Gets the count. - /// - /// The count. - public int Count - { - get { return _members.Count; } - } - - /// - /// Gets a value indicating whether this instance is read only. - /// - /// - /// true if this instance is read only; otherwise, false. - /// - public bool IsReadOnly - { - get { return false; } - } - - /// - /// Removes the specified item. - /// - /// The item. - /// - public bool Remove(KeyValuePair item) - { - return _members.Remove(item.Key); - } - - /// - /// Gets the enumerator. - /// - /// - public IEnumerator> GetEnumerator() - { - return _members.GetEnumerator(); - } - - /// - /// Returns an enumerator that iterates through a collection. - /// - /// - /// An object that can be used to iterate through the collection. - /// - IEnumerator IEnumerable.GetEnumerator() - { - return _members.GetEnumerator(); - } - - /// - /// Returns a json that represents the current . - /// - /// - /// A json that represents the current . - /// - public override string ToString() - { - return SimpleJson.SerializeObject(this); - } - -#if SIMPLE_JSON_DYNAMIC - /// - /// Provides implementation for type conversion operations. Classes derived from the class can override this method to specify dynamic behavior for operations that convert an object from one type to another. - /// - /// 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 class, binder.Type returns the 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. - /// The result of the type conversion operation. - /// - /// Alwasy returns true. - /// - public override bool TryConvert(ConvertBinder binder, out object result) - { - // - if (binder == null) - throw new ArgumentNullException("binder"); - // - Type targetType = binder.Type; - - if ((targetType == typeof(IEnumerable)) || - (targetType == typeof(IEnumerable>)) || - (targetType == typeof(IDictionary)) || - (targetType == typeof(IDictionary))) - { - result = this; - return true; - } - - return base.TryConvert(binder, out result); - } - - /// - /// Provides the implementation for operations that delete an object member. This method is not intended for use in C# or Visual Basic. - /// - /// Provides information about the deletion. - /// - /// Alwasy returns true. - /// - public override bool TryDeleteMember(DeleteMemberBinder binder) - { - // - if (binder == null) - throw new ArgumentNullException("binder"); - // - return _members.Remove(binder.Name); - } - - /// - /// Provides the implementation for operations that get a value by index. Classes derived from the class can override this method to specify dynamic behavior for indexing operations. - /// - /// Provides information about the operation. - /// 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, is equal to 3. - /// The result of the index operation. - /// - /// Alwasy returns true. - /// - public override bool TryGetIndex(GetIndexBinder binder, object[] indexes, out object result) - { - if (indexes == null) throw new ArgumentNullException("indexes"); - if (indexes.Length == 1) - { - result = ((IDictionary)this)[(string)indexes[0]]; - return true; - } - result = null; - return true; - } - - /// - /// Provides the implementation for operations that get member values. Classes derived from the class can override this method to specify dynamic behavior for operations such as getting a value for a property. - /// - /// 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 class, binder.Name returns "SampleProperty". The binder.IgnoreCase property specifies whether the member name is case-sensitive. - /// The result of the get operation. For example, if the method is called for a property, you can assign the property value to . - /// - /// Alwasy returns true. - /// - public override bool TryGetMember(GetMemberBinder binder, out object result) - { - object value; - if (_members.TryGetValue(binder.Name, out value)) - { - result = value; - return true; - } - result = null; - return true; - } - - /// - /// Provides the implementation for operations that set a value by index. Classes derived from the class can override this method to specify dynamic behavior for operations that access objects by a specified index. - /// - /// Provides information about the operation. - /// 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 class, is equal to 3. - /// 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 class, is equal to 10. - /// - /// 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. - /// - public override bool TrySetIndex(SetIndexBinder binder, object[] indexes, object value) - { - if (indexes == null) throw new ArgumentNullException("indexes"); - if (indexes.Length == 1) - { - ((IDictionary)this)[(string)indexes[0]] = value; - return true; - } - return base.TrySetIndex(binder, indexes, value); - } - - /// - /// Provides the implementation for operations that set member values. Classes derived from the class can override this method to specify dynamic behavior for operations such as setting a value for a property. - /// - /// 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 class, binder.Name returns "SampleProperty". The binder.IgnoreCase property specifies whether the member name is case-sensitive. - /// The value to set to the member. For example, for sampleObject.SampleProperty = "Test", where sampleObject is an instance of the class derived from the class, the is "Test". - /// - /// 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.) - /// - public override bool TrySetMember(SetMemberBinder binder, object value) - { - // - if (binder == null) - throw new ArgumentNullException("binder"); - // - _members[binder.Name] = value; - return true; - } - - /// - /// Returns the enumeration of all dynamic member names. - /// - /// - /// A sequence that contains dynamic member names. - /// - public override IEnumerable GetDynamicMemberNames() - { - foreach (var key in Keys) - yield return key; - } -#endif - } -} - -namespace SimpleJson -{ - /// - /// This class encodes and decodes JSON strings. - /// Spec. details, see http://www.json.org/ - /// - /// JSON uses Arrays and Objects. These correspond here to the datatypes JsonArray(IList<object>) and JsonObject(IDictionary<string,object>). - /// All numbers are parsed to doubles. - /// - [GeneratedCode("simple-json", "1.0.0")] -#if SIMPLE_JSON_INTERNAL - internal -#else - public -#endif - static class SimpleJson - { - private const int TOKEN_NONE = 0; - private const int TOKEN_CURLY_OPEN = 1; - private const int TOKEN_CURLY_CLOSE = 2; - private const int TOKEN_SQUARED_OPEN = 3; - private const int TOKEN_SQUARED_CLOSE = 4; - private const int TOKEN_COLON = 5; - private const int TOKEN_COMMA = 6; - private const int TOKEN_STRING = 7; - private const int TOKEN_NUMBER = 8; - private const int TOKEN_TRUE = 9; - private const int TOKEN_FALSE = 10; - private const int TOKEN_NULL = 11; - private const int BUILDER_CAPACITY = 2000; - - private static readonly char[] EscapeTable; - private static readonly char[] EscapeCharacters = new char[] { '"', '\\', '\b', '\f', '\n', '\r', '\t' }; - private static readonly string EscapeCharactersString = new string(EscapeCharacters); - - static SimpleJson() - { - EscapeTable = new char[93]; - EscapeTable['"'] = '"'; - EscapeTable['\\'] = '\\'; - EscapeTable['\b'] = 'b'; - EscapeTable['\f'] = 'f'; - EscapeTable['\n'] = 'n'; - EscapeTable['\r'] = 'r'; - EscapeTable['\t'] = 't'; - } - - /// - /// Parses the string json into a value - /// - /// A JSON string. - /// An IList<object>, a IDictionary<string,object>, a double, a string, null, true, or false - public static object DeserializeObject(string json) - { - object obj; - if (TryDeserializeObject(json, out obj)) - return obj; - throw new SerializationException("Invalid JSON string"); - } - - /// - /// Try parsing the json string into a value. - /// - /// - /// A JSON string. - /// - /// - /// The object. - /// - /// - /// Returns true if successfull otherwise false. - /// - [SuppressMessage("Microsoft.Design", "CA1007:UseGenericsWhereAppropriate", Justification = "Need to support .NET 2")] - public static bool TryDeserializeObject(string json, out object obj) - { - bool success = true; - if (json != null) - { - char[] charArray = json.ToCharArray(); - int index = 0; - obj = ParseValue(charArray, ref index, ref success); - } - else - obj = null; - - return success; - } - - public static object DeserializeObject(string json, Type type, IJsonSerializerStrategy jsonSerializerStrategy) - { - object jsonObject = DeserializeObject(json); - return type == null || jsonObject != null && ReflectionUtils.IsAssignableFrom(jsonObject.GetType(), type) - ? jsonObject - : (jsonSerializerStrategy ?? CurrentJsonSerializerStrategy).DeserializeObject(jsonObject, type); - } - - public static object DeserializeObject(string json, Type type) - { - return DeserializeObject(json, type, null); - } - - public static T DeserializeObject(string json, IJsonSerializerStrategy jsonSerializerStrategy) - { - return (T)DeserializeObject(json, typeof(T), jsonSerializerStrategy); - } - - public static T DeserializeObject(string json) - { - return (T)DeserializeObject(json, typeof(T), null); - } - - /// - /// Converts a IDictionary<string,object> / IList<object> object into a JSON string - /// - /// A IDictionary<string,object> / IList<object> - /// Serializer strategy to use - /// A JSON encoded string, or null if object 'json' is not serializable - public static string SerializeObject(object json, IJsonSerializerStrategy jsonSerializerStrategy) - { - StringBuilder builder = new StringBuilder(BUILDER_CAPACITY); - bool success = SerializeValue(jsonSerializerStrategy, json, builder); - return (success ? builder.ToString() : null); - } - - public static string SerializeObject(object json) - { - return SerializeObject(json, CurrentJsonSerializerStrategy); - } - - public static string EscapeToJavascriptString(string jsonString) - { - if (string.IsNullOrEmpty(jsonString)) - return jsonString; - - StringBuilder sb = new StringBuilder(); - char c; - - for (int i = 0; i < jsonString.Length;) - { - c = jsonString[i++]; - - if (c == '\\') - { - int remainingLength = jsonString.Length - i; - if (remainingLength >= 2) - { - char lookahead = jsonString[i]; - if (lookahead == '\\') - { - sb.Append('\\'); - ++i; - } - else if (lookahead == '"') - { - sb.Append("\""); - ++i; - } - else if (lookahead == 't') - { - sb.Append('\t'); - ++i; - } - else if (lookahead == 'b') - { - sb.Append('\b'); - ++i; - } - else if (lookahead == 'n') - { - sb.Append('\n'); - ++i; - } - else if (lookahead == 'r') - { - sb.Append('\r'); - ++i; - } - } - } - else - { - sb.Append(c); - } - } - return sb.ToString(); - } - - static IDictionary ParseObject(char[] json, ref int index, ref bool success) - { - IDictionary table = new JsonObject(); - int token; - - // { - NextToken(json, ref index); - - bool done = false; - while (!done) - { - token = LookAhead(json, index); - if (token == TOKEN_NONE) - { - success = false; - return null; - } - else if (token == TOKEN_COMMA) - NextToken(json, ref index); - else if (token == TOKEN_CURLY_CLOSE) - { - NextToken(json, ref index); - return table; - } - else - { - // name - string name = ParseString(json, ref index, ref success); - if (!success) - { - success = false; - return null; - } - // : - token = NextToken(json, ref index); - if (token != TOKEN_COLON) - { - success = false; - return null; - } - // value - object value = ParseValue(json, ref index, ref success); - if (!success) - { - success = false; - return null; - } - table[name] = value; - } - } - return table; - } - - static JsonArray ParseArray(char[] json, ref int index, ref bool success) - { - JsonArray array = new JsonArray(); - - // [ - NextToken(json, ref index); - - bool done = false; - while (!done) - { - int token = LookAhead(json, index); - if (token == TOKEN_NONE) - { - success = false; - return null; - } - else if (token == TOKEN_COMMA) - NextToken(json, ref index); - else if (token == TOKEN_SQUARED_CLOSE) - { - NextToken(json, ref index); - break; - } - else - { - object value = ParseValue(json, ref index, ref success); - if (!success) - return null; - array.Add(value); - } - } - return array; - } - - static object ParseValue(char[] json, ref int index, ref bool success) - { - switch (LookAhead(json, index)) - { - case TOKEN_STRING: - return ParseString(json, ref index, ref success); - case TOKEN_NUMBER: - return ParseNumber(json, ref index, ref success); - case TOKEN_CURLY_OPEN: - return ParseObject(json, ref index, ref success); - case TOKEN_SQUARED_OPEN: - return ParseArray(json, ref index, ref success); - case TOKEN_TRUE: - NextToken(json, ref index); - return true; - case TOKEN_FALSE: - NextToken(json, ref index); - return false; - case TOKEN_NULL: - NextToken(json, ref index); - return null; - case TOKEN_NONE: - break; - } - success = false; - return null; - } - - static string ParseString(char[] json, ref int index, ref bool success) - { - StringBuilder s = new StringBuilder(BUILDER_CAPACITY); - char c; - - EatWhitespace(json, ref index); - - // " - c = json[index++]; - bool complete = false; - while (!complete) - { - if (index == json.Length) - break; - - c = json[index++]; - if (c == '"') - { - complete = true; - break; - } - else if (c == '\\') - { - if (index == json.Length) - break; - c = json[index++]; - if (c == '"') - s.Append('"'); - else if (c == '\\') - s.Append('\\'); - else if (c == '/') - s.Append('/'); - else if (c == 'b') - s.Append('\b'); - else if (c == 'f') - s.Append('\f'); - else if (c == 'n') - s.Append('\n'); - else if (c == 'r') - s.Append('\r'); - else if (c == 't') - s.Append('\t'); - else if (c == 'u') - { - int remainingLength = json.Length - index; - if (remainingLength >= 4) - { - // parse the 32 bit hex into an integer codepoint - uint codePoint; - if (!(success = UInt32.TryParse(new string(json, index, 4), NumberStyles.HexNumber, CultureInfo.InvariantCulture, out codePoint))) - return ""; - - // convert the integer codepoint to a unicode char and add to string - if (0xD800 <= codePoint && codePoint <= 0xDBFF) // if high surrogate - { - index += 4; // skip 4 chars - remainingLength = json.Length - index; - if (remainingLength >= 6) - { - uint lowCodePoint; - if (new string(json, index, 2) == "\\u" && UInt32.TryParse(new string(json, index + 2, 4), NumberStyles.HexNumber, CultureInfo.InvariantCulture, out lowCodePoint)) - { - if (0xDC00 <= lowCodePoint && lowCodePoint <= 0xDFFF) // if low surrogate - { - s.Append((char)codePoint); - s.Append((char)lowCodePoint); - index += 6; // skip 6 chars - continue; - } - } - } - success = false; // invalid surrogate pair - return ""; - } - s.Append(ConvertFromUtf32((int)codePoint)); - // skip 4 chars - index += 4; - } - else - break; - } - } - else - s.Append(c); - } - if (!complete) - { - success = false; - return null; - } - return s.ToString(); - } - - private static string ConvertFromUtf32(int utf32) - { - // http://www.java2s.com/Open-Source/CSharp/2.6.4-mono-.net-core/System/System/Char.cs.htm - if (utf32 < 0 || utf32 > 0x10FFFF) - throw new ArgumentOutOfRangeException("utf32", "The argument must be from 0 to 0x10FFFF."); - if (0xD800 <= utf32 && utf32 <= 0xDFFF) - throw new ArgumentOutOfRangeException("utf32", "The argument must not be in surrogate pair range."); - if (utf32 < 0x10000) - return new string((char)utf32, 1); - utf32 -= 0x10000; - return new string(new char[] { (char)((utf32 >> 10) + 0xD800), (char)(utf32 % 0x0400 + 0xDC00) }); - } - - static object ParseNumber(char[] json, ref int index, ref bool success) - { - EatWhitespace(json, ref index); - int lastIndex = GetLastIndexOfNumber(json, index); - int charLength = (lastIndex - index) + 1; - object returnNumber; - string str = new string(json, index, charLength); - if (str.IndexOf(".", StringComparison.OrdinalIgnoreCase) != -1 || str.IndexOf("e", StringComparison.OrdinalIgnoreCase) != -1) - { - double number; - success = double.TryParse(new string(json, index, charLength), NumberStyles.Any, CultureInfo.InvariantCulture, out number); - returnNumber = number; - } - else - { - long number; - success = long.TryParse(new string(json, index, charLength), NumberStyles.Any, CultureInfo.InvariantCulture, out number); - returnNumber = number; - } - index = lastIndex + 1; - return returnNumber; - } - - static int GetLastIndexOfNumber(char[] json, int index) - { - int lastIndex; - for (lastIndex = index; lastIndex < json.Length; lastIndex++) - if ("0123456789+-.eE".IndexOf(json[lastIndex]) == -1) break; - return lastIndex - 1; - } - - static void EatWhitespace(char[] json, ref int index) - { - for (; index < json.Length; index++) - if (" \t\n\r\b\f".IndexOf(json[index]) == -1) break; - } - - static int LookAhead(char[] json, int index) - { - int saveIndex = index; - return NextToken(json, ref saveIndex); - } - - [SuppressMessage("Microsoft.Maintainability", "CA1502:AvoidExcessiveComplexity")] - static int NextToken(char[] json, ref int index) - { - EatWhitespace(json, ref index); - if (index == json.Length) - return TOKEN_NONE; - char c = json[index]; - index++; - switch (c) - { - case '{': - return TOKEN_CURLY_OPEN; - case '}': - return TOKEN_CURLY_CLOSE; - case '[': - return TOKEN_SQUARED_OPEN; - case ']': - return TOKEN_SQUARED_CLOSE; - case ',': - return TOKEN_COMMA; - case '"': - return TOKEN_STRING; - case '0': - case '1': - case '2': - case '3': - case '4': - case '5': - case '6': - case '7': - case '8': - case '9': - case '-': - return TOKEN_NUMBER; - case ':': - return TOKEN_COLON; - } - index--; - int remainingLength = json.Length - index; - // false - if (remainingLength >= 5) - { - if (json[index] == 'f' && json[index + 1] == 'a' && json[index + 2] == 'l' && json[index + 3] == 's' && json[index + 4] == 'e') - { - index += 5; - return TOKEN_FALSE; - } - } - // true - if (remainingLength >= 4) - { - if (json[index] == 't' && json[index + 1] == 'r' && json[index + 2] == 'u' && json[index + 3] == 'e') - { - index += 4; - return TOKEN_TRUE; - } - } - // null - if (remainingLength >= 4) - { - if (json[index] == 'n' && json[index + 1] == 'u' && json[index + 2] == 'l' && json[index + 3] == 'l') - { - index += 4; - return TOKEN_NULL; - } - } - return TOKEN_NONE; - } - - static bool SerializeValue(IJsonSerializerStrategy jsonSerializerStrategy, object value, StringBuilder builder) - { - bool success = true; - string stringValue = value as string; - if (stringValue != null) - success = SerializeString(stringValue, builder); - else - { - IDictionary dict = value as IDictionary; - if (dict != null) - { - success = SerializeObject(jsonSerializerStrategy, dict.Keys, dict.Values, builder); - } - else - { - IDictionary stringDictionary = value as IDictionary; - if (stringDictionary != null) - { - success = SerializeObject(jsonSerializerStrategy, stringDictionary.Keys, stringDictionary.Values, builder); - } - else - { - IEnumerable enumerableValue = value as IEnumerable; - if (enumerableValue != null) - success = SerializeArray(jsonSerializerStrategy, enumerableValue, builder); - else if (IsNumeric(value)) - success = SerializeNumber(value, builder); - else if (value is bool) - builder.Append((bool)value ? "true" : "false"); - else if (value == null) - builder.Append("null"); - else - { - object serializedObject; - success = jsonSerializerStrategy.TrySerializeNonPrimitiveObject(value, out serializedObject); - if (success) - SerializeValue(jsonSerializerStrategy, serializedObject, builder); - } - } - } - } - return success; - } - - static bool SerializeObject(IJsonSerializerStrategy jsonSerializerStrategy, IEnumerable keys, IEnumerable values, StringBuilder builder) - { - builder.Append("{"); - IEnumerator ke = keys.GetEnumerator(); - IEnumerator ve = values.GetEnumerator(); - bool first = true; - while (ke.MoveNext() && ve.MoveNext()) - { - object key = ke.Current; - object value = ve.Current; - if (!first) - builder.Append(","); - string stringKey = key as string; - if (stringKey != null) - SerializeString(stringKey, builder); - else - if (!SerializeValue(jsonSerializerStrategy, value, builder)) return false; - builder.Append(":"); - if (!SerializeValue(jsonSerializerStrategy, value, builder)) - return false; - first = false; - } - builder.Append("}"); - return true; - } - - static bool SerializeArray(IJsonSerializerStrategy jsonSerializerStrategy, IEnumerable anArray, StringBuilder builder) - { - builder.Append("["); - bool first = true; - foreach (object value in anArray) - { - if (!first) - builder.Append(","); - if (!SerializeValue(jsonSerializerStrategy, value, builder)) - return false; - first = false; - } - builder.Append("]"); - return true; - } - - static bool SerializeString(string aString, StringBuilder builder) - { - // Happy path if there's nothing to be escaped. IndexOfAny is highly optimized (and unmanaged) - if (aString.IndexOfAny(EscapeCharacters) == -1) - { - builder.Append('"'); - builder.Append(aString); - builder.Append('"'); - - return true; - } - - builder.Append('"'); - int safeCharacterCount = 0; - char[] charArray = aString.ToCharArray(); - - for (int i = 0; i < charArray.Length; i++) - { - char c = charArray[i]; - - // Non ascii characters are fine, buffer them up and send them to the builder - // in larger chunks if possible. The escape table is a 1:1 translation table - // with \0 [default(char)] denoting a safe character. - if (c >= EscapeTable.Length || EscapeTable[c] == default(char)) - { - safeCharacterCount++; - } - else - { - if (safeCharacterCount > 0) - { - builder.Append(charArray, i - safeCharacterCount, safeCharacterCount); - safeCharacterCount = 0; - } - - builder.Append('\\'); - builder.Append(EscapeTable[c]); - } - } - - if (safeCharacterCount > 0) - { - builder.Append(charArray, charArray.Length - safeCharacterCount, safeCharacterCount); - } - - builder.Append('"'); - return true; - } - - static bool SerializeNumber(object number, StringBuilder builder) - { - if (number is long) - builder.Append(((long)number).ToString(CultureInfo.InvariantCulture)); - else if (number is ulong) - builder.Append(((ulong)number).ToString(CultureInfo.InvariantCulture)); - else if (number is int) - builder.Append(((int)number).ToString(CultureInfo.InvariantCulture)); - else if (number is uint) - builder.Append(((uint)number).ToString(CultureInfo.InvariantCulture)); - else if (number is decimal) - builder.Append(((decimal)number).ToString(CultureInfo.InvariantCulture)); - else if (number is float) - builder.Append(((float)number).ToString(CultureInfo.InvariantCulture)); - else - builder.Append(Convert.ToDouble(number, CultureInfo.InvariantCulture).ToString("r", CultureInfo.InvariantCulture)); - return true; - } - - /// - /// Determines if a given object is numeric in any way - /// (can be integer, double, null, etc). - /// - static bool IsNumeric(object value) - { - if (value is sbyte) return true; - if (value is byte) return true; - if (value is short) return true; - if (value is ushort) return true; - if (value is int) return true; - if (value is uint) return true; - if (value is long) return true; - if (value is ulong) return true; - if (value is float) return true; - if (value is double) return true; - if (value is decimal) return true; - return false; - } - - private static IJsonSerializerStrategy _currentJsonSerializerStrategy; - public static IJsonSerializerStrategy CurrentJsonSerializerStrategy - { - get - { - return _currentJsonSerializerStrategy ?? - (_currentJsonSerializerStrategy = -#if SIMPLE_JSON_DATACONTRACT - DataContractJsonSerializerStrategy -#else - PocoJsonSerializerStrategy -#endif -); - } - set - { - _currentJsonSerializerStrategy = value; - } - } - - private static PocoJsonSerializerStrategy _pocoJsonSerializerStrategy; - [EditorBrowsable(EditorBrowsableState.Advanced)] - public static PocoJsonSerializerStrategy PocoJsonSerializerStrategy - { - get - { - return _pocoJsonSerializerStrategy ?? (_pocoJsonSerializerStrategy = new PocoJsonSerializerStrategy()); - } - } - -#if SIMPLE_JSON_DATACONTRACT - - private static DataContractJsonSerializerStrategy _dataContractJsonSerializerStrategy; - [System.ComponentModel.EditorBrowsable(EditorBrowsableState.Advanced)] - public static DataContractJsonSerializerStrategy DataContractJsonSerializerStrategy - { - get - { - return _dataContractJsonSerializerStrategy ?? (_dataContractJsonSerializerStrategy = new DataContractJsonSerializerStrategy()); - } - } - -#endif - } - - [GeneratedCode("simple-json", "1.0.0")] -#if SIMPLE_JSON_INTERNAL - internal -#else - public -#endif - interface IJsonSerializerStrategy - { - [SuppressMessage("Microsoft.Design", "CA1007:UseGenericsWhereAppropriate", Justification = "Need to support .NET 2")] - bool TrySerializeNonPrimitiveObject(object input, out object output); - object DeserializeObject(object value, Type type); - } - - [GeneratedCode("simple-json", "1.0.0")] -#if SIMPLE_JSON_INTERNAL - internal -#else - public -#endif - class PocoJsonSerializerStrategy : IJsonSerializerStrategy - { - internal IDictionary ConstructorCache; - internal IDictionary> GetCache; - internal IDictionary>> SetCache; - - internal static readonly Type[] EmptyTypes = new Type[0]; - internal static readonly Type[] ArrayConstructorParameterTypes = new Type[] { typeof(int) }; - - private static readonly string[] Iso8601Format = new string[] - { - @"yyyy-MM-dd\THH:mm:ss.FFFFFFF\Z", - @"yyyy-MM-dd\THH:mm:ss\Z", - @"yyyy-MM-dd\THH:mm:ssK" - }; - - public PocoJsonSerializerStrategy() - { - ConstructorCache = new ReflectionUtils.ThreadSafeDictionary(ContructorDelegateFactory); - GetCache = new ReflectionUtils.ThreadSafeDictionary>(GetterValueFactory); - SetCache = new ReflectionUtils.ThreadSafeDictionary>>(SetterValueFactory); - } - - protected virtual string MapClrMemberNameToJsonFieldName(string clrPropertyName) - { - return clrPropertyName; - } - - internal virtual ReflectionUtils.ConstructorDelegate ContructorDelegateFactory(Type key) - { - return ReflectionUtils.GetContructor(key, key.IsArray ? ArrayConstructorParameterTypes : EmptyTypes); - } - - internal virtual IDictionary GetterValueFactory(Type type) - { - IDictionary result = new Dictionary(); - foreach (PropertyInfo propertyInfo in ReflectionUtils.GetProperties(type)) - { - if (propertyInfo.CanRead) - { - MethodInfo getMethod = ReflectionUtils.GetGetterMethodInfo(propertyInfo); - if (getMethod.IsStatic || !getMethod.IsPublic) - continue; - result[MapClrMemberNameToJsonFieldName(propertyInfo.Name)] = ReflectionUtils.GetGetMethod(propertyInfo); - } - } - foreach (FieldInfo fieldInfo in ReflectionUtils.GetFields(type)) - { - if (fieldInfo.IsStatic || !fieldInfo.IsPublic) - continue; - result[MapClrMemberNameToJsonFieldName(fieldInfo.Name)] = ReflectionUtils.GetGetMethod(fieldInfo); - } - return result; - } - - internal virtual IDictionary> SetterValueFactory(Type type) - { - IDictionary> result = new Dictionary>(); - foreach (PropertyInfo propertyInfo in ReflectionUtils.GetProperties(type)) - { - if (propertyInfo.CanWrite) - { - MethodInfo setMethod = ReflectionUtils.GetSetterMethodInfo(propertyInfo); - if (setMethod.IsStatic || !setMethod.IsPublic) - continue; - result[MapClrMemberNameToJsonFieldName(propertyInfo.Name)] = new KeyValuePair(propertyInfo.PropertyType, ReflectionUtils.GetSetMethod(propertyInfo)); - } - } - foreach (FieldInfo fieldInfo in ReflectionUtils.GetFields(type)) - { - if (fieldInfo.IsInitOnly || fieldInfo.IsStatic || !fieldInfo.IsPublic) - continue; - result[MapClrMemberNameToJsonFieldName(fieldInfo.Name)] = new KeyValuePair(fieldInfo.FieldType, ReflectionUtils.GetSetMethod(fieldInfo)); - } - return result; - } - - public virtual bool TrySerializeNonPrimitiveObject(object input, out object output) - { - return TrySerializeKnownTypes(input, out output) || TrySerializeUnknownTypes(input, out output); - } - - [SuppressMessage("Microsoft.Maintainability", "CA1502:AvoidExcessiveComplexity")] - public virtual object DeserializeObject(object value, Type type) - { - if (type == null) throw new ArgumentNullException("type"); - string str = value as string; - - if (type == typeof(Guid) && string.IsNullOrEmpty(str)) - return default(Guid); - - if (value == null) - return null; - - object obj = null; - - if (str != null) - { - if (str.Length != 0) // We know it can't be null now. - { - if (type == typeof(DateTime) || (ReflectionUtils.IsNullableType(type) && Nullable.GetUnderlyingType(type) == typeof(DateTime))) - return DateTime.ParseExact(str, Iso8601Format, CultureInfo.InvariantCulture, DateTimeStyles.AssumeUniversal | DateTimeStyles.AdjustToUniversal); - if (type == typeof(DateTimeOffset) || (ReflectionUtils.IsNullableType(type) && Nullable.GetUnderlyingType(type) == typeof(DateTimeOffset))) - return DateTimeOffset.ParseExact(str, Iso8601Format, CultureInfo.InvariantCulture, DateTimeStyles.AssumeUniversal | DateTimeStyles.AdjustToUniversal); - if (type == typeof(Guid) || (ReflectionUtils.IsNullableType(type) && Nullable.GetUnderlyingType(type) == typeof(Guid))) - return new Guid(str); - if (type == typeof(Uri)) - { - bool isValid = Uri.IsWellFormedUriString(str, UriKind.RelativeOrAbsolute); - - Uri result; - if (isValid && Uri.TryCreate(str, UriKind.RelativeOrAbsolute, out result)) - return result; - - return null; - } - - if (type == typeof(string)) - return str; - - return Convert.ChangeType(str, type, CultureInfo.InvariantCulture); - } - else - { - if (type == typeof(Guid)) - obj = default(Guid); - else if (ReflectionUtils.IsNullableType(type) && Nullable.GetUnderlyingType(type) == typeof(Guid)) - obj = null; - else - obj = str; - } - // Empty string case - if (!ReflectionUtils.IsNullableType(type) && Nullable.GetUnderlyingType(type) == typeof(Guid)) - return str; - } - else if (value is bool) - return value; - - bool valueIsLong = value is long; - bool valueIsDouble = value is double; - if ((valueIsLong && type == typeof(long)) || (valueIsDouble && type == typeof(double))) - return value; - if ((valueIsDouble && type != typeof(double)) || (valueIsLong && type != typeof(long))) - { - 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) - ? Convert.ChangeType(value, type, CultureInfo.InvariantCulture) - : value; - } - else - { - IDictionary objects = value as IDictionary; - if (objects != null) - { - IDictionary jsonObject = objects; - - if (ReflectionUtils.IsTypeDictionary(type)) - { - // if dictionary then - Type[] types = ReflectionUtils.GetGenericTypeArguments(type); - Type keyType = types[0]; - Type valueType = types[1]; - - Type genericType = typeof(Dictionary<,>).MakeGenericType(keyType, valueType); - - IDictionary dict = (IDictionary)ConstructorCache[genericType](); - - foreach (KeyValuePair kvp in jsonObject) - dict.Add(kvp.Key, DeserializeObject(kvp.Value, valueType)); - - obj = dict; - } - else - { - if (type == typeof(object)) - obj = value; - else - { - obj = ConstructorCache[type](); - foreach (KeyValuePair> setter in SetCache[type]) - { - object jsonValue; - if (jsonObject.TryGetValue(setter.Key, out jsonValue)) - { - jsonValue = DeserializeObject(jsonValue, setter.Value.Key); - setter.Value.Value(obj, jsonValue); - } - } - } - } - } - else - { - IList valueAsList = value as IList; - if (valueAsList != null) - { - IList jsonObject = valueAsList; - IList list = null; - - if (type.IsArray) - { - list = (IList)ConstructorCache[type](jsonObject.Count); - int i = 0; - foreach (object o in jsonObject) - list[i++] = DeserializeObject(o, type.GetElementType()); - } - else if (ReflectionUtils.IsTypeGenericeCollectionInterface(type) || ReflectionUtils.IsAssignableFrom(typeof(IList), type)) - { - Type innerType = ReflectionUtils.GetGenericListElementType(type); - list = (IList)(ConstructorCache[type] ?? ConstructorCache[typeof(List<>).MakeGenericType(innerType)])(jsonObject.Count); - foreach (object o in jsonObject) - list.Add(DeserializeObject(o, innerType)); - } - obj = list; - } - } - return obj; - } - if (ReflectionUtils.IsNullableType(type)) - return ReflectionUtils.ToNullableType(obj, type); - return obj; - } - - protected virtual object SerializeEnum(Enum p) - { - return Convert.ToDouble(p, CultureInfo.InvariantCulture); - } - - [SuppressMessage("Microsoft.Design", "CA1007:UseGenericsWhereAppropriate", Justification = "Need to support .NET 2")] - protected virtual bool TrySerializeKnownTypes(object input, out object output) - { - bool returnValue = true; - if (input is DateTime) - output = ((DateTime)input).ToUniversalTime().ToString(Iso8601Format[0], CultureInfo.InvariantCulture); - else if (input is DateTimeOffset) - output = ((DateTimeOffset)input).ToUniversalTime().ToString(Iso8601Format[0], CultureInfo.InvariantCulture); - else if (input is Guid) - output = ((Guid)input).ToString("D"); - else if (input is Uri) - output = input.ToString(); - else - { - Enum inputEnum = input as Enum; - if (inputEnum != null) - output = SerializeEnum(inputEnum); - else - { - returnValue = false; - output = null; - } - } - return returnValue; - } - [SuppressMessage("Microsoft.Design", "CA1007:UseGenericsWhereAppropriate", Justification = "Need to support .NET 2")] - protected virtual bool TrySerializeUnknownTypes(object input, out object output) - { - if (input == null) throw new ArgumentNullException("input"); - output = null; - Type type = input.GetType(); - if (type.FullName == null) - return false; - IDictionary obj = new JsonObject(); - IDictionary getters = GetCache[type]; - foreach (KeyValuePair getter in getters) - { - if (getter.Value != null) - obj.Add(MapClrMemberNameToJsonFieldName(getter.Key), getter.Value(input)); - } - output = obj; - return true; - } - } - -#if SIMPLE_JSON_DATACONTRACT - [GeneratedCode("simple-json", "1.0.0")] -#if SIMPLE_JSON_INTERNAL - internal -#else - public -#endif - class DataContractJsonSerializerStrategy : PocoJsonSerializerStrategy - { - public DataContractJsonSerializerStrategy() - { - GetCache = new ReflectionUtils.ThreadSafeDictionary>(GetterValueFactory); - SetCache = new ReflectionUtils.ThreadSafeDictionary>>(SetterValueFactory); - } - - internal override IDictionary GetterValueFactory(Type type) - { - bool hasDataContract = ReflectionUtils.GetAttribute(type, typeof(DataContractAttribute)) != null; - if (!hasDataContract) - return base.GetterValueFactory(type); - string jsonKey; - IDictionary result = new Dictionary(); - foreach (PropertyInfo propertyInfo in ReflectionUtils.GetProperties(type)) - { - if (propertyInfo.CanRead) - { - MethodInfo getMethod = ReflectionUtils.GetGetterMethodInfo(propertyInfo); - if (!getMethod.IsStatic && CanAdd(propertyInfo, out jsonKey)) - result[jsonKey] = ReflectionUtils.GetGetMethod(propertyInfo); - } - } - foreach (FieldInfo fieldInfo in ReflectionUtils.GetFields(type)) - { - if (!fieldInfo.IsStatic && CanAdd(fieldInfo, out jsonKey)) - result[jsonKey] = ReflectionUtils.GetGetMethod(fieldInfo); - } - return result; - } - - internal override IDictionary> SetterValueFactory(Type type) - { - bool hasDataContract = ReflectionUtils.GetAttribute(type, typeof(DataContractAttribute)) != null; - if (!hasDataContract) - return base.SetterValueFactory(type); - string jsonKey; - IDictionary> result = new Dictionary>(); - foreach (PropertyInfo propertyInfo in ReflectionUtils.GetProperties(type)) - { - if (propertyInfo.CanWrite) - { - MethodInfo setMethod = ReflectionUtils.GetSetterMethodInfo(propertyInfo); - if (!setMethod.IsStatic && CanAdd(propertyInfo, out jsonKey)) - result[jsonKey] = new KeyValuePair(propertyInfo.PropertyType, ReflectionUtils.GetSetMethod(propertyInfo)); - } - } - foreach (FieldInfo fieldInfo in ReflectionUtils.GetFields(type)) - { - if (!fieldInfo.IsInitOnly && !fieldInfo.IsStatic && CanAdd(fieldInfo, out jsonKey)) - result[jsonKey] = new KeyValuePair(fieldInfo.FieldType, ReflectionUtils.GetSetMethod(fieldInfo)); - } - // todo implement sorting for DATACONTRACT. - return result; - } - - private static bool CanAdd(MemberInfo info, out string jsonKey) - { - jsonKey = null; - if (ReflectionUtils.GetAttribute(info, typeof(IgnoreDataMemberAttribute)) != null) - return false; - DataMemberAttribute dataMemberAttribute = (DataMemberAttribute)ReflectionUtils.GetAttribute(info, typeof(DataMemberAttribute)); - if (dataMemberAttribute == null) - return false; - jsonKey = string.IsNullOrEmpty(dataMemberAttribute.Name) ? info.Name : dataMemberAttribute.Name; - return true; - } - } - -#endif - - namespace Reflection - { - // This class is meant to be copied into other libraries. So we want to exclude it from Code Analysis rules - // that might be in place in the target project. - [GeneratedCode("reflection-utils", "1.0.0")] -#if SIMPLE_JSON_REFLECTION_UTILS_PUBLIC - public -#else - internal -#endif - class ReflectionUtils - { - private static readonly object[] EmptyObjects = new object[] { }; - - public delegate object GetDelegate(object source); - public delegate void SetDelegate(object source, object value); - public delegate object ConstructorDelegate(params object[] args); - - public delegate TValue ThreadSafeDictionaryValueFactory(TKey key); - -#if SIMPLE_JSON_TYPEINFO - public static TypeInfo GetTypeInfo(Type type) - { - return type.GetTypeInfo(); - } -#else - public static Type GetTypeInfo(Type type) - { - return type; - } -#endif - - public static Attribute GetAttribute(MemberInfo info, Type type) - { -#if SIMPLE_JSON_TYPEINFO - if (info == null || type == null || !info.IsDefined(type)) - return null; - return info.GetCustomAttribute(type); -#else - if (info == null || type == null || !Attribute.IsDefined(info, type)) - return null; - return Attribute.GetCustomAttribute(info, type); -#endif - } - - public static Type GetGenericListElementType(Type type) - { - IEnumerable interfaces; -#if SIMPLE_JSON_TYPEINFO - interfaces = type.GetTypeInfo().ImplementedInterfaces; -#else - interfaces = type.GetInterfaces(); -#endif - foreach (Type implementedInterface in interfaces) - { - if (IsTypeGeneric(implementedInterface) && - implementedInterface.GetGenericTypeDefinition() == typeof(IList<>)) - { - return GetGenericTypeArguments(implementedInterface)[0]; - } - } - return GetGenericTypeArguments(type)[0]; - } - - public static Attribute GetAttribute(Type objectType, Type attributeType) - { - -#if SIMPLE_JSON_TYPEINFO - if (objectType == null || attributeType == null || !objectType.GetTypeInfo().IsDefined(attributeType)) - return null; - return objectType.GetTypeInfo().GetCustomAttribute(attributeType); -#else - if (objectType == null || attributeType == null || !Attribute.IsDefined(objectType, attributeType)) - return null; - return Attribute.GetCustomAttribute(objectType, attributeType); -#endif - } - - public static Type[] GetGenericTypeArguments(Type type) - { -#if SIMPLE_JSON_TYPEINFO - return type.GetTypeInfo().GenericTypeArguments; -#else - return type.GetGenericArguments(); -#endif - } - - public static bool IsTypeGeneric(Type type) - { - return GetTypeInfo(type).IsGenericType; - } - - public static bool IsTypeGenericeCollectionInterface(Type type) - { - if (!IsTypeGeneric(type)) - return false; - - Type genericDefinition = type.GetGenericTypeDefinition(); - - return (genericDefinition == typeof(IList<>) - || genericDefinition == typeof(ICollection<>) - || genericDefinition == typeof(IEnumerable<>) -#if SIMPLE_JSON_READONLY_COLLECTIONS - || genericDefinition == typeof(IReadOnlyCollection<>) - || genericDefinition == typeof(IReadOnlyList<>) -#endif - ); - } - - public static bool IsAssignableFrom(Type type1, Type type2) - { - return GetTypeInfo(type1).IsAssignableFrom(GetTypeInfo(type2)); - } - - public static bool IsTypeDictionary(Type type) - { -#if SIMPLE_JSON_TYPEINFO - if (typeof(IDictionary<,>).GetTypeInfo().IsAssignableFrom(type.GetTypeInfo())) - return true; -#else - if (typeof(System.Collections.IDictionary).IsAssignableFrom(type)) - return true; -#endif - if (!GetTypeInfo(type).IsGenericType) - return false; - - Type genericDefinition = type.GetGenericTypeDefinition(); - return genericDefinition == typeof(IDictionary<,>); - } - - public static bool IsNullableType(Type type) - { - return GetTypeInfo(type).IsGenericType && type.GetGenericTypeDefinition() == typeof(Nullable<>); - } - - public static object ToNullableType(object obj, Type nullableType) - { - return obj == null ? null : Convert.ChangeType(obj, Nullable.GetUnderlyingType(nullableType), CultureInfo.InvariantCulture); - } - - public static bool IsValueType(Type type) - { - return GetTypeInfo(type).IsValueType; - } - - public static IEnumerable GetConstructors(Type type) - { -#if SIMPLE_JSON_TYPEINFO - return type.GetTypeInfo().DeclaredConstructors; -#else - return type.GetConstructors(); -#endif - } - - public static ConstructorInfo GetConstructorInfo(Type type, params Type[] argsType) - { - IEnumerable constructorInfos = GetConstructors(type); - int i; - bool matches; - foreach (ConstructorInfo constructorInfo in constructorInfos) - { - ParameterInfo[] parameters = constructorInfo.GetParameters(); - if (argsType.Length != parameters.Length) - continue; - - i = 0; - matches = true; - foreach (ParameterInfo parameterInfo in constructorInfo.GetParameters()) - { - if (parameterInfo.ParameterType != argsType[i]) - { - matches = false; - break; - } - } - - if (matches) - return constructorInfo; - } - - return null; - } - - public static IEnumerable GetProperties(Type type) - { -#if SIMPLE_JSON_TYPEINFO - return type.GetRuntimeProperties(); -#else - return type.GetProperties(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static); -#endif - } - - public static IEnumerable GetFields(Type type) - { -#if SIMPLE_JSON_TYPEINFO - return type.GetRuntimeFields(); -#else - return type.GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static); -#endif - } - - public static MethodInfo GetGetterMethodInfo(PropertyInfo propertyInfo) - { -#if SIMPLE_JSON_TYPEINFO - return propertyInfo.GetMethod; -#else - return propertyInfo.GetGetMethod(true); -#endif - } - - public static MethodInfo GetSetterMethodInfo(PropertyInfo propertyInfo) - { -#if SIMPLE_JSON_TYPEINFO - return propertyInfo.SetMethod; -#else - return propertyInfo.GetSetMethod(true); -#endif - } - - public static ConstructorDelegate GetContructor(ConstructorInfo constructorInfo) - { -#if SIMPLE_JSON_NO_LINQ_EXPRESSION - return GetConstructorByReflection(constructorInfo); -#else - return GetConstructorByExpression(constructorInfo); -#endif - } - - public static ConstructorDelegate GetContructor(Type type, params Type[] argsType) - { -#if SIMPLE_JSON_NO_LINQ_EXPRESSION - return GetConstructorByReflection(type, argsType); -#else - return GetConstructorByExpression(type, argsType); -#endif - } - - public static ConstructorDelegate GetConstructorByReflection(ConstructorInfo constructorInfo) - { - return delegate (object[] args) { return constructorInfo.Invoke(args); }; - } - - public static ConstructorDelegate GetConstructorByReflection(Type type, params Type[] argsType) - { - ConstructorInfo constructorInfo = GetConstructorInfo(type, argsType); - return constructorInfo == null ? null : GetConstructorByReflection(constructorInfo); - } - -#if !SIMPLE_JSON_NO_LINQ_EXPRESSION - - public static ConstructorDelegate GetConstructorByExpression(ConstructorInfo constructorInfo) - { - ParameterInfo[] paramsInfo = constructorInfo.GetParameters(); - ParameterExpression param = Expression.Parameter(typeof(object[]), "args"); - Expression[] argsExp = new Expression[paramsInfo.Length]; - for (int i = 0; i < paramsInfo.Length; i++) - { - Expression index = Expression.Constant(i); - Type paramType = paramsInfo[i].ParameterType; - Expression paramAccessorExp = Expression.ArrayIndex(param, index); - Expression paramCastExp = Expression.Convert(paramAccessorExp, paramType); - argsExp[i] = paramCastExp; - } - NewExpression newExp = Expression.New(constructorInfo, argsExp); - Expression> lambda = Expression.Lambda>(newExp, param); - Func compiledLambda = lambda.Compile(); - return delegate (object[] args) { return compiledLambda(args); }; - } - - public static ConstructorDelegate GetConstructorByExpression(Type type, params Type[] argsType) - { - ConstructorInfo constructorInfo = GetConstructorInfo(type, argsType); - return constructorInfo == null ? null : GetConstructorByExpression(constructorInfo); - } - -#endif - - public static GetDelegate GetGetMethod(PropertyInfo propertyInfo) - { -#if SIMPLE_JSON_NO_LINQ_EXPRESSION - return GetGetMethodByReflection(propertyInfo); -#else - return GetGetMethodByExpression(propertyInfo); -#endif - } - - public static GetDelegate GetGetMethod(FieldInfo fieldInfo) - { -#if SIMPLE_JSON_NO_LINQ_EXPRESSION - return GetGetMethodByReflection(fieldInfo); -#else - return GetGetMethodByExpression(fieldInfo); -#endif - } - - public static GetDelegate GetGetMethodByReflection(PropertyInfo propertyInfo) - { - MethodInfo methodInfo = GetGetterMethodInfo(propertyInfo); - return delegate (object source) { return methodInfo.Invoke(source, EmptyObjects); }; - } - - public static GetDelegate GetGetMethodByReflection(FieldInfo fieldInfo) - { - return delegate (object source) { return fieldInfo.GetValue(source); }; - } - -#if !SIMPLE_JSON_NO_LINQ_EXPRESSION - - public static GetDelegate GetGetMethodByExpression(PropertyInfo propertyInfo) - { - MethodInfo getMethodInfo = GetGetterMethodInfo(propertyInfo); - ParameterExpression instance = Expression.Parameter(typeof(object), "instance"); - UnaryExpression instanceCast = (!IsValueType(propertyInfo.DeclaringType)) ? Expression.TypeAs(instance, propertyInfo.DeclaringType) : Expression.Convert(instance, propertyInfo.DeclaringType); - Func compiled = Expression.Lambda>(Expression.TypeAs(Expression.Call(instanceCast, getMethodInfo), typeof(object)), instance).Compile(); - return delegate (object source) { return compiled(source); }; - } - - public static GetDelegate GetGetMethodByExpression(FieldInfo fieldInfo) - { - ParameterExpression instance = Expression.Parameter(typeof(object), "instance"); - MemberExpression member = Expression.Field(Expression.Convert(instance, fieldInfo.DeclaringType), fieldInfo); - GetDelegate compiled = Expression.Lambda(Expression.Convert(member, typeof(object)), instance).Compile(); - return delegate (object source) { return compiled(source); }; - } - -#endif - - public static SetDelegate GetSetMethod(PropertyInfo propertyInfo) - { -#if SIMPLE_JSON_NO_LINQ_EXPRESSION - return GetSetMethodByReflection(propertyInfo); -#else - return GetSetMethodByExpression(propertyInfo); -#endif - } - - public static SetDelegate GetSetMethod(FieldInfo fieldInfo) - { -#if SIMPLE_JSON_NO_LINQ_EXPRESSION - return GetSetMethodByReflection(fieldInfo); -#else - return GetSetMethodByExpression(fieldInfo); -#endif - } - - public static SetDelegate GetSetMethodByReflection(PropertyInfo propertyInfo) - { - MethodInfo methodInfo = GetSetterMethodInfo(propertyInfo); - return delegate (object source, object value) { methodInfo.Invoke(source, new object[] { value }); }; - } - - public static SetDelegate GetSetMethodByReflection(FieldInfo fieldInfo) - { - return delegate (object source, object value) { fieldInfo.SetValue(source, value); }; - } - -#if !SIMPLE_JSON_NO_LINQ_EXPRESSION - - public static SetDelegate GetSetMethodByExpression(PropertyInfo propertyInfo) - { - MethodInfo setMethodInfo = GetSetterMethodInfo(propertyInfo); - ParameterExpression instance = Expression.Parameter(typeof(object), "instance"); - ParameterExpression value = Expression.Parameter(typeof(object), "value"); - UnaryExpression instanceCast = (!IsValueType(propertyInfo.DeclaringType)) ? Expression.TypeAs(instance, propertyInfo.DeclaringType) : Expression.Convert(instance, propertyInfo.DeclaringType); - UnaryExpression valueCast = (!IsValueType(propertyInfo.PropertyType)) ? Expression.TypeAs(value, propertyInfo.PropertyType) : Expression.Convert(value, propertyInfo.PropertyType); - Action compiled = Expression.Lambda>(Expression.Call(instanceCast, setMethodInfo, valueCast), new ParameterExpression[] { instance, value }).Compile(); - return delegate (object source, object val) { compiled(source, val); }; - } - - public static SetDelegate GetSetMethodByExpression(FieldInfo fieldInfo) - { - ParameterExpression instance = Expression.Parameter(typeof(object), "instance"); - ParameterExpression value = Expression.Parameter(typeof(object), "value"); - Action compiled = Expression.Lambda>( - Assign(Expression.Field(Expression.Convert(instance, fieldInfo.DeclaringType), fieldInfo), Expression.Convert(value, fieldInfo.FieldType)), instance, value).Compile(); - return delegate (object source, object val) { compiled(source, val); }; - } - - public static BinaryExpression Assign(Expression left, Expression right) - { -#if SIMPLE_JSON_TYPEINFO - return Expression.Assign(left, right); -#else - MethodInfo assign = typeof(Assigner<>).MakeGenericType(left.Type).GetMethod("Assign"); - BinaryExpression assignExpr = Expression.Add(left, right, assign); - return assignExpr; -#endif - } - - private static class Assigner - { - public static T Assign(ref T left, T right) - { - return (left = right); - } - } - -#endif - - public sealed class ThreadSafeDictionary : IDictionary - { - private readonly object _lock = new object(); - private readonly ThreadSafeDictionaryValueFactory _valueFactory; - private Dictionary _dictionary; - - public ThreadSafeDictionary(ThreadSafeDictionaryValueFactory valueFactory) - { - _valueFactory = valueFactory; - } - - private TValue Get(TKey key) - { - if (_dictionary == null) - return AddValue(key); - TValue value; - if (!_dictionary.TryGetValue(key, out value)) - return AddValue(key); - return value; - } - - private TValue AddValue(TKey key) - { - TValue value = _valueFactory(key); - lock (_lock) - { - if (_dictionary == null) - { - _dictionary = new Dictionary(); - _dictionary[key] = value; - } - else - { - TValue val; - if (_dictionary.TryGetValue(key, out val)) - return val; - Dictionary dict = new Dictionary(_dictionary); - dict[key] = value; - _dictionary = dict; - } - } - return value; - } - - public void Add(TKey key, TValue value) - { - throw new NotImplementedException(); - } - - public bool ContainsKey(TKey key) - { - return _dictionary.ContainsKey(key); - } - - public ICollection Keys - { - get { return _dictionary.Keys; } - } - - public bool Remove(TKey key) - { - throw new NotImplementedException(); - } - - public bool TryGetValue(TKey key, out TValue value) - { - value = this[key]; - return true; - } - - public ICollection Values - { - get { return _dictionary.Values; } - } - - public TValue this[TKey key] - { - get { return Get(key); } - set { throw new NotImplementedException(); } - } - - public void Add(KeyValuePair item) - { - throw new NotImplementedException(); - } - - public void Clear() - { - throw new NotImplementedException(); - } - - public bool Contains(KeyValuePair item) - { - throw new NotImplementedException(); - } - - public void CopyTo(KeyValuePair[] array, int arrayIndex) - { - throw new NotImplementedException(); - } - - public int Count - { - get { return _dictionary.Count; } - } - - public bool IsReadOnly - { - get { throw new NotImplementedException(); } - } - - public bool Remove(KeyValuePair item) - { - throw new NotImplementedException(); - } - - public IEnumerator> GetEnumerator() - { - return _dictionary.GetEnumerator(); - } - - System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() - { - return _dictionary.GetEnumerator(); - } - } - - } - } -} -// ReSharper restore LoopCanBeConvertedToQuery -// ReSharper restore RedundantExplicitArrayCreation -// ReSharper restore SuggestUseVarKeywordEvident \ No newline at end of file diff --git a/src/TablesAndTuples/TablesAndTuples.csproj b/src/TablesAndTuples/TablesAndTuples.csproj deleted file mode 100644 index ce1697ae..00000000 --- a/src/TablesAndTuples/TablesAndTuples.csproj +++ /dev/null @@ -1,8 +0,0 @@ - - - - Exe - netcoreapp2.0 - - - diff --git a/src/TablesAndTuples/WixColumnDefinition.cs b/src/TablesAndTuples/WixColumnDefinition.cs deleted file mode 100644 index 2d60c9dc..00000000 --- a/src/TablesAndTuples/WixColumnDefinition.cs +++ /dev/null @@ -1,296 +0,0 @@ -using System; -using System.Xml; - -namespace TablesAndSymbols -{ - class WixColumnDefinition - { - 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) - { - this.Name = name; - this.Type = type; - this.Length = length; - this.PrimaryKey = primaryKey; - this.Nullable = nullable; - this.ModularizeType = modularizeType; - this.ForceLocalizable = forceLocalizable; - this.MinValue = minValue; - this.MaxValue = maxValue; - this.KeyTable = keyTable; - this.KeyColumn = keyColumn; - this.Category = category; - this.Possibilities = possibilities; - this.Description = description; - this.UseCData = useCData; - this.Unreal = unreal; - } - - public string Name { get; } - public ColumnType Type { get; } - public int Length { get; } - public bool PrimaryKey { get; } - public bool Nullable { get; } - public ColumnModularizeType? ModularizeType { get; } - public bool ForceLocalizable { get; } - public long? MinValue { get; } - public long? MaxValue { get; } - public string KeyTable { get; } - public int? KeyColumn { get; } - public ColumnCategory Category { get; } - public string Possibilities { get; } - public string Description { get; } - public bool UseCData { get; } - public bool Unreal { get; } - - internal static WixColumnDefinition Read(XmlReader reader) - { - if (!reader.LocalName.Equals("columnDefinition")) - { - throw new XmlException(); - } - - ColumnCategory category = ColumnCategory.Unknown; - string description = null; - bool empty = reader.IsEmptyElement; - int? keyColumn = null; - string keyTable = null; - int length = -1; - bool localizable = false; - long? maxValue = null; - long? minValue = null; - var modularize = ColumnModularizeType.None; - string name = null; - bool nullable = false; - string possibilities = null; - bool primaryKey = false; - var type = ColumnType.Unknown; - bool useCData = false; - bool unreal = false; - - // parse the attributes - while (reader.MoveToNextAttribute()) - { - switch (reader.LocalName) - { - case "category": - switch (reader.Value) - { - case "anyPath": - category = ColumnCategory.AnyPath; - break; - case "binary": - category = ColumnCategory.Binary; - break; - case "cabinet": - category = ColumnCategory.Cabinet; - break; - case "condition": - category = ColumnCategory.Condition; - break; - case "customSource": - category = ColumnCategory.CustomSource; - break; - case "defaultDir": - category = ColumnCategory.DefaultDir; - break; - case "doubleInteger": - category = ColumnCategory.DoubleInteger; - break; - case "filename": - category = ColumnCategory.Filename; - break; - case "formatted": - category = ColumnCategory.Formatted; - break; - case "formattedSddl": - category = ColumnCategory.FormattedSDDLText; - break; - case "guid": - category = ColumnCategory.Guid; - break; - case "identifier": - category = ColumnCategory.Identifier; - break; - case "integer": - category = ColumnCategory.Integer; - break; - case "language": - category = ColumnCategory.Language; - break; - case "lowerCase": - category = ColumnCategory.LowerCase; - break; - case "path": - category = ColumnCategory.Path; - break; - case "paths": - category = ColumnCategory.Paths; - break; - case "property": - category = ColumnCategory.Property; - break; - case "regPath": - category = ColumnCategory.RegPath; - break; - case "shortcut": - category = ColumnCategory.Shortcut; - break; - case "template": - category = ColumnCategory.Template; - break; - case "text": - category = ColumnCategory.Text; - break; - case "timeDate": - category = ColumnCategory.TimeDate; - break; - case "upperCase": - category = ColumnCategory.UpperCase; - break; - case "version": - category = ColumnCategory.Version; - break; - case "wildCardFilename": - category = ColumnCategory.WildCardFilename; - break; - default: - throw new InvalidOperationException(); - } - break; - case "description": - description = reader.Value; - break; - case "keyColumn": - keyColumn = Convert.ToInt32(reader.Value, 10); - break; - case "keyTable": - keyTable = reader.Value; - break; - case "length": - length = Convert.ToInt32(reader.Value, 10); - break; - case "localizable": - localizable = reader.Value.Equals("yes"); - break; - case "maxValue": - maxValue = Convert.ToInt32(reader.Value, 10); - break; - case "minValue": - minValue = Convert.ToInt32(reader.Value, 10); - break; - case "modularize": - switch (reader.Value) - { - case "column": - modularize = ColumnModularizeType.Column; - break; - case "companionFile": - modularize = ColumnModularizeType.CompanionFile; - break; - case "condition": - modularize = ColumnModularizeType.Condition; - break; - case "controlEventArgument": - modularize = ColumnModularizeType.ControlEventArgument; - break; - case "controlText": - modularize = ColumnModularizeType.ControlText; - break; - case "icon": - modularize = ColumnModularizeType.Icon; - break; - case "none": - modularize = ColumnModularizeType.None; - break; - case "property": - modularize = ColumnModularizeType.Property; - break; - case "semicolonDelimited": - modularize = ColumnModularizeType.SemicolonDelimited; - break; - default: - throw new XmlException(); - } - break; - case "name": - switch (reader.Value) - { - case "CREATE": - case "DELETE": - case "DROP": - case "INSERT": - throw new XmlException(); - default: - name = reader.Value; - break; - } - break; - case "nullable": - nullable = reader.Value.Equals("yes"); - break; - case "primaryKey": - primaryKey = reader.Value.Equals("yes"); - break; - case "set": - possibilities = reader.Value; - break; - case "type": - switch (reader.Value) - { - case "localized": - type = ColumnType.Localized; - break; - case "number": - type = ColumnType.Number; - break; - case "object": - type = ColumnType.Object; - break; - case "string": - type = ColumnType.String; - break; - case "preserved": - type = ColumnType.Preserved; - break; - default: - throw new XmlException(); - } - break; - case "useCData": - useCData = reader.Value.Equals("yes"); - break; - case "unreal": - unreal = reader.Value.Equals("yes"); - break; - } - } - - // parse the child elements (there should be none) - if (!empty) - { - bool done = false; - - while (!done && reader.Read()) - { - switch (reader.NodeType) - { - case XmlNodeType.Element: - throw new XmlException(); - case XmlNodeType.EndElement: - done = true; - break; - } - } - - if (!done) - { - throw new XmlException(); - } - } - - WixColumnDefinition columnDefinition = new WixColumnDefinition(name, type, length, primaryKey, nullable, category, minValue, maxValue, keyTable, keyColumn, possibilities, description, modularize, localizable, useCData, unreal); - - return columnDefinition; - } - } -} diff --git a/src/TablesAndTuples/WixTableDefinition.cs b/src/TablesAndTuples/WixTableDefinition.cs deleted file mode 100644 index 61dcbb0a..00000000 --- a/src/TablesAndTuples/WixTableDefinition.cs +++ /dev/null @@ -1,169 +0,0 @@ -using System.Collections.Generic; -using System.Linq; -using System.Xml; - -namespace TablesAndSymbols -{ - class WixTableDefinition - { - public WixTableDefinition(string name, IEnumerable columns, bool unreal, bool symbolless, string symbolDefinitionName, bool? symbolIdIsPrimaryKey) - { - this.Name = name; - this.VariableName = name.Replace("_", ""); - this.Unreal = unreal; - this.Columns = columns?.ToArray(); - this.Symbolless = symbolless; - this.SymbolDefinitionName = symbolless ? null : symbolDefinitionName ?? this.VariableName; - this.SymbolIdIsPrimaryKey = symbolIdIsPrimaryKey ?? DeriveSymbolIdIsPrimaryKey(this.Columns); - } - - public string Name { get; } - - public string VariableName { get; } - - public string SymbolDefinitionName { get; } - - public bool Unreal { get; } - - public WixColumnDefinition[] Columns { get; } - - public bool SymbolIdIsPrimaryKey { get; } - - public bool Symbolless { get; } - - static WixTableDefinition Read(XmlReader reader) - { - var empty = reader.IsEmptyElement; - string name = null; - string symbolDefinitionName = null; - var unreal = false; - bool? symbolIdIsPrimaryKey = null; - var symbolless = false; - - while (reader.MoveToNextAttribute()) - { - switch (reader.LocalName) - { - case "name": - name = reader.Value; - break; - case "symbolDefinitionName": - symbolDefinitionName = reader.Value; - break; - case "symbolIdIsPrimaryKey": - symbolIdIsPrimaryKey = reader.Value.Equals("yes"); - break; - case "symbolless": - symbolless = reader.Value.Equals("yes"); - break; - case "unreal": - unreal = reader.Value.Equals("yes"); - break; - } - } - - if (null == name) - { - throw new XmlException(); - } - - var columns = new List(); - - // parse the child elements - if (!empty) - { - var done = false; - - while (!done && reader.Read()) - { - switch (reader.NodeType) - { - case XmlNodeType.Element: - switch (reader.LocalName) - { - case "columnDefinition": - var columnDefinition = WixColumnDefinition.Read(reader); - columns.Add(columnDefinition); - break; - default: - throw new XmlException(); - } - break; - case XmlNodeType.EndElement: - done = true; - break; - } - } - - if (!done) - { - throw new XmlException(); - } - } - - return new WixTableDefinition(name, columns.ToArray(), unreal, symbolless, symbolDefinitionName, symbolIdIsPrimaryKey); - } - - static bool DeriveSymbolIdIsPrimaryKey(WixColumnDefinition[] columns) - { - return columns[0].PrimaryKey && - columns[0].Type == ColumnType.String && - columns[0].Category == ColumnCategory.Identifier && - !columns[0].Name.EndsWith("_") && - (columns.Length == 1 || !columns.Skip(1).Any(t => t.PrimaryKey)); - } - - public static List LoadCollection(string inputPath) - { - using (var reader = XmlReader.Create(inputPath)) - { - reader.MoveToContent(); - - if ("tableDefinitions" != reader.LocalName) - { - throw new XmlException(); - } - - var empty = reader.IsEmptyElement; - var tableDefinitions = new List(); - - while (reader.MoveToNextAttribute()) - { - } - - // parse the child elements - if (!empty) - { - var done = false; - - while (!done && reader.Read()) - { - switch (reader.NodeType) - { - case XmlNodeType.Element: - switch (reader.LocalName) - { - case "tableDefinition": - tableDefinitions.Add(WixTableDefinition.Read(reader)); - break; - default: - throw new XmlException(); - } - break; - case XmlNodeType.EndElement: - done = true; - break; - } - } - - if (!done) - { - throw new XmlException(); - } - } - - return tableDefinitions; - } - } - } -} diff --git a/src/WixBuildTools.MsgGen/AssemblyInfo.cs b/src/WixBuildTools.MsgGen/AssemblyInfo.cs deleted file mode 100644 index 378adbf0..00000000 --- a/src/WixBuildTools.MsgGen/AssemblyInfo.cs +++ /dev/null @@ -1,8 +0,0 @@ -// 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. - -using System.Reflection; - -// General Information about an assembly is controlled through the following -// set of attributes. Change these attribute values to modify the information -// associated with an assembly. -[assembly: AssemblyCulture("")] diff --git a/src/WixBuildTools.MsgGen/GenerateMessageFiles.cs b/src/WixBuildTools.MsgGen/GenerateMessageFiles.cs deleted file mode 100644 index 6f51dbf9..00000000 --- a/src/WixBuildTools.MsgGen/GenerateMessageFiles.cs +++ /dev/null @@ -1,250 +0,0 @@ -// 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. - -namespace WixBuildTools.MsgGen -{ - using System; - using System.CodeDom; - using System.Collections; - using System.Globalization; - using System.Reflection; - using System.Resources; - using System.Xml; - - /// - /// Message files generation class. - /// - public class GenerateMessageFiles - { - /// - /// Generate the message files. - /// - /// Input Xml document containing message definitions. - /// CodeDom container. - /// Writer for default resource file. - public static void Generate(XmlDocument messagesDoc, CodeCompileUnit codeCompileUnit, ResourceWriter resourceWriter) - { - Hashtable usedNumbers = new Hashtable(); - - if (null == messagesDoc) - { - throw new ArgumentNullException("messagesDoc"); - } - - if (null == codeCompileUnit) - { - throw new ArgumentNullException("codeCompileUnit"); - } - - if (null == resourceWriter) - { - throw new ArgumentNullException("resourceWriter"); - } - - string namespaceAttr = messagesDoc.DocumentElement.GetAttribute("Namespace"); - string resourcesAttr = messagesDoc.DocumentElement.GetAttribute("Resources"); - - // namespace - CodeNamespace messagesNamespace = new CodeNamespace(namespaceAttr); - codeCompileUnit.Namespaces.Add(messagesNamespace); - - // imports - messagesNamespace.Imports.Add(new CodeNamespaceImport("System")); - messagesNamespace.Imports.Add(new CodeNamespaceImport("System.Reflection")); - messagesNamespace.Imports.Add(new CodeNamespaceImport("System.Resources")); - if (namespaceAttr != "WixToolset.Data") - { - messagesNamespace.Imports.Add(new CodeNamespaceImport("WixToolset.Data")); - } - - foreach (XmlElement classElement in messagesDoc.DocumentElement.ChildNodes) - { - string className = classElement.GetAttribute("Name"); - string baseContainerName = classElement.GetAttribute("BaseContainerName"); - string containerName = classElement.GetAttribute("ContainerName"); - string messageLevel = classElement.GetAttribute("Level"); - - // message container class - messagesNamespace.Types.Add(CreateContainer(namespaceAttr, baseContainerName, containerName, messageLevel, resourcesAttr)); - - // class - CodeTypeDeclaration messagesClass = new CodeTypeDeclaration(className); - messagesClass.TypeAttributes = TypeAttributes.Class | TypeAttributes.Public | TypeAttributes.Sealed; - messagesNamespace.Types.Add(messagesClass); - - // private constructor (needed since all methods in this class are static) - CodeConstructor constructor = new CodeConstructor(); - constructor.Attributes = MemberAttributes.Private; - constructor.ReturnType = null; - messagesClass.Members.Add(constructor); - - // messages - foreach (XmlElement messageElement in classElement.ChildNodes) - { - int number; - string id = messageElement.GetAttribute("Id"); - string numberString = messageElement.GetAttribute("Number"); - bool sourceLineNumbers = true; - - // determine the message number (and ensure it was set properly) - if (0 < numberString.Length) - { - number = Convert.ToInt32(numberString, CultureInfo.InvariantCulture); - } - else - { - throw new ApplicationException(String.Format("Message number must be assigned for {0} '{1}'.", containerName, id)); - } - - // check for message number collisions - if (usedNumbers.Contains(number)) - { - throw new ApplicationException(String.Format("Collision detected between two or more messages with number '{0}'.", number)); - } - - usedNumbers.Add(number, null); - - if ("no" == messageElement.GetAttribute("SourceLineNumbers")) - { - sourceLineNumbers = false; - } - - int instanceCount = 0; - foreach (XmlElement instanceElement in messageElement.ChildNodes) - { - string formatString = instanceElement.InnerText.Trim(); - string resourceName = String.Concat(className, "_", id, "_", (++instanceCount).ToString()); - - // create a resource - resourceWriter.AddResource(resourceName, formatString); - - // create method - CodeMemberMethod method = new CodeMemberMethod(); - method.ReturnType = new CodeTypeReference(baseContainerName); - method.Attributes = MemberAttributes.Public | MemberAttributes.Static; - messagesClass.Members.Add(method); - - // method name - method.Name = id; - - // return statement - CodeMethodReturnStatement stmt = new CodeMethodReturnStatement(); - method.Statements.Add(stmt); - - // return statement expression - CodeObjectCreateExpression expr = new CodeObjectCreateExpression(); - stmt.Expression = expr; - - // new struct - expr.CreateType = new CodeTypeReference(containerName); - - // optionally have sourceLineNumbers as the first parameter - if (sourceLineNumbers) - { - // sourceLineNumbers parameter - expr.Parameters.Add(new CodeArgumentReferenceExpression("sourceLineNumbers")); - } - else - { - expr.Parameters.Add(new CodePrimitiveExpression(null)); - } - - // message number parameter - expr.Parameters.Add(new CodePrimitiveExpression(number)); - - // resource name parameter - expr.Parameters.Add(new CodePrimitiveExpression(resourceName)); - - // optionally have sourceLineNumbers as the first parameter - if (sourceLineNumbers) - { - method.Parameters.Add(new CodeParameterDeclarationExpression("SourceLineNumber", "sourceLineNumbers")); - } - - foreach (XmlNode parameterNode in instanceElement.ChildNodes) - { - XmlElement parameterElement; - - if (null != (parameterElement = parameterNode as XmlElement)) - { - string type = parameterElement.GetAttribute("Type"); - string name = parameterElement.GetAttribute("Name"); - - // method parameter - method.Parameters.Add(new CodeParameterDeclarationExpression(type, name)); - - // String.Format parameter - expr.Parameters.Add(new CodeArgumentReferenceExpression(name)); - } - } - } - } - } - } - - /// - /// Create message container class. - /// - /// Namespace to use for resources stream. - /// Name of the base message container class. - /// Name of the message container class. - /// Message level of for the message. - /// Name of the resources stream (will get namespace prepended). - /// Message container class CodeDom object. - private static CodeTypeDeclaration CreateContainer(string namespaceName, string baseContainerName, string containerName, string messageLevel, string resourcesName) - { - CodeTypeDeclaration messageContainer = new CodeTypeDeclaration(); - - messageContainer.Name = containerName; - messageContainer.BaseTypes.Add(new CodeTypeReference(baseContainerName)); - - // constructor - CodeConstructor constructor = new CodeConstructor(); - constructor.Attributes = MemberAttributes.Public; - constructor.ReturnType = null; - messageContainer.Members.Add(constructor); - - CodeMemberField resourceManager = new CodeMemberField(); - resourceManager.Attributes = MemberAttributes.Private | MemberAttributes.Static; - resourceManager.Name = "resourceManager"; - resourceManager.Type = new CodeTypeReference("ResourceManager"); - resourceManager.InitExpression = new CodeObjectCreateExpression("ResourceManager", new CodeSnippetExpression(String.Format("\"{0}.{1}\"", namespaceName, resourcesName)), new CodeSnippetExpression("Assembly.GetExecutingAssembly()")); - messageContainer.Members.Add(resourceManager); - - // constructor parameters - constructor.Parameters.Add(new CodeParameterDeclarationExpression("SourceLineNumber", "sourceLineNumbers")); - constructor.Parameters.Add(new CodeParameterDeclarationExpression(typeof(int), "id")); - constructor.Parameters.Add(new CodeParameterDeclarationExpression(typeof(string), "resourceName")); - CodeParameterDeclarationExpression messageArgsParam = new CodeParameterDeclarationExpression("params object[]", "messageArgs"); - constructor.Parameters.Add(messageArgsParam); - - constructor.BaseConstructorArgs.Add(new CodeArgumentReferenceExpression("sourceLineNumbers")); - constructor.BaseConstructorArgs.Add(new CodeArgumentReferenceExpression("id")); - constructor.BaseConstructorArgs.Add(new CodeArgumentReferenceExpression("resourceName")); - constructor.BaseConstructorArgs.Add(new CodeArgumentReferenceExpression("messageArgs")); - - // assign base.Level if messageLevel is specified - if (!String.IsNullOrEmpty(messageLevel)) - { - CodePropertyReferenceExpression levelReference = new CodePropertyReferenceExpression(new CodeBaseReferenceExpression(), "Level"); - CodeFieldReferenceExpression messageLevelField = new CodeFieldReferenceExpression(new CodeTypeReferenceExpression("MessageLevel"), messageLevel); - constructor.Statements.Add(new CodeAssignStatement(levelReference, messageLevelField)); - } - - // Assign base.ResourceManager property - CodePropertyReferenceExpression baseResourceManagerReference = new CodePropertyReferenceExpression(new CodeBaseReferenceExpression(), "ResourceManager"); - CodeFieldReferenceExpression resourceManagerField = new CodeFieldReferenceExpression(null, "resourceManager"); - constructor.Statements.Add(new CodeAssignStatement(baseResourceManagerReference, resourceManagerField)); - - //CodeMemberProperty resourceManagerProperty = new CodeMemberProperty(); - //resourceManagerProperty.Attributes = MemberAttributes.Public | MemberAttributes.Override; - //resourceManagerProperty.Name = "ResourceManager"; - //resourceManagerProperty.Type = new CodeTypeReference("ResourceManager"); - //CodeFieldReferenceExpression resourceManagerReference = new CodeFieldReferenceExpression(); - //resourceManagerReference.FieldName = "resourceManager"; - //resourceManagerProperty.GetStatements.Add(new CodeMethodReturnStatement(resourceManagerReference)); - //messageContainer.Members.Add(resourceManagerProperty); - - return messageContainer; - } - } -} diff --git a/src/WixBuildTools.MsgGen/MsgGen.cs b/src/WixBuildTools.MsgGen/MsgGen.cs deleted file mode 100644 index ff4a4a90..00000000 --- a/src/WixBuildTools.MsgGen/MsgGen.cs +++ /dev/null @@ -1,261 +0,0 @@ -// 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. - -namespace WixBuildTools.MsgGen -{ - using Microsoft.CSharp; - using System; - using System.CodeDom; - using System.CodeDom.Compiler; - using System.Collections; - using System.IO; - using System.Reflection; - using System.Resources; - using System.Runtime.InteropServices; - using System.Xml; - using System.Xml.Schema; - - /// - /// The main entry point for MsgGen. - /// - public class MsgGen - { - /// - /// The main entry point for MsgGen. - /// - /// Commandline arguments for the application. - /// Returns the application error code. - [STAThread] - public static int Main(string[] args) - { - try - { - MsgGenMain msgGen = new MsgGenMain(args); - } - catch (Exception e) - { - Console.WriteLine("MsgGen.exe : fatal error MSGG0000: {0}\r\n\r\nStack Trace:\r\n{1}", e.Message, e.StackTrace); - if (e is NullReferenceException || e is SEHException) - { - throw; - } - return 2; - } - - return 0; - } - - /// - /// Main class for MsgGen. - /// - private class MsgGenMain - { - private bool showLogo; - private bool showHelp; - - private string sourceFile; - private string destClassFile; - private string destResourcesFile; - - /// - /// Main method for the MsgGen application within the MsgGenMain class. - /// - /// Commandline arguments to the application. - public MsgGenMain(string[] args) - { - this.showLogo = true; - this.showHelp = false; - - this.sourceFile = null; - this.destClassFile = null; - this.destResourcesFile = null; - - // parse the command line - this.ParseCommandLine(args); - - if (null == this.sourceFile || null == this.destClassFile) - { - this.showHelp = true; - } - if (null == this.destResourcesFile) - { - this.destResourcesFile = Path.ChangeExtension(this.destClassFile, ".resources"); - } - - // get the assemblies - Assembly msgGenAssembly = Assembly.GetExecutingAssembly(); - - if (this.showLogo) - { - Console.WriteLine("Microsoft (R) Message Generation Tool version {0}", msgGenAssembly.GetName().Version.ToString()); - Console.WriteLine("Copyright (C) Microsoft Corporation 2004. All rights reserved."); - Console.WriteLine(); - } - if (this.showHelp) - { - Console.WriteLine(" usage: MsgGen.exe [-?] [-nologo] sourceFile destClassFile [destResourcesFile]"); - Console.WriteLine(); - Console.WriteLine(" -? this help information"); - Console.WriteLine(); - Console.WriteLine("For more information see: http://wix.sourceforge.net"); - return; // exit - } - - // load the schema - XmlReader reader = null; - XmlSchemaCollection schemaCollection = null; - try - { - reader = new XmlTextReader(msgGenAssembly.GetManifestResourceStream("WixBuildTools.MsgGen.Xsd.messages.xsd")); - schemaCollection = new XmlSchemaCollection(); - schemaCollection.Add("http://schemas.microsoft.com/genmsgs/2004/07/messages", reader); - } - finally - { - reader.Close(); - } - - // load the source file and process it - using (StreamReader sr = new StreamReader(this.sourceFile)) - { - XmlParserContext context = new XmlParserContext(null, null, null, XmlSpace.None); - XmlValidatingReader validatingReader = new XmlValidatingReader(sr.BaseStream, XmlNodeType.Document, context); - validatingReader.Schemas.Add(schemaCollection); - - XmlDocument errorsDoc = new XmlDocument(); - errorsDoc.Load(validatingReader); - - CodeCompileUnit codeCompileUnit = new CodeCompileUnit(); - - using (ResourceWriter resourceWriter = new ResourceWriter(this.destResourcesFile)) - { - GenerateMessageFiles.Generate(errorsDoc, codeCompileUnit, resourceWriter); - - GenerateCSharpCode(codeCompileUnit, this.destClassFile); - } - } - } - - /// - /// Generate the actual C# code. - /// - /// The code DOM. - /// Destination C# source file. - public static void GenerateCSharpCode(CodeCompileUnit codeCompileUnit, string destClassFile) - { - // generate the code with the C# code provider - CSharpCodeProvider provider = new CSharpCodeProvider(); - - // obtain an ICodeGenerator from the CodeDomProvider class - ICodeGenerator gen = provider.CreateGenerator(); - - // create a TextWriter to a StreamWriter to the output file - using (StreamWriter sw = new StreamWriter(destClassFile)) - { - using (IndentedTextWriter tw = new IndentedTextWriter(sw, " ")) - { - CodeGeneratorOptions options = new CodeGeneratorOptions(); - - // code generation options - options.BlankLinesBetweenMembers = true; - options.BracingStyle = "C"; - - // generate source code using the code generator - gen.GenerateCodeFromCompileUnit(codeCompileUnit, tw, options); - } - } - } - - /// - /// Parse the commandline arguments. - /// - /// Commandline arguments. - private void ParseCommandLine(string[] args) - { - for (int i = 0; i < args.Length; ++i) - { - string arg = args[i]; - if (null == arg || "" == arg) // skip blank arguments - { - continue; - } - - if ('-' == arg[0] || '/' == arg[0]) - { - string parameter = arg.Substring(1); - if ("nologo" == parameter) - { - this.showLogo = false; - } - else if ("?" == parameter || "help" == parameter) - { - this.showHelp = true; - } - } - else if ('@' == arg[0]) - { - using (StreamReader reader = new StreamReader(arg.Substring(1))) - { - string line; - ArrayList newArgs = new ArrayList(); - - while (null != (line = reader.ReadLine())) - { - string newArg = ""; - bool betweenQuotes = false; - for (int j = 0; j < line.Length; ++j) - { - // skip whitespace - if (!betweenQuotes && (' ' == line[j] || '\t' == line[j])) - { - if ("" != newArg) - { - newArgs.Add(newArg); - newArg = null; - } - - continue; - } - - // if we're escaping a quote - if ('\\' == line[j] && '"' == line[j]) - { - ++j; - } - else if ('"' == line[j]) // if we've hit a new quote - { - betweenQuotes = !betweenQuotes; - continue; - } - - newArg = String.Concat(newArg, line[j]); - } - if ("" != newArg) - { - newArgs.Add(newArg); - } - } - string[] ar = (string[])newArgs.ToArray(typeof(string)); - this.ParseCommandLine(ar); - } - } - else if (null == this.sourceFile) - { - this.sourceFile = arg; - } - else if (null == this.destClassFile) - { - this.destClassFile = arg; - } - else if (null == this.destResourcesFile) - { - this.destResourcesFile = arg; - } - else - { - throw new ArgumentException(String.Format("Unknown argument '{0}'.", arg)); - } - } - } - } - } -} diff --git a/src/WixBuildTools.MsgGen/WixBuildTools.MsgGen.csproj b/src/WixBuildTools.MsgGen/WixBuildTools.MsgGen.csproj deleted file mode 100644 index 954ffa59..00000000 --- a/src/WixBuildTools.MsgGen/WixBuildTools.MsgGen.csproj +++ /dev/null @@ -1,27 +0,0 @@ - - - - Exe - net461 - true - true - embedded - true - CS0618 - - - - - - - - - - - - - - - - - diff --git a/src/WixBuildTools.MsgGen/Xsd/messages.xsd b/src/WixBuildTools.MsgGen/Xsd/messages.xsd deleted file mode 100644 index fd086502..00000000 --- a/src/WixBuildTools.MsgGen/Xsd/messages.xsd +++ /dev/null @@ -1,101 +0,0 @@ - - - - - - - - Schema for describing any kind of messages. - - - - - - - - - - Namespace of the generated class. - - - Resources stream for messages. Will get namespace prepended to it. - - - - - - - - - - - Name of the generated class. - - - Name of the generated container class. - - - Name of the base container class. - - - Optional message level for this container class and derivative classes. - - - - - - - - - - - Name of the message type. - - - Override the number for this message type. - - - Associate SourceLineNumbers with this message. The default value is "yes". - - - - - - - - - - - - - - - - Type of the parameter. - - - Name of the parameter. - - - - - - Values of this type will either be "yes" or "no". - - - - - - - - The message level for this message which corresponds to the WixToolset.MessageLevel enumeration. - - - - - - - - diff --git a/src/WixBuildTools.MsgGen/build/WixBuildTools.MsgGen.targets b/src/WixBuildTools.MsgGen/build/WixBuildTools.MsgGen.targets deleted file mode 100644 index dfa7bcbe..00000000 --- a/src/WixBuildTools.MsgGen/build/WixBuildTools.MsgGen.targets +++ /dev/null @@ -1,102 +0,0 @@ - - - - - - $(MSBuildAllProjects);$(MSBuildThisFileFullPath) - - - - - - - - - $(MSBuildThisFileDirectory)..\tools\ - - - - - - PrepareMsgGen - - - MsgGen; - $(PrepareResourcesDependsOn) - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/WixBuildTools.MsgGen/buildCrossTargeting/WixBuildTools.MsgGen.targets b/src/WixBuildTools.MsgGen/buildCrossTargeting/WixBuildTools.MsgGen.targets deleted file mode 100644 index a3985af5..00000000 --- a/src/WixBuildTools.MsgGen/buildCrossTargeting/WixBuildTools.MsgGen.targets +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - diff --git a/src/WixBuildTools.TestSupport.Native/AssemblyInfo.cpp b/src/WixBuildTools.TestSupport.Native/AssemblyInfo.cpp deleted file mode 100644 index 23a48993..00000000 --- a/src/WixBuildTools.TestSupport.Native/AssemblyInfo.cpp +++ /dev/null @@ -1,17 +0,0 @@ -// 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. - -#include "precomp.h" - -using namespace System::Reflection; -using namespace System::Runtime::CompilerServices; -using namespace System::Runtime::InteropServices; - -// -// General Information about an assembly is controlled through the following -// set of attributes. Change these attribute values to modify the information -// associated with an assembly. -// -[assembly: AssemblyTitleAttribute("WixBuildTools.TestSupport.Native")]; -[assembly: AssemblyDescriptionAttribute("")]; -[assembly: AssemblyCultureAttribute("")]; -[assembly: ComVisible(false)]; diff --git a/src/WixBuildTools.TestSupport.Native/NativeAssert.h b/src/WixBuildTools.TestSupport.Native/NativeAssert.h deleted file mode 100644 index 34af4f34..00000000 --- a/src/WixBuildTools.TestSupport.Native/NativeAssert.h +++ /dev/null @@ -1,85 +0,0 @@ -#pragma once -// 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. - - -namespace WixBuildTools { -namespace TestSupport { - - using namespace System; - using namespace System::Collections::Generic; - using namespace System::Linq; - using namespace Xunit; - - public ref class NativeAssert : WixAssert - { - public: - static void NotNull(LPCWSTR wz) - { - if (!wz) - { - Assert::NotNull(nullptr); - } - } - - // For some reason, naming these NotStringEqual methods "NotEqual" breaks Intellisense in files that call any overload of the NotEqual method. - static void NotStringEqual(LPCWSTR expected, LPCWSTR actual) - { - NativeAssert::NotStringEqual(expected, actual, FALSE); - } - - static void NotStringEqual(LPCWSTR expected, LPCWSTR actual, BOOL ignoreCase) - { - IEqualityComparer^ comparer = ignoreCase ? StringComparer::InvariantCultureIgnoreCase : StringComparer::InvariantCulture; - Assert::NotEqual(NativeAssert::LPWSTRToString(expected), NativeAssert::LPWSTRToString(actual), comparer); - } - - // For some reason, naming these StringEqual methods "Equal" breaks Intellisense in files that call any overload of the Equal method. - static void StringEqual(LPCWSTR expected, LPCWSTR actual) - { - NativeAssert::StringEqual(expected, actual, FALSE); - } - - static void StringEqual(LPCWSTR expected, LPCWSTR actual, BOOL ignoreCase) - { - IEqualityComparer^ comparer = ignoreCase ? StringComparer::InvariantCultureIgnoreCase : StringComparer::InvariantCulture; - Assert::Equal(NativeAssert::LPWSTRToString(expected), NativeAssert::LPWSTRToString(actual), comparer); - } - - static void Succeeded(HRESULT hr, LPCSTR zFormat, LPCSTR zArg, ... array^ zArgs) - { - array^ formatArgs = gcnew array(zArgs->Length + 1); - formatArgs[0] = NativeAssert::LPSTRToString(zArg); - for (int i = 0; i < zArgs->Length; ++i) - { - formatArgs[i + 1] = NativeAssert::LPSTRToString(zArgs[i]); - } - WixAssert::Succeeded(hr, gcnew String(zFormat), formatArgs); - } - - static void Succeeded(HRESULT hr, LPCSTR zFormat, ... array^ wzArgs) - { - array^ formatArgs = gcnew array(wzArgs->Length); - for (int i = 0; i < wzArgs->Length; ++i) - { - formatArgs[i] = NativeAssert::LPWSTRToString(wzArgs[i]); - } - WixAssert::Succeeded(hr, gcnew String(zFormat), formatArgs); - } - - static void ValidReturnCode(HRESULT hr, ... array^ validReturnCodes) - { - Assert::Contains(hr, (IEnumerable^)validReturnCodes); - } - - private: - static String^ LPSTRToString(LPCSTR z) - { - return z ? gcnew String(z) : nullptr; - } - static String^ LPWSTRToString(LPCWSTR wz) - { - return wz ? gcnew String(wz) : nullptr; - } - }; -} -} diff --git a/src/WixBuildTools.TestSupport.Native/WixBuildTools.TestSupport.Native.nuspec b/src/WixBuildTools.TestSupport.Native/WixBuildTools.TestSupport.Native.nuspec deleted file mode 100644 index 2852826b..00000000 --- a/src/WixBuildTools.TestSupport.Native/WixBuildTools.TestSupport.Native.nuspec +++ /dev/null @@ -1,26 +0,0 @@ - - - - $id$ - $version$ - $authors$ - $authors$ - MS-RL - https://github.com/wixtoolset/WixBuildTools - false - $description$ - $copyright$ - - - - - - - - - - - - - - diff --git a/src/WixBuildTools.TestSupport.Native/WixBuildTools.TestSupport.Native.vcxproj b/src/WixBuildTools.TestSupport.Native/WixBuildTools.TestSupport.Native.vcxproj deleted file mode 100644 index aefdb4fb..00000000 --- a/src/WixBuildTools.TestSupport.Native/WixBuildTools.TestSupport.Native.vcxproj +++ /dev/null @@ -1,85 +0,0 @@ - - - - - - - - - - - - Debug - Win32 - - - Release - Win32 - - - - {8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942} - {95BABD97-FBDB-453A-AF8A-FA031A07B599} - WixBuildTools::TestSupport - ManagedCProj - DynamicLibrary - Unicode - true - WixBuildTools C++/CLI Test Support - - - - - - - - - - - - - - - - - - - Create - - 4564;4691 - - - - - - - - - - - - - - - - {6C57EF2C-979A-4106-A9E5-FE342810619A} - - - - - - - - - - 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}. - - - - - - - - - - \ No newline at end of file diff --git a/src/WixBuildTools.TestSupport.Native/WixBuildTools.TestSupport.Native.vcxproj.filters b/src/WixBuildTools.TestSupport.Native/WixBuildTools.TestSupport.Native.vcxproj.filters deleted file mode 100644 index 34c1380f..00000000 --- a/src/WixBuildTools.TestSupport.Native/WixBuildTools.TestSupport.Native.vcxproj.filters +++ /dev/null @@ -1,33 +0,0 @@ - - - - - {4FC737F1-C7A5-4376-A066-2A32D752A2FF} - cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx - - - {93995380-89BD-4b04-88EB-625FBE52EBFB} - h;hh;hpp;hxx;hm;inl;inc;xsd - - - {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} - rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms - - - - - Header Files - - - Header Files - - - - - Source Files - - - Source Files - - - \ No newline at end of file diff --git a/src/WixBuildTools.TestSupport.Native/build/WixBuildTools.TestSupport.Native.props b/src/WixBuildTools.TestSupport.Native/build/WixBuildTools.TestSupport.Native.props deleted file mode 100644 index 4a7a0035..00000000 --- a/src/WixBuildTools.TestSupport.Native/build/WixBuildTools.TestSupport.Native.props +++ /dev/null @@ -1,29 +0,0 @@ - - - - - - - $([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildProjectDirectory), version.json)) - - - - - v142 - v4.7.2 - - - - $(RepoRootDir)\packages\xunit.abstractions.2.0.3\lib\netstandard2.0\xunit.abstractions.dll - - - $(RepoRootDir)\packages\xunit.assert.2.4.1\lib\netstandard1.1\xunit.assert.dll - - - $(RepoRootDir)\packages\xunit.extensibility.core.2.4.1\lib\netstandard1.1\xunit.core.dll - - - $(RepoRootDir)\packages\xunit.extensibility.execution.2.4.1\lib\net452\xunit.execution.desktop.dll - - - \ No newline at end of file diff --git a/src/WixBuildTools.TestSupport.Native/build/WixBuildTools.TestSupport.Native.targets b/src/WixBuildTools.TestSupport.Native/build/WixBuildTools.TestSupport.Native.targets deleted file mode 100644 index 77e72e95..00000000 --- a/src/WixBuildTools.TestSupport.Native/build/WixBuildTools.TestSupport.Native.targets +++ /dev/null @@ -1,19 +0,0 @@ - - - - - - - - - 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}. - - - - - - - - - - \ No newline at end of file diff --git a/src/WixBuildTools.TestSupport.Native/packages.config b/src/WixBuildTools.TestSupport.Native/packages.config deleted file mode 100644 index 917d7a63..00000000 --- a/src/WixBuildTools.TestSupport.Native/packages.config +++ /dev/null @@ -1,17 +0,0 @@ - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/src/WixBuildTools.TestSupport.Native/precomp.cpp b/src/WixBuildTools.TestSupport.Native/precomp.cpp deleted file mode 100644 index 37664a1c..00000000 --- a/src/WixBuildTools.TestSupport.Native/precomp.cpp +++ /dev/null @@ -1,3 +0,0 @@ -// 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. - -#include "precomp.h" diff --git a/src/WixBuildTools.TestSupport.Native/precomp.h b/src/WixBuildTools.TestSupport.Native/precomp.h deleted file mode 100644 index f54b55be..00000000 --- a/src/WixBuildTools.TestSupport.Native/precomp.h +++ /dev/null @@ -1,11 +0,0 @@ -#pragma once -// 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. - - -#include -#include - -#include "NativeAssert.h" - -#pragma managed -#include diff --git a/src/WixBuildTools.TestSupport/Builder.cs b/src/WixBuildTools.TestSupport/Builder.cs deleted file mode 100644 index ef0de8c9..00000000 --- a/src/WixBuildTools.TestSupport/Builder.cs +++ /dev/null @@ -1,70 +0,0 @@ -// 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. - -namespace WixBuildTools.TestSupport -{ - using System; - using System.Collections.Generic; - using System.IO; - - public class Builder - { - public Builder(string sourceFolder, Type extensionType = null, string[] bindPaths = null, string outputFile = null) - { - this.SourceFolder = sourceFolder; - this.ExtensionType = extensionType; - this.BindPaths = bindPaths; - this.OutputFile = outputFile ?? "test.msi"; - } - - public string[] BindPaths { get; set; } - - public Type ExtensionType { get; set; } - - public string OutputFile { get; set; } - - public string SourceFolder { get; } - - public string[] BuildAndQuery(Action buildFunc, params string[] tables) - { - var sourceFiles = Directory.GetFiles(this.SourceFolder, "*.wxs"); - var wxlFiles = Directory.GetFiles(this.SourceFolder, "*.wxl"); - - using (var fs = new DisposableFileSystem()) - { - var intermediateFolder = fs.GetFolder(); - var outputPath = Path.Combine(intermediateFolder, "bin", this.OutputFile); - - var args = new List - { - "build", - "-o", outputPath, - "-intermediateFolder", intermediateFolder, - }; - - if (this.ExtensionType != null) - { - args.Add("-ext"); - args.Add(Path.GetFullPath(new Uri(this.ExtensionType.Assembly.CodeBase).LocalPath)); - } - - args.AddRange(sourceFiles); - - foreach (var wxlFile in wxlFiles) - { - args.Add("-loc"); - args.Add(wxlFile); - } - - foreach (var bindPath in this.BindPaths) - { - args.Add("-bindpath"); - args.Add(bindPath); - } - - buildFunc(args.ToArray()); - - return Query.QueryDatabase(outputPath, tables); - } - } - } -} diff --git a/src/WixBuildTools.TestSupport/DisposableFileSystem.cs b/src/WixBuildTools.TestSupport/DisposableFileSystem.cs deleted file mode 100644 index f096db72..00000000 --- a/src/WixBuildTools.TestSupport/DisposableFileSystem.cs +++ /dev/null @@ -1,93 +0,0 @@ -// 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. - -namespace WixBuildTools.TestSupport -{ - using System; - using System.Collections.Generic; - using System.IO; - - public class DisposableFileSystem : IDisposable - { - protected bool Disposed { get; private set; } - - private List CleanupPaths { get; } = new List(); - - public bool Keep { get; } - - public DisposableFileSystem(bool keep = false) - { - this.Keep = keep; - } - - protected string GetFile(bool create = false) - { - var path = Path.GetTempFileName(); - - if (!create) - { - File.Delete(path); - } - - this.CleanupPaths.Add(path); - - return path; - } - - public string GetFolder(bool create = false) - { - var path = Path.Combine(Path.GetTempPath(), Path.GetRandomFileName()); - - if (create) - { - Directory.CreateDirectory(path); - } - - this.CleanupPaths.Add(path); - - return path; - } - - - #region // IDisposable - - public void Dispose() - { - this.Dispose(true); - GC.SuppressFinalize(this); - } - - protected virtual void Dispose(bool disposing) - { - if (this.Disposed) - { - return; - } - - if (disposing && !this.Keep) - { - foreach (var path in this.CleanupPaths) - { - try - { - if (File.Exists(path)) - { - File.Delete(path); - } - else if (Directory.Exists(path)) - { - Directory.Delete(path, true); - } - } - catch - { - // Best effort delete, so ignore any failures. - } - } - } - - this.Disposed = true; - } - - #endregion - } -} diff --git a/src/WixBuildTools.TestSupport/DotnetRunner.cs b/src/WixBuildTools.TestSupport/DotnetRunner.cs deleted file mode 100644 index 82391178..00000000 --- a/src/WixBuildTools.TestSupport/DotnetRunner.cs +++ /dev/null @@ -1,57 +0,0 @@ -// 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. - -namespace WixBuildTools.TestSupport -{ - using System; - using System.Collections.Generic; - using System.IO; - - public class DotnetRunner : ExternalExecutable - { - private static readonly object InitLock = new object(); - private static bool Initialized; - private static DotnetRunner Instance; - - public static ExternalExecutableResult Execute(string command, string[] arguments = null) => - InitAndExecute(command, arguments); - - private static ExternalExecutableResult InitAndExecute(string command, string[] arguments) - { - lock (InitLock) - { - if (!Initialized) - { - Initialized = true; - var dotnetPath = Environment.GetEnvironmentVariable("DOTNET_HOST_PATH"); - if (String.IsNullOrEmpty(dotnetPath) || !File.Exists(dotnetPath)) - { - dotnetPath = "dotnet"; - } - - Instance = new DotnetRunner(dotnetPath); - } - } - - return Instance.ExecuteCore(command, arguments); - } - - private DotnetRunner(string exePath) : base(exePath) { } - - private ExternalExecutableResult ExecuteCore(string command, string[] arguments) - { - var total = new List - { - command, - }; - - if (arguments != null) - { - total.AddRange(arguments); - } - - var args = CombineArguments(total); - var mergeErrorIntoOutput = true; - return this.Run(args, mergeErrorIntoOutput); - } - } -} diff --git a/src/WixBuildTools.TestSupport/ExternalExecutable.cs b/src/WixBuildTools.TestSupport/ExternalExecutable.cs deleted file mode 100644 index eb07aa13..00000000 --- a/src/WixBuildTools.TestSupport/ExternalExecutable.cs +++ /dev/null @@ -1,88 +0,0 @@ -// 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. - -namespace WixBuildTools.TestSupport -{ - using System.Collections.Concurrent; - using System.Collections.Generic; - using System.Diagnostics; - using System.IO; - using System.Text; - - public abstract class ExternalExecutable - { - private readonly string exePath; - - protected ExternalExecutable(string exePath) - { - this.exePath = exePath; - } - - protected ExternalExecutableResult Run(string args, bool mergeErrorIntoOutput = false, string workingDirectory = null) - { - var startInfo = new ProcessStartInfo(this.exePath, args) - { - CreateNoWindow = true, - RedirectStandardError = true, - RedirectStandardOutput = true, - UseShellExecute = false, - WorkingDirectory = workingDirectory ?? Path.GetDirectoryName(this.exePath), - }; - - using (var process = Process.Start(startInfo)) - { - // This implementation of merging the streams does not guarantee that lines are retrieved in the same order that they were written. - // If the process is simultaneously writing to both streams, this is impossible to do anyway. - var standardOutput = new ConcurrentQueue(); - var standardError = mergeErrorIntoOutput ? standardOutput : new ConcurrentQueue(); - - process.ErrorDataReceived += (s, e) => { if (e.Data != null) { standardError.Enqueue(e.Data); } }; - process.OutputDataReceived += (s, e) => { if (e.Data != null) { standardOutput.Enqueue(e.Data); } }; - - process.BeginErrorReadLine(); - process.BeginOutputReadLine(); - - process.WaitForExit(); - - return new ExternalExecutableResult - { - ExitCode = process.ExitCode, - StandardError = mergeErrorIntoOutput ? null : standardError.ToArray(), - StandardOutput = standardOutput.ToArray(), - StartInfo = startInfo, - }; - } - } - - // This is internal because it assumes backslashes aren't used as escape characters and there aren't any double quotes. - internal static string CombineArguments(IEnumerable arguments) - { - if (arguments == null) - { - return null; - } - - var sb = new StringBuilder(); - - foreach (var arg in arguments) - { - if (sb.Length > 0) - { - sb.Append(' '); - } - - if (arg.IndexOf(' ') > -1) - { - sb.Append("\""); - sb.Append(arg); - sb.Append("\""); - } - else - { - sb.Append(arg); - } - } - - return sb.ToString(); - } - } -} diff --git a/src/WixBuildTools.TestSupport/ExternalExecutableResult.cs b/src/WixBuildTools.TestSupport/ExternalExecutableResult.cs deleted file mode 100644 index 19b5183b..00000000 --- a/src/WixBuildTools.TestSupport/ExternalExecutableResult.cs +++ /dev/null @@ -1,17 +0,0 @@ -// 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. - -namespace WixBuildTools.TestSupport -{ - using System.Diagnostics; - - public class ExternalExecutableResult - { - public int ExitCode { get; set; } - - public string[] StandardError { get; set; } - - public string[] StandardOutput { get; set; } - - public ProcessStartInfo StartInfo { get; set; } - } -} diff --git a/src/WixBuildTools.TestSupport/FakeBuildEngine.cs b/src/WixBuildTools.TestSupport/FakeBuildEngine.cs deleted file mode 100644 index 20545970..00000000 --- a/src/WixBuildTools.TestSupport/FakeBuildEngine.cs +++ /dev/null @@ -1,33 +0,0 @@ -// 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. - -namespace WixBuildTools.TestSupport -{ - using System.Collections; - using System.Text; - using Microsoft.Build.Framework; - - public class FakeBuildEngine : IBuildEngine - { - private readonly StringBuilder output = new StringBuilder(); - - public int ColumnNumberOfTaskNode => 0; - - public bool ContinueOnError => false; - - public int LineNumberOfTaskNode => 0; - - public string ProjectFileOfTaskNode => "fake_wix.targets"; - - public string Output => this.output.ToString(); - - public bool BuildProjectFile(string projectFileName, string[] targetNames, IDictionary globalProperties, IDictionary targetOutputs) => throw new System.NotImplementedException(); - - public void LogCustomEvent(CustomBuildEventArgs e) => this.output.AppendLine(e.Message); - - public void LogErrorEvent(BuildErrorEventArgs e) => this.output.AppendLine(e.Message); - - public void LogMessageEvent(BuildMessageEventArgs e) => this.output.AppendLine(e.Message); - - public void LogWarningEvent(BuildWarningEventArgs e) => this.output.AppendLine(e.Message); - } -} diff --git a/src/WixBuildTools.TestSupport/MsbuildRunner.cs b/src/WixBuildTools.TestSupport/MsbuildRunner.cs deleted file mode 100644 index 35e53de6..00000000 --- a/src/WixBuildTools.TestSupport/MsbuildRunner.cs +++ /dev/null @@ -1,168 +0,0 @@ -// 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. - -namespace WixBuildTools.TestSupport -{ - using System; - using System.Collections.Generic; - using System.IO; - - public class MsbuildRunner : ExternalExecutable - { - private static readonly string VswhereFindArguments = "-property installationPath"; - private static readonly string Msbuild15RelativePath = @"MSBuild\15.0\Bin\MSBuild.exe"; - private static readonly string Msbuild15RelativePath64 = @"MSBuild\15.0\Bin\amd64\MSBuild.exe"; - private static readonly string MsbuildCurrentRelativePath = @"MSBuild\Current\Bin\MSBuild.exe"; - private static readonly string MsbuildCurrentRelativePath64 = @"MSBuild\Current\Bin\amd64\MSBuild.exe"; - - private static readonly object InitLock = new object(); - - private static bool Initialized; - private static MsbuildRunner Msbuild15Runner; - private static MsbuildRunner Msbuild15Runner64; - private static MsbuildRunner MsbuildCurrentRunner; - private static MsbuildRunner MsbuildCurrentRunner64; - - public static MsbuildRunnerResult Execute(string projectPath, string[] arguments = null, bool x64 = false) => - InitAndExecute(String.Empty, projectPath, arguments, x64); - - public static MsbuildRunnerResult ExecuteWithMsbuild15(string projectPath, string[] arguments = null, bool x64 = false) => - InitAndExecute("15", projectPath, arguments, x64); - - public static MsbuildRunnerResult ExecuteWithMsbuildCurrent(string projectPath, string[] arguments = null, bool x64 = false) => - InitAndExecute("Current", projectPath, arguments, x64); - - private static MsbuildRunnerResult InitAndExecute(string msbuildVersion, string projectPath, string[] arguments, bool x64) - { - lock (InitLock) - { - if (!Initialized) - { - Initialized = true; - var vswhereResult = VswhereRunner.Execute(VswhereFindArguments, true); - if (vswhereResult.ExitCode != 0) - { - throw new InvalidOperationException($"Failed to execute vswhere.exe, exit code: {vswhereResult.ExitCode}. Output:\r\n{String.Join("\r\n", vswhereResult.StandardOutput)}"); - } - - string msbuild15Path = null; - string msbuild15Path64 = null; - string msbuildCurrentPath = null; - string msbuildCurrentPath64 = null; - - foreach (var installPath in vswhereResult.StandardOutput) - { - if (msbuildCurrentPath == null) - { - var path = Path.Combine(installPath, MsbuildCurrentRelativePath); - if (File.Exists(path)) - { - msbuildCurrentPath = path; - } - } - - if (msbuildCurrentPath64 == null) - { - var path = Path.Combine(installPath, MsbuildCurrentRelativePath64); - if (File.Exists(path)) - { - msbuildCurrentPath64 = path; - } - } - - if (msbuild15Path == null) - { - var path = Path.Combine(installPath, Msbuild15RelativePath); - if (File.Exists(path)) - { - msbuild15Path = path; - } - } - - if (msbuild15Path64 == null) - { - var path = Path.Combine(installPath, Msbuild15RelativePath64); - if (File.Exists(path)) - { - msbuild15Path64 = path; - } - } - } - - if (msbuildCurrentPath != null) - { - MsbuildCurrentRunner = new MsbuildRunner(msbuildCurrentPath); - } - - if (msbuildCurrentPath64 != null) - { - MsbuildCurrentRunner64 = new MsbuildRunner(msbuildCurrentPath64); - } - - if (msbuild15Path != null) - { - Msbuild15Runner = new MsbuildRunner(msbuild15Path); - } - - if (msbuild15Path64 != null) - { - Msbuild15Runner64 = new MsbuildRunner(msbuild15Path64); - } - } - } - - MsbuildRunner runner; - switch (msbuildVersion) - { - case "15": - { - runner = x64 ? Msbuild15Runner64 : Msbuild15Runner; - break; - } - case "Current": - { - runner = x64 ? MsbuildCurrentRunner64 : MsbuildCurrentRunner; - break; - } - default: - { - runner = x64 ? MsbuildCurrentRunner64 ?? Msbuild15Runner64 - : MsbuildCurrentRunner ?? Msbuild15Runner; - break; - } - } - - if (runner == null) - { - throw new InvalidOperationException($"Failed to find an installed{(x64 ? " 64-bit" : String.Empty)} MSBuild{msbuildVersion}"); - } - - return runner.ExecuteCore(projectPath, arguments); - } - - private MsbuildRunner(string exePath) : base(exePath) { } - - private MsbuildRunnerResult ExecuteCore(string projectPath, string[] arguments) - { - var total = new List - { - projectPath, - }; - - if (arguments != null) - { - total.AddRange(arguments); - } - - var args = CombineArguments(total); - var mergeErrorIntoOutput = true; - var workingFolder = Path.GetDirectoryName(projectPath); - var result = this.Run(args, mergeErrorIntoOutput, workingFolder); - - return new MsbuildRunnerResult - { - ExitCode = result.ExitCode, - Output = result.StandardOutput, - }; - } - } -} diff --git a/src/WixBuildTools.TestSupport/MsbuildRunnerResult.cs b/src/WixBuildTools.TestSupport/MsbuildRunnerResult.cs deleted file mode 100644 index 5610987e..00000000 --- a/src/WixBuildTools.TestSupport/MsbuildRunnerResult.cs +++ /dev/null @@ -1,19 +0,0 @@ -// 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. - -namespace WixBuildTools.TestSupport -{ - using System; - using Xunit; - - public class MsbuildRunnerResult - { - public int ExitCode { get; set; } - - public string[] Output { get; set; } - - public void AssertSuccess() - { - Assert.True(0 == this.ExitCode, $"MSBuild failed unexpectedly. Output:\r\n{String.Join("\r\n", this.Output)}"); - } - } -} diff --git a/src/WixBuildTools.TestSupport/Pushd.cs b/src/WixBuildTools.TestSupport/Pushd.cs deleted file mode 100644 index d0545215..00000000 --- a/src/WixBuildTools.TestSupport/Pushd.cs +++ /dev/null @@ -1,46 +0,0 @@ -// 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. - -namespace WixBuildTools.TestSupport -{ - using System; - using System.IO; - - public class Pushd : IDisposable - { - protected bool Disposed { get; private set; } - - public Pushd(string path) - { - this.PreviousDirectory = Directory.GetCurrentDirectory(); - - Directory.SetCurrentDirectory(path); - } - - public string PreviousDirectory { get; } - - #region // IDisposable - - public void Dispose() - { - this.Dispose(true); - GC.SuppressFinalize(this); - } - - protected virtual void Dispose(bool disposing) - { - if (this.Disposed) - { - return; - } - - if (disposing) - { - Directory.SetCurrentDirectory(this.PreviousDirectory); - } - - this.Disposed = true; - } - - #endregion - } -} diff --git a/src/WixBuildTools.TestSupport/Query.cs b/src/WixBuildTools.TestSupport/Query.cs deleted file mode 100644 index 101a8890..00000000 --- a/src/WixBuildTools.TestSupport/Query.cs +++ /dev/null @@ -1,172 +0,0 @@ -// 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. - -namespace WixBuildTools.TestSupport -{ - using System; - using System.Collections.Generic; - using System.IO; - using System.Linq; - using System.Text; - using WixToolset.Dtf.Compression.Cab; - using WixToolset.Dtf.WindowsInstaller; - - public class Query - { - public static string[] QueryDatabase(string path, string[] tables) - { - var results = new List(); - var resultsByTable = QueryDatabaseByTable(path, tables); - var sortedTables = tables.ToList(); - sortedTables.Sort(); - foreach (var tableName in sortedTables) - { - var rows = resultsByTable[tableName]; - rows?.ForEach(r => results.Add($"{tableName}:{r}")); - } - return results.ToArray(); - } - - /// - /// Returns rows from requested tables formatted to facilitate testing. - /// If the table did not exist in the database, its list will be null. - /// - /// - /// - /// - public static Dictionary> QueryDatabaseByTable(string path, string[] tables) - { - var results = new Dictionary>(); - - if (tables?.Length > 0) - { - var sb = new StringBuilder(); - using (var db = new Database(path)) - { - foreach (var table in tables) - { - if (table == "_SummaryInformation") - { - var entries = new List(); - results.Add(table, entries); - - entries.Add($"Title\t{db.SummaryInfo.Title}"); - entries.Add($"Subject\t{db.SummaryInfo.Subject}"); - entries.Add($"Author\t{db.SummaryInfo.Author}"); - entries.Add($"Keywords\t{db.SummaryInfo.Keywords}"); - entries.Add($"Comments\t{db.SummaryInfo.Comments}"); - entries.Add($"Template\t{db.SummaryInfo.Template}"); - entries.Add($"CodePage\t{db.SummaryInfo.CodePage}"); - entries.Add($"PageCount\t{db.SummaryInfo.PageCount}"); - entries.Add($"WordCount\t{db.SummaryInfo.WordCount}"); - entries.Add($"CharacterCount\t{db.SummaryInfo.CharacterCount}"); - entries.Add($"Security\t{db.SummaryInfo.Security}"); - - continue; - } - - if (!db.IsTablePersistent(table)) - { - results.Add(table, null); - continue; - } - - var rows = new List(); - results.Add(table, rows); - - using (var view = db.OpenView("SELECT * FROM `{0}`", table)) - { - view.Execute(); - - Record record; - while ((record = view.Fetch()) != null) - { - sb.Clear(); - - using (record) - { - for (var i = 0; i < record.FieldCount; ++i) - { - if (i > 0) - { - sb.Append("\t"); - } - - sb.Append(record[i + 1]?.ToString()); - } - } - - rows.Add(sb.ToString()); - } - } - rows.Sort(); - } - } - } - - return results; - } - - public static CabFileInfo[] GetCabinetFiles(string path) - { - var cab = new CabInfo(path); - - var result = cab.GetFiles(); - - return result.Select(c => c).ToArray(); - } - - public static void ExtractStream(string path, string streamName, string outputPath) - { - Directory.CreateDirectory(Path.GetDirectoryName(outputPath)); - - using (var db = new Database(path)) - using (var view = db.OpenView("SELECT `Data` FROM `_Streams` WHERE `Name` = '{0}'", streamName)) - { - view.Execute(); - - using (var record = view.Fetch()) - { - record.GetStream(1, outputPath); - } - } - } - - public static void ExtractSubStorage(string path, string subStorageName, string outputPath) - { - Directory.CreateDirectory(Path.GetDirectoryName(outputPath)); - - using (var db = new Database(path)) - using (var view = db.OpenView("SELECT `Name`, `Data` FROM `_Storages` WHERE `Name` = '{0}'", subStorageName)) - { - view.Execute(); - - using (var record = view.Fetch()) - { - var name = record.GetString(1); - record.GetStream(2, outputPath); - } - } - } - - public static string[] GetSubStorageNames(string path) - { - var result = new List(); - - using (var db = new Database(path)) - using (var view = db.OpenView("SELECT `Name` FROM `_Storages`")) - { - view.Execute(); - - Record record; - while ((record = view.Fetch()) != null) - { - var name = record.GetString(1); - result.Add(name); - } - } - - result.Sort(); - return result.ToArray(); - } - } -} diff --git a/src/WixBuildTools.TestSupport/RobocopyRunner.cs b/src/WixBuildTools.TestSupport/RobocopyRunner.cs deleted file mode 100644 index 49d53351..00000000 --- a/src/WixBuildTools.TestSupport/RobocopyRunner.cs +++ /dev/null @@ -1,16 +0,0 @@ -// 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. - -namespace WixBuildTools.TestSupport -{ - public class RobocopyRunner : ExternalExecutable - { - private static readonly RobocopyRunner Instance = new RobocopyRunner(); - - private RobocopyRunner() : base("robocopy") { } - - public static ExternalExecutableResult Execute(string args) - { - return Instance.Run(args); - } - } -} diff --git a/src/WixBuildTools.TestSupport/SucceededException.cs b/src/WixBuildTools.TestSupport/SucceededException.cs deleted file mode 100644 index 00b31d68..00000000 --- a/src/WixBuildTools.TestSupport/SucceededException.cs +++ /dev/null @@ -1,18 +0,0 @@ -// 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. - -namespace WixBuildTools.TestSupport -{ - using System; - using Xunit.Sdk; - - public class SucceededException : XunitException - { - public SucceededException(int hr, string userMessage) - : base(String.Format("WixAssert.Succeeded() Failure\r\n" + - "HRESULT: 0x{0:X8}\r\n" + - "Message: {1}", - hr, userMessage)) - { - } - } -} diff --git a/src/WixBuildTools.TestSupport/TestData.cs b/src/WixBuildTools.TestSupport/TestData.cs deleted file mode 100644 index 8587330d..00000000 --- a/src/WixBuildTools.TestSupport/TestData.cs +++ /dev/null @@ -1,16 +0,0 @@ -// 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. - -namespace WixBuildTools.TestSupport -{ - using System; - using System.IO; - - public class TestData - { - public static string Get(params string[] paths) - { - var localPath = Path.GetDirectoryName(new Uri(System.Reflection.Assembly.GetCallingAssembly().CodeBase).LocalPath); - return Path.Combine(localPath, Path.Combine(paths)); - } - } -} diff --git a/src/WixBuildTools.TestSupport/TestDataFolderFileSystem.cs b/src/WixBuildTools.TestSupport/TestDataFolderFileSystem.cs deleted file mode 100644 index 8d670bf0..00000000 --- a/src/WixBuildTools.TestSupport/TestDataFolderFileSystem.cs +++ /dev/null @@ -1,42 +0,0 @@ -// 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. - -namespace WixBuildTools.TestSupport -{ - using System; - - /// - /// This class builds on top of DisposableFileSystem - /// to make it easy to write a test that needs a whole folder of test data copied to a temp location - /// that will automatically be cleaned up at the end of the test. - /// - public class TestDataFolderFileSystem : IDisposable - { - private DisposableFileSystem fileSystem; - - public string BaseFolder { get; private set; } - - public void Dispose() - { - this.fileSystem?.Dispose(); - } - - public void Initialize(string sourceDirectoryPath) - { - if (this.fileSystem != null) - { - throw new InvalidOperationException(); - } - this.fileSystem = new DisposableFileSystem(); - - this.BaseFolder = this.fileSystem.GetFolder(); - - RobocopyFolder(sourceDirectoryPath, this.BaseFolder); - } - - private static ExternalExecutableResult RobocopyFolder(string sourceFolderPath, string destinationFolderPath) - { - var args = $"\"{sourceFolderPath}\" \"{destinationFolderPath}\" /E /R:1 /W:1"; - return RobocopyRunner.Execute(args); - } - } -} diff --git a/src/WixBuildTools.TestSupport/VswhereRunner.cs b/src/WixBuildTools.TestSupport/VswhereRunner.cs deleted file mode 100644 index 0197e125..00000000 --- a/src/WixBuildTools.TestSupport/VswhereRunner.cs +++ /dev/null @@ -1,41 +0,0 @@ -// 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. - -namespace WixBuildTools.TestSupport -{ - using System; - using System.IO; - - public class VswhereRunner : ExternalExecutable - { - private static readonly string VswhereRelativePath = @"Microsoft Visual Studio\Installer\vswhere.exe"; - - private static readonly object InitLock = new object(); - private static bool Initialized; - private static VswhereRunner Instance; - - public static ExternalExecutableResult Execute(string args, bool mergeErrorIntoOutput = false) => - InitAndExecute(args, mergeErrorIntoOutput); - - private static ExternalExecutableResult InitAndExecute(string args, bool mergeErrorIntoOutput) - { - lock (InitLock) - { - if (!Initialized) - { - Initialized = true; - var vswherePath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ProgramFilesX86), VswhereRelativePath); - if (!File.Exists(vswherePath)) - { - throw new InvalidOperationException($"Failed to find vswhere at: {vswherePath}"); - } - - Instance = new VswhereRunner(vswherePath); - } - } - - return Instance.Run(args, mergeErrorIntoOutput); - } - - private VswhereRunner(string exePath) : base(exePath) { } - } -} diff --git a/src/WixBuildTools.TestSupport/WixAssert.cs b/src/WixBuildTools.TestSupport/WixAssert.cs deleted file mode 100644 index 5638a787..00000000 --- a/src/WixBuildTools.TestSupport/WixAssert.cs +++ /dev/null @@ -1,47 +0,0 @@ -// 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. - -namespace WixBuildTools.TestSupport -{ - using System; - using System.Linq; - using System.Xml.Linq; - using Xunit; - - public class WixAssert : Assert - { - public static void CompareLineByLine(string[] expectedLines, string[] actualLines) - { - for (var i = 0; i < expectedLines.Length; ++i) - { - Assert.True(actualLines.Length > i, $"{i}: expectedLines longer than actualLines"); - Assert.Equal($"{i}: {expectedLines[i]}", $"{i}: {actualLines[i]}"); - } - - Assert.True(expectedLines.Length == actualLines.Length, "actualLines longer than expectedLines"); - } - - public static void CompareXml(XContainer xExpected, XContainer xActual) - { - 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}"))}"); - 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}"))}"); - - CompareLineByLine(expecteds.OrderBy(s => s).ToArray(), actuals.OrderBy(s => s).ToArray()); - } - - public static void CompareXml(string expectedPath, string actualPath) - { - var expectedDoc = XDocument.Load(expectedPath, LoadOptions.PreserveWhitespace | LoadOptions.SetBaseUri | LoadOptions.SetLineInfo); - var actualDoc = XDocument.Load(actualPath, LoadOptions.PreserveWhitespace | LoadOptions.SetBaseUri | LoadOptions.SetLineInfo); - - CompareXml(expectedDoc, actualDoc); - } - - public static void Succeeded(int hr, string format, params object[] formatArgs) - { - if (0 > hr) - { - throw new SucceededException(hr, String.Format(format, formatArgs)); - } - } - } -} diff --git a/src/WixBuildTools.TestSupport/WixBuildTools.TestSupport.csproj b/src/WixBuildTools.TestSupport/WixBuildTools.TestSupport.csproj deleted file mode 100644 index f59e5eca..00000000 --- a/src/WixBuildTools.TestSupport/WixBuildTools.TestSupport.csproj +++ /dev/null @@ -1,31 +0,0 @@ - - - - - - - netstandard2.0;net461;net472 - true - embedded - true - true - CS1591 - - - - - - - - - - - - - - - - - - - diff --git a/src/WixBuildTools.XsdGen/AssemblyInfo.cs b/src/WixBuildTools.XsdGen/AssemblyInfo.cs deleted file mode 100644 index b3740b2a..00000000 --- a/src/WixBuildTools.XsdGen/AssemblyInfo.cs +++ /dev/null @@ -1,9 +0,0 @@ -// 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. - -using System; -using System.Reflection; -using System.Runtime.InteropServices; - -[assembly: AssemblyCulture("")] -[assembly: CLSCompliant(true)] -[assembly: ComVisible(false)] diff --git a/src/WixBuildTools.XsdGen/CodeDomInterfaces.cs b/src/WixBuildTools.XsdGen/CodeDomInterfaces.cs deleted file mode 100644 index 850839d4..00000000 --- a/src/WixBuildTools.XsdGen/CodeDomInterfaces.cs +++ /dev/null @@ -1,96 +0,0 @@ -// 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. - -namespace WixToolset.Serialize -{ - using System; - using System.Collections; - using System.Xml; - - /// - /// Interface for generated schema elements. - /// - public interface ISchemaElement - { - /// - /// Gets and sets the parent of this element. May be null. - /// - /// An ISchemaElement that has this element as a child. - ISchemaElement ParentElement - { - get; - set; - } - - /// - /// Outputs xml representing this element, including the associated attributes - /// and any nested elements. - /// - /// XmlTextWriter to be used when outputting the element. - void OutputXml(XmlWriter writer); - } - - /// - /// Interface for generated schema elements. Implemented by elements that have child - /// elements. - /// - public interface IParentElement - { - /// - /// Gets an enumerable collection of the children of this element. - /// - /// An enumerable collection of the children of this element. - IEnumerable Children - { - get; - } - - /// - /// Gets an enumerable collection of the children of this element, filtered - /// by the passed in type. - /// - /// The type of children to retrieve. - IEnumerable this[Type childType] - { - get; - } - - /// - /// Adds a child to this element. - /// - /// Child to add. - void AddChild(ISchemaElement child); - - /// - /// Removes a child from this element. - /// - /// Child to remove. - void RemoveChild(ISchemaElement child); - } - - /// - /// Interface for generated schema elements. Implemented by classes with attributes. - /// - public interface ISetAttributes - { - /// - /// Sets the attribute with the given name to the given value. The value here is - /// a string, and is converted to the strongly-typed version inside this method. - /// - /// The name of the attribute to set. - /// The value to assign to the attribute. - void SetAttribute(string name, string value); - } - - /// - /// Interface for generated schema elements. Implemented by classes with children. - /// - public interface ICreateChildren - { - /// - /// Creates an instance of the child with the passed in name. - /// - /// String matching the element name of the child when represented in XML. - /// An instance of that child. - ISchemaElement CreateChild(string childName); - } -} diff --git a/src/WixBuildTools.XsdGen/CodeDomReader.cs b/src/WixBuildTools.XsdGen/CodeDomReader.cs deleted file mode 100644 index 5198f264..00000000 --- a/src/WixBuildTools.XsdGen/CodeDomReader.cs +++ /dev/null @@ -1,159 +0,0 @@ -// 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. - -namespace WixToolset.Serialize -{ - using System; - using System.Collections; - using System.Reflection; - using System.Xml; - - /// - /// Class used for reading XML files in to the CodeDom. - /// - public class CodeDomReader - { - private Assembly[] assemblies; - - /// - /// Creates a new CodeDomReader, using the current assembly. - /// - public CodeDomReader() - { - this.assemblies = new Assembly[] { Assembly.GetExecutingAssembly() }; - } - - /// - /// Creates a new CodeDomReader, and takes in a list of assemblies in which to - /// look for elements. - /// - /// Assemblies in which to look for types that correspond - /// to elements. - public CodeDomReader(Assembly[] assemblies) - { - this.assemblies = assemblies; - } - - /// - /// Loads an XML file into a strongly-typed code dom. - /// - /// File to load into the code dom. - /// The strongly-typed object at the root of the tree. - public ISchemaElement Load(string filePath) - { - XmlDocument document = new XmlDocument(); - document.Load(filePath); - ISchemaElement schemaElement = null; - - foreach (XmlNode node in document.ChildNodes) - { - XmlElement element = node as XmlElement; - if (element != null) - { - if (schemaElement != null) - { - throw new InvalidOperationException("Multiple root elements found in file."); - } - - schemaElement = this.CreateObjectFromElement(element); - this.ParseObjectFromElement(schemaElement, element); - } - } - return schemaElement; - } - - /// - /// Parses an ISchemaElement from the XmlElement. - /// - /// ISchemaElement to fill in. - /// XmlElement to parse from. - private void ParseObjectFromElement(ISchemaElement schemaElement, XmlElement element) - { - foreach (XmlAttribute attribute in element.Attributes) - { - this.SetAttributeOnObject(schemaElement, attribute.LocalName, attribute.Value); - } - - foreach (XmlNode node in element.ChildNodes) - { - XmlElement childElement = node as XmlElement; - if (childElement != null) - { - ISchemaElement childSchemaElement = null; - ICreateChildren createChildren = schemaElement as ICreateChildren; - if (createChildren == null) - { - throw new InvalidOperationException("ISchemaElement with name " + element.LocalName + " does not implement ICreateChildren."); - } - else - { - childSchemaElement = createChildren.CreateChild(childElement.LocalName); - } - - if (childSchemaElement == null) - { - childSchemaElement = this.CreateObjectFromElement(childElement); - if (childSchemaElement == null) - { - throw new InvalidOperationException("XmlElement with name " + childElement.LocalName + " does not have a corresponding ISchemaElement."); - } - } - - this.ParseObjectFromElement(childSchemaElement, childElement); - IParentElement parentElement = (IParentElement)schemaElement; - parentElement.AddChild(childSchemaElement); - } - else - { - XmlText childText = node as XmlText; - if (childText != null) - { - this.SetAttributeOnObject(schemaElement, "Content", childText.Value); - } - } - } - } - - /// - /// Sets an attribute on an ISchemaElement. - /// - /// Schema element to set attribute on. - /// Name of the attribute to set. - /// Value to set on the attribute. - private void SetAttributeOnObject(ISchemaElement schemaElement, string name, string value) - { - ISetAttributes setAttributes = schemaElement as ISetAttributes; - if (setAttributes == null) - { - throw new InvalidOperationException("ISchemaElement with name " - + schemaElement.GetType().FullName.ToString() - + " does not implement ISetAttributes."); - } - else - { - setAttributes.SetAttribute(name, value); - } - } - - /// - /// Creates an object from an XML element by digging through the assembly list. - /// - /// XML Element to create an ISchemaElement from. - /// A constructed ISchemaElement. - private ISchemaElement CreateObjectFromElement(XmlElement element) - { - ISchemaElement schemaElement = null; - foreach (Assembly assembly in this.assemblies) - { - foreach (Type type in assembly.GetTypes()) - { - if (type.FullName.EndsWith(element.LocalName) - && typeof(ISchemaElement).IsAssignableFrom(type)) - { - schemaElement = (ISchemaElement)Activator.CreateInstance(type); - } - } - } - return schemaElement; - } - } -} diff --git a/src/WixBuildTools.XsdGen/ElementCollection.cs b/src/WixBuildTools.XsdGen/ElementCollection.cs deleted file mode 100644 index 3f0bff16..00000000 --- a/src/WixBuildTools.XsdGen/ElementCollection.cs +++ /dev/null @@ -1,642 +0,0 @@ -// 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. - -namespace WixToolset.Serialize -{ - using System; - using System.Collections; - using System.Globalization; - - /// - /// Collection used in the CodeDOM for the children of a given element. Provides type-checking - /// on the allowed children to ensure that only allowed types are added. - /// - public class ElementCollection : ICollection, IEnumerable - { - private CollectionType collectionType; - private int minimum = 1; - private int maximum = 1; - private int totalContainedItems; - private int containersUsed; - private ArrayList items; - - /// - /// Creates a new element collection. - /// - /// Type of the collection to create. - public ElementCollection(CollectionType collectionType) - { - this.collectionType = collectionType; - this.items = new ArrayList(); - } - - /// - /// Creates a new element collection. - /// - /// Type of the collection to create. - /// When used with a type 'Choice', specifies a minimum number of allowed children. - /// When used with a type 'Choice', specifies a maximum number of allowed children. - public ElementCollection(CollectionType collectionType, int minimum, int maximum) : this(collectionType) - { - this.minimum = minimum; - this.maximum = maximum; - } - - /// - /// Enum representing types of XML collections. - /// - public enum CollectionType - { - /// - /// A choice type, corresponding to the XSD choice element. - /// - Choice, - - /// - /// A sequence type, corresponding to the XSD sequence element. - /// - Sequence - } - - /// - /// Gets the type of collection. - /// - /// The type of collection. - public CollectionType Type - { - get { return this.collectionType; } - } - - /// - /// Gets the count of child elements in this collection (counts ISchemaElements, not nested collections). - /// - /// The count of child elements in this collection (counts ISchemaElements, not nested collections). - public int Count - { - get { return this.totalContainedItems; } - } - - /// - /// Gets the flag specifying whether this collection is synchronized. Always returns false. - /// - /// The flag specifying whether this collection is synchronized. Always returns false. - public bool IsSynchronized - { - get { return false; } - } - - /// - /// Gets an object external callers can synchronize on. - /// - /// An object external callers can synchronize on. - public object SyncRoot - { - get { return this; } - } - - /// - /// Adds a child element to this collection. - /// - /// The element to add. - /// Thrown if the child is not of an allowed type. - public void AddElement(ISchemaElement element) - { - foreach (object obj in this.items) - { - bool containerUsed; - - CollectionItem collectionItem = obj as CollectionItem; - if (collectionItem != null) - { - containerUsed = collectionItem.Elements.Count != 0; - if (collectionItem.ElementType.IsAssignableFrom(element.GetType())) - { - collectionItem.AddElement(element); - - if (!containerUsed) - { - this.containersUsed++; - } - - this.totalContainedItems++; - return; - } - - continue; - } - - ElementCollection collection = obj as ElementCollection; - if (collection != null) - { - containerUsed = collection.Count != 0; - - try - { - collection.AddElement(element); - - if (!containerUsed) - { - this.containersUsed++; - } - - this.totalContainedItems++; - return; - } - catch (ArgumentException) - { - // Eat the exception and keep looking. We'll throw our own if we can't find its home. - } - - continue; - } - } - - throw new ArgumentException(String.Format( - CultureInfo.InvariantCulture, - "Element of type {0} is not valid for this collection.", - element.GetType().Name)); - } - - /// - /// Removes a child element from this collection. - /// - /// The element to remove. - /// Thrown if the element is not of an allowed type. - public void RemoveElement(ISchemaElement element) - { - foreach (object obj in this.items) - { - CollectionItem collectionItem = obj as CollectionItem; - if (collectionItem != null) - { - if (collectionItem.ElementType.IsAssignableFrom(element.GetType())) - { - if (collectionItem.Elements.Count == 0) - { - return; - } - - collectionItem.RemoveElement(element); - - if (collectionItem.Elements.Count == 0) - { - this.containersUsed--; - } - - this.totalContainedItems--; - return; - } - - continue; - } - - ElementCollection collection = obj as ElementCollection; - if (collection != null) - { - if (collection.Count == 0) - { - continue; - } - - try - { - collection.RemoveElement(element); - - if (collection.Count == 0) - { - this.containersUsed--; - } - - this.totalContainedItems--; - return; - } - catch (ArgumentException) - { - // Eat the exception and keep looking. We'll throw our own if we can't find its home. - } - - continue; - } - } - - throw new ArgumentException(String.Format( - CultureInfo.InvariantCulture, - "Element of type {0} is not valid for this collection.", - element.GetType().Name)); - } - - /// - /// Copies this collection to an array. - /// - /// Array to copy to. - /// Offset into the array. - public void CopyTo(Array array, int index) - { - int item = 0; - foreach (ISchemaElement element in this) - { - array.SetValue(element, (long)(item + index)); - item++; - } - } - - /// - /// Creates an enumerator for walking the elements in this collection. - /// - /// A newly created enumerator. - public IEnumerator GetEnumerator() - { - return new ElementCollectionEnumerator(this); - } - - /// - /// Gets an enumerable collection of children of a given type. - /// - /// Type of children to get. - /// A collection of children. - /// Thrown if the type isn't a valid child type. - public IEnumerable Filter(Type childType) - { - foreach (object container in this.items) - { - CollectionItem collectionItem = container as CollectionItem; - if (collectionItem != null) - { - if (collectionItem.ElementType.IsAssignableFrom(childType)) - { - return collectionItem.Elements; - } - - continue; - } - - ElementCollection elementCollection = container as ElementCollection; - if (elementCollection != null) - { - IEnumerable nestedFilter = elementCollection.Filter(childType); - if (nestedFilter != null) - { - return nestedFilter; - } - - continue; - } - } - - throw new ArgumentException(String.Format( - CultureInfo.InvariantCulture, - "Type {0} is not valid for this collection.", - childType.Name)); - } - - /// - /// Adds a type to this collection. - /// - /// CollectionItem representing the type to add. - public void AddItem(CollectionItem collectionItem) - { - this.items.Add(collectionItem); - } - - /// - /// Adds a nested collection to this collection. - /// - /// ElementCollection to add. - public void AddCollection(ElementCollection collection) - { - this.items.Add(collection); - } - - /// - /// Class used to represent a given type in the child collection of an element. Abstract, - /// has subclasses for choice and sequence (which can do cardinality checks). - /// - public abstract class CollectionItem - { - private Type elementType; - private ArrayList elements; - - /// - /// Creates a new CollectionItem for the given element type. - /// - /// Type of the element for this collection item. - public CollectionItem(Type elementType) - { - this.elementType = elementType; - this.elements = new ArrayList(); - } - - /// - /// Gets the type of this collection's items. - /// - public Type ElementType - { - get { return this.elementType; } - } - - /// - /// Gets the elements of this collection. - /// - public ArrayList Elements - { - get { return this.elements; } - } - - /// - /// Adds an element to this collection. Must be of an assignable type to the collection's - /// type. - /// - /// The element to add. - /// Thrown if the type isn't assignable to the collection's type. - public void AddElement(ISchemaElement element) - { - if (!this.elementType.IsAssignableFrom(element.GetType())) - { - throw new ArgumentException( - String.Format( - CultureInfo.InvariantCulture, - "Element must be a subclass of {0}, but was of type {1}.", - this.elementType.Name, - element.GetType().Name), - "element"); - } - - this.elements.Add(element); - } - - /// - /// Removes an element from this collection. - /// - /// The element to remove. - /// Thrown if the element's type isn't assignable to the collection's type. - public void RemoveElement(ISchemaElement element) - { - if (!this.elementType.IsAssignableFrom(element.GetType())) - { - throw new ArgumentException( - String.Format( - CultureInfo.InvariantCulture, - "Element must be a subclass of {0}, but was of type {1}.", - this.elementType.Name, - element.GetType().Name), - "element"); - } - - this.elements.Remove(element); - } - } - - /// - /// Class representing a choice item. Doesn't do cardinality checks. - /// - public class ChoiceItem : CollectionItem - { - /// - /// Creates a new choice item. - /// - /// Type of the created item. - public ChoiceItem(Type elementType) : base(elementType) - { - } - } - - /// - /// Class representing a sequence item. Can do cardinality checks, if required. - /// - public class SequenceItem : CollectionItem - { - private int minimum = 1; - private int maximum = 1; - - /// - /// Creates a new sequence item. - /// - /// Type of the created item. - public SequenceItem(Type elementType) : base(elementType) - { - } - - /// - /// Creates a new sequence item with the specified minimum and maximum. - /// - /// Type of the created item. - /// Minimum number of elements. - /// Maximum number of elements. - public SequenceItem(Type elementType, int minimum, int maximum) : base(elementType) - { - this.minimum = minimum; - this.maximum = maximum; - } - } - - /// - /// Enumerator for the ElementCollection. - /// - private class ElementCollectionEnumerator : IEnumerator - { - private ElementCollection collection; - private Stack collectionStack; - - /// - /// Creates a new ElementCollectionEnumerator. - /// - /// The collection to create an enumerator for. - public ElementCollectionEnumerator(ElementCollection collection) - { - this.collection = collection; - } - - /// - /// Gets the current object from the enumerator. - /// - public object Current - { - get - { - if (this.collectionStack != null && this.collectionStack.Count > 0) - { - CollectionSymbol symbol = (CollectionSymbol)this.collectionStack.Peek(); - object container = symbol.Collection.items[symbol.ContainerIndex]; - - CollectionItem collectionItem = container as CollectionItem; - if (collectionItem != null) - { - return collectionItem.Elements[symbol.ItemIndex]; - } - - throw new InvalidOperationException(String.Format( - CultureInfo.InvariantCulture, - "Element of type {0} found in enumerator. Must be ChoiceItem or SequenceItem.", - container.GetType().Name)); - } - - return null; - } - } - - /// - /// Resets the enumerator to the beginning. - /// - public void Reset() - { - if (this.collectionStack != null) - { - this.collectionStack.Clear(); - this.collectionStack = null; - } - } - - /// - /// Moves the enumerator to the next item. - /// - /// True if there is a next item, false otherwise. - public bool MoveNext() - { - if (this.collectionStack == null) - { - if (this.collection.Count == 0) - { - return false; - } - - this.collectionStack = new Stack(); - this.collectionStack.Push(new CollectionSymbol(this.collection)); - } - - CollectionSymbol symbol = (CollectionSymbol)this.collectionStack.Peek(); - - if (this.FindNext(symbol)) - { - return true; - } - - this.collectionStack.Pop(); - if (this.collectionStack.Count == 0) - { - return false; - } - - return this.MoveNext(); - } - - /// - /// Pushes a collection onto the stack. - /// - /// The collection to push. - private void PushCollection(ElementCollection collection) - { - if (collection.Count <= 0) - { - throw new ArgumentException(String.Format( - CultureInfo.InvariantCulture, - "Collection has {0} elements. Must have at least one.", - collection.Count)); - } - - CollectionSymbol symbol = new CollectionSymbol(collection); - this.collectionStack.Push(symbol); - this.FindNext(symbol); - } - - /// - /// Finds the next item from a given symbol. - /// - /// The symbol to start looking from. - /// True if a next element is found, false otherwise. - private bool FindNext(CollectionSymbol symbol) - { - object container = symbol.Collection.items[symbol.ContainerIndex]; - - CollectionItem collectionItem = container as CollectionItem; - if (collectionItem != null) - { - if (symbol.ItemIndex + 1 < collectionItem.Elements.Count) - { - symbol.ItemIndex++; - return true; - } - } - - ElementCollection elementCollection = container as ElementCollection; - if (elementCollection != null && elementCollection.Count > 0 && symbol.ItemIndex == -1) - { - symbol.ItemIndex++; - this.PushCollection(elementCollection); - return true; - } - - symbol.ItemIndex = 0; - - for (int i = symbol.ContainerIndex + 1; i < symbol.Collection.items.Count; ++i) - { - object nestedContainer = symbol.Collection.items[i]; - - CollectionItem nestedCollectionItem = nestedContainer as CollectionItem; - if (nestedCollectionItem != null) - { - if (nestedCollectionItem.Elements.Count > 0) - { - symbol.ContainerIndex = i; - return true; - } - } - - ElementCollection nestedElementCollection = nestedContainer as ElementCollection; - if (nestedElementCollection != null && nestedElementCollection.Count > 0) - { - symbol.ContainerIndex = i; - this.PushCollection(nestedElementCollection); - return true; - } - } - - return false; - } - - /// - /// Class representing a single point in the collection. Consists of an ElementCollection, - /// a container index, and an index into the container. - /// - private class CollectionSymbol - { - private ElementCollection collection; - private int containerIndex; - private int itemIndex = -1; - - /// - /// Creates a new CollectionSymbol. - /// - /// The collection for the symbol. - public CollectionSymbol(ElementCollection collection) - { - this.collection = collection; - } - - /// - /// Gets the collection for the symbol. - /// - public ElementCollection Collection - { - get { return this.collection; } - } - - /// - /// Gets and sets the index of the container in the collection. - /// - public int ContainerIndex - { - get { return this.containerIndex; } - set { this.containerIndex = value; } - } - - /// - /// Gets and sets the index of the item in the container. - /// - public int ItemIndex - { - get { return this.itemIndex; } - set { this.itemIndex = value; } - } - } - } - } -} diff --git a/src/WixBuildTools.XsdGen/StronglyTypedClasses.cs b/src/WixBuildTools.XsdGen/StronglyTypedClasses.cs deleted file mode 100644 index 4a41f8a9..00000000 --- a/src/WixBuildTools.XsdGen/StronglyTypedClasses.cs +++ /dev/null @@ -1,1498 +0,0 @@ -// 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. - -namespace WixToolset.Tools -{ - using System; - using System.CodeDom; - using System.Collections; - using System.Collections.Generic; - using System.Collections.Specialized; - using System.IO; - using System.Reflection; - using System.Text; - using System.Text.RegularExpressions; - using System.Xml; - using System.Xml.Schema; - - /// - /// Type containing static Generate method, which fills in a compile unit from a - /// given schema. - /// - internal class StronglyTypedClasses - { - private static string outputXmlComment = "Processes this element and all child elements into an XmlWriter."; - private static Hashtable simpleTypeNamesToClrTypeNames; - private static Dictionary typeNamesToEnumDeclarations; - private static Dictionary enumsToParseMethodClasses; - private static Regex multiUppercaseNameRegex = new Regex("[A-Z][A-Z][A-Z]", RegexOptions.Compiled); - private static Dictionary refToAttributeGroups; - private static CodeTypeDeclaration enumHelperClass; - - /// - /// Private constructor for static class. - /// - private StronglyTypedClasses() - { - } - - /// - /// Generates strongly typed serialization classes for the given schema document - /// under the given namespace and generates a code compile unit. - /// - /// Schema document to generate classes for. - /// Namespace to be used for the generated code. - /// Namespace in which to find common classes and interfaces, - /// like ISchemaElement. - /// A fully populated CodeCompileUnit, which can be serialized in the language of choice. - public static CodeCompileUnit Generate(XmlSchema xmlSchema, string generateNamespace, string commonNamespace) - { - if (xmlSchema == null) - { - throw new ArgumentNullException("xmlSchema"); - } - if (generateNamespace == null) - { - throw new ArgumentNullException("generateNamespace"); - } - - simpleTypeNamesToClrTypeNames = new Hashtable(); - typeNamesToEnumDeclarations = new Dictionary(); - refToAttributeGroups = new Dictionary(); - enumsToParseMethodClasses = new Dictionary(); - - CodeCompileUnit codeCompileUnit = new CodeCompileUnit(); - CodeNamespace codeNamespace = new CodeNamespace(generateNamespace); - codeCompileUnit.Namespaces.Add(codeNamespace); - codeNamespace.Imports.Add(new CodeNamespaceImport("System")); - codeNamespace.Imports.Add(new CodeNamespaceImport("System.CodeDom.Compiler")); // for GeneratedCodeAttribute - codeNamespace.Imports.Add(new CodeNamespaceImport("System.Collections")); - codeNamespace.Imports.Add(new CodeNamespaceImport("System.Diagnostics.CodeAnalysis")); - codeNamespace.Imports.Add(new CodeNamespaceImport("System.Globalization")); - codeNamespace.Imports.Add(new CodeNamespaceImport("System.Xml")); - if (commonNamespace != null) - { - codeNamespace.Imports.Add(new CodeNamespaceImport(commonNamespace)); - } - - // NOTE: This hash table serves double duty so be sure to have the XSD - // type name mapped to the CLR type name *and* the CLR type name - // mapped to the same CLR type name. Look at long and bool for - // examples below (and before you ask, no I don't know why DateTime - // just works). - simpleTypeNamesToClrTypeNames.Add("dateTime", "DateTime"); - simpleTypeNamesToClrTypeNames.Add("integer", "int"); - simpleTypeNamesToClrTypeNames.Add("int", "int"); - simpleTypeNamesToClrTypeNames.Add("NMTOKEN", "string"); - simpleTypeNamesToClrTypeNames.Add("string", "string"); - simpleTypeNamesToClrTypeNames.Add("nonNegativeInteger", "long"); - simpleTypeNamesToClrTypeNames.Add("long", "long"); - simpleTypeNamesToClrTypeNames.Add("boolean", "bool"); - simpleTypeNamesToClrTypeNames.Add("bool", "bool"); - - xmlSchema.Compile(null); - - foreach (XmlSchemaAttributeGroup schemaAttributeGroup in xmlSchema.AttributeGroups.Values) - { - refToAttributeGroups.Add(schemaAttributeGroup.Name, schemaAttributeGroup); - } - - foreach (XmlSchemaObject schemaObject in xmlSchema.SchemaTypes.Values) - { - XmlSchemaSimpleType schemaSimpleType = schemaObject as XmlSchemaSimpleType; - if (schemaSimpleType != null) - { - ProcessSimpleType(schemaSimpleType, codeNamespace); - } - } - - foreach (XmlSchemaObject schemaObject in xmlSchema.SchemaTypes.Values) - { - XmlSchemaComplexType schemaComplexType = schemaObject as XmlSchemaComplexType; - if (schemaComplexType != null) - { - ProcessComplexType(schemaComplexType, codeNamespace); - } - } - - foreach (XmlSchemaObject schemaObject in xmlSchema.Elements.Values) - { - XmlSchemaElement schemaElement = schemaObject as XmlSchemaElement; - if (schemaElement != null) - { - ProcessElement(schemaElement, codeNamespace); - } - } - - return codeCompileUnit; - } - - /// - /// Processes an XmlSchemaElement into corresponding types. - /// - /// XmlSchemaElement to be processed. - /// CodeNamespace to be used when outputting code. - private static void ProcessElement(XmlSchemaElement schemaElement, CodeNamespace codeNamespace) - { - string elementType = schemaElement.SchemaTypeName.Name; - string elementNamespace = schemaElement.QualifiedName.Namespace; - string elementDocumentation = GetDocumentation(schemaElement.Annotation); - - if ((elementType == null || elementType.Length == 0) && schemaElement.SchemaType != null) - { - ProcessComplexType(schemaElement.Name, elementNamespace, (XmlSchemaComplexType)schemaElement.SchemaType, elementDocumentation, codeNamespace); - } - else - { - if (elementType == null || elementType.Length == 0) - { - elementType = "string"; - } - - CodeTypeDeclaration typeDeclaration = new CodeTypeDeclaration(schemaElement.Name); - typeDeclaration.CustomAttributes.Add(GetGeneratedCodeAttribute()); - typeDeclaration.Attributes = MemberAttributes.Public; - typeDeclaration.IsClass = true; - - if (elementDocumentation != null) - { - GenerateSummaryComment(typeDeclaration.Comments, elementDocumentation); - } - - CodeMemberMethod outputXmlMethod = new CodeMemberMethod(); - outputXmlMethod.Attributes = MemberAttributes.Public; - outputXmlMethod.ImplementationTypes.Add("ISchemaElement"); - outputXmlMethod.Name = "OutputXml"; - outputXmlMethod.Parameters.Add(new CodeParameterDeclarationExpression("XmlWriter", "writer")); - outputXmlMethod.Statements.Add(GetArgumentNullCheckStatement("writer", false)); - outputXmlMethod.Statements.Add(new CodeMethodInvokeExpression(new CodeVariableReferenceExpression("writer"), "WriteStartElement", new CodeSnippetExpression(String.Concat("\"", schemaElement.Name, "\"")), new CodeSnippetExpression(String.Concat("\"", elementNamespace, "\"")))); - GenerateSummaryComment(outputXmlMethod.Comments, outputXmlComment); - - if (simpleTypeNamesToClrTypeNames.ContainsKey(elementType)) - { - CodeMemberField parentField = new CodeMemberField("ISchemaElement", "parentElement"); - typeDeclaration.Members.Add(parentField); - - CodeMemberProperty parentProperty = new CodeMemberProperty(); - parentProperty.Attributes = MemberAttributes.Public; - parentProperty.ImplementationTypes.Add("ISchemaElement"); - parentProperty.Name = "ParentElement"; - parentProperty.Type = new CodeTypeReference("ISchemaElement"); - parentProperty.GetStatements.Add(new CodeMethodReturnStatement(new CodeFieldReferenceExpression(new CodeThisReferenceExpression(), "parentElement"))); - parentProperty.SetStatements.Add(new CodeAssignStatement(new CodeFieldReferenceExpression(new CodeThisReferenceExpression(), "parentElement"), new CodeVariableReferenceExpression("value"))); - typeDeclaration.Members.Add(parentProperty); - - CodeMemberMethod setAttributeMethod = new CodeMemberMethod(); - setAttributeMethod.Attributes = MemberAttributes.Public; - setAttributeMethod.ImplementationTypes.Add("ISetAttributes"); - setAttributeMethod.Name = "SetAttribute"; - setAttributeMethod.Parameters.Add(new CodeParameterDeclarationExpression(typeof(string), "name")); - setAttributeMethod.Parameters.Add(new CodeParameterDeclarationExpression(typeof(string), "value")); - setAttributeMethod.PrivateImplementationType = new CodeTypeReference("ISetAttributes"); - setAttributeMethod.CustomAttributes.Add(GetCodeAnalysisSuppressionAttribute("Microsoft.Design", "CA1033:InterfaceMethodsShouldBeCallableByChildTypes")); - setAttributeMethod.Statements.Add(GetArgumentNullCheckStatement("name", true)); - - GenerateFieldAndProperty("Content", (string)simpleTypeNamesToClrTypeNames[elementType], typeDeclaration, outputXmlMethod, setAttributeMethod, null, elementDocumentation, true, false); - - typeDeclaration.Members.Add(setAttributeMethod); - typeDeclaration.BaseTypes.Add(new CodeTypeReference("ISetAttributes")); - } - else - { - typeDeclaration.BaseTypes.Add(elementType); - outputXmlMethod.Statements.Add(new CodeMethodInvokeExpression(new CodeBaseReferenceExpression(), "OutputXml", new CodeVariableReferenceExpression("writer"))); - outputXmlMethod.Attributes |= MemberAttributes.Override; - } - - outputXmlMethod.Statements.Add(new CodeMethodInvokeExpression(new CodeVariableReferenceExpression("writer"), "WriteEndElement")); - - typeDeclaration.BaseTypes.Add(new CodeTypeReference("ISchemaElement")); - typeDeclaration.Members.Add(outputXmlMethod); - codeNamespace.Types.Add(typeDeclaration); - } - } - - /// - /// Processes an XmlSchemaComplexType into corresponding types. - /// - /// XmlSchemaComplexType to be processed. - /// CodeNamespace to be used when outputting code. - private static void ProcessComplexType(XmlSchemaComplexType complexType, CodeNamespace codeNamespace) - { - CodeMemberMethod outputXmlMethod = new CodeMemberMethod(); - outputXmlMethod.Attributes = MemberAttributes.Public; - outputXmlMethod.ImplementationTypes.Add("ISchemaElement"); - outputXmlMethod.Name = "OutputXml"; - outputXmlMethod.Parameters.Add(new CodeParameterDeclarationExpression("XmlWriter", "writer")); - outputXmlMethod.Statements.Add(GetArgumentNullCheckStatement("writer", false)); - GenerateSummaryComment(outputXmlMethod.Comments, outputXmlComment); - - CodeMemberMethod setAttributeMethod = new CodeMemberMethod(); - setAttributeMethod.Attributes = MemberAttributes.Public; - setAttributeMethod.ImplementationTypes.Add("ISetAttributes"); - setAttributeMethod.Name = "SetAttribute"; - setAttributeMethod.Parameters.Add(new CodeParameterDeclarationExpression(typeof(string), "name")); - setAttributeMethod.Parameters.Add(new CodeParameterDeclarationExpression(typeof(string), "value")); - setAttributeMethod.PrivateImplementationType = new CodeTypeReference("ISetAttributes"); - setAttributeMethod.CustomAttributes.Add(GetCodeAnalysisSuppressionAttribute("Microsoft.Design", "CA1033:InterfaceMethodsShouldBeCallableByChildTypes")); - setAttributeMethod.Statements.Add(GetArgumentNullCheckStatement("name", true)); - - string documentation = GetDocumentation(complexType.Annotation); - - ProcessSimpleContent(complexType.Name, (XmlSchemaSimpleContentExtension)complexType.ContentModel.Content, documentation, codeNamespace, outputXmlMethod, setAttributeMethod, true); - } - - /// - /// Processes an XmlSchemaComplexType into corresponding types. - /// - /// Name to use for the type being output. - /// Namespace of the xml element. - /// XmlSchemaComplexType to be processed. - /// Documentation for the element. - /// CodeNamespace to be used when outputting code. - private static void ProcessComplexType(string typeName, string elementNamespace, XmlSchemaComplexType complexType, string documentation, CodeNamespace codeNamespace) - { - CodeMemberMethod outputXmlMethod = new CodeMemberMethod(); - outputXmlMethod.Attributes = MemberAttributes.Public; - outputXmlMethod.ImplementationTypes.Add("ISchemaElement"); - outputXmlMethod.Name = "OutputXml"; - outputXmlMethod.Parameters.Add(new CodeParameterDeclarationExpression("XmlWriter", "writer")); - outputXmlMethod.Statements.Add(GetArgumentNullCheckStatement("writer", false)); - outputXmlMethod.Statements.Add(new CodeMethodInvokeExpression(new CodeVariableReferenceExpression("writer"), "WriteStartElement", new CodeSnippetExpression(String.Concat("\"", typeName, "\"")), new CodeSnippetExpression(String.Concat("\"", elementNamespace, "\"")))); - GenerateSummaryComment(outputXmlMethod.Comments, outputXmlComment); - - CodeMemberMethod setAttributeMethod = new CodeMemberMethod(); - setAttributeMethod.Attributes = MemberAttributes.Public; - setAttributeMethod.ImplementationTypes.Add("ISetAttributes"); - setAttributeMethod.Name = "SetAttribute"; - setAttributeMethod.Parameters.Add(new CodeParameterDeclarationExpression(typeof(string), "name")); - setAttributeMethod.Parameters.Add(new CodeParameterDeclarationExpression(typeof(string), "value")); - setAttributeMethod.PrivateImplementationType = new CodeTypeReference("ISetAttributes"); - setAttributeMethod.CustomAttributes.Add(GetCodeAnalysisSuppressionAttribute("Microsoft.Design", "CA1033:InterfaceMethodsShouldBeCallableByChildTypes")); - setAttributeMethod.Statements.Add(GetArgumentNullCheckStatement("name", true)); - - if (complexType.ContentModel == null) - { - CodeTypeDeclaration typeDeclaration = new CodeTypeDeclaration(typeName); - typeDeclaration.CustomAttributes.Add(GetGeneratedCodeAttribute()); - typeDeclaration.Attributes = MemberAttributes.Public; - typeDeclaration.IsClass = true; - CodeIterationStatement childEnumStatement = null; - - if (documentation != null) - { - GenerateSummaryComment(typeDeclaration.Comments, documentation); - } - - if (complexType.Particle != null) - { - CodeMemberField childrenField = new CodeMemberField("ElementCollection", "children"); - typeDeclaration.Members.Add(childrenField); - - CodeMemberProperty childrenProperty = new CodeMemberProperty(); - childrenProperty.Attributes = MemberAttributes.Public; - childrenProperty.ImplementationTypes.Add("IParentElement"); - childrenProperty.Name = "Children"; - childrenProperty.Type = new CodeTypeReference("IEnumerable"); - childrenProperty.GetStatements.Add(new CodeMethodReturnStatement(new CodeFieldReferenceExpression(new CodeThisReferenceExpression(), "children"))); - typeDeclaration.Members.Add(childrenProperty); - - CodeMemberProperty filterChildrenProperty = new CodeMemberProperty(); - filterChildrenProperty.Attributes = MemberAttributes.Public; - filterChildrenProperty.ImplementationTypes.Add("IParentElement"); - filterChildrenProperty.Name = "Item"; - filterChildrenProperty.Parameters.Add(new CodeParameterDeclarationExpression(typeof(Type), "childType")); - filterChildrenProperty.Type = new CodeTypeReference("IEnumerable"); - filterChildrenProperty.GetStatements.Add(new CodeMethodReturnStatement(new CodeMethodInvokeExpression(new CodeFieldReferenceExpression(new CodeThisReferenceExpression(), "children"), "Filter", new CodeVariableReferenceExpression("childType")))); - filterChildrenProperty.CustomAttributes.Add(GetCodeAnalysisSuppressionAttribute("Microsoft.Design", "CA1043:UseIntegralOrStringArgumentForIndexers")); - typeDeclaration.Members.Add(filterChildrenProperty); - - CodeMemberMethod addChildMethod = new CodeMemberMethod(); - addChildMethod.Attributes = MemberAttributes.Public; - addChildMethod.ImplementationTypes.Add("IParentElement"); - addChildMethod.Name = "AddChild"; - addChildMethod.Parameters.Add(new CodeParameterDeclarationExpression("ISchemaElement", "child")); - addChildMethod.Statements.Add(GetArgumentNullCheckStatement("child", false)); - CodeExpressionStatement addChildStatement = new CodeExpressionStatement(new CodeMethodInvokeExpression(new CodeFieldReferenceExpression(new CodeThisReferenceExpression(), "children"), "AddElement", new CodeVariableReferenceExpression("child"))); - addChildMethod.Statements.Add(addChildStatement); - CodeAssignStatement setParentStatement = new CodeAssignStatement(new CodePropertyReferenceExpression(new CodeVariableReferenceExpression("child"), "ParentElement"), new CodeThisReferenceExpression()); - addChildMethod.Statements.Add(setParentStatement); - typeDeclaration.Members.Add(addChildMethod); - - CodeMemberMethod removeChildMethod = new CodeMemberMethod(); - removeChildMethod.Attributes = MemberAttributes.Public; - removeChildMethod.ImplementationTypes.Add("IParentElement"); - removeChildMethod.Name = "RemoveChild"; - removeChildMethod.Parameters.Add(new CodeParameterDeclarationExpression("ISchemaElement", "child")); - removeChildMethod.Statements.Add(GetArgumentNullCheckStatement("child", false)); - CodeExpressionStatement removeChildStatement = new CodeExpressionStatement(new CodeMethodInvokeExpression(new CodeFieldReferenceExpression(new CodeThisReferenceExpression(), "children"), "RemoveElement", new CodeVariableReferenceExpression("child"))); - removeChildMethod.Statements.Add(removeChildStatement); - CodeAssignStatement nullParentStatement = new CodeAssignStatement(new CodePropertyReferenceExpression(new CodeVariableReferenceExpression("child"), "ParentElement"), new CodePrimitiveExpression(null)); - removeChildMethod.Statements.Add(nullParentStatement); - typeDeclaration.Members.Add(removeChildMethod); - - CodeMemberMethod createChildMethod = new CodeMemberMethod(); - createChildMethod.Attributes = MemberAttributes.Public; - createChildMethod.ImplementationTypes.Add("ICreateChildren"); - createChildMethod.Name = "CreateChild"; - createChildMethod.Parameters.Add(new CodeParameterDeclarationExpression(typeof(string), "childName")); - createChildMethod.PrivateImplementationType = new CodeTypeReference("ICreateChildren"); - createChildMethod.CustomAttributes.Add(GetCodeAnalysisSuppressionAttribute("Microsoft.Design", "CA1033:InterfaceMethodsShouldBeCallableByChildTypes")); - createChildMethod.ReturnType = new CodeTypeReference("ISchemaElement"); - createChildMethod.Statements.Add(GetArgumentNullCheckStatement("childName", true)); - createChildMethod.Statements.Add(new CodeVariableDeclarationStatement("ISchemaElement", "childValue", new CodePrimitiveExpression(null))); - - CodeConstructor typeConstructor = new CodeConstructor(); - typeConstructor.Attributes = MemberAttributes.Public; - - CodeVariableReferenceExpression collectionVariable = null; - - XmlSchemaChoice schemaChoice = complexType.Particle as XmlSchemaChoice; - if (schemaChoice != null) - { - collectionVariable = ProcessSchemaGroup(schemaChoice, typeConstructor, createChildMethod); - } - else - { - XmlSchemaSequence schemaSequence = complexType.Particle as XmlSchemaSequence; - if (schemaSequence != null) - { - collectionVariable = ProcessSchemaGroup(schemaSequence, typeConstructor, createChildMethod); - } - } - - typeConstructor.Statements.Add(new CodeAssignStatement(new CodeFieldReferenceExpression(new CodeThisReferenceExpression(), "children"), collectionVariable)); - typeDeclaration.Members.Add(typeConstructor); - - CodeConditionStatement childNameNotFound = new CodeConditionStatement(); - childNameNotFound.Condition = new CodeBinaryOperatorExpression(new CodePrimitiveExpression(null), CodeBinaryOperatorType.ValueEquality, new CodeVariableReferenceExpression("childValue")); - 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.\""))))); - createChildMethod.Statements.Add(childNameNotFound); - - if (createChildMethod.Statements.Count > 8) - { - createChildMethod.CustomAttributes.Add(GetCodeAnalysisSuppressionAttribute("Microsoft.Maintainability", "CA1502:AvoidExcessiveComplexity")); - } - - createChildMethod.Statements.Add(new CodeMethodReturnStatement(new CodeVariableReferenceExpression("childValue"))); - typeDeclaration.Members.Add(createChildMethod); - - childEnumStatement = new CodeIterationStatement(); - childEnumStatement.InitStatement = new CodeVariableDeclarationStatement("IEnumerator", "enumerator", new CodeMethodInvokeExpression(new CodeFieldReferenceExpression(new CodeThisReferenceExpression(), "children"), "GetEnumerator")); - childEnumStatement.TestExpression = new CodeMethodInvokeExpression(new CodeVariableReferenceExpression("enumerator"), "MoveNext"); - childEnumStatement.Statements.Add(new CodeVariableDeclarationStatement("ISchemaElement", "childElement", new CodeCastExpression("ISchemaElement", new CodePropertyReferenceExpression(new CodeVariableReferenceExpression("enumerator"), "Current")))); - childEnumStatement.Statements.Add(new CodeMethodInvokeExpression(new CodeVariableReferenceExpression("childElement"), "OutputXml", new CodeVariableReferenceExpression("writer"))); - childEnumStatement.IncrementStatement = new CodeExpressionStatement(new CodeSnippetExpression("")); - - typeDeclaration.BaseTypes.Add(new CodeTypeReference("IParentElement")); - typeDeclaration.BaseTypes.Add(new CodeTypeReference("ICreateChildren")); - } - - // TODO: Handle xs:anyAttribute. - ProcessAttributes(complexType.Attributes, typeDeclaration, outputXmlMethod, setAttributeMethod); - - if (childEnumStatement != null) - { - outputXmlMethod.Statements.Add(childEnumStatement); - } - - typeDeclaration.BaseTypes.Add(new CodeTypeReference("ISchemaElement")); - typeDeclaration.BaseTypes.Add(new CodeTypeReference("ISetAttributes")); - - CodeMemberField parentField = new CodeMemberField("ISchemaElement", "parentElement"); - typeDeclaration.Members.Add(parentField); - - CodeMemberProperty parentProperty = new CodeMemberProperty(); - parentProperty.Attributes = MemberAttributes.Public; - parentProperty.ImplementationTypes.Add("ISchemaElement"); - parentProperty.Name = "ParentElement"; - parentProperty.Type = new CodeTypeReference("ISchemaElement"); - parentProperty.GetStatements.Add(new CodeMethodReturnStatement(new CodeFieldReferenceExpression(new CodeThisReferenceExpression(), "parentElement"))); - parentProperty.SetStatements.Add(new CodeAssignStatement(new CodeFieldReferenceExpression(new CodeThisReferenceExpression(), "parentElement"), new CodeVariableReferenceExpression("value"))); - typeDeclaration.Members.Add(parentProperty); - - if (outputXmlMethod.Statements.Count > 8) - { - outputXmlMethod.CustomAttributes.Add(GetCodeAnalysisSuppressionAttribute("Microsoft.Maintainability", "CA1502:AvoidExcessiveComplexity")); - } - - if (setAttributeMethod.Statements.Count > 8) - { - setAttributeMethod.CustomAttributes.Add(GetCodeAnalysisSuppressionAttribute("Microsoft.Maintainability", "CA1502:AvoidExcessiveComplexity")); - } - - typeDeclaration.Members.Add(outputXmlMethod); - typeDeclaration.Members.Add(setAttributeMethod); - codeNamespace.Types.Add(typeDeclaration); - } - else - { - ProcessSimpleContent(typeName, (XmlSchemaSimpleContentExtension)complexType.ContentModel.Content, documentation, codeNamespace, outputXmlMethod, setAttributeMethod, false); - } - - outputXmlMethod.Statements.Add(new CodeMethodInvokeExpression(new CodeVariableReferenceExpression("writer"), "WriteEndElement")); - } - - /// - /// Processes a collection of attributes, generating the required fields and properties. - /// - /// List of attribute or attributeGroupRef elements being processed. - /// CodeTypeDeclaration to be used when outputting code. - /// Member method for the OutputXml method. - /// Member method for the SetAttribute method. - private static void ProcessAttributes(XmlSchemaObjectCollection attributes, CodeTypeDeclaration typeDeclaration, CodeMemberMethod outputXmlMethod, CodeMemberMethod setAttributeMethod) - { - foreach (XmlSchemaObject schemaObject in attributes) - { - XmlSchemaAttribute schemaAttribute = schemaObject as XmlSchemaAttribute; - if (schemaAttribute != null) - { - ProcessAttribute(schemaAttribute, typeDeclaration, outputXmlMethod, setAttributeMethod); - } - else - { - XmlSchemaAttributeGroupRef schemaAttributeGroupRef = schemaObject as XmlSchemaAttributeGroupRef; - if (schemaAttributeGroupRef != null) - { - XmlSchemaAttributeGroup schemaAttributeGroup = refToAttributeGroups[schemaAttributeGroupRef.RefName.Name]; - // recurse! - ProcessAttributes(schemaAttributeGroup.Attributes, typeDeclaration, outputXmlMethod, setAttributeMethod); - } - } - } - } - - /// - /// Processes an XmlSchemaGroupBase element. - /// - /// Element group to process. - /// Constructor to which statements should be added. - /// Method used for creating children on read-in. - /// A reference to the local variable containing the collection. - private static CodeVariableReferenceExpression ProcessSchemaGroup(XmlSchemaGroupBase schemaGroup, CodeConstructor constructor, CodeMemberMethod createChildMethod) - { - return ProcessSchemaGroup(schemaGroup, constructor, createChildMethod, 0); - } - - /// - /// Processes an XmlSchemaGroupBase element. - /// - /// Element group to process. - /// Constructor to which statements should be added. - /// Method used for creating children on read-in. - /// Depth to which this collection is nested. - /// A reference to the local variable containing the collection. - private static CodeVariableReferenceExpression ProcessSchemaGroup(XmlSchemaGroupBase schemaGroup, CodeConstructor constructor, CodeMemberMethod createChildMethod, int depth) - { - string collectionName = String.Format("childCollection{0}", depth); - CodeVariableReferenceExpression collectionVariableReference = new CodeVariableReferenceExpression(collectionName); - CodeVariableDeclarationStatement collectionStatement = new CodeVariableDeclarationStatement("ElementCollection", collectionName); - if (schemaGroup is XmlSchemaChoice) - { - collectionStatement.InitExpression = new CodeObjectCreateExpression("ElementCollection", new CodePropertyReferenceExpression(new CodeTypeReferenceExpression("ElementCollection.CollectionType"), "Choice")); - } - else - { - collectionStatement.InitExpression = new CodeObjectCreateExpression("ElementCollection", new CodePropertyReferenceExpression(new CodeTypeReferenceExpression("ElementCollection.CollectionType"), "Sequence")); - } - constructor.Statements.Add(collectionStatement); - - foreach (XmlSchemaObject obj in schemaGroup.Items) - { - XmlSchemaElement schemaElement = obj as XmlSchemaElement; - if (schemaElement != null) - { - if (schemaGroup is XmlSchemaChoice) - { - CodeMethodInvokeExpression addItemInvoke = new CodeMethodInvokeExpression(collectionVariableReference, "AddItem", new CodeObjectCreateExpression("ElementCollection.ChoiceItem", new CodeTypeOfExpression(schemaElement.RefName.Name))); - constructor.Statements.Add(addItemInvoke); - } - else - { - CodeMethodInvokeExpression addItemInvoke = new CodeMethodInvokeExpression(collectionVariableReference, "AddItem", new CodeObjectCreateExpression("ElementCollection.SequenceItem", new CodeTypeOfExpression(schemaElement.RefName.Name))); - constructor.Statements.Add(addItemInvoke); - } - - CodeConditionStatement createChildIf = new CodeConditionStatement(); - createChildIf.Condition = new CodeBinaryOperatorExpression(new CodeSnippetExpression(String.Concat("\"", schemaElement.RefName.Name, "\"")), CodeBinaryOperatorType.ValueEquality, new CodeVariableReferenceExpression("childName")); - createChildIf.TrueStatements.Add(new CodeAssignStatement(new CodeVariableReferenceExpression("childValue"), new CodeObjectCreateExpression(schemaElement.RefName.Name))); - createChildMethod.Statements.Add(createChildIf); - - continue; - } - - XmlSchemaAny schemaAny = obj as XmlSchemaAny; - if (schemaAny != null) - { - if (schemaGroup is XmlSchemaChoice) - { - CodeMethodInvokeExpression addItemInvoke = new CodeMethodInvokeExpression(collectionVariableReference, "AddItem", new CodeObjectCreateExpression("ElementCollection.ChoiceItem", new CodeTypeOfExpression("ISchemaElement"))); - constructor.Statements.Add(addItemInvoke); - } - else - { - CodeMethodInvokeExpression addItemInvoke = new CodeMethodInvokeExpression(collectionVariableReference, "AddItem", new CodeObjectCreateExpression("ElementCollection.SequenceItem", new CodeTypeOfExpression("ISchemaElement"), new CodeSnippetExpression("0"), new CodeSnippetExpression("-1"))); - constructor.Statements.Add(addItemInvoke); - } - - continue; - } - - XmlSchemaGroupBase schemaGroupBase = obj as XmlSchemaGroupBase; - if (schemaGroupBase != null) - { - CodeVariableReferenceExpression nestedCollectionReference = ProcessSchemaGroup(schemaGroupBase, constructor, createChildMethod, depth + 1); - CodeMethodInvokeExpression addCollectionInvoke = new CodeMethodInvokeExpression(collectionVariableReference, "AddCollection", nestedCollectionReference); - constructor.Statements.Add(addCollectionInvoke); - - continue; - } - } - - return collectionVariableReference; - } - - /// - /// Processes an XmlSchemaSimpleContentExtension into corresponding types. - /// - /// Name of the type being generated. - /// XmlSchemaSimpleContentExtension being processed. - /// Documentation for the simple content. - /// CodeNamespace to be used when outputting code. - /// Method to use when outputting Xml. - /// Method to use when setting an attribute. - /// If true, generate an abstract class. - private static void ProcessSimpleContent(string typeName, XmlSchemaSimpleContentExtension simpleContent, string documentation, CodeNamespace codeNamespace, CodeMemberMethod outputXmlMethod, CodeMemberMethod setAttributeMethod, bool abstractClass) - { - CodeTypeDeclaration typeDeclaration = new CodeTypeDeclaration(typeName); - typeDeclaration.CustomAttributes.Add(GetGeneratedCodeAttribute()); - typeDeclaration.Attributes = MemberAttributes.Public; - typeDeclaration.IsClass = true; - - if (documentation != null) - { - GenerateSummaryComment(typeDeclaration.Comments, documentation); - } - - if (abstractClass) - { - typeDeclaration.TypeAttributes = System.Reflection.TypeAttributes.Abstract | System.Reflection.TypeAttributes.Public; - } - - // TODO: Handle xs:anyAttribute here. - foreach (XmlSchemaAttribute schemaAttribute in simpleContent.Attributes) - { - ProcessAttribute(schemaAttribute, typeDeclaration, outputXmlMethod, setAttributeMethod); - } - - // This needs to come last, so that the generation code generates the inner content after the attributes. - string contentDocumentation = GetDocumentation(simpleContent.Annotation); - GenerateFieldAndProperty("Content", (string)simpleTypeNamesToClrTypeNames[simpleContent.BaseTypeName.Name], typeDeclaration, outputXmlMethod, setAttributeMethod, null, contentDocumentation, true, false); - - typeDeclaration.BaseTypes.Add(new CodeTypeReference("ISchemaElement")); - typeDeclaration.BaseTypes.Add(new CodeTypeReference("ISetAttributes")); - - CodeMemberField parentField = new CodeMemberField("ISchemaElement", "parentElement"); - typeDeclaration.Members.Add(parentField); - - CodeMemberProperty parentProperty = new CodeMemberProperty(); - parentProperty.Attributes = MemberAttributes.Public; - parentProperty.ImplementationTypes.Add("ISchemaElement"); - parentProperty.Name = "ParentElement"; - parentProperty.Type = new CodeTypeReference("ISchemaElement"); - parentProperty.GetStatements.Add(new CodeMethodReturnStatement(new CodeFieldReferenceExpression(new CodeThisReferenceExpression(), "parentElement"))); - parentProperty.SetStatements.Add(new CodeAssignStatement(new CodeFieldReferenceExpression(new CodeThisReferenceExpression(), "parentElement"), new CodeVariableReferenceExpression("value"))); - typeDeclaration.Members.Add(parentProperty); - - if (outputXmlMethod.Statements.Count > 8) - { - outputXmlMethod.CustomAttributes.Add(GetCodeAnalysisSuppressionAttribute("Microsoft.Maintainability", "CA1502:AvoidExcessiveComplexity")); - } - - if (setAttributeMethod.Statements.Count > 8) - { - setAttributeMethod.CustomAttributes.Add(GetCodeAnalysisSuppressionAttribute("Microsoft.Maintainability", "CA1502:AvoidExcessiveComplexity")); - } - - typeDeclaration.Members.Add(outputXmlMethod); - typeDeclaration.Members.Add(setAttributeMethod); - codeNamespace.Types.Add(typeDeclaration); - } - - /// - /// Processes an attribute, generating the required field and property. Potentially generates - /// an enum for an attribute restriction. - /// - /// Attribute element being processed. - /// CodeTypeDeclaration to be used when outputting code. - /// Member method for the OutputXml method. - /// Member method for the SetAttribute method. - private static void ProcessAttribute(XmlSchemaAttribute attribute, CodeTypeDeclaration typeDeclaration, CodeMemberMethod outputXmlMethod, CodeMemberMethod setAttributeMethod) - { - string attributeName = attribute.QualifiedName.Name; - string rawAttributeType = attribute.AttributeSchemaType.QualifiedName.Name; - string attributeType = null; - EnumDeclaration enumDeclaration = null; - if (rawAttributeType == null || rawAttributeType.Length == 0) - { - ProcessSimpleType(attributeName, attribute.AttributeSchemaType, true, out enumDeclaration, out attributeType); - - if (enumDeclaration != null) - { - typeDeclaration.Members.Add(enumDeclaration.TypeDeclaration); - AddEnumHelperMethods(enumDeclaration, typeDeclaration); - } - } - else - { - attributeType = (string)simpleTypeNamesToClrTypeNames[rawAttributeType]; - } - - string documentation = GetDocumentation(attribute.Annotation); - - // TODO: Handle required fields. - GenerateFieldAndProperty(attributeName, attributeType, typeDeclaration, outputXmlMethod, setAttributeMethod, enumDeclaration, documentation, false, false); - } - - /// - /// Gets the first sentence of a documentation element and returns it as a string. - /// - /// The annotation in which to look for a documentation element. - /// The string representing the first sentence, or null if none found. - private static string GetDocumentation(XmlSchemaAnnotation annotation) - { - string documentation = null; - - if (annotation != null && annotation.Items != null) - { - foreach (XmlSchemaObject obj in annotation.Items) - { - XmlSchemaDocumentation schemaDocumentation = obj as XmlSchemaDocumentation; - if (schemaDocumentation != null) - { - if (schemaDocumentation.Markup.Length > 0) - { - XmlText text = schemaDocumentation.Markup[0] as XmlText; - if (text != null) - { - documentation = text.Value; - } - } - break; - } - } - } - - if (documentation != null) - { - documentation = documentation.Trim(); - } - return documentation; - } - - /// - /// Makes a valid enum value out of the passed in value. May remove spaces, add 'Item' to the - /// start if it begins with an integer, or strip out punctuation. - /// - /// Enum value to be processed. - /// Enum value with invalid characters removed. - private static string MakeEnumValue(string enumValue) - { - if (Char.IsDigit(enumValue[0])) - { - enumValue = String.Concat("Item", enumValue); - } - - StringBuilder newValue = new StringBuilder(); - for (int i = 0; i < enumValue.Length; ++i) - { - if (!Char.IsPunctuation(enumValue[i]) && !Char.IsSymbol(enumValue[i]) && !Char.IsWhiteSpace(enumValue[i])) - { - newValue.Append(enumValue[i]); - } - } - - return newValue.ToString(); - } - - /// - /// Generates the private field and public property for a piece of data. - /// - /// Name of the property being generated. - /// Name of the type for the property. - /// Type declaration into which the field and property should be placed. - /// Member method for the OutputXml method. - /// Member method for the SetAttribute method. - /// EnumDeclaration, which is null unless called from a locally defined enum attribute. - /// Comment string to be placed on the property. - /// If true, the field will be placed in nested content when outputting to XML. - /// If true, the generated serialization code will throw if the field is not set. - private static void GenerateFieldAndProperty(string propertyName, string typeName, CodeTypeDeclaration typeDeclaration, CodeMemberMethod outputXmlMethod, CodeMemberMethod setAttributeMethod, EnumDeclaration enumDeclaration, string documentation, bool nestedContent, bool requiredField) - { - string fieldName = String.Concat(propertyName.Substring(0, 1).ToLower(), propertyName.Substring(1), "Field"); - string fieldNameSet = String.Concat(fieldName, "Set"); - Type type = GetClrTypeByXmlName(typeName); - CodeMemberField fieldMember; - if (type == null) - { - fieldMember = new CodeMemberField(typeName, fieldName); - } - else - { - fieldMember = new CodeMemberField(type, fieldName); - } - fieldMember.Attributes = MemberAttributes.Private; - typeDeclaration.Members.Add(fieldMember); - typeDeclaration.Members.Add(new CodeMemberField(typeof(bool), fieldNameSet)); - - CodeMemberProperty propertyMember = new CodeMemberProperty(); - propertyMember.Attributes = MemberAttributes.Public | MemberAttributes.Final; - if (documentation != null) - { - GenerateSummaryComment(propertyMember.Comments, documentation); - } - propertyMember.Name = propertyName; - if (type == null) - { - propertyMember.Type = new CodeTypeReference(typeName); - } - else - { - propertyMember.Type = new CodeTypeReference(type); - } - - if (propertyMember.Name.StartsWith("src")) - { - propertyMember.CustomAttributes.Add(GetCodeAnalysisSuppressionAttribute("Microsoft.Naming", "CA1709:IdentifiersShouldBeCasedCorrectly")); - } - else if (StronglyTypedClasses.multiUppercaseNameRegex.Match(propertyMember.Name).Success) - { - propertyMember.CustomAttributes.Add(GetCodeAnalysisSuppressionAttribute("Microsoft.Naming", "CA1705:LongAcronymsShouldBePascalCased")); - } - - CodeMethodReturnStatement returnStatement = new CodeMethodReturnStatement(); - returnStatement.Expression = new CodeFieldReferenceExpression(new CodeThisReferenceExpression(), fieldName); - propertyMember.GetStatements.Add(returnStatement); - - CodeAssignStatement assignmentStatement = new CodeAssignStatement(); - propertyMember.SetStatements.Add(new CodeAssignStatement(new CodeFieldReferenceExpression(new CodeThisReferenceExpression(), fieldNameSet), new CodePrimitiveExpression(true))); - assignmentStatement.Left = new CodeFieldReferenceExpression(new CodeThisReferenceExpression(), fieldName); - assignmentStatement.Right = new CodePropertySetValueReferenceExpression(); - propertyMember.SetStatements.Add(assignmentStatement); - - CodeConditionStatement fieldSetStatement = new CodeConditionStatement(); - fieldSetStatement.Condition = new CodeFieldReferenceExpression(new CodeThisReferenceExpression(), fieldNameSet); - - CodeAssignStatement fieldSetAttrStatement = new CodeAssignStatement(); - fieldSetAttrStatement.Left = new CodeFieldReferenceExpression(new CodeThisReferenceExpression(), fieldNameSet); - fieldSetAttrStatement.Right = new CodePrimitiveExpression(true); - - CodeConditionStatement attributeNameMatchStatement = new CodeConditionStatement(); - attributeNameMatchStatement.Condition = new CodeBinaryOperatorExpression(new CodeSnippetExpression(String.Concat("\"", propertyName, "\"")), CodeBinaryOperatorType.IdentityEquality, new CodeVariableReferenceExpression("name")); - - string clrTypeName = (string)simpleTypeNamesToClrTypeNames[typeName]; - switch (clrTypeName) - { - case "string": - if (nestedContent) - { - fieldSetStatement.TrueStatements.Add(new CodeMethodInvokeExpression(new CodeVariableReferenceExpression("writer"), "WriteString", new CodeFieldReferenceExpression(new CodeThisReferenceExpression(), fieldName))); - attributeNameMatchStatement.TrueStatements.Add(new CodeAssignStatement(new CodeFieldReferenceExpression(new CodeThisReferenceExpression(), fieldName), new CodeVariableReferenceExpression("value"))); - } - else - { - fieldSetStatement.TrueStatements.Add(new CodeMethodInvokeExpression(new CodeVariableReferenceExpression("writer"), "WriteAttributeString", new CodeSnippetExpression(String.Concat("\"", propertyName, "\"")), new CodeFieldReferenceExpression(new CodeThisReferenceExpression(), fieldName))); - attributeNameMatchStatement.TrueStatements.Add(new CodeAssignStatement(new CodeFieldReferenceExpression(new CodeThisReferenceExpression(), fieldName), new CodeVariableReferenceExpression("value"))); - } - break; - case "bool": - if (nestedContent) - { - fieldSetStatement.TrueStatements.Add(new CodeMethodInvokeExpression(new CodeVariableReferenceExpression("writer"), "WriteString", new CodeMethodInvokeExpression(new CodeFieldReferenceExpression(new CodeThisReferenceExpression(), fieldName), "ToString", new CodePropertyReferenceExpression(new CodeTypeReferenceExpression("CultureInfo"), "InvariantCulture")))); - } - else - { - 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")))); - } - 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")))); - break; - case "int": - case "long": - if (nestedContent) - { - fieldSetStatement.TrueStatements.Add(new CodeMethodInvokeExpression(new CodeVariableReferenceExpression("writer"), "WriteString", new CodeMethodInvokeExpression(new CodeFieldReferenceExpression(new CodeThisReferenceExpression(), fieldName), "ToString", new CodePropertyReferenceExpression(new CodeTypeReferenceExpression("CultureInfo"), "InvariantCulture")))); - } - else - { - 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")))); - } - 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")))); - break; - default: - if (typeName == "DateTime") - { - if (nestedContent) - { - fieldSetStatement.TrueStatements.Add(new CodeMethodInvokeExpression(new CodeVariableReferenceExpression("writer"), "WriteString", new CodeMethodInvokeExpression(new CodeFieldReferenceExpression(new CodeThisReferenceExpression(), fieldName), "ToString", new CodePropertyReferenceExpression(new CodeTypeReferenceExpression("CultureInfo"), "InvariantCulture")))); - } - else - { - 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")))); - } - 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")))); - break; - } - - if (enumDeclaration == null) - { - GenerateOutputForEnum(fieldSetStatement, attributeNameMatchStatement, typeNamesToEnumDeclarations[typeName], fieldName, propertyName); - } - else - { - GenerateOutputForEnum(fieldSetStatement, attributeNameMatchStatement, enumDeclaration, fieldName, propertyName); - } - break; - } - - attributeNameMatchStatement.TrueStatements.Add(fieldSetAttrStatement); - - // TODO: Add throw to falseStatements if required field not set. - outputXmlMethod.Statements.Add(fieldSetStatement); - setAttributeMethod.Statements.Add(attributeNameMatchStatement); - - typeDeclaration.Members.Add(propertyMember); - } - - /// - /// Generates output for an enum type. Will generate a switch statement for normal enums, and if statements - /// for a flags enum. - /// - /// If statement to add statements to. - /// If statement to add statements to. - /// Enum declaration for this field. Could be locally defined enum or global. - /// Name of the private field. - /// Name of the property (and XML attribute). - private static void GenerateOutputForEnum(CodeConditionStatement fieldSetStatement, CodeConditionStatement attributeNameMatchStatement, EnumDeclaration enumDeclaration, string fieldName, string propertyName) - { - CodeTypeDeclaration enumParent = enumsToParseMethodClasses[enumDeclaration]; - - if (enumDeclaration.Flags) - { - CodeVariableDeclarationStatement outputValueVariable = new CodeVariableDeclarationStatement(typeof(string), "outputValue", new CodeSnippetExpression("\"\"")); - fieldSetStatement.TrueStatements.Add(outputValueVariable); - - foreach (string key in enumDeclaration.Values) - { - CodeConditionStatement enumIfStatement = new CodeConditionStatement(); - 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")); - CodeConditionStatement lengthIfStatement = new CodeConditionStatement(); - lengthIfStatement.Condition = new CodeBinaryOperatorExpression(new CodePropertyReferenceExpression(new CodeVariableReferenceExpression("outputValue"), "Length"), CodeBinaryOperatorType.IdentityInequality, new CodeSnippetExpression("0")); - lengthIfStatement.TrueStatements.Add(new CodeAssignStatement(new CodeVariableReferenceExpression("outputValue"), new CodeBinaryOperatorExpression(new CodeVariableReferenceExpression("outputValue"), CodeBinaryOperatorType.Add, new CodeSnippetExpression("\" \"")))); - enumIfStatement.TrueStatements.Add(lengthIfStatement); - enumIfStatement.TrueStatements.Add(new CodeAssignStatement(new CodeVariableReferenceExpression("outputValue"), new CodeBinaryOperatorExpression(new CodeVariableReferenceExpression("outputValue"), CodeBinaryOperatorType.Add, new CodeSnippetExpression(String.Concat("\"", key, "\""))))); - fieldSetStatement.TrueStatements.Add(enumIfStatement); - } - - attributeNameMatchStatement.TrueStatements.Add(new CodeMethodInvokeExpression( - new CodeTypeReferenceExpression(enumParent.Name), - String.Concat("TryParse", enumDeclaration.Name), - new CodeVariableReferenceExpression("value"), - new CodeDirectionExpression(FieldDirection.Out, new CodeFieldReferenceExpression(new CodeThisReferenceExpression(), fieldName)))); - - fieldSetStatement.TrueStatements.Add(new CodeMethodInvokeExpression(new CodeVariableReferenceExpression("writer"), "WriteAttributeString", new CodeSnippetExpression(String.Concat("\"", propertyName, "\"")), new CodeSnippetExpression(String.Concat("outputValue")))); - } - else - { - foreach (string key in enumDeclaration.Values) - { - CodeConditionStatement enumOutStatement = new CodeConditionStatement(); - enumOutStatement.Condition = new CodeBinaryOperatorExpression(new CodeFieldReferenceExpression(new CodeThisReferenceExpression(), fieldName), CodeBinaryOperatorType.ValueEquality, new CodePropertyReferenceExpression(new CodeSnippetExpression(enumDeclaration.Name), MakeEnumValue(key))); - enumOutStatement.TrueStatements.Add(new CodeMethodInvokeExpression(new CodeVariableReferenceExpression("writer"), "WriteAttributeString", new CodeSnippetExpression(String.Concat("\"", propertyName, "\"")), new CodeSnippetExpression(String.Concat("\"", key, "\"")))); - fieldSetStatement.TrueStatements.Add(enumOutStatement); - } - - attributeNameMatchStatement.TrueStatements.Add(new CodeAssignStatement( - new CodeFieldReferenceExpression(new CodeThisReferenceExpression(), fieldName), - new CodeMethodInvokeExpression(new CodeTypeReferenceExpression(enumParent.Name), - String.Concat("Parse", enumDeclaration.Name), - new CodeVariableReferenceExpression("value")))); - } - } - - /// - /// Generates a summary comment. - /// - /// Comments collection to add the comments to. - /// Content of the comment. - private static void GenerateSummaryComment(CodeCommentStatementCollection comments, string content) - { - using (StringWriter sw = new StringWriter()) - { - XmlTextWriter writer = null; - - // create the comment as xml to ensure proper escaping of special xml characters - try - { - writer = new XmlTextWriter(sw); - writer.Indentation = 0; - - writer.WriteStartElement("summary"); - writer.WriteString(Environment.NewLine); - - string nextComment; - int newlineIndex = content.IndexOf(Environment.NewLine); - int offset = 0; - while (newlineIndex != -1) - { - nextComment = content.Substring(offset, newlineIndex - offset).Trim(); - writer.WriteString(nextComment); - writer.WriteString(Environment.NewLine); - offset = newlineIndex + Environment.NewLine.Length; - newlineIndex = content.IndexOf(Environment.NewLine, offset); - } - nextComment = content.Substring(offset).Trim(); - writer.WriteString(nextComment); - writer.WriteString(Environment.NewLine); - - writer.WriteEndElement(); - } - finally - { - if (null != writer) - { - writer.Close(); - } - } - - // create the comment statements (one per line of xml) - using (StringReader sr = new StringReader(sw.ToString())) - { - string line; - - while (null != (line = sr.ReadLine())) - { - comments.Add(new CodeCommentStatement(line, true)); - } - } - } - } - - /// - /// Gets the CLR type for simple XML type. - /// - /// Plain text name of type. - /// Type corresponding to parameter. - private static Type GetClrTypeByXmlName(string typeName) - { - switch (typeName) - { - case "bool": - return typeof(bool); - case "int": - return typeof(int); - case "long": - return typeof(long); - case "string": - return typeof(string); - default: - return null; - } - } - - /// - /// Processes an XmlSchemaSimpleType into corresponding types. - /// - /// XmlSchemaSimpleType to be processed. - /// CodeNamespace to be used when outputting code. - private static void ProcessSimpleType(XmlSchemaSimpleType simpleType, CodeNamespace codeNamespace) - { - EnumDeclaration enumDeclaration; - string simpleTypeName = simpleType.Name; - string baseTypeName; - - ProcessSimpleType(simpleTypeName, simpleType, false, out enumDeclaration, out baseTypeName); - - simpleTypeNamesToClrTypeNames.Add(simpleTypeName, baseTypeName); - - if (enumDeclaration != null) - { - codeNamespace.Types.Add(enumDeclaration.TypeDeclaration); - typeNamesToEnumDeclarations.Add(simpleTypeName, enumDeclaration); - AddEnumHelperMethods(enumDeclaration, codeNamespace); - } - } - - /// - /// Processes an XmlSchemaSimpleType into corresponding code. - /// - /// Name for the type. - /// XmlSchemaSimpleType to be processed. - /// CodeNamespace to be used when outputting code for global types. - /// CodeTypeDeclaration to be used when outputting code for nested types. - /// Member method for the OutputXml method for nested types. - /// Member method for the SetAttribute method for nested types. - private static void ProcessSimpleType(string simpleTypeName, XmlSchemaSimpleType simpleType, bool nestedType, out EnumDeclaration enumDeclaration, out string baseTypeName) - { - enumDeclaration = null; - baseTypeName = null; - - // XSD supports simpleTypes derived by union, list, or restriction; restrictions can have any - // combination of pattern, enumeration, length, and more; lists can contain any other simpleType. - // XsdGen, in contrast, only supports a limited set of values... - // Unions are weakly supported by just using the first member type - // restrictions must either be all enumeration or a single pattern, a list must be of a - // single simpleType which itself is only a restriction of enumeration. - if (simpleType.Content is XmlSchemaSimpleTypeUnion) - { - XmlSchemaSimpleTypeUnion union = simpleType.Content as XmlSchemaSimpleTypeUnion; - if (union.MemberTypes.Length > 0) - { - baseTypeName = union.MemberTypes[0].Name; - return; - } - else - { - baseTypeName = "string"; - return; - } - } - - bool listType = false; // XSD lists become [Flag]enums in C#... - XmlSchemaSimpleTypeList simpleTypeList = simpleType.Content as XmlSchemaSimpleTypeList; - XmlSchemaSimpleTypeRestriction simpleTypeRestriction = simpleType.Content as XmlSchemaSimpleTypeRestriction; - - if (simpleTypeList != null) - { - baseTypeName = simpleTypeList.ItemTypeName.Name; - - if (String.IsNullOrEmpty(baseTypeName)) - { - simpleTypeRestriction = simpleTypeList.ItemType.Content as XmlSchemaSimpleTypeRestriction; - if (simpleTypeRestriction == null) - { - string appName = typeof(XsdGen).Assembly.GetName().Name; - throw new NotImplementedException(string.Format("{0} does not support a that does not contain a /.", appName)); - } - - listType = true; - } - else - { - // We expect to find an existing enum already declared! - EnumDeclaration existingEnumDeclaration = typeNamesToEnumDeclarations[baseTypeName]; - // TODO: do we need to further alter the Flags setter code because of the helper stuff? - // As far as I can tell, this code is never exercised by our existing XSDs! - existingEnumDeclaration.SetFlags(); - } - } - - if (simpleTypeRestriction == null) - { - string appName = typeof(XsdGen).Assembly.GetName().Name; - throw new NotImplementedException(string.Format("{0} does not understand this simpleType!", appName)); - } - - bool foundPattern = false; - foreach (XmlSchemaFacet facet in simpleTypeRestriction.Facets) - { - XmlSchemaEnumerationFacet enumFacet = facet as XmlSchemaEnumerationFacet; - XmlSchemaPatternFacet patternFacet = facet as XmlSchemaPatternFacet; - - if (enumFacet != null) - { - if (foundPattern) - { - string appName = typeof(XsdGen).Assembly.GetName().Name; - throw new NotImplementedException(string.Format("{0} does not support restrictions containing both and .", appName)); - } - - if (enumDeclaration == null) - { - // For nested types, the simple name comes from the attribute name, with "Type" appended - // to prevent name collision with the attribute member itself. - if (nestedType) - { - simpleTypeName = String.Concat(simpleTypeName, "Type"); - } - baseTypeName = simpleTypeName; - - string typeDocumentation = GetDocumentation(simpleType.Annotation); - enumDeclaration = new EnumDeclaration(simpleTypeName, typeDocumentation); - } - - string documentation = GetDocumentation(enumFacet.Annotation); - enumDeclaration.AddValue(enumFacet.Value, documentation); - } - - if (patternFacet != null) - { - if (enumDeclaration != null) - { - string appName = typeof(XsdGen).Assembly.GetName().Name; - throw new NotImplementedException(string.Format("{0} does not support restrictions containing both and .", appName)); - } - - if (foundPattern) - { - string appName = typeof(XsdGen).Assembly.GetName().Name; - throw new NotImplementedException(string.Format("{0} does not support restrictions multiple elements.", appName)); - } - - foundPattern = true; - } - } - - if (enumDeclaration != null && listType) - { - enumDeclaration.SetFlags(); - } - - if (String.IsNullOrEmpty(baseTypeName)) - { - baseTypeName = (string)simpleTypeNamesToClrTypeNames[simpleTypeRestriction.BaseTypeName.Name]; - } - } - - /// - /// Creates an attribute declaration indicating generated code including the tool name and version. - /// - /// GeneratedCodeAttribute declearation. - private static CodeAttributeDeclaration GetGeneratedCodeAttribute() - { - AssemblyName generatorAssemblyName = typeof(XsdGen).Assembly.GetName(); - return new CodeAttributeDeclaration("GeneratedCode", - new CodeAttributeArgument(new CodePrimitiveExpression(generatorAssemblyName.Name)), - new CodeAttributeArgument(new CodePrimitiveExpression(generatorAssemblyName.Version.ToString()))); - } - - /// - /// Creates a code statement to throw an exception if an argument is null. - /// - /// Name of the argument to check. - /// True to check for null-or-empty instead of just null - /// Code condition statement. - private static CodeConditionStatement GetArgumentNullCheckStatement(string argumentName, bool nullOrEmpty) - { - CodeConditionStatement conditionStatement = new CodeConditionStatement(); - if (nullOrEmpty) - { - conditionStatement.Condition = new CodeMethodInvokeExpression(new CodeMethodReferenceExpression(new CodeTypeReferenceExpression("String"), "IsNullOrEmpty"), new CodeVariableReferenceExpression(argumentName)); - } - else - { - conditionStatement.Condition = new CodeBinaryOperatorExpression(new CodePrimitiveExpression(null), CodeBinaryOperatorType.ValueEquality, new CodeVariableReferenceExpression(argumentName)); - } - - conditionStatement.TrueStatements.Add(new CodeThrowExceptionStatement(new CodeObjectCreateExpression("ArgumentNullException", new CodeSnippetExpression(String.Concat("\"", argumentName, "\""))))); - return conditionStatement; - } - - /// - /// Creates an attribute declaration to suppress a particular code-analysis message. - /// - /// Code analysis category, such as "Microsoft.Design" - /// Code analysis ID number. - /// SuppressMessageAttribute declaration. - private static CodeAttributeDeclaration GetCodeAnalysisSuppressionAttribute(string category, string checkId) - { - return new CodeAttributeDeclaration("SuppressMessage", - new CodeAttributeArgument(new CodePrimitiveExpression(category)), - new CodeAttributeArgument(new CodePrimitiveExpression(checkId))); - } - - /// - /// Class representing an enum declaration. - /// - internal class EnumDeclaration - { - private string enumTypeName; - private CodeTypeDeclaration declaration; - private bool flags; - private StringCollection enumValues; - - /// - /// Creates a new enum declaration with the given name. - /// - /// Name of the type for the enum. - /// Documentation for the enum type. - public EnumDeclaration(string enumTypeName, string documentation) - { - this.enumTypeName = enumTypeName; - - this.declaration = new CodeTypeDeclaration(enumTypeName); - this.declaration.CustomAttributes.Add(GetGeneratedCodeAttribute()); - this.declaration.Attributes = MemberAttributes.Public; - this.declaration.IsEnum = true; - - if (documentation != null) - { - GenerateSummaryComment(this.declaration.Comments, documentation); - } - - this.enumValues = new StringCollection(); - } - - public CodeTypeDeclaration TypeDeclaration - { - get { return this.declaration; } - } - - /// - /// Gets the enumeration values. - /// - /// The enumeration values. - public ICollection Values - { - get { return this.enumValues; } - } - - /// - /// Gets the enumeration name. - /// - /// The enumeration name. - public string Name - { - get { return this.enumTypeName; } - } - - /// - /// Gets the enumeration flags property. - /// - /// Whether the enumeration is a [Flags] type. - public bool Flags - { - get { return this.flags; } - } - - /// - /// Sets the [Flags] property on the enumeration. Once set, this cannot be undone. - /// - public void SetFlags() - { - if (this.flags) - { - return; - } - - this.flags = true; - - this.declaration.CustomAttributes.Add(new CodeAttributeDeclaration("Flags")); - SwitchToNoneValue(); - - int enumValue = 0; - foreach (CodeMemberField enumField in this.declaration.Members) - { - enumField.InitExpression = new CodeSnippetExpression(enumValue.ToString()); - if (enumValue == 0) - { - enumValue = 1; - } - else - { - enumValue *= 2; - } - } - } - - private void InjectIllegalAndNotSetValues() - { - CodeMemberField memberIllegal = new CodeMemberField(typeof(int), "IllegalValue"); - CodeMemberField memberNotSet = new CodeMemberField(typeof(int), "NotSet"); - - memberIllegal.InitExpression = new CodeFieldReferenceExpression(new CodeTypeReferenceExpression(typeof(int)), "MaxValue"); - // Using "-1" for "NotSet" ensure that the next value is zero, which is consistent - // with older (3.0) behavior. - memberNotSet.InitExpression = new CodePrimitiveExpression(-1); - - this.declaration.Members.Insert(0, memberIllegal); - this.declaration.Members.Insert(1, memberNotSet); - } - - private void SwitchToNoneValue() - { - if (this.enumValues.Count > 0) - { - // Remove the "IllegalValue" and "NotSet" values first. - this.declaration.Members.RemoveAt(0); - this.declaration.Members.RemoveAt(0); - - CodeMemberField memberNone = new CodeMemberField(typeof(int), "None"); - memberNone.InitExpression = new CodePrimitiveExpression(0); - - this.declaration.Members.Insert(0, memberNone); - } - } - - /// - /// Add a value to the enumeration. - /// - /// The value to add. - public void AddValue(string enumValue, string documentation) - { - if (this.enumValues.Count == 0) - { - InjectIllegalAndNotSetValues(); - } - - this.enumValues.Add(enumValue); - CodeMemberField memberField = new CodeMemberField(typeof(int), MakeEnumValue(enumValue)); - //memberField.Attributes - this.declaration.Members.Add(memberField); - if (documentation != null) - { - GenerateSummaryComment(memberField.Comments, documentation); - } - } - } - - private static void AddEnumHelperMethods(EnumDeclaration enumDeclaration, CodeNamespace codeNamespace) - { - if (enumHelperClass == null) - { - enumHelperClass = new CodeTypeDeclaration("Enums"); - enumHelperClass.CustomAttributes.Add(GetGeneratedCodeAttribute()); - // The static and final attributes don't seem to get applied, but we'd prefer if they were. - enumHelperClass.Attributes = MemberAttributes.Public | MemberAttributes.Static | MemberAttributes.Final; - codeNamespace.Types.Add(enumHelperClass); - } - - AddEnumHelperMethods(enumDeclaration, enumHelperClass); - } - - private static void AddEnumHelperMethods(EnumDeclaration enumDeclaration, CodeTypeDeclaration parentType) - { - CodeTypeReference stringType = new CodeTypeReference(typeof(string)); - CodeTypeReference boolType = new CodeTypeReference(typeof(bool)); - CodeTypeReference enumType = new CodeTypeReference(typeof(Enum)); - CodeTypeReference newEnumType = new CodeTypeReference(enumDeclaration.Name); - - CodePrimitiveExpression falseValue = new CodePrimitiveExpression(false); - CodePrimitiveExpression trueValue = new CodePrimitiveExpression(true); - CodeMethodReturnStatement returnFalse = new CodeMethodReturnStatement(falseValue); - CodeMethodReturnStatement returnTrue = new CodeMethodReturnStatement(trueValue); - - string parseMethodName = String.Concat("Parse", enumDeclaration.Name); - string tryParseMethodName = String.Concat("TryParse", enumDeclaration.Name); - - CodeFieldReferenceExpression defaultEnumValue = null; - CodeFieldReferenceExpression illegalEnumValue = null; - bool addParse = true; - if (enumDeclaration.Flags) - { - defaultEnumValue = new CodeFieldReferenceExpression(new CodeTypeReferenceExpression(newEnumType), "None"); - illegalEnumValue = defaultEnumValue; - // Because there's no "IllegalValue" for [Flags] enums, we can't create the Parse() - // method. We can still create the TryParse() method, though! - addParse = false; - } - else - { - defaultEnumValue = new CodeFieldReferenceExpression(new CodeTypeReferenceExpression(newEnumType), "NotSet"); - illegalEnumValue = new CodeFieldReferenceExpression(new CodeTypeReferenceExpression(newEnumType), "IllegalValue"); - } - - if (addParse) - { - CodeMemberMethod parseNewEnum = new CodeMemberMethod(); - GenerateSummaryComment(parseNewEnum.Comments, String.Format("Parses a {0} from a string.", enumDeclaration.Name)); - parseNewEnum.Attributes = MemberAttributes.Public | MemberAttributes.Static; - parseNewEnum.Name = parseMethodName; - parseNewEnum.ReturnType = newEnumType; - parseNewEnum.Parameters.Add(new CodeParameterDeclarationExpression(stringType, "value")); - - parseNewEnum.Statements.Add(new CodeVariableDeclarationStatement(newEnumType, "parsedValue")); - - // Just delegate to the TryParse version... - parseNewEnum.Statements.Add(new CodeMethodInvokeExpression( - new CodeMethodReferenceExpression(new CodeTypeReferenceExpression(parentType.Name), tryParseMethodName), - new CodeArgumentReferenceExpression("value"), - new CodeDirectionExpression(FieldDirection.Out, new CodeVariableReferenceExpression("parsedValue")))); - - parseNewEnum.Statements.Add(new CodeMethodReturnStatement(new CodeVariableReferenceExpression("parsedValue"))); - parentType.Members.Add(parseNewEnum); - } - - CodeMemberMethod tryParseNewEnum = new CodeMemberMethod(); - GenerateSummaryComment(tryParseNewEnum.Comments, String.Format("Tries to parse a {0} from a string.", enumDeclaration.Name)); - tryParseNewEnum.Attributes = MemberAttributes.Public | MemberAttributes.Static; - tryParseNewEnum.Name = tryParseMethodName; - tryParseNewEnum.ReturnType = boolType; - CodeParameterDeclarationExpression valueDeclaration = new CodeParameterDeclarationExpression(stringType, "value"); - CodeParameterDeclarationExpression parsedValueDeclaration = new CodeParameterDeclarationExpression(newEnumType, "parsedValue"); - parsedValueDeclaration.Direction = FieldDirection.Out; - tryParseNewEnum.Parameters.Add(valueDeclaration); - tryParseNewEnum.Parameters.Add(parsedValueDeclaration); - - CodeArgumentReferenceExpression value = new CodeArgumentReferenceExpression(valueDeclaration.Name); - CodeArgumentReferenceExpression parsedValue = new CodeArgumentReferenceExpression(parsedValueDeclaration.Name); - - tryParseNewEnum.Statements.Add(new CodeAssignStatement(parsedValue, defaultEnumValue)); - - tryParseNewEnum.Statements.Add(new CodeConditionStatement( - new CodeMethodInvokeExpression(new CodeMethodReferenceExpression(new CodeTypeReferenceExpression(stringType), "IsNullOrEmpty"), value), - returnFalse)); - - // The structure is similar, but distinct, for regular and flag-style enums. In particular, - // for a flags-style enum we have to be able to parse multiple values, separated by - // spaces, and each value is bitwise-OR'd together. - CodeStatementCollection nestedIfParent = tryParseNewEnum.Statements; - CodeExpression valueToTest = value; - - // For Flags-style enums, we need to loop over the space-separated values... - if (enumDeclaration.Flags) - { - CodeVariableDeclarationStatement split = new CodeVariableDeclarationStatement(typeof(string[]), "splitValue", - new CodeMethodInvokeExpression(value, "Split", - new CodeMethodInvokeExpression(new CodePrimitiveExpression(" \t\r\n"), "ToCharArray"), - new CodeFieldReferenceExpression(new CodeTypeReferenceExpression(typeof(StringSplitOptions)), "RemoveEmptyEntries"))); - tryParseNewEnum.Statements.Add(split); - - CodeIterationStatement flagLoop = new CodeIterationStatement( - new CodeVariableDeclarationStatement(typeof(IEnumerator), "enumerator", - new CodeMethodInvokeExpression(new CodeVariableReferenceExpression(split.Name), "GetEnumerator")), - new CodeMethodInvokeExpression(new CodeVariableReferenceExpression("enumerator"), "MoveNext"), - new CodeSnippetStatement("")); - tryParseNewEnum.Statements.Add(flagLoop); - - CodeVariableDeclarationStatement currentValue = new CodeVariableDeclarationStatement(typeof(string), "currentValue", - new CodeCastExpression(stringType, - new CodePropertyReferenceExpression(new CodeVariableReferenceExpression("enumerator"), "Current"))); - flagLoop.Statements.Add(currentValue); - valueToTest = new CodeVariableReferenceExpression(currentValue.Name); - - nestedIfParent = flagLoop.Statements; - } - - // We can't just Enum.Parse, because some values are also keywords (like 'string', 'int', 'default'), - // and these get generated as '@'-prefixed values. Instead, we 'switch' on the value and do it manually. - // Actually, we if/else, because CodeDom doesn't support 'switch'! Also, we nest the successive 'if's - // in order to short-circuit the parsing as soon as there's a match. - foreach (string enumValue in enumDeclaration.Values) - { - CodeFieldReferenceExpression enumValueReference = new CodeFieldReferenceExpression(new CodeTypeReferenceExpression(newEnumType), MakeEnumValue(enumValue)); - CodeConditionStatement ifStatement = new CodeConditionStatement( - new CodeBinaryOperatorExpression(new CodePrimitiveExpression(enumValue), CodeBinaryOperatorType.ValueEquality, valueToTest)); - if (enumDeclaration.Flags) - { - ifStatement.TrueStatements.Add(new CodeAssignStatement(parsedValue, - new CodeBinaryOperatorExpression(parsedValue, CodeBinaryOperatorType.BitwiseOr, enumValueReference))); - } - else - { - ifStatement.TrueStatements.Add(new CodeAssignStatement(parsedValue, enumValueReference)); - } - nestedIfParent.Add(ifStatement); - nestedIfParent = ifStatement.FalseStatements; - } - - // Finally, if we didn't find a match, it's illegal (or none, for flags)! - nestedIfParent.Add(new CodeAssignStatement(parsedValue, illegalEnumValue)); - nestedIfParent.Add(returnFalse); - - tryParseNewEnum.Statements.Add(returnTrue); - - parentType.Members.Add(tryParseNewEnum); - - enumsToParseMethodClasses.Add(enumDeclaration, parentType); - } - } -} diff --git a/src/WixBuildTools.XsdGen/WixBuildTools.XsdGen.csproj b/src/WixBuildTools.XsdGen/WixBuildTools.XsdGen.csproj deleted file mode 100644 index 2e3a0382..00000000 --- a/src/WixBuildTools.XsdGen/WixBuildTools.XsdGen.csproj +++ /dev/null @@ -1,28 +0,0 @@ - - - - Exe - - net461 - true - false - true - embedded - true - CS0618 - - - - - - - - - - - - - - - - diff --git a/src/WixBuildTools.XsdGen/XsdGen.cs b/src/WixBuildTools.XsdGen/XsdGen.cs deleted file mode 100644 index a1374df3..00000000 --- a/src/WixBuildTools.XsdGen/XsdGen.cs +++ /dev/null @@ -1,124 +0,0 @@ -// 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. - -namespace WixToolset.Tools -{ - using System; - using System.CodeDom; - using System.CodeDom.Compiler; - using System.Collections; - using System.IO; - using System.Xml; - using System.Xml.Schema; - using Microsoft.CSharp; - using WixToolset; - - /// - /// Generates a strongly-typed C# class from an XML schema (XSD). - /// - public class XsdGen - { - private string xsdFile; - private string outFile; - private string outputNamespace; - private string commonNamespace; - private bool showHelp; - - /// - /// Constructor for the XsdGen class. - /// - /// Command-line arguments passed to the program. - private XsdGen(string[] args) - { - this.ParseCommandlineArgs(args); - - // show usage information - if (this.showHelp) - { - Console.WriteLine("usage: XsdGen.exe .xsd []"); - return; - } - - // ensure that the schema file exists - if (!File.Exists(this.xsdFile)) - { - throw new ApplicationException(String.Format("Schema file does not exist: '{0}'.", this.xsdFile)); - } - - XmlSchema document = null; - using (StreamReader xsdFileReader = new StreamReader(this.xsdFile)) - { - document = XmlSchema.Read(xsdFileReader, new ValidationEventHandler(this.ValidationHandler)); - } - - CodeCompileUnit codeCompileUnit = StronglyTypedClasses.Generate(document, this.outputNamespace, this.commonNamespace); - - using (CSharpCodeProvider codeProvider = new CSharpCodeProvider()) - { - ICodeGenerator generator = codeProvider.CreateGenerator(); - - CodeGeneratorOptions options = new CodeGeneratorOptions(); - options.BlankLinesBetweenMembers = true; - options.BracingStyle = "C"; - options.IndentString = " "; - - using (StreamWriter csharpFileWriter = new StreamWriter(this.outFile)) - { - generator.GenerateCodeFromCompileUnit(codeCompileUnit, csharpFileWriter, options); - } - } - } - - /// - /// The main entry point for the application. - /// - /// The command line arguments. - /// The error code. - [STAThread] - public static int Main(string[] args) - { - try - { - XsdGen xsdGen = new XsdGen(args); - } - catch (Exception e) - { - Console.WriteLine("XsdGen.exe : fatal error MSF0000: {0}\r\n\r\nStack Trace:\r\n{1}", e.Message, e.StackTrace); - return 1; - } - - return 0; - } - - /// - /// Validation event handler. - /// - /// Sender for the event. - /// Event args. - public void ValidationHandler(object sender, ValidationEventArgs e) - { - } - - /// - /// Parse the command line arguments. - /// - /// Command-line arguments. - private void ParseCommandlineArgs(string[] args) - { - if (3 > args.Length) - { - this.showHelp = true; - } - else - { - this.xsdFile = args[0]; - this.outFile = args[1]; - this.outputNamespace = args[2]; - - if (args.Length >= 4) - { - this.commonNamespace = args[3]; - } - } - } - } -} diff --git a/src/WixBuildTools.XsdGen/build/WixBuildTools.XsdGen.targets b/src/WixBuildTools.XsdGen/build/WixBuildTools.XsdGen.targets deleted file mode 100644 index ca1b89f6..00000000 --- a/src/WixBuildTools.XsdGen/build/WixBuildTools.XsdGen.targets +++ /dev/null @@ -1,67 +0,0 @@ - - - - - - $(MSBuildAllProjects);$(MSBuildThisFileFullPath) - - - - - - - - - - $(MSBuildThisFileDirectory)..\tools\full\ - - - - - - - - XsdGen; - $(PrepareResourcesDependsOn) - - - - - - $(IntermediateOutputPath)%(XsdGenSource.Filename).cs - %(XsdGenSource.CommonNamespace) - - - - - - - - - - - %(XsdGenCsFile.Filename)%(XsdGenCsFile.Extension) - - - - - diff --git a/src/WixBuildTools.XsdGen/buildCrossTargeting/WixBuildTools.XsdGen.targets b/src/WixBuildTools.XsdGen/buildCrossTargeting/WixBuildTools.XsdGen.targets deleted file mode 100644 index 58692095..00000000 --- a/src/WixBuildTools.XsdGen/buildCrossTargeting/WixBuildTools.XsdGen.targets +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - 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 @@ + + + + + true + true + $([System.IO.Path]::GetFullPath($(MSBuildThisFileDirectory)wix.snk)) + false + + 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 @@ + + + + + + Win32 + $(BaseIntermediateOutputPath)$(Configuration)\$(Platform)\ + $(OutputPath)$(Platform)\ + + + + $([Microsoft.Build.Utilities.ToolLocationHelper]::GetLatestSDKTargetPlatformVersion('Windows', '10.0')) + + + + + $(DisableSpecificCompilerWarnings) + Level4 + $(ProjectDir)inc;$(MSBuildProjectDirectory);$(IntDir);$(SqlCESdkIncludePath);$(ProjectAdditionalIncludeDirectories);%(AdditionalIncludeDirectories) + WIN32;_WINDOWS;_WIN32_MSI=500;_WIN32_WINNT=0x0501;$(ArmPreprocessorDefinitions);$(UnicodePreprocessorDefinitions);_CRT_STDIO_LEGACY_WIDE_SPECIFIERS;_WINSOCK_DEPRECATED_NO_WARNINGS;%(PreprocessorDefinitions) + Use + precomp.h + StdCall + true + false + -YlprecompDefine + /Zc:threadSafeInit- %(AdditionalOptions) + true + + + $(ArmPreprocessorDefinitions);%(PreprocessorDefinitions) + $(ProjectAdditionalResourceIncludeDirectories);%(AdditionalIncludeDirectories) + + + $(OutDir);$(AdditionalMultiTargetLibraryPath);$(ProjectAdditionalLibraryDirectories);%(AdditionalLibraryDirectories) + + + $(ProjectSubSystem) + $(ProjectModuleDefinitionFile) + $(ResourceOnlyDll) + true + $(ProjectAdditionalLinkLibraries);advapi32.lib;comdlg32.lib;user32.lib;oleaut32.lib;gdi32.lib;shell32.lib;ole32.lib;version.lib;%(AdditionalDependencies) + $(OutDir);$(AdditionalMultiTargetLibraryPath);$(ArmLibraryDirectories);$(ProjectAdditionalLinkLibraryDirectories);%(AdditionalLibraryDirectories) + /IGNORE:4099 %(AdditionalOptions) + + + + + + NoExtensions + + + + + CDecl + + + + + OldStyle + true + true + + + + + Disabled + EnableFastChecks + _DEBUG;DEBUG;%(PreprocessorDefinitions) + MultiThreadedDebug + + + + + + MultiThreadedDebugDll + + + + + MinSpace + NDEBUG;%(PreprocessorDefinitions) + true + true + MultiThreaded + + + true + true + + + + + + MultiThreadedDll + + + + + $(LinkKeyFile) + $(LinkDelaySign) + + + 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 @@ + + + + + + Debug + false + MSB3246 + + $(MSBuildProjectName) + $([System.IO.Path]::GetFullPath($(MSBuildThisFileDirectory)..\build\)) + $(BaseOutputPath)obj\$(ProjectName)\ + $(BaseOutputPath)$(Configuration)\ + + WiX Toolset Team + WiX Toolset + Copyright (c) .NET Foundation and contributors. All rights reserved. + MS-RL + WiX Toolset + + + + + + + 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 @@ + + + + + + + false + $(OutputPath)\$(AssemblyName).xml + + + + true + $(SolutionPath) + $(NCrunchOriginalSolutionPath) + + + + + + + $([System.IO.File]::ReadAllText($(TheSolutionPath))) + $([System.IO.Path]::GetDirectoryName( $(TheSolutionPath) )) + (?<="[PackageName]", ")(.*)(?=", ") + + + + + + %(Identity) + $(SolutionFileContent.Contains('\%(Identity).csproj')) + + + + + $(RegexPattern.Replace('[PackageName]','%(PackageName)') ) + $([System.Text.RegularExpressions.Regex]::Match('$(SolutionFileContent)', '%(Pattern)')) + + + + + + + + + + + + + + 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 @@ + + + + Exe + netcoreapp2.0 + + + 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 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; +using System.Xml.Linq; + +namespace MessagesToMessages +{ + class Program + { + static readonly XNamespace ns = "http://schemas.microsoft.com/genmsgs/2004/07/messages"; + static readonly XName ClassDefinition = ns + "Class"; + static readonly XName MessageDefinition = ns + "Message"; + static readonly XName InstanceDefinition = ns + "Instance"; + static readonly XName ParameterDefinition = ns + "Parameter"; + static readonly XName Id = "Id"; + static readonly XName Level = "Level"; + static readonly XName Number = "Number"; + static readonly XName SourceLineNumbers = "SourceLineNumbers"; + static readonly XName Type = "Type"; + static readonly XName Name = "Name"; + + static void Main(string[] args) + { + if (args.Length == 0) + { + return; + } + else if (args.Length < 2) + { + Console.WriteLine("Need to specify output folder as well."); + } + else if (!Directory.Exists(args[1])) + { + Console.WriteLine("Output folder does not exist: {0}", args[1]); + } + + var messages = ReadXml(Path.GetFullPath(args[0])); + + foreach (var m in messages.GroupBy(m => m.Level)) + { + var className = m.First().ClassName; + var result = GenerateCs(className, m.Key, m); + + var path = Path.Combine(args[1], className + ".cs"); + File.WriteAllText(path, result); + } + } + + private static IEnumerable ReadXml(string inputPath) + { + var doc = XDocument.Load(inputPath); + + foreach (var xClass in doc.Root.Descendants(ClassDefinition)) + { + var name = xClass.Attribute(Name)?.Value; + var level = xClass.Attribute(Level)?.Value; + + if (String.IsNullOrEmpty(name)) + { + name = level + "Messages"; + } + if (String.IsNullOrEmpty(level)) + { + if (name.EndsWith("Errors", StringComparison.InvariantCultureIgnoreCase)) + { + level = "Error"; + } + else if (name.EndsWith("Verboses", StringComparison.InvariantCultureIgnoreCase)) + { + level = "Verbose"; + } + else if (name.EndsWith("Warnings", StringComparison.InvariantCultureIgnoreCase)) + { + level = "Warning"; + } + } + + var unique = new HashSet(); + var lastNumber = 0; + foreach (var xMessage in xClass.Elements(MessageDefinition)) + { + var id = xMessage.Attribute(Id).Value; + + if (!unique.Add(id)) + { + Console.WriteLine("Duplicated message: {0}", id); + } + + if (!Int32.TryParse(xMessage.Attribute(Number)?.Value, out var number)) + { + number = lastNumber; + } + lastNumber = number + 1; + + var sln = xMessage.Attribute(SourceLineNumbers)?.Value != "no"; + + var suffix = 0; + foreach (var xInstance in xMessage.Elements(InstanceDefinition)) + { + var value = xInstance.Value.Trim(); + + var parameters = xInstance.Elements(ParameterDefinition).Select(ReadParameter).ToList(); + + 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() }; + + ++suffix; + } + } + } + } + + private static Parameter ReadParameter(XElement element) + { + var name = element.Attribute(Name)?.Value; + var type = element.Attribute(Type)?.Value ?? "string"; + + if (type.StartsWith("System.")) + { + type = type.Substring(7); + } + + switch (type) + { + case "String": + type = "string"; + break; + + case "Int32": + type = "int"; + break; + + case "Int64": + type = "long"; + break; + } + + return new Parameter { Name = name, Type = type }; + } + + private static string GenerateCs(string className, string level, IEnumerable messages) + { + var header = String.Join(Environment.NewLine, + "// 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.", + "", + "namespace WixToolset.Data", + "{", + " using System;", + " using System.Resources;", + "", + " public static class {0}", + " {"); + + var messageFormat = String.Join(Environment.NewLine, + " public static Message {0}({1})", + " {{", + " return Message({4}, Ids.{0}, \"{3}\"{2});", + " }}", + ""); + + + var endMessagesFormat = String.Join(Environment.NewLine, + " private static Message Message(SourceLineNumber sourceLineNumber, Ids id, string format, params object[] args)", + " {{", + " return new Message(sourceLineNumber, MessageLevel.{0}, (int)id, format, args);", + " }}", + "", + " private static Message Message(SourceLineNumber sourceLineNumber, Ids id, ResourceManager resourceManager, string resourceName, params object[] args)", + " {{", + " return new Message(sourceLineNumber, MessageLevel.{0}, (int)id, resourceManager, resourceName, args);", + " }}", + "", + " public enum Ids", + " {{"); + + var idEnumFormat = + " {0} = {1},"; + var footer = String.Join(Environment.NewLine, + " }", + " }", + "}", + ""); + + var sb = new StringBuilder(); + + sb.AppendLine(header.Replace("{0}", className)); + + foreach (var message in messages.OrderBy(m => m.Id)) + { + var paramsWithTypes = String.Join(", ", message.Parameters.Select(p => $"{p.Type} {p.Name}")); + var paramsWithoutTypes = String.Join(", ", message.Parameters.Select(p => p.Name)); + + if (message.SourceLineNumbers) + { + paramsWithTypes = String.IsNullOrEmpty(paramsWithTypes) + ? "SourceLineNumber sourceLineNumbers" + : "SourceLineNumber sourceLineNumbers, " + paramsWithTypes; + } + + if (!String.IsNullOrEmpty(paramsWithoutTypes)) + { + paramsWithoutTypes = ", " + paramsWithoutTypes; + } + + sb.AppendFormat(messageFormat, message.Id, paramsWithTypes, paramsWithoutTypes, ToCSharpString(message.Value), message.SourceLineNumbers ? "sourceLineNumbers" : "null"); + + sb.AppendLine(); + } + + sb.AppendFormat(endMessagesFormat, level); + sb.AppendLine(); + + var unique = new HashSet(); + foreach (var message in messages.OrderBy(m => m.Number)) + { + if (unique.Add(message.Number)) + { + sb.AppendFormat(idEnumFormat, message.Id, message.Number); + sb.AppendLine(); + } + } + + sb.Append(footer); + + return sb.ToString(); + } + + private static string ToCSharpString(string value) + { + return value.Replace("\\", "\\\\").Replace("\"", "\\\"").Replace("\r", "\\r").Replace("\n", "\\n").Replace("\t", "\\t"); + } + + private class Message + { + public string Id { get; set; } + + public string Suffix { get; set; } + + public string ClassName { get; set; } + + public string Level { get; set; } + + public int Number { get; set; } + + public bool SourceLineNumbers { get; set; } + + public string Value { get; set; } + + public IEnumerable Parameters { get; set; } + } + + + private class Parameter + { + public string Name { get; set; } + + public string Type { get; set; } + } + } +} 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 @@ +{ + "profiles": { + "TablesAndSymbols": { + "commandName": "Project", + "commandLineArgs": "E:\\src\\wixtoolset\\Core\\src\\WixToolset.Core\\Data\\messages.xml E:\\src\\wixtoolset\\Data\\src\\WixToolset.Data", + "workingDirectory": "E:\\src\\wixtoolset\\Core\\src\\WixToolset.Core\\" + } + } +} \ 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 @@ +//----------------------------------------------------------------------- +// +// Copyright (c) 2011, The Outercurve Foundation. +// +// Licensed under the MIT License (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.opensource.org/licenses/mit-license.php +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// Nathan Totten (ntotten.com), Jim Zimmerman (jimzimmerman.com) and Prabir Shrestha (prabir.me) +// https://github.com/facebook-csharp-sdk/simple-json +//----------------------------------------------------------------------- + +// VERSION: + +// NOTE: uncomment the following line to make SimpleJson class internal. +#define SIMPLE_JSON_INTERNAL + +// NOTE: uncomment the following line to make JsonArray and JsonObject class internal. +#define SIMPLE_JSON_OBJARRAYINTERNAL + +// NOTE: uncomment the following line to enable dynamic support. +//#define SIMPLE_JSON_DYNAMIC + +// NOTE: uncomment the following line to enable DataContract support. +//#define SIMPLE_JSON_DATACONTRACT + +// NOTE: uncomment the following line to enable IReadOnlyCollection and IReadOnlyList support. +//#define SIMPLE_JSON_READONLY_COLLECTIONS + +// NOTE: uncomment the following line to disable linq expressions/compiled lambda (better performance) instead of method.invoke(). +// define if you are using .net framework <= 3.0 or < WP7.5 +//#define SIMPLE_JSON_NO_LINQ_EXPRESSION + +// NOTE: uncomment the following line if you are compiling under Window Metro style application/library. +// usually already defined in properties +//#define NETFX_CORE; + +// If you are targetting WinStore, WP8 and NET4.5+ PCL make sure to #define SIMPLE_JSON_TYPEINFO; + +// 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 + +#if NETFX_CORE +#define SIMPLE_JSON_TYPEINFO +#endif + +using System; +using System.CodeDom.Compiler; +using System.Collections; +using System.Collections.Generic; +#if !SIMPLE_JSON_NO_LINQ_EXPRESSION +using System.Linq.Expressions; +#endif +using System.ComponentModel; +using System.Diagnostics.CodeAnalysis; +#if SIMPLE_JSON_DYNAMIC +using System.Dynamic; +#endif +using System.Globalization; +using System.Reflection; +using System.Runtime.Serialization; +using System.Text; +using SimpleJson.Reflection; + +// ReSharper disable LoopCanBeConvertedToQuery +// ReSharper disable RedundantExplicitArrayCreation +// ReSharper disable SuggestUseVarKeywordEvident +namespace SimpleJson +{ + /// + /// Represents the json array. + /// + [GeneratedCode("simple-json", "1.0.0")] + [EditorBrowsable(EditorBrowsableState.Never)] + [SuppressMessage("Microsoft.Naming", "CA1710:IdentifiersShouldHaveCorrectSuffix")] +#if SIMPLE_JSON_OBJARRAYINTERNAL + internal +#else + public +#endif + class JsonArray : List + { + /// + /// Initializes a new instance of the class. + /// + public JsonArray() { } + + /// + /// Initializes a new instance of the class. + /// + /// The capacity of the json array. + public JsonArray(int capacity) : base(capacity) { } + + /// + /// The json representation of the array. + /// + /// The json representation of the array. + public override string ToString() + { + return SimpleJson.SerializeObject(this) ?? string.Empty; + } + } + + /// + /// Represents the json object. + /// + [GeneratedCode("simple-json", "1.0.0")] + [EditorBrowsable(EditorBrowsableState.Never)] + [SuppressMessage("Microsoft.Naming", "CA1710:IdentifiersShouldHaveCorrectSuffix")] +#if SIMPLE_JSON_OBJARRAYINTERNAL + internal +#else + public +#endif + class JsonObject : +#if SIMPLE_JSON_DYNAMIC + DynamicObject, +#endif + IDictionary + { + /// + /// The internal member dictionary. + /// + private readonly Dictionary _members; + + /// + /// Initializes a new instance of . + /// + public JsonObject() + { + _members = new Dictionary(); + } + + /// + /// Initializes a new instance of . + /// + /// The implementation to use when comparing keys, or null to use the default for the type of the key. + public JsonObject(IEqualityComparer comparer) + { + _members = new Dictionary(comparer); + } + + /// + /// Gets the at the specified index. + /// + /// + public object this[int index] + { + get { return GetAtIndex(_members, index); } + } + + internal static object GetAtIndex(IDictionary obj, int index) + { + if (obj == null) + throw new ArgumentNullException("obj"); + if (index >= obj.Count) + throw new ArgumentOutOfRangeException("index"); + int i = 0; + foreach (KeyValuePair o in obj) + if (i++ == index) return o.Value; + return null; + } + + /// + /// Adds the specified key. + /// + /// The key. + /// The value. + public void Add(string key, object value) + { + _members.Add(key, value); + } + + /// + /// Determines whether the specified key contains key. + /// + /// The key. + /// + /// true if the specified key contains key; otherwise, false. + /// + public bool ContainsKey(string key) + { + return _members.ContainsKey(key); + } + + /// + /// Gets the keys. + /// + /// The keys. + public ICollection Keys + { + get { return _members.Keys; } + } + + /// + /// Removes the specified key. + /// + /// The key. + /// + public bool Remove(string key) + { + return _members.Remove(key); + } + + /// + /// Tries the get value. + /// + /// The key. + /// The value. + /// + public bool TryGetValue(string key, out object value) + { + return _members.TryGetValue(key, out value); + } + + /// + /// Gets the values. + /// + /// The values. + public ICollection Values + { + get { return _members.Values; } + } + + /// + /// Gets or sets the with the specified key. + /// + /// + public object this[string key] + { + get { return _members[key]; } + set { _members[key] = value; } + } + + /// + /// Adds the specified item. + /// + /// The item. + public void Add(KeyValuePair item) + { + _members.Add(item.Key, item.Value); + } + + /// + /// Clears this instance. + /// + public void Clear() + { + _members.Clear(); + } + + /// + /// Determines whether [contains] [the specified item]. + /// + /// The item. + /// + /// true if [contains] [the specified item]; otherwise, false. + /// + public bool Contains(KeyValuePair item) + { + return _members.ContainsKey(item.Key) && _members[item.Key] == item.Value; + } + + /// + /// Copies to. + /// + /// The array. + /// Index of the array. + public void CopyTo(KeyValuePair[] array, int arrayIndex) + { + if (array == null) throw new ArgumentNullException("array"); + int num = Count; + foreach (KeyValuePair kvp in this) + { + array[arrayIndex++] = kvp; + if (--num <= 0) + return; + } + } + + /// + /// Gets the count. + /// + /// The count. + public int Count + { + get { return _members.Count; } + } + + /// + /// Gets a value indicating whether this instance is read only. + /// + /// + /// true if this instance is read only; otherwise, false. + /// + public bool IsReadOnly + { + get { return false; } + } + + /// + /// Removes the specified item. + /// + /// The item. + /// + public bool Remove(KeyValuePair item) + { + return _members.Remove(item.Key); + } + + /// + /// Gets the enumerator. + /// + /// + public IEnumerator> GetEnumerator() + { + return _members.GetEnumerator(); + } + + /// + /// Returns an enumerator that iterates through a collection. + /// + /// + /// An object that can be used to iterate through the collection. + /// + IEnumerator IEnumerable.GetEnumerator() + { + return _members.GetEnumerator(); + } + + /// + /// Returns a json that represents the current . + /// + /// + /// A json that represents the current . + /// + public override string ToString() + { + return SimpleJson.SerializeObject(this); + } + +#if SIMPLE_JSON_DYNAMIC + /// + /// Provides implementation for type conversion operations. Classes derived from the class can override this method to specify dynamic behavior for operations that convert an object from one type to another. + /// + /// 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 class, binder.Type returns the 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. + /// The result of the type conversion operation. + /// + /// Alwasy returns true. + /// + public override bool TryConvert(ConvertBinder binder, out object result) + { + // + if (binder == null) + throw new ArgumentNullException("binder"); + // + Type targetType = binder.Type; + + if ((targetType == typeof(IEnumerable)) || + (targetType == typeof(IEnumerable>)) || + (targetType == typeof(IDictionary)) || + (targetType == typeof(IDictionary))) + { + result = this; + return true; + } + + return base.TryConvert(binder, out result); + } + + /// + /// Provides the implementation for operations that delete an object member. This method is not intended for use in C# or Visual Basic. + /// + /// Provides information about the deletion. + /// + /// Alwasy returns true. + /// + public override bool TryDeleteMember(DeleteMemberBinder binder) + { + // + if (binder == null) + throw new ArgumentNullException("binder"); + // + return _members.Remove(binder.Name); + } + + /// + /// Provides the implementation for operations that get a value by index. Classes derived from the class can override this method to specify dynamic behavior for indexing operations. + /// + /// Provides information about the operation. + /// 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, is equal to 3. + /// The result of the index operation. + /// + /// Alwasy returns true. + /// + public override bool TryGetIndex(GetIndexBinder binder, object[] indexes, out object result) + { + if (indexes == null) throw new ArgumentNullException("indexes"); + if (indexes.Length == 1) + { + result = ((IDictionary)this)[(string)indexes[0]]; + return true; + } + result = null; + return true; + } + + /// + /// Provides the implementation for operations that get member values. Classes derived from the class can override this method to specify dynamic behavior for operations such as getting a value for a property. + /// + /// 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 class, binder.Name returns "SampleProperty". The binder.IgnoreCase property specifies whether the member name is case-sensitive. + /// The result of the get operation. For example, if the method is called for a property, you can assign the property value to . + /// + /// Alwasy returns true. + /// + public override bool TryGetMember(GetMemberBinder binder, out object result) + { + object value; + if (_members.TryGetValue(binder.Name, out value)) + { + result = value; + return true; + } + result = null; + return true; + } + + /// + /// Provides the implementation for operations that set a value by index. Classes derived from the class can override this method to specify dynamic behavior for operations that access objects by a specified index. + /// + /// Provides information about the operation. + /// 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 class, is equal to 3. + /// 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 class, is equal to 10. + /// + /// 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. + /// + public override bool TrySetIndex(SetIndexBinder binder, object[] indexes, object value) + { + if (indexes == null) throw new ArgumentNullException("indexes"); + if (indexes.Length == 1) + { + ((IDictionary)this)[(string)indexes[0]] = value; + return true; + } + return base.TrySetIndex(binder, indexes, value); + } + + /// + /// Provides the implementation for operations that set member values. Classes derived from the class can override this method to specify dynamic behavior for operations such as setting a value for a property. + /// + /// 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 class, binder.Name returns "SampleProperty". The binder.IgnoreCase property specifies whether the member name is case-sensitive. + /// The value to set to the member. For example, for sampleObject.SampleProperty = "Test", where sampleObject is an instance of the class derived from the class, the is "Test". + /// + /// 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.) + /// + public override bool TrySetMember(SetMemberBinder binder, object value) + { + // + if (binder == null) + throw new ArgumentNullException("binder"); + // + _members[binder.Name] = value; + return true; + } + + /// + /// Returns the enumeration of all dynamic member names. + /// + /// + /// A sequence that contains dynamic member names. + /// + public override IEnumerable GetDynamicMemberNames() + { + foreach (var key in Keys) + yield return key; + } +#endif + } +} + +namespace SimpleJson +{ + /// + /// This class encodes and decodes JSON strings. + /// Spec. details, see http://www.json.org/ + /// + /// JSON uses Arrays and Objects. These correspond here to the datatypes JsonArray(IList<object>) and JsonObject(IDictionary<string,object>). + /// All numbers are parsed to doubles. + /// + [GeneratedCode("simple-json", "1.0.0")] +#if SIMPLE_JSON_INTERNAL + internal +#else + public +#endif + static class SimpleJson + { + private const int TOKEN_NONE = 0; + private const int TOKEN_CURLY_OPEN = 1; + private const int TOKEN_CURLY_CLOSE = 2; + private const int TOKEN_SQUARED_OPEN = 3; + private const int TOKEN_SQUARED_CLOSE = 4; + private const int TOKEN_COLON = 5; + private const int TOKEN_COMMA = 6; + private const int TOKEN_STRING = 7; + private const int TOKEN_NUMBER = 8; + private const int TOKEN_TRUE = 9; + private const int TOKEN_FALSE = 10; + private const int TOKEN_NULL = 11; + private const int BUILDER_CAPACITY = 2000; + + private static readonly char[] EscapeTable; + private static readonly char[] EscapeCharacters = new char[] { '"', '\\', '\b', '\f', '\n', '\r', '\t' }; + private static readonly string EscapeCharactersString = new string(EscapeCharacters); + + static SimpleJson() + { + EscapeTable = new char[93]; + EscapeTable['"'] = '"'; + EscapeTable['\\'] = '\\'; + EscapeTable['\b'] = 'b'; + EscapeTable['\f'] = 'f'; + EscapeTable['\n'] = 'n'; + EscapeTable['\r'] = 'r'; + EscapeTable['\t'] = 't'; + } + + /// + /// Parses the string json into a value + /// + /// A JSON string. + /// An IList<object>, a IDictionary<string,object>, a double, a string, null, true, or false + public static object DeserializeObject(string json) + { + object obj; + if (TryDeserializeObject(json, out obj)) + return obj; + throw new SerializationException("Invalid JSON string"); + } + + /// + /// Try parsing the json string into a value. + /// + /// + /// A JSON string. + /// + /// + /// The object. + /// + /// + /// Returns true if successfull otherwise false. + /// + [SuppressMessage("Microsoft.Design", "CA1007:UseGenericsWhereAppropriate", Justification = "Need to support .NET 2")] + public static bool TryDeserializeObject(string json, out object obj) + { + bool success = true; + if (json != null) + { + char[] charArray = json.ToCharArray(); + int index = 0; + obj = ParseValue(charArray, ref index, ref success); + } + else + obj = null; + + return success; + } + + public static object DeserializeObject(string json, Type type, IJsonSerializerStrategy jsonSerializerStrategy) + { + object jsonObject = DeserializeObject(json); + return type == null || jsonObject != null && ReflectionUtils.IsAssignableFrom(jsonObject.GetType(), type) + ? jsonObject + : (jsonSerializerStrategy ?? CurrentJsonSerializerStrategy).DeserializeObject(jsonObject, type); + } + + public static object DeserializeObject(string json, Type type) + { + return DeserializeObject(json, type, null); + } + + public static T DeserializeObject(string json, IJsonSerializerStrategy jsonSerializerStrategy) + { + return (T)DeserializeObject(json, typeof(T), jsonSerializerStrategy); + } + + public static T DeserializeObject(string json) + { + return (T)DeserializeObject(json, typeof(T), null); + } + + /// + /// Converts a IDictionary<string,object> / IList<object> object into a JSON string + /// + /// A IDictionary<string,object> / IList<object> + /// Serializer strategy to use + /// A JSON encoded string, or null if object 'json' is not serializable + public static string SerializeObject(object json, IJsonSerializerStrategy jsonSerializerStrategy) + { + StringBuilder builder = new StringBuilder(BUILDER_CAPACITY); + bool success = SerializeValue(jsonSerializerStrategy, json, builder); + return (success ? builder.ToString() : null); + } + + public static string SerializeObject(object json) + { + return SerializeObject(json, CurrentJsonSerializerStrategy); + } + + public static string EscapeToJavascriptString(string jsonString) + { + if (string.IsNullOrEmpty(jsonString)) + return jsonString; + + StringBuilder sb = new StringBuilder(); + char c; + + for (int i = 0; i < jsonString.Length;) + { + c = jsonString[i++]; + + if (c == '\\') + { + int remainingLength = jsonString.Length - i; + if (remainingLength >= 2) + { + char lookahead = jsonString[i]; + if (lookahead == '\\') + { + sb.Append('\\'); + ++i; + } + else if (lookahead == '"') + { + sb.Append("\""); + ++i; + } + else if (lookahead == 't') + { + sb.Append('\t'); + ++i; + } + else if (lookahead == 'b') + { + sb.Append('\b'); + ++i; + } + else if (lookahead == 'n') + { + sb.Append('\n'); + ++i; + } + else if (lookahead == 'r') + { + sb.Append('\r'); + ++i; + } + } + } + else + { + sb.Append(c); + } + } + return sb.ToString(); + } + + static IDictionary ParseObject(char[] json, ref int index, ref bool success) + { + IDictionary table = new JsonObject(); + int token; + + // { + NextToken(json, ref index); + + bool done = false; + while (!done) + { + token = LookAhead(json, index); + if (token == TOKEN_NONE) + { + success = false; + return null; + } + else if (token == TOKEN_COMMA) + NextToken(json, ref index); + else if (token == TOKEN_CURLY_CLOSE) + { + NextToken(json, ref index); + return table; + } + else + { + // name + string name = ParseString(json, ref index, ref success); + if (!success) + { + success = false; + return null; + } + // : + token = NextToken(json, ref index); + if (token != TOKEN_COLON) + { + success = false; + return null; + } + // value + object value = ParseValue(json, ref index, ref success); + if (!success) + { + success = false; + return null; + } + table[name] = value; + } + } + return table; + } + + static JsonArray ParseArray(char[] json, ref int index, ref bool success) + { + JsonArray array = new JsonArray(); + + // [ + NextToken(json, ref index); + + bool done = false; + while (!done) + { + int token = LookAhead(json, index); + if (token == TOKEN_NONE) + { + success = false; + return null; + } + else if (token == TOKEN_COMMA) + NextToken(json, ref index); + else if (token == TOKEN_SQUARED_CLOSE) + { + NextToken(json, ref index); + break; + } + else + { + object value = ParseValue(json, ref index, ref success); + if (!success) + return null; + array.Add(value); + } + } + return array; + } + + static object ParseValue(char[] json, ref int index, ref bool success) + { + switch (LookAhead(json, index)) + { + case TOKEN_STRING: + return ParseString(json, ref index, ref success); + case TOKEN_NUMBER: + return ParseNumber(json, ref index, ref success); + case TOKEN_CURLY_OPEN: + return ParseObject(json, ref index, ref success); + case TOKEN_SQUARED_OPEN: + return ParseArray(json, ref index, ref success); + case TOKEN_TRUE: + NextToken(json, ref index); + return true; + case TOKEN_FALSE: + NextToken(json, ref index); + return false; + case TOKEN_NULL: + NextToken(json, ref index); + return null; + case TOKEN_NONE: + break; + } + success = false; + return null; + } + + static string ParseString(char[] json, ref int index, ref bool success) + { + StringBuilder s = new StringBuilder(BUILDER_CAPACITY); + char c; + + EatWhitespace(json, ref index); + + // " + c = json[index++]; + bool complete = false; + while (!complete) + { + if (index == json.Length) + break; + + c = json[index++]; + if (c == '"') + { + complete = true; + break; + } + else if (c == '\\') + { + if (index == json.Length) + break; + c = json[index++]; + if (c == '"') + s.Append('"'); + else if (c == '\\') + s.Append('\\'); + else if (c == '/') + s.Append('/'); + else if (c == 'b') + s.Append('\b'); + else if (c == 'f') + s.Append('\f'); + else if (c == 'n') + s.Append('\n'); + else if (c == 'r') + s.Append('\r'); + else if (c == 't') + s.Append('\t'); + else if (c == 'u') + { + int remainingLength = json.Length - index; + if (remainingLength >= 4) + { + // parse the 32 bit hex into an integer codepoint + uint codePoint; + if (!(success = UInt32.TryParse(new string(json, index, 4), NumberStyles.HexNumber, CultureInfo.InvariantCulture, out codePoint))) + return ""; + + // convert the integer codepoint to a unicode char and add to string + if (0xD800 <= codePoint && codePoint <= 0xDBFF) // if high surrogate + { + index += 4; // skip 4 chars + remainingLength = json.Length - index; + if (remainingLength >= 6) + { + uint lowCodePoint; + if (new string(json, index, 2) == "\\u" && UInt32.TryParse(new string(json, index + 2, 4), NumberStyles.HexNumber, CultureInfo.InvariantCulture, out lowCodePoint)) + { + if (0xDC00 <= lowCodePoint && lowCodePoint <= 0xDFFF) // if low surrogate + { + s.Append((char)codePoint); + s.Append((char)lowCodePoint); + index += 6; // skip 6 chars + continue; + } + } + } + success = false; // invalid surrogate pair + return ""; + } + s.Append(ConvertFromUtf32((int)codePoint)); + // skip 4 chars + index += 4; + } + else + break; + } + } + else + s.Append(c); + } + if (!complete) + { + success = false; + return null; + } + return s.ToString(); + } + + private static string ConvertFromUtf32(int utf32) + { + // http://www.java2s.com/Open-Source/CSharp/2.6.4-mono-.net-core/System/System/Char.cs.htm + if (utf32 < 0 || utf32 > 0x10FFFF) + throw new ArgumentOutOfRangeException("utf32", "The argument must be from 0 to 0x10FFFF."); + if (0xD800 <= utf32 && utf32 <= 0xDFFF) + throw new ArgumentOutOfRangeException("utf32", "The argument must not be in surrogate pair range."); + if (utf32 < 0x10000) + return new string((char)utf32, 1); + utf32 -= 0x10000; + return new string(new char[] { (char)((utf32 >> 10) + 0xD800), (char)(utf32 % 0x0400 + 0xDC00) }); + } + + static object ParseNumber(char[] json, ref int index, ref bool success) + { + EatWhitespace(json, ref index); + int lastIndex = GetLastIndexOfNumber(json, index); + int charLength = (lastIndex - index) + 1; + object returnNumber; + string str = new string(json, index, charLength); + if (str.IndexOf(".", StringComparison.OrdinalIgnoreCase) != -1 || str.IndexOf("e", StringComparison.OrdinalIgnoreCase) != -1) + { + double number; + success = double.TryParse(new string(json, index, charLength), NumberStyles.Any, CultureInfo.InvariantCulture, out number); + returnNumber = number; + } + else + { + long number; + success = long.TryParse(new string(json, index, charLength), NumberStyles.Any, CultureInfo.InvariantCulture, out number); + returnNumber = number; + } + index = lastIndex + 1; + return returnNumber; + } + + static int GetLastIndexOfNumber(char[] json, int index) + { + int lastIndex; + for (lastIndex = index; lastIndex < json.Length; lastIndex++) + if ("0123456789+-.eE".IndexOf(json[lastIndex]) == -1) break; + return lastIndex - 1; + } + + static void EatWhitespace(char[] json, ref int index) + { + for (; index < json.Length; index++) + if (" \t\n\r\b\f".IndexOf(json[index]) == -1) break; + } + + static int LookAhead(char[] json, int index) + { + int saveIndex = index; + return NextToken(json, ref saveIndex); + } + + [SuppressMessage("Microsoft.Maintainability", "CA1502:AvoidExcessiveComplexity")] + static int NextToken(char[] json, ref int index) + { + EatWhitespace(json, ref index); + if (index == json.Length) + return TOKEN_NONE; + char c = json[index]; + index++; + switch (c) + { + case '{': + return TOKEN_CURLY_OPEN; + case '}': + return TOKEN_CURLY_CLOSE; + case '[': + return TOKEN_SQUARED_OPEN; + case ']': + return TOKEN_SQUARED_CLOSE; + case ',': + return TOKEN_COMMA; + case '"': + return TOKEN_STRING; + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + case '-': + return TOKEN_NUMBER; + case ':': + return TOKEN_COLON; + } + index--; + int remainingLength = json.Length - index; + // false + if (remainingLength >= 5) + { + if (json[index] == 'f' && json[index + 1] == 'a' && json[index + 2] == 'l' && json[index + 3] == 's' && json[index + 4] == 'e') + { + index += 5; + return TOKEN_FALSE; + } + } + // true + if (remainingLength >= 4) + { + if (json[index] == 't' && json[index + 1] == 'r' && json[index + 2] == 'u' && json[index + 3] == 'e') + { + index += 4; + return TOKEN_TRUE; + } + } + // null + if (remainingLength >= 4) + { + if (json[index] == 'n' && json[index + 1] == 'u' && json[index + 2] == 'l' && json[index + 3] == 'l') + { + index += 4; + return TOKEN_NULL; + } + } + return TOKEN_NONE; + } + + static bool SerializeValue(IJsonSerializerStrategy jsonSerializerStrategy, object value, StringBuilder builder) + { + bool success = true; + string stringValue = value as string; + if (stringValue != null) + success = SerializeString(stringValue, builder); + else + { + IDictionary dict = value as IDictionary; + if (dict != null) + { + success = SerializeObject(jsonSerializerStrategy, dict.Keys, dict.Values, builder); + } + else + { + IDictionary stringDictionary = value as IDictionary; + if (stringDictionary != null) + { + success = SerializeObject(jsonSerializerStrategy, stringDictionary.Keys, stringDictionary.Values, builder); + } + else + { + IEnumerable enumerableValue = value as IEnumerable; + if (enumerableValue != null) + success = SerializeArray(jsonSerializerStrategy, enumerableValue, builder); + else if (IsNumeric(value)) + success = SerializeNumber(value, builder); + else if (value is bool) + builder.Append((bool)value ? "true" : "false"); + else if (value == null) + builder.Append("null"); + else + { + object serializedObject; + success = jsonSerializerStrategy.TrySerializeNonPrimitiveObject(value, out serializedObject); + if (success) + SerializeValue(jsonSerializerStrategy, serializedObject, builder); + } + } + } + } + return success; + } + + static bool SerializeObject(IJsonSerializerStrategy jsonSerializerStrategy, IEnumerable keys, IEnumerable values, StringBuilder builder) + { + builder.Append("{"); + IEnumerator ke = keys.GetEnumerator(); + IEnumerator ve = values.GetEnumerator(); + bool first = true; + while (ke.MoveNext() && ve.MoveNext()) + { + object key = ke.Current; + object value = ve.Current; + if (!first) + builder.Append(","); + string stringKey = key as string; + if (stringKey != null) + SerializeString(stringKey, builder); + else + if (!SerializeValue(jsonSerializerStrategy, value, builder)) return false; + builder.Append(":"); + if (!SerializeValue(jsonSerializerStrategy, value, builder)) + return false; + first = false; + } + builder.Append("}"); + return true; + } + + static bool SerializeArray(IJsonSerializerStrategy jsonSerializerStrategy, IEnumerable anArray, StringBuilder builder) + { + builder.Append("["); + bool first = true; + foreach (object value in anArray) + { + if (!first) + builder.Append(","); + if (!SerializeValue(jsonSerializerStrategy, value, builder)) + return false; + first = false; + } + builder.Append("]"); + return true; + } + + static bool SerializeString(string aString, StringBuilder builder) + { + // Happy path if there's nothing to be escaped. IndexOfAny is highly optimized (and unmanaged) + if (aString.IndexOfAny(EscapeCharacters) == -1) + { + builder.Append('"'); + builder.Append(aString); + builder.Append('"'); + + return true; + } + + builder.Append('"'); + int safeCharacterCount = 0; + char[] charArray = aString.ToCharArray(); + + for (int i = 0; i < charArray.Length; i++) + { + char c = charArray[i]; + + // Non ascii characters are fine, buffer them up and send them to the builder + // in larger chunks if possible. The escape table is a 1:1 translation table + // with \0 [default(char)] denoting a safe character. + if (c >= EscapeTable.Length || EscapeTable[c] == default(char)) + { + safeCharacterCount++; + } + else + { + if (safeCharacterCount > 0) + { + builder.Append(charArray, i - safeCharacterCount, safeCharacterCount); + safeCharacterCount = 0; + } + + builder.Append('\\'); + builder.Append(EscapeTable[c]); + } + } + + if (safeCharacterCount > 0) + { + builder.Append(charArray, charArray.Length - safeCharacterCount, safeCharacterCount); + } + + builder.Append('"'); + return true; + } + + static bool SerializeNumber(object number, StringBuilder builder) + { + if (number is long) + builder.Append(((long)number).ToString(CultureInfo.InvariantCulture)); + else if (number is ulong) + builder.Append(((ulong)number).ToString(CultureInfo.InvariantCulture)); + else if (number is int) + builder.Append(((int)number).ToString(CultureInfo.InvariantCulture)); + else if (number is uint) + builder.Append(((uint)number).ToString(CultureInfo.InvariantCulture)); + else if (number is decimal) + builder.Append(((decimal)number).ToString(CultureInfo.InvariantCulture)); + else if (number is float) + builder.Append(((float)number).ToString(CultureInfo.InvariantCulture)); + else + builder.Append(Convert.ToDouble(number, CultureInfo.InvariantCulture).ToString("r", CultureInfo.InvariantCulture)); + return true; + } + + /// + /// Determines if a given object is numeric in any way + /// (can be integer, double, null, etc). + /// + static bool IsNumeric(object value) + { + if (value is sbyte) return true; + if (value is byte) return true; + if (value is short) return true; + if (value is ushort) return true; + if (value is int) return true; + if (value is uint) return true; + if (value is long) return true; + if (value is ulong) return true; + if (value is float) return true; + if (value is double) return true; + if (value is decimal) return true; + return false; + } + + private static IJsonSerializerStrategy _currentJsonSerializerStrategy; + public static IJsonSerializerStrategy CurrentJsonSerializerStrategy + { + get + { + return _currentJsonSerializerStrategy ?? + (_currentJsonSerializerStrategy = +#if SIMPLE_JSON_DATACONTRACT + DataContractJsonSerializerStrategy +#else + PocoJsonSerializerStrategy +#endif +); + } + set + { + _currentJsonSerializerStrategy = value; + } + } + + private static PocoJsonSerializerStrategy _pocoJsonSerializerStrategy; + [EditorBrowsable(EditorBrowsableState.Advanced)] + public static PocoJsonSerializerStrategy PocoJsonSerializerStrategy + { + get + { + return _pocoJsonSerializerStrategy ?? (_pocoJsonSerializerStrategy = new PocoJsonSerializerStrategy()); + } + } + +#if SIMPLE_JSON_DATACONTRACT + + private static DataContractJsonSerializerStrategy _dataContractJsonSerializerStrategy; + [System.ComponentModel.EditorBrowsable(EditorBrowsableState.Advanced)] + public static DataContractJsonSerializerStrategy DataContractJsonSerializerStrategy + { + get + { + return _dataContractJsonSerializerStrategy ?? (_dataContractJsonSerializerStrategy = new DataContractJsonSerializerStrategy()); + } + } + +#endif + } + + [GeneratedCode("simple-json", "1.0.0")] +#if SIMPLE_JSON_INTERNAL + internal +#else + public +#endif + interface IJsonSerializerStrategy + { + [SuppressMessage("Microsoft.Design", "CA1007:UseGenericsWhereAppropriate", Justification = "Need to support .NET 2")] + bool TrySerializeNonPrimitiveObject(object input, out object output); + object DeserializeObject(object value, Type type); + } + + [GeneratedCode("simple-json", "1.0.0")] +#if SIMPLE_JSON_INTERNAL + internal +#else + public +#endif + class PocoJsonSerializerStrategy : IJsonSerializerStrategy + { + internal IDictionary ConstructorCache; + internal IDictionary> GetCache; + internal IDictionary>> SetCache; + + internal static readonly Type[] EmptyTypes = new Type[0]; + internal static readonly Type[] ArrayConstructorParameterTypes = new Type[] { typeof(int) }; + + private static readonly string[] Iso8601Format = new string[] + { + @"yyyy-MM-dd\THH:mm:ss.FFFFFFF\Z", + @"yyyy-MM-dd\THH:mm:ss\Z", + @"yyyy-MM-dd\THH:mm:ssK" + }; + + public PocoJsonSerializerStrategy() + { + ConstructorCache = new ReflectionUtils.ThreadSafeDictionary(ContructorDelegateFactory); + GetCache = new ReflectionUtils.ThreadSafeDictionary>(GetterValueFactory); + SetCache = new ReflectionUtils.ThreadSafeDictionary>>(SetterValueFactory); + } + + protected virtual string MapClrMemberNameToJsonFieldName(string clrPropertyName) + { + return clrPropertyName; + } + + internal virtual ReflectionUtils.ConstructorDelegate ContructorDelegateFactory(Type key) + { + return ReflectionUtils.GetContructor(key, key.IsArray ? ArrayConstructorParameterTypes : EmptyTypes); + } + + internal virtual IDictionary GetterValueFactory(Type type) + { + IDictionary result = new Dictionary(); + foreach (PropertyInfo propertyInfo in ReflectionUtils.GetProperties(type)) + { + if (propertyInfo.CanRead) + { + MethodInfo getMethod = ReflectionUtils.GetGetterMethodInfo(propertyInfo); + if (getMethod.IsStatic || !getMethod.IsPublic) + continue; + result[MapClrMemberNameToJsonFieldName(propertyInfo.Name)] = ReflectionUtils.GetGetMethod(propertyInfo); + } + } + foreach (FieldInfo fieldInfo in ReflectionUtils.GetFields(type)) + { + if (fieldInfo.IsStatic || !fieldInfo.IsPublic) + continue; + result[MapClrMemberNameToJsonFieldName(fieldInfo.Name)] = ReflectionUtils.GetGetMethod(fieldInfo); + } + return result; + } + + internal virtual IDictionary> SetterValueFactory(Type type) + { + IDictionary> result = new Dictionary>(); + foreach (PropertyInfo propertyInfo in ReflectionUtils.GetProperties(type)) + { + if (propertyInfo.CanWrite) + { + MethodInfo setMethod = ReflectionUtils.GetSetterMethodInfo(propertyInfo); + if (setMethod.IsStatic || !setMethod.IsPublic) + continue; + result[MapClrMemberNameToJsonFieldName(propertyInfo.Name)] = new KeyValuePair(propertyInfo.PropertyType, ReflectionUtils.GetSetMethod(propertyInfo)); + } + } + foreach (FieldInfo fieldInfo in ReflectionUtils.GetFields(type)) + { + if (fieldInfo.IsInitOnly || fieldInfo.IsStatic || !fieldInfo.IsPublic) + continue; + result[MapClrMemberNameToJsonFieldName(fieldInfo.Name)] = new KeyValuePair(fieldInfo.FieldType, ReflectionUtils.GetSetMethod(fieldInfo)); + } + return result; + } + + public virtual bool TrySerializeNonPrimitiveObject(object input, out object output) + { + return TrySerializeKnownTypes(input, out output) || TrySerializeUnknownTypes(input, out output); + } + + [SuppressMessage("Microsoft.Maintainability", "CA1502:AvoidExcessiveComplexity")] + public virtual object DeserializeObject(object value, Type type) + { + if (type == null) throw new ArgumentNullException("type"); + string str = value as string; + + if (type == typeof(Guid) && string.IsNullOrEmpty(str)) + return default(Guid); + + if (value == null) + return null; + + object obj = null; + + if (str != null) + { + if (str.Length != 0) // We know it can't be null now. + { + if (type == typeof(DateTime) || (ReflectionUtils.IsNullableType(type) && Nullable.GetUnderlyingType(type) == typeof(DateTime))) + return DateTime.ParseExact(str, Iso8601Format, CultureInfo.InvariantCulture, DateTimeStyles.AssumeUniversal | DateTimeStyles.AdjustToUniversal); + if (type == typeof(DateTimeOffset) || (ReflectionUtils.IsNullableType(type) && Nullable.GetUnderlyingType(type) == typeof(DateTimeOffset))) + return DateTimeOffset.ParseExact(str, Iso8601Format, CultureInfo.InvariantCulture, DateTimeStyles.AssumeUniversal | DateTimeStyles.AdjustToUniversal); + if (type == typeof(Guid) || (ReflectionUtils.IsNullableType(type) && Nullable.GetUnderlyingType(type) == typeof(Guid))) + return new Guid(str); + if (type == typeof(Uri)) + { + bool isValid = Uri.IsWellFormedUriString(str, UriKind.RelativeOrAbsolute); + + Uri result; + if (isValid && Uri.TryCreate(str, UriKind.RelativeOrAbsolute, out result)) + return result; + + return null; + } + + if (type == typeof(string)) + return str; + + return Convert.ChangeType(str, type, CultureInfo.InvariantCulture); + } + else + { + if (type == typeof(Guid)) + obj = default(Guid); + else if (ReflectionUtils.IsNullableType(type) && Nullable.GetUnderlyingType(type) == typeof(Guid)) + obj = null; + else + obj = str; + } + // Empty string case + if (!ReflectionUtils.IsNullableType(type) && Nullable.GetUnderlyingType(type) == typeof(Guid)) + return str; + } + else if (value is bool) + return value; + + bool valueIsLong = value is long; + bool valueIsDouble = value is double; + if ((valueIsLong && type == typeof(long)) || (valueIsDouble && type == typeof(double))) + return value; + if ((valueIsDouble && type != typeof(double)) || (valueIsLong && type != typeof(long))) + { + 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) + ? Convert.ChangeType(value, type, CultureInfo.InvariantCulture) + : value; + } + else + { + IDictionary objects = value as IDictionary; + if (objects != null) + { + IDictionary jsonObject = objects; + + if (ReflectionUtils.IsTypeDictionary(type)) + { + // if dictionary then + Type[] types = ReflectionUtils.GetGenericTypeArguments(type); + Type keyType = types[0]; + Type valueType = types[1]; + + Type genericType = typeof(Dictionary<,>).MakeGenericType(keyType, valueType); + + IDictionary dict = (IDictionary)ConstructorCache[genericType](); + + foreach (KeyValuePair kvp in jsonObject) + dict.Add(kvp.Key, DeserializeObject(kvp.Value, valueType)); + + obj = dict; + } + else + { + if (type == typeof(object)) + obj = value; + else + { + obj = ConstructorCache[type](); + foreach (KeyValuePair> setter in SetCache[type]) + { + object jsonValue; + if (jsonObject.TryGetValue(setter.Key, out jsonValue)) + { + jsonValue = DeserializeObject(jsonValue, setter.Value.Key); + setter.Value.Value(obj, jsonValue); + } + } + } + } + } + else + { + IList valueAsList = value as IList; + if (valueAsList != null) + { + IList jsonObject = valueAsList; + IList list = null; + + if (type.IsArray) + { + list = (IList)ConstructorCache[type](jsonObject.Count); + int i = 0; + foreach (object o in jsonObject) + list[i++] = DeserializeObject(o, type.GetElementType()); + } + else if (ReflectionUtils.IsTypeGenericeCollectionInterface(type) || ReflectionUtils.IsAssignableFrom(typeof(IList), type)) + { + Type innerType = ReflectionUtils.GetGenericListElementType(type); + list = (IList)(ConstructorCache[type] ?? ConstructorCache[typeof(List<>).MakeGenericType(innerType)])(jsonObject.Count); + foreach (object o in jsonObject) + list.Add(DeserializeObject(o, innerType)); + } + obj = list; + } + } + return obj; + } + if (ReflectionUtils.IsNullableType(type)) + return ReflectionUtils.ToNullableType(obj, type); + return obj; + } + + protected virtual object SerializeEnum(Enum p) + { + return Convert.ToDouble(p, CultureInfo.InvariantCulture); + } + + [SuppressMessage("Microsoft.Design", "CA1007:UseGenericsWhereAppropriate", Justification = "Need to support .NET 2")] + protected virtual bool TrySerializeKnownTypes(object input, out object output) + { + bool returnValue = true; + if (input is DateTime) + output = ((DateTime)input).ToUniversalTime().ToString(Iso8601Format[0], CultureInfo.InvariantCulture); + else if (input is DateTimeOffset) + output = ((DateTimeOffset)input).ToUniversalTime().ToString(Iso8601Format[0], CultureInfo.InvariantCulture); + else if (input is Guid) + output = ((Guid)input).ToString("D"); + else if (input is Uri) + output = input.ToString(); + else + { + Enum inputEnum = input as Enum; + if (inputEnum != null) + output = SerializeEnum(inputEnum); + else + { + returnValue = false; + output = null; + } + } + return returnValue; + } + [SuppressMessage("Microsoft.Design", "CA1007:UseGenericsWhereAppropriate", Justification = "Need to support .NET 2")] + protected virtual bool TrySerializeUnknownTypes(object input, out object output) + { + if (input == null) throw new ArgumentNullException("input"); + output = null; + Type type = input.GetType(); + if (type.FullName == null) + return false; + IDictionary obj = new JsonObject(); + IDictionary getters = GetCache[type]; + foreach (KeyValuePair getter in getters) + { + if (getter.Value != null) + obj.Add(MapClrMemberNameToJsonFieldName(getter.Key), getter.Value(input)); + } + output = obj; + return true; + } + } + +#if SIMPLE_JSON_DATACONTRACT + [GeneratedCode("simple-json", "1.0.0")] +#if SIMPLE_JSON_INTERNAL + internal +#else + public +#endif + class DataContractJsonSerializerStrategy : PocoJsonSerializerStrategy + { + public DataContractJsonSerializerStrategy() + { + GetCache = new ReflectionUtils.ThreadSafeDictionary>(GetterValueFactory); + SetCache = new ReflectionUtils.ThreadSafeDictionary>>(SetterValueFactory); + } + + internal override IDictionary GetterValueFactory(Type type) + { + bool hasDataContract = ReflectionUtils.GetAttribute(type, typeof(DataContractAttribute)) != null; + if (!hasDataContract) + return base.GetterValueFactory(type); + string jsonKey; + IDictionary result = new Dictionary(); + foreach (PropertyInfo propertyInfo in ReflectionUtils.GetProperties(type)) + { + if (propertyInfo.CanRead) + { + MethodInfo getMethod = ReflectionUtils.GetGetterMethodInfo(propertyInfo); + if (!getMethod.IsStatic && CanAdd(propertyInfo, out jsonKey)) + result[jsonKey] = ReflectionUtils.GetGetMethod(propertyInfo); + } + } + foreach (FieldInfo fieldInfo in ReflectionUtils.GetFields(type)) + { + if (!fieldInfo.IsStatic && CanAdd(fieldInfo, out jsonKey)) + result[jsonKey] = ReflectionUtils.GetGetMethod(fieldInfo); + } + return result; + } + + internal override IDictionary> SetterValueFactory(Type type) + { + bool hasDataContract = ReflectionUtils.GetAttribute(type, typeof(DataContractAttribute)) != null; + if (!hasDataContract) + return base.SetterValueFactory(type); + string jsonKey; + IDictionary> result = new Dictionary>(); + foreach (PropertyInfo propertyInfo in ReflectionUtils.GetProperties(type)) + { + if (propertyInfo.CanWrite) + { + MethodInfo setMethod = ReflectionUtils.GetSetterMethodInfo(propertyInfo); + if (!setMethod.IsStatic && CanAdd(propertyInfo, out jsonKey)) + result[jsonKey] = new KeyValuePair(propertyInfo.PropertyType, ReflectionUtils.GetSetMethod(propertyInfo)); + } + } + foreach (FieldInfo fieldInfo in ReflectionUtils.GetFields(type)) + { + if (!fieldInfo.IsInitOnly && !fieldInfo.IsStatic && CanAdd(fieldInfo, out jsonKey)) + result[jsonKey] = new KeyValuePair(fieldInfo.FieldType, ReflectionUtils.GetSetMethod(fieldInfo)); + } + // todo implement sorting for DATACONTRACT. + return result; + } + + private static bool CanAdd(MemberInfo info, out string jsonKey) + { + jsonKey = null; + if (ReflectionUtils.GetAttribute(info, typeof(IgnoreDataMemberAttribute)) != null) + return false; + DataMemberAttribute dataMemberAttribute = (DataMemberAttribute)ReflectionUtils.GetAttribute(info, typeof(DataMemberAttribute)); + if (dataMemberAttribute == null) + return false; + jsonKey = string.IsNullOrEmpty(dataMemberAttribute.Name) ? info.Name : dataMemberAttribute.Name; + return true; + } + } + +#endif + + namespace Reflection + { + // This class is meant to be copied into other libraries. So we want to exclude it from Code Analysis rules + // that might be in place in the target project. + [GeneratedCode("reflection-utils", "1.0.0")] +#if SIMPLE_JSON_REFLECTION_UTILS_PUBLIC + public +#else + internal +#endif + class ReflectionUtils + { + private static readonly object[] EmptyObjects = new object[] { }; + + public delegate object GetDelegate(object source); + public delegate void SetDelegate(object source, object value); + public delegate object ConstructorDelegate(params object[] args); + + public delegate TValue ThreadSafeDictionaryValueFactory(TKey key); + +#if SIMPLE_JSON_TYPEINFO + public static TypeInfo GetTypeInfo(Type type) + { + return type.GetTypeInfo(); + } +#else + public static Type GetTypeInfo(Type type) + { + return type; + } +#endif + + public static Attribute GetAttribute(MemberInfo info, Type type) + { +#if SIMPLE_JSON_TYPEINFO + if (info == null || type == null || !info.IsDefined(type)) + return null; + return info.GetCustomAttribute(type); +#else + if (info == null || type == null || !Attribute.IsDefined(info, type)) + return null; + return Attribute.GetCustomAttribute(info, type); +#endif + } + + public static Type GetGenericListElementType(Type type) + { + IEnumerable interfaces; +#if SIMPLE_JSON_TYPEINFO + interfaces = type.GetTypeInfo().ImplementedInterfaces; +#else + interfaces = type.GetInterfaces(); +#endif + foreach (Type implementedInterface in interfaces) + { + if (IsTypeGeneric(implementedInterface) && + implementedInterface.GetGenericTypeDefinition() == typeof(IList<>)) + { + return GetGenericTypeArguments(implementedInterface)[0]; + } + } + return GetGenericTypeArguments(type)[0]; + } + + public static Attribute GetAttribute(Type objectType, Type attributeType) + { + +#if SIMPLE_JSON_TYPEINFO + if (objectType == null || attributeType == null || !objectType.GetTypeInfo().IsDefined(attributeType)) + return null; + return objectType.GetTypeInfo().GetCustomAttribute(attributeType); +#else + if (objectType == null || attributeType == null || !Attribute.IsDefined(objectType, attributeType)) + return null; + return Attribute.GetCustomAttribute(objectType, attributeType); +#endif + } + + public static Type[] GetGenericTypeArguments(Type type) + { +#if SIMPLE_JSON_TYPEINFO + return type.GetTypeInfo().GenericTypeArguments; +#else + return type.GetGenericArguments(); +#endif + } + + public static bool IsTypeGeneric(Type type) + { + return GetTypeInfo(type).IsGenericType; + } + + public static bool IsTypeGenericeCollectionInterface(Type type) + { + if (!IsTypeGeneric(type)) + return false; + + Type genericDefinition = type.GetGenericTypeDefinition(); + + return (genericDefinition == typeof(IList<>) + || genericDefinition == typeof(ICollection<>) + || genericDefinition == typeof(IEnumerable<>) +#if SIMPLE_JSON_READONLY_COLLECTIONS + || genericDefinition == typeof(IReadOnlyCollection<>) + || genericDefinition == typeof(IReadOnlyList<>) +#endif + ); + } + + public static bool IsAssignableFrom(Type type1, Type type2) + { + return GetTypeInfo(type1).IsAssignableFrom(GetTypeInfo(type2)); + } + + public static bool IsTypeDictionary(Type type) + { +#if SIMPLE_JSON_TYPEINFO + if (typeof(IDictionary<,>).GetTypeInfo().IsAssignableFrom(type.GetTypeInfo())) + return true; +#else + if (typeof(System.Collections.IDictionary).IsAssignableFrom(type)) + return true; +#endif + if (!GetTypeInfo(type).IsGenericType) + return false; + + Type genericDefinition = type.GetGenericTypeDefinition(); + return genericDefinition == typeof(IDictionary<,>); + } + + public static bool IsNullableType(Type type) + { + return GetTypeInfo(type).IsGenericType && type.GetGenericTypeDefinition() == typeof(Nullable<>); + } + + public static object ToNullableType(object obj, Type nullableType) + { + return obj == null ? null : Convert.ChangeType(obj, Nullable.GetUnderlyingType(nullableType), CultureInfo.InvariantCulture); + } + + public static bool IsValueType(Type type) + { + return GetTypeInfo(type).IsValueType; + } + + public static IEnumerable GetConstructors(Type type) + { +#if SIMPLE_JSON_TYPEINFO + return type.GetTypeInfo().DeclaredConstructors; +#else + return type.GetConstructors(); +#endif + } + + public static ConstructorInfo GetConstructorInfo(Type type, params Type[] argsType) + { + IEnumerable constructorInfos = GetConstructors(type); + int i; + bool matches; + foreach (ConstructorInfo constructorInfo in constructorInfos) + { + ParameterInfo[] parameters = constructorInfo.GetParameters(); + if (argsType.Length != parameters.Length) + continue; + + i = 0; + matches = true; + foreach (ParameterInfo parameterInfo in constructorInfo.GetParameters()) + { + if (parameterInfo.ParameterType != argsType[i]) + { + matches = false; + break; + } + } + + if (matches) + return constructorInfo; + } + + return null; + } + + public static IEnumerable GetProperties(Type type) + { +#if SIMPLE_JSON_TYPEINFO + return type.GetRuntimeProperties(); +#else + return type.GetProperties(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static); +#endif + } + + public static IEnumerable GetFields(Type type) + { +#if SIMPLE_JSON_TYPEINFO + return type.GetRuntimeFields(); +#else + return type.GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static); +#endif + } + + public static MethodInfo GetGetterMethodInfo(PropertyInfo propertyInfo) + { +#if SIMPLE_JSON_TYPEINFO + return propertyInfo.GetMethod; +#else + return propertyInfo.GetGetMethod(true); +#endif + } + + public static MethodInfo GetSetterMethodInfo(PropertyInfo propertyInfo) + { +#if SIMPLE_JSON_TYPEINFO + return propertyInfo.SetMethod; +#else + return propertyInfo.GetSetMethod(true); +#endif + } + + public static ConstructorDelegate GetContructor(ConstructorInfo constructorInfo) + { +#if SIMPLE_JSON_NO_LINQ_EXPRESSION + return GetConstructorByReflection(constructorInfo); +#else + return GetConstructorByExpression(constructorInfo); +#endif + } + + public static ConstructorDelegate GetContructor(Type type, params Type[] argsType) + { +#if SIMPLE_JSON_NO_LINQ_EXPRESSION + return GetConstructorByReflection(type, argsType); +#else + return GetConstructorByExpression(type, argsType); +#endif + } + + public static ConstructorDelegate GetConstructorByReflection(ConstructorInfo constructorInfo) + { + return delegate (object[] args) { return constructorInfo.Invoke(args); }; + } + + public static ConstructorDelegate GetConstructorByReflection(Type type, params Type[] argsType) + { + ConstructorInfo constructorInfo = GetConstructorInfo(type, argsType); + return constructorInfo == null ? null : GetConstructorByReflection(constructorInfo); + } + +#if !SIMPLE_JSON_NO_LINQ_EXPRESSION + + public static ConstructorDelegate GetConstructorByExpression(ConstructorInfo constructorInfo) + { + ParameterInfo[] paramsInfo = constructorInfo.GetParameters(); + ParameterExpression param = Expression.Parameter(typeof(object[]), "args"); + Expression[] argsExp = new Expression[paramsInfo.Length]; + for (int i = 0; i < paramsInfo.Length; i++) + { + Expression index = Expression.Constant(i); + Type paramType = paramsInfo[i].ParameterType; + Expression paramAccessorExp = Expression.ArrayIndex(param, index); + Expression paramCastExp = Expression.Convert(paramAccessorExp, paramType); + argsExp[i] = paramCastExp; + } + NewExpression newExp = Expression.New(constructorInfo, argsExp); + Expression> lambda = Expression.Lambda>(newExp, param); + Func compiledLambda = lambda.Compile(); + return delegate (object[] args) { return compiledLambda(args); }; + } + + public static ConstructorDelegate GetConstructorByExpression(Type type, params Type[] argsType) + { + ConstructorInfo constructorInfo = GetConstructorInfo(type, argsType); + return constructorInfo == null ? null : GetConstructorByExpression(constructorInfo); + } + +#endif + + public static GetDelegate GetGetMethod(PropertyInfo propertyInfo) + { +#if SIMPLE_JSON_NO_LINQ_EXPRESSION + return GetGetMethodByReflection(propertyInfo); +#else + return GetGetMethodByExpression(propertyInfo); +#endif + } + + public static GetDelegate GetGetMethod(FieldInfo fieldInfo) + { +#if SIMPLE_JSON_NO_LINQ_EXPRESSION + return GetGetMethodByReflection(fieldInfo); +#else + return GetGetMethodByExpression(fieldInfo); +#endif + } + + public static GetDelegate GetGetMethodByReflection(PropertyInfo propertyInfo) + { + MethodInfo methodInfo = GetGetterMethodInfo(propertyInfo); + return delegate (object source) { return methodInfo.Invoke(source, EmptyObjects); }; + } + + public static GetDelegate GetGetMethodByReflection(FieldInfo fieldInfo) + { + return delegate (object source) { return fieldInfo.GetValue(source); }; + } + +#if !SIMPLE_JSON_NO_LINQ_EXPRESSION + + public static GetDelegate GetGetMethodByExpression(PropertyInfo propertyInfo) + { + MethodInfo getMethodInfo = GetGetterMethodInfo(propertyInfo); + ParameterExpression instance = Expression.Parameter(typeof(object), "instance"); + UnaryExpression instanceCast = (!IsValueType(propertyInfo.DeclaringType)) ? Expression.TypeAs(instance, propertyInfo.DeclaringType) : Expression.Convert(instance, propertyInfo.DeclaringType); + Func compiled = Expression.Lambda>(Expression.TypeAs(Expression.Call(instanceCast, getMethodInfo), typeof(object)), instance).Compile(); + return delegate (object source) { return compiled(source); }; + } + + public static GetDelegate GetGetMethodByExpression(FieldInfo fieldInfo) + { + ParameterExpression instance = Expression.Parameter(typeof(object), "instance"); + MemberExpression member = Expression.Field(Expression.Convert(instance, fieldInfo.DeclaringType), fieldInfo); + GetDelegate compiled = Expression.Lambda(Expression.Convert(member, typeof(object)), instance).Compile(); + return delegate (object source) { return compiled(source); }; + } + +#endif + + public static SetDelegate GetSetMethod(PropertyInfo propertyInfo) + { +#if SIMPLE_JSON_NO_LINQ_EXPRESSION + return GetSetMethodByReflection(propertyInfo); +#else + return GetSetMethodByExpression(propertyInfo); +#endif + } + + public static SetDelegate GetSetMethod(FieldInfo fieldInfo) + { +#if SIMPLE_JSON_NO_LINQ_EXPRESSION + return GetSetMethodByReflection(fieldInfo); +#else + return GetSetMethodByExpression(fieldInfo); +#endif + } + + public static SetDelegate GetSetMethodByReflection(PropertyInfo propertyInfo) + { + MethodInfo methodInfo = GetSetterMethodInfo(propertyInfo); + return delegate (object source, object value) { methodInfo.Invoke(source, new object[] { value }); }; + } + + public static SetDelegate GetSetMethodByReflection(FieldInfo fieldInfo) + { + return delegate (object source, object value) { fieldInfo.SetValue(source, value); }; + } + +#if !SIMPLE_JSON_NO_LINQ_EXPRESSION + + public static SetDelegate GetSetMethodByExpression(PropertyInfo propertyInfo) + { + MethodInfo setMethodInfo = GetSetterMethodInfo(propertyInfo); + ParameterExpression instance = Expression.Parameter(typeof(object), "instance"); + ParameterExpression value = Expression.Parameter(typeof(object), "value"); + UnaryExpression instanceCast = (!IsValueType(propertyInfo.DeclaringType)) ? Expression.TypeAs(instance, propertyInfo.DeclaringType) : Expression.Convert(instance, propertyInfo.DeclaringType); + UnaryExpression valueCast = (!IsValueType(propertyInfo.PropertyType)) ? Expression.TypeAs(value, propertyInfo.PropertyType) : Expression.Convert(value, propertyInfo.PropertyType); + Action compiled = Expression.Lambda>(Expression.Call(instanceCast, setMethodInfo, valueCast), new ParameterExpression[] { instance, value }).Compile(); + return delegate (object source, object val) { compiled(source, val); }; + } + + public static SetDelegate GetSetMethodByExpression(FieldInfo fieldInfo) + { + ParameterExpression instance = Expression.Parameter(typeof(object), "instance"); + ParameterExpression value = Expression.Parameter(typeof(object), "value"); + Action compiled = Expression.Lambda>( + Assign(Expression.Field(Expression.Convert(instance, fieldInfo.DeclaringType), fieldInfo), Expression.Convert(value, fieldInfo.FieldType)), instance, value).Compile(); + return delegate (object source, object val) { compiled(source, val); }; + } + + public static BinaryExpression Assign(Expression left, Expression right) + { +#if SIMPLE_JSON_TYPEINFO + return Expression.Assign(left, right); +#else + MethodInfo assign = typeof(Assigner<>).MakeGenericType(left.Type).GetMethod("Assign"); + BinaryExpression assignExpr = Expression.Add(left, right, assign); + return assignExpr; +#endif + } + + private static class Assigner + { + public static T Assign(ref T left, T right) + { + return (left = right); + } + } + +#endif + + public sealed class ThreadSafeDictionary : IDictionary + { + private readonly object _lock = new object(); + private readonly ThreadSafeDictionaryValueFactory _valueFactory; + private Dictionary _dictionary; + + public ThreadSafeDictionary(ThreadSafeDictionaryValueFactory valueFactory) + { + _valueFactory = valueFactory; + } + + private TValue Get(TKey key) + { + if (_dictionary == null) + return AddValue(key); + TValue value; + if (!_dictionary.TryGetValue(key, out value)) + return AddValue(key); + return value; + } + + private TValue AddValue(TKey key) + { + TValue value = _valueFactory(key); + lock (_lock) + { + if (_dictionary == null) + { + _dictionary = new Dictionary(); + _dictionary[key] = value; + } + else + { + TValue val; + if (_dictionary.TryGetValue(key, out val)) + return val; + Dictionary dict = new Dictionary(_dictionary); + dict[key] = value; + _dictionary = dict; + } + } + return value; + } + + public void Add(TKey key, TValue value) + { + throw new NotImplementedException(); + } + + public bool ContainsKey(TKey key) + { + return _dictionary.ContainsKey(key); + } + + public ICollection Keys + { + get { return _dictionary.Keys; } + } + + public bool Remove(TKey key) + { + throw new NotImplementedException(); + } + + public bool TryGetValue(TKey key, out TValue value) + { + value = this[key]; + return true; + } + + public ICollection Values + { + get { return _dictionary.Values; } + } + + public TValue this[TKey key] + { + get { return Get(key); } + set { throw new NotImplementedException(); } + } + + public void Add(KeyValuePair item) + { + throw new NotImplementedException(); + } + + public void Clear() + { + throw new NotImplementedException(); + } + + public bool Contains(KeyValuePair item) + { + throw new NotImplementedException(); + } + + public void CopyTo(KeyValuePair[] array, int arrayIndex) + { + throw new NotImplementedException(); + } + + public int Count + { + get { return _dictionary.Count; } + } + + public bool IsReadOnly + { + get { throw new NotImplementedException(); } + } + + public bool Remove(KeyValuePair item) + { + throw new NotImplementedException(); + } + + public IEnumerator> GetEnumerator() + { + return _dictionary.GetEnumerator(); + } + + System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() + { + return _dictionary.GetEnumerator(); + } + } + + } + } +} +// ReSharper restore LoopCanBeConvertedToQuery +// ReSharper restore RedundantExplicitArrayCreation +// 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 @@ +# WixBuildTools +Internal 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 @@ +namespace TablesAndSymbols +{ + public enum ColumnCategory + { + Unknown, + Text, + UpperCase, + LowerCase, + Integer, + DoubleInteger, + TimeDate, + Identifier, + Property, + Filename, + WildCardFilename, + Path, + Paths, + AnyPath, + DefaultDir, + RegPath, + Formatted, + Template, + Condition, + Guid, + Version, + Language, + Binary, + CustomSource, + Cabinet, + Shortcut, + FormattedSDDLText, + } + + public enum ColumnModularizeType + { + None, + Column, + Icon, + CompanionFile, + Condition, + ControlEventArgument, + ControlText, + Property, + SemicolonDelimited, + } + + public enum ColumnType + { + Unknown, + String, + Localized, + Number, + Object, + Preserved, + } +} 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 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; +using System.Text.RegularExpressions; +using System.Xml.Linq; +using SimpleJson; + +namespace TablesAndSymbols +{ + class Program + { + static void Main(string[] args) + { + if (args.Length == 0) + { + return; + } + else if (Path.GetExtension(args[0]) == ".xml") + { + if (args.Length < 2) + { + Console.WriteLine("Need to specify output json file as well."); + return; + } + if (Path.GetExtension(args[1]) != ".json") + { + Console.WriteLine("Output needs to be .json"); + return; + } + + string prefix = null; + if (args.Length > 2) + { + prefix = args[2]; + } + + var csFile = Path.Combine(Path.GetDirectoryName(args[1]), String.Concat(prefix ?? "WindowsInstaller", "TableDefinitions.cs")); + + ReadXmlWriteJson(Path.GetFullPath(args[0]), Path.GetFullPath(args[1]), Path.GetFullPath(csFile), prefix); + } + else if (Path.GetExtension(args[0]) == ".json") + { + string prefix = null; + if (args.Length < 2) + { + Console.WriteLine("Need to specify output folder."); + return; + } + else if (args.Length > 2) + { + prefix = args[2]; + } + + ReadJsonWriteCs(Path.GetFullPath(args[0]), Path.GetFullPath(args[1]), prefix); + } + } + + private static void ReadXmlWriteJson(string inputPath, string outputPath, string csOutputPath, string prefix) + { + var tableDefinitions = ReadXmlWriteCs(inputPath, csOutputPath, prefix); + + var array = new JsonArray(); + + foreach (var tableDefinition in tableDefinitions) + { + if (tableDefinition.Symbolless) + { + continue; + } + var symbolType = tableDefinition.SymbolDefinitionName; + + var fields = new JsonArray(); + var firstField = true; + + foreach (var columnDefinition in tableDefinition.Columns) + { + if (firstField) + { + firstField = false; + if (tableDefinition.SymbolIdIsPrimaryKey) + { + continue; + } + } + + var fieldName = columnDefinition.Name; + fieldName = Regex.Replace(fieldName, "^([^_]+)_([^_]*)$", x => + { + return $"{x.Groups[2].Value}{x.Groups[1].Value}Ref"; + }); + var type = columnDefinition.Type.ToString().ToLower(); + + if (type == "localized") + { + type = "string"; + } + else if (type == "object") + { + type = "path"; + } + else if (columnDefinition.Type == ColumnType.Number && columnDefinition.Length == 2 && + columnDefinition.MinValue == 0 && columnDefinition.MaxValue == 1) + { + type = "bool"; + } + + if (columnDefinition.Type == ColumnType.Number && columnDefinition.Nullable) + { + type += "?"; + } + + var field = new JsonObject + { + { fieldName, type } + }; + + fields.Add(field); + } + + var obj = new JsonObject + { + { symbolType, fields } + }; + array.Add(obj); + } + + array.Sort(CompareSymbolDefinitions); + + var strat = new PocoJsonSerializerStrategy(); + var json = SimpleJson.SimpleJson.SerializeObject(array, strat); + + Directory.CreateDirectory(Path.GetDirectoryName(outputPath)); + File.WriteAllText(outputPath, json); + } + + private static List ReadXmlWriteCs(string inputPath, string outputPath, string prefix) + { + var tableDefinitions = WixTableDefinition.LoadCollection(inputPath); + var text = GenerateCsTableDefinitionsFileText(prefix, tableDefinitions); + Console.WriteLine("Writing: {0}", outputPath); + File.WriteAllText(outputPath, text); + return tableDefinitions; + } + + private static void ReadJsonWriteCs(string inputPath, string outputFolder, string prefix) + { + var json = File.ReadAllText(inputPath); + var symbols = SimpleJson.SimpleJson.DeserializeObject(json) as JsonArray; + + var symbolNames = new List(); + + foreach (var symbolDefinition in symbols.Cast()) + { + var symbolName = symbolDefinition.Keys.Single(); + var fields = symbolDefinition.Values.Single() as JsonArray; + + var list = GetFields(fields).ToList(); + + symbolNames.Add(symbolName); + + var text = GenerateSymbolFileText(prefix, symbolName, list); + + var pathSymbol = Path.Combine(outputFolder, symbolName + "Symbol.cs"); + Console.WriteLine("Writing: {0}", pathSymbol); + File.WriteAllText(pathSymbol, text); + } + + var content = SymbolNamesFileContent(prefix, symbolNames); + var pathNames = Path.Combine(outputFolder, String.Concat(prefix, "SymbolDefinitions.cs")); + Console.WriteLine("Writing: {0}", pathNames); + File.WriteAllText(pathNames, content); + } + + private static IEnumerable<(string Name, string Type, string ClrType, string AsFunction)> GetFields(JsonArray fields) + { + foreach (var field in fields.Cast()) + { + var fieldName = field.Keys.Single(); + var fieldType = field.Values.Single() as string; + + var clrType = ConvertToClrType(fieldType); + fieldType = ConvertToFieldType(fieldType); + + var asFunction = $"As{(clrType.Contains("?") ? "Nullable" : "")}{fieldType}()"; + + yield return (Name: fieldName, Type: fieldType, ClrType: clrType, AsFunction: asFunction); + } + } + + private static string GenerateCsTableDefinitionsFileText(string prefix, List tableDefinitions) + { + var ns = prefix ?? "Data"; + + var startClassDef = String.Join(Environment.NewLine, + "// 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.", + "", + "namespace WixToolset.{1}", + "{", + " using WixToolset.Data.WindowsInstaller;", + "", + " public static class {2}TableDefinitions", + " {"); + var startTableDef = String.Join(Environment.NewLine, + " public static readonly TableDefinition {1} = new TableDefinition(", + " \"{2}\",", + " {3},", + " new[]", + " {"); + var columnDef = + " new ColumnDefinition(\"{1}\", ColumnType.{2}, {3}, primaryKey: {4}, nullable: {5}, ColumnCategory.{6}"; + var endColumnsDef = String.Join(Environment.NewLine, + " },"); + var unrealDef = + " unreal: true,"; + var endTableDef = String.Join(Environment.NewLine, + " symbolIdIsPrimaryKey: {1}", + " );", + ""); + var startAllTablesDef = String.Join(Environment.NewLine, + " public static readonly TableDefinition[] All = new[]", + " {"); + var allTableDef = + " {1},"; + var endAllTablesDef = + " };"; + var endClassDef = String.Join(Environment.NewLine, + " }", + "}"); + + var sb = new StringBuilder(); + + sb.AppendLine(startClassDef.Replace("{1}", ns).Replace("{2}", prefix)); + foreach (var tableDefinition in tableDefinitions) + { + var symbolDefinition = tableDefinition.Symbolless ? "null" : $"{prefix}SymbolDefinitions.{tableDefinition.SymbolDefinitionName}"; + sb.AppendLine(startTableDef.Replace("{1}", tableDefinition.VariableName).Replace("{2}", tableDefinition.Name).Replace("{3}", symbolDefinition)); + foreach (var columnDefinition in tableDefinition.Columns) + { + sb.Append(columnDef.Replace("{1}", columnDefinition.Name).Replace("{2}", columnDefinition.Type.ToString()).Replace("{3}", columnDefinition.Length.ToString()) + .Replace("{4}", columnDefinition.PrimaryKey.ToString().ToLower()).Replace("{5}", columnDefinition.Nullable.ToString().ToLower()).Replace("{6}", columnDefinition.Category.ToString())); + if (columnDefinition.MinValue.HasValue) + { + sb.AppendFormat(", minValue: {0}", columnDefinition.MinValue.Value); + } + if (columnDefinition.MaxValue.HasValue) + { + sb.AppendFormat(", maxValue: {0}", columnDefinition.MaxValue.Value); + } + if (!String.IsNullOrEmpty(columnDefinition.KeyTable)) + { + sb.AppendFormat(", keyTable: \"{0}\"", columnDefinition.KeyTable); + } + if (columnDefinition.KeyColumn.HasValue) + { + sb.AppendFormat(", keyColumn: {0}", columnDefinition.KeyColumn.Value); + } + if (!String.IsNullOrEmpty(columnDefinition.Possibilities)) + { + sb.AppendFormat(", possibilities: \"{0}\"", columnDefinition.Possibilities); + } + if (!String.IsNullOrEmpty(columnDefinition.Description)) + { + sb.AppendFormat(", description: \"{0}\"", columnDefinition.Description.Replace("\\", "\\\\").Replace("\"", "\\\"")); + } + if (columnDefinition.ModularizeType.HasValue && columnDefinition.ModularizeType.Value != ColumnModularizeType.None) + { + sb.AppendFormat(", modularizeType: ColumnModularizeType.{0}", columnDefinition.ModularizeType.ToString()); + } + if (columnDefinition.ForceLocalizable) + { + sb.Append(", forceLocalizable: true"); + } + if (columnDefinition.UseCData) + { + sb.Append(", useCData: true"); + } + if (columnDefinition.Unreal) + { + sb.Append(", unreal: true"); + } + sb.AppendLine("),"); + } + sb.AppendLine(endColumnsDef); + if (tableDefinition.Unreal) + { + sb.AppendLine(unrealDef); + } + sb.AppendLine(endTableDef.Replace("{1}", tableDefinition.SymbolIdIsPrimaryKey.ToString().ToLower())); + } + sb.AppendLine(startAllTablesDef); + foreach (var tableDefinition in tableDefinitions) + { + sb.AppendLine(allTableDef.Replace("{1}", tableDefinition.VariableName)); + } + sb.AppendLine(endAllTablesDef); + sb.AppendLine(endClassDef); + + return sb.ToString(); + } + + private static string GenerateSymbolFileText(string prefix, string symbolName, List<(string Name, string Type, string ClrType, string AsFunction)> symbolFields) + { + var ns = prefix ?? "Data"; + var toString = String.IsNullOrEmpty(prefix) ? null : ".ToString()"; + + var startFileDef = String.Join(Environment.NewLine, + "// 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.", + "", + "namespace WixToolset.{2}", + "{"); + var usingDataDef = + " using WixToolset.Data;"; + var startSymbolDef = String.Join(Environment.NewLine, + " using WixToolset.{2}.Symbols;", + "", + " public static partial class {3}SymbolDefinitions", + " {", + " public static readonly IntermediateSymbolDefinition {1} = new IntermediateSymbolDefinition(", + " {3}SymbolDefinitionType.{1}{4},", + " new{5}[]", + " {"); + var fieldDef = + " new IntermediateFieldDefinition(nameof({1}SymbolFields.{2}), IntermediateFieldType.{3}),"; + var endSymbolDef = String.Join(Environment.NewLine, + " },", + " typeof({1}Symbol));", + " }", + "}", + "", + "namespace WixToolset.{2}.Symbols", + "{"); + var startEnumDef = String.Join(Environment.NewLine, + " public enum {1}SymbolFields", + " {"); + var fieldEnum = + " {2},"; + var startSymbol = String.Join(Environment.NewLine, + " }", + "", + " public class {1}Symbol : IntermediateSymbol", + " {", + " public {1}Symbol() : base({3}SymbolDefinitions.{1}, null, null)", + " {", + " }", + "", + " public {1}Symbol(SourceLineNumber sourceLineNumber, Identifier id = null) : base({3}SymbolDefinitions.{1}, sourceLineNumber, id)", + " {", + " }", + "", + " public IntermediateField this[{1}SymbolFields index] => this.Fields[(int)index];"); + var fieldProp = String.Join(Environment.NewLine, + "", + " public {4} {2}", + " {", + " get => {6}this.Fields[(int){1}SymbolFields.{2}]{5};", + " set => this.Set((int){1}SymbolFields.{2}, value);", + " }"); + var endSymbol = String.Join(Environment.NewLine, + " }", + "}"); + + var sb = new StringBuilder(); + + sb.AppendLine(startFileDef.Replace("{2}", ns)); + if (ns != "Data") + { + sb.AppendLine(usingDataDef); + } + sb.AppendLine(startSymbolDef.Replace("{1}", symbolName).Replace("{2}", ns).Replace("{3}", prefix).Replace("{4}", toString).Replace("{5}", symbolFields.Any() ? null : " IntermediateFieldDefinition")); + foreach (var field in symbolFields) + { + sb.AppendLine(fieldDef.Replace("{1}", symbolName).Replace("{2}", field.Name).Replace("{3}", field.Type)); + } + sb.AppendLine(endSymbolDef.Replace("{1}", symbolName).Replace("{2}", ns).Replace("{3}", prefix)); + if (ns != "Data") + { + sb.AppendLine(usingDataDef); + sb.AppendLine(); + } + sb.AppendLine(startEnumDef.Replace("{1}", symbolName)); + foreach (var field in symbolFields) + { + sb.AppendLine(fieldEnum.Replace("{1}", symbolName).Replace("{2}", field.Name)); + } + sb.AppendLine(startSymbol.Replace("{1}", symbolName).Replace("{2}", ns).Replace("{3}", prefix)); + foreach (var field in symbolFields) + { + var useCast = ns == "Data" && field.AsFunction != "AsPath()"; + var cast = useCast ? $"({field.ClrType})" : null; + var asFunction = useCast ? null : $".{field.AsFunction}"; + sb.AppendLine(fieldProp.Replace("{1}", symbolName).Replace("{2}", field.Name).Replace("{3}", field.Type).Replace("{4}", field.ClrType).Replace("{5}", asFunction).Replace("{6}", cast)); + } + sb.Append(endSymbol); + + return sb.ToString(); + } + + private static string SymbolNamesFileContent(string prefix, List symbolNames) + { + var ns = prefix ?? "Data"; + + var header = String.Join(Environment.NewLine, + "// 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.", + "", + "namespace WixToolset.{2}", + "{", + " using System;", + " using WixToolset.Data;", + "", + " public enum {3}SymbolDefinitionType", + " {"); + var namesFormat = + " {1},"; + var midpoint = String.Join(Environment.NewLine, + " }", + "", + " public static partial class {3}SymbolDefinitions", + " {", + " public static readonly Version Version = new Version(\"4.0.0\");", + "", + " public static IntermediateSymbolDefinition ByName(string name)", + " {", + " if (!Enum.TryParse(name, out {3}SymbolDefinitionType type))", + " {", + " return null;", + " }", + "", + " return ByType(type);", + " }", + "", + " public static IntermediateSymbolDefinition ByType({3}SymbolDefinitionType type)", + " {", + " switch (type)", + " {"); + + var caseFormat = String.Join(Environment.NewLine, + " case {3}SymbolDefinitionType.{1}:", + " return {3}SymbolDefinitions.{1};", + ""); + + var footer = String.Join(Environment.NewLine, + " default:", + " throw new ArgumentOutOfRangeException(nameof(type));", + " }", + " }", + " }", + "}"); + + var sb = new StringBuilder(); + + sb.AppendLine(header.Replace("{2}", ns).Replace("{3}", prefix)); + foreach (var symbolName in symbolNames) + { + sb.AppendLine(namesFormat.Replace("{1}", symbolName).Replace("{2}", ns).Replace("{3}", prefix)); + } + sb.AppendLine(midpoint.Replace("{2}", ns).Replace("{3}", prefix)); + foreach (var symbolName in symbolNames) + { + sb.AppendLine(caseFormat.Replace("{1}", symbolName).Replace("{2}", ns).Replace("{3}", prefix)); + } + sb.AppendLine(footer); + + return sb.ToString(); + } + + private static string ConvertToFieldType(string fieldType) + { + switch (fieldType.ToLowerInvariant()) + { + case "bool": + return "Bool"; + case "bool?": + return "Number"; + + case "string": + case "preserved": + return "String"; + + case "number": + case "number?": + return "Number"; + + case "path": + return "Path"; + } + + throw new ArgumentException(fieldType); + } + + private static string ConvertToClrType(string fieldType) + { + switch (fieldType.ToLowerInvariant()) + { + case "bool": + return "bool"; + case "bool?": + return "bool?"; + + case "string": + case "preserved": + return "string"; + + case "number": + return "int"; + case "number?": + return "int?"; + + case "path": + return "IntermediateFieldPathValue"; + } + + throw new ArgumentException(fieldType); + } + + private static int CompareSymbolDefinitions(object x, object y) + { + var first = (JsonObject)x; + var second = (JsonObject)y; + + var firstType = first.Keys.Single(); + var secondType = second.Keys.Single(); + + return firstType.CompareTo(secondType); + } + } +} 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 @@ +//----------------------------------------------------------------------- +// +// Copyright (c) 2011, The Outercurve Foundation. +// +// Licensed under the MIT License (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.opensource.org/licenses/mit-license.php +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// Nathan Totten (ntotten.com), Jim Zimmerman (jimzimmerman.com) and Prabir Shrestha (prabir.me) +// https://github.com/facebook-csharp-sdk/simple-json +//----------------------------------------------------------------------- + +// VERSION: + +// NOTE: uncomment the following line to make SimpleJson class internal. +#define SIMPLE_JSON_INTERNAL + +// NOTE: uncomment the following line to make JsonArray and JsonObject class internal. +#define SIMPLE_JSON_OBJARRAYINTERNAL + +// NOTE: uncomment the following line to enable dynamic support. +//#define SIMPLE_JSON_DYNAMIC + +// NOTE: uncomment the following line to enable DataContract support. +//#define SIMPLE_JSON_DATACONTRACT + +// NOTE: uncomment the following line to enable IReadOnlyCollection and IReadOnlyList support. +//#define SIMPLE_JSON_READONLY_COLLECTIONS + +// NOTE: uncomment the following line to disable linq expressions/compiled lambda (better performance) instead of method.invoke(). +// define if you are using .net framework <= 3.0 or < WP7.5 +//#define SIMPLE_JSON_NO_LINQ_EXPRESSION + +// NOTE: uncomment the following line if you are compiling under Window Metro style application/library. +// usually already defined in properties +//#define NETFX_CORE; + +// If you are targetting WinStore, WP8 and NET4.5+ PCL make sure to #define SIMPLE_JSON_TYPEINFO; + +// 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 + +#if NETFX_CORE +#define SIMPLE_JSON_TYPEINFO +#endif + +using System; +using System.CodeDom.Compiler; +using System.Collections; +using System.Collections.Generic; +#if !SIMPLE_JSON_NO_LINQ_EXPRESSION +using System.Linq.Expressions; +#endif +using System.ComponentModel; +using System.Diagnostics.CodeAnalysis; +#if SIMPLE_JSON_DYNAMIC +using System.Dynamic; +#endif +using System.Globalization; +using System.Reflection; +using System.Runtime.Serialization; +using System.Text; +using SimpleJson.Reflection; + +// ReSharper disable LoopCanBeConvertedToQuery +// ReSharper disable RedundantExplicitArrayCreation +// ReSharper disable SuggestUseVarKeywordEvident +namespace SimpleJson +{ + /// + /// Represents the json array. + /// + [GeneratedCode("simple-json", "1.0.0")] + [EditorBrowsable(EditorBrowsableState.Never)] + [SuppressMessage("Microsoft.Naming", "CA1710:IdentifiersShouldHaveCorrectSuffix")] +#if SIMPLE_JSON_OBJARRAYINTERNAL + internal +#else + public +#endif + class JsonArray : List + { + /// + /// Initializes a new instance of the class. + /// + public JsonArray() { } + + /// + /// Initializes a new instance of the class. + /// + /// The capacity of the json array. + public JsonArray(int capacity) : base(capacity) { } + + /// + /// The json representation of the array. + /// + /// The json representation of the array. + public override string ToString() + { + return SimpleJson.SerializeObject(this) ?? string.Empty; + } + } + + /// + /// Represents the json object. + /// + [GeneratedCode("simple-json", "1.0.0")] + [EditorBrowsable(EditorBrowsableState.Never)] + [SuppressMessage("Microsoft.Naming", "CA1710:IdentifiersShouldHaveCorrectSuffix")] +#if SIMPLE_JSON_OBJARRAYINTERNAL + internal +#else + public +#endif + class JsonObject : +#if SIMPLE_JSON_DYNAMIC + DynamicObject, +#endif + IDictionary + { + /// + /// The internal member dictionary. + /// + private readonly Dictionary _members; + + /// + /// Initializes a new instance of . + /// + public JsonObject() + { + _members = new Dictionary(); + } + + /// + /// Initializes a new instance of . + /// + /// The implementation to use when comparing keys, or null to use the default for the type of the key. + public JsonObject(IEqualityComparer comparer) + { + _members = new Dictionary(comparer); + } + + /// + /// Gets the at the specified index. + /// + /// + public object this[int index] + { + get { return GetAtIndex(_members, index); } + } + + internal static object GetAtIndex(IDictionary obj, int index) + { + if (obj == null) + throw new ArgumentNullException("obj"); + if (index >= obj.Count) + throw new ArgumentOutOfRangeException("index"); + int i = 0; + foreach (KeyValuePair o in obj) + if (i++ == index) return o.Value; + return null; + } + + /// + /// Adds the specified key. + /// + /// The key. + /// The value. + public void Add(string key, object value) + { + _members.Add(key, value); + } + + /// + /// Determines whether the specified key contains key. + /// + /// The key. + /// + /// true if the specified key contains key; otherwise, false. + /// + public bool ContainsKey(string key) + { + return _members.ContainsKey(key); + } + + /// + /// Gets the keys. + /// + /// The keys. + public ICollection Keys + { + get { return _members.Keys; } + } + + /// + /// Removes the specified key. + /// + /// The key. + /// + public bool Remove(string key) + { + return _members.Remove(key); + } + + /// + /// Tries the get value. + /// + /// The key. + /// The value. + /// + public bool TryGetValue(string key, out object value) + { + return _members.TryGetValue(key, out value); + } + + /// + /// Gets the values. + /// + /// The values. + public ICollection Values + { + get { return _members.Values; } + } + + /// + /// Gets or sets the with the specified key. + /// + /// + public object this[string key] + { + get { return _members[key]; } + set { _members[key] = value; } + } + + /// + /// Adds the specified item. + /// + /// The item. + public void Add(KeyValuePair item) + { + _members.Add(item.Key, item.Value); + } + + /// + /// Clears this instance. + /// + public void Clear() + { + _members.Clear(); + } + + /// + /// Determines whether [contains] [the specified item]. + /// + /// The item. + /// + /// true if [contains] [the specified item]; otherwise, false. + /// + public bool Contains(KeyValuePair item) + { + return _members.ContainsKey(item.Key) && _members[item.Key] == item.Value; + } + + /// + /// Copies to. + /// + /// The array. + /// Index of the array. + public void CopyTo(KeyValuePair[] array, int arrayIndex) + { + if (array == null) throw new ArgumentNullException("array"); + int num = Count; + foreach (KeyValuePair kvp in this) + { + array[arrayIndex++] = kvp; + if (--num <= 0) + return; + } + } + + /// + /// Gets the count. + /// + /// The count. + public int Count + { + get { return _members.Count; } + } + + /// + /// Gets a value indicating whether this instance is read only. + /// + /// + /// true if this instance is read only; otherwise, false. + /// + public bool IsReadOnly + { + get { return false; } + } + + /// + /// Removes the specified item. + /// + /// The item. + /// + public bool Remove(KeyValuePair item) + { + return _members.Remove(item.Key); + } + + /// + /// Gets the enumerator. + /// + /// + public IEnumerator> GetEnumerator() + { + return _members.GetEnumerator(); + } + + /// + /// Returns an enumerator that iterates through a collection. + /// + /// + /// An object that can be used to iterate through the collection. + /// + IEnumerator IEnumerable.GetEnumerator() + { + return _members.GetEnumerator(); + } + + /// + /// Returns a json that represents the current . + /// + /// + /// A json that represents the current . + /// + public override string ToString() + { + return SimpleJson.SerializeObject(this); + } + +#if SIMPLE_JSON_DYNAMIC + /// + /// Provides implementation for type conversion operations. Classes derived from the class can override this method to specify dynamic behavior for operations that convert an object from one type to another. + /// + /// 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 class, binder.Type returns the 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. + /// The result of the type conversion operation. + /// + /// Alwasy returns true. + /// + public override bool TryConvert(ConvertBinder binder, out object result) + { + // + if (binder == null) + throw new ArgumentNullException("binder"); + // + Type targetType = binder.Type; + + if ((targetType == typeof(IEnumerable)) || + (targetType == typeof(IEnumerable>)) || + (targetType == typeof(IDictionary)) || + (targetType == typeof(IDictionary))) + { + result = this; + return true; + } + + return base.TryConvert(binder, out result); + } + + /// + /// Provides the implementation for operations that delete an object member. This method is not intended for use in C# or Visual Basic. + /// + /// Provides information about the deletion. + /// + /// Alwasy returns true. + /// + public override bool TryDeleteMember(DeleteMemberBinder binder) + { + // + if (binder == null) + throw new ArgumentNullException("binder"); + // + return _members.Remove(binder.Name); + } + + /// + /// Provides the implementation for operations that get a value by index. Classes derived from the class can override this method to specify dynamic behavior for indexing operations. + /// + /// Provides information about the operation. + /// 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, is equal to 3. + /// The result of the index operation. + /// + /// Alwasy returns true. + /// + public override bool TryGetIndex(GetIndexBinder binder, object[] indexes, out object result) + { + if (indexes == null) throw new ArgumentNullException("indexes"); + if (indexes.Length == 1) + { + result = ((IDictionary)this)[(string)indexes[0]]; + return true; + } + result = null; + return true; + } + + /// + /// Provides the implementation for operations that get member values. Classes derived from the class can override this method to specify dynamic behavior for operations such as getting a value for a property. + /// + /// 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 class, binder.Name returns "SampleProperty". The binder.IgnoreCase property specifies whether the member name is case-sensitive. + /// The result of the get operation. For example, if the method is called for a property, you can assign the property value to . + /// + /// Alwasy returns true. + /// + public override bool TryGetMember(GetMemberBinder binder, out object result) + { + object value; + if (_members.TryGetValue(binder.Name, out value)) + { + result = value; + return true; + } + result = null; + return true; + } + + /// + /// Provides the implementation for operations that set a value by index. Classes derived from the class can override this method to specify dynamic behavior for operations that access objects by a specified index. + /// + /// Provides information about the operation. + /// 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 class, is equal to 3. + /// 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 class, is equal to 10. + /// + /// 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. + /// + public override bool TrySetIndex(SetIndexBinder binder, object[] indexes, object value) + { + if (indexes == null) throw new ArgumentNullException("indexes"); + if (indexes.Length == 1) + { + ((IDictionary)this)[(string)indexes[0]] = value; + return true; + } + return base.TrySetIndex(binder, indexes, value); + } + + /// + /// Provides the implementation for operations that set member values. Classes derived from the class can override this method to specify dynamic behavior for operations such as setting a value for a property. + /// + /// 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 class, binder.Name returns "SampleProperty". The binder.IgnoreCase property specifies whether the member name is case-sensitive. + /// The value to set to the member. For example, for sampleObject.SampleProperty = "Test", where sampleObject is an instance of the class derived from the class, the is "Test". + /// + /// 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.) + /// + public override bool TrySetMember(SetMemberBinder binder, object value) + { + // + if (binder == null) + throw new ArgumentNullException("binder"); + // + _members[binder.Name] = value; + return true; + } + + /// + /// Returns the enumeration of all dynamic member names. + /// + /// + /// A sequence that contains dynamic member names. + /// + public override IEnumerable GetDynamicMemberNames() + { + foreach (var key in Keys) + yield return key; + } +#endif + } +} + +namespace SimpleJson +{ + /// + /// This class encodes and decodes JSON strings. + /// Spec. details, see http://www.json.org/ + /// + /// JSON uses Arrays and Objects. These correspond here to the datatypes JsonArray(IList<object>) and JsonObject(IDictionary<string,object>). + /// All numbers are parsed to doubles. + /// + [GeneratedCode("simple-json", "1.0.0")] +#if SIMPLE_JSON_INTERNAL + internal +#else + public +#endif + static class SimpleJson + { + private const int TOKEN_NONE = 0; + private const int TOKEN_CURLY_OPEN = 1; + private const int TOKEN_CURLY_CLOSE = 2; + private const int TOKEN_SQUARED_OPEN = 3; + private const int TOKEN_SQUARED_CLOSE = 4; + private const int TOKEN_COLON = 5; + private const int TOKEN_COMMA = 6; + private const int TOKEN_STRING = 7; + private const int TOKEN_NUMBER = 8; + private const int TOKEN_TRUE = 9; + private const int TOKEN_FALSE = 10; + private const int TOKEN_NULL = 11; + private const int BUILDER_CAPACITY = 2000; + + private static readonly char[] EscapeTable; + private static readonly char[] EscapeCharacters = new char[] { '"', '\\', '\b', '\f', '\n', '\r', '\t' }; + private static readonly string EscapeCharactersString = new string(EscapeCharacters); + + static SimpleJson() + { + EscapeTable = new char[93]; + EscapeTable['"'] = '"'; + EscapeTable['\\'] = '\\'; + EscapeTable['\b'] = 'b'; + EscapeTable['\f'] = 'f'; + EscapeTable['\n'] = 'n'; + EscapeTable['\r'] = 'r'; + EscapeTable['\t'] = 't'; + } + + /// + /// Parses the string json into a value + /// + /// A JSON string. + /// An IList<object>, a IDictionary<string,object>, a double, a string, null, true, or false + public static object DeserializeObject(string json) + { + object obj; + if (TryDeserializeObject(json, out obj)) + return obj; + throw new SerializationException("Invalid JSON string"); + } + + /// + /// Try parsing the json string into a value. + /// + /// + /// A JSON string. + /// + /// + /// The object. + /// + /// + /// Returns true if successfull otherwise false. + /// + [SuppressMessage("Microsoft.Design", "CA1007:UseGenericsWhereAppropriate", Justification = "Need to support .NET 2")] + public static bool TryDeserializeObject(string json, out object obj) + { + bool success = true; + if (json != null) + { + char[] charArray = json.ToCharArray(); + int index = 0; + obj = ParseValue(charArray, ref index, ref success); + } + else + obj = null; + + return success; + } + + public static object DeserializeObject(string json, Type type, IJsonSerializerStrategy jsonSerializerStrategy) + { + object jsonObject = DeserializeObject(json); + return type == null || jsonObject != null && ReflectionUtils.IsAssignableFrom(jsonObject.GetType(), type) + ? jsonObject + : (jsonSerializerStrategy ?? CurrentJsonSerializerStrategy).DeserializeObject(jsonObject, type); + } + + public static object DeserializeObject(string json, Type type) + { + return DeserializeObject(json, type, null); + } + + public static T DeserializeObject(string json, IJsonSerializerStrategy jsonSerializerStrategy) + { + return (T)DeserializeObject(json, typeof(T), jsonSerializerStrategy); + } + + public static T DeserializeObject(string json) + { + return (T)DeserializeObject(json, typeof(T), null); + } + + /// + /// Converts a IDictionary<string,object> / IList<object> object into a JSON string + /// + /// A IDictionary<string,object> / IList<object> + /// Serializer strategy to use + /// A JSON encoded string, or null if object 'json' is not serializable + public static string SerializeObject(object json, IJsonSerializerStrategy jsonSerializerStrategy) + { + StringBuilder builder = new StringBuilder(BUILDER_CAPACITY); + bool success = SerializeValue(jsonSerializerStrategy, json, builder); + return (success ? builder.ToString() : null); + } + + public static string SerializeObject(object json) + { + return SerializeObject(json, CurrentJsonSerializerStrategy); + } + + public static string EscapeToJavascriptString(string jsonString) + { + if (string.IsNullOrEmpty(jsonString)) + return jsonString; + + StringBuilder sb = new StringBuilder(); + char c; + + for (int i = 0; i < jsonString.Length;) + { + c = jsonString[i++]; + + if (c == '\\') + { + int remainingLength = jsonString.Length - i; + if (remainingLength >= 2) + { + char lookahead = jsonString[i]; + if (lookahead == '\\') + { + sb.Append('\\'); + ++i; + } + else if (lookahead == '"') + { + sb.Append("\""); + ++i; + } + else if (lookahead == 't') + { + sb.Append('\t'); + ++i; + } + else if (lookahead == 'b') + { + sb.Append('\b'); + ++i; + } + else if (lookahead == 'n') + { + sb.Append('\n'); + ++i; + } + else if (lookahead == 'r') + { + sb.Append('\r'); + ++i; + } + } + } + else + { + sb.Append(c); + } + } + return sb.ToString(); + } + + static IDictionary ParseObject(char[] json, ref int index, ref bool success) + { + IDictionary table = new JsonObject(); + int token; + + // { + NextToken(json, ref index); + + bool done = false; + while (!done) + { + token = LookAhead(json, index); + if (token == TOKEN_NONE) + { + success = false; + return null; + } + else if (token == TOKEN_COMMA) + NextToken(json, ref index); + else if (token == TOKEN_CURLY_CLOSE) + { + NextToken(json, ref index); + return table; + } + else + { + // name + string name = ParseString(json, ref index, ref success); + if (!success) + { + success = false; + return null; + } + // : + token = NextToken(json, ref index); + if (token != TOKEN_COLON) + { + success = false; + return null; + } + // value + object value = ParseValue(json, ref index, ref success); + if (!success) + { + success = false; + return null; + } + table[name] = value; + } + } + return table; + } + + static JsonArray ParseArray(char[] json, ref int index, ref bool success) + { + JsonArray array = new JsonArray(); + + // [ + NextToken(json, ref index); + + bool done = false; + while (!done) + { + int token = LookAhead(json, index); + if (token == TOKEN_NONE) + { + success = false; + return null; + } + else if (token == TOKEN_COMMA) + NextToken(json, ref index); + else if (token == TOKEN_SQUARED_CLOSE) + { + NextToken(json, ref index); + break; + } + else + { + object value = ParseValue(json, ref index, ref success); + if (!success) + return null; + array.Add(value); + } + } + return array; + } + + static object ParseValue(char[] json, ref int index, ref bool success) + { + switch (LookAhead(json, index)) + { + case TOKEN_STRING: + return ParseString(json, ref index, ref success); + case TOKEN_NUMBER: + return ParseNumber(json, ref index, ref success); + case TOKEN_CURLY_OPEN: + return ParseObject(json, ref index, ref success); + case TOKEN_SQUARED_OPEN: + return ParseArray(json, ref index, ref success); + case TOKEN_TRUE: + NextToken(json, ref index); + return true; + case TOKEN_FALSE: + NextToken(json, ref index); + return false; + case TOKEN_NULL: + NextToken(json, ref index); + return null; + case TOKEN_NONE: + break; + } + success = false; + return null; + } + + static string ParseString(char[] json, ref int index, ref bool success) + { + StringBuilder s = new StringBuilder(BUILDER_CAPACITY); + char c; + + EatWhitespace(json, ref index); + + // " + c = json[index++]; + bool complete = false; + while (!complete) + { + if (index == json.Length) + break; + + c = json[index++]; + if (c == '"') + { + complete = true; + break; + } + else if (c == '\\') + { + if (index == json.Length) + break; + c = json[index++]; + if (c == '"') + s.Append('"'); + else if (c == '\\') + s.Append('\\'); + else if (c == '/') + s.Append('/'); + else if (c == 'b') + s.Append('\b'); + else if (c == 'f') + s.Append('\f'); + else if (c == 'n') + s.Append('\n'); + else if (c == 'r') + s.Append('\r'); + else if (c == 't') + s.Append('\t'); + else if (c == 'u') + { + int remainingLength = json.Length - index; + if (remainingLength >= 4) + { + // parse the 32 bit hex into an integer codepoint + uint codePoint; + if (!(success = UInt32.TryParse(new string(json, index, 4), NumberStyles.HexNumber, CultureInfo.InvariantCulture, out codePoint))) + return ""; + + // convert the integer codepoint to a unicode char and add to string + if (0xD800 <= codePoint && codePoint <= 0xDBFF) // if high surrogate + { + index += 4; // skip 4 chars + remainingLength = json.Length - index; + if (remainingLength >= 6) + { + uint lowCodePoint; + if (new string(json, index, 2) == "\\u" && UInt32.TryParse(new string(json, index + 2, 4), NumberStyles.HexNumber, CultureInfo.InvariantCulture, out lowCodePoint)) + { + if (0xDC00 <= lowCodePoint && lowCodePoint <= 0xDFFF) // if low surrogate + { + s.Append((char)codePoint); + s.Append((char)lowCodePoint); + index += 6; // skip 6 chars + continue; + } + } + } + success = false; // invalid surrogate pair + return ""; + } + s.Append(ConvertFromUtf32((int)codePoint)); + // skip 4 chars + index += 4; + } + else + break; + } + } + else + s.Append(c); + } + if (!complete) + { + success = false; + return null; + } + return s.ToString(); + } + + private static string ConvertFromUtf32(int utf32) + { + // http://www.java2s.com/Open-Source/CSharp/2.6.4-mono-.net-core/System/System/Char.cs.htm + if (utf32 < 0 || utf32 > 0x10FFFF) + throw new ArgumentOutOfRangeException("utf32", "The argument must be from 0 to 0x10FFFF."); + if (0xD800 <= utf32 && utf32 <= 0xDFFF) + throw new ArgumentOutOfRangeException("utf32", "The argument must not be in surrogate pair range."); + if (utf32 < 0x10000) + return new string((char)utf32, 1); + utf32 -= 0x10000; + return new string(new char[] { (char)((utf32 >> 10) + 0xD800), (char)(utf32 % 0x0400 + 0xDC00) }); + } + + static object ParseNumber(char[] json, ref int index, ref bool success) + { + EatWhitespace(json, ref index); + int lastIndex = GetLastIndexOfNumber(json, index); + int charLength = (lastIndex - index) + 1; + object returnNumber; + string str = new string(json, index, charLength); + if (str.IndexOf(".", StringComparison.OrdinalIgnoreCase) != -1 || str.IndexOf("e", StringComparison.OrdinalIgnoreCase) != -1) + { + double number; + success = double.TryParse(new string(json, index, charLength), NumberStyles.Any, CultureInfo.InvariantCulture, out number); + returnNumber = number; + } + else + { + long number; + success = long.TryParse(new string(json, index, charLength), NumberStyles.Any, CultureInfo.InvariantCulture, out number); + returnNumber = number; + } + index = lastIndex + 1; + return returnNumber; + } + + static int GetLastIndexOfNumber(char[] json, int index) + { + int lastIndex; + for (lastIndex = index; lastIndex < json.Length; lastIndex++) + if ("0123456789+-.eE".IndexOf(json[lastIndex]) == -1) break; + return lastIndex - 1; + } + + static void EatWhitespace(char[] json, ref int index) + { + for (; index < json.Length; index++) + if (" \t\n\r\b\f".IndexOf(json[index]) == -1) break; + } + + static int LookAhead(char[] json, int index) + { + int saveIndex = index; + return NextToken(json, ref saveIndex); + } + + [SuppressMessage("Microsoft.Maintainability", "CA1502:AvoidExcessiveComplexity")] + static int NextToken(char[] json, ref int index) + { + EatWhitespace(json, ref index); + if (index == json.Length) + return TOKEN_NONE; + char c = json[index]; + index++; + switch (c) + { + case '{': + return TOKEN_CURLY_OPEN; + case '}': + return TOKEN_CURLY_CLOSE; + case '[': + return TOKEN_SQUARED_OPEN; + case ']': + return TOKEN_SQUARED_CLOSE; + case ',': + return TOKEN_COMMA; + case '"': + return TOKEN_STRING; + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + case '-': + return TOKEN_NUMBER; + case ':': + return TOKEN_COLON; + } + index--; + int remainingLength = json.Length - index; + // false + if (remainingLength >= 5) + { + if (json[index] == 'f' && json[index + 1] == 'a' && json[index + 2] == 'l' && json[index + 3] == 's' && json[index + 4] == 'e') + { + index += 5; + return TOKEN_FALSE; + } + } + // true + if (remainingLength >= 4) + { + if (json[index] == 't' && json[index + 1] == 'r' && json[index + 2] == 'u' && json[index + 3] == 'e') + { + index += 4; + return TOKEN_TRUE; + } + } + // null + if (remainingLength >= 4) + { + if (json[index] == 'n' && json[index + 1] == 'u' && json[index + 2] == 'l' && json[index + 3] == 'l') + { + index += 4; + return TOKEN_NULL; + } + } + return TOKEN_NONE; + } + + static bool SerializeValue(IJsonSerializerStrategy jsonSerializerStrategy, object value, StringBuilder builder) + { + bool success = true; + string stringValue = value as string; + if (stringValue != null) + success = SerializeString(stringValue, builder); + else + { + IDictionary dict = value as IDictionary; + if (dict != null) + { + success = SerializeObject(jsonSerializerStrategy, dict.Keys, dict.Values, builder); + } + else + { + IDictionary stringDictionary = value as IDictionary; + if (stringDictionary != null) + { + success = SerializeObject(jsonSerializerStrategy, stringDictionary.Keys, stringDictionary.Values, builder); + } + else + { + IEnumerable enumerableValue = value as IEnumerable; + if (enumerableValue != null) + success = SerializeArray(jsonSerializerStrategy, enumerableValue, builder); + else if (IsNumeric(value)) + success = SerializeNumber(value, builder); + else if (value is bool) + builder.Append((bool)value ? "true" : "false"); + else if (value == null) + builder.Append("null"); + else + { + object serializedObject; + success = jsonSerializerStrategy.TrySerializeNonPrimitiveObject(value, out serializedObject); + if (success) + SerializeValue(jsonSerializerStrategy, serializedObject, builder); + } + } + } + } + return success; + } + + static bool SerializeObject(IJsonSerializerStrategy jsonSerializerStrategy, IEnumerable keys, IEnumerable values, StringBuilder builder) + { + builder.Append("{"); + IEnumerator ke = keys.GetEnumerator(); + IEnumerator ve = values.GetEnumerator(); + bool first = true; + while (ke.MoveNext() && ve.MoveNext()) + { + object key = ke.Current; + object value = ve.Current; + if (!first) + builder.Append(","); + string stringKey = key as string; + if (stringKey != null) + SerializeString(stringKey, builder); + else + if (!SerializeValue(jsonSerializerStrategy, value, builder)) return false; + builder.Append(":"); + if (!SerializeValue(jsonSerializerStrategy, value, builder)) + return false; + first = false; + } + builder.Append("}"); + return true; + } + + static bool SerializeArray(IJsonSerializerStrategy jsonSerializerStrategy, IEnumerable anArray, StringBuilder builder) + { + builder.Append("["); + bool first = true; + foreach (object value in anArray) + { + if (!first) + builder.Append(","); + if (!SerializeValue(jsonSerializerStrategy, value, builder)) + return false; + first = false; + } + builder.Append("]"); + return true; + } + + static bool SerializeString(string aString, StringBuilder builder) + { + // Happy path if there's nothing to be escaped. IndexOfAny is highly optimized (and unmanaged) + if (aString.IndexOfAny(EscapeCharacters) == -1) + { + builder.Append('"'); + builder.Append(aString); + builder.Append('"'); + + return true; + } + + builder.Append('"'); + int safeCharacterCount = 0; + char[] charArray = aString.ToCharArray(); + + for (int i = 0; i < charArray.Length; i++) + { + char c = charArray[i]; + + // Non ascii characters are fine, buffer them up and send them to the builder + // in larger chunks if possible. The escape table is a 1:1 translation table + // with \0 [default(char)] denoting a safe character. + if (c >= EscapeTable.Length || EscapeTable[c] == default(char)) + { + safeCharacterCount++; + } + else + { + if (safeCharacterCount > 0) + { + builder.Append(charArray, i - safeCharacterCount, safeCharacterCount); + safeCharacterCount = 0; + } + + builder.Append('\\'); + builder.Append(EscapeTable[c]); + } + } + + if (safeCharacterCount > 0) + { + builder.Append(charArray, charArray.Length - safeCharacterCount, safeCharacterCount); + } + + builder.Append('"'); + return true; + } + + static bool SerializeNumber(object number, StringBuilder builder) + { + if (number is long) + builder.Append(((long)number).ToString(CultureInfo.InvariantCulture)); + else if (number is ulong) + builder.Append(((ulong)number).ToString(CultureInfo.InvariantCulture)); + else if (number is int) + builder.Append(((int)number).ToString(CultureInfo.InvariantCulture)); + else if (number is uint) + builder.Append(((uint)number).ToString(CultureInfo.InvariantCulture)); + else if (number is decimal) + builder.Append(((decimal)number).ToString(CultureInfo.InvariantCulture)); + else if (number is float) + builder.Append(((float)number).ToString(CultureInfo.InvariantCulture)); + else + builder.Append(Convert.ToDouble(number, CultureInfo.InvariantCulture).ToString("r", CultureInfo.InvariantCulture)); + return true; + } + + /// + /// Determines if a given object is numeric in any way + /// (can be integer, double, null, etc). + /// + static bool IsNumeric(object value) + { + if (value is sbyte) return true; + if (value is byte) return true; + if (value is short) return true; + if (value is ushort) return true; + if (value is int) return true; + if (value is uint) return true; + if (value is long) return true; + if (value is ulong) return true; + if (value is float) return true; + if (value is double) return true; + if (value is decimal) return true; + return false; + } + + private static IJsonSerializerStrategy _currentJsonSerializerStrategy; + public static IJsonSerializerStrategy CurrentJsonSerializerStrategy + { + get + { + return _currentJsonSerializerStrategy ?? + (_currentJsonSerializerStrategy = +#if SIMPLE_JSON_DATACONTRACT + DataContractJsonSerializerStrategy +#else + PocoJsonSerializerStrategy +#endif +); + } + set + { + _currentJsonSerializerStrategy = value; + } + } + + private static PocoJsonSerializerStrategy _pocoJsonSerializerStrategy; + [EditorBrowsable(EditorBrowsableState.Advanced)] + public static PocoJsonSerializerStrategy PocoJsonSerializerStrategy + { + get + { + return _pocoJsonSerializerStrategy ?? (_pocoJsonSerializerStrategy = new PocoJsonSerializerStrategy()); + } + } + +#if SIMPLE_JSON_DATACONTRACT + + private static DataContractJsonSerializerStrategy _dataContractJsonSerializerStrategy; + [System.ComponentModel.EditorBrowsable(EditorBrowsableState.Advanced)] + public static DataContractJsonSerializerStrategy DataContractJsonSerializerStrategy + { + get + { + return _dataContractJsonSerializerStrategy ?? (_dataContractJsonSerializerStrategy = new DataContractJsonSerializerStrategy()); + } + } + +#endif + } + + [GeneratedCode("simple-json", "1.0.0")] +#if SIMPLE_JSON_INTERNAL + internal +#else + public +#endif + interface IJsonSerializerStrategy + { + [SuppressMessage("Microsoft.Design", "CA1007:UseGenericsWhereAppropriate", Justification = "Need to support .NET 2")] + bool TrySerializeNonPrimitiveObject(object input, out object output); + object DeserializeObject(object value, Type type); + } + + [GeneratedCode("simple-json", "1.0.0")] +#if SIMPLE_JSON_INTERNAL + internal +#else + public +#endif + class PocoJsonSerializerStrategy : IJsonSerializerStrategy + { + internal IDictionary ConstructorCache; + internal IDictionary> GetCache; + internal IDictionary>> SetCache; + + internal static readonly Type[] EmptyTypes = new Type[0]; + internal static readonly Type[] ArrayConstructorParameterTypes = new Type[] { typeof(int) }; + + private static readonly string[] Iso8601Format = new string[] + { + @"yyyy-MM-dd\THH:mm:ss.FFFFFFF\Z", + @"yyyy-MM-dd\THH:mm:ss\Z", + @"yyyy-MM-dd\THH:mm:ssK" + }; + + public PocoJsonSerializerStrategy() + { + ConstructorCache = new ReflectionUtils.ThreadSafeDictionary(ContructorDelegateFactory); + GetCache = new ReflectionUtils.ThreadSafeDictionary>(GetterValueFactory); + SetCache = new ReflectionUtils.ThreadSafeDictionary>>(SetterValueFactory); + } + + protected virtual string MapClrMemberNameToJsonFieldName(string clrPropertyName) + { + return clrPropertyName; + } + + internal virtual ReflectionUtils.ConstructorDelegate ContructorDelegateFactory(Type key) + { + return ReflectionUtils.GetContructor(key, key.IsArray ? ArrayConstructorParameterTypes : EmptyTypes); + } + + internal virtual IDictionary GetterValueFactory(Type type) + { + IDictionary result = new Dictionary(); + foreach (PropertyInfo propertyInfo in ReflectionUtils.GetProperties(type)) + { + if (propertyInfo.CanRead) + { + MethodInfo getMethod = ReflectionUtils.GetGetterMethodInfo(propertyInfo); + if (getMethod.IsStatic || !getMethod.IsPublic) + continue; + result[MapClrMemberNameToJsonFieldName(propertyInfo.Name)] = ReflectionUtils.GetGetMethod(propertyInfo); + } + } + foreach (FieldInfo fieldInfo in ReflectionUtils.GetFields(type)) + { + if (fieldInfo.IsStatic || !fieldInfo.IsPublic) + continue; + result[MapClrMemberNameToJsonFieldName(fieldInfo.Name)] = ReflectionUtils.GetGetMethod(fieldInfo); + } + return result; + } + + internal virtual IDictionary> SetterValueFactory(Type type) + { + IDictionary> result = new Dictionary>(); + foreach (PropertyInfo propertyInfo in ReflectionUtils.GetProperties(type)) + { + if (propertyInfo.CanWrite) + { + MethodInfo setMethod = ReflectionUtils.GetSetterMethodInfo(propertyInfo); + if (setMethod.IsStatic || !setMethod.IsPublic) + continue; + result[MapClrMemberNameToJsonFieldName(propertyInfo.Name)] = new KeyValuePair(propertyInfo.PropertyType, ReflectionUtils.GetSetMethod(propertyInfo)); + } + } + foreach (FieldInfo fieldInfo in ReflectionUtils.GetFields(type)) + { + if (fieldInfo.IsInitOnly || fieldInfo.IsStatic || !fieldInfo.IsPublic) + continue; + result[MapClrMemberNameToJsonFieldName(fieldInfo.Name)] = new KeyValuePair(fieldInfo.FieldType, ReflectionUtils.GetSetMethod(fieldInfo)); + } + return result; + } + + public virtual bool TrySerializeNonPrimitiveObject(object input, out object output) + { + return TrySerializeKnownTypes(input, out output) || TrySerializeUnknownTypes(input, out output); + } + + [SuppressMessage("Microsoft.Maintainability", "CA1502:AvoidExcessiveComplexity")] + public virtual object DeserializeObject(object value, Type type) + { + if (type == null) throw new ArgumentNullException("type"); + string str = value as string; + + if (type == typeof(Guid) && string.IsNullOrEmpty(str)) + return default(Guid); + + if (value == null) + return null; + + object obj = null; + + if (str != null) + { + if (str.Length != 0) // We know it can't be null now. + { + if (type == typeof(DateTime) || (ReflectionUtils.IsNullableType(type) && Nullable.GetUnderlyingType(type) == typeof(DateTime))) + return DateTime.ParseExact(str, Iso8601Format, CultureInfo.InvariantCulture, DateTimeStyles.AssumeUniversal | DateTimeStyles.AdjustToUniversal); + if (type == typeof(DateTimeOffset) || (ReflectionUtils.IsNullableType(type) && Nullable.GetUnderlyingType(type) == typeof(DateTimeOffset))) + return DateTimeOffset.ParseExact(str, Iso8601Format, CultureInfo.InvariantCulture, DateTimeStyles.AssumeUniversal | DateTimeStyles.AdjustToUniversal); + if (type == typeof(Guid) || (ReflectionUtils.IsNullableType(type) && Nullable.GetUnderlyingType(type) == typeof(Guid))) + return new Guid(str); + if (type == typeof(Uri)) + { + bool isValid = Uri.IsWellFormedUriString(str, UriKind.RelativeOrAbsolute); + + Uri result; + if (isValid && Uri.TryCreate(str, UriKind.RelativeOrAbsolute, out result)) + return result; + + return null; + } + + if (type == typeof(string)) + return str; + + return Convert.ChangeType(str, type, CultureInfo.InvariantCulture); + } + else + { + if (type == typeof(Guid)) + obj = default(Guid); + else if (ReflectionUtils.IsNullableType(type) && Nullable.GetUnderlyingType(type) == typeof(Guid)) + obj = null; + else + obj = str; + } + // Empty string case + if (!ReflectionUtils.IsNullableType(type) && Nullable.GetUnderlyingType(type) == typeof(Guid)) + return str; + } + else if (value is bool) + return value; + + bool valueIsLong = value is long; + bool valueIsDouble = value is double; + if ((valueIsLong && type == typeof(long)) || (valueIsDouble && type == typeof(double))) + return value; + if ((valueIsDouble && type != typeof(double)) || (valueIsLong && type != typeof(long))) + { + 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) + ? Convert.ChangeType(value, type, CultureInfo.InvariantCulture) + : value; + } + else + { + IDictionary objects = value as IDictionary; + if (objects != null) + { + IDictionary jsonObject = objects; + + if (ReflectionUtils.IsTypeDictionary(type)) + { + // if dictionary then + Type[] types = ReflectionUtils.GetGenericTypeArguments(type); + Type keyType = types[0]; + Type valueType = types[1]; + + Type genericType = typeof(Dictionary<,>).MakeGenericType(keyType, valueType); + + IDictionary dict = (IDictionary)ConstructorCache[genericType](); + + foreach (KeyValuePair kvp in jsonObject) + dict.Add(kvp.Key, DeserializeObject(kvp.Value, valueType)); + + obj = dict; + } + else + { + if (type == typeof(object)) + obj = value; + else + { + obj = ConstructorCache[type](); + foreach (KeyValuePair> setter in SetCache[type]) + { + object jsonValue; + if (jsonObject.TryGetValue(setter.Key, out jsonValue)) + { + jsonValue = DeserializeObject(jsonValue, setter.Value.Key); + setter.Value.Value(obj, jsonValue); + } + } + } + } + } + else + { + IList valueAsList = value as IList; + if (valueAsList != null) + { + IList jsonObject = valueAsList; + IList list = null; + + if (type.IsArray) + { + list = (IList)ConstructorCache[type](jsonObject.Count); + int i = 0; + foreach (object o in jsonObject) + list[i++] = DeserializeObject(o, type.GetElementType()); + } + else if (ReflectionUtils.IsTypeGenericeCollectionInterface(type) || ReflectionUtils.IsAssignableFrom(typeof(IList), type)) + { + Type innerType = ReflectionUtils.GetGenericListElementType(type); + list = (IList)(ConstructorCache[type] ?? ConstructorCache[typeof(List<>).MakeGenericType(innerType)])(jsonObject.Count); + foreach (object o in jsonObject) + list.Add(DeserializeObject(o, innerType)); + } + obj = list; + } + } + return obj; + } + if (ReflectionUtils.IsNullableType(type)) + return ReflectionUtils.ToNullableType(obj, type); + return obj; + } + + protected virtual object SerializeEnum(Enum p) + { + return Convert.ToDouble(p, CultureInfo.InvariantCulture); + } + + [SuppressMessage("Microsoft.Design", "CA1007:UseGenericsWhereAppropriate", Justification = "Need to support .NET 2")] + protected virtual bool TrySerializeKnownTypes(object input, out object output) + { + bool returnValue = true; + if (input is DateTime) + output = ((DateTime)input).ToUniversalTime().ToString(Iso8601Format[0], CultureInfo.InvariantCulture); + else if (input is DateTimeOffset) + output = ((DateTimeOffset)input).ToUniversalTime().ToString(Iso8601Format[0], CultureInfo.InvariantCulture); + else if (input is Guid) + output = ((Guid)input).ToString("D"); + else if (input is Uri) + output = input.ToString(); + else + { + Enum inputEnum = input as Enum; + if (inputEnum != null) + output = SerializeEnum(inputEnum); + else + { + returnValue = false; + output = null; + } + } + return returnValue; + } + [SuppressMessage("Microsoft.Design", "CA1007:UseGenericsWhereAppropriate", Justification = "Need to support .NET 2")] + protected virtual bool TrySerializeUnknownTypes(object input, out object output) + { + if (input == null) throw new ArgumentNullException("input"); + output = null; + Type type = input.GetType(); + if (type.FullName == null) + return false; + IDictionary obj = new JsonObject(); + IDictionary getters = GetCache[type]; + foreach (KeyValuePair getter in getters) + { + if (getter.Value != null) + obj.Add(MapClrMemberNameToJsonFieldName(getter.Key), getter.Value(input)); + } + output = obj; + return true; + } + } + +#if SIMPLE_JSON_DATACONTRACT + [GeneratedCode("simple-json", "1.0.0")] +#if SIMPLE_JSON_INTERNAL + internal +#else + public +#endif + class DataContractJsonSerializerStrategy : PocoJsonSerializerStrategy + { + public DataContractJsonSerializerStrategy() + { + GetCache = new ReflectionUtils.ThreadSafeDictionary>(GetterValueFactory); + SetCache = new ReflectionUtils.ThreadSafeDictionary>>(SetterValueFactory); + } + + internal override IDictionary GetterValueFactory(Type type) + { + bool hasDataContract = ReflectionUtils.GetAttribute(type, typeof(DataContractAttribute)) != null; + if (!hasDataContract) + return base.GetterValueFactory(type); + string jsonKey; + IDictionary result = new Dictionary(); + foreach (PropertyInfo propertyInfo in ReflectionUtils.GetProperties(type)) + { + if (propertyInfo.CanRead) + { + MethodInfo getMethod = ReflectionUtils.GetGetterMethodInfo(propertyInfo); + if (!getMethod.IsStatic && CanAdd(propertyInfo, out jsonKey)) + result[jsonKey] = ReflectionUtils.GetGetMethod(propertyInfo); + } + } + foreach (FieldInfo fieldInfo in ReflectionUtils.GetFields(type)) + { + if (!fieldInfo.IsStatic && CanAdd(fieldInfo, out jsonKey)) + result[jsonKey] = ReflectionUtils.GetGetMethod(fieldInfo); + } + return result; + } + + internal override IDictionary> SetterValueFactory(Type type) + { + bool hasDataContract = ReflectionUtils.GetAttribute(type, typeof(DataContractAttribute)) != null; + if (!hasDataContract) + return base.SetterValueFactory(type); + string jsonKey; + IDictionary> result = new Dictionary>(); + foreach (PropertyInfo propertyInfo in ReflectionUtils.GetProperties(type)) + { + if (propertyInfo.CanWrite) + { + MethodInfo setMethod = ReflectionUtils.GetSetterMethodInfo(propertyInfo); + if (!setMethod.IsStatic && CanAdd(propertyInfo, out jsonKey)) + result[jsonKey] = new KeyValuePair(propertyInfo.PropertyType, ReflectionUtils.GetSetMethod(propertyInfo)); + } + } + foreach (FieldInfo fieldInfo in ReflectionUtils.GetFields(type)) + { + if (!fieldInfo.IsInitOnly && !fieldInfo.IsStatic && CanAdd(fieldInfo, out jsonKey)) + result[jsonKey] = new KeyValuePair(fieldInfo.FieldType, ReflectionUtils.GetSetMethod(fieldInfo)); + } + // todo implement sorting for DATACONTRACT. + return result; + } + + private static bool CanAdd(MemberInfo info, out string jsonKey) + { + jsonKey = null; + if (ReflectionUtils.GetAttribute(info, typeof(IgnoreDataMemberAttribute)) != null) + return false; + DataMemberAttribute dataMemberAttribute = (DataMemberAttribute)ReflectionUtils.GetAttribute(info, typeof(DataMemberAttribute)); + if (dataMemberAttribute == null) + return false; + jsonKey = string.IsNullOrEmpty(dataMemberAttribute.Name) ? info.Name : dataMemberAttribute.Name; + return true; + } + } + +#endif + + namespace Reflection + { + // This class is meant to be copied into other libraries. So we want to exclude it from Code Analysis rules + // that might be in place in the target project. + [GeneratedCode("reflection-utils", "1.0.0")] +#if SIMPLE_JSON_REFLECTION_UTILS_PUBLIC + public +#else + internal +#endif + class ReflectionUtils + { + private static readonly object[] EmptyObjects = new object[] { }; + + public delegate object GetDelegate(object source); + public delegate void SetDelegate(object source, object value); + public delegate object ConstructorDelegate(params object[] args); + + public delegate TValue ThreadSafeDictionaryValueFactory(TKey key); + +#if SIMPLE_JSON_TYPEINFO + public static TypeInfo GetTypeInfo(Type type) + { + return type.GetTypeInfo(); + } +#else + public static Type GetTypeInfo(Type type) + { + return type; + } +#endif + + public static Attribute GetAttribute(MemberInfo info, Type type) + { +#if SIMPLE_JSON_TYPEINFO + if (info == null || type == null || !info.IsDefined(type)) + return null; + return info.GetCustomAttribute(type); +#else + if (info == null || type == null || !Attribute.IsDefined(info, type)) + return null; + return Attribute.GetCustomAttribute(info, type); +#endif + } + + public static Type GetGenericListElementType(Type type) + { + IEnumerable interfaces; +#if SIMPLE_JSON_TYPEINFO + interfaces = type.GetTypeInfo().ImplementedInterfaces; +#else + interfaces = type.GetInterfaces(); +#endif + foreach (Type implementedInterface in interfaces) + { + if (IsTypeGeneric(implementedInterface) && + implementedInterface.GetGenericTypeDefinition() == typeof(IList<>)) + { + return GetGenericTypeArguments(implementedInterface)[0]; + } + } + return GetGenericTypeArguments(type)[0]; + } + + public static Attribute GetAttribute(Type objectType, Type attributeType) + { + +#if SIMPLE_JSON_TYPEINFO + if (objectType == null || attributeType == null || !objectType.GetTypeInfo().IsDefined(attributeType)) + return null; + return objectType.GetTypeInfo().GetCustomAttribute(attributeType); +#else + if (objectType == null || attributeType == null || !Attribute.IsDefined(objectType, attributeType)) + return null; + return Attribute.GetCustomAttribute(objectType, attributeType); +#endif + } + + public static Type[] GetGenericTypeArguments(Type type) + { +#if SIMPLE_JSON_TYPEINFO + return type.GetTypeInfo().GenericTypeArguments; +#else + return type.GetGenericArguments(); +#endif + } + + public static bool IsTypeGeneric(Type type) + { + return GetTypeInfo(type).IsGenericType; + } + + public static bool IsTypeGenericeCollectionInterface(Type type) + { + if (!IsTypeGeneric(type)) + return false; + + Type genericDefinition = type.GetGenericTypeDefinition(); + + return (genericDefinition == typeof(IList<>) + || genericDefinition == typeof(ICollection<>) + || genericDefinition == typeof(IEnumerable<>) +#if SIMPLE_JSON_READONLY_COLLECTIONS + || genericDefinition == typeof(IReadOnlyCollection<>) + || genericDefinition == typeof(IReadOnlyList<>) +#endif + ); + } + + public static bool IsAssignableFrom(Type type1, Type type2) + { + return GetTypeInfo(type1).IsAssignableFrom(GetTypeInfo(type2)); + } + + public static bool IsTypeDictionary(Type type) + { +#if SIMPLE_JSON_TYPEINFO + if (typeof(IDictionary<,>).GetTypeInfo().IsAssignableFrom(type.GetTypeInfo())) + return true; +#else + if (typeof(System.Collections.IDictionary).IsAssignableFrom(type)) + return true; +#endif + if (!GetTypeInfo(type).IsGenericType) + return false; + + Type genericDefinition = type.GetGenericTypeDefinition(); + return genericDefinition == typeof(IDictionary<,>); + } + + public static bool IsNullableType(Type type) + { + return GetTypeInfo(type).IsGenericType && type.GetGenericTypeDefinition() == typeof(Nullable<>); + } + + public static object ToNullableType(object obj, Type nullableType) + { + return obj == null ? null : Convert.ChangeType(obj, Nullable.GetUnderlyingType(nullableType), CultureInfo.InvariantCulture); + } + + public static bool IsValueType(Type type) + { + return GetTypeInfo(type).IsValueType; + } + + public static IEnumerable GetConstructors(Type type) + { +#if SIMPLE_JSON_TYPEINFO + return type.GetTypeInfo().DeclaredConstructors; +#else + return type.GetConstructors(); +#endif + } + + public static ConstructorInfo GetConstructorInfo(Type type, params Type[] argsType) + { + IEnumerable constructorInfos = GetConstructors(type); + int i; + bool matches; + foreach (ConstructorInfo constructorInfo in constructorInfos) + { + ParameterInfo[] parameters = constructorInfo.GetParameters(); + if (argsType.Length != parameters.Length) + continue; + + i = 0; + matches = true; + foreach (ParameterInfo parameterInfo in constructorInfo.GetParameters()) + { + if (parameterInfo.ParameterType != argsType[i]) + { + matches = false; + break; + } + } + + if (matches) + return constructorInfo; + } + + return null; + } + + public static IEnumerable GetProperties(Type type) + { +#if SIMPLE_JSON_TYPEINFO + return type.GetRuntimeProperties(); +#else + return type.GetProperties(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static); +#endif + } + + public static IEnumerable GetFields(Type type) + { +#if SIMPLE_JSON_TYPEINFO + return type.GetRuntimeFields(); +#else + return type.GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static); +#endif + } + + public static MethodInfo GetGetterMethodInfo(PropertyInfo propertyInfo) + { +#if SIMPLE_JSON_TYPEINFO + return propertyInfo.GetMethod; +#else + return propertyInfo.GetGetMethod(true); +#endif + } + + public static MethodInfo GetSetterMethodInfo(PropertyInfo propertyInfo) + { +#if SIMPLE_JSON_TYPEINFO + return propertyInfo.SetMethod; +#else + return propertyInfo.GetSetMethod(true); +#endif + } + + public static ConstructorDelegate GetContructor(ConstructorInfo constructorInfo) + { +#if SIMPLE_JSON_NO_LINQ_EXPRESSION + return GetConstructorByReflection(constructorInfo); +#else + return GetConstructorByExpression(constructorInfo); +#endif + } + + public static ConstructorDelegate GetContructor(Type type, params Type[] argsType) + { +#if SIMPLE_JSON_NO_LINQ_EXPRESSION + return GetConstructorByReflection(type, argsType); +#else + return GetConstructorByExpression(type, argsType); +#endif + } + + public static ConstructorDelegate GetConstructorByReflection(ConstructorInfo constructorInfo) + { + return delegate (object[] args) { return constructorInfo.Invoke(args); }; + } + + public static ConstructorDelegate GetConstructorByReflection(Type type, params Type[] argsType) + { + ConstructorInfo constructorInfo = GetConstructorInfo(type, argsType); + return constructorInfo == null ? null : GetConstructorByReflection(constructorInfo); + } + +#if !SIMPLE_JSON_NO_LINQ_EXPRESSION + + public static ConstructorDelegate GetConstructorByExpression(ConstructorInfo constructorInfo) + { + ParameterInfo[] paramsInfo = constructorInfo.GetParameters(); + ParameterExpression param = Expression.Parameter(typeof(object[]), "args"); + Expression[] argsExp = new Expression[paramsInfo.Length]; + for (int i = 0; i < paramsInfo.Length; i++) + { + Expression index = Expression.Constant(i); + Type paramType = paramsInfo[i].ParameterType; + Expression paramAccessorExp = Expression.ArrayIndex(param, index); + Expression paramCastExp = Expression.Convert(paramAccessorExp, paramType); + argsExp[i] = paramCastExp; + } + NewExpression newExp = Expression.New(constructorInfo, argsExp); + Expression> lambda = Expression.Lambda>(newExp, param); + Func compiledLambda = lambda.Compile(); + return delegate (object[] args) { return compiledLambda(args); }; + } + + public static ConstructorDelegate GetConstructorByExpression(Type type, params Type[] argsType) + { + ConstructorInfo constructorInfo = GetConstructorInfo(type, argsType); + return constructorInfo == null ? null : GetConstructorByExpression(constructorInfo); + } + +#endif + + public static GetDelegate GetGetMethod(PropertyInfo propertyInfo) + { +#if SIMPLE_JSON_NO_LINQ_EXPRESSION + return GetGetMethodByReflection(propertyInfo); +#else + return GetGetMethodByExpression(propertyInfo); +#endif + } + + public static GetDelegate GetGetMethod(FieldInfo fieldInfo) + { +#if SIMPLE_JSON_NO_LINQ_EXPRESSION + return GetGetMethodByReflection(fieldInfo); +#else + return GetGetMethodByExpression(fieldInfo); +#endif + } + + public static GetDelegate GetGetMethodByReflection(PropertyInfo propertyInfo) + { + MethodInfo methodInfo = GetGetterMethodInfo(propertyInfo); + return delegate (object source) { return methodInfo.Invoke(source, EmptyObjects); }; + } + + public static GetDelegate GetGetMethodByReflection(FieldInfo fieldInfo) + { + return delegate (object source) { return fieldInfo.GetValue(source); }; + } + +#if !SIMPLE_JSON_NO_LINQ_EXPRESSION + + public static GetDelegate GetGetMethodByExpression(PropertyInfo propertyInfo) + { + MethodInfo getMethodInfo = GetGetterMethodInfo(propertyInfo); + ParameterExpression instance = Expression.Parameter(typeof(object), "instance"); + UnaryExpression instanceCast = (!IsValueType(propertyInfo.DeclaringType)) ? Expression.TypeAs(instance, propertyInfo.DeclaringType) : Expression.Convert(instance, propertyInfo.DeclaringType); + Func compiled = Expression.Lambda>(Expression.TypeAs(Expression.Call(instanceCast, getMethodInfo), typeof(object)), instance).Compile(); + return delegate (object source) { return compiled(source); }; + } + + public static GetDelegate GetGetMethodByExpression(FieldInfo fieldInfo) + { + ParameterExpression instance = Expression.Parameter(typeof(object), "instance"); + MemberExpression member = Expression.Field(Expression.Convert(instance, fieldInfo.DeclaringType), fieldInfo); + GetDelegate compiled = Expression.Lambda(Expression.Convert(member, typeof(object)), instance).Compile(); + return delegate (object source) { return compiled(source); }; + } + +#endif + + public static SetDelegate GetSetMethod(PropertyInfo propertyInfo) + { +#if SIMPLE_JSON_NO_LINQ_EXPRESSION + return GetSetMethodByReflection(propertyInfo); +#else + return GetSetMethodByExpression(propertyInfo); +#endif + } + + public static SetDelegate GetSetMethod(FieldInfo fieldInfo) + { +#if SIMPLE_JSON_NO_LINQ_EXPRESSION + return GetSetMethodByReflection(fieldInfo); +#else + return GetSetMethodByExpression(fieldInfo); +#endif + } + + public static SetDelegate GetSetMethodByReflection(PropertyInfo propertyInfo) + { + MethodInfo methodInfo = GetSetterMethodInfo(propertyInfo); + return delegate (object source, object value) { methodInfo.Invoke(source, new object[] { value }); }; + } + + public static SetDelegate GetSetMethodByReflection(FieldInfo fieldInfo) + { + return delegate (object source, object value) { fieldInfo.SetValue(source, value); }; + } + +#if !SIMPLE_JSON_NO_LINQ_EXPRESSION + + public static SetDelegate GetSetMethodByExpression(PropertyInfo propertyInfo) + { + MethodInfo setMethodInfo = GetSetterMethodInfo(propertyInfo); + ParameterExpression instance = Expression.Parameter(typeof(object), "instance"); + ParameterExpression value = Expression.Parameter(typeof(object), "value"); + UnaryExpression instanceCast = (!IsValueType(propertyInfo.DeclaringType)) ? Expression.TypeAs(instance, propertyInfo.DeclaringType) : Expression.Convert(instance, propertyInfo.DeclaringType); + UnaryExpression valueCast = (!IsValueType(propertyInfo.PropertyType)) ? Expression.TypeAs(value, propertyInfo.PropertyType) : Expression.Convert(value, propertyInfo.PropertyType); + Action compiled = Expression.Lambda>(Expression.Call(instanceCast, setMethodInfo, valueCast), new ParameterExpression[] { instance, value }).Compile(); + return delegate (object source, object val) { compiled(source, val); }; + } + + public static SetDelegate GetSetMethodByExpression(FieldInfo fieldInfo) + { + ParameterExpression instance = Expression.Parameter(typeof(object), "instance"); + ParameterExpression value = Expression.Parameter(typeof(object), "value"); + Action compiled = Expression.Lambda>( + Assign(Expression.Field(Expression.Convert(instance, fieldInfo.DeclaringType), fieldInfo), Expression.Convert(value, fieldInfo.FieldType)), instance, value).Compile(); + return delegate (object source, object val) { compiled(source, val); }; + } + + public static BinaryExpression Assign(Expression left, Expression right) + { +#if SIMPLE_JSON_TYPEINFO + return Expression.Assign(left, right); +#else + MethodInfo assign = typeof(Assigner<>).MakeGenericType(left.Type).GetMethod("Assign"); + BinaryExpression assignExpr = Expression.Add(left, right, assign); + return assignExpr; +#endif + } + + private static class Assigner + { + public static T Assign(ref T left, T right) + { + return (left = right); + } + } + +#endif + + public sealed class ThreadSafeDictionary : IDictionary + { + private readonly object _lock = new object(); + private readonly ThreadSafeDictionaryValueFactory _valueFactory; + private Dictionary _dictionary; + + public ThreadSafeDictionary(ThreadSafeDictionaryValueFactory valueFactory) + { + _valueFactory = valueFactory; + } + + private TValue Get(TKey key) + { + if (_dictionary == null) + return AddValue(key); + TValue value; + if (!_dictionary.TryGetValue(key, out value)) + return AddValue(key); + return value; + } + + private TValue AddValue(TKey key) + { + TValue value = _valueFactory(key); + lock (_lock) + { + if (_dictionary == null) + { + _dictionary = new Dictionary(); + _dictionary[key] = value; + } + else + { + TValue val; + if (_dictionary.TryGetValue(key, out val)) + return val; + Dictionary dict = new Dictionary(_dictionary); + dict[key] = value; + _dictionary = dict; + } + } + return value; + } + + public void Add(TKey key, TValue value) + { + throw new NotImplementedException(); + } + + public bool ContainsKey(TKey key) + { + return _dictionary.ContainsKey(key); + } + + public ICollection Keys + { + get { return _dictionary.Keys; } + } + + public bool Remove(TKey key) + { + throw new NotImplementedException(); + } + + public bool TryGetValue(TKey key, out TValue value) + { + value = this[key]; + return true; + } + + public ICollection Values + { + get { return _dictionary.Values; } + } + + public TValue this[TKey key] + { + get { return Get(key); } + set { throw new NotImplementedException(); } + } + + public void Add(KeyValuePair item) + { + throw new NotImplementedException(); + } + + public void Clear() + { + throw new NotImplementedException(); + } + + public bool Contains(KeyValuePair item) + { + throw new NotImplementedException(); + } + + public void CopyTo(KeyValuePair[] array, int arrayIndex) + { + throw new NotImplementedException(); + } + + public int Count + { + get { return _dictionary.Count; } + } + + public bool IsReadOnly + { + get { throw new NotImplementedException(); } + } + + public bool Remove(KeyValuePair item) + { + throw new NotImplementedException(); + } + + public IEnumerator> GetEnumerator() + { + return _dictionary.GetEnumerator(); + } + + System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() + { + return _dictionary.GetEnumerator(); + } + } + + } + } +} +// ReSharper restore LoopCanBeConvertedToQuery +// ReSharper restore RedundantExplicitArrayCreation +// 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 @@ + + + + Exe + netcoreapp2.0 + + + 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 @@ +using System; +using System.Xml; + +namespace TablesAndSymbols +{ + class WixColumnDefinition + { + 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) + { + this.Name = name; + this.Type = type; + this.Length = length; + this.PrimaryKey = primaryKey; + this.Nullable = nullable; + this.ModularizeType = modularizeType; + this.ForceLocalizable = forceLocalizable; + this.MinValue = minValue; + this.MaxValue = maxValue; + this.KeyTable = keyTable; + this.KeyColumn = keyColumn; + this.Category = category; + this.Possibilities = possibilities; + this.Description = description; + this.UseCData = useCData; + this.Unreal = unreal; + } + + public string Name { get; } + public ColumnType Type { get; } + public int Length { get; } + public bool PrimaryKey { get; } + public bool Nullable { get; } + public ColumnModularizeType? ModularizeType { get; } + public bool ForceLocalizable { get; } + public long? MinValue { get; } + public long? MaxValue { get; } + public string KeyTable { get; } + public int? KeyColumn { get; } + public ColumnCategory Category { get; } + public string Possibilities { get; } + public string Description { get; } + public bool UseCData { get; } + public bool Unreal { get; } + + internal static WixColumnDefinition Read(XmlReader reader) + { + if (!reader.LocalName.Equals("columnDefinition")) + { + throw new XmlException(); + } + + ColumnCategory category = ColumnCategory.Unknown; + string description = null; + bool empty = reader.IsEmptyElement; + int? keyColumn = null; + string keyTable = null; + int length = -1; + bool localizable = false; + long? maxValue = null; + long? minValue = null; + var modularize = ColumnModularizeType.None; + string name = null; + bool nullable = false; + string possibilities = null; + bool primaryKey = false; + var type = ColumnType.Unknown; + bool useCData = false; + bool unreal = false; + + // parse the attributes + while (reader.MoveToNextAttribute()) + { + switch (reader.LocalName) + { + case "category": + switch (reader.Value) + { + case "anyPath": + category = ColumnCategory.AnyPath; + break; + case "binary": + category = ColumnCategory.Binary; + break; + case "cabinet": + category = ColumnCategory.Cabinet; + break; + case "condition": + category = ColumnCategory.Condition; + break; + case "customSource": + category = ColumnCategory.CustomSource; + break; + case "defaultDir": + category = ColumnCategory.DefaultDir; + break; + case "doubleInteger": + category = ColumnCategory.DoubleInteger; + break; + case "filename": + category = ColumnCategory.Filename; + break; + case "formatted": + category = ColumnCategory.Formatted; + break; + case "formattedSddl": + category = ColumnCategory.FormattedSDDLText; + break; + case "guid": + category = ColumnCategory.Guid; + break; + case "identifier": + category = ColumnCategory.Identifier; + break; + case "integer": + category = ColumnCategory.Integer; + break; + case "language": + category = ColumnCategory.Language; + break; + case "lowerCase": + category = ColumnCategory.LowerCase; + break; + case "path": + category = ColumnCategory.Path; + break; + case "paths": + category = ColumnCategory.Paths; + break; + case "property": + category = ColumnCategory.Property; + break; + case "regPath": + category = ColumnCategory.RegPath; + break; + case "shortcut": + category = ColumnCategory.Shortcut; + break; + case "template": + category = ColumnCategory.Template; + break; + case "text": + category = ColumnCategory.Text; + break; + case "timeDate": + category = ColumnCategory.TimeDate; + break; + case "upperCase": + category = ColumnCategory.UpperCase; + break; + case "version": + category = ColumnCategory.Version; + break; + case "wildCardFilename": + category = ColumnCategory.WildCardFilename; + break; + default: + throw new InvalidOperationException(); + } + break; + case "description": + description = reader.Value; + break; + case "keyColumn": + keyColumn = Convert.ToInt32(reader.Value, 10); + break; + case "keyTable": + keyTable = reader.Value; + break; + case "length": + length = Convert.ToInt32(reader.Value, 10); + break; + case "localizable": + localizable = reader.Value.Equals("yes"); + break; + case "maxValue": + maxValue = Convert.ToInt32(reader.Value, 10); + break; + case "minValue": + minValue = Convert.ToInt32(reader.Value, 10); + break; + case "modularize": + switch (reader.Value) + { + case "column": + modularize = ColumnModularizeType.Column; + break; + case "companionFile": + modularize = ColumnModularizeType.CompanionFile; + break; + case "condition": + modularize = ColumnModularizeType.Condition; + break; + case "controlEventArgument": + modularize = ColumnModularizeType.ControlEventArgument; + break; + case "controlText": + modularize = ColumnModularizeType.ControlText; + break; + case "icon": + modularize = ColumnModularizeType.Icon; + break; + case "none": + modularize = ColumnModularizeType.None; + break; + case "property": + modularize = ColumnModularizeType.Property; + break; + case "semicolonDelimited": + modularize = ColumnModularizeType.SemicolonDelimited; + break; + default: + throw new XmlException(); + } + break; + case "name": + switch (reader.Value) + { + case "CREATE": + case "DELETE": + case "DROP": + case "INSERT": + throw new XmlException(); + default: + name = reader.Value; + break; + } + break; + case "nullable": + nullable = reader.Value.Equals("yes"); + break; + case "primaryKey": + primaryKey = reader.Value.Equals("yes"); + break; + case "set": + possibilities = reader.Value; + break; + case "type": + switch (reader.Value) + { + case "localized": + type = ColumnType.Localized; + break; + case "number": + type = ColumnType.Number; + break; + case "object": + type = ColumnType.Object; + break; + case "string": + type = ColumnType.String; + break; + case "preserved": + type = ColumnType.Preserved; + break; + default: + throw new XmlException(); + } + break; + case "useCData": + useCData = reader.Value.Equals("yes"); + break; + case "unreal": + unreal = reader.Value.Equals("yes"); + break; + } + } + + // parse the child elements (there should be none) + if (!empty) + { + bool done = false; + + while (!done && reader.Read()) + { + switch (reader.NodeType) + { + case XmlNodeType.Element: + throw new XmlException(); + case XmlNodeType.EndElement: + done = true; + break; + } + } + + if (!done) + { + throw new XmlException(); + } + } + + WixColumnDefinition columnDefinition = new WixColumnDefinition(name, type, length, primaryKey, nullable, category, minValue, maxValue, keyTable, keyColumn, possibilities, description, modularize, localizable, useCData, unreal); + + return columnDefinition; + } + } +} 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 @@ +using System.Collections.Generic; +using System.Linq; +using System.Xml; + +namespace TablesAndSymbols +{ + class WixTableDefinition + { + public WixTableDefinition(string name, IEnumerable columns, bool unreal, bool symbolless, string symbolDefinitionName, bool? symbolIdIsPrimaryKey) + { + this.Name = name; + this.VariableName = name.Replace("_", ""); + this.Unreal = unreal; + this.Columns = columns?.ToArray(); + this.Symbolless = symbolless; + this.SymbolDefinitionName = symbolless ? null : symbolDefinitionName ?? this.VariableName; + this.SymbolIdIsPrimaryKey = symbolIdIsPrimaryKey ?? DeriveSymbolIdIsPrimaryKey(this.Columns); + } + + public string Name { get; } + + public string VariableName { get; } + + public string SymbolDefinitionName { get; } + + public bool Unreal { get; } + + public WixColumnDefinition[] Columns { get; } + + public bool SymbolIdIsPrimaryKey { get; } + + public bool Symbolless { get; } + + static WixTableDefinition Read(XmlReader reader) + { + var empty = reader.IsEmptyElement; + string name = null; + string symbolDefinitionName = null; + var unreal = false; + bool? symbolIdIsPrimaryKey = null; + var symbolless = false; + + while (reader.MoveToNextAttribute()) + { + switch (reader.LocalName) + { + case "name": + name = reader.Value; + break; + case "symbolDefinitionName": + symbolDefinitionName = reader.Value; + break; + case "symbolIdIsPrimaryKey": + symbolIdIsPrimaryKey = reader.Value.Equals("yes"); + break; + case "symbolless": + symbolless = reader.Value.Equals("yes"); + break; + case "unreal": + unreal = reader.Value.Equals("yes"); + break; + } + } + + if (null == name) + { + throw new XmlException(); + } + + var columns = new List(); + + // parse the child elements + if (!empty) + { + var done = false; + + while (!done && reader.Read()) + { + switch (reader.NodeType) + { + case XmlNodeType.Element: + switch (reader.LocalName) + { + case "columnDefinition": + var columnDefinition = WixColumnDefinition.Read(reader); + columns.Add(columnDefinition); + break; + default: + throw new XmlException(); + } + break; + case XmlNodeType.EndElement: + done = true; + break; + } + } + + if (!done) + { + throw new XmlException(); + } + } + + return new WixTableDefinition(name, columns.ToArray(), unreal, symbolless, symbolDefinitionName, symbolIdIsPrimaryKey); + } + + static bool DeriveSymbolIdIsPrimaryKey(WixColumnDefinition[] columns) + { + return columns[0].PrimaryKey && + columns[0].Type == ColumnType.String && + columns[0].Category == ColumnCategory.Identifier && + !columns[0].Name.EndsWith("_") && + (columns.Length == 1 || !columns.Skip(1).Any(t => t.PrimaryKey)); + } + + public static List LoadCollection(string inputPath) + { + using (var reader = XmlReader.Create(inputPath)) + { + reader.MoveToContent(); + + if ("tableDefinitions" != reader.LocalName) + { + throw new XmlException(); + } + + var empty = reader.IsEmptyElement; + var tableDefinitions = new List(); + + while (reader.MoveToNextAttribute()) + { + } + + // parse the child elements + if (!empty) + { + var done = false; + + while (!done && reader.Read()) + { + switch (reader.NodeType) + { + case XmlNodeType.Element: + switch (reader.LocalName) + { + case "tableDefinition": + tableDefinitions.Add(WixTableDefinition.Read(reader)); + break; + default: + throw new XmlException(); + } + break; + case XmlNodeType.EndElement: + done = true; + break; + } + } + + if (!done) + { + throw new XmlException(); + } + } + + return tableDefinitions; + } + } + } +} 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 @@ +// 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. + +using System.Reflection; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: 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 @@ +// 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. + +namespace WixBuildTools.MsgGen +{ + using System; + using System.CodeDom; + using System.Collections; + using System.Globalization; + using System.Reflection; + using System.Resources; + using System.Xml; + + /// + /// Message files generation class. + /// + public class GenerateMessageFiles + { + /// + /// Generate the message files. + /// + /// Input Xml document containing message definitions. + /// CodeDom container. + /// Writer for default resource file. + public static void Generate(XmlDocument messagesDoc, CodeCompileUnit codeCompileUnit, ResourceWriter resourceWriter) + { + Hashtable usedNumbers = new Hashtable(); + + if (null == messagesDoc) + { + throw new ArgumentNullException("messagesDoc"); + } + + if (null == codeCompileUnit) + { + throw new ArgumentNullException("codeCompileUnit"); + } + + if (null == resourceWriter) + { + throw new ArgumentNullException("resourceWriter"); + } + + string namespaceAttr = messagesDoc.DocumentElement.GetAttribute("Namespace"); + string resourcesAttr = messagesDoc.DocumentElement.GetAttribute("Resources"); + + // namespace + CodeNamespace messagesNamespace = new CodeNamespace(namespaceAttr); + codeCompileUnit.Namespaces.Add(messagesNamespace); + + // imports + messagesNamespace.Imports.Add(new CodeNamespaceImport("System")); + messagesNamespace.Imports.Add(new CodeNamespaceImport("System.Reflection")); + messagesNamespace.Imports.Add(new CodeNamespaceImport("System.Resources")); + if (namespaceAttr != "WixToolset.Data") + { + messagesNamespace.Imports.Add(new CodeNamespaceImport("WixToolset.Data")); + } + + foreach (XmlElement classElement in messagesDoc.DocumentElement.ChildNodes) + { + string className = classElement.GetAttribute("Name"); + string baseContainerName = classElement.GetAttribute("BaseContainerName"); + string containerName = classElement.GetAttribute("ContainerName"); + string messageLevel = classElement.GetAttribute("Level"); + + // message container class + messagesNamespace.Types.Add(CreateContainer(namespaceAttr, baseContainerName, containerName, messageLevel, resourcesAttr)); + + // class + CodeTypeDeclaration messagesClass = new CodeTypeDeclaration(className); + messagesClass.TypeAttributes = TypeAttributes.Class | TypeAttributes.Public | TypeAttributes.Sealed; + messagesNamespace.Types.Add(messagesClass); + + // private constructor (needed since all methods in this class are static) + CodeConstructor constructor = new CodeConstructor(); + constructor.Attributes = MemberAttributes.Private; + constructor.ReturnType = null; + messagesClass.Members.Add(constructor); + + // messages + foreach (XmlElement messageElement in classElement.ChildNodes) + { + int number; + string id = messageElement.GetAttribute("Id"); + string numberString = messageElement.GetAttribute("Number"); + bool sourceLineNumbers = true; + + // determine the message number (and ensure it was set properly) + if (0 < numberString.Length) + { + number = Convert.ToInt32(numberString, CultureInfo.InvariantCulture); + } + else + { + throw new ApplicationException(String.Format("Message number must be assigned for {0} '{1}'.", containerName, id)); + } + + // check for message number collisions + if (usedNumbers.Contains(number)) + { + throw new ApplicationException(String.Format("Collision detected between two or more messages with number '{0}'.", number)); + } + + usedNumbers.Add(number, null); + + if ("no" == messageElement.GetAttribute("SourceLineNumbers")) + { + sourceLineNumbers = false; + } + + int instanceCount = 0; + foreach (XmlElement instanceElement in messageElement.ChildNodes) + { + string formatString = instanceElement.InnerText.Trim(); + string resourceName = String.Concat(className, "_", id, "_", (++instanceCount).ToString()); + + // create a resource + resourceWriter.AddResource(resourceName, formatString); + + // create method + CodeMemberMethod method = new CodeMemberMethod(); + method.ReturnType = new CodeTypeReference(baseContainerName); + method.Attributes = MemberAttributes.Public | MemberAttributes.Static; + messagesClass.Members.Add(method); + + // method name + method.Name = id; + + // return statement + CodeMethodReturnStatement stmt = new CodeMethodReturnStatement(); + method.Statements.Add(stmt); + + // return statement expression + CodeObjectCreateExpression expr = new CodeObjectCreateExpression(); + stmt.Expression = expr; + + // new struct + expr.CreateType = new CodeTypeReference(containerName); + + // optionally have sourceLineNumbers as the first parameter + if (sourceLineNumbers) + { + // sourceLineNumbers parameter + expr.Parameters.Add(new CodeArgumentReferenceExpression("sourceLineNumbers")); + } + else + { + expr.Parameters.Add(new CodePrimitiveExpression(null)); + } + + // message number parameter + expr.Parameters.Add(new CodePrimitiveExpression(number)); + + // resource name parameter + expr.Parameters.Add(new CodePrimitiveExpression(resourceName)); + + // optionally have sourceLineNumbers as the first parameter + if (sourceLineNumbers) + { + method.Parameters.Add(new CodeParameterDeclarationExpression("SourceLineNumber", "sourceLineNumbers")); + } + + foreach (XmlNode parameterNode in instanceElement.ChildNodes) + { + XmlElement parameterElement; + + if (null != (parameterElement = parameterNode as XmlElement)) + { + string type = parameterElement.GetAttribute("Type"); + string name = parameterElement.GetAttribute("Name"); + + // method parameter + method.Parameters.Add(new CodeParameterDeclarationExpression(type, name)); + + // String.Format parameter + expr.Parameters.Add(new CodeArgumentReferenceExpression(name)); + } + } + } + } + } + } + + /// + /// Create message container class. + /// + /// Namespace to use for resources stream. + /// Name of the base message container class. + /// Name of the message container class. + /// Message level of for the message. + /// Name of the resources stream (will get namespace prepended). + /// Message container class CodeDom object. + private static CodeTypeDeclaration CreateContainer(string namespaceName, string baseContainerName, string containerName, string messageLevel, string resourcesName) + { + CodeTypeDeclaration messageContainer = new CodeTypeDeclaration(); + + messageContainer.Name = containerName; + messageContainer.BaseTypes.Add(new CodeTypeReference(baseContainerName)); + + // constructor + CodeConstructor constructor = new CodeConstructor(); + constructor.Attributes = MemberAttributes.Public; + constructor.ReturnType = null; + messageContainer.Members.Add(constructor); + + CodeMemberField resourceManager = new CodeMemberField(); + resourceManager.Attributes = MemberAttributes.Private | MemberAttributes.Static; + resourceManager.Name = "resourceManager"; + resourceManager.Type = new CodeTypeReference("ResourceManager"); + resourceManager.InitExpression = new CodeObjectCreateExpression("ResourceManager", new CodeSnippetExpression(String.Format("\"{0}.{1}\"", namespaceName, resourcesName)), new CodeSnippetExpression("Assembly.GetExecutingAssembly()")); + messageContainer.Members.Add(resourceManager); + + // constructor parameters + constructor.Parameters.Add(new CodeParameterDeclarationExpression("SourceLineNumber", "sourceLineNumbers")); + constructor.Parameters.Add(new CodeParameterDeclarationExpression(typeof(int), "id")); + constructor.Parameters.Add(new CodeParameterDeclarationExpression(typeof(string), "resourceName")); + CodeParameterDeclarationExpression messageArgsParam = new CodeParameterDeclarationExpression("params object[]", "messageArgs"); + constructor.Parameters.Add(messageArgsParam); + + constructor.BaseConstructorArgs.Add(new CodeArgumentReferenceExpression("sourceLineNumbers")); + constructor.BaseConstructorArgs.Add(new CodeArgumentReferenceExpression("id")); + constructor.BaseConstructorArgs.Add(new CodeArgumentReferenceExpression("resourceName")); + constructor.BaseConstructorArgs.Add(new CodeArgumentReferenceExpression("messageArgs")); + + // assign base.Level if messageLevel is specified + if (!String.IsNullOrEmpty(messageLevel)) + { + CodePropertyReferenceExpression levelReference = new CodePropertyReferenceExpression(new CodeBaseReferenceExpression(), "Level"); + CodeFieldReferenceExpression messageLevelField = new CodeFieldReferenceExpression(new CodeTypeReferenceExpression("MessageLevel"), messageLevel); + constructor.Statements.Add(new CodeAssignStatement(levelReference, messageLevelField)); + } + + // Assign base.ResourceManager property + CodePropertyReferenceExpression baseResourceManagerReference = new CodePropertyReferenceExpression(new CodeBaseReferenceExpression(), "ResourceManager"); + CodeFieldReferenceExpression resourceManagerField = new CodeFieldReferenceExpression(null, "resourceManager"); + constructor.Statements.Add(new CodeAssignStatement(baseResourceManagerReference, resourceManagerField)); + + //CodeMemberProperty resourceManagerProperty = new CodeMemberProperty(); + //resourceManagerProperty.Attributes = MemberAttributes.Public | MemberAttributes.Override; + //resourceManagerProperty.Name = "ResourceManager"; + //resourceManagerProperty.Type = new CodeTypeReference("ResourceManager"); + //CodeFieldReferenceExpression resourceManagerReference = new CodeFieldReferenceExpression(); + //resourceManagerReference.FieldName = "resourceManager"; + //resourceManagerProperty.GetStatements.Add(new CodeMethodReturnStatement(resourceManagerReference)); + //messageContainer.Members.Add(resourceManagerProperty); + + return messageContainer; + } + } +} 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 @@ +// 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. + +namespace WixBuildTools.MsgGen +{ + using Microsoft.CSharp; + using System; + using System.CodeDom; + using System.CodeDom.Compiler; + using System.Collections; + using System.IO; + using System.Reflection; + using System.Resources; + using System.Runtime.InteropServices; + using System.Xml; + using System.Xml.Schema; + + /// + /// The main entry point for MsgGen. + /// + public class MsgGen + { + /// + /// The main entry point for MsgGen. + /// + /// Commandline arguments for the application. + /// Returns the application error code. + [STAThread] + public static int Main(string[] args) + { + try + { + MsgGenMain msgGen = new MsgGenMain(args); + } + catch (Exception e) + { + Console.WriteLine("MsgGen.exe : fatal error MSGG0000: {0}\r\n\r\nStack Trace:\r\n{1}", e.Message, e.StackTrace); + if (e is NullReferenceException || e is SEHException) + { + throw; + } + return 2; + } + + return 0; + } + + /// + /// Main class for MsgGen. + /// + private class MsgGenMain + { + private bool showLogo; + private bool showHelp; + + private string sourceFile; + private string destClassFile; + private string destResourcesFile; + + /// + /// Main method for the MsgGen application within the MsgGenMain class. + /// + /// Commandline arguments to the application. + public MsgGenMain(string[] args) + { + this.showLogo = true; + this.showHelp = false; + + this.sourceFile = null; + this.destClassFile = null; + this.destResourcesFile = null; + + // parse the command line + this.ParseCommandLine(args); + + if (null == this.sourceFile || null == this.destClassFile) + { + this.showHelp = true; + } + if (null == this.destResourcesFile) + { + this.destResourcesFile = Path.ChangeExtension(this.destClassFile, ".resources"); + } + + // get the assemblies + Assembly msgGenAssembly = Assembly.GetExecutingAssembly(); + + if (this.showLogo) + { + Console.WriteLine("Microsoft (R) Message Generation Tool version {0}", msgGenAssembly.GetName().Version.ToString()); + Console.WriteLine("Copyright (C) Microsoft Corporation 2004. All rights reserved."); + Console.WriteLine(); + } + if (this.showHelp) + { + Console.WriteLine(" usage: MsgGen.exe [-?] [-nologo] sourceFile destClassFile [destResourcesFile]"); + Console.WriteLine(); + Console.WriteLine(" -? this help information"); + Console.WriteLine(); + Console.WriteLine("For more information see: http://wix.sourceforge.net"); + return; // exit + } + + // load the schema + XmlReader reader = null; + XmlSchemaCollection schemaCollection = null; + try + { + reader = new XmlTextReader(msgGenAssembly.GetManifestResourceStream("WixBuildTools.MsgGen.Xsd.messages.xsd")); + schemaCollection = new XmlSchemaCollection(); + schemaCollection.Add("http://schemas.microsoft.com/genmsgs/2004/07/messages", reader); + } + finally + { + reader.Close(); + } + + // load the source file and process it + using (StreamReader sr = new StreamReader(this.sourceFile)) + { + XmlParserContext context = new XmlParserContext(null, null, null, XmlSpace.None); + XmlValidatingReader validatingReader = new XmlValidatingReader(sr.BaseStream, XmlNodeType.Document, context); + validatingReader.Schemas.Add(schemaCollection); + + XmlDocument errorsDoc = new XmlDocument(); + errorsDoc.Load(validatingReader); + + CodeCompileUnit codeCompileUnit = new CodeCompileUnit(); + + using (ResourceWriter resourceWriter = new ResourceWriter(this.destResourcesFile)) + { + GenerateMessageFiles.Generate(errorsDoc, codeCompileUnit, resourceWriter); + + GenerateCSharpCode(codeCompileUnit, this.destClassFile); + } + } + } + + /// + /// Generate the actual C# code. + /// + /// The code DOM. + /// Destination C# source file. + public static void GenerateCSharpCode(CodeCompileUnit codeCompileUnit, string destClassFile) + { + // generate the code with the C# code provider + CSharpCodeProvider provider = new CSharpCodeProvider(); + + // obtain an ICodeGenerator from the CodeDomProvider class + ICodeGenerator gen = provider.CreateGenerator(); + + // create a TextWriter to a StreamWriter to the output file + using (StreamWriter sw = new StreamWriter(destClassFile)) + { + using (IndentedTextWriter tw = new IndentedTextWriter(sw, " ")) + { + CodeGeneratorOptions options = new CodeGeneratorOptions(); + + // code generation options + options.BlankLinesBetweenMembers = true; + options.BracingStyle = "C"; + + // generate source code using the code generator + gen.GenerateCodeFromCompileUnit(codeCompileUnit, tw, options); + } + } + } + + /// + /// Parse the commandline arguments. + /// + /// Commandline arguments. + private void ParseCommandLine(string[] args) + { + for (int i = 0; i < args.Length; ++i) + { + string arg = args[i]; + if (null == arg || "" == arg) // skip blank arguments + { + continue; + } + + if ('-' == arg[0] || '/' == arg[0]) + { + string parameter = arg.Substring(1); + if ("nologo" == parameter) + { + this.showLogo = false; + } + else if ("?" == parameter || "help" == parameter) + { + this.showHelp = true; + } + } + else if ('@' == arg[0]) + { + using (StreamReader reader = new StreamReader(arg.Substring(1))) + { + string line; + ArrayList newArgs = new ArrayList(); + + while (null != (line = reader.ReadLine())) + { + string newArg = ""; + bool betweenQuotes = false; + for (int j = 0; j < line.Length; ++j) + { + // skip whitespace + if (!betweenQuotes && (' ' == line[j] || '\t' == line[j])) + { + if ("" != newArg) + { + newArgs.Add(newArg); + newArg = null; + } + + continue; + } + + // if we're escaping a quote + if ('\\' == line[j] && '"' == line[j]) + { + ++j; + } + else if ('"' == line[j]) // if we've hit a new quote + { + betweenQuotes = !betweenQuotes; + continue; + } + + newArg = String.Concat(newArg, line[j]); + } + if ("" != newArg) + { + newArgs.Add(newArg); + } + } + string[] ar = (string[])newArgs.ToArray(typeof(string)); + this.ParseCommandLine(ar); + } + } + else if (null == this.sourceFile) + { + this.sourceFile = arg; + } + else if (null == this.destClassFile) + { + this.destClassFile = arg; + } + else if (null == this.destResourcesFile) + { + this.destResourcesFile = arg; + } + else + { + throw new ArgumentException(String.Format("Unknown argument '{0}'.", arg)); + } + } + } + } + } +} 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 @@ + + + + Exe + net461 + true + true + embedded + true + CS0618 + + + + + + + + + + + + + + + + + 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 @@ + + + + + + + + Schema for describing any kind of messages. + + + + + + + + + + Namespace of the generated class. + + + Resources stream for messages. Will get namespace prepended to it. + + + + + + + + + + + Name of the generated class. + + + Name of the generated container class. + + + Name of the base container class. + + + Optional message level for this container class and derivative classes. + + + + + + + + + + + Name of the message type. + + + Override the number for this message type. + + + Associate SourceLineNumbers with this message. The default value is "yes". + + + + + + + + + + + + + + + + Type of the parameter. + + + Name of the parameter. + + + + + + Values of this type will either be "yes" or "no". + + + + + + + + The message level for this message which corresponds to the WixToolset.MessageLevel enumeration. + + + + + + + + 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 @@ + + + + + + $(MSBuildAllProjects);$(MSBuildThisFileFullPath) + + + + + + + + + $(MSBuildThisFileDirectory)..\tools\ + + + + + + PrepareMsgGen + + + MsgGen; + $(PrepareResourcesDependsOn) + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 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 @@ + + + + + + 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 @@ +// 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. + +#include "precomp.h" + +using namespace System::Reflection; +using namespace System::Runtime::CompilerServices; +using namespace System::Runtime::InteropServices; + +// +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +// +[assembly: AssemblyTitleAttribute("WixBuildTools.TestSupport.Native")]; +[assembly: AssemblyDescriptionAttribute("")]; +[assembly: AssemblyCultureAttribute("")]; +[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 @@ +#pragma once +// 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. + + +namespace WixBuildTools { +namespace TestSupport { + + using namespace System; + using namespace System::Collections::Generic; + using namespace System::Linq; + using namespace Xunit; + + public ref class NativeAssert : WixAssert + { + public: + static void NotNull(LPCWSTR wz) + { + if (!wz) + { + Assert::NotNull(nullptr); + } + } + + // For some reason, naming these NotStringEqual methods "NotEqual" breaks Intellisense in files that call any overload of the NotEqual method. + static void NotStringEqual(LPCWSTR expected, LPCWSTR actual) + { + NativeAssert::NotStringEqual(expected, actual, FALSE); + } + + static void NotStringEqual(LPCWSTR expected, LPCWSTR actual, BOOL ignoreCase) + { + IEqualityComparer^ comparer = ignoreCase ? StringComparer::InvariantCultureIgnoreCase : StringComparer::InvariantCulture; + Assert::NotEqual(NativeAssert::LPWSTRToString(expected), NativeAssert::LPWSTRToString(actual), comparer); + } + + // For some reason, naming these StringEqual methods "Equal" breaks Intellisense in files that call any overload of the Equal method. + static void StringEqual(LPCWSTR expected, LPCWSTR actual) + { + NativeAssert::StringEqual(expected, actual, FALSE); + } + + static void StringEqual(LPCWSTR expected, LPCWSTR actual, BOOL ignoreCase) + { + IEqualityComparer^ comparer = ignoreCase ? StringComparer::InvariantCultureIgnoreCase : StringComparer::InvariantCulture; + Assert::Equal(NativeAssert::LPWSTRToString(expected), NativeAssert::LPWSTRToString(actual), comparer); + } + + static void Succeeded(HRESULT hr, LPCSTR zFormat, LPCSTR zArg, ... array^ zArgs) + { + array^ formatArgs = gcnew array(zArgs->Length + 1); + formatArgs[0] = NativeAssert::LPSTRToString(zArg); + for (int i = 0; i < zArgs->Length; ++i) + { + formatArgs[i + 1] = NativeAssert::LPSTRToString(zArgs[i]); + } + WixAssert::Succeeded(hr, gcnew String(zFormat), formatArgs); + } + + static void Succeeded(HRESULT hr, LPCSTR zFormat, ... array^ wzArgs) + { + array^ formatArgs = gcnew array(wzArgs->Length); + for (int i = 0; i < wzArgs->Length; ++i) + { + formatArgs[i] = NativeAssert::LPWSTRToString(wzArgs[i]); + } + WixAssert::Succeeded(hr, gcnew String(zFormat), formatArgs); + } + + static void ValidReturnCode(HRESULT hr, ... array^ validReturnCodes) + { + Assert::Contains(hr, (IEnumerable^)validReturnCodes); + } + + private: + static String^ LPSTRToString(LPCSTR z) + { + return z ? gcnew String(z) : nullptr; + } + static String^ LPWSTRToString(LPCWSTR wz) + { + return wz ? gcnew String(wz) : nullptr; + } + }; +} +} 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 @@ + + + + $id$ + $version$ + $authors$ + $authors$ + MS-RL + https://github.com/wixtoolset/WixBuildTools + false + $description$ + $copyright$ + + + + + + + + + + + + + + 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 @@ + + + + + + + + + + + + Debug + Win32 + + + Release + Win32 + + + + {8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942} + {95BABD97-FBDB-453A-AF8A-FA031A07B599} + WixBuildTools::TestSupport + ManagedCProj + DynamicLibrary + Unicode + true + WixBuildTools C++/CLI Test Support + + + + + + + + + + + + + + + + + + + Create + + 4564;4691 + + + + + + + + + + + + + + + + {6C57EF2C-979A-4106-A9E5-FE342810619A} + + + + + + + + + + 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}. + + + + + + + + + + \ 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 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hh;hpp;hxx;hm;inl;inc;xsd + + + {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms + + + + + Header Files + + + Header Files + + + + + Source Files + + + Source Files + + + \ 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 @@ + + + + + + + $([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildProjectDirectory), version.json)) + + + + + v142 + v4.7.2 + + + + $(RepoRootDir)\packages\xunit.abstractions.2.0.3\lib\netstandard2.0\xunit.abstractions.dll + + + $(RepoRootDir)\packages\xunit.assert.2.4.1\lib\netstandard1.1\xunit.assert.dll + + + $(RepoRootDir)\packages\xunit.extensibility.core.2.4.1\lib\netstandard1.1\xunit.core.dll + + + $(RepoRootDir)\packages\xunit.extensibility.execution.2.4.1\lib\net452\xunit.execution.desktop.dll + + + \ 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 @@ + + + + + + + + + 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}. + + + + + + + + + + \ 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 @@ + + + + + + + + + + + + + + + + + \ 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 @@ +// 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. + +#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 @@ +#pragma once +// 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. + + +#include +#include + +#include "NativeAssert.h" + +#pragma managed +#include 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 @@ +// 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. + +namespace WixBuildTools.TestSupport +{ + using System; + using System.Collections.Generic; + using System.IO; + + public class Builder + { + public Builder(string sourceFolder, Type extensionType = null, string[] bindPaths = null, string outputFile = null) + { + this.SourceFolder = sourceFolder; + this.ExtensionType = extensionType; + this.BindPaths = bindPaths; + this.OutputFile = outputFile ?? "test.msi"; + } + + public string[] BindPaths { get; set; } + + public Type ExtensionType { get; set; } + + public string OutputFile { get; set; } + + public string SourceFolder { get; } + + public string[] BuildAndQuery(Action buildFunc, params string[] tables) + { + var sourceFiles = Directory.GetFiles(this.SourceFolder, "*.wxs"); + var wxlFiles = Directory.GetFiles(this.SourceFolder, "*.wxl"); + + using (var fs = new DisposableFileSystem()) + { + var intermediateFolder = fs.GetFolder(); + var outputPath = Path.Combine(intermediateFolder, "bin", this.OutputFile); + + var args = new List + { + "build", + "-o", outputPath, + "-intermediateFolder", intermediateFolder, + }; + + if (this.ExtensionType != null) + { + args.Add("-ext"); + args.Add(Path.GetFullPath(new Uri(this.ExtensionType.Assembly.CodeBase).LocalPath)); + } + + args.AddRange(sourceFiles); + + foreach (var wxlFile in wxlFiles) + { + args.Add("-loc"); + args.Add(wxlFile); + } + + foreach (var bindPath in this.BindPaths) + { + args.Add("-bindpath"); + args.Add(bindPath); + } + + buildFunc(args.ToArray()); + + return Query.QueryDatabase(outputPath, tables); + } + } + } +} 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 @@ +// 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. + +namespace WixBuildTools.TestSupport +{ + using System; + using System.Collections.Generic; + using System.IO; + + public class DisposableFileSystem : IDisposable + { + protected bool Disposed { get; private set; } + + private List CleanupPaths { get; } = new List(); + + public bool Keep { get; } + + public DisposableFileSystem(bool keep = false) + { + this.Keep = keep; + } + + protected string GetFile(bool create = false) + { + var path = Path.GetTempFileName(); + + if (!create) + { + File.Delete(path); + } + + this.CleanupPaths.Add(path); + + return path; + } + + public string GetFolder(bool create = false) + { + var path = Path.Combine(Path.GetTempPath(), Path.GetRandomFileName()); + + if (create) + { + Directory.CreateDirectory(path); + } + + this.CleanupPaths.Add(path); + + return path; + } + + + #region // IDisposable + + public void Dispose() + { + this.Dispose(true); + GC.SuppressFinalize(this); + } + + protected virtual void Dispose(bool disposing) + { + if (this.Disposed) + { + return; + } + + if (disposing && !this.Keep) + { + foreach (var path in this.CleanupPaths) + { + try + { + if (File.Exists(path)) + { + File.Delete(path); + } + else if (Directory.Exists(path)) + { + Directory.Delete(path, true); + } + } + catch + { + // Best effort delete, so ignore any failures. + } + } + } + + this.Disposed = true; + } + + #endregion + } +} 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 @@ +// 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. + +namespace WixBuildTools.TestSupport +{ + using System; + using System.Collections.Generic; + using System.IO; + + public class DotnetRunner : ExternalExecutable + { + private static readonly object InitLock = new object(); + private static bool Initialized; + private static DotnetRunner Instance; + + public static ExternalExecutableResult Execute(string command, string[] arguments = null) => + InitAndExecute(command, arguments); + + private static ExternalExecutableResult InitAndExecute(string command, string[] arguments) + { + lock (InitLock) + { + if (!Initialized) + { + Initialized = true; + var dotnetPath = Environment.GetEnvironmentVariable("DOTNET_HOST_PATH"); + if (String.IsNullOrEmpty(dotnetPath) || !File.Exists(dotnetPath)) + { + dotnetPath = "dotnet"; + } + + Instance = new DotnetRunner(dotnetPath); + } + } + + return Instance.ExecuteCore(command, arguments); + } + + private DotnetRunner(string exePath) : base(exePath) { } + + private ExternalExecutableResult ExecuteCore(string command, string[] arguments) + { + var total = new List + { + command, + }; + + if (arguments != null) + { + total.AddRange(arguments); + } + + var args = CombineArguments(total); + var mergeErrorIntoOutput = true; + return this.Run(args, mergeErrorIntoOutput); + } + } +} 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 @@ +// 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. + +namespace WixBuildTools.TestSupport +{ + using System.Collections.Concurrent; + using System.Collections.Generic; + using System.Diagnostics; + using System.IO; + using System.Text; + + public abstract class ExternalExecutable + { + private readonly string exePath; + + protected ExternalExecutable(string exePath) + { + this.exePath = exePath; + } + + protected ExternalExecutableResult Run(string args, bool mergeErrorIntoOutput = false, string workingDirectory = null) + { + var startInfo = new ProcessStartInfo(this.exePath, args) + { + CreateNoWindow = true, + RedirectStandardError = true, + RedirectStandardOutput = true, + UseShellExecute = false, + WorkingDirectory = workingDirectory ?? Path.GetDirectoryName(this.exePath), + }; + + using (var process = Process.Start(startInfo)) + { + // This implementation of merging the streams does not guarantee that lines are retrieved in the same order that they were written. + // If the process is simultaneously writing to both streams, this is impossible to do anyway. + var standardOutput = new ConcurrentQueue(); + var standardError = mergeErrorIntoOutput ? standardOutput : new ConcurrentQueue(); + + process.ErrorDataReceived += (s, e) => { if (e.Data != null) { standardError.Enqueue(e.Data); } }; + process.OutputDataReceived += (s, e) => { if (e.Data != null) { standardOutput.Enqueue(e.Data); } }; + + process.BeginErrorReadLine(); + process.BeginOutputReadLine(); + + process.WaitForExit(); + + return new ExternalExecutableResult + { + ExitCode = process.ExitCode, + StandardError = mergeErrorIntoOutput ? null : standardError.ToArray(), + StandardOutput = standardOutput.ToArray(), + StartInfo = startInfo, + }; + } + } + + // This is internal because it assumes backslashes aren't used as escape characters and there aren't any double quotes. + internal static string CombineArguments(IEnumerable arguments) + { + if (arguments == null) + { + return null; + } + + var sb = new StringBuilder(); + + foreach (var arg in arguments) + { + if (sb.Length > 0) + { + sb.Append(' '); + } + + if (arg.IndexOf(' ') > -1) + { + sb.Append("\""); + sb.Append(arg); + sb.Append("\""); + } + else + { + sb.Append(arg); + } + } + + return sb.ToString(); + } + } +} 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 @@ +// 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. + +namespace WixBuildTools.TestSupport +{ + using System.Diagnostics; + + public class ExternalExecutableResult + { + public int ExitCode { get; set; } + + public string[] StandardError { get; set; } + + public string[] StandardOutput { get; set; } + + public ProcessStartInfo StartInfo { get; set; } + } +} 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 @@ +// 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. + +namespace WixBuildTools.TestSupport +{ + using System.Collections; + using System.Text; + using Microsoft.Build.Framework; + + public class FakeBuildEngine : IBuildEngine + { + private readonly StringBuilder output = new StringBuilder(); + + public int ColumnNumberOfTaskNode => 0; + + public bool ContinueOnError => false; + + public int LineNumberOfTaskNode => 0; + + public string ProjectFileOfTaskNode => "fake_wix.targets"; + + public string Output => this.output.ToString(); + + public bool BuildProjectFile(string projectFileName, string[] targetNames, IDictionary globalProperties, IDictionary targetOutputs) => throw new System.NotImplementedException(); + + public void LogCustomEvent(CustomBuildEventArgs e) => this.output.AppendLine(e.Message); + + public void LogErrorEvent(BuildErrorEventArgs e) => this.output.AppendLine(e.Message); + + public void LogMessageEvent(BuildMessageEventArgs e) => this.output.AppendLine(e.Message); + + public void LogWarningEvent(BuildWarningEventArgs e) => this.output.AppendLine(e.Message); + } +} 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 @@ +// 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. + +namespace WixBuildTools.TestSupport +{ + using System; + using System.Collections.Generic; + using System.IO; + + public class MsbuildRunner : ExternalExecutable + { + private static readonly string VswhereFindArguments = "-property installationPath"; + private static readonly string Msbuild15RelativePath = @"MSBuild\15.0\Bin\MSBuild.exe"; + private static readonly string Msbuild15RelativePath64 = @"MSBuild\15.0\Bin\amd64\MSBuild.exe"; + private static readonly string MsbuildCurrentRelativePath = @"MSBuild\Current\Bin\MSBuild.exe"; + private static readonly string MsbuildCurrentRelativePath64 = @"MSBuild\Current\Bin\amd64\MSBuild.exe"; + + private static readonly object InitLock = new object(); + + private static bool Initialized; + private static MsbuildRunner Msbuild15Runner; + private static MsbuildRunner Msbuild15Runner64; + private static MsbuildRunner MsbuildCurrentRunner; + private static MsbuildRunner MsbuildCurrentRunner64; + + public static MsbuildRunnerResult Execute(string projectPath, string[] arguments = null, bool x64 = false) => + InitAndExecute(String.Empty, projectPath, arguments, x64); + + public static MsbuildRunnerResult ExecuteWithMsbuild15(string projectPath, string[] arguments = null, bool x64 = false) => + InitAndExecute("15", projectPath, arguments, x64); + + public static MsbuildRunnerResult ExecuteWithMsbuildCurrent(string projectPath, string[] arguments = null, bool x64 = false) => + InitAndExecute("Current", projectPath, arguments, x64); + + private static MsbuildRunnerResult InitAndExecute(string msbuildVersion, string projectPath, string[] arguments, bool x64) + { + lock (InitLock) + { + if (!Initialized) + { + Initialized = true; + var vswhereResult = VswhereRunner.Execute(VswhereFindArguments, true); + if (vswhereResult.ExitCode != 0) + { + throw new InvalidOperationException($"Failed to execute vswhere.exe, exit code: {vswhereResult.ExitCode}. Output:\r\n{String.Join("\r\n", vswhereResult.StandardOutput)}"); + } + + string msbuild15Path = null; + string msbuild15Path64 = null; + string msbuildCurrentPath = null; + string msbuildCurrentPath64 = null; + + foreach (var installPath in vswhereResult.StandardOutput) + { + if (msbuildCurrentPath == null) + { + var path = Path.Combine(installPath, MsbuildCurrentRelativePath); + if (File.Exists(path)) + { + msbuildCurrentPath = path; + } + } + + if (msbuildCurrentPath64 == null) + { + var path = Path.Combine(installPath, MsbuildCurrentRelativePath64); + if (File.Exists(path)) + { + msbuildCurrentPath64 = path; + } + } + + if (msbuild15Path == null) + { + var path = Path.Combine(installPath, Msbuild15RelativePath); + if (File.Exists(path)) + { + msbuild15Path = path; + } + } + + if (msbuild15Path64 == null) + { + var path = Path.Combine(installPath, Msbuild15RelativePath64); + if (File.Exists(path)) + { + msbuild15Path64 = path; + } + } + } + + if (msbuildCurrentPath != null) + { + MsbuildCurrentRunner = new MsbuildRunner(msbuildCurrentPath); + } + + if (msbuildCurrentPath64 != null) + { + MsbuildCurrentRunner64 = new MsbuildRunner(msbuildCurrentPath64); + } + + if (msbuild15Path != null) + { + Msbuild15Runner = new MsbuildRunner(msbuild15Path); + } + + if (msbuild15Path64 != null) + { + Msbuild15Runner64 = new MsbuildRunner(msbuild15Path64); + } + } + } + + MsbuildRunner runner; + switch (msbuildVersion) + { + case "15": + { + runner = x64 ? Msbuild15Runner64 : Msbuild15Runner; + break; + } + case "Current": + { + runner = x64 ? MsbuildCurrentRunner64 : MsbuildCurrentRunner; + break; + } + default: + { + runner = x64 ? MsbuildCurrentRunner64 ?? Msbuild15Runner64 + : MsbuildCurrentRunner ?? Msbuild15Runner; + break; + } + } + + if (runner == null) + { + throw new InvalidOperationException($"Failed to find an installed{(x64 ? " 64-bit" : String.Empty)} MSBuild{msbuildVersion}"); + } + + return runner.ExecuteCore(projectPath, arguments); + } + + private MsbuildRunner(string exePath) : base(exePath) { } + + private MsbuildRunnerResult ExecuteCore(string projectPath, string[] arguments) + { + var total = new List + { + projectPath, + }; + + if (arguments != null) + { + total.AddRange(arguments); + } + + var args = CombineArguments(total); + var mergeErrorIntoOutput = true; + var workingFolder = Path.GetDirectoryName(projectPath); + var result = this.Run(args, mergeErrorIntoOutput, workingFolder); + + return new MsbuildRunnerResult + { + ExitCode = result.ExitCode, + Output = result.StandardOutput, + }; + } + } +} 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 @@ +// 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. + +namespace WixBuildTools.TestSupport +{ + using System; + using Xunit; + + public class MsbuildRunnerResult + { + public int ExitCode { get; set; } + + public string[] Output { get; set; } + + public void AssertSuccess() + { + Assert.True(0 == this.ExitCode, $"MSBuild failed unexpectedly. Output:\r\n{String.Join("\r\n", this.Output)}"); + } + } +} 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 @@ +// 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. + +namespace WixBuildTools.TestSupport +{ + using System; + using System.IO; + + public class Pushd : IDisposable + { + protected bool Disposed { get; private set; } + + public Pushd(string path) + { + this.PreviousDirectory = Directory.GetCurrentDirectory(); + + Directory.SetCurrentDirectory(path); + } + + public string PreviousDirectory { get; } + + #region // IDisposable + + public void Dispose() + { + this.Dispose(true); + GC.SuppressFinalize(this); + } + + protected virtual void Dispose(bool disposing) + { + if (this.Disposed) + { + return; + } + + if (disposing) + { + Directory.SetCurrentDirectory(this.PreviousDirectory); + } + + this.Disposed = true; + } + + #endregion + } +} 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 @@ +// 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. + +namespace WixBuildTools.TestSupport +{ + using System; + using System.Collections.Generic; + using System.IO; + using System.Linq; + using System.Text; + using WixToolset.Dtf.Compression.Cab; + using WixToolset.Dtf.WindowsInstaller; + + public class Query + { + public static string[] QueryDatabase(string path, string[] tables) + { + var results = new List(); + var resultsByTable = QueryDatabaseByTable(path, tables); + var sortedTables = tables.ToList(); + sortedTables.Sort(); + foreach (var tableName in sortedTables) + { + var rows = resultsByTable[tableName]; + rows?.ForEach(r => results.Add($"{tableName}:{r}")); + } + return results.ToArray(); + } + + /// + /// Returns rows from requested tables formatted to facilitate testing. + /// If the table did not exist in the database, its list will be null. + /// + /// + /// + /// + public static Dictionary> QueryDatabaseByTable(string path, string[] tables) + { + var results = new Dictionary>(); + + if (tables?.Length > 0) + { + var sb = new StringBuilder(); + using (var db = new Database(path)) + { + foreach (var table in tables) + { + if (table == "_SummaryInformation") + { + var entries = new List(); + results.Add(table, entries); + + entries.Add($"Title\t{db.SummaryInfo.Title}"); + entries.Add($"Subject\t{db.SummaryInfo.Subject}"); + entries.Add($"Author\t{db.SummaryInfo.Author}"); + entries.Add($"Keywords\t{db.SummaryInfo.Keywords}"); + entries.Add($"Comments\t{db.SummaryInfo.Comments}"); + entries.Add($"Template\t{db.SummaryInfo.Template}"); + entries.Add($"CodePage\t{db.SummaryInfo.CodePage}"); + entries.Add($"PageCount\t{db.SummaryInfo.PageCount}"); + entries.Add($"WordCount\t{db.SummaryInfo.WordCount}"); + entries.Add($"CharacterCount\t{db.SummaryInfo.CharacterCount}"); + entries.Add($"Security\t{db.SummaryInfo.Security}"); + + continue; + } + + if (!db.IsTablePersistent(table)) + { + results.Add(table, null); + continue; + } + + var rows = new List(); + results.Add(table, rows); + + using (var view = db.OpenView("SELECT * FROM `{0}`", table)) + { + view.Execute(); + + Record record; + while ((record = view.Fetch()) != null) + { + sb.Clear(); + + using (record) + { + for (var i = 0; i < record.FieldCount; ++i) + { + if (i > 0) + { + sb.Append("\t"); + } + + sb.Append(record[i + 1]?.ToString()); + } + } + + rows.Add(sb.ToString()); + } + } + rows.Sort(); + } + } + } + + return results; + } + + public static CabFileInfo[] GetCabinetFiles(string path) + { + var cab = new CabInfo(path); + + var result = cab.GetFiles(); + + return result.Select(c => c).ToArray(); + } + + public static void ExtractStream(string path, string streamName, string outputPath) + { + Directory.CreateDirectory(Path.GetDirectoryName(outputPath)); + + using (var db = new Database(path)) + using (var view = db.OpenView("SELECT `Data` FROM `_Streams` WHERE `Name` = '{0}'", streamName)) + { + view.Execute(); + + using (var record = view.Fetch()) + { + record.GetStream(1, outputPath); + } + } + } + + public static void ExtractSubStorage(string path, string subStorageName, string outputPath) + { + Directory.CreateDirectory(Path.GetDirectoryName(outputPath)); + + using (var db = new Database(path)) + using (var view = db.OpenView("SELECT `Name`, `Data` FROM `_Storages` WHERE `Name` = '{0}'", subStorageName)) + { + view.Execute(); + + using (var record = view.Fetch()) + { + var name = record.GetString(1); + record.GetStream(2, outputPath); + } + } + } + + public static string[] GetSubStorageNames(string path) + { + var result = new List(); + + using (var db = new Database(path)) + using (var view = db.OpenView("SELECT `Name` FROM `_Storages`")) + { + view.Execute(); + + Record record; + while ((record = view.Fetch()) != null) + { + var name = record.GetString(1); + result.Add(name); + } + } + + result.Sort(); + return result.ToArray(); + } + } +} 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 @@ +// 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. + +namespace WixBuildTools.TestSupport +{ + public class RobocopyRunner : ExternalExecutable + { + private static readonly RobocopyRunner Instance = new RobocopyRunner(); + + private RobocopyRunner() : base("robocopy") { } + + public static ExternalExecutableResult Execute(string args) + { + return Instance.Run(args); + } + } +} 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 @@ +// 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. + +namespace WixBuildTools.TestSupport +{ + using System; + using Xunit.Sdk; + + public class SucceededException : XunitException + { + public SucceededException(int hr, string userMessage) + : base(String.Format("WixAssert.Succeeded() Failure\r\n" + + "HRESULT: 0x{0:X8}\r\n" + + "Message: {1}", + hr, userMessage)) + { + } + } +} 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 @@ +// 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. + +namespace WixBuildTools.TestSupport +{ + using System; + using System.IO; + + public class TestData + { + public static string Get(params string[] paths) + { + var localPath = Path.GetDirectoryName(new Uri(System.Reflection.Assembly.GetCallingAssembly().CodeBase).LocalPath); + return Path.Combine(localPath, Path.Combine(paths)); + } + } +} 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 @@ +// 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. + +namespace WixBuildTools.TestSupport +{ + using System; + + /// + /// This class builds on top of DisposableFileSystem + /// to make it easy to write a test that needs a whole folder of test data copied to a temp location + /// that will automatically be cleaned up at the end of the test. + /// + public class TestDataFolderFileSystem : IDisposable + { + private DisposableFileSystem fileSystem; + + public string BaseFolder { get; private set; } + + public void Dispose() + { + this.fileSystem?.Dispose(); + } + + public void Initialize(string sourceDirectoryPath) + { + if (this.fileSystem != null) + { + throw new InvalidOperationException(); + } + this.fileSystem = new DisposableFileSystem(); + + this.BaseFolder = this.fileSystem.GetFolder(); + + RobocopyFolder(sourceDirectoryPath, this.BaseFolder); + } + + private static ExternalExecutableResult RobocopyFolder(string sourceFolderPath, string destinationFolderPath) + { + var args = $"\"{sourceFolderPath}\" \"{destinationFolderPath}\" /E /R:1 /W:1"; + return RobocopyRunner.Execute(args); + } + } +} 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 @@ +// 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. + +namespace WixBuildTools.TestSupport +{ + using System; + using System.IO; + + public class VswhereRunner : ExternalExecutable + { + private static readonly string VswhereRelativePath = @"Microsoft Visual Studio\Installer\vswhere.exe"; + + private static readonly object InitLock = new object(); + private static bool Initialized; + private static VswhereRunner Instance; + + public static ExternalExecutableResult Execute(string args, bool mergeErrorIntoOutput = false) => + InitAndExecute(args, mergeErrorIntoOutput); + + private static ExternalExecutableResult InitAndExecute(string args, bool mergeErrorIntoOutput) + { + lock (InitLock) + { + if (!Initialized) + { + Initialized = true; + var vswherePath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ProgramFilesX86), VswhereRelativePath); + if (!File.Exists(vswherePath)) + { + throw new InvalidOperationException($"Failed to find vswhere at: {vswherePath}"); + } + + Instance = new VswhereRunner(vswherePath); + } + } + + return Instance.Run(args, mergeErrorIntoOutput); + } + + private VswhereRunner(string exePath) : base(exePath) { } + } +} 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 @@ +// 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. + +namespace WixBuildTools.TestSupport +{ + using System; + using System.Linq; + using System.Xml.Linq; + using Xunit; + + public class WixAssert : Assert + { + public static void CompareLineByLine(string[] expectedLines, string[] actualLines) + { + for (var i = 0; i < expectedLines.Length; ++i) + { + Assert.True(actualLines.Length > i, $"{i}: expectedLines longer than actualLines"); + Assert.Equal($"{i}: {expectedLines[i]}", $"{i}: {actualLines[i]}"); + } + + Assert.True(expectedLines.Length == actualLines.Length, "actualLines longer than expectedLines"); + } + + public static void CompareXml(XContainer xExpected, XContainer xActual) + { + 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}"))}"); + 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}"))}"); + + CompareLineByLine(expecteds.OrderBy(s => s).ToArray(), actuals.OrderBy(s => s).ToArray()); + } + + public static void CompareXml(string expectedPath, string actualPath) + { + var expectedDoc = XDocument.Load(expectedPath, LoadOptions.PreserveWhitespace | LoadOptions.SetBaseUri | LoadOptions.SetLineInfo); + var actualDoc = XDocument.Load(actualPath, LoadOptions.PreserveWhitespace | LoadOptions.SetBaseUri | LoadOptions.SetLineInfo); + + CompareXml(expectedDoc, actualDoc); + } + + public static void Succeeded(int hr, string format, params object[] formatArgs) + { + if (0 > hr) + { + throw new SucceededException(hr, String.Format(format, formatArgs)); + } + } + } +} 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 @@ + + + + + + + netstandard2.0;net461;net472 + true + embedded + true + true + CS1591 + + + + + + + + + + + + + + + + + + + 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 @@ +// 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. + +using System; +using System.Reflection; +using System.Runtime.InteropServices; + +[assembly: AssemblyCulture("")] +[assembly: CLSCompliant(true)] +[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 @@ +// 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. + +namespace WixToolset.Serialize +{ + using System; + using System.Collections; + using System.Xml; + + /// + /// Interface for generated schema elements. + /// + public interface ISchemaElement + { + /// + /// Gets and sets the parent of this element. May be null. + /// + /// An ISchemaElement that has this element as a child. + ISchemaElement ParentElement + { + get; + set; + } + + /// + /// Outputs xml representing this element, including the associated attributes + /// and any nested elements. + /// + /// XmlTextWriter to be used when outputting the element. + void OutputXml(XmlWriter writer); + } + + /// + /// Interface for generated schema elements. Implemented by elements that have child + /// elements. + /// + public interface IParentElement + { + /// + /// Gets an enumerable collection of the children of this element. + /// + /// An enumerable collection of the children of this element. + IEnumerable Children + { + get; + } + + /// + /// Gets an enumerable collection of the children of this element, filtered + /// by the passed in type. + /// + /// The type of children to retrieve. + IEnumerable this[Type childType] + { + get; + } + + /// + /// Adds a child to this element. + /// + /// Child to add. + void AddChild(ISchemaElement child); + + /// + /// Removes a child from this element. + /// + /// Child to remove. + void RemoveChild(ISchemaElement child); + } + + /// + /// Interface for generated schema elements. Implemented by classes with attributes. + /// + public interface ISetAttributes + { + /// + /// Sets the attribute with the given name to the given value. The value here is + /// a string, and is converted to the strongly-typed version inside this method. + /// + /// The name of the attribute to set. + /// The value to assign to the attribute. + void SetAttribute(string name, string value); + } + + /// + /// Interface for generated schema elements. Implemented by classes with children. + /// + public interface ICreateChildren + { + /// + /// Creates an instance of the child with the passed in name. + /// + /// String matching the element name of the child when represented in XML. + /// An instance of that child. + ISchemaElement CreateChild(string childName); + } +} 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 @@ +// 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. + +namespace WixToolset.Serialize +{ + using System; + using System.Collections; + using System.Reflection; + using System.Xml; + + /// + /// Class used for reading XML files in to the CodeDom. + /// + public class CodeDomReader + { + private Assembly[] assemblies; + + /// + /// Creates a new CodeDomReader, using the current assembly. + /// + public CodeDomReader() + { + this.assemblies = new Assembly[] { Assembly.GetExecutingAssembly() }; + } + + /// + /// Creates a new CodeDomReader, and takes in a list of assemblies in which to + /// look for elements. + /// + /// Assemblies in which to look for types that correspond + /// to elements. + public CodeDomReader(Assembly[] assemblies) + { + this.assemblies = assemblies; + } + + /// + /// Loads an XML file into a strongly-typed code dom. + /// + /// File to load into the code dom. + /// The strongly-typed object at the root of the tree. + public ISchemaElement Load(string filePath) + { + XmlDocument document = new XmlDocument(); + document.Load(filePath); + ISchemaElement schemaElement = null; + + foreach (XmlNode node in document.ChildNodes) + { + XmlElement element = node as XmlElement; + if (element != null) + { + if (schemaElement != null) + { + throw new InvalidOperationException("Multiple root elements found in file."); + } + + schemaElement = this.CreateObjectFromElement(element); + this.ParseObjectFromElement(schemaElement, element); + } + } + return schemaElement; + } + + /// + /// Parses an ISchemaElement from the XmlElement. + /// + /// ISchemaElement to fill in. + /// XmlElement to parse from. + private void ParseObjectFromElement(ISchemaElement schemaElement, XmlElement element) + { + foreach (XmlAttribute attribute in element.Attributes) + { + this.SetAttributeOnObject(schemaElement, attribute.LocalName, attribute.Value); + } + + foreach (XmlNode node in element.ChildNodes) + { + XmlElement childElement = node as XmlElement; + if (childElement != null) + { + ISchemaElement childSchemaElement = null; + ICreateChildren createChildren = schemaElement as ICreateChildren; + if (createChildren == null) + { + throw new InvalidOperationException("ISchemaElement with name " + element.LocalName + " does not implement ICreateChildren."); + } + else + { + childSchemaElement = createChildren.CreateChild(childElement.LocalName); + } + + if (childSchemaElement == null) + { + childSchemaElement = this.CreateObjectFromElement(childElement); + if (childSchemaElement == null) + { + throw new InvalidOperationException("XmlElement with name " + childElement.LocalName + " does not have a corresponding ISchemaElement."); + } + } + + this.ParseObjectFromElement(childSchemaElement, childElement); + IParentElement parentElement = (IParentElement)schemaElement; + parentElement.AddChild(childSchemaElement); + } + else + { + XmlText childText = node as XmlText; + if (childText != null) + { + this.SetAttributeOnObject(schemaElement, "Content", childText.Value); + } + } + } + } + + /// + /// Sets an attribute on an ISchemaElement. + /// + /// Schema element to set attribute on. + /// Name of the attribute to set. + /// Value to set on the attribute. + private void SetAttributeOnObject(ISchemaElement schemaElement, string name, string value) + { + ISetAttributes setAttributes = schemaElement as ISetAttributes; + if (setAttributes == null) + { + throw new InvalidOperationException("ISchemaElement with name " + + schemaElement.GetType().FullName.ToString() + + " does not implement ISetAttributes."); + } + else + { + setAttributes.SetAttribute(name, value); + } + } + + /// + /// Creates an object from an XML element by digging through the assembly list. + /// + /// XML Element to create an ISchemaElement from. + /// A constructed ISchemaElement. + private ISchemaElement CreateObjectFromElement(XmlElement element) + { + ISchemaElement schemaElement = null; + foreach (Assembly assembly in this.assemblies) + { + foreach (Type type in assembly.GetTypes()) + { + if (type.FullName.EndsWith(element.LocalName) + && typeof(ISchemaElement).IsAssignableFrom(type)) + { + schemaElement = (ISchemaElement)Activator.CreateInstance(type); + } + } + } + return schemaElement; + } + } +} 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 @@ +// 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. + +namespace WixToolset.Serialize +{ + using System; + using System.Collections; + using System.Globalization; + + /// + /// Collection used in the CodeDOM for the children of a given element. Provides type-checking + /// on the allowed children to ensure that only allowed types are added. + /// + public class ElementCollection : ICollection, IEnumerable + { + private CollectionType collectionType; + private int minimum = 1; + private int maximum = 1; + private int totalContainedItems; + private int containersUsed; + private ArrayList items; + + /// + /// Creates a new element collection. + /// + /// Type of the collection to create. + public ElementCollection(CollectionType collectionType) + { + this.collectionType = collectionType; + this.items = new ArrayList(); + } + + /// + /// Creates a new element collection. + /// + /// Type of the collection to create. + /// When used with a type 'Choice', specifies a minimum number of allowed children. + /// When used with a type 'Choice', specifies a maximum number of allowed children. + public ElementCollection(CollectionType collectionType, int minimum, int maximum) : this(collectionType) + { + this.minimum = minimum; + this.maximum = maximum; + } + + /// + /// Enum representing types of XML collections. + /// + public enum CollectionType + { + /// + /// A choice type, corresponding to the XSD choice element. + /// + Choice, + + /// + /// A sequence type, corresponding to the XSD sequence element. + /// + Sequence + } + + /// + /// Gets the type of collection. + /// + /// The type of collection. + public CollectionType Type + { + get { return this.collectionType; } + } + + /// + /// Gets the count of child elements in this collection (counts ISchemaElements, not nested collections). + /// + /// The count of child elements in this collection (counts ISchemaElements, not nested collections). + public int Count + { + get { return this.totalContainedItems; } + } + + /// + /// Gets the flag specifying whether this collection is synchronized. Always returns false. + /// + /// The flag specifying whether this collection is synchronized. Always returns false. + public bool IsSynchronized + { + get { return false; } + } + + /// + /// Gets an object external callers can synchronize on. + /// + /// An object external callers can synchronize on. + public object SyncRoot + { + get { return this; } + } + + /// + /// Adds a child element to this collection. + /// + /// The element to add. + /// Thrown if the child is not of an allowed type. + public void AddElement(ISchemaElement element) + { + foreach (object obj in this.items) + { + bool containerUsed; + + CollectionItem collectionItem = obj as CollectionItem; + if (collectionItem != null) + { + containerUsed = collectionItem.Elements.Count != 0; + if (collectionItem.ElementType.IsAssignableFrom(element.GetType())) + { + collectionItem.AddElement(element); + + if (!containerUsed) + { + this.containersUsed++; + } + + this.totalContainedItems++; + return; + } + + continue; + } + + ElementCollection collection = obj as ElementCollection; + if (collection != null) + { + containerUsed = collection.Count != 0; + + try + { + collection.AddElement(element); + + if (!containerUsed) + { + this.containersUsed++; + } + + this.totalContainedItems++; + return; + } + catch (ArgumentException) + { + // Eat the exception and keep looking. We'll throw our own if we can't find its home. + } + + continue; + } + } + + throw new ArgumentException(String.Format( + CultureInfo.InvariantCulture, + "Element of type {0} is not valid for this collection.", + element.GetType().Name)); + } + + /// + /// Removes a child element from this collection. + /// + /// The element to remove. + /// Thrown if the element is not of an allowed type. + public void RemoveElement(ISchemaElement element) + { + foreach (object obj in this.items) + { + CollectionItem collectionItem = obj as CollectionItem; + if (collectionItem != null) + { + if (collectionItem.ElementType.IsAssignableFrom(element.GetType())) + { + if (collectionItem.Elements.Count == 0) + { + return; + } + + collectionItem.RemoveElement(element); + + if (collectionItem.Elements.Count == 0) + { + this.containersUsed--; + } + + this.totalContainedItems--; + return; + } + + continue; + } + + ElementCollection collection = obj as ElementCollection; + if (collection != null) + { + if (collection.Count == 0) + { + continue; + } + + try + { + collection.RemoveElement(element); + + if (collection.Count == 0) + { + this.containersUsed--; + } + + this.totalContainedItems--; + return; + } + catch (ArgumentException) + { + // Eat the exception and keep looking. We'll throw our own if we can't find its home. + } + + continue; + } + } + + throw new ArgumentException(String.Format( + CultureInfo.InvariantCulture, + "Element of type {0} is not valid for this collection.", + element.GetType().Name)); + } + + /// + /// Copies this collection to an array. + /// + /// Array to copy to. + /// Offset into the array. + public void CopyTo(Array array, int index) + { + int item = 0; + foreach (ISchemaElement element in this) + { + array.SetValue(element, (long)(item + index)); + item++; + } + } + + /// + /// Creates an enumerator for walking the elements in this collection. + /// + /// A newly created enumerator. + public IEnumerator GetEnumerator() + { + return new ElementCollectionEnumerator(this); + } + + /// + /// Gets an enumerable collection of children of a given type. + /// + /// Type of children to get. + /// A collection of children. + /// Thrown if the type isn't a valid child type. + public IEnumerable Filter(Type childType) + { + foreach (object container in this.items) + { + CollectionItem collectionItem = container as CollectionItem; + if (collectionItem != null) + { + if (collectionItem.ElementType.IsAssignableFrom(childType)) + { + return collectionItem.Elements; + } + + continue; + } + + ElementCollection elementCollection = container as ElementCollection; + if (elementCollection != null) + { + IEnumerable nestedFilter = elementCollection.Filter(childType); + if (nestedFilter != null) + { + return nestedFilter; + } + + continue; + } + } + + throw new ArgumentException(String.Format( + CultureInfo.InvariantCulture, + "Type {0} is not valid for this collection.", + childType.Name)); + } + + /// + /// Adds a type to this collection. + /// + /// CollectionItem representing the type to add. + public void AddItem(CollectionItem collectionItem) + { + this.items.Add(collectionItem); + } + + /// + /// Adds a nested collection to this collection. + /// + /// ElementCollection to add. + public void AddCollection(ElementCollection collection) + { + this.items.Add(collection); + } + + /// + /// Class used to represent a given type in the child collection of an element. Abstract, + /// has subclasses for choice and sequence (which can do cardinality checks). + /// + public abstract class CollectionItem + { + private Type elementType; + private ArrayList elements; + + /// + /// Creates a new CollectionItem for the given element type. + /// + /// Type of the element for this collection item. + public CollectionItem(Type elementType) + { + this.elementType = elementType; + this.elements = new ArrayList(); + } + + /// + /// Gets the type of this collection's items. + /// + public Type ElementType + { + get { return this.elementType; } + } + + /// + /// Gets the elements of this collection. + /// + public ArrayList Elements + { + get { return this.elements; } + } + + /// + /// Adds an element to this collection. Must be of an assignable type to the collection's + /// type. + /// + /// The element to add. + /// Thrown if the type isn't assignable to the collection's type. + public void AddElement(ISchemaElement element) + { + if (!this.elementType.IsAssignableFrom(element.GetType())) + { + throw new ArgumentException( + String.Format( + CultureInfo.InvariantCulture, + "Element must be a subclass of {0}, but was of type {1}.", + this.elementType.Name, + element.GetType().Name), + "element"); + } + + this.elements.Add(element); + } + + /// + /// Removes an element from this collection. + /// + /// The element to remove. + /// Thrown if the element's type isn't assignable to the collection's type. + public void RemoveElement(ISchemaElement element) + { + if (!this.elementType.IsAssignableFrom(element.GetType())) + { + throw new ArgumentException( + String.Format( + CultureInfo.InvariantCulture, + "Element must be a subclass of {0}, but was of type {1}.", + this.elementType.Name, + element.GetType().Name), + "element"); + } + + this.elements.Remove(element); + } + } + + /// + /// Class representing a choice item. Doesn't do cardinality checks. + /// + public class ChoiceItem : CollectionItem + { + /// + /// Creates a new choice item. + /// + /// Type of the created item. + public ChoiceItem(Type elementType) : base(elementType) + { + } + } + + /// + /// Class representing a sequence item. Can do cardinality checks, if required. + /// + public class SequenceItem : CollectionItem + { + private int minimum = 1; + private int maximum = 1; + + /// + /// Creates a new sequence item. + /// + /// Type of the created item. + public SequenceItem(Type elementType) : base(elementType) + { + } + + /// + /// Creates a new sequence item with the specified minimum and maximum. + /// + /// Type of the created item. + /// Minimum number of elements. + /// Maximum number of elements. + public SequenceItem(Type elementType, int minimum, int maximum) : base(elementType) + { + this.minimum = minimum; + this.maximum = maximum; + } + } + + /// + /// Enumerator for the ElementCollection. + /// + private class ElementCollectionEnumerator : IEnumerator + { + private ElementCollection collection; + private Stack collectionStack; + + /// + /// Creates a new ElementCollectionEnumerator. + /// + /// The collection to create an enumerator for. + public ElementCollectionEnumerator(ElementCollection collection) + { + this.collection = collection; + } + + /// + /// Gets the current object from the enumerator. + /// + public object Current + { + get + { + if (this.collectionStack != null && this.collectionStack.Count > 0) + { + CollectionSymbol symbol = (CollectionSymbol)this.collectionStack.Peek(); + object container = symbol.Collection.items[symbol.ContainerIndex]; + + CollectionItem collectionItem = container as CollectionItem; + if (collectionItem != null) + { + return collectionItem.Elements[symbol.ItemIndex]; + } + + throw new InvalidOperationException(String.Format( + CultureInfo.InvariantCulture, + "Element of type {0} found in enumerator. Must be ChoiceItem or SequenceItem.", + container.GetType().Name)); + } + + return null; + } + } + + /// + /// Resets the enumerator to the beginning. + /// + public void Reset() + { + if (this.collectionStack != null) + { + this.collectionStack.Clear(); + this.collectionStack = null; + } + } + + /// + /// Moves the enumerator to the next item. + /// + /// True if there is a next item, false otherwise. + public bool MoveNext() + { + if (this.collectionStack == null) + { + if (this.collection.Count == 0) + { + return false; + } + + this.collectionStack = new Stack(); + this.collectionStack.Push(new CollectionSymbol(this.collection)); + } + + CollectionSymbol symbol = (CollectionSymbol)this.collectionStack.Peek(); + + if (this.FindNext(symbol)) + { + return true; + } + + this.collectionStack.Pop(); + if (this.collectionStack.Count == 0) + { + return false; + } + + return this.MoveNext(); + } + + /// + /// Pushes a collection onto the stack. + /// + /// The collection to push. + private void PushCollection(ElementCollection collection) + { + if (collection.Count <= 0) + { + throw new ArgumentException(String.Format( + CultureInfo.InvariantCulture, + "Collection has {0} elements. Must have at least one.", + collection.Count)); + } + + CollectionSymbol symbol = new CollectionSymbol(collection); + this.collectionStack.Push(symbol); + this.FindNext(symbol); + } + + /// + /// Finds the next item from a given symbol. + /// + /// The symbol to start looking from. + /// True if a next element is found, false otherwise. + private bool FindNext(CollectionSymbol symbol) + { + object container = symbol.Collection.items[symbol.ContainerIndex]; + + CollectionItem collectionItem = container as CollectionItem; + if (collectionItem != null) + { + if (symbol.ItemIndex + 1 < collectionItem.Elements.Count) + { + symbol.ItemIndex++; + return true; + } + } + + ElementCollection elementCollection = container as ElementCollection; + if (elementCollection != null && elementCollection.Count > 0 && symbol.ItemIndex == -1) + { + symbol.ItemIndex++; + this.PushCollection(elementCollection); + return true; + } + + symbol.ItemIndex = 0; + + for (int i = symbol.ContainerIndex + 1; i < symbol.Collection.items.Count; ++i) + { + object nestedContainer = symbol.Collection.items[i]; + + CollectionItem nestedCollectionItem = nestedContainer as CollectionItem; + if (nestedCollectionItem != null) + { + if (nestedCollectionItem.Elements.Count > 0) + { + symbol.ContainerIndex = i; + return true; + } + } + + ElementCollection nestedElementCollection = nestedContainer as ElementCollection; + if (nestedElementCollection != null && nestedElementCollection.Count > 0) + { + symbol.ContainerIndex = i; + this.PushCollection(nestedElementCollection); + return true; + } + } + + return false; + } + + /// + /// Class representing a single point in the collection. Consists of an ElementCollection, + /// a container index, and an index into the container. + /// + private class CollectionSymbol + { + private ElementCollection collection; + private int containerIndex; + private int itemIndex = -1; + + /// + /// Creates a new CollectionSymbol. + /// + /// The collection for the symbol. + public CollectionSymbol(ElementCollection collection) + { + this.collection = collection; + } + + /// + /// Gets the collection for the symbol. + /// + public ElementCollection Collection + { + get { return this.collection; } + } + + /// + /// Gets and sets the index of the container in the collection. + /// + public int ContainerIndex + { + get { return this.containerIndex; } + set { this.containerIndex = value; } + } + + /// + /// Gets and sets the index of the item in the container. + /// + public int ItemIndex + { + get { return this.itemIndex; } + set { this.itemIndex = value; } + } + } + } + } +} 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 @@ +// 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. + +namespace WixToolset.Tools +{ + using System; + using System.CodeDom; + using System.Collections; + using System.Collections.Generic; + using System.Collections.Specialized; + using System.IO; + using System.Reflection; + using System.Text; + using System.Text.RegularExpressions; + using System.Xml; + using System.Xml.Schema; + + /// + /// Type containing static Generate method, which fills in a compile unit from a + /// given schema. + /// + internal class StronglyTypedClasses + { + private static string outputXmlComment = "Processes this element and all child elements into an XmlWriter."; + private static Hashtable simpleTypeNamesToClrTypeNames; + private static Dictionary typeNamesToEnumDeclarations; + private static Dictionary enumsToParseMethodClasses; + private static Regex multiUppercaseNameRegex = new Regex("[A-Z][A-Z][A-Z]", RegexOptions.Compiled); + private static Dictionary refToAttributeGroups; + private static CodeTypeDeclaration enumHelperClass; + + /// + /// Private constructor for static class. + /// + private StronglyTypedClasses() + { + } + + /// + /// Generates strongly typed serialization classes for the given schema document + /// under the given namespace and generates a code compile unit. + /// + /// Schema document to generate classes for. + /// Namespace to be used for the generated code. + /// Namespace in which to find common classes and interfaces, + /// like ISchemaElement. + /// A fully populated CodeCompileUnit, which can be serialized in the language of choice. + public static CodeCompileUnit Generate(XmlSchema xmlSchema, string generateNamespace, string commonNamespace) + { + if (xmlSchema == null) + { + throw new ArgumentNullException("xmlSchema"); + } + if (generateNamespace == null) + { + throw new ArgumentNullException("generateNamespace"); + } + + simpleTypeNamesToClrTypeNames = new Hashtable(); + typeNamesToEnumDeclarations = new Dictionary(); + refToAttributeGroups = new Dictionary(); + enumsToParseMethodClasses = new Dictionary(); + + CodeCompileUnit codeCompileUnit = new CodeCompileUnit(); + CodeNamespace codeNamespace = new CodeNamespace(generateNamespace); + codeCompileUnit.Namespaces.Add(codeNamespace); + codeNamespace.Imports.Add(new CodeNamespaceImport("System")); + codeNamespace.Imports.Add(new CodeNamespaceImport("System.CodeDom.Compiler")); // for GeneratedCodeAttribute + codeNamespace.Imports.Add(new CodeNamespaceImport("System.Collections")); + codeNamespace.Imports.Add(new CodeNamespaceImport("System.Diagnostics.CodeAnalysis")); + codeNamespace.Imports.Add(new CodeNamespaceImport("System.Globalization")); + codeNamespace.Imports.Add(new CodeNamespaceImport("System.Xml")); + if (commonNamespace != null) + { + codeNamespace.Imports.Add(new CodeNamespaceImport(commonNamespace)); + } + + // NOTE: This hash table serves double duty so be sure to have the XSD + // type name mapped to the CLR type name *and* the CLR type name + // mapped to the same CLR type name. Look at long and bool for + // examples below (and before you ask, no I don't know why DateTime + // just works). + simpleTypeNamesToClrTypeNames.Add("dateTime", "DateTime"); + simpleTypeNamesToClrTypeNames.Add("integer", "int"); + simpleTypeNamesToClrTypeNames.Add("int", "int"); + simpleTypeNamesToClrTypeNames.Add("NMTOKEN", "string"); + simpleTypeNamesToClrTypeNames.Add("string", "string"); + simpleTypeNamesToClrTypeNames.Add("nonNegativeInteger", "long"); + simpleTypeNamesToClrTypeNames.Add("long", "long"); + simpleTypeNamesToClrTypeNames.Add("boolean", "bool"); + simpleTypeNamesToClrTypeNames.Add("bool", "bool"); + + xmlSchema.Compile(null); + + foreach (XmlSchemaAttributeGroup schemaAttributeGroup in xmlSchema.AttributeGroups.Values) + { + refToAttributeGroups.Add(schemaAttributeGroup.Name, schemaAttributeGroup); + } + + foreach (XmlSchemaObject schemaObject in xmlSchema.SchemaTypes.Values) + { + XmlSchemaSimpleType schemaSimpleType = schemaObject as XmlSchemaSimpleType; + if (schemaSimpleType != null) + { + ProcessSimpleType(schemaSimpleType, codeNamespace); + } + } + + foreach (XmlSchemaObject schemaObject in xmlSchema.SchemaTypes.Values) + { + XmlSchemaComplexType schemaComplexType = schemaObject as XmlSchemaComplexType; + if (schemaComplexType != null) + { + ProcessComplexType(schemaComplexType, codeNamespace); + } + } + + foreach (XmlSchemaObject schemaObject in xmlSchema.Elements.Values) + { + XmlSchemaElement schemaElement = schemaObject as XmlSchemaElement; + if (schemaElement != null) + { + ProcessElement(schemaElement, codeNamespace); + } + } + + return codeCompileUnit; + } + + /// + /// Processes an XmlSchemaElement into corresponding types. + /// + /// XmlSchemaElement to be processed. + /// CodeNamespace to be used when outputting code. + private static void ProcessElement(XmlSchemaElement schemaElement, CodeNamespace codeNamespace) + { + string elementType = schemaElement.SchemaTypeName.Name; + string elementNamespace = schemaElement.QualifiedName.Namespace; + string elementDocumentation = GetDocumentation(schemaElement.Annotation); + + if ((elementType == null || elementType.Length == 0) && schemaElement.SchemaType != null) + { + ProcessComplexType(schemaElement.Name, elementNamespace, (XmlSchemaComplexType)schemaElement.SchemaType, elementDocumentation, codeNamespace); + } + else + { + if (elementType == null || elementType.Length == 0) + { + elementType = "string"; + } + + CodeTypeDeclaration typeDeclaration = new CodeTypeDeclaration(schemaElement.Name); + typeDeclaration.CustomAttributes.Add(GetGeneratedCodeAttribute()); + typeDeclaration.Attributes = MemberAttributes.Public; + typeDeclaration.IsClass = true; + + if (elementDocumentation != null) + { + GenerateSummaryComment(typeDeclaration.Comments, elementDocumentation); + } + + CodeMemberMethod outputXmlMethod = new CodeMemberMethod(); + outputXmlMethod.Attributes = MemberAttributes.Public; + outputXmlMethod.ImplementationTypes.Add("ISchemaElement"); + outputXmlMethod.Name = "OutputXml"; + outputXmlMethod.Parameters.Add(new CodeParameterDeclarationExpression("XmlWriter", "writer")); + outputXmlMethod.Statements.Add(GetArgumentNullCheckStatement("writer", false)); + outputXmlMethod.Statements.Add(new CodeMethodInvokeExpression(new CodeVariableReferenceExpression("writer"), "WriteStartElement", new CodeSnippetExpression(String.Concat("\"", schemaElement.Name, "\"")), new CodeSnippetExpression(String.Concat("\"", elementNamespace, "\"")))); + GenerateSummaryComment(outputXmlMethod.Comments, outputXmlComment); + + if (simpleTypeNamesToClrTypeNames.ContainsKey(elementType)) + { + CodeMemberField parentField = new CodeMemberField("ISchemaElement", "parentElement"); + typeDeclaration.Members.Add(parentField); + + CodeMemberProperty parentProperty = new CodeMemberProperty(); + parentProperty.Attributes = MemberAttributes.Public; + parentProperty.ImplementationTypes.Add("ISchemaElement"); + parentProperty.Name = "ParentElement"; + parentProperty.Type = new CodeTypeReference("ISchemaElement"); + parentProperty.GetStatements.Add(new CodeMethodReturnStatement(new CodeFieldReferenceExpression(new CodeThisReferenceExpression(), "parentElement"))); + parentProperty.SetStatements.Add(new CodeAssignStatement(new CodeFieldReferenceExpression(new CodeThisReferenceExpression(), "parentElement"), new CodeVariableReferenceExpression("value"))); + typeDeclaration.Members.Add(parentProperty); + + CodeMemberMethod setAttributeMethod = new CodeMemberMethod(); + setAttributeMethod.Attributes = MemberAttributes.Public; + setAttributeMethod.ImplementationTypes.Add("ISetAttributes"); + setAttributeMethod.Name = "SetAttribute"; + setAttributeMethod.Parameters.Add(new CodeParameterDeclarationExpression(typeof(string), "name")); + setAttributeMethod.Parameters.Add(new CodeParameterDeclarationExpression(typeof(string), "value")); + setAttributeMethod.PrivateImplementationType = new CodeTypeReference("ISetAttributes"); + setAttributeMethod.CustomAttributes.Add(GetCodeAnalysisSuppressionAttribute("Microsoft.Design", "CA1033:InterfaceMethodsShouldBeCallableByChildTypes")); + setAttributeMethod.Statements.Add(GetArgumentNullCheckStatement("name", true)); + + GenerateFieldAndProperty("Content", (string)simpleTypeNamesToClrTypeNames[elementType], typeDeclaration, outputXmlMethod, setAttributeMethod, null, elementDocumentation, true, false); + + typeDeclaration.Members.Add(setAttributeMethod); + typeDeclaration.BaseTypes.Add(new CodeTypeReference("ISetAttributes")); + } + else + { + typeDeclaration.BaseTypes.Add(elementType); + outputXmlMethod.Statements.Add(new CodeMethodInvokeExpression(new CodeBaseReferenceExpression(), "OutputXml", new CodeVariableReferenceExpression("writer"))); + outputXmlMethod.Attributes |= MemberAttributes.Override; + } + + outputXmlMethod.Statements.Add(new CodeMethodInvokeExpression(new CodeVariableReferenceExpression("writer"), "WriteEndElement")); + + typeDeclaration.BaseTypes.Add(new CodeTypeReference("ISchemaElement")); + typeDeclaration.Members.Add(outputXmlMethod); + codeNamespace.Types.Add(typeDeclaration); + } + } + + /// + /// Processes an XmlSchemaComplexType into corresponding types. + /// + /// XmlSchemaComplexType to be processed. + /// CodeNamespace to be used when outputting code. + private static void ProcessComplexType(XmlSchemaComplexType complexType, CodeNamespace codeNamespace) + { + CodeMemberMethod outputXmlMethod = new CodeMemberMethod(); + outputXmlMethod.Attributes = MemberAttributes.Public; + outputXmlMethod.ImplementationTypes.Add("ISchemaElement"); + outputXmlMethod.Name = "OutputXml"; + outputXmlMethod.Parameters.Add(new CodeParameterDeclarationExpression("XmlWriter", "writer")); + outputXmlMethod.Statements.Add(GetArgumentNullCheckStatement("writer", false)); + GenerateSummaryComment(outputXmlMethod.Comments, outputXmlComment); + + CodeMemberMethod setAttributeMethod = new CodeMemberMethod(); + setAttributeMethod.Attributes = MemberAttributes.Public; + setAttributeMethod.ImplementationTypes.Add("ISetAttributes"); + setAttributeMethod.Name = "SetAttribute"; + setAttributeMethod.Parameters.Add(new CodeParameterDeclarationExpression(typeof(string), "name")); + setAttributeMethod.Parameters.Add(new CodeParameterDeclarationExpression(typeof(string), "value")); + setAttributeMethod.PrivateImplementationType = new CodeTypeReference("ISetAttributes"); + setAttributeMethod.CustomAttributes.Add(GetCodeAnalysisSuppressionAttribute("Microsoft.Design", "CA1033:InterfaceMethodsShouldBeCallableByChildTypes")); + setAttributeMethod.Statements.Add(GetArgumentNullCheckStatement("name", true)); + + string documentation = GetDocumentation(complexType.Annotation); + + ProcessSimpleContent(complexType.Name, (XmlSchemaSimpleContentExtension)complexType.ContentModel.Content, documentation, codeNamespace, outputXmlMethod, setAttributeMethod, true); + } + + /// + /// Processes an XmlSchemaComplexType into corresponding types. + /// + /// Name to use for the type being output. + /// Namespace of the xml element. + /// XmlSchemaComplexType to be processed. + /// Documentation for the element. + /// CodeNamespace to be used when outputting code. + private static void ProcessComplexType(string typeName, string elementNamespace, XmlSchemaComplexType complexType, string documentation, CodeNamespace codeNamespace) + { + CodeMemberMethod outputXmlMethod = new CodeMemberMethod(); + outputXmlMethod.Attributes = MemberAttributes.Public; + outputXmlMethod.ImplementationTypes.Add("ISchemaElement"); + outputXmlMethod.Name = "OutputXml"; + outputXmlMethod.Parameters.Add(new CodeParameterDeclarationExpression("XmlWriter", "writer")); + outputXmlMethod.Statements.Add(GetArgumentNullCheckStatement("writer", false)); + outputXmlMethod.Statements.Add(new CodeMethodInvokeExpression(new CodeVariableReferenceExpression("writer"), "WriteStartElement", new CodeSnippetExpression(String.Concat("\"", typeName, "\"")), new CodeSnippetExpression(String.Concat("\"", elementNamespace, "\"")))); + GenerateSummaryComment(outputXmlMethod.Comments, outputXmlComment); + + CodeMemberMethod setAttributeMethod = new CodeMemberMethod(); + setAttributeMethod.Attributes = MemberAttributes.Public; + setAttributeMethod.ImplementationTypes.Add("ISetAttributes"); + setAttributeMethod.Name = "SetAttribute"; + setAttributeMethod.Parameters.Add(new CodeParameterDeclarationExpression(typeof(string), "name")); + setAttributeMethod.Parameters.Add(new CodeParameterDeclarationExpression(typeof(string), "value")); + setAttributeMethod.PrivateImplementationType = new CodeTypeReference("ISetAttributes"); + setAttributeMethod.CustomAttributes.Add(GetCodeAnalysisSuppressionAttribute("Microsoft.Design", "CA1033:InterfaceMethodsShouldBeCallableByChildTypes")); + setAttributeMethod.Statements.Add(GetArgumentNullCheckStatement("name", true)); + + if (complexType.ContentModel == null) + { + CodeTypeDeclaration typeDeclaration = new CodeTypeDeclaration(typeName); + typeDeclaration.CustomAttributes.Add(GetGeneratedCodeAttribute()); + typeDeclaration.Attributes = MemberAttributes.Public; + typeDeclaration.IsClass = true; + CodeIterationStatement childEnumStatement = null; + + if (documentation != null) + { + GenerateSummaryComment(typeDeclaration.Comments, documentation); + } + + if (complexType.Particle != null) + { + CodeMemberField childrenField = new CodeMemberField("ElementCollection", "children"); + typeDeclaration.Members.Add(childrenField); + + CodeMemberProperty childrenProperty = new CodeMemberProperty(); + childrenProperty.Attributes = MemberAttributes.Public; + childrenProperty.ImplementationTypes.Add("IParentElement"); + childrenProperty.Name = "Children"; + childrenProperty.Type = new CodeTypeReference("IEnumerable"); + childrenProperty.GetStatements.Add(new CodeMethodReturnStatement(new CodeFieldReferenceExpression(new CodeThisReferenceExpression(), "children"))); + typeDeclaration.Members.Add(childrenProperty); + + CodeMemberProperty filterChildrenProperty = new CodeMemberProperty(); + filterChildrenProperty.Attributes = MemberAttributes.Public; + filterChildrenProperty.ImplementationTypes.Add("IParentElement"); + filterChildrenProperty.Name = "Item"; + filterChildrenProperty.Parameters.Add(new CodeParameterDeclarationExpression(typeof(Type), "childType")); + filterChildrenProperty.Type = new CodeTypeReference("IEnumerable"); + filterChildrenProperty.GetStatements.Add(new CodeMethodReturnStatement(new CodeMethodInvokeExpression(new CodeFieldReferenceExpression(new CodeThisReferenceExpression(), "children"), "Filter", new CodeVariableReferenceExpression("childType")))); + filterChildrenProperty.CustomAttributes.Add(GetCodeAnalysisSuppressionAttribute("Microsoft.Design", "CA1043:UseIntegralOrStringArgumentForIndexers")); + typeDeclaration.Members.Add(filterChildrenProperty); + + CodeMemberMethod addChildMethod = new CodeMemberMethod(); + addChildMethod.Attributes = MemberAttributes.Public; + addChildMethod.ImplementationTypes.Add("IParentElement"); + addChildMethod.Name = "AddChild"; + addChildMethod.Parameters.Add(new CodeParameterDeclarationExpression("ISchemaElement", "child")); + addChildMethod.Statements.Add(GetArgumentNullCheckStatement("child", false)); + CodeExpressionStatement addChildStatement = new CodeExpressionStatement(new CodeMethodInvokeExpression(new CodeFieldReferenceExpression(new CodeThisReferenceExpression(), "children"), "AddElement", new CodeVariableReferenceExpression("child"))); + addChildMethod.Statements.Add(addChildStatement); + CodeAssignStatement setParentStatement = new CodeAssignStatement(new CodePropertyReferenceExpression(new CodeVariableReferenceExpression("child"), "ParentElement"), new CodeThisReferenceExpression()); + addChildMethod.Statements.Add(setParentStatement); + typeDeclaration.Members.Add(addChildMethod); + + CodeMemberMethod removeChildMethod = new CodeMemberMethod(); + removeChildMethod.Attributes = MemberAttributes.Public; + removeChildMethod.ImplementationTypes.Add("IParentElement"); + removeChildMethod.Name = "RemoveChild"; + removeChildMethod.Parameters.Add(new CodeParameterDeclarationExpression("ISchemaElement", "child")); + removeChildMethod.Statements.Add(GetArgumentNullCheckStatement("child", false)); + CodeExpressionStatement removeChildStatement = new CodeExpressionStatement(new CodeMethodInvokeExpression(new CodeFieldReferenceExpression(new CodeThisReferenceExpression(), "children"), "RemoveElement", new CodeVariableReferenceExpression("child"))); + removeChildMethod.Statements.Add(removeChildStatement); + CodeAssignStatement nullParentStatement = new CodeAssignStatement(new CodePropertyReferenceExpression(new CodeVariableReferenceExpression("child"), "ParentElement"), new CodePrimitiveExpression(null)); + removeChildMethod.Statements.Add(nullParentStatement); + typeDeclaration.Members.Add(removeChildMethod); + + CodeMemberMethod createChildMethod = new CodeMemberMethod(); + createChildMethod.Attributes = MemberAttributes.Public; + createChildMethod.ImplementationTypes.Add("ICreateChildren"); + createChildMethod.Name = "CreateChild"; + createChildMethod.Parameters.Add(new CodeParameterDeclarationExpression(typeof(string), "childName")); + createChildMethod.PrivateImplementationType = new CodeTypeReference("ICreateChildren"); + createChildMethod.CustomAttributes.Add(GetCodeAnalysisSuppressionAttribute("Microsoft.Design", "CA1033:InterfaceMethodsShouldBeCallableByChildTypes")); + createChildMethod.ReturnType = new CodeTypeReference("ISchemaElement"); + createChildMethod.Statements.Add(GetArgumentNullCheckStatement("childName", true)); + createChildMethod.Statements.Add(new CodeVariableDeclarationStatement("ISchemaElement", "childValue", new CodePrimitiveExpression(null))); + + CodeConstructor typeConstructor = new CodeConstructor(); + typeConstructor.Attributes = MemberAttributes.Public; + + CodeVariableReferenceExpression collectionVariable = null; + + XmlSchemaChoice schemaChoice = complexType.Particle as XmlSchemaChoice; + if (schemaChoice != null) + { + collectionVariable = ProcessSchemaGroup(schemaChoice, typeConstructor, createChildMethod); + } + else + { + XmlSchemaSequence schemaSequence = complexType.Particle as XmlSchemaSequence; + if (schemaSequence != null) + { + collectionVariable = ProcessSchemaGroup(schemaSequence, typeConstructor, createChildMethod); + } + } + + typeConstructor.Statements.Add(new CodeAssignStatement(new CodeFieldReferenceExpression(new CodeThisReferenceExpression(), "children"), collectionVariable)); + typeDeclaration.Members.Add(typeConstructor); + + CodeConditionStatement childNameNotFound = new CodeConditionStatement(); + childNameNotFound.Condition = new CodeBinaryOperatorExpression(new CodePrimitiveExpression(null), CodeBinaryOperatorType.ValueEquality, new CodeVariableReferenceExpression("childValue")); + 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.\""))))); + createChildMethod.Statements.Add(childNameNotFound); + + if (createChildMethod.Statements.Count > 8) + { + createChildMethod.CustomAttributes.Add(GetCodeAnalysisSuppressionAttribute("Microsoft.Maintainability", "CA1502:AvoidExcessiveComplexity")); + } + + createChildMethod.Statements.Add(new CodeMethodReturnStatement(new CodeVariableReferenceExpression("childValue"))); + typeDeclaration.Members.Add(createChildMethod); + + childEnumStatement = new CodeIterationStatement(); + childEnumStatement.InitStatement = new CodeVariableDeclarationStatement("IEnumerator", "enumerator", new CodeMethodInvokeExpression(new CodeFieldReferenceExpression(new CodeThisReferenceExpression(), "children"), "GetEnumerator")); + childEnumStatement.TestExpression = new CodeMethodInvokeExpression(new CodeVariableReferenceExpression("enumerator"), "MoveNext"); + childEnumStatement.Statements.Add(new CodeVariableDeclarationStatement("ISchemaElement", "childElement", new CodeCastExpression("ISchemaElement", new CodePropertyReferenceExpression(new CodeVariableReferenceExpression("enumerator"), "Current")))); + childEnumStatement.Statements.Add(new CodeMethodInvokeExpression(new CodeVariableReferenceExpression("childElement"), "OutputXml", new CodeVariableReferenceExpression("writer"))); + childEnumStatement.IncrementStatement = new CodeExpressionStatement(new CodeSnippetExpression("")); + + typeDeclaration.BaseTypes.Add(new CodeTypeReference("IParentElement")); + typeDeclaration.BaseTypes.Add(new CodeTypeReference("ICreateChildren")); + } + + // TODO: Handle xs:anyAttribute. + ProcessAttributes(complexType.Attributes, typeDeclaration, outputXmlMethod, setAttributeMethod); + + if (childEnumStatement != null) + { + outputXmlMethod.Statements.Add(childEnumStatement); + } + + typeDeclaration.BaseTypes.Add(new CodeTypeReference("ISchemaElement")); + typeDeclaration.BaseTypes.Add(new CodeTypeReference("ISetAttributes")); + + CodeMemberField parentField = new CodeMemberField("ISchemaElement", "parentElement"); + typeDeclaration.Members.Add(parentField); + + CodeMemberProperty parentProperty = new CodeMemberProperty(); + parentProperty.Attributes = MemberAttributes.Public; + parentProperty.ImplementationTypes.Add("ISchemaElement"); + parentProperty.Name = "ParentElement"; + parentProperty.Type = new CodeTypeReference("ISchemaElement"); + parentProperty.GetStatements.Add(new CodeMethodReturnStatement(new CodeFieldReferenceExpression(new CodeThisReferenceExpression(), "parentElement"))); + parentProperty.SetStatements.Add(new CodeAssignStatement(new CodeFieldReferenceExpression(new CodeThisReferenceExpression(), "parentElement"), new CodeVariableReferenceExpression("value"))); + typeDeclaration.Members.Add(parentProperty); + + if (outputXmlMethod.Statements.Count > 8) + { + outputXmlMethod.CustomAttributes.Add(GetCodeAnalysisSuppressionAttribute("Microsoft.Maintainability", "CA1502:AvoidExcessiveComplexity")); + } + + if (setAttributeMethod.Statements.Count > 8) + { + setAttributeMethod.CustomAttributes.Add(GetCodeAnalysisSuppressionAttribute("Microsoft.Maintainability", "CA1502:AvoidExcessiveComplexity")); + } + + typeDeclaration.Members.Add(outputXmlMethod); + typeDeclaration.Members.Add(setAttributeMethod); + codeNamespace.Types.Add(typeDeclaration); + } + else + { + ProcessSimpleContent(typeName, (XmlSchemaSimpleContentExtension)complexType.ContentModel.Content, documentation, codeNamespace, outputXmlMethod, setAttributeMethod, false); + } + + outputXmlMethod.Statements.Add(new CodeMethodInvokeExpression(new CodeVariableReferenceExpression("writer"), "WriteEndElement")); + } + + /// + /// Processes a collection of attributes, generating the required fields and properties. + /// + /// List of attribute or attributeGroupRef elements being processed. + /// CodeTypeDeclaration to be used when outputting code. + /// Member method for the OutputXml method. + /// Member method for the SetAttribute method. + private static void ProcessAttributes(XmlSchemaObjectCollection attributes, CodeTypeDeclaration typeDeclaration, CodeMemberMethod outputXmlMethod, CodeMemberMethod setAttributeMethod) + { + foreach (XmlSchemaObject schemaObject in attributes) + { + XmlSchemaAttribute schemaAttribute = schemaObject as XmlSchemaAttribute; + if (schemaAttribute != null) + { + ProcessAttribute(schemaAttribute, typeDeclaration, outputXmlMethod, setAttributeMethod); + } + else + { + XmlSchemaAttributeGroupRef schemaAttributeGroupRef = schemaObject as XmlSchemaAttributeGroupRef; + if (schemaAttributeGroupRef != null) + { + XmlSchemaAttributeGroup schemaAttributeGroup = refToAttributeGroups[schemaAttributeGroupRef.RefName.Name]; + // recurse! + ProcessAttributes(schemaAttributeGroup.Attributes, typeDeclaration, outputXmlMethod, setAttributeMethod); + } + } + } + } + + /// + /// Processes an XmlSchemaGroupBase element. + /// + /// Element group to process. + /// Constructor to which statements should be added. + /// Method used for creating children on read-in. + /// A reference to the local variable containing the collection. + private static CodeVariableReferenceExpression ProcessSchemaGroup(XmlSchemaGroupBase schemaGroup, CodeConstructor constructor, CodeMemberMethod createChildMethod) + { + return ProcessSchemaGroup(schemaGroup, constructor, createChildMethod, 0); + } + + /// + /// Processes an XmlSchemaGroupBase element. + /// + /// Element group to process. + /// Constructor to which statements should be added. + /// Method used for creating children on read-in. + /// Depth to which this collection is nested. + /// A reference to the local variable containing the collection. + private static CodeVariableReferenceExpression ProcessSchemaGroup(XmlSchemaGroupBase schemaGroup, CodeConstructor constructor, CodeMemberMethod createChildMethod, int depth) + { + string collectionName = String.Format("childCollection{0}", depth); + CodeVariableReferenceExpression collectionVariableReference = new CodeVariableReferenceExpression(collectionName); + CodeVariableDeclarationStatement collectionStatement = new CodeVariableDeclarationStatement("ElementCollection", collectionName); + if (schemaGroup is XmlSchemaChoice) + { + collectionStatement.InitExpression = new CodeObjectCreateExpression("ElementCollection", new CodePropertyReferenceExpression(new CodeTypeReferenceExpression("ElementCollection.CollectionType"), "Choice")); + } + else + { + collectionStatement.InitExpression = new CodeObjectCreateExpression("ElementCollection", new CodePropertyReferenceExpression(new CodeTypeReferenceExpression("ElementCollection.CollectionType"), "Sequence")); + } + constructor.Statements.Add(collectionStatement); + + foreach (XmlSchemaObject obj in schemaGroup.Items) + { + XmlSchemaElement schemaElement = obj as XmlSchemaElement; + if (schemaElement != null) + { + if (schemaGroup is XmlSchemaChoice) + { + CodeMethodInvokeExpression addItemInvoke = new CodeMethodInvokeExpression(collectionVariableReference, "AddItem", new CodeObjectCreateExpression("ElementCollection.ChoiceItem", new CodeTypeOfExpression(schemaElement.RefName.Name))); + constructor.Statements.Add(addItemInvoke); + } + else + { + CodeMethodInvokeExpression addItemInvoke = new CodeMethodInvokeExpression(collectionVariableReference, "AddItem", new CodeObjectCreateExpression("ElementCollection.SequenceItem", new CodeTypeOfExpression(schemaElement.RefName.Name))); + constructor.Statements.Add(addItemInvoke); + } + + CodeConditionStatement createChildIf = new CodeConditionStatement(); + createChildIf.Condition = new CodeBinaryOperatorExpression(new CodeSnippetExpression(String.Concat("\"", schemaElement.RefName.Name, "\"")), CodeBinaryOperatorType.ValueEquality, new CodeVariableReferenceExpression("childName")); + createChildIf.TrueStatements.Add(new CodeAssignStatement(new CodeVariableReferenceExpression("childValue"), new CodeObjectCreateExpression(schemaElement.RefName.Name))); + createChildMethod.Statements.Add(createChildIf); + + continue; + } + + XmlSchemaAny schemaAny = obj as XmlSchemaAny; + if (schemaAny != null) + { + if (schemaGroup is XmlSchemaChoice) + { + CodeMethodInvokeExpression addItemInvoke = new CodeMethodInvokeExpression(collectionVariableReference, "AddItem", new CodeObjectCreateExpression("ElementCollection.ChoiceItem", new CodeTypeOfExpression("ISchemaElement"))); + constructor.Statements.Add(addItemInvoke); + } + else + { + CodeMethodInvokeExpression addItemInvoke = new CodeMethodInvokeExpression(collectionVariableReference, "AddItem", new CodeObjectCreateExpression("ElementCollection.SequenceItem", new CodeTypeOfExpression("ISchemaElement"), new CodeSnippetExpression("0"), new CodeSnippetExpression("-1"))); + constructor.Statements.Add(addItemInvoke); + } + + continue; + } + + XmlSchemaGroupBase schemaGroupBase = obj as XmlSchemaGroupBase; + if (schemaGroupBase != null) + { + CodeVariableReferenceExpression nestedCollectionReference = ProcessSchemaGroup(schemaGroupBase, constructor, createChildMethod, depth + 1); + CodeMethodInvokeExpression addCollectionInvoke = new CodeMethodInvokeExpression(collectionVariableReference, "AddCollection", nestedCollectionReference); + constructor.Statements.Add(addCollectionInvoke); + + continue; + } + } + + return collectionVariableReference; + } + + /// + /// Processes an XmlSchemaSimpleContentExtension into corresponding types. + /// + /// Name of the type being generated. + /// XmlSchemaSimpleContentExtension being processed. + /// Documentation for the simple content. + /// CodeNamespace to be used when outputting code. + /// Method to use when outputting Xml. + /// Method to use when setting an attribute. + /// If true, generate an abstract class. + private static void ProcessSimpleContent(string typeName, XmlSchemaSimpleContentExtension simpleContent, string documentation, CodeNamespace codeNamespace, CodeMemberMethod outputXmlMethod, CodeMemberMethod setAttributeMethod, bool abstractClass) + { + CodeTypeDeclaration typeDeclaration = new CodeTypeDeclaration(typeName); + typeDeclaration.CustomAttributes.Add(GetGeneratedCodeAttribute()); + typeDeclaration.Attributes = MemberAttributes.Public; + typeDeclaration.IsClass = true; + + if (documentation != null) + { + GenerateSummaryComment(typeDeclaration.Comments, documentation); + } + + if (abstractClass) + { + typeDeclaration.TypeAttributes = System.Reflection.TypeAttributes.Abstract | System.Reflection.TypeAttributes.Public; + } + + // TODO: Handle xs:anyAttribute here. + foreach (XmlSchemaAttribute schemaAttribute in simpleContent.Attributes) + { + ProcessAttribute(schemaAttribute, typeDeclaration, outputXmlMethod, setAttributeMethod); + } + + // This needs to come last, so that the generation code generates the inner content after the attributes. + string contentDocumentation = GetDocumentation(simpleContent.Annotation); + GenerateFieldAndProperty("Content", (string)simpleTypeNamesToClrTypeNames[simpleContent.BaseTypeName.Name], typeDeclaration, outputXmlMethod, setAttributeMethod, null, contentDocumentation, true, false); + + typeDeclaration.BaseTypes.Add(new CodeTypeReference("ISchemaElement")); + typeDeclaration.BaseTypes.Add(new CodeTypeReference("ISetAttributes")); + + CodeMemberField parentField = new CodeMemberField("ISchemaElement", "parentElement"); + typeDeclaration.Members.Add(parentField); + + CodeMemberProperty parentProperty = new CodeMemberProperty(); + parentProperty.Attributes = MemberAttributes.Public; + parentProperty.ImplementationTypes.Add("ISchemaElement"); + parentProperty.Name = "ParentElement"; + parentProperty.Type = new CodeTypeReference("ISchemaElement"); + parentProperty.GetStatements.Add(new CodeMethodReturnStatement(new CodeFieldReferenceExpression(new CodeThisReferenceExpression(), "parentElement"))); + parentProperty.SetStatements.Add(new CodeAssignStatement(new CodeFieldReferenceExpression(new CodeThisReferenceExpression(), "parentElement"), new CodeVariableReferenceExpression("value"))); + typeDeclaration.Members.Add(parentProperty); + + if (outputXmlMethod.Statements.Count > 8) + { + outputXmlMethod.CustomAttributes.Add(GetCodeAnalysisSuppressionAttribute("Microsoft.Maintainability", "CA1502:AvoidExcessiveComplexity")); + } + + if (setAttributeMethod.Statements.Count > 8) + { + setAttributeMethod.CustomAttributes.Add(GetCodeAnalysisSuppressionAttribute("Microsoft.Maintainability", "CA1502:AvoidExcessiveComplexity")); + } + + typeDeclaration.Members.Add(outputXmlMethod); + typeDeclaration.Members.Add(setAttributeMethod); + codeNamespace.Types.Add(typeDeclaration); + } + + /// + /// Processes an attribute, generating the required field and property. Potentially generates + /// an enum for an attribute restriction. + /// + /// Attribute element being processed. + /// CodeTypeDeclaration to be used when outputting code. + /// Member method for the OutputXml method. + /// Member method for the SetAttribute method. + private static void ProcessAttribute(XmlSchemaAttribute attribute, CodeTypeDeclaration typeDeclaration, CodeMemberMethod outputXmlMethod, CodeMemberMethod setAttributeMethod) + { + string attributeName = attribute.QualifiedName.Name; + string rawAttributeType = attribute.AttributeSchemaType.QualifiedName.Name; + string attributeType = null; + EnumDeclaration enumDeclaration = null; + if (rawAttributeType == null || rawAttributeType.Length == 0) + { + ProcessSimpleType(attributeName, attribute.AttributeSchemaType, true, out enumDeclaration, out attributeType); + + if (enumDeclaration != null) + { + typeDeclaration.Members.Add(enumDeclaration.TypeDeclaration); + AddEnumHelperMethods(enumDeclaration, typeDeclaration); + } + } + else + { + attributeType = (string)simpleTypeNamesToClrTypeNames[rawAttributeType]; + } + + string documentation = GetDocumentation(attribute.Annotation); + + // TODO: Handle required fields. + GenerateFieldAndProperty(attributeName, attributeType, typeDeclaration, outputXmlMethod, setAttributeMethod, enumDeclaration, documentation, false, false); + } + + /// + /// Gets the first sentence of a documentation element and returns it as a string. + /// + /// The annotation in which to look for a documentation element. + /// The string representing the first sentence, or null if none found. + private static string GetDocumentation(XmlSchemaAnnotation annotation) + { + string documentation = null; + + if (annotation != null && annotation.Items != null) + { + foreach (XmlSchemaObject obj in annotation.Items) + { + XmlSchemaDocumentation schemaDocumentation = obj as XmlSchemaDocumentation; + if (schemaDocumentation != null) + { + if (schemaDocumentation.Markup.Length > 0) + { + XmlText text = schemaDocumentation.Markup[0] as XmlText; + if (text != null) + { + documentation = text.Value; + } + } + break; + } + } + } + + if (documentation != null) + { + documentation = documentation.Trim(); + } + return documentation; + } + + /// + /// Makes a valid enum value out of the passed in value. May remove spaces, add 'Item' to the + /// start if it begins with an integer, or strip out punctuation. + /// + /// Enum value to be processed. + /// Enum value with invalid characters removed. + private static string MakeEnumValue(string enumValue) + { + if (Char.IsDigit(enumValue[0])) + { + enumValue = String.Concat("Item", enumValue); + } + + StringBuilder newValue = new StringBuilder(); + for (int i = 0; i < enumValue.Length; ++i) + { + if (!Char.IsPunctuation(enumValue[i]) && !Char.IsSymbol(enumValue[i]) && !Char.IsWhiteSpace(enumValue[i])) + { + newValue.Append(enumValue[i]); + } + } + + return newValue.ToString(); + } + + /// + /// Generates the private field and public property for a piece of data. + /// + /// Name of the property being generated. + /// Name of the type for the property. + /// Type declaration into which the field and property should be placed. + /// Member method for the OutputXml method. + /// Member method for the SetAttribute method. + /// EnumDeclaration, which is null unless called from a locally defined enum attribute. + /// Comment string to be placed on the property. + /// If true, the field will be placed in nested content when outputting to XML. + /// If true, the generated serialization code will throw if the field is not set. + private static void GenerateFieldAndProperty(string propertyName, string typeName, CodeTypeDeclaration typeDeclaration, CodeMemberMethod outputXmlMethod, CodeMemberMethod setAttributeMethod, EnumDeclaration enumDeclaration, string documentation, bool nestedContent, bool requiredField) + { + string fieldName = String.Concat(propertyName.Substring(0, 1).ToLower(), propertyName.Substring(1), "Field"); + string fieldNameSet = String.Concat(fieldName, "Set"); + Type type = GetClrTypeByXmlName(typeName); + CodeMemberField fieldMember; + if (type == null) + { + fieldMember = new CodeMemberField(typeName, fieldName); + } + else + { + fieldMember = new CodeMemberField(type, fieldName); + } + fieldMember.Attributes = MemberAttributes.Private; + typeDeclaration.Members.Add(fieldMember); + typeDeclaration.Members.Add(new CodeMemberField(typeof(bool), fieldNameSet)); + + CodeMemberProperty propertyMember = new CodeMemberProperty(); + propertyMember.Attributes = MemberAttributes.Public | MemberAttributes.Final; + if (documentation != null) + { + GenerateSummaryComment(propertyMember.Comments, documentation); + } + propertyMember.Name = propertyName; + if (type == null) + { + propertyMember.Type = new CodeTypeReference(typeName); + } + else + { + propertyMember.Type = new CodeTypeReference(type); + } + + if (propertyMember.Name.StartsWith("src")) + { + propertyMember.CustomAttributes.Add(GetCodeAnalysisSuppressionAttribute("Microsoft.Naming", "CA1709:IdentifiersShouldBeCasedCorrectly")); + } + else if (StronglyTypedClasses.multiUppercaseNameRegex.Match(propertyMember.Name).Success) + { + propertyMember.CustomAttributes.Add(GetCodeAnalysisSuppressionAttribute("Microsoft.Naming", "CA1705:LongAcronymsShouldBePascalCased")); + } + + CodeMethodReturnStatement returnStatement = new CodeMethodReturnStatement(); + returnStatement.Expression = new CodeFieldReferenceExpression(new CodeThisReferenceExpression(), fieldName); + propertyMember.GetStatements.Add(returnStatement); + + CodeAssignStatement assignmentStatement = new CodeAssignStatement(); + propertyMember.SetStatements.Add(new CodeAssignStatement(new CodeFieldReferenceExpression(new CodeThisReferenceExpression(), fieldNameSet), new CodePrimitiveExpression(true))); + assignmentStatement.Left = new CodeFieldReferenceExpression(new CodeThisReferenceExpression(), fieldName); + assignmentStatement.Right = new CodePropertySetValueReferenceExpression(); + propertyMember.SetStatements.Add(assignmentStatement); + + CodeConditionStatement fieldSetStatement = new CodeConditionStatement(); + fieldSetStatement.Condition = new CodeFieldReferenceExpression(new CodeThisReferenceExpression(), fieldNameSet); + + CodeAssignStatement fieldSetAttrStatement = new CodeAssignStatement(); + fieldSetAttrStatement.Left = new CodeFieldReferenceExpression(new CodeThisReferenceExpression(), fieldNameSet); + fieldSetAttrStatement.Right = new CodePrimitiveExpression(true); + + CodeConditionStatement attributeNameMatchStatement = new CodeConditionStatement(); + attributeNameMatchStatement.Condition = new CodeBinaryOperatorExpression(new CodeSnippetExpression(String.Concat("\"", propertyName, "\"")), CodeBinaryOperatorType.IdentityEquality, new CodeVariableReferenceExpression("name")); + + string clrTypeName = (string)simpleTypeNamesToClrTypeNames[typeName]; + switch (clrTypeName) + { + case "string": + if (nestedContent) + { + fieldSetStatement.TrueStatements.Add(new CodeMethodInvokeExpression(new CodeVariableReferenceExpression("writer"), "WriteString", new CodeFieldReferenceExpression(new CodeThisReferenceExpression(), fieldName))); + attributeNameMatchStatement.TrueStatements.Add(new CodeAssignStatement(new CodeFieldReferenceExpression(new CodeThisReferenceExpression(), fieldName), new CodeVariableReferenceExpression("value"))); + } + else + { + fieldSetStatement.TrueStatements.Add(new CodeMethodInvokeExpression(new CodeVariableReferenceExpression("writer"), "WriteAttributeString", new CodeSnippetExpression(String.Concat("\"", propertyName, "\"")), new CodeFieldReferenceExpression(new CodeThisReferenceExpression(), fieldName))); + attributeNameMatchStatement.TrueStatements.Add(new CodeAssignStatement(new CodeFieldReferenceExpression(new CodeThisReferenceExpression(), fieldName), new CodeVariableReferenceExpression("value"))); + } + break; + case "bool": + if (nestedContent) + { + fieldSetStatement.TrueStatements.Add(new CodeMethodInvokeExpression(new CodeVariableReferenceExpression("writer"), "WriteString", new CodeMethodInvokeExpression(new CodeFieldReferenceExpression(new CodeThisReferenceExpression(), fieldName), "ToString", new CodePropertyReferenceExpression(new CodeTypeReferenceExpression("CultureInfo"), "InvariantCulture")))); + } + else + { + 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")))); + } + 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")))); + break; + case "int": + case "long": + if (nestedContent) + { + fieldSetStatement.TrueStatements.Add(new CodeMethodInvokeExpression(new CodeVariableReferenceExpression("writer"), "WriteString", new CodeMethodInvokeExpression(new CodeFieldReferenceExpression(new CodeThisReferenceExpression(), fieldName), "ToString", new CodePropertyReferenceExpression(new CodeTypeReferenceExpression("CultureInfo"), "InvariantCulture")))); + } + else + { + 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")))); + } + 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")))); + break; + default: + if (typeName == "DateTime") + { + if (nestedContent) + { + fieldSetStatement.TrueStatements.Add(new CodeMethodInvokeExpression(new CodeVariableReferenceExpression("writer"), "WriteString", new CodeMethodInvokeExpression(new CodeFieldReferenceExpression(new CodeThisReferenceExpression(), fieldName), "ToString", new CodePropertyReferenceExpression(new CodeTypeReferenceExpression("CultureInfo"), "InvariantCulture")))); + } + else + { + 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")))); + } + 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")))); + break; + } + + if (enumDeclaration == null) + { + GenerateOutputForEnum(fieldSetStatement, attributeNameMatchStatement, typeNamesToEnumDeclarations[typeName], fieldName, propertyName); + } + else + { + GenerateOutputForEnum(fieldSetStatement, attributeNameMatchStatement, enumDeclaration, fieldName, propertyName); + } + break; + } + + attributeNameMatchStatement.TrueStatements.Add(fieldSetAttrStatement); + + // TODO: Add throw to falseStatements if required field not set. + outputXmlMethod.Statements.Add(fieldSetStatement); + setAttributeMethod.Statements.Add(attributeNameMatchStatement); + + typeDeclaration.Members.Add(propertyMember); + } + + /// + /// Generates output for an enum type. Will generate a switch statement for normal enums, and if statements + /// for a flags enum. + /// + /// If statement to add statements to. + /// If statement to add statements to. + /// Enum declaration for this field. Could be locally defined enum or global. + /// Name of the private field. + /// Name of the property (and XML attribute). + private static void GenerateOutputForEnum(CodeConditionStatement fieldSetStatement, CodeConditionStatement attributeNameMatchStatement, EnumDeclaration enumDeclaration, string fieldName, string propertyName) + { + CodeTypeDeclaration enumParent = enumsToParseMethodClasses[enumDeclaration]; + + if (enumDeclaration.Flags) + { + CodeVariableDeclarationStatement outputValueVariable = new CodeVariableDeclarationStatement(typeof(string), "outputValue", new CodeSnippetExpression("\"\"")); + fieldSetStatement.TrueStatements.Add(outputValueVariable); + + foreach (string key in enumDeclaration.Values) + { + CodeConditionStatement enumIfStatement = new CodeConditionStatement(); + 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")); + CodeConditionStatement lengthIfStatement = new CodeConditionStatement(); + lengthIfStatement.Condition = new CodeBinaryOperatorExpression(new CodePropertyReferenceExpression(new CodeVariableReferenceExpression("outputValue"), "Length"), CodeBinaryOperatorType.IdentityInequality, new CodeSnippetExpression("0")); + lengthIfStatement.TrueStatements.Add(new CodeAssignStatement(new CodeVariableReferenceExpression("outputValue"), new CodeBinaryOperatorExpression(new CodeVariableReferenceExpression("outputValue"), CodeBinaryOperatorType.Add, new CodeSnippetExpression("\" \"")))); + enumIfStatement.TrueStatements.Add(lengthIfStatement); + enumIfStatement.TrueStatements.Add(new CodeAssignStatement(new CodeVariableReferenceExpression("outputValue"), new CodeBinaryOperatorExpression(new CodeVariableReferenceExpression("outputValue"), CodeBinaryOperatorType.Add, new CodeSnippetExpression(String.Concat("\"", key, "\""))))); + fieldSetStatement.TrueStatements.Add(enumIfStatement); + } + + attributeNameMatchStatement.TrueStatements.Add(new CodeMethodInvokeExpression( + new CodeTypeReferenceExpression(enumParent.Name), + String.Concat("TryParse", enumDeclaration.Name), + new CodeVariableReferenceExpression("value"), + new CodeDirectionExpression(FieldDirection.Out, new CodeFieldReferenceExpression(new CodeThisReferenceExpression(), fieldName)))); + + fieldSetStatement.TrueStatements.Add(new CodeMethodInvokeExpression(new CodeVariableReferenceExpression("writer"), "WriteAttributeString", new CodeSnippetExpression(String.Concat("\"", propertyName, "\"")), new CodeSnippetExpression(String.Concat("outputValue")))); + } + else + { + foreach (string key in enumDeclaration.Values) + { + CodeConditionStatement enumOutStatement = new CodeConditionStatement(); + enumOutStatement.Condition = new CodeBinaryOperatorExpression(new CodeFieldReferenceExpression(new CodeThisReferenceExpression(), fieldName), CodeBinaryOperatorType.ValueEquality, new CodePropertyReferenceExpression(new CodeSnippetExpression(enumDeclaration.Name), MakeEnumValue(key))); + enumOutStatement.TrueStatements.Add(new CodeMethodInvokeExpression(new CodeVariableReferenceExpression("writer"), "WriteAttributeString", new CodeSnippetExpression(String.Concat("\"", propertyName, "\"")), new CodeSnippetExpression(String.Concat("\"", key, "\"")))); + fieldSetStatement.TrueStatements.Add(enumOutStatement); + } + + attributeNameMatchStatement.TrueStatements.Add(new CodeAssignStatement( + new CodeFieldReferenceExpression(new CodeThisReferenceExpression(), fieldName), + new CodeMethodInvokeExpression(new CodeTypeReferenceExpression(enumParent.Name), + String.Concat("Parse", enumDeclaration.Name), + new CodeVariableReferenceExpression("value")))); + } + } + + /// + /// Generates a summary comment. + /// + /// Comments collection to add the comments to. + /// Content of the comment. + private static void GenerateSummaryComment(CodeCommentStatementCollection comments, string content) + { + using (StringWriter sw = new StringWriter()) + { + XmlTextWriter writer = null; + + // create the comment as xml to ensure proper escaping of special xml characters + try + { + writer = new XmlTextWriter(sw); + writer.Indentation = 0; + + writer.WriteStartElement("summary"); + writer.WriteString(Environment.NewLine); + + string nextComment; + int newlineIndex = content.IndexOf(Environment.NewLine); + int offset = 0; + while (newlineIndex != -1) + { + nextComment = content.Substring(offset, newlineIndex - offset).Trim(); + writer.WriteString(nextComment); + writer.WriteString(Environment.NewLine); + offset = newlineIndex + Environment.NewLine.Length; + newlineIndex = content.IndexOf(Environment.NewLine, offset); + } + nextComment = content.Substring(offset).Trim(); + writer.WriteString(nextComment); + writer.WriteString(Environment.NewLine); + + writer.WriteEndElement(); + } + finally + { + if (null != writer) + { + writer.Close(); + } + } + + // create the comment statements (one per line of xml) + using (StringReader sr = new StringReader(sw.ToString())) + { + string line; + + while (null != (line = sr.ReadLine())) + { + comments.Add(new CodeCommentStatement(line, true)); + } + } + } + } + + /// + /// Gets the CLR type for simple XML type. + /// + /// Plain text name of type. + /// Type corresponding to parameter. + private static Type GetClrTypeByXmlName(string typeName) + { + switch (typeName) + { + case "bool": + return typeof(bool); + case "int": + return typeof(int); + case "long": + return typeof(long); + case "string": + return typeof(string); + default: + return null; + } + } + + /// + /// Processes an XmlSchemaSimpleType into corresponding types. + /// + /// XmlSchemaSimpleType to be processed. + /// CodeNamespace to be used when outputting code. + private static void ProcessSimpleType(XmlSchemaSimpleType simpleType, CodeNamespace codeNamespace) + { + EnumDeclaration enumDeclaration; + string simpleTypeName = simpleType.Name; + string baseTypeName; + + ProcessSimpleType(simpleTypeName, simpleType, false, out enumDeclaration, out baseTypeName); + + simpleTypeNamesToClrTypeNames.Add(simpleTypeName, baseTypeName); + + if (enumDeclaration != null) + { + codeNamespace.Types.Add(enumDeclaration.TypeDeclaration); + typeNamesToEnumDeclarations.Add(simpleTypeName, enumDeclaration); + AddEnumHelperMethods(enumDeclaration, codeNamespace); + } + } + + /// + /// Processes an XmlSchemaSimpleType into corresponding code. + /// + /// Name for the type. + /// XmlSchemaSimpleType to be processed. + /// CodeNamespace to be used when outputting code for global types. + /// CodeTypeDeclaration to be used when outputting code for nested types. + /// Member method for the OutputXml method for nested types. + /// Member method for the SetAttribute method for nested types. + private static void ProcessSimpleType(string simpleTypeName, XmlSchemaSimpleType simpleType, bool nestedType, out EnumDeclaration enumDeclaration, out string baseTypeName) + { + enumDeclaration = null; + baseTypeName = null; + + // XSD supports simpleTypes derived by union, list, or restriction; restrictions can have any + // combination of pattern, enumeration, length, and more; lists can contain any other simpleType. + // XsdGen, in contrast, only supports a limited set of values... + // Unions are weakly supported by just using the first member type + // restrictions must either be all enumeration or a single pattern, a list must be of a + // single simpleType which itself is only a restriction of enumeration. + if (simpleType.Content is XmlSchemaSimpleTypeUnion) + { + XmlSchemaSimpleTypeUnion union = simpleType.Content as XmlSchemaSimpleTypeUnion; + if (union.MemberTypes.Length > 0) + { + baseTypeName = union.MemberTypes[0].Name; + return; + } + else + { + baseTypeName = "string"; + return; + } + } + + bool listType = false; // XSD lists become [Flag]enums in C#... + XmlSchemaSimpleTypeList simpleTypeList = simpleType.Content as XmlSchemaSimpleTypeList; + XmlSchemaSimpleTypeRestriction simpleTypeRestriction = simpleType.Content as XmlSchemaSimpleTypeRestriction; + + if (simpleTypeList != null) + { + baseTypeName = simpleTypeList.ItemTypeName.Name; + + if (String.IsNullOrEmpty(baseTypeName)) + { + simpleTypeRestriction = simpleTypeList.ItemType.Content as XmlSchemaSimpleTypeRestriction; + if (simpleTypeRestriction == null) + { + string appName = typeof(XsdGen).Assembly.GetName().Name; + throw new NotImplementedException(string.Format("{0} does not support a that does not contain a /.", appName)); + } + + listType = true; + } + else + { + // We expect to find an existing enum already declared! + EnumDeclaration existingEnumDeclaration = typeNamesToEnumDeclarations[baseTypeName]; + // TODO: do we need to further alter the Flags setter code because of the helper stuff? + // As far as I can tell, this code is never exercised by our existing XSDs! + existingEnumDeclaration.SetFlags(); + } + } + + if (simpleTypeRestriction == null) + { + string appName = typeof(XsdGen).Assembly.GetName().Name; + throw new NotImplementedException(string.Format("{0} does not understand this simpleType!", appName)); + } + + bool foundPattern = false; + foreach (XmlSchemaFacet facet in simpleTypeRestriction.Facets) + { + XmlSchemaEnumerationFacet enumFacet = facet as XmlSchemaEnumerationFacet; + XmlSchemaPatternFacet patternFacet = facet as XmlSchemaPatternFacet; + + if (enumFacet != null) + { + if (foundPattern) + { + string appName = typeof(XsdGen).Assembly.GetName().Name; + throw new NotImplementedException(string.Format("{0} does not support restrictions containing both and .", appName)); + } + + if (enumDeclaration == null) + { + // For nested types, the simple name comes from the attribute name, with "Type" appended + // to prevent name collision with the attribute member itself. + if (nestedType) + { + simpleTypeName = String.Concat(simpleTypeName, "Type"); + } + baseTypeName = simpleTypeName; + + string typeDocumentation = GetDocumentation(simpleType.Annotation); + enumDeclaration = new EnumDeclaration(simpleTypeName, typeDocumentation); + } + + string documentation = GetDocumentation(enumFacet.Annotation); + enumDeclaration.AddValue(enumFacet.Value, documentation); + } + + if (patternFacet != null) + { + if (enumDeclaration != null) + { + string appName = typeof(XsdGen).Assembly.GetName().Name; + throw new NotImplementedException(string.Format("{0} does not support restrictions containing both and .", appName)); + } + + if (foundPattern) + { + string appName = typeof(XsdGen).Assembly.GetName().Name; + throw new NotImplementedException(string.Format("{0} does not support restrictions multiple elements.", appName)); + } + + foundPattern = true; + } + } + + if (enumDeclaration != null && listType) + { + enumDeclaration.SetFlags(); + } + + if (String.IsNullOrEmpty(baseTypeName)) + { + baseTypeName = (string)simpleTypeNamesToClrTypeNames[simpleTypeRestriction.BaseTypeName.Name]; + } + } + + /// + /// Creates an attribute declaration indicating generated code including the tool name and version. + /// + /// GeneratedCodeAttribute declearation. + private static CodeAttributeDeclaration GetGeneratedCodeAttribute() + { + AssemblyName generatorAssemblyName = typeof(XsdGen).Assembly.GetName(); + return new CodeAttributeDeclaration("GeneratedCode", + new CodeAttributeArgument(new CodePrimitiveExpression(generatorAssemblyName.Name)), + new CodeAttributeArgument(new CodePrimitiveExpression(generatorAssemblyName.Version.ToString()))); + } + + /// + /// Creates a code statement to throw an exception if an argument is null. + /// + /// Name of the argument to check. + /// True to check for null-or-empty instead of just null + /// Code condition statement. + private static CodeConditionStatement GetArgumentNullCheckStatement(string argumentName, bool nullOrEmpty) + { + CodeConditionStatement conditionStatement = new CodeConditionStatement(); + if (nullOrEmpty) + { + conditionStatement.Condition = new CodeMethodInvokeExpression(new CodeMethodReferenceExpression(new CodeTypeReferenceExpression("String"), "IsNullOrEmpty"), new CodeVariableReferenceExpression(argumentName)); + } + else + { + conditionStatement.Condition = new CodeBinaryOperatorExpression(new CodePrimitiveExpression(null), CodeBinaryOperatorType.ValueEquality, new CodeVariableReferenceExpression(argumentName)); + } + + conditionStatement.TrueStatements.Add(new CodeThrowExceptionStatement(new CodeObjectCreateExpression("ArgumentNullException", new CodeSnippetExpression(String.Concat("\"", argumentName, "\""))))); + return conditionStatement; + } + + /// + /// Creates an attribute declaration to suppress a particular code-analysis message. + /// + /// Code analysis category, such as "Microsoft.Design" + /// Code analysis ID number. + /// SuppressMessageAttribute declaration. + private static CodeAttributeDeclaration GetCodeAnalysisSuppressionAttribute(string category, string checkId) + { + return new CodeAttributeDeclaration("SuppressMessage", + new CodeAttributeArgument(new CodePrimitiveExpression(category)), + new CodeAttributeArgument(new CodePrimitiveExpression(checkId))); + } + + /// + /// Class representing an enum declaration. + /// + internal class EnumDeclaration + { + private string enumTypeName; + private CodeTypeDeclaration declaration; + private bool flags; + private StringCollection enumValues; + + /// + /// Creates a new enum declaration with the given name. + /// + /// Name of the type for the enum. + /// Documentation for the enum type. + public EnumDeclaration(string enumTypeName, string documentation) + { + this.enumTypeName = enumTypeName; + + this.declaration = new CodeTypeDeclaration(enumTypeName); + this.declaration.CustomAttributes.Add(GetGeneratedCodeAttribute()); + this.declaration.Attributes = MemberAttributes.Public; + this.declaration.IsEnum = true; + + if (documentation != null) + { + GenerateSummaryComment(this.declaration.Comments, documentation); + } + + this.enumValues = new StringCollection(); + } + + public CodeTypeDeclaration TypeDeclaration + { + get { return this.declaration; } + } + + /// + /// Gets the enumeration values. + /// + /// The enumeration values. + public ICollection Values + { + get { return this.enumValues; } + } + + /// + /// Gets the enumeration name. + /// + /// The enumeration name. + public string Name + { + get { return this.enumTypeName; } + } + + /// + /// Gets the enumeration flags property. + /// + /// Whether the enumeration is a [Flags] type. + public bool Flags + { + get { return this.flags; } + } + + /// + /// Sets the [Flags] property on the enumeration. Once set, this cannot be undone. + /// + public void SetFlags() + { + if (this.flags) + { + return; + } + + this.flags = true; + + this.declaration.CustomAttributes.Add(new CodeAttributeDeclaration("Flags")); + SwitchToNoneValue(); + + int enumValue = 0; + foreach (CodeMemberField enumField in this.declaration.Members) + { + enumField.InitExpression = new CodeSnippetExpression(enumValue.ToString()); + if (enumValue == 0) + { + enumValue = 1; + } + else + { + enumValue *= 2; + } + } + } + + private void InjectIllegalAndNotSetValues() + { + CodeMemberField memberIllegal = new CodeMemberField(typeof(int), "IllegalValue"); + CodeMemberField memberNotSet = new CodeMemberField(typeof(int), "NotSet"); + + memberIllegal.InitExpression = new CodeFieldReferenceExpression(new CodeTypeReferenceExpression(typeof(int)), "MaxValue"); + // Using "-1" for "NotSet" ensure that the next value is zero, which is consistent + // with older (3.0) behavior. + memberNotSet.InitExpression = new CodePrimitiveExpression(-1); + + this.declaration.Members.Insert(0, memberIllegal); + this.declaration.Members.Insert(1, memberNotSet); + } + + private void SwitchToNoneValue() + { + if (this.enumValues.Count > 0) + { + // Remove the "IllegalValue" and "NotSet" values first. + this.declaration.Members.RemoveAt(0); + this.declaration.Members.RemoveAt(0); + + CodeMemberField memberNone = new CodeMemberField(typeof(int), "None"); + memberNone.InitExpression = new CodePrimitiveExpression(0); + + this.declaration.Members.Insert(0, memberNone); + } + } + + /// + /// Add a value to the enumeration. + /// + /// The value to add. + public void AddValue(string enumValue, string documentation) + { + if (this.enumValues.Count == 0) + { + InjectIllegalAndNotSetValues(); + } + + this.enumValues.Add(enumValue); + CodeMemberField memberField = new CodeMemberField(typeof(int), MakeEnumValue(enumValue)); + //memberField.Attributes + this.declaration.Members.Add(memberField); + if (documentation != null) + { + GenerateSummaryComment(memberField.Comments, documentation); + } + } + } + + private static void AddEnumHelperMethods(EnumDeclaration enumDeclaration, CodeNamespace codeNamespace) + { + if (enumHelperClass == null) + { + enumHelperClass = new CodeTypeDeclaration("Enums"); + enumHelperClass.CustomAttributes.Add(GetGeneratedCodeAttribute()); + // The static and final attributes don't seem to get applied, but we'd prefer if they were. + enumHelperClass.Attributes = MemberAttributes.Public | MemberAttributes.Static | MemberAttributes.Final; + codeNamespace.Types.Add(enumHelperClass); + } + + AddEnumHelperMethods(enumDeclaration, enumHelperClass); + } + + private static void AddEnumHelperMethods(EnumDeclaration enumDeclaration, CodeTypeDeclaration parentType) + { + CodeTypeReference stringType = new CodeTypeReference(typeof(string)); + CodeTypeReference boolType = new CodeTypeReference(typeof(bool)); + CodeTypeReference enumType = new CodeTypeReference(typeof(Enum)); + CodeTypeReference newEnumType = new CodeTypeReference(enumDeclaration.Name); + + CodePrimitiveExpression falseValue = new CodePrimitiveExpression(false); + CodePrimitiveExpression trueValue = new CodePrimitiveExpression(true); + CodeMethodReturnStatement returnFalse = new CodeMethodReturnStatement(falseValue); + CodeMethodReturnStatement returnTrue = new CodeMethodReturnStatement(trueValue); + + string parseMethodName = String.Concat("Parse", enumDeclaration.Name); + string tryParseMethodName = String.Concat("TryParse", enumDeclaration.Name); + + CodeFieldReferenceExpression defaultEnumValue = null; + CodeFieldReferenceExpression illegalEnumValue = null; + bool addParse = true; + if (enumDeclaration.Flags) + { + defaultEnumValue = new CodeFieldReferenceExpression(new CodeTypeReferenceExpression(newEnumType), "None"); + illegalEnumValue = defaultEnumValue; + // Because there's no "IllegalValue" for [Flags] enums, we can't create the Parse() + // method. We can still create the TryParse() method, though! + addParse = false; + } + else + { + defaultEnumValue = new CodeFieldReferenceExpression(new CodeTypeReferenceExpression(newEnumType), "NotSet"); + illegalEnumValue = new CodeFieldReferenceExpression(new CodeTypeReferenceExpression(newEnumType), "IllegalValue"); + } + + if (addParse) + { + CodeMemberMethod parseNewEnum = new CodeMemberMethod(); + GenerateSummaryComment(parseNewEnum.Comments, String.Format("Parses a {0} from a string.", enumDeclaration.Name)); + parseNewEnum.Attributes = MemberAttributes.Public | MemberAttributes.Static; + parseNewEnum.Name = parseMethodName; + parseNewEnum.ReturnType = newEnumType; + parseNewEnum.Parameters.Add(new CodeParameterDeclarationExpression(stringType, "value")); + + parseNewEnum.Statements.Add(new CodeVariableDeclarationStatement(newEnumType, "parsedValue")); + + // Just delegate to the TryParse version... + parseNewEnum.Statements.Add(new CodeMethodInvokeExpression( + new CodeMethodReferenceExpression(new CodeTypeReferenceExpression(parentType.Name), tryParseMethodName), + new CodeArgumentReferenceExpression("value"), + new CodeDirectionExpression(FieldDirection.Out, new CodeVariableReferenceExpression("parsedValue")))); + + parseNewEnum.Statements.Add(new CodeMethodReturnStatement(new CodeVariableReferenceExpression("parsedValue"))); + parentType.Members.Add(parseNewEnum); + } + + CodeMemberMethod tryParseNewEnum = new CodeMemberMethod(); + GenerateSummaryComment(tryParseNewEnum.Comments, String.Format("Tries to parse a {0} from a string.", enumDeclaration.Name)); + tryParseNewEnum.Attributes = MemberAttributes.Public | MemberAttributes.Static; + tryParseNewEnum.Name = tryParseMethodName; + tryParseNewEnum.ReturnType = boolType; + CodeParameterDeclarationExpression valueDeclaration = new CodeParameterDeclarationExpression(stringType, "value"); + CodeParameterDeclarationExpression parsedValueDeclaration = new CodeParameterDeclarationExpression(newEnumType, "parsedValue"); + parsedValueDeclaration.Direction = FieldDirection.Out; + tryParseNewEnum.Parameters.Add(valueDeclaration); + tryParseNewEnum.Parameters.Add(parsedValueDeclaration); + + CodeArgumentReferenceExpression value = new CodeArgumentReferenceExpression(valueDeclaration.Name); + CodeArgumentReferenceExpression parsedValue = new CodeArgumentReferenceExpression(parsedValueDeclaration.Name); + + tryParseNewEnum.Statements.Add(new CodeAssignStatement(parsedValue, defaultEnumValue)); + + tryParseNewEnum.Statements.Add(new CodeConditionStatement( + new CodeMethodInvokeExpression(new CodeMethodReferenceExpression(new CodeTypeReferenceExpression(stringType), "IsNullOrEmpty"), value), + returnFalse)); + + // The structure is similar, but distinct, for regular and flag-style enums. In particular, + // for a flags-style enum we have to be able to parse multiple values, separated by + // spaces, and each value is bitwise-OR'd together. + CodeStatementCollection nestedIfParent = tryParseNewEnum.Statements; + CodeExpression valueToTest = value; + + // For Flags-style enums, we need to loop over the space-separated values... + if (enumDeclaration.Flags) + { + CodeVariableDeclarationStatement split = new CodeVariableDeclarationStatement(typeof(string[]), "splitValue", + new CodeMethodInvokeExpression(value, "Split", + new CodeMethodInvokeExpression(new CodePrimitiveExpression(" \t\r\n"), "ToCharArray"), + new CodeFieldReferenceExpression(new CodeTypeReferenceExpression(typeof(StringSplitOptions)), "RemoveEmptyEntries"))); + tryParseNewEnum.Statements.Add(split); + + CodeIterationStatement flagLoop = new CodeIterationStatement( + new CodeVariableDeclarationStatement(typeof(IEnumerator), "enumerator", + new CodeMethodInvokeExpression(new CodeVariableReferenceExpression(split.Name), "GetEnumerator")), + new CodeMethodInvokeExpression(new CodeVariableReferenceExpression("enumerator"), "MoveNext"), + new CodeSnippetStatement("")); + tryParseNewEnum.Statements.Add(flagLoop); + + CodeVariableDeclarationStatement currentValue = new CodeVariableDeclarationStatement(typeof(string), "currentValue", + new CodeCastExpression(stringType, + new CodePropertyReferenceExpression(new CodeVariableReferenceExpression("enumerator"), "Current"))); + flagLoop.Statements.Add(currentValue); + valueToTest = new CodeVariableReferenceExpression(currentValue.Name); + + nestedIfParent = flagLoop.Statements; + } + + // We can't just Enum.Parse, because some values are also keywords (like 'string', 'int', 'default'), + // and these get generated as '@'-prefixed values. Instead, we 'switch' on the value and do it manually. + // Actually, we if/else, because CodeDom doesn't support 'switch'! Also, we nest the successive 'if's + // in order to short-circuit the parsing as soon as there's a match. + foreach (string enumValue in enumDeclaration.Values) + { + CodeFieldReferenceExpression enumValueReference = new CodeFieldReferenceExpression(new CodeTypeReferenceExpression(newEnumType), MakeEnumValue(enumValue)); + CodeConditionStatement ifStatement = new CodeConditionStatement( + new CodeBinaryOperatorExpression(new CodePrimitiveExpression(enumValue), CodeBinaryOperatorType.ValueEquality, valueToTest)); + if (enumDeclaration.Flags) + { + ifStatement.TrueStatements.Add(new CodeAssignStatement(parsedValue, + new CodeBinaryOperatorExpression(parsedValue, CodeBinaryOperatorType.BitwiseOr, enumValueReference))); + } + else + { + ifStatement.TrueStatements.Add(new CodeAssignStatement(parsedValue, enumValueReference)); + } + nestedIfParent.Add(ifStatement); + nestedIfParent = ifStatement.FalseStatements; + } + + // Finally, if we didn't find a match, it's illegal (or none, for flags)! + nestedIfParent.Add(new CodeAssignStatement(parsedValue, illegalEnumValue)); + nestedIfParent.Add(returnFalse); + + tryParseNewEnum.Statements.Add(returnTrue); + + parentType.Members.Add(tryParseNewEnum); + + enumsToParseMethodClasses.Add(enumDeclaration, parentType); + } + } +} 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 @@ + + + + Exe + + net461 + true + false + true + embedded + true + CS0618 + + + + + + + + + + + + + + + + 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 @@ +// 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. + +namespace WixToolset.Tools +{ + using System; + using System.CodeDom; + using System.CodeDom.Compiler; + using System.Collections; + using System.IO; + using System.Xml; + using System.Xml.Schema; + using Microsoft.CSharp; + using WixToolset; + + /// + /// Generates a strongly-typed C# class from an XML schema (XSD). + /// + public class XsdGen + { + private string xsdFile; + private string outFile; + private string outputNamespace; + private string commonNamespace; + private bool showHelp; + + /// + /// Constructor for the XsdGen class. + /// + /// Command-line arguments passed to the program. + private XsdGen(string[] args) + { + this.ParseCommandlineArgs(args); + + // show usage information + if (this.showHelp) + { + Console.WriteLine("usage: XsdGen.exe .xsd []"); + return; + } + + // ensure that the schema file exists + if (!File.Exists(this.xsdFile)) + { + throw new ApplicationException(String.Format("Schema file does not exist: '{0}'.", this.xsdFile)); + } + + XmlSchema document = null; + using (StreamReader xsdFileReader = new StreamReader(this.xsdFile)) + { + document = XmlSchema.Read(xsdFileReader, new ValidationEventHandler(this.ValidationHandler)); + } + + CodeCompileUnit codeCompileUnit = StronglyTypedClasses.Generate(document, this.outputNamespace, this.commonNamespace); + + using (CSharpCodeProvider codeProvider = new CSharpCodeProvider()) + { + ICodeGenerator generator = codeProvider.CreateGenerator(); + + CodeGeneratorOptions options = new CodeGeneratorOptions(); + options.BlankLinesBetweenMembers = true; + options.BracingStyle = "C"; + options.IndentString = " "; + + using (StreamWriter csharpFileWriter = new StreamWriter(this.outFile)) + { + generator.GenerateCodeFromCompileUnit(codeCompileUnit, csharpFileWriter, options); + } + } + } + + /// + /// The main entry point for the application. + /// + /// The command line arguments. + /// The error code. + [STAThread] + public static int Main(string[] args) + { + try + { + XsdGen xsdGen = new XsdGen(args); + } + catch (Exception e) + { + Console.WriteLine("XsdGen.exe : fatal error MSF0000: {0}\r\n\r\nStack Trace:\r\n{1}", e.Message, e.StackTrace); + return 1; + } + + return 0; + } + + /// + /// Validation event handler. + /// + /// Sender for the event. + /// Event args. + public void ValidationHandler(object sender, ValidationEventArgs e) + { + } + + /// + /// Parse the command line arguments. + /// + /// Command-line arguments. + private void ParseCommandlineArgs(string[] args) + { + if (3 > args.Length) + { + this.showHelp = true; + } + else + { + this.xsdFile = args[0]; + this.outFile = args[1]; + this.outputNamespace = args[2]; + + if (args.Length >= 4) + { + this.commonNamespace = args[3]; + } + } + } + } +} 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 @@ + + + + + + $(MSBuildAllProjects);$(MSBuildThisFileFullPath) + + + + + + + + + + $(MSBuildThisFileDirectory)..\tools\full\ + + + + + + + + XsdGen; + $(PrepareResourcesDependsOn) + + + + + + $(IntermediateOutputPath)%(XsdGenSource.Filename).cs + %(XsdGenSource.CommonNamespace) + + + + + + + + + + + %(XsdGenCsFile.Filename)%(XsdGenCsFile.Extension) + + + + + 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 @@ + + + + + + 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 @@ +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 15 +VisualStudioVersion = 15.0.27130.2003 +MinimumVisualStudioVersion = 15.0.26124.0 +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WixBuildTools.XsdGen", "src\WixBuildTools.XsdGen\WixBuildTools.XsdGen.csproj", "{E89E52C9-A4A1-4174-A1B1-3B72975E6ED6}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WixBuildTools.MsgGen", "src\WixBuildTools.MsgGen\WixBuildTools.MsgGen.csproj", "{DB6EF6F3-51B1-4214-9A14-D501C23F6FA4}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WixBuildTools.TestSupport", "src\WixBuildTools.TestSupport\WixBuildTools.TestSupport.csproj", "{6C57EF2C-979A-4106-A9E5-FE342810619A}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "WixBuildTools.TestSupport.Native", "src\WixBuildTools.TestSupport.Native\WixBuildTools.TestSupport.Native.vcxproj", "{95BABD97-FBDB-453A-AF8A-FA031A07B599}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{F6660B22-092F-4BC5-A303-E6F696C31E1B}" + ProjectSection(SolutionItems) = preProject + .editorconfig = .editorconfig + EndProjectSection +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Debug|x64 = Debug|x64 + Debug|x86 = Debug|x86 + Release|Any CPU = Release|Any CPU + Release|x64 = Release|x64 + Release|x86 = Release|x86 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {E89E52C9-A4A1-4174-A1B1-3B72975E6ED6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {E89E52C9-A4A1-4174-A1B1-3B72975E6ED6}.Debug|Any CPU.Build.0 = Debug|Any CPU + {E89E52C9-A4A1-4174-A1B1-3B72975E6ED6}.Debug|x64.ActiveCfg = Debug|Any CPU + {E89E52C9-A4A1-4174-A1B1-3B72975E6ED6}.Debug|x64.Build.0 = Debug|Any CPU + {E89E52C9-A4A1-4174-A1B1-3B72975E6ED6}.Debug|x86.ActiveCfg = Debug|Any CPU + {E89E52C9-A4A1-4174-A1B1-3B72975E6ED6}.Debug|x86.Build.0 = Debug|Any CPU + {E89E52C9-A4A1-4174-A1B1-3B72975E6ED6}.Release|Any CPU.ActiveCfg = Release|Any CPU + {E89E52C9-A4A1-4174-A1B1-3B72975E6ED6}.Release|Any CPU.Build.0 = Release|Any CPU + {E89E52C9-A4A1-4174-A1B1-3B72975E6ED6}.Release|x64.ActiveCfg = Release|Any CPU + {E89E52C9-A4A1-4174-A1B1-3B72975E6ED6}.Release|x64.Build.0 = Release|Any CPU + {E89E52C9-A4A1-4174-A1B1-3B72975E6ED6}.Release|x86.ActiveCfg = Release|Any CPU + {E89E52C9-A4A1-4174-A1B1-3B72975E6ED6}.Release|x86.Build.0 = Release|Any CPU + {DB6EF6F3-51B1-4214-9A14-D501C23F6FA4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {DB6EF6F3-51B1-4214-9A14-D501C23F6FA4}.Debug|Any CPU.Build.0 = Debug|Any CPU + {DB6EF6F3-51B1-4214-9A14-D501C23F6FA4}.Debug|x64.ActiveCfg = Debug|Any CPU + {DB6EF6F3-51B1-4214-9A14-D501C23F6FA4}.Debug|x64.Build.0 = Debug|Any CPU + {DB6EF6F3-51B1-4214-9A14-D501C23F6FA4}.Debug|x86.ActiveCfg = Debug|Any CPU + {DB6EF6F3-51B1-4214-9A14-D501C23F6FA4}.Debug|x86.Build.0 = Debug|Any CPU + {DB6EF6F3-51B1-4214-9A14-D501C23F6FA4}.Release|Any CPU.ActiveCfg = Release|Any CPU + {DB6EF6F3-51B1-4214-9A14-D501C23F6FA4}.Release|Any CPU.Build.0 = Release|Any CPU + {DB6EF6F3-51B1-4214-9A14-D501C23F6FA4}.Release|x64.ActiveCfg = Release|Any CPU + {DB6EF6F3-51B1-4214-9A14-D501C23F6FA4}.Release|x64.Build.0 = Release|Any CPU + {DB6EF6F3-51B1-4214-9A14-D501C23F6FA4}.Release|x86.ActiveCfg = Release|Any CPU + {DB6EF6F3-51B1-4214-9A14-D501C23F6FA4}.Release|x86.Build.0 = Release|Any CPU + {6C57EF2C-979A-4106-A9E5-FE342810619A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {6C57EF2C-979A-4106-A9E5-FE342810619A}.Debug|Any CPU.Build.0 = Debug|Any CPU + {6C57EF2C-979A-4106-A9E5-FE342810619A}.Debug|x64.ActiveCfg = Debug|Any CPU + {6C57EF2C-979A-4106-A9E5-FE342810619A}.Debug|x64.Build.0 = Debug|Any CPU + {6C57EF2C-979A-4106-A9E5-FE342810619A}.Debug|x86.ActiveCfg = Debug|Any CPU + {6C57EF2C-979A-4106-A9E5-FE342810619A}.Debug|x86.Build.0 = Debug|Any CPU + {6C57EF2C-979A-4106-A9E5-FE342810619A}.Release|Any CPU.ActiveCfg = Release|Any CPU + {6C57EF2C-979A-4106-A9E5-FE342810619A}.Release|Any CPU.Build.0 = Release|Any CPU + {6C57EF2C-979A-4106-A9E5-FE342810619A}.Release|x64.ActiveCfg = Release|Any CPU + {6C57EF2C-979A-4106-A9E5-FE342810619A}.Release|x64.Build.0 = Release|Any CPU + {6C57EF2C-979A-4106-A9E5-FE342810619A}.Release|x86.ActiveCfg = Release|Any CPU + {6C57EF2C-979A-4106-A9E5-FE342810619A}.Release|x86.Build.0 = Release|Any CPU + {95BABD97-FBDB-453A-AF8A-FA031A07B599}.Debug|Any CPU.ActiveCfg = Debug|Win32 + {95BABD97-FBDB-453A-AF8A-FA031A07B599}.Debug|Any CPU.Build.0 = Debug|Win32 + {95BABD97-FBDB-453A-AF8A-FA031A07B599}.Debug|x64.ActiveCfg = Debug|Win32 + {95BABD97-FBDB-453A-AF8A-FA031A07B599}.Debug|x86.ActiveCfg = Debug|Win32 + {95BABD97-FBDB-453A-AF8A-FA031A07B599}.Debug|x86.Build.0 = Debug|Win32 + {95BABD97-FBDB-453A-AF8A-FA031A07B599}.Release|Any CPU.ActiveCfg = Release|Win32 + {95BABD97-FBDB-453A-AF8A-FA031A07B599}.Release|Any CPU.Build.0 = Release|Win32 + {95BABD97-FBDB-453A-AF8A-FA031A07B599}.Release|x64.ActiveCfg = Release|Win32 + {95BABD97-FBDB-453A-AF8A-FA031A07B599}.Release|x86.ActiveCfg = Release|Win32 + {95BABD97-FBDB-453A-AF8A-FA031A07B599}.Release|x86.Build.0 = Release|Win32 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {83E9E075-B440-471A-9C37-9D84BA0AE3E0} + EndGlobalSection +EndGlobal 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 @@ + + + True + True + + \ 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 @@ +@setlocal +@pushd %~dp0 + +nuget restore || exit /b + +dotnet pack -c Release src\WixBuildTools.MsgGen\WixBuildTools.MsgGen.csproj || exit /b +dotnet pack -c Release src\WixBuildTools.TestSupport\WixBuildTools.TestSupport.csproj || exit /b +dotnet pack -c Release src\WixBuildTools.XsdGen\WixBuildTools.XsdGen.csproj || exit /b + +msbuild -p:Configuration=Release -t:PackNativeNuget src\WixBuildTools.TestSupport.Native\WixBuildTools.TestSupport.Native.vcxproj || exit /b + +@popd +@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 @@ +# 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. +# +# Do NOT modify this file. Update the canonical version in Home\repo-template\src\appveyor.yml +# then update all of the repos. + +branches: + only: + - master + - develop + +image: Visual Studio 2019 + +version: 0.0.0.{build} +configuration: Release + +environment: + DOTNET_SKIP_FIRST_TIME_EXPERIENCE: true + DOTNET_CLI_TELEMETRY_OPTOUT: 1 + NUGET_XMLDOC_MODE: skip + +build_script: + - appveyor.cmd + +pull_requests: + do_not_increment_build_number: true + +nuget: + disable_publish_on_pr: true + +skip_branch_with_pr: true +skip_tags: true + +artifacts: +- path: build\Release\**\*.nupkg + name: nuget +- path: build\Release\**\*.msi + name: msi + +notifications: +- provider: Slack + incoming_webhook: + 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 @@ + + + + + + + + \ No newline at end of file diff --git a/src/version.json b/src/version.json new file mode 100644 index 00000000..5f857771 --- /dev/null +++ b/src/version.json @@ -0,0 +1,11 @@ +{ + "version": "4.0", + "publicReleaseRefSpec": [ + "^refs/heads/master$" + ], + "cloudBuild": { + "buildNumber": { + "enabled": true + } + } +} diff --git a/version.json b/version.json deleted file mode 100644 index 5f857771..00000000 --- a/version.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "version": "4.0", - "publicReleaseRefSpec": [ - "^refs/heads/master$" - ], - "cloudBuild": { - "buildNumber": { - "enabled": true - } - } -} -- cgit v1.2.3-55-g6feb