├── .config
└── dotnet-tools.json
├── .editorconfig
├── appveyor.yml
├── TODO.md
├── LICENSE
├── src
└── Serialize.OpenXml.CodeGen
│ ├── Serialize.OpenXml.CodeGen.csproj
│ ├── IOpenXmlHandler.cs
│ ├── TypeMonitorCollection.cs
│ ├── NamespaceAliasOrder.cs
│ ├── Extensions
│ ├── CodeStatementCollectionExtensions.cs
│ ├── StringExtensions.cs
│ └── TypeExtensions.cs
│ ├── UriEqualityComparer.cs
│ ├── OpenXmlPartBluePrintCollection.cs
│ ├── ISerializeSettings.cs
│ ├── IOpenXmlElementHandler.cs
│ ├── DefaultSerializeSettings.cs
│ ├── IOpenXmlPartHandler.cs
│ ├── OpenXmlPartBluePrint.cs
│ ├── NamespaceAliasOptions.cs
│ ├── CodeNamespaceImportComparer.cs
│ ├── TypeMonitor.cs
│ ├── OpenXmlPartContainerExtensions.cs
│ ├── OpenXmlPackageExtensions.cs
│ └── OpenXmlPartExtensions.cs
├── Serialize.OpenXml.CodeGen.sln
├── README.md
├── CHANGELOG.md
└── .gitignore
/.config/dotnet-tools.json:
--------------------------------------------------------------------------------
1 | {
2 | "version": 1,
3 | "isRoot": true,
4 | "tools": {
5 | "cake.tool": {
6 | "version": "1.3.0",
7 | "commands": [
8 | "dotnet-cake"
9 | ]
10 | }
11 | }
12 | }
--------------------------------------------------------------------------------
/.editorconfig:
--------------------------------------------------------------------------------
1 | # top-most EditorConfig file
2 | root = true
3 |
4 | [*]
5 | trim_trailing_whitespace = true
6 | indent_style = space
7 |
8 | [*.ps1]
9 | indent_size = 2
10 |
11 | [*.{cs,cake,csproj,sh}]
12 | indent_size = 4
13 |
--------------------------------------------------------------------------------
/appveyor.yml:
--------------------------------------------------------------------------------
1 | version: 0.5.0.{build}
2 | os: Visual Studio 2022
3 |
4 | # Only focus on the master branch
5 | branches:
6 | only:
7 | - master
8 | - main # If/when the master branch becomes 'main'
9 |
10 | install:
11 |
12 | build_script:
13 | - cmd: dotnet --info
14 | - cmd: dotnet tool restore
15 | - cmd: dotnet cake
16 |
17 | test: off
18 |
19 | artifacts:
20 | - path: artifacts/zip/*.zip
21 | - path: artifacts/nuget/*.nupkg
--------------------------------------------------------------------------------
/TODO.md:
--------------------------------------------------------------------------------
1 | TODO
2 | ====
3 |
4 | This is a simple list of current planned items to work on
5 |
6 | ## Version 1.0
7 |
8 | * Setup automated build process
9 | * ~~Cake~~
10 | * ~~Appveyor~~
11 | * ~~TravisCI (maybe)~~
12 | * ~~Create nuget package~~
13 | * ~~Create async versions of the CreateSourceCode extension methods~~
14 | * Add hooks system to allow users to create custom code generation by OpenXml type _(in progress)_
15 | * Setup Wiki
16 | * Add examples
17 | * Add test cases
18 | * Simplify code generation, if possible
19 |
20 | ## Version 2.0
21 |
22 | * Investigate using rosyln to generate source code
23 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2020 Ryan Boggs
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/src/Serialize.OpenXml.CodeGen/Serialize.OpenXml.CodeGen.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | $(MSBuildProjectDirectory)/../../CHANGELOG.md
5 |
6 |
7 | netstandard2.0
8 | 0.5.0-beta
9 | Ryan Boggs
10 | MIT
11 | https://github.com/Docx2Src/Serialize.OpenXml.CodeGen
12 | false
13 | https://github.com/Docx2Src/Serialize.OpenXml.CodeGens
14 | $([System.IO.File]::ReadAllText($(ChangeLogFile)))
15 | git
16 | openxml;codedom;DocumentFormat
17 | .NET assembly class responsible for converting OpenXml based documents into corrisponding dotnet code
18 | ../../artifacts/nuget
19 | true
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
--------------------------------------------------------------------------------
/src/Serialize.OpenXml.CodeGen/IOpenXmlHandler.cs:
--------------------------------------------------------------------------------
1 | /* MIT License
2 |
3 | Copyright (c) 2020 Ryan Boggs
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy of this
6 | software and associated documentation files (the "Software"), to deal in the Software
7 | without restriction, including without limitation the rights to use, copy, modify,
8 | merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
9 | permit persons to whom the Software is furnished to do so, subject to the following
10 | conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all copies
13 | or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
16 | INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
17 | PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
18 | FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
19 | OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
20 | DEALINGS IN THE SOFTWARE.
21 | */
22 |
23 | using System;
24 |
25 | namespace Serialize.OpenXml.CodeGen
26 | {
27 | ///
28 | /// Defines objects that provide custom code generation instructions for
29 | /// specific OpenXml SDK objects that the process may encounter.
30 | ///
31 | public interface IOpenXmlHandler
32 | {
33 | #region Properties
34 |
35 | ///
36 | /// The of object that the current object generates
37 | /// source code for.
38 | ///
39 | Type OpenXmlType { get; }
40 |
41 | #endregion
42 | }
43 | }
--------------------------------------------------------------------------------
/src/Serialize.OpenXml.CodeGen/TypeMonitorCollection.cs:
--------------------------------------------------------------------------------
1 | /* MIT License
2 |
3 | Copyright (c) 2021 Ryan Boggs
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy of this
6 | software and associated documentation files (the "Software"), to deal in the Software
7 | without restriction, including without limitation the rights to use, copy, modify,
8 | merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
9 | permit persons to whom the Software is furnished to do so, subject to the following
10 | conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all copies
13 | or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
16 | INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
17 | PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
18 | FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
19 | OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
20 | DEALINGS IN THE SOFTWARE.
21 | */
22 |
23 | using System;
24 |
25 | namespace Serialize.OpenXml.CodeGen
26 | {
27 | ///
28 | /// Simple collection to maintain the objects for
29 | /// the code generation of a given .
30 | ///
31 | internal class TypeMonitorCollection
32 | : System.Collections.ObjectModel.KeyedCollection
33 | {
34 | #region Protected Instance Methods
35 |
36 | ///
37 | protected override Type GetKeyForItem(TypeMonitor item) => item.Type;
38 |
39 | #endregion
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/Serialize.OpenXml.CodeGen.sln:
--------------------------------------------------------------------------------
1 |
2 | Microsoft Visual Studio Solution File, Format Version 12.00
3 | # Visual Studio 15
4 | VisualStudioVersion = 15.0.26124.0
5 | MinimumVisualStudioVersion = 15.0.26124.0
6 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Serialize.OpenXml.CodeGen", "src\Serialize.OpenXml.CodeGen\Serialize.OpenXml.CodeGen.csproj", "{1A80CC41-59B0-48DF-8E32-0E276841F769}"
7 | EndProject
8 | Global
9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution
10 | Debug|Any CPU = Debug|Any CPU
11 | Debug|x64 = Debug|x64
12 | Debug|x86 = Debug|x86
13 | Release|Any CPU = Release|Any CPU
14 | Release|x64 = Release|x64
15 | Release|x86 = Release|x86
16 | EndGlobalSection
17 | GlobalSection(ProjectConfigurationPlatforms) = postSolution
18 | {1A80CC41-59B0-48DF-8E32-0E276841F769}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
19 | {1A80CC41-59B0-48DF-8E32-0E276841F769}.Debug|Any CPU.Build.0 = Debug|Any CPU
20 | {1A80CC41-59B0-48DF-8E32-0E276841F769}.Debug|x64.ActiveCfg = Debug|Any CPU
21 | {1A80CC41-59B0-48DF-8E32-0E276841F769}.Debug|x64.Build.0 = Debug|Any CPU
22 | {1A80CC41-59B0-48DF-8E32-0E276841F769}.Debug|x86.ActiveCfg = Debug|Any CPU
23 | {1A80CC41-59B0-48DF-8E32-0E276841F769}.Debug|x86.Build.0 = Debug|Any CPU
24 | {1A80CC41-59B0-48DF-8E32-0E276841F769}.Release|Any CPU.ActiveCfg = Release|Any CPU
25 | {1A80CC41-59B0-48DF-8E32-0E276841F769}.Release|Any CPU.Build.0 = Release|Any CPU
26 | {1A80CC41-59B0-48DF-8E32-0E276841F769}.Release|x64.ActiveCfg = Release|Any CPU
27 | {1A80CC41-59B0-48DF-8E32-0E276841F769}.Release|x64.Build.0 = Release|Any CPU
28 | {1A80CC41-59B0-48DF-8E32-0E276841F769}.Release|x86.ActiveCfg = Release|Any CPU
29 | {1A80CC41-59B0-48DF-8E32-0E276841F769}.Release|x86.Build.0 = Release|Any CPU
30 | EndGlobalSection
31 | GlobalSection(SolutionProperties) = preSolution
32 | HideSolutionNode = FALSE
33 | EndGlobalSection
34 | GlobalSection(ExtensibilityGlobals) = postSolution
35 | SolutionGuid = {9AB1B35F-7F1E-42E8-81B4-DBCE1CEBF92F}
36 | EndGlobalSection
37 | EndGlobal
38 |
--------------------------------------------------------------------------------
/src/Serialize.OpenXml.CodeGen/NamespaceAliasOrder.cs:
--------------------------------------------------------------------------------
1 | /* MIT License
2 |
3 | Copyright (c) 2020 Ryan Boggs
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy of this
6 | software and associated documentation files (the "Software"), to deal in the Software
7 | without restriction, including without limitation the rights to use, copy, modify,
8 | merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
9 | permit persons to whom the Software is furnished to do so, subject to the following
10 | conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all copies
13 | or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
16 | INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
17 | PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
18 | FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
19 | OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
20 | DEALINGS IN THE SOFTWARE.
21 | */
22 |
23 | namespace Serialize.OpenXml.CodeGen
24 | {
25 | ///
26 | /// Identifies which order that the namespace alias assignment
27 | /// is performed for a given .NET language.
28 | ///
29 | public enum NamespaceAliasOrder : byte
30 | {
31 | ///
32 | /// Indicates that a given .NET language does not support
33 | /// namespace aliases.
34 | ///
35 | None = 0,
36 |
37 | ///
38 | /// Indicates that the namespace should be placed before
39 | /// the alias in the import/using statement.
40 | ///
41 | NamespaceFirst,
42 |
43 | ///
44 | /// /// Indicates that the alias should be placed before
45 | /// the namespace in the import/using statement.
46 | ///
47 | AliasFirst
48 | }
49 | }
--------------------------------------------------------------------------------
/src/Serialize.OpenXml.CodeGen/Extensions/CodeStatementCollectionExtensions.cs:
--------------------------------------------------------------------------------
1 | /* MIT License
2 |
3 | Copyright (c) 2020 Ryan Boggs
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy of this
6 | software and associated documentation files (the "Software"), to deal in the Software
7 | without restriction, including without limitation the rights to use, copy, modify,
8 | merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
9 | permit persons to whom the Software is furnished to do so, subject to the following
10 | conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all copies
13 | or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
16 | INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
17 | PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
18 | FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
19 | OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
20 | DEALINGS IN THE SOFTWARE.
21 | */
22 |
23 | using System;
24 | using System.CodeDom;
25 |
26 | namespace Serialize.OpenXml.CodeGen.Extentions
27 | {
28 | ///
29 | /// Static class containing extension methods for the class.
30 | ///
31 | public static class CodeStatementCollectionExtensions
32 | {
33 | #region Public Static Methods
34 |
35 | ///
36 | /// Adds a blank/empty code statement to an existing object.
37 | ///
38 | ///
39 | /// The existing object to add the blank line statement to.
40 | ///
41 | public static void AddBlankLine(this CodeStatementCollection statements) =>
42 | statements.Add(new CodeSnippetStatement(String.Empty));
43 |
44 | #endregion
45 | }
46 | }
--------------------------------------------------------------------------------
/src/Serialize.OpenXml.CodeGen/UriEqualityComparer.cs:
--------------------------------------------------------------------------------
1 | /* MIT License
2 |
3 | Copyright (c) 2020 Ryan Boggs
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy of this
6 | software and associated documentation files (the "Software"), to deal in the Software
7 | without restriction, including without limitation the rights to use, copy, modify,
8 | merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
9 | permit persons to whom the Software is furnished to do so, subject to the following
10 | conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all copies
13 | or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
16 | INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
17 | PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
18 | FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
19 | OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
20 | DEALINGS IN THE SOFTWARE.
21 | */
22 |
23 | using System;
24 | using System.Collections.Generic;
25 | using DocumentFormat.OpenXml.Packaging;
26 |
27 | namespace Serialize.OpenXml.CodeGen
28 | {
29 | ///
30 | /// Equality comparer class used to ensure that circular part references are avoided when
31 | /// trying to build source code for objects.
32 | ///
33 | public sealed class UriEqualityComparer : EqualityComparer
34 | {
35 | #region Public Static Fields
36 |
37 | ///
38 | /// A default to use when comparing 2
39 | /// objects.
40 | ///
41 | public static readonly UriEqualityComparer DefaultComparer = new UriEqualityComparer();
42 |
43 | #endregion
44 |
45 | #region Public Instance Methods
46 |
47 | ///
48 | public override bool Equals(Uri x, Uri y)
49 | {
50 | if (x == null || y == null) return false;
51 | return x == y;
52 | }
53 |
54 | ///
55 | public override int GetHashCode(Uri obj)
56 | {
57 | return obj.ToString().GetHashCode();
58 | }
59 |
60 | #endregion
61 | }
62 | }
--------------------------------------------------------------------------------
/src/Serialize.OpenXml.CodeGen/OpenXmlPartBluePrintCollection.cs:
--------------------------------------------------------------------------------
1 | /* MIT License
2 |
3 | Copyright (c) 2020 Ryan Boggs
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy of this
6 | software and associated documentation files (the "Software"), to deal in the Software
7 | without restriction, including without limitation the rights to use, copy, modify,
8 | merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
9 | permit persons to whom the Software is furnished to do so, subject to the following
10 | conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all copies
13 | or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
16 | INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
17 | PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
18 | FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
19 | OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
20 | DEALINGS IN THE SOFTWARE.
21 | */
22 |
23 | using System;
24 | using System.Collections.ObjectModel;
25 |
26 | namespace Serialize.OpenXml.CodeGen
27 | {
28 | ///
29 | /// Collection class used to organize all of the objects
30 | /// for a single request.
31 | ///
32 | public sealed class OpenXmlPartBluePrintCollection : KeyedCollection
33 | {
34 | #region Internal Constructors
35 |
36 | ///
37 | /// Initializes a new instance of the class
38 | /// that is empty.
39 | ///
40 | internal OpenXmlPartBluePrintCollection() : base(new UriEqualityComparer()) { }
41 |
42 | #endregion
43 |
44 | #region Public Instance Methods
45 |
46 | ///
47 | /// Gets the object associated with the specified
48 | /// .
49 | ///
50 | ///
51 | /// The of the object to get.
52 | ///
53 | ///
54 | /// When the method returns, contains the object
55 | /// associated with , if found; otherwise, the default value
56 | /// of is returned ().
57 | /// This parameter is passed uninitialized.
58 | ///
59 | ///
60 | /// if collection contains an
61 | /// object with the specified ; otherwise, .
62 | ///
63 | public bool TryGetValue(Uri uri, out OpenXmlPartBluePrint bp)
64 | {
65 | if (uri == null || !Contains(uri))
66 | {
67 | bp = null;
68 | return false;
69 | }
70 | bp = this[uri];
71 | return true;
72 | }
73 |
74 | #endregion
75 |
76 | #region Protected Instance Methods
77 |
78 | ///
79 | protected override Uri GetKeyForItem(OpenXmlPartBluePrint item) => item.Uri;
80 |
81 | #endregion
82 | }
83 | }
--------------------------------------------------------------------------------
/src/Serialize.OpenXml.CodeGen/ISerializeSettings.cs:
--------------------------------------------------------------------------------
1 | /* MIT License
2 |
3 | Copyright (c) 2020 Ryan Boggs
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy of this
6 | software and associated documentation files (the "Software"), to deal in the Software
7 | without restriction, including without limitation the rights to use, copy, modify,
8 | merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
9 | permit persons to whom the Software is furnished to do so, subject to the following
10 | conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all copies
13 | or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
16 | INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
17 | PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
18 | FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
19 | OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
20 | DEALINGS IN THE SOFTWARE.
21 | */
22 |
23 | using System;
24 | using System.Collections.Generic;
25 | using System.Xml;
26 | using DocumentFormat.OpenXml;
27 |
28 | namespace Serialize.OpenXml.CodeGen
29 | {
30 | ///
31 | /// Defines objects containing settings to use when generating
32 | /// source code for OpenXml SDK based files.
33 | ///
34 | public interface ISerializeSettings
35 | {
36 | #region Properties
37 |
38 | ///
39 | /// Gets the lookup collection of objects to use
40 | /// for custom code generations for specific OpenXml SDK objects.
41 | ///
42 | ///
43 | /// If this collection is or if the current OpenXml SDK
44 | /// object type being analyzed does not exist, then the default code generation
45 | /// process will be used.
46 | ///
47 | IReadOnlyDictionary Handlers { get; }
48 |
49 | ///
50 | /// Gets all of the setting values of the
51 | /// objects to ignore.
52 | ///
53 | ///
54 | /// If this collection is or if the
55 | /// value of a object does not exist in this collection,
56 | /// then the object will be processed as a normal
57 | /// object.
58 | ///
59 | IReadOnlyList IgnoreMiscNodeTypes { get; }
60 |
61 | ///
62 | /// Indicates whether or not to ignore
63 | /// objects when generating source code.
64 | ///
65 | bool IgnoreUnknownElements { get; }
66 |
67 | ///
68 | /// Gets the object to use during the code
69 | /// generation process.
70 | ///
71 | NamespaceAliasOptions NamespaceAliasOptions { get; }
72 |
73 | ///
74 | /// Gets the name of the namespace to use for the generated code.
75 | ///
76 | string NamespaceName { get; }
77 |
78 | ///
79 | /// Indicates whether or not to generate unique variable names for each
80 | /// object being serialized.
81 | ///
82 | bool UseUniqueVariableNames { get; }
83 |
84 | #endregion
85 | }
86 | }
--------------------------------------------------------------------------------
/src/Serialize.OpenXml.CodeGen/IOpenXmlElementHandler.cs:
--------------------------------------------------------------------------------
1 | /* MIT License
2 |
3 | Copyright (c) 2020 Ryan Boggs
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy of this
6 | software and associated documentation files (the "Software"), to deal in the Software
7 | without restriction, including without limitation the rights to use, copy, modify,
8 | merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
9 | permit persons to whom the Software is furnished to do so, subject to the following
10 | conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all copies
13 | or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
16 | INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
17 | PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
18 | FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
19 | OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
20 | DEALINGS IN THE SOFTWARE.
21 | */
22 |
23 | using System;
24 | using System.CodeDom;
25 | using System.Collections.Generic;
26 | using System.Collections.ObjectModel;
27 | using System.Threading;
28 | using DocumentFormat.OpenXml;
29 |
30 | namespace Serialize.OpenXml.CodeGen
31 | {
32 | ///
33 | /// Defines objects that provide custom code generation instructions for
34 | /// derived objects that the process may
35 | /// encounter.
36 | ///
37 | public interface IOpenXmlElementHandler : IOpenXmlHandler
38 | {
39 | #region Methods
40 |
41 | ///
42 | /// Builds custom code objects that would build the contents of
43 | /// .
44 | ///
45 | ///
46 | /// The object to codify.
47 | ///
48 | ///
49 | /// The to use during the code generation
50 | /// process.
51 | ///
52 | ///
53 | /// A lookup containing the
54 | /// available elements to use for variable naming
55 | /// purposes.
56 | ///
57 | ///
58 | /// Collection used to keep track of all
59 | /// openxml namespaces used during the process.
60 | ///
61 | ///
62 | /// Task cancellation token from the parent method.
63 | ///
64 | ///
65 | /// The variable name of the root object that was built
66 | /// from the .
67 | ///
68 | ///
69 | /// A collection of code statements and expressions that could be used to generate
70 | /// a new object from code.
71 | ///
72 | ///
73 | /// If this method returns , the default implementation will
74 | /// be used instead.
75 | ///
76 | CodeStatementCollection BuildCodeStatements(
77 | OpenXmlElement element,
78 | ISerializeSettings settings,
79 | KeyedCollection types,
80 | IDictionary namespaces,
81 | CancellationToken token,
82 | out string elementName);
83 |
84 | #endregion
85 | }
86 | }
--------------------------------------------------------------------------------
/src/Serialize.OpenXml.CodeGen/DefaultSerializeSettings.cs:
--------------------------------------------------------------------------------
1 | /* MIT License
2 |
3 | Copyright (c) 2020 Ryan Boggs
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy of this
6 | software and associated documentation files (the "Software"), to deal in the Software
7 | without restriction, including without limitation the rights to use, copy, modify,
8 | merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
9 | permit persons to whom the Software is furnished to do so, subject to the following
10 | conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all copies
13 | or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
16 | INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
17 | PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
18 | FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
19 | OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
20 | DEALINGS IN THE SOFTWARE.
21 | */
22 |
23 | using System;
24 | using System.Collections.Generic;
25 | using System.Xml;
26 | using System.Threading.Tasks;
27 |
28 | namespace Serialize.OpenXml.CodeGen
29 | {
30 | ///
31 | /// A default implementation of the interface
32 | /// to use when custom object is not provided.
33 | ///
34 | internal sealed class DefaultSerializeSettings : ISerializeSettings
35 | {
36 | #region Internal Static Fields
37 |
38 | ///
39 | /// object used to call the async methods in
40 | /// a synchronous fashion.
41 | ///
42 | ///
43 | /// This is being used in order to avoid big breaking changes.
44 | ///
45 | internal static readonly TaskFactory TaskIndustry = new TaskFactory(
46 | System.Threading.CancellationToken.None,
47 | TaskCreationOptions.None,
48 | TaskContinuationOptions.None,
49 | TaskScheduler.Default);
50 |
51 | #endregion
52 |
53 | #region Public Constructors
54 |
55 | ///
56 | /// Initializes a new instance of the
57 | /// class that is empty.
58 | ///
59 | public DefaultSerializeSettings() : this(NamespaceAliasOptions.Default) {}
60 |
61 | ///
62 | /// Initializes a new instance of the
63 | /// class with the requested object.
64 | ///
65 | ///
66 | public DefaultSerializeSettings(NamespaceAliasOptions nsAliasOpts)
67 | {
68 | NamespaceAliasOptions = nsAliasOpts
69 | ?? throw new ArgumentNullException(nameof(nsAliasOpts));
70 | }
71 |
72 | #endregion
73 |
74 | #region Public Instance Properties
75 |
76 | ///
77 | public IReadOnlyDictionary Handlers => null;
78 |
79 | ///
80 | public IReadOnlyList IgnoreMiscNodeTypes => null;
81 |
82 | ///
83 | public bool IgnoreUnknownElements => false;
84 |
85 | ///
86 | public NamespaceAliasOptions NamespaceAliasOptions
87 | {
88 | get;
89 | private set;
90 | }
91 |
92 | ///
93 | public string NamespaceName => "OpenXmlSample";
94 |
95 | ///
96 | public bool UseUniqueVariableNames => false;
97 |
98 | #endregion
99 | }
100 | }
--------------------------------------------------------------------------------
/src/Serialize.OpenXml.CodeGen/IOpenXmlPartHandler.cs:
--------------------------------------------------------------------------------
1 | /* MIT License
2 |
3 | Copyright (c) 2020 Ryan Boggs
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy of this
6 | software and associated documentation files (the "Software"), to deal in the Software
7 | without restriction, including without limitation the rights to use, copy, modify,
8 | merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
9 | permit persons to whom the Software is furnished to do so, subject to the following
10 | conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all copies
13 | or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
16 | INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
17 | PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
18 | FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
19 | OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
20 | DEALINGS IN THE SOFTWARE.
21 | */
22 |
23 | using System;
24 | using System.CodeDom;
25 | using System.Collections.Generic;
26 | using System.Threading;
27 | using DocumentFormat.OpenXml.Packaging;
28 |
29 | namespace Serialize.OpenXml.CodeGen
30 | {
31 | ///
32 | /// Defines objects that provide custom code generation instructions for
33 | /// derived objects that the process may encounter.
34 | ///
35 | public interface IOpenXmlPartHandler : IOpenXmlHandler
36 | {
37 | #region Methods
38 |
39 | ///
40 | /// Creates the appropriate code objects needed to create the entry method for the
41 | /// current request.
42 | ///
43 | ///
44 | /// The object and relationship id to build code for.
45 | ///
46 | ///
47 | /// The to use during the code generation
48 | /// process.
49 | ///
50 | /// ///
51 | /// A lookup object containing the
52 | /// number of times a given type was referenced. This is used for variable naming
53 | /// purposes.
54 | ///
55 | ///
56 | /// Collection used to keep track of all
57 | /// openxml namespaces used during the process.
58 | ///
59 | ///
60 | /// The collection of objects that have already been
61 | /// visited.
62 | ///
63 | ///
64 | /// The root variable name and to use when building code
65 | /// statements to create new objects.
66 | ///
67 | ///
68 | /// Task cancellation token from the parent method.
69 | ///
70 | ///
71 | /// A collection of code statements and expressions that could be used to generate
72 | /// a new object from code.
73 | ///
74 | ///
75 | /// If this method returns , the default implementation will
76 | /// be used instead.
77 | ///
78 | CodeStatementCollection BuildEntryMethodCodeStatements(
79 | IdPartPair part,
80 | ISerializeSettings settings,
81 | IDictionary typeCounts,
82 | IDictionary namespaces,
83 | OpenXmlPartBluePrintCollection blueprints,
84 | KeyValuePair rootVar,
85 | CancellationToken token);
86 |
87 | ///
88 | /// Builds the helper method of a given .
89 | ///
90 | ///
91 | /// The object to generate the source code for.
92 | ///
93 | ///
94 | /// The name of the method to use when building the
95 | ///
96 | ///
97 | /// The to use during the code generation
98 | /// process.
99 | ///
100 | ///
101 | /// Collection used to keep track of all
102 | /// openxml namespaces used during the process.
103 | ///
104 | ///
105 | /// Task cancellation token from the parent method.
106 | ///
107 | ///
108 | /// A new object containing the necessary source code
109 | /// to recreate .
110 | ///
111 | ///
112 | /// If this method returns , the default implementation will
113 | /// be used instead.
114 | ///
115 | CodeMemberMethod BuildHelperMethod(
116 | OpenXmlPart part,
117 | string methodName,
118 | ISerializeSettings settings,
119 | IDictionary namespaces,
120 | CancellationToken token);
121 |
122 | #endregion
123 | }
124 | }
125 |
--------------------------------------------------------------------------------
/src/Serialize.OpenXml.CodeGen/OpenXmlPartBluePrint.cs:
--------------------------------------------------------------------------------
1 | /* MIT License
2 |
3 | Copyright (c) 2020 Ryan Boggs
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy of this
6 | software and associated documentation files (the "Software"), to deal in the Software
7 | without restriction, including without limitation the rights to use, copy, modify,
8 | merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
9 | permit persons to whom the Software is furnished to do so, subject to the following
10 | conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all copies
13 | or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
16 | INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
17 | PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
18 | FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
19 | OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
20 | DEALINGS IN THE SOFTWARE.
21 | */
22 |
23 | using System;
24 | using System.Globalization;
25 | using DocumentFormat.OpenXml.Packaging;
26 |
27 | namespace Serialize.OpenXml.CodeGen
28 | {
29 | ///
30 | /// Simple organization class used to keep track of all the OpenXmlPart details needed
31 | /// to complete a code generation request.
32 | ///
33 | public sealed class OpenXmlPartBluePrint : IEquatable
34 | {
35 | #region Public Constructrs
36 |
37 | ///
38 | /// Initializes a new instance of the class
39 | /// with the required object and instance variable name.
40 | ///
41 | ///
42 | /// The object.
43 | ///
44 | ///
45 | /// Variable name that was initialized with in the
46 | /// generated code.
47 | ///
48 | ///
49 | /// is or
50 | /// is or blank.
51 | ///
52 | public OpenXmlPartBluePrint(OpenXmlPart part, string varName)
53 | {
54 | Part = part ?? throw new ArgumentNullException(nameof(part));
55 | VariableName = varName ?? throw new ArgumentNullException(nameof(varName));
56 | MethodName = this.CreateMethodName(VariableName);
57 | PartType = Part.GetType();
58 | }
59 |
60 | #endregion
61 |
62 | #region Public Instance Properties
63 |
64 | ///
65 | /// Gets the name to use when generating the code responsible for creating
66 | /// .
67 | ///
68 | public string MethodName { get; private set;}
69 |
70 | ///
71 | /// Gets the object that this instance represents.
72 | ///
73 | public OpenXmlPart Part { get; private set; }
74 |
75 | ///
76 | /// Gets the of that this instance represents.
77 | ///
78 | public Type PartType { get; private set; }
79 |
80 | ///
81 | /// Gets the for this object.
82 | ///
83 | public Uri Uri => Part?.Uri;
84 |
85 | ///
86 | /// Gets the variable name that was initialized with.
87 | ///
88 | public string VariableName { get; private set; }
89 |
90 | #endregion
91 |
92 | #region Public Instance Methods
93 |
94 | ///
95 | public override bool Equals(object obj)
96 | {
97 | if (obj == null) return false;
98 | if (!(obj is OpenXmlPartBluePrint)) return false;
99 | if (Object.ReferenceEquals((object)this, obj)) return true;
100 | return Equals(obj as OpenXmlPartBluePrint);
101 | }
102 |
103 | ///
104 | public bool Equals(OpenXmlPartBluePrint other)
105 | {
106 | if (other == null) return false;
107 | return UriEqualityComparer.DefaultComparer.Equals(Uri, other.Uri);
108 | }
109 |
110 | ///
111 | public override int GetHashCode()
112 | {
113 | if (Uri == null) return 0;
114 | return Uri.GetHashCode();
115 | }
116 |
117 | #endregion
118 |
119 | #region Private Instance Methods
120 |
121 | ///
122 | /// Create a method name for the current instance.
123 | ///
124 | ///
125 | /// The variable name that this instance was initialized with.
126 | ///
127 | ///
128 | /// A method name.
129 | ///
130 | private string CreateMethodName(string varName)
131 | {
132 | CultureInfo ci = CultureInfo.CurrentCulture;
133 | char[] chars = new char[varName.Length];
134 |
135 | for (int i = 0; i < varName.Length; i++)
136 | {
137 | if (i == 0)
138 | {
139 | chars[i] = !Char.IsUpper(varName[i])
140 | ? Char.ToUpper(varName[i], ci)
141 | : varName[i];
142 | continue;
143 | }
144 | chars[i] = varName[i];
145 | }
146 | return $"Generate{new string(chars)}";
147 | }
148 |
149 | #endregion
150 | }
151 | }
--------------------------------------------------------------------------------
/src/Serialize.OpenXml.CodeGen/Extensions/StringExtensions.cs:
--------------------------------------------------------------------------------
1 | /* MIT License
2 |
3 | Copyright (c) 2020 Ryan Boggs
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy of this
6 | software and associated documentation files (the "Software"), to deal in the Software
7 | without restriction, including without limitation the rights to use, copy, modify,
8 | merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
9 | permit persons to whom the Software is furnished to do so, subject to the following
10 | conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all copies
13 | or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
16 | INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
17 | PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
18 | FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
19 | OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
20 | DEALINGS IN THE SOFTWARE.
21 | */
22 |
23 | using System;
24 | using System.CodeDom;
25 | using System.Globalization;
26 | using System.IO;
27 | using System.Linq;
28 | using System.Text;
29 | using System.Text.RegularExpressions;
30 |
31 | namespace Serialize.OpenXml.CodeGen.Extentions
32 | {
33 | ///
34 | /// Static class containing extension methods for the class.
35 | ///
36 | public static class StringExtensions
37 | {
38 | #region Public Static Methods
39 |
40 | ///
41 | /// Strips out the standard header text that is included when a
42 | ///
43 | /// derived class
44 | /// generates dotnet code.
45 | ///
46 | ///
47 | /// The raw code produced by the
48 | ///
49 | /// derived class.
50 | ///
51 | ///
52 | /// A new code value with the default headers removed.
53 | ///
54 | public static string RemoveOutputHeaders(this string raw)
55 | {
56 | var indicator = new string('-', 78);
57 | var sb = new StringBuilder();
58 | bool inHeader = false;
59 |
60 | using (var sr = new StringReader(raw))
61 | {
62 | string currentLine = sr.ReadLine();
63 | while (currentLine != null)
64 | {
65 | if (currentLine.EndsWith(indicator))
66 | {
67 | if (inHeader) currentLine = sr.ReadLine();
68 | inHeader = !inHeader;
69 | }
70 | if (Regex.IsMatch(currentLine, "\".*\\\'.*\""))
71 | {
72 | currentLine = currentLine.Replace("\\'", "'");
73 | }
74 | if (!inHeader) sb.AppendLine(currentLine);
75 | currentLine = sr.ReadLine();
76 | }
77 | }
78 | return sb.ToString();
79 | }
80 |
81 | ///
82 | /// Retrieves only the upper case characters from a given string.
83 | ///
84 | /// The to analyze.
85 | ///
86 | /// The upper case characters from .
87 | ///
88 | public static string RetrieveUpperCaseChars(this string s)
89 | {
90 | var cs = s.Where(c => Char.IsUpper(c)).ToArray();
91 | return new string(cs);
92 | }
93 |
94 | ///
95 | /// Ensures that the first letter of a given string is lowercase.
96 | ///
97 | ///
98 | /// The to manipulate.
99 | ///
100 | ///
101 | /// The same value as with the first character converted
102 | /// to lowercase.
103 | ///
104 | public static string ToCamelCase(this string s)
105 | {
106 | if (String.IsNullOrWhiteSpace(s)) return s;
107 | var sArray = s.ToCharArray();
108 |
109 | sArray[0] = Char.ToLowerInvariant(sArray[0]);
110 | return new string(sArray);
111 | }
112 |
113 | ///
114 | /// Ensures that the only the first letter of is
115 | /// capitalized.
116 | ///
117 | ///
118 | /// The to capitalize.
119 | ///
120 | ///
121 | /// A new with the value of that has only
122 | /// the first letter capitalized.
123 | ///
124 | public static string ToTitleCase(this string s)
125 | {
126 | CultureInfo ci = CultureInfo.CurrentCulture;
127 | char[] chars = new char[s.Length];
128 |
129 | for (int i = 0; i < s.Length; i++)
130 | {
131 | if (i == 0)
132 | {
133 | chars[i] = !Char.IsUpper(s[i])
134 | ? Char.ToUpper(s[i], ci)
135 | : s[i];
136 | continue;
137 | }
138 | chars[i] = !Char.IsWhiteSpace(s[i]) && !Char.IsLower(s[i])
139 | ? Char.ToLower(s[i], ci)
140 | : s[i];
141 | }
142 | return new string(chars);
143 | }
144 |
145 | #endregion
146 | }
147 | }
--------------------------------------------------------------------------------
/src/Serialize.OpenXml.CodeGen/NamespaceAliasOptions.cs:
--------------------------------------------------------------------------------
1 | /* MIT License
2 |
3 | Copyright (c) 2020 Ryan Boggs
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy of this
6 | software and associated documentation files (the "Software"), to deal in the Software
7 | without restriction, including without limitation the rights to use, copy, modify,
8 | merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
9 | permit persons to whom the Software is furnished to do so, subject to the following
10 | conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all copies
13 | or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
16 | INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
17 | PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
18 | FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
19 | OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
20 | DEALINGS IN THE SOFTWARE.
21 | */
22 |
23 | using System;
24 | using System.CodeDom;
25 |
26 | namespace Serialize.OpenXml.CodeGen
27 | {
28 | ///
29 | /// Class used to organize namespace import information
30 | /// during the OpenXml document reflection process.
31 | ///
32 | public class NamespaceAliasOptions
33 | {
34 | #region Public Static Fields
35 |
36 | ///
37 | /// Gets a object to use
38 | /// with .NET languages that do not support namespace aliasing.
39 | ///
40 | public static readonly NamespaceAliasOptions Empty
41 | = new NamespaceAliasOptions();
42 |
43 | ///
44 | /// Gets a object to use
45 | /// with the first class Microsoft .NET languages, C# and
46 | /// VB.NET.
47 | ///
48 | public static readonly NamespaceAliasOptions Default
49 | = new NamespaceAliasOptions()
50 | {
51 | Order = NamespaceAliasOrder.AliasFirst,
52 | AssignmentOperator = "="
53 | };
54 |
55 | #endregion
56 |
57 | #region Public Constructors
58 |
59 | ///
60 | /// Initializes a new instance of the
61 | /// class that is empty.
62 | ///
63 | public NamespaceAliasOptions()
64 | {
65 | AssignmentOperator = String.Empty;
66 | Order = NamespaceAliasOrder.None;
67 | }
68 |
69 | #endregion
70 |
71 | #region Public Instance Properties
72 |
73 | ///
74 | /// Gets or sets the text used to assign an alias to a namespace.
75 | ///
76 | public string AssignmentOperator
77 | {
78 | get;
79 | set;
80 | }
81 |
82 | ///
83 | /// Indicates which order the namespace alias assignment is
84 | /// specified.
85 | ///
86 | public NamespaceAliasOrder Order
87 | {
88 | get;
89 | set;
90 | }
91 |
92 | #endregion
93 |
94 | #region Public Instance Methods
95 |
96 | ///
97 | /// Builds a new object based
98 | /// on the settings of the current object.
99 | ///
100 | ///
101 | /// The namespace to build the new
102 | /// object.
103 | ///
104 | ///
105 | /// The alias name to use when building the new
106 | /// object.
107 | ///
108 | ///
109 | /// A new object.
110 | ///
111 | public virtual CodeNamespaceImport BuildNamespaceImport(string ns, string alias)
112 | {
113 | if (String.IsNullOrWhiteSpace(alias) ||
114 | Order == NamespaceAliasOrder.None)
115 | return new CodeNamespaceImport(ns);
116 |
117 | const string assignment = "{0} {1} {2}";
118 | string name = String.Empty;
119 |
120 | switch (Order)
121 | {
122 | case NamespaceAliasOrder.NamespaceFirst:
123 | name = String.Format(assignment,
124 | ns.Trim(),
125 | AssignmentOperator.Trim(),
126 | alias.Trim());
127 | break;
128 | case NamespaceAliasOrder.AliasFirst:
129 | name = String.Format(assignment,
130 | alias.Trim(),
131 | AssignmentOperator.Trim(),
132 | ns.Trim());
133 | break;
134 | }
135 | return new CodeNamespaceImport(name);
136 | }
137 |
138 | ///
139 | /// Builds a new object based
140 | /// on the settings of the current object.
141 | ///
142 | ///
143 | /// The object containing the namespace to build
144 | /// the new object.
145 | ///
146 | ///
147 | /// The alias name to use when building the new
148 | /// object.
149 | ///
150 | ///
151 | /// A new object.
152 | ///
153 | public CodeNamespaceImport BuildNamespaceImport(Type ns, string alias)
154 | {
155 | return BuildNamespaceImport(ns.Namespace, alias);
156 | }
157 |
158 | #endregion
159 | }
160 | }
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | Open XML Document Code Generator
2 | ================================
3 |
4 | 
5 |
6 | [](https://ci.appveyor.com/project/rmboggs/serialize-openxml-codegen/branch/master) 
7 |
8 | The Open XML Document Code Generator is a dotnet standard library that contains processes that convert OpenXML documents (such as .docx, .pptx, and .xlsx) into [CodeCompileUnit](https://docs.microsoft.com/en-us/dotnet/api/system.codedom.codecompileunit?view=netcore-3.1) objects that can be transformed into source code using any class that inherits from the [CodeDomProvider](https://docs.microsoft.com/en-us/dotnet/api/system.codedom.compiler.codedomprovider?view=netcore-3.1) class. This allows source code generation into other .NET languages, such as Visual Basic.net, for greater learning possibilities.
9 |
10 | Please be aware that while this project is producing working code, I would consider this is in beta status and not yet ready for production. More testing should be done before it will be production ready.
11 |
12 | As of version 0.4.0-beta, this project is now generating code that can reproduce entire basic OpenXml documents. More testing is required of advanced document types before this project can move beyond beta status.
13 |
14 | Looking for a front end interface for this project? Check out [DocxToSource](https://github.com/rmboggs/DocxToSource). There is still much work to be done but it is becoming a nice replacement for the old OpenXml SDK Productivity Tool.
15 |
16 | ## Examples
17 |
18 | Generate the [CodeCompileUnit](https://docs.microsoft.com/en-us/dotnet/api/system.codedom.codecompileunit?view=netcore-3.1) to process manually:
19 |
20 | ```cs
21 | using System;
22 | using System.IO;
23 | using System.Linq;
24 | using System.Text;
25 | using Serialize.OpenXml.CodeGen;
26 | using System.CodeDom.Compiler;
27 | using Microsoft.CSharp;
28 | using DocumentFormat.OpenXml;
29 | using DocumentFormat.OpenXml.Packaging;
30 |
31 | namespace CodeGenSample
32 | {
33 | class Program
34 | {
35 | private static readonly CodeGeneratorOptions Cgo = new CodeGeneratorOptions()
36 | {
37 | BracingStyle = "C"
38 | };
39 |
40 | static void Main(string[] args)
41 | {
42 | var sourceFile = new FileInfo(@"C:\Temp\Sample1.xlsx");
43 | var targetFile = new FileInfo(@"C:\Temp\Sample1.cs");
44 |
45 | using (var source = sourceFile.Open(FileMode.Open, FileAccess.Read, FileShare.Read))
46 | {
47 | using (var xlsx = SpreadsheetDocument.Open(source, false))
48 | {
49 | if (xlsx != null)
50 | {
51 | var codeString = new StringBuilder();
52 | var cs = new CSharpCodeProvider();
53 |
54 | // This will build the CodeCompileUnit object containing all of
55 | // the commands that would create the source code to rebuild Sample1.xlsx
56 | var code = xlsx.GenerateSourceCode();
57 |
58 | // This will convert the CodeCompileUnit into C# source code
59 | using (var sw = new StringWriter(codeString))
60 | {
61 | cs.GenerateCodeFromCompileUnit(code, sw, Cgo);
62 | }
63 |
64 | // Save the source code to the target file
65 | using (var target = targetFile.Open(FileMode.Create, FileAccess.ReadWrite))
66 | {
67 | using (var tw = new StreamWriter(target))
68 | {
69 | tw.Write(codeString.ToString().Trim());
70 | }
71 | target.Close();
72 | }
73 | }
74 | }
75 | source.Close();
76 | }
77 | Console.WriteLine("Press any key to quit");
78 | Console.ReadKey(true);
79 | }
80 | }
81 | }
82 | ```
83 |
84 | Generate the actual source code as a string value:
85 |
86 | ```cs
87 | using System;
88 | using System.IO;
89 | using System.Linq;
90 | using System.Text;
91 | using Serialize.OpenXml.CodeGen;
92 | using System.CodeDom.Compiler;
93 | using Microsoft.VisualBasic;
94 | using DocumentFormat.OpenXml;
95 | using DocumentFormat.OpenXml.Packaging;
96 |
97 | namespace CodeGenSample
98 | {
99 | class Program
100 | {
101 | static void Main(string[] args)
102 | {
103 | var sourceFile = new FileInfo(@"./Sample1.xlsx");
104 | var targetFile = new FileInfo(@"./Sample1.vb");
105 |
106 | using (var source = sourceFile.Open(FileMode.Open, FileAccess.Read, FileShare.Read))
107 | {
108 | using (var xlsx = SpreadsheetDocument.Open(source, false))
109 | {
110 | if (xlsx != null)
111 | {
112 | // Generate VB.NET source code
113 | var vb = new VBCodeProvider();
114 |
115 | // Save the source code to the target file
116 | using (var target = targetFile.Open(FileMode.Create, FileAccess.ReadWrite))
117 | {
118 | using (var tw = new StreamWriter(target))
119 | {
120 | // Providing the CodeDomProvider object as a parameter will
121 | // cause the method to return the source code as a string
122 | tw.Write(xlsx.GenerateSourceCode(vb).Trim());
123 | }
124 | target.Close();
125 | }
126 | }
127 | }
128 | source.Close();
129 | }
130 | Console.WriteLine("Press any key to quit");
131 | Console.ReadKey(true);
132 | }
133 | }
134 | }
135 | ```
136 |
--------------------------------------------------------------------------------
/src/Serialize.OpenXml.CodeGen/CodeNamespaceImportComparer.cs:
--------------------------------------------------------------------------------
1 | /* MIT License
2 |
3 | Copyright (c) 2020 Ryan Boggs
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy of this
6 | software and associated documentation files (the "Software"), to deal in the Software
7 | without restriction, including without limitation the rights to use, copy, modify,
8 | merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
9 | permit persons to whom the Software is furnished to do so, subject to the following
10 | conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all copies
13 | or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
16 | INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
17 | PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
18 | FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
19 | OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
20 | DEALINGS IN THE SOFTWARE.
21 | */
22 |
23 | using System;
24 | using System.Collections.Generic;
25 | using System.CodeDom;
26 |
27 | namespace Serialize.OpenXml.CodeGen
28 | {
29 | ///
30 | /// Comparer based class used to sort objects.
31 | ///
32 | public class CodeNamespaceImportComparer
33 | : Comparer,
34 | IEqualityComparer
35 | {
36 | #region Private Static Fields
37 |
38 | ///
39 | /// Holds the comparer object to use for all string comparison operations.
40 | ///
41 | private static readonly StringComparer _cmpr = StringComparer.Ordinal;
42 |
43 | #endregion
44 |
45 | #region Private Instance Fields
46 |
47 | ///
48 | /// Holds the object to use when comparing.
49 | ///
50 | private readonly NamespaceAliasOptions _opts;
51 |
52 | #endregion
53 |
54 | #region Public Constructors
55 |
56 | ///
57 | /// Initializes a new instance of the
58 | /// class with the object of the current
59 | /// request.
60 | ///
61 | ///
62 | /// The object to use when comparing.
63 | ///
64 | public CodeNamespaceImportComparer(NamespaceAliasOptions options)
65 | {
66 | _opts = options ?? throw new ArgumentNullException(nameof(options));
67 | }
68 |
69 | #endregion
70 |
71 | #region Public Instance Methods
72 |
73 | ///
74 | public override int Compare(CodeNamespaceImport x, CodeNamespaceImport y)
75 | {
76 | // Check to make sure that either both CodeNamespaceImport objects
77 | // have the assignment operator or both do not have one.
78 | if (!String.IsNullOrWhiteSpace(_opts.AssignmentOperator))
79 | {
80 | if (x.Namespace.Contains(_opts.AssignmentOperator) &&
81 | !y.Namespace.Contains(_opts.AssignmentOperator))
82 | {
83 | return 1;
84 | }
85 | if (!x.Namespace.Contains(_opts.AssignmentOperator) &&
86 | y.Namespace.Contains(_opts.AssignmentOperator))
87 | {
88 | return -1;
89 | }
90 | }
91 |
92 | // Check the namespace name first.
93 | if (!_cmpr.Equals(x.Namespace, y.Namespace))
94 | {
95 | return _cmpr.Compare(x.Namespace, y.Namespace);
96 | }
97 |
98 | // See if any of the LinePragma properties are null and return the appropriate code.
99 | if (x.LinePragma is null && y.LinePragma is null) return 0;
100 | if (x.LinePragma is null) return 1;
101 | if (y.LinePragma is null) return -1;
102 |
103 | // Compare linepragma properties if both parameters have them.
104 | if (!_cmpr.Equals(x.LinePragma.FileName, y.LinePragma.FileName))
105 | {
106 | return _cmpr.Compare(x.LinePragma.FileName, y.LinePragma.FileName);
107 | }
108 | if (!x.LinePragma.LineNumber.Equals(y.LinePragma.LineNumber))
109 | {
110 | return x.LinePragma.LineNumber.CompareTo(y.LinePragma.LineNumber);
111 | }
112 | return 0;
113 | }
114 |
115 | ///
116 | public bool Equals(CodeNamespaceImport x, CodeNamespaceImport y)
117 | {
118 | // Check the namespace name first.
119 | if (!_cmpr.Equals(x.Namespace, y.Namespace)) return false;
120 |
121 | // Check the linepragma properties next
122 | if (x.LinePragma != null)
123 | {
124 | if (y.LinePragma == null) return false;
125 |
126 | if (!_cmpr.Equals(x.LinePragma.FileName, y.LinePragma.FileName)) return false;
127 | if (!x.LinePragma.LineNumber.Equals(y.LinePragma.LineNumber)) return false;
128 | }
129 | else
130 | {
131 | if (y.LinePragma != null) return false;
132 | }
133 | return true;
134 | }
135 |
136 | ///
137 | public int GetHashCode(CodeNamespaceImport obj)
138 | {
139 | if (obj is null) return 0;
140 |
141 | unchecked
142 | {
143 | int hash = unchecked((int)2166136261);
144 | const int prime = 16777619;
145 |
146 | hash = (hash ^ obj.Namespace.GetHashCode()) * prime;
147 |
148 | if (obj.LinePragma != null)
149 | {
150 | hash = (hash ^ obj.LinePragma.FileName.GetHashCode()) * prime;
151 | hash = (hash ^ obj.LinePragma.LineNumber.GetHashCode()) * prime;
152 | }
153 |
154 | return hash;
155 | }
156 | }
157 |
158 | #endregion
159 | }
160 | }
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # Changelog
2 |
3 | All notable changes to this project will be documented in this file.
4 |
5 | The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/)
6 | and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html).
7 |
8 | ## [0.5.1-beta] - TBD
9 |
10 | ### Changed
11 | - Cleaned up `CancellationToken.ThrowIfCancellationRequested()` calls.
12 | - Update project url references.
13 | - Update DocumentFormat.OpenXml reference to 2.18.0.
14 | - Update System.CodeDom reference to 7.0.0.
15 |
16 | ## [0.5.0-beta] - 2022-05-29
17 |
18 | ### Added
19 | - Added asynchronous versions of the `GenerateSourceCode(...)` extenson methods for
20 | `OpenXmlElement`, `OpenXmlPart`, and `OpenXmlPackage` objects.
21 |
22 | ### Changed
23 | - *\[Breaking Change\]:* Updated `IOpenXmlElementHandler` and `IOpenXmlPartHandler`
24 | interfaces to add `CancellationToken` parameters to all applicable method
25 | definitions.
26 | - Update DocumentFormat.OpenXml reference to 2.16.0.
27 | - Update System.CodeDom reference to 6.0.0.
28 |
29 | ### Fixed
30 | - When creating and initializing `OpenXmlUnknownElement` variables, use the
31 | `CreateOpenXmlUnknownElement` static method instead of the traditional constructor.
32 | - When the `BuildCodeStatements` method of `OpenXmlElementExtensions` extensions class
33 | encounterd a DateTime property it was throwing a `System.ArgumentException` Invalid
34 | Primitive Type System.DateTime. fixed by @vtgrady2k
35 |
36 | ## [0.4.2-beta] - 2021-11-22
37 |
38 | ### Changed
39 | - Refactored the variable name generation process to reuse existing variable names
40 | when they become available.
41 | - *\[Breaking Change\]:* Added `UseUniqueVariableNames` property to `ISerializeSettings`
42 | interface. This allows to switch between unique and reused variable names.
43 | - *\[Breaking Change\]:* Changed `typeCounts` parameter to `types` in the
44 | `IOpenXmlElementHandler.BuildCodeStatements(...)` method to account for the repurposing
45 | of existing variable name.
46 | - Update DocumentFormat.OpenXml reference to 2.14.0.
47 |
48 | ### Fixed
49 | - Use the correct `CodeExpression` classes for the `XmlNodeType` parameter of the
50 | `OpenXmlMiscNode` constructor.
51 |
52 | ## [0.4.1-beta] - 2021-08-08
53 |
54 | ### Changed
55 | - Refactored the using directive generation logic to better match how the OpenXML SDK
56 | Productivity Tool used to create them. Using directive aliases are now dynamically
57 | generated based on the OpenXml object that the code output is based on.
58 | - *\[Breaking Change\]:* Updated the `namespace` parameter type in the `IOpenXmlElementHandler`
59 | and `IOpenXmlPartHandler` interface methods from `ISet` to `IDictionary`
60 | to account for the new namespace/using directive generation logic. The following
61 | interface methods are impacted:
62 | - `IOpenXmlElementHandler.BuildCodeStatements(...)`
63 | - `IOpenXmlPartHandler.BuildEntryMethodCodeStatements(...)`
64 | - `IOpenXmlPartHandler.BuildHelperMethod(...)`
65 |
66 | ### Fixed
67 |
68 | - Issue related to Hyperlink and external relationship references were not being added properly
69 | in all `OpenXmlPart` code creation scenarios.
70 | - Use the right constructor parameters for `OpenXmlMiscNode` objects.
71 |
72 | ## [0.4.0-beta] - 2021-08-02
73 |
74 | ### Added
75 |
76 | - New `ISerializeSettings` interface to allows greater flexibility in the source code generation.
77 | - New `IOpenXmlHandler`, `IOpenXmlElementHandler`, and `IOpenXmlPartHandler` interfaces that will
78 | allow developers to control how source code is created.
79 |
80 | ### Changed
81 |
82 | - Change visibility of many of the static method helpers so developers can use them in their custom
83 | code generation.
84 | - Update DocumentFormat.OpenXml reference to 2.13.0.
85 |
86 | ### Fixed
87 |
88 | - Make sure that the return type of generated element methods include the namespace alias if
89 | needed.
90 | - Choose between the default method or contentType parameter method for the custom OpenXmlPart.AddNewPart
91 | methods (ex: pkg.AddExtendedFilePropertiesPart() or mainDocumentPart.AddImagePart("image/x-emf"))
92 |
93 | ## [0.3.2-alpha] - 2020-07-30
94 |
95 | ### Changed
96 |
97 | - Updated process to account for more OpenXmlPart classes that may require custom AddNewPart methods
98 | to initialize.
99 | - Changed the `CreatePackage` method to take in a `String` parameter for the full file path of the target file
100 | instead of a `Stream` when generating code for `OpenXmlPackage` objects. This was to avoid using a C# `ref`
101 | parameter that made using the generated code in a C# project more difficult to use.
102 |
103 | ### Fixed
104 |
105 | - TargetInvocationException/FormatException when trying to parse a value that is not valid for
106 | `OpenXmlSimpleType` derived types being evaluated. [See this](https://github.com/OfficeDev/Open-XML-SDK/issues/780)
107 | for more details.
108 | - When encountering OpenXmlUnknownElement objects, make sure to initialize them with the appropriate `ctor` method.
109 | - Correct the initialization parameters for the generated `AddExternalRelationship` method.
110 | - Issue where AddPart methods for OpenXmlPart paths that have already been visited are generated on variables
111 | that do not exist.
112 |
113 | ## [0.3.1-alpha] - 2020-07-25
114 |
115 | ### Fixed
116 |
117 | - TargetInvocationException/FormatException when trying to parse a value that is not valid for
118 | the `EnumValue` type being evaluated. [See this](https://github.com/OfficeDev/Open-XML-SDK/issues/780)
119 | for more details.
120 |
121 | ## [0.3.0-alpha] - 2020-07-20
122 |
123 | ### Changed
124 |
125 | - Update DocumentFormat.OpenXml reference to 2.11.3.
126 |
127 | ### Fixed
128 |
129 | - Ambiguous Match Exception occuring when trying to identify parts that need to use the
130 | `AddImagePart` initialization method.
131 |
132 | ## [0.2.1-alpha] - 2020-07-03
133 |
134 | ### Changed
135 |
136 | - Change the parameters for all of the methods to `ref` parameters. This changes the generated
137 | VB code to create `byref` parameters instead of `byval` ones.
138 |
139 | ## [0.2.0-alpha] - 2020-06-27
140 |
141 | ### Added
142 |
143 | - Added documentation output
144 |
145 | ### Changed
146 |
147 | - Use the alias `AP` for DocumentFormat.OpenXml.ExtendedProperties namespace objects
148 | - Use the `AddImagePart` method for initializing `ImagePart` objects.
149 | - Included the content type parameter for the `AddNewPart` method for `EmbeddedPackagePart` objects.
150 |
151 | ## [0.1.0-alpha] - 2020-06-24
152 |
153 | ### Added
154 |
155 | - Added initial project to convert OpenXml SDK based documents to source code files.
156 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | ## Ignore Visual Studio temporary files, build results, and
2 | ## files generated by popular Visual Studio add-ons.
3 | ##
4 | ## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore
5 |
6 | # User-specific files
7 | *.rsuser
8 | *.suo
9 | *.user
10 | *.userosscache
11 | *.sln.docstates
12 |
13 | # User-specific files (MonoDevelop/Xamarin Studio)
14 | *.userprefs
15 |
16 | # Mono auto generated files
17 | mono_crash.*
18 |
19 | # Build results
20 | [Dd]ebug/
21 | [Dd]ebugPublic/
22 | [Rr]elease/
23 | [Rr]eleases/
24 | x64/
25 | x86/
26 | [Aa][Rr][Mm]/
27 | [Aa][Rr][Mm]64/
28 | bld/
29 | [Bb]in/
30 | [Oo]bj/
31 | [Ll]og/
32 | [Ll]ogs/
33 |
34 | # Visual Studio 2015/2017 cache/options directory
35 | .vs/
36 | # Uncomment if you have tasks that create the project's static files in wwwroot
37 | #wwwroot/
38 |
39 | # Visual Studio 2017 auto generated files
40 | Generated\ Files/
41 |
42 | # MSTest test Results
43 | [Tt]est[Rr]esult*/
44 | [Bb]uild[Ll]og.*
45 |
46 | # NUnit
47 | *.VisualState.xml
48 | TestResult.xml
49 | nunit-*.xml
50 |
51 | # Build Results of an ATL Project
52 | [Dd]ebugPS/
53 | [Rr]eleasePS/
54 | dlldata.c
55 |
56 | # Benchmark Results
57 | BenchmarkDotNet.Artifacts/
58 |
59 | # .NET Core
60 | project.lock.json
61 | project.fragment.lock.json
62 | artifacts/
63 |
64 | # StyleCop
65 | StyleCopReport.xml
66 |
67 | # Files built by Visual Studio
68 | *_i.c
69 | *_p.c
70 | *_h.h
71 | *.ilk
72 | *.meta
73 | *.obj
74 | *.iobj
75 | *.pch
76 | *.pdb
77 | *.ipdb
78 | *.pgc
79 | *.pgd
80 | *.rsp
81 | *.sbr
82 | *.tlb
83 | *.tli
84 | *.tlh
85 | *.tmp
86 | *.tmp_proj
87 | *_wpftmp.csproj
88 | *.log
89 | *.vspscc
90 | *.vssscc
91 | .builds
92 | *.pidb
93 | *.svclog
94 | *.scc
95 |
96 | # Chutzpah Test files
97 | _Chutzpah*
98 |
99 | # Visual C++ cache files
100 | ipch/
101 | *.aps
102 | *.ncb
103 | *.opendb
104 | *.opensdf
105 | *.sdf
106 | *.cachefile
107 | *.VC.db
108 | *.VC.VC.opendb
109 |
110 | # Visual Studio profiler
111 | *.psess
112 | *.vsp
113 | *.vspx
114 | *.sap
115 |
116 | # Visual Studio Trace Files
117 | *.e2e
118 |
119 | # TFS 2012 Local Workspace
120 | $tf/
121 |
122 | # Guidance Automation Toolkit
123 | *.gpState
124 |
125 | # ReSharper is a .NET coding add-in
126 | _ReSharper*/
127 | *.[Rr]e[Ss]harper
128 | *.DotSettings.user
129 |
130 | # TeamCity is a build add-in
131 | _TeamCity*
132 |
133 | # DotCover is a Code Coverage Tool
134 | *.dotCover
135 |
136 | # AxoCover is a Code Coverage Tool
137 | .axoCover/*
138 | !.axoCover/settings.json
139 |
140 | # Visual Studio code coverage results
141 | *.coverage
142 | *.coveragexml
143 |
144 | # NCrunch
145 | _NCrunch_*
146 | .*crunch*.local.xml
147 | nCrunchTemp_*
148 |
149 | # MightyMoose
150 | *.mm.*
151 | AutoTest.Net/
152 |
153 | # Web workbench (sass)
154 | .sass-cache/
155 |
156 | # Installshield output folder
157 | [Ee]xpress/
158 |
159 | # DocProject is a documentation generator add-in
160 | DocProject/buildhelp/
161 | DocProject/Help/*.HxT
162 | DocProject/Help/*.HxC
163 | DocProject/Help/*.hhc
164 | DocProject/Help/*.hhk
165 | DocProject/Help/*.hhp
166 | DocProject/Help/Html2
167 | DocProject/Help/html
168 |
169 | # Click-Once directory
170 | publish/
171 |
172 | # Publish Web Output
173 | *.[Pp]ublish.xml
174 | *.azurePubxml
175 | # Note: Comment the next line if you want to checkin your web deploy settings,
176 | # but database connection strings (with potential passwords) will be unencrypted
177 | *.pubxml
178 | *.publishproj
179 |
180 | # Microsoft Azure Web App publish settings. Comment the next line if you want to
181 | # checkin your Azure Web App publish settings, but sensitive information contained
182 | # in these scripts will be unencrypted
183 | PublishScripts/
184 |
185 | # NuGet Packages
186 | *.nupkg
187 | # NuGet Symbol Packages
188 | *.snupkg
189 | # The packages folder can be ignored because of Package Restore
190 | **/[Pp]ackages/*
191 | # except build/, which is used as an MSBuild target.
192 | !**/[Pp]ackages/build/
193 | # Uncomment if necessary however generally it will be regenerated when needed
194 | #!**/[Pp]ackages/repositories.config
195 | # NuGet v3's project.json files produces more ignorable files
196 | *.nuget.props
197 | *.nuget.targets
198 |
199 | # Microsoft Azure Build Output
200 | csx/
201 | *.build.csdef
202 |
203 | # Microsoft Azure Emulator
204 | ecf/
205 | rcf/
206 |
207 | # Windows Store app package directories and files
208 | AppPackages/
209 | BundleArtifacts/
210 | Package.StoreAssociation.xml
211 | _pkginfo.txt
212 | *.appx
213 | *.appxbundle
214 | *.appxupload
215 |
216 | # Visual Studio cache files
217 | # files ending in .cache can be ignored
218 | *.[Cc]ache
219 | # but keep track of directories ending in .cache
220 | !?*.[Cc]ache/
221 |
222 | # Others
223 | ClientBin/
224 | ~$*
225 | *~
226 | *.dbmdl
227 | *.dbproj.schemaview
228 | *.jfm
229 | *.pfx
230 | *.publishsettings
231 | orleans.codegen.cs
232 |
233 | # Including strong name files can present a security risk
234 | # (https://github.com/github/gitignore/pull/2483#issue-259490424)
235 | #*.snk
236 |
237 | # Since there are multiple workflows, uncomment next line to ignore bower_components
238 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
239 | #bower_components/
240 |
241 | # RIA/Silverlight projects
242 | Generated_Code/
243 |
244 | # Backup & report files from converting an old project file
245 | # to a newer Visual Studio version. Backup files are not needed,
246 | # because we have git ;-)
247 | _UpgradeReport_Files/
248 | Backup*/
249 | UpgradeLog*.XML
250 | UpgradeLog*.htm
251 | ServiceFabricBackup/
252 | *.rptproj.bak
253 |
254 | # SQL Server files
255 | *.mdf
256 | *.ldf
257 | *.ndf
258 |
259 | # Business Intelligence projects
260 | *.rdl.data
261 | *.bim.layout
262 | *.bim_*.settings
263 | *.rptproj.rsuser
264 | *- [Bb]ackup.rdl
265 | *- [Bb]ackup ([0-9]).rdl
266 | *- [Bb]ackup ([0-9][0-9]).rdl
267 |
268 | # Microsoft Fakes
269 | FakesAssemblies/
270 |
271 | # GhostDoc plugin setting file
272 | *.GhostDoc.xml
273 |
274 | # Node.js Tools for Visual Studio
275 | .ntvs_analysis.dat
276 | node_modules/
277 |
278 | # Visual Studio 6 build log
279 | *.plg
280 |
281 | # Visual Studio 6 workspace options file
282 | *.opt
283 |
284 | # Visual Studio 6 auto-generated workspace file (contains which files were open etc.)
285 | *.vbw
286 |
287 | # Visual Studio LightSwitch build output
288 | **/*.HTMLClient/GeneratedArtifacts
289 | **/*.DesktopClient/GeneratedArtifacts
290 | **/*.DesktopClient/ModelManifest.xml
291 | **/*.Server/GeneratedArtifacts
292 | **/*.Server/ModelManifest.xml
293 | _Pvt_Extensions
294 |
295 | # Paket dependency manager
296 | .paket/paket.exe
297 | paket-files/
298 |
299 | # FAKE - F# Make
300 | .fake/
301 |
302 | # CodeRush personal settings
303 | .cr/personal
304 |
305 | # Python Tools for Visual Studio (PTVS)
306 | __pycache__/
307 | *.pyc
308 |
309 | # Cake - Uncomment if you are using it
310 | tools/**
311 | !tools/packages.config
312 |
313 | # Tabs Studio
314 | *.tss
315 |
316 | # Telerik's JustMock configuration file
317 | *.jmconfig
318 |
319 | # BizTalk build output
320 | *.btp.cs
321 | *.btm.cs
322 | *.odx.cs
323 | *.xsd.cs
324 |
325 | # OpenCover UI analysis results
326 | OpenCover/
327 |
328 | # Azure Stream Analytics local run output
329 | ASALocalRun/
330 |
331 | # MSBuild Binary and Structured Log
332 | *.binlog
333 |
334 | # NVidia Nsight GPU debugger configuration file
335 | *.nvuser
336 |
337 | # MFractors (Xamarin productivity tool) working folder
338 | .mfractor/
339 |
340 | # Local History for Visual Studio
341 | .localhistory/
342 |
343 | # BeatPulse healthcheck temp database
344 | healthchecksdb
345 |
346 | # Backup folder for Package Reference Convert tool in Visual Studio 2017
347 | MigrationBackup/
348 |
349 | # Ionide (cross platform F# VS Code tools) working folder
350 | .ionide/
351 |
--------------------------------------------------------------------------------
/src/Serialize.OpenXml.CodeGen/TypeMonitor.cs:
--------------------------------------------------------------------------------
1 | /* MIT License
2 |
3 | Copyright (c) 2021 Ryan Boggs
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy of this
6 | software and associated documentation files (the "Software"), to deal in the Software
7 | without restriction, including without limitation the rights to use, copy, modify,
8 | merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
9 | permit persons to whom the Software is furnished to do so, subject to the following
10 | conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all copies
13 | or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
16 | INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
17 | PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
18 | FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
19 | OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
20 | DEALINGS IN THE SOFTWARE.
21 | */
22 |
23 | using Serialize.OpenXml.CodeGen.Extentions;
24 | using System;
25 | using System.Collections;
26 | using System.Collections.Generic;
27 | using System.Linq;
28 |
29 | namespace Serialize.OpenXml.CodeGen
30 | {
31 | ///
32 | /// Class designed to monitor the variable names generated by a single type.
33 | ///
34 | ///
35 | /// This will be used only for code generation of
36 | /// objects.
37 | ///
38 | public class TypeMonitor : IReadOnlyDictionary
39 | {
40 | #region Private Instance Fields
41 |
42 | ///
43 | /// Holds the that the current instance will
44 | /// represent for its lifetime.
45 | ///
46 | private readonly Type _type;
47 |
48 | ///
49 | /// Holds the counter used when unique variable names are generated.
50 | ///
51 | private int _uniqueCount = 0;
52 |
53 | ///
54 | /// The object containing all of the
55 | /// variable names that have already been generated and their 'consumed'
56 | /// indicators.
57 | ///
58 | #pragma warning disable IDE0044 // Add readonly modifier
59 | private Dictionary _values = new Dictionary();
60 | #pragma warning restore IDE0044 // Add readonly modifier
61 |
62 | #endregion
63 |
64 | #region Public Constructors
65 |
66 | ///
67 | /// Initializes a new instance of the class with
68 | /// the type that it will represent.
69 | ///
70 | ///
71 | /// The that this new object will represent.
72 | ///
73 | public TypeMonitor(Type t)
74 | {
75 | _type = t ?? throw new ArgumentNullException(nameof(t));
76 | }
77 |
78 | #endregion
79 |
80 | #region Internal Static Properties
81 |
82 | ///
83 | /// Indicates whether or not to use unique/numbered variable names
84 | /// for the current request.
85 | ///
86 | internal static bool UseUniqueVariableNames { get; set; } = false;
87 |
88 | #endregion
89 |
90 | #region Public Instance Properties
91 |
92 | ///
93 | public bool this[string key]
94 | {
95 | get => _values[key];
96 | set => _values[key] = value;
97 | }
98 |
99 | ///
100 | public IEnumerable Keys => _values.Keys;
101 |
102 | ///
103 | public IEnumerable Values => _values.Values;
104 |
105 | ///
106 | public int Count => UseUniqueVariableNames ? _uniqueCount : _values.Count;
107 |
108 | ///
109 | /// Gets the that the current object represents.
110 | ///
111 | public Type Type { get => _type; }
112 |
113 | #endregion
114 |
115 | #region Protected Instance Properties
116 |
117 | ///
118 | /// Gets the underlying object
119 | /// for the current instance.
120 | ///
121 | protected IDictionary Dictionary { get { return _values; } }
122 |
123 | #endregion
124 |
125 | #region Public Instance Methods
126 |
127 | ///
128 | public bool ContainsKey(string key) => _values.ContainsKey(key);
129 |
130 | ///
131 | /// Retrieves an existing or new variable name, depending on what is
132 | /// available at the time, to generate new code statements with.
133 | ///
134 | ///
135 | /// Collection used to keep
136 | /// track of all openxml namespaces used during the process.
137 | ///
138 | ///
139 | /// The variable name to use.
140 | ///
141 | ///
142 | /// if already exists
143 | /// in existing generated code with constructor statements; otherwise
144 | /// is returned, indicating that no existing
145 | /// generated code yet exists for .
146 | ///
147 | public bool GetVariableName(IDictionary namespaces, out string varName)
148 | {
149 | if (UseUniqueVariableNames)
150 | {
151 |
152 | varName = Type.GenerateVariableName(_uniqueCount++, namespaces);
153 | return false;
154 | }
155 | else
156 | {
157 | return CreateAndTrackVariableName(namespaces, out varName);
158 | }
159 | }
160 |
161 | ///
162 | public bool TryGetValue(string key, out bool value)
163 | {
164 | return _values.TryGetValue(key, out value);
165 | }
166 |
167 | #endregion
168 |
169 | #region Protected Instance Methods
170 |
171 | ///
172 | /// Adds a new key/value pair to this collection.
173 | ///
174 | /// The key of the new pair.
175 | /// The value of the new pair.
176 | protected void Add(string key, bool value) => _values.Add(key, value);
177 |
178 | ///
179 | /// Responible for retrieving an existing or new variable name,
180 | /// depending on what is available at the time, to generate new
181 | /// code statements with.
182 | ///
183 | ///
184 | /// Collection used to keep
185 | /// track of all openxml namespaces used during the process.
186 | ///
187 | ///
188 | /// The variable name to use.
189 | ///
190 | ///
191 | /// if already exists
192 | /// in existing generated code with constructor statements; otherwise
193 | /// is returned, indicating that no existing
194 | /// generated code yet exists for .
195 | ///
196 | protected virtual bool CreateAndTrackVariableName(
197 | IDictionary namespaces, out string varName)
198 | {
199 | if (Count > 0 && this.Values.Any(v => v))
200 | {
201 | varName = this.LastOrDefault(v => v.Value).Key;
202 | this[varName] = false;
203 | return true;
204 | }
205 |
206 | int tries = _values.Count;
207 | varName = Type.GenerateVariableName(tries, namespaces);
208 | Add(varName, false);
209 | return false;
210 | }
211 |
212 | #endregion
213 |
214 | #region Private Instance Methods;
215 |
216 | ///
217 | IEnumerator> IEnumerable>
218 | .GetEnumerator()
219 | {
220 | return ((IEnumerable>)_values).GetEnumerator();
221 | }
222 |
223 | ///
224 | IEnumerator IEnumerable.GetEnumerator()
225 | {
226 | return _values.GetEnumerator();
227 | }
228 |
229 | #endregion
230 | }
231 | }
232 |
--------------------------------------------------------------------------------
/src/Serialize.OpenXml.CodeGen/OpenXmlPartContainerExtensions.cs:
--------------------------------------------------------------------------------
1 | /* MIT License
2 |
3 | Copyright (c) 2021 Ryan Boggs
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy of this
6 | software and associated documentation files (the "Software"), to deal in the Software
7 | without restriction, including without limitation the rights to use, copy, modify,
8 | merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
9 | permit persons to whom the Software is furnished to do so, subject to the following
10 | conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all copies
13 | or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
16 | INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
17 | PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
18 | FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
19 | OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
20 | DEALINGS IN THE SOFTWARE.
21 | */
22 |
23 | using DocumentFormat.OpenXml.Packaging;
24 | using Serialize.OpenXml.CodeGen.Extentions;
25 | using System;
26 | using System.CodeDom;
27 | using System.Collections.Generic;
28 | using System.Linq;
29 |
30 | namespace Serialize.OpenXml.CodeGen
31 | {
32 | ///
33 | /// Static class that is responsible for generating
34 | /// objects for derived objects.
35 | ///
36 | public static class OpenXmlPartContainerExtensions
37 | {
38 | #region Public Static Methods
39 |
40 | ///
41 | /// Creates a collection of code statements that describe how to add external relationships
42 | /// to a object.
43 | ///
44 | ///
45 | /// The collection of objects to build the code
46 | /// statements for.
47 | ///
48 | ///
49 | /// The name of the object that the external
50 | /// relationship assignments should be for.
51 | ///
52 | ///
53 | /// A collection of code statements that could be used to generate and assign new
54 | /// objects to a
55 | /// object.
56 | ///
57 | public static CodeStatementCollection BuildExternalRelationshipStatements(
58 | this IEnumerable relationships, string parentName)
59 | {
60 | if (String.IsNullOrWhiteSpace(parentName))
61 | throw new ArgumentNullException(nameof(parentName));
62 |
63 | return relationships.BuildExternalRelationshipStatements(
64 | new CodeVariableReferenceExpression(parentName));
65 | }
66 |
67 | ///
68 | /// Creates a collection of code statements that describe how to add external relationships
69 | /// to a object.
70 | ///
71 | ///
72 | /// The collection of objects to build the code
73 | /// statements for.
74 | ///
75 | ///
76 | /// The object that the generated code statements will
77 | /// reference to build the external relationship assignments.
78 | ///
79 | ///
80 | /// A collection of code statements that could be used to generate and assign new
81 | /// objects to a
82 | /// object.
83 | ///
84 | public static CodeStatementCollection BuildExternalRelationshipStatements(
85 | this IEnumerable relationships, CodeExpression parent)
86 | {
87 | if (parent is null) throw new ArgumentNullException(nameof(parent));
88 |
89 | var result = new CodeStatementCollection();
90 |
91 | // Return an empty code statement collection if the hyperlinks parameter is empty.
92 | if (relationships.Count() == 0) return result;
93 |
94 | CodeObjectCreateExpression createExpression;
95 | CodeMethodReferenceExpression methodReferenceExpression;
96 | CodeMethodInvokeExpression invokeExpression;
97 | CodePrimitiveExpression param;
98 | CodeTypeReference typeReference;
99 |
100 | foreach (var ex in relationships)
101 | {
102 | // Need special care to create the uri for the current object.
103 | typeReference = new CodeTypeReference(ex.Uri.GetType());
104 | param = new CodePrimitiveExpression(ex.Uri.ToString());
105 | createExpression = new CodeObjectCreateExpression(typeReference, param);
106 |
107 | // Create the AddHyperlinkRelationship statement
108 | methodReferenceExpression = new CodeMethodReferenceExpression(parent,
109 | "AddExternalRelationship");
110 | invokeExpression = new CodeMethodInvokeExpression(methodReferenceExpression,
111 | new CodePrimitiveExpression(ex.RelationshipType),
112 | createExpression,
113 | new CodePrimitiveExpression(ex.Id));
114 | result.Add(invokeExpression);
115 | }
116 | return result;
117 | }
118 |
119 | ///
120 | /// Creates a collection of code statements that describe how to add hyperlink
121 | /// relationships to a object.
122 | ///
123 | ///
124 | /// The collection of objects to build the code
125 | /// statements for.
126 | ///
127 | ///
128 | /// The name of the object that the hyperlink
129 | /// relationship assignments should be for.
130 | ///
131 | ///
132 | /// A collection of code statements that could be used to generate and assign new
133 | /// objects to a
134 | /// object.
135 | ///
136 | public static CodeStatementCollection BuildHyperlinkRelationshipStatements(
137 | this IEnumerable hyperlinks, string parentName)
138 | {
139 | if (String.IsNullOrWhiteSpace(parentName))
140 | {
141 | throw new ArgumentNullException(nameof(parentName));
142 | }
143 |
144 | return hyperlinks.BuildHyperlinkRelationshipStatements(
145 | new CodeVariableReferenceExpression(parentName));
146 | }
147 |
148 | ///
149 | /// Creates a collection of code statements that describe how to add hyperlink
150 | /// relationships to a object.
151 | ///
152 | ///
153 | /// The collection of objects to build the code
154 | /// statements for.
155 | ///
156 | ///
157 | /// The object that the generated code statements will
158 | /// reference to build the hyperlink relationship assignments.
159 | ///
160 | ///
161 | /// A collection of code statements that could be used to generate and assign new
162 | /// objects to a
163 | /// object.
164 | ///
165 | public static CodeStatementCollection BuildHyperlinkRelationshipStatements(
166 | this IEnumerable hyperlinks, CodeExpression parent)
167 | {
168 | if (parent is null) throw new ArgumentNullException(nameof(parent));
169 |
170 | var result = new CodeStatementCollection();
171 |
172 | // Return an empty code statement collection if the hyperlinks parameter is empty.
173 | if (hyperlinks.Count() == 0) return result;
174 |
175 | CodeObjectCreateExpression createExpression;
176 | CodeMethodReferenceExpression methodReferenceExpression;
177 | CodeMethodInvokeExpression invokeExpression;
178 | CodePrimitiveExpression param;
179 | CodeTypeReference typeReference;
180 |
181 | foreach (var hl in hyperlinks)
182 | {
183 | // Need special care to create the uri for the current object.
184 | typeReference = new CodeTypeReference(hl.Uri.GetType());
185 | param = new CodePrimitiveExpression(hl.Uri.ToString());
186 | createExpression = new CodeObjectCreateExpression(typeReference, param);
187 |
188 | // Create the AddHyperlinkRelationship statement
189 | methodReferenceExpression = new CodeMethodReferenceExpression(parent,
190 | "AddHyperlinkRelationship");
191 | invokeExpression = new CodeMethodInvokeExpression(methodReferenceExpression,
192 | createExpression,
193 | new CodePrimitiveExpression(hl.IsExternal),
194 | new CodePrimitiveExpression(hl.Id));
195 | result.Add(invokeExpression);
196 | }
197 | return result;
198 | }
199 |
200 | ///
201 | /// Locates any relationship elements associated with an
202 | /// object and generates codes statements for them.
203 | ///
204 | ///
205 | /// The
206 | ///
207 | ///
208 | /// The variable name that the generated code will reference.
209 | ///
210 | ///
211 | /// A new containing all of the code statements
212 | /// needed to create the relationships associated with .
213 | ///
214 | ///
215 | /// If doesn't contain any relationships, then an empty
216 | /// is returned.
217 | ///
218 | public static CodeStatementCollection GenerateRelationshipCodeStatements(
219 | this OpenXmlPartContainer part, string varName)
220 | {
221 | if (String.IsNullOrEmpty(varName)) throw new ArgumentNullException(nameof(varName));
222 |
223 | return part.GenerateRelationshipCodeStatements(
224 | new CodeVariableReferenceExpression(varName));
225 | }
226 |
227 | ///
228 | /// Locates any relationship elements associated with an
229 | /// object and generates codes statements for them.
230 | ///
231 | ///
232 | /// The
233 | ///
234 | ///
235 | /// The variable that the generated code will reference.
236 | ///
237 | ///
238 | /// A new containing all of the code statements
239 | /// needed to create the relationships associated with .
240 | ///
241 | ///
242 | /// If doesn't contain any relationships, then an empty
243 | /// is returned.
244 | ///
245 | public static CodeStatementCollection GenerateRelationshipCodeStatements(
246 | this OpenXmlPartContainer part, CodeExpression variable)
247 | {
248 | if (variable is null) throw new ArgumentNullException(nameof(variable));
249 |
250 | var result = new CodeStatementCollection();
251 |
252 | // Generate statements for the hyperlink relationships if applicable.
253 | if (part.HyperlinkRelationships.Count() > 0)
254 | {
255 | // Add a line break first for easier reading
256 | result.AddBlankLine();
257 | result.AddRange(
258 | part.HyperlinkRelationships.BuildHyperlinkRelationshipStatements(variable));
259 | }
260 |
261 | // Generate statement for the non-hyperlink/external relationships if applicable
262 | if (part.ExternalRelationships.Count() > 0)
263 | {
264 | // Add a line break first for easier reading
265 | result.AddBlankLine();
266 | result.AddRange(
267 | part.ExternalRelationships.BuildExternalRelationshipStatements(variable));
268 | }
269 | return result;
270 | }
271 |
272 | #endregion
273 | }
274 | }
275 |
--------------------------------------------------------------------------------
/src/Serialize.OpenXml.CodeGen/Extensions/TypeExtensions.cs:
--------------------------------------------------------------------------------
1 | /* MIT License
2 |
3 | Copyright (c) 2020 Ryan Boggs
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy of this
6 | software and associated documentation files (the "Software"), to deal in the Software
7 | without restriction, including without limitation the rights to use, copy, modify,
8 | merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
9 | permit persons to whom the Software is furnished to do so, subject to the following
10 | conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all copies
13 | or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
16 | INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
17 | PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
18 | FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
19 | OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
20 | DEALINGS IN THE SOFTWARE.
21 | */
22 |
23 | using DocumentFormat.OpenXml;
24 | using System;
25 | using System.CodeDom;
26 | using System.Collections.Generic;
27 | using System.Linq;
28 | using System.Reflection;
29 | using System.Text;
30 |
31 | namespace Serialize.OpenXml.CodeGen.Extentions
32 | {
33 | ///
34 | /// Collection of extension methods for the class
35 | /// specific to the generating code dom representations of
36 | /// OpenXml objects.
37 | ///
38 | public static class TypeExtensions
39 | {
40 | #region Static Constructors
41 |
42 | ///
43 | /// Static constructor.
44 | ///
45 | static TypeExtensions()
46 | {
47 | // Now setup the simple type collection.
48 | var simpleTypes = new Type[]
49 | {
50 | typeof(StringValue),
51 | typeof(OpenXmlSimpleValue),
52 | typeof(OpenXmlSimpleValue),
53 | typeof(OpenXmlSimpleValue),
54 | typeof(OpenXmlSimpleValue),
55 | typeof(OpenXmlSimpleValue),
56 | typeof(OpenXmlSimpleValue),
57 | typeof(OpenXmlSimpleValue),
58 | typeof(OpenXmlSimpleValue),
59 | typeof(OpenXmlSimpleValue),
60 | typeof(OpenXmlSimpleValue),
61 | typeof(OpenXmlSimpleValue),
62 | typeof(OpenXmlSimpleValue),
63 | typeof(OpenXmlSimpleValue)
64 | };
65 |
66 | SimpleValueTypes = simpleTypes.ToList();
67 | }
68 |
69 | #endregion
70 |
71 | #region Public Static Properties
72 |
73 | ///
74 | /// Gets a collection of based types.
75 | ///
76 | ///
77 | /// This is used to identify property types of objects that
78 | /// can be initialized with simple values of their base type counterparts.
79 | ///
80 | public static IReadOnlyList SimpleValueTypes { get; private set; }
81 |
82 | #endregion
83 |
84 | #region Public Static methods
85 |
86 | ///
87 | /// Checks to see if a exists in a different namespace.
88 | ///
89 | ///
90 | /// The with the name to search for.
91 | ///
92 | ///
93 | /// The of collected namespaces
94 | /// to search in.
95 | ///
96 | ///
97 | /// if exists in a different
98 | /// namespace; otherwise, .
99 | ///
100 | public static bool ExistsInDifferentNamespace(this Type t,
101 | IDictionary namespaces)
102 | {
103 | if (namespaces is null) throw new ArgumentNullException(nameof(namespaces));
104 | if (namespaces.Count == 0) return false;
105 |
106 | Type tmp;
107 | bool foundElsewhere = false;
108 | Type[] classes;
109 | string tmpName;
110 |
111 | bool getTypesWhere(Type c) =>
112 | !String.IsNullOrEmpty(c.Namespace) &&
113 | c.Namespace.Equals(t.Namespace, StringComparison.Ordinal);
114 |
115 | foreach (var ns in namespaces)
116 | {
117 | // First scan for the actual type name in other existing
118 | // namespaces
119 | tmpName = $"{ns.Key}.{t.Name}";
120 | tmp = t.Assembly.GetType(tmpName) ?? Type.GetType(tmpName);
121 |
122 | if (tmp != null)
123 | {
124 | foundElsewhere = true;
125 | break;
126 | }
127 | }
128 |
129 | if (!foundElsewhere)
130 | {
131 | // Next, try to scan for other classes in the type's namespace
132 | // in other namespaces
133 | classes = Assembly.GetAssembly(t).GetTypes()
134 | .Where(getTypesWhere)
135 | .ToArray();
136 |
137 | foreach (var ns in namespaces)
138 | {
139 | foreach (var cl in classes)
140 | {
141 | tmpName = $"{ns.Key}.{cl.Name}";
142 | tmp = t.Assembly.GetType(tmpName) ?? Type.GetType(tmpName);
143 |
144 | if (tmp != null)
145 | {
146 | foundElsewhere = true;
147 | break;
148 | }
149 | }
150 | if (foundElsewhere) break;
151 | }
152 | }
153 |
154 | return foundElsewhere;
155 | }
156 |
157 | ///
158 | /// Checks to see if a exists in a different namespace.
159 | ///
160 | ///
161 | /// The with the name to search for.
162 | ///
163 | ///
164 | /// The of collected namespaces
165 | /// to search in.
166 | ///
167 | ///
168 | /// The namespace alias to use, if necessary.
169 | ///
170 | ///
171 | /// if exists in a different
172 | /// namespace; otherwise, .
173 | ///
174 | public static bool ExistsInDifferentNamespace(this Type t,
175 | IDictionary namespaces, out string alias)
176 | {
177 | if (namespaces is null) throw new ArgumentNullException(nameof(namespaces));
178 | if (namespaces.Count == 0)
179 | {
180 | alias = String.Empty;
181 | return false;
182 | }
183 |
184 | Type tmp;
185 | bool foundElsewhere = false;
186 | Type[] classes;
187 | string tmpName;
188 | string al = String.Empty;
189 |
190 | bool getTypesWhere(Type c) =>
191 | !String.IsNullOrEmpty(c.Namespace) &&
192 | c.Namespace.Equals(t.Namespace, StringComparison.Ordinal);
193 |
194 | // Tries to find an alias to use for the specified type
195 | string findAlias(Type c)
196 | {
197 | foundElsewhere = true;
198 | string result = null;
199 |
200 | // If the current type is a subclass of OpenXmlElement
201 | // try to initialize it with a default constructor to
202 | // get its prefix for the namespace alias
203 | if (c.IsSubclassOf(typeof(OpenXmlElement)))
204 | {
205 | var ctor = c.GetConstructor(Type.EmptyTypes);
206 |
207 | if (ctor != null)
208 | {
209 | var element = c.Assembly.CreateInstance(c.FullName) as OpenXmlElement;
210 | result = element.Prefix.ToUpperInvariant();
211 | }
212 | }
213 |
214 | // Create a new alias if the element prefix could not be located.
215 | if (String.IsNullOrEmpty(result))
216 | {
217 | var sb = new StringBuilder();
218 | var ns = c.Namespace;
219 |
220 | // Use only upper case or numeric characters from the
221 | // type's namespace as the new alias.
222 | for (int i = 0; i < ns.Length; i++)
223 | {
224 | if (Char.IsUpper(ns[i]) || Char.IsDigit(ns[i]))
225 | {
226 | sb.Append(ns[i]);
227 | }
228 | }
229 | result = sb.ToString();
230 | }
231 | return result;
232 | }
233 |
234 | foreach (var ns in namespaces)
235 | {
236 | // First scan for the actual type name in other existing
237 | // namespaces
238 | tmpName = $"{ns.Key}.{t.Name}";
239 | tmp = t.Assembly.GetType(tmpName) ?? Type.GetType(tmpName);
240 |
241 | if (tmp != null)
242 | {
243 | al = findAlias(t);
244 | break;
245 | }
246 | }
247 |
248 | if (!foundElsewhere)
249 | {
250 | // Next, try to scan for other classes in the type's namespace
251 | // in other namespaces
252 | classes = Assembly.GetAssembly(t).GetTypes()
253 | .Where(getTypesWhere)
254 | .ToArray();
255 |
256 | foreach (var ns in namespaces)
257 | {
258 | foreach (var cl in classes)
259 | {
260 | tmpName = $"{ns.Key}.{cl.Name}";
261 | tmp = t.Assembly.GetType(tmpName) ?? Type.GetType(tmpName);
262 |
263 |
264 | if (tmp != null)
265 | {
266 | al = findAlias(cl);
267 | break;
268 | }
269 | }
270 | if (foundElsewhere) break;
271 | }
272 | }
273 |
274 | alias = al;
275 | return foundElsewhere;
276 | }
277 |
278 | ///
279 | /// Generates a variable name to use when generating the appropriate
280 | /// CodeDom objects for a given .
281 | ///
282 | ///
283 | /// The to generate the variable name for.
284 | ///
285 | ///
286 | /// The number of variables that have been already created for
287 | /// .
288 | ///
289 | ///
290 | /// Collection used to keep
291 | /// track of all openxml namespaces used during the process.
292 | ///
293 | ///
294 | /// A new variable name to use to represent .
295 | ///
296 | public static string GenerateVariableName(
297 | this Type t,
298 | int tries,
299 | IDictionary namespaces)
300 | {
301 | if (namespaces is null) throw new ArgumentNullException(nameof(namespaces));
302 |
303 | string tmp; // Hold the generated name
304 | string nsPrefix = String.Empty;
305 |
306 | // Include the namespace alias as part of the variable name
307 | if (namespaces.ContainsKey(t.Namespace) &&
308 | !String.IsNullOrWhiteSpace(namespaces[t.Namespace]))
309 | {
310 | nsPrefix = namespaces[t.Namespace].ToLowerInvariant();
311 | }
312 |
313 | // Simply return the generated name if the current
314 | // type is not considered generic.
315 | if (!t.IsGenericType)
316 | {
317 | tmp = String.Concat(nsPrefix, t.Name).ToCamelCase();
318 | if (tries > 0)
319 | {
320 | return String.Concat(tmp, tries);
321 | }
322 | return tmp;
323 | }
324 |
325 | // Include the generic types as part of the var name.
326 | var sb = new StringBuilder();
327 | foreach (var item in t.GenericTypeArguments)
328 | {
329 | sb.Append(item.Name.RetrieveUpperCaseChars().ToTitleCase());
330 | }
331 | tmp = t.Name;
332 |
333 | if (tries > 0)
334 | {
335 | return String.Concat(nsPrefix,
336 | tmp.Substring(0, tmp.IndexOf("`")),
337 | sb.ToString(),
338 | tries).ToCamelCase();
339 | }
340 |
341 | return String.Concat(nsPrefix,
342 | tmp.Substring(0, tmp.IndexOf("`")),
343 | sb.ToString()).ToCamelCase();
344 | }
345 |
346 | ///
347 | /// Creates a class name to use when generating
348 | /// source code.
349 | ///
350 | ///
351 | /// The object containing the class name to evaluate.
352 | ///
353 | ///
354 | /// Collection used to keep track of a
355 | /// ll openxml namespaces used during the process.
356 | ///
357 | ///
358 | /// The value to evaluate when building the
359 | /// appropriate class name.
360 | ///
361 | ///
362 | /// The class name to use when building a new
363 | /// object.
364 | ///
365 | public static string GetObjectTypeName(this Type t,
366 | IDictionary namespaces, NamespaceAliasOrder order)
367 | {
368 | if (!namespaces.ContainsKey(t.Namespace) || order == NamespaceAliasOrder.None)
369 | {
370 | return t.FullName;
371 | }
372 | if (!String.IsNullOrWhiteSpace(namespaces[t.Namespace]))
373 | {
374 | return $"{namespaces[t.Namespace]}.{t.Name}";
375 | }
376 | return t.Name;
377 | }
378 |
379 | ///
380 | /// Gets all of the objects that inherit from
381 | /// the class in .
382 | ///
383 | ///
384 | /// The object to retrieve the
385 | /// objects from.
386 | ///
387 | ///
388 | /// A collection of objects that inherit from
389 | /// the class.
390 | ///
391 | ///
392 | /// All necessary OpenXml object properties inherit from the
393 | /// class. This makes it easier to
394 | /// iterate through.
395 | ///
396 | public static IReadOnlyList GetOpenXmlSimpleTypeProperties(this Type t)
397 | {
398 | return t.GetOpenXmlSimpleTypeProperties(true);
399 | }
400 |
401 | ///
402 | /// Gets all of the objects that inherit from
403 | /// the class in .
404 | ///
405 | ///
406 | /// The object to retrieve the
407 | /// objects from.
408 | ///
409 | ///
410 | /// Include properties with types that inherit from
411 | /// type.
412 | ///
413 | ///
414 | /// A collection of objects that inherit from
415 | /// the class.
416 | ///
417 | ///
418 | /// All necessary OpenXml object properties inherit from the
419 | /// class. This makes it easier to
420 | /// iterate through.
421 | ///
422 | public static IReadOnlyList GetOpenXmlSimpleTypeProperties(
423 | this Type t, bool includeSimpleValueTypes)
424 | {
425 | var props = t.GetProperties();
426 | var result = new List();
427 |
428 | // Collect all properties that are of type or subclass of
429 | // OpenXmlSimpleType
430 | foreach (var p in props)
431 | {
432 | if (p.PropertyType.Equals(typeof(OpenXmlSimpleType)) ||
433 | p.PropertyType.IsSubclassOf(typeof(OpenXmlSimpleType)))
434 | {
435 | if (includeSimpleValueTypes || !p.PropertyType.IsSimpleValueType())
436 | {
437 | result.Add(p);
438 | }
439 | }
440 | }
441 | // Return the collected
442 | return result;
443 | }
444 |
445 | ///
446 | /// Gets all of the objects that inherit from
447 | /// the class in .
448 | ///
449 | ///
450 | /// The object to retrieve the
451 | /// objects from.
452 | ///
453 | ///
454 | /// A collection of objects that inherit from
455 | /// the class.
456 | ///
457 | ///
458 | /// All necessary OpenXml object properties inherit from the
459 | /// class. This makes it easier to
460 | /// iterate through.
461 | ///
462 | public static IReadOnlyList GetOpenXmlSimpleValuesProperties(this Type t)
463 | {
464 | var props = t.GetProperties();
465 | var result = new List();
466 |
467 | // Collect all properties that are of type or subclass of
468 | // OpenXmlSimpleType
469 | foreach (var p in props)
470 | {
471 | foreach (var item in SimpleValueTypes)
472 | {
473 | if (p.PropertyType.Equals(item) ||
474 | p.PropertyType.IsSubclassOf(item))
475 | {
476 | result.Add(p);
477 | break;
478 | }
479 | }
480 | }
481 | // Return the collected properties
482 | return result;
483 | }
484 |
485 | ///
486 | /// Gets all objects from
487 | /// of type .
488 | ///
489 | ///
490 | /// The to retrieve the objects
491 | /// from.
492 | ///
493 | ///
494 | /// A read only collection of objects with a
495 | /// property type.
496 | ///
497 | public static IReadOnlyList GetStringValueProperties(this Type t)
498 | => t.GetProperties().Where(s => s.PropertyType == typeof(StringValue)).ToList();
499 |
500 | ///
501 | /// Checks to see if is EnumValue`1.
502 | ///
503 | ///
504 | /// The to evaluate.
505 | ///
506 | ///
507 | /// if the type of
508 | /// is EnumValue`1; otherwise, .
509 | ///
510 | public static bool IsEnumValueType(this Type t) =>
511 | t.Name.Equals("EnumValue`1", StringComparison.Ordinal);
512 |
513 | ///
514 | /// Indicates whether or not is considered
515 | /// a type.
516 | ///
517 | ///
518 | /// The to check.
519 | ///
520 | ///
521 | /// if is derived
522 | /// from an OpenXmlSimpleValue type; otherwise, .
523 | ///
524 | public static bool IsSimpleValueType(this Type t)
525 | {
526 | foreach (var item in SimpleValueTypes)
527 | {
528 | if (t.Equals(item) || t.IsSubclassOf(item))
529 | {
530 | return true;
531 | }
532 | }
533 | return false;
534 | }
535 |
536 | #endregion
537 | }
538 | }
--------------------------------------------------------------------------------
/src/Serialize.OpenXml.CodeGen/OpenXmlPackageExtensions.cs:
--------------------------------------------------------------------------------
1 | /* MIT License
2 |
3 | Copyright (c) 2020 Ryan Boggs
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy of this
6 | software and associated documentation files (the "Software"), to deal in the Software
7 | without restriction, including without limitation the rights to use, copy, modify,
8 | merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
9 | permit persons to whom the Software is furnished to do so, subject to the following
10 | conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all copies
13 | or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
16 | INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
17 | PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
18 | FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
19 | OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
20 | DEALINGS IN THE SOFTWARE.
21 | */
22 |
23 | using DocumentFormat.OpenXml.Packaging;
24 | using Serialize.OpenXml.CodeGen.Extentions;
25 | using System;
26 | using System.CodeDom;
27 | using System.CodeDom.Compiler;
28 | using System.Collections.Generic;
29 | using System.IO;
30 | using System.Linq;
31 | using System.Threading;
32 | using System.Threading.Tasks;
33 |
34 | namespace Serialize.OpenXml.CodeGen
35 | {
36 | ///
37 | /// Static class that converts packages
38 | /// into Code DOM objects.
39 | ///
40 | public static class OpenXmlPackageExtensions
41 | {
42 | #region Public Static Methods
43 |
44 | ///
45 | /// Converts an into a CodeDom object that can be used
46 | /// to build code in a given .NET language to build the referenced .
47 | ///
48 | ///
49 | /// The object to generate source code for.
50 | ///
51 | ///
52 | /// A new containing the instructions to build
53 | /// the referenced .
54 | ///
55 | public static CodeCompileUnit GenerateSourceCode(this OpenXmlPackage pkg)
56 | {
57 | return pkg.GenerateSourceCode(new DefaultSerializeSettings());
58 | }
59 |
60 | ///
61 | /// Converts an into a CodeDom object that can be used
62 | /// to build code in a given .NET language to build the referenced .
63 | ///
64 | ///
65 | /// The object to generate source code for.
66 | ///
67 | ///
68 | /// The to apply to the resulting source code.
69 | ///
70 | ///
71 | /// A new containing the instructions to build
72 | /// the referenced .
73 | ///
74 | public static CodeCompileUnit GenerateSourceCode(this OpenXmlPackage pkg,
75 | NamespaceAliasOptions opts)
76 | {
77 | return pkg.GenerateSourceCode(new DefaultSerializeSettings(opts));
78 | }
79 |
80 | ///
81 | /// Converts an into a CodeDom object that can be used
82 | /// to build code in a given .NET language to build the referenced .
83 | ///
84 | ///
85 | /// The object to generate source code for.
86 | ///
87 | ///
88 | /// The to use during the code generation
89 | /// process.
90 | ///
91 | ///
92 | /// A new containing the instructions to build
93 | /// the referenced .
94 | ///
95 | public static CodeCompileUnit GenerateSourceCode(this OpenXmlPackage pkg,
96 | ISerializeSettings settings)
97 | {
98 | return DefaultSerializeSettings.TaskIndustry.StartNew(
99 | () => pkg.GenerateSourceCodeAsync(settings, CancellationToken.None))
100 | .Unwrap()
101 | .GetAwaiter()
102 | .GetResult();
103 | }
104 |
105 | ///
106 | /// Converts an into a representation
107 | /// of dotnet source code that can be compiled to build .
108 | ///
109 | ///
110 | /// The object to generate source code for.
111 | ///
112 | ///
113 | /// The object to create the resulting source code.
114 | ///
115 | ///
116 | /// A representation of the source code generated by
117 | /// that could create when compiled.
118 | ///
119 | public static string GenerateSourceCode(this OpenXmlPackage pkg, CodeDomProvider provider)
120 | {
121 | return pkg.GenerateSourceCode(new DefaultSerializeSettings(), provider);
122 | }
123 |
124 | ///
125 | /// Converts an into a representation
126 | /// of dotnet source code that can be compiled to build .
127 | ///
128 | ///
129 | /// The object to generate source code for.
130 | ///
131 | ///
132 | /// The to apply to the resulting source code.
133 | ///
134 | ///
135 | /// The object to create the resulting source code.
136 | ///
137 | ///
138 | /// A representation of the source code generated by
139 | /// that could create when compiled.
140 | ///
141 | public static string GenerateSourceCode(
142 | this OpenXmlPackage pkg, NamespaceAliasOptions opts, CodeDomProvider provider)
143 | {
144 | return pkg.GenerateSourceCode(new DefaultSerializeSettings(opts), provider);
145 | }
146 |
147 | ///
148 | /// Converts an into a representation
149 | /// of dotnet source code that can be compiled to build .
150 | ///
151 | ///
152 | /// The object to generate source code for.
153 | ///
154 | ///
155 | /// The to use during the code generation
156 | /// process.
157 | ///
158 | ///
159 | /// The object to create the resulting source code.
160 | ///
161 | ///
162 | /// A representation of the source code generated by
163 | /// that could create when compiled.
164 | ///
165 | public static string GenerateSourceCode(this OpenXmlPackage pkg,
166 | ISerializeSettings settings, CodeDomProvider provider)
167 | {
168 | var codeString = new System.Text.StringBuilder();
169 | var code = pkg.GenerateSourceCode(settings);
170 |
171 | using (var sw = new StringWriter(codeString))
172 | {
173 | provider.GenerateCodeFromCompileUnit(code, sw,
174 | new CodeGeneratorOptions() { BracingStyle = "C" });
175 | }
176 | return codeString.ToString().RemoveOutputHeaders().Trim();
177 | }
178 |
179 | ///
180 | /// Converts an into a CodeDom object that can be used
181 | /// to build code in a given .NET language to build the referenced .
182 | ///
183 | ///
184 | /// The object to generate source code for.
185 | ///
186 | ///
187 | /// Task cancellation token.
188 | ///
189 | ///
190 | /// A new containing the instructions to build
191 | /// the referenced .
192 | ///
193 | public static async Task GenerateSourceCodeAsync(
194 | this OpenXmlPackage pkg,
195 | CancellationToken token)
196 | {
197 | return await pkg.GenerateSourceCodeAsync(
198 | new DefaultSerializeSettings(),
199 | token);
200 | }
201 |
202 | ///
203 | /// Converts an into a CodeDom object that can be used
204 | /// to build code in a given .NET language to build the referenced .
205 | ///
206 | ///
207 | /// The object to generate source code for.
208 | ///
209 | ///
210 | /// The to apply to the resulting source code.
211 | ///
212 | ///
213 | /// Task cancellation token.
214 | ///
215 | ///
216 | /// A new containing the instructions to build
217 | /// the referenced .
218 | ///
219 | public static async Task GenerateSourceCodeAsync(
220 | this OpenXmlPackage pkg,
221 | NamespaceAliasOptions opts,
222 | CancellationToken token)
223 | {
224 | return await pkg.GenerateSourceCodeAsync(new DefaultSerializeSettings(opts), token);
225 | }
226 |
227 | ///
228 | /// Converts an into a CodeDom object that can be used
229 | /// to build code in a given .NET language to build the referenced .
230 | ///
231 | ///
232 | /// The object to generate source code for.
233 | ///
234 | ///
235 | /// The to use during the code generation
236 | /// process.
237 | ///
238 | ///
239 | /// Task cancellation token.
240 | ///
241 | ///
242 | /// A new containing the instructions to build
243 | /// the referenced .
244 | ///
245 | public static async Task GenerateSourceCodeAsync(
246 | this OpenXmlPackage pkg,
247 | ISerializeSettings settings,
248 | CancellationToken token)
249 | {
250 | const string pkgVarName = "pkg";
251 | const string paramName = "pathToFile";
252 |
253 | return await Task.Run(() =>
254 | {
255 | var result = new CodeCompileUnit();
256 | var pkgType = pkg.GetType();
257 | var pkgTypeName = pkgType.Name;
258 | var partTypeCounts = new Dictionary();
259 | var namespaces = new Dictionary();
260 | var mainNamespace = new CodeNamespace(settings.NamespaceName);
261 | var bluePrints = new OpenXmlPartBluePrintCollection();
262 | CodeConditionStatement conditionStatement;
263 | CodeMemberMethod entryPoint;
264 | CodeMemberMethod createParts;
265 | CodeTypeDeclaration mainClass;
266 | CodeTryCatchFinallyStatement tryAndCatch;
267 | CodeFieldReferenceExpression docTypeVarRef = null;
268 | CodeStatementCollection relCodeStatements;
269 | Type docTypeEnum;
270 | string docTypeEnumVal;
271 | KeyValuePair rootVarType;
272 |
273 | // Set the var uniqueness indicator
274 | TypeMonitor.UseUniqueVariableNames = settings.UseUniqueVariableNames;
275 |
276 | // Add all initial namespace names first
277 | if (!namespaces.ContainsKey("System"))
278 | {
279 | // Adding system first because the entry point for
280 | // packages has a string parameter that does not use
281 | // the 'string' keyword.
282 | namespaces.Add("System", String.Empty);
283 | }
284 |
285 | // The OpenXmlDocument derived parts all contain a property called "DocumentType"
286 | // but the property types differ depending on the derived part. Need to get both
287 | // the enum name of selected value to use as a parameter for the Create statement.
288 | switch (pkg)
289 | {
290 | case PresentationDocument p:
291 | docTypeEnum = p.DocumentType.GetType();
292 | docTypeEnumVal = p.DocumentType.ToString();
293 | break;
294 | case SpreadsheetDocument s:
295 | docTypeEnum = s.DocumentType.GetType();
296 | docTypeEnumVal = s.DocumentType.ToString();
297 | break;
298 | case WordprocessingDocument w:
299 | docTypeEnum = w.DocumentType.GetType();
300 | docTypeEnumVal = w.DocumentType.ToString();
301 | break;
302 | default:
303 | throw new ArgumentException(
304 | "object is not a recognized OpenXmlPackage type.",
305 | nameof(pkg));
306 | }
307 |
308 | // Check to see if the task has been cancelled.
309 | token.ThrowIfCancellationRequested();
310 |
311 | // Create the entry method
312 | entryPoint = new CodeMemberMethod()
313 | {
314 | Name = "CreatePackage",
315 | ReturnType = new CodeTypeReference(),
316 | Attributes = MemberAttributes.Public | MemberAttributes.Final
317 | };
318 | entryPoint.Parameters.Add(
319 | new CodeParameterDeclarationExpression(typeof(string).Name, paramName));
320 |
321 | // Create package declaration expression first
322 | entryPoint.Statements.Add(new CodeVariableDeclarationStatement(
323 | pkgTypeName, pkgVarName, new CodePrimitiveExpression(null)));
324 |
325 | // Add the required DocumentType parameter here, if available
326 | if (docTypeEnum != null)
327 | {
328 | if (!namespaces.ContainsKey(docTypeEnum.Namespace))
329 | {
330 | // All OpenXmlPackage objects are in the DocumentFormat.OpenXml.Packaging
331 | // namespace so there shouldn't be any collisions here.
332 | namespaces.Add(docTypeEnum.Namespace, String.Empty);
333 | }
334 |
335 | var simpleFieldRef = new CodeVariableReferenceExpression(
336 | docTypeEnum.GetObjectTypeName(namespaces,
337 | settings.NamespaceAliasOptions.Order));
338 | docTypeVarRef = new CodeFieldReferenceExpression(
339 | simpleFieldRef, docTypeEnumVal);
340 | }
341 |
342 | // initialize package var
343 | var pkgCreateMethod = new CodeMethodReferenceExpression(
344 | new CodeTypeReferenceExpression(pkgTypeName),
345 | "Create");
346 | var pkgCreateInvoke = new CodeMethodInvokeExpression(pkgCreateMethod,
347 | new CodeArgumentReferenceExpression(paramName),
348 | docTypeVarRef);
349 | var initializePkg = new CodeAssignStatement(
350 | new CodeVariableReferenceExpression(pkgVarName),
351 | pkgCreateInvoke);
352 |
353 | // Call CreateParts method
354 | var partsCreateMethod = new CodeMethodReferenceExpression(
355 | new CodeThisReferenceExpression(),
356 | "CreateParts");
357 | var partsCreateInvoke = new CodeMethodInvokeExpression(
358 | partsCreateMethod,
359 | new CodeDirectionExpression(FieldDirection.Ref,
360 | new CodeVariableReferenceExpression(pkgVarName)));
361 |
362 | // Clean up pkg var
363 | var pkgDisposeMethod = new CodeMethodReferenceExpression(
364 | new CodeVariableReferenceExpression(pkgVarName),
365 | "Dispose");
366 | var pkgDisposeInvoke = new CodeMethodInvokeExpression(
367 | pkgDisposeMethod);
368 |
369 | // Setup the try/catch statement to properly initialize the pkg var
370 | tryAndCatch = new CodeTryCatchFinallyStatement();
371 |
372 | // Try statements
373 | tryAndCatch.TryStatements.Add(initializePkg);
374 | tryAndCatch.TryStatements.AddBlankLine();
375 | tryAndCatch.TryStatements.Add(partsCreateInvoke);
376 |
377 | // If statement to ensure pkgVarName is not null before trying to dispose
378 | conditionStatement = new CodeConditionStatement(
379 | new CodeBinaryOperatorExpression(
380 | new CodeVariableReferenceExpression(pkgVarName),
381 | CodeBinaryOperatorType.IdentityInequality,
382 | new CodePrimitiveExpression(null)));
383 |
384 | conditionStatement.TrueStatements.Add(pkgDisposeInvoke);
385 |
386 | // Finally statements
387 | tryAndCatch.FinallyStatements.Add(conditionStatement);
388 | entryPoint.Statements.Add(tryAndCatch);
389 |
390 | // Check to see if the task has been cancelled.
391 | token.ThrowIfCancellationRequested();
392 |
393 | // Create the CreateParts method
394 | createParts = new CodeMemberMethod()
395 | {
396 | Name = "CreateParts",
397 | ReturnType = new CodeTypeReference(),
398 | Attributes = MemberAttributes.Private | MemberAttributes.Final
399 | };
400 | createParts.Parameters.Add(new CodeParameterDeclarationExpression(
401 | pkgTypeName, pkgVarName)
402 | { Direction = FieldDirection.Ref });
403 |
404 | relCodeStatements = pkg.GenerateRelationshipCodeStatements(pkgVarName);
405 | if (relCodeStatements.Count > 0)
406 | {
407 | createParts.Statements.AddRange(relCodeStatements);
408 | }
409 |
410 | // Add all of the child part references here
411 | if (pkg.Parts != null)
412 | {
413 | var customNewPartTypes = new Type[]
414 | {
415 | typeof(PresentationPart),
416 | typeof(WorkbookPart),
417 | typeof(MainDocumentPart)
418 | };
419 | OpenXmlPartBluePrint bpTemp;
420 | CodeMethodReferenceExpression referenceExpression;
421 | CodeMethodInvokeExpression invokeExpression;
422 | CodeMethodReferenceExpression methodReference;
423 | Type currentPartType;
424 | string varName = null;
425 | string initMethodName = null;
426 | string mainPartTypeName;
427 |
428 | foreach (var pair in pkg.Parts)
429 | {
430 | // Check to see if the task has been cancelled.
431 | token.ThrowIfCancellationRequested();
432 |
433 | // Need special handling rules for WorkbookPart, MainDocumentPart, and
434 | // PresentationPart objects. They cannot be created using the usual
435 | // "AddNewPart" methods, unfortunately.
436 | currentPartType = pair.OpenXmlPart.GetType();
437 | if (customNewPartTypes.Contains(currentPartType))
438 | {
439 | if (!namespaces.ContainsKey(currentPartType.Namespace))
440 | {
441 | // All OpenXmlPart objects are in the
442 | // DocumentFormat.OpenXml.Packaging namespace so there shouldn't
443 | // be any collisions here.
444 | namespaces.Add(currentPartType.Namespace, String.Empty);
445 | }
446 | mainPartTypeName = currentPartType.Name;
447 | if (pair.OpenXmlPart is PresentationPart)
448 | {
449 | varName = "presentationPart";
450 | initMethodName = "AddPresentationPart";
451 | }
452 | else if (pair.OpenXmlPart is WorkbookPart)
453 | {
454 | varName = "workbookPart";
455 | initMethodName = "AddWorkbookPart";
456 | }
457 | else if (pair.OpenXmlPart is MainDocumentPart)
458 | {
459 | varName = "mainDocumentPart";
460 | initMethodName = "AddMainDocumentPart";
461 | }
462 | rootVarType = new KeyValuePair(varName, currentPartType);
463 |
464 | // Setup the blueprint
465 | bpTemp = new OpenXmlPartBluePrint(pair.OpenXmlPart, varName);
466 |
467 | // Setup the add new part statement for the current OpenXmlPart object
468 | referenceExpression = new CodeMethodReferenceExpression(
469 | new CodeArgumentReferenceExpression(pkgVarName), initMethodName);
470 |
471 | invokeExpression = new CodeMethodInvokeExpression(referenceExpression);
472 |
473 | createParts.Statements.Add(new CodeVariableDeclarationStatement(
474 | mainPartTypeName, varName, invokeExpression));
475 |
476 | // Add the call to the method to populate the current OpenXmlPart object
477 | methodReference = new CodeMethodReferenceExpression(
478 | new CodeThisReferenceExpression(), bpTemp.MethodName);
479 | createParts.Statements.Add(new CodeMethodInvokeExpression(
480 | methodReference,
481 | new CodeDirectionExpression(FieldDirection.Ref,
482 | new CodeVariableReferenceExpression(varName))));
483 |
484 | // Add the current main part to the collection of blueprints to ensure
485 | // that the appropriate 'Generate*' method is added to the collection
486 | // of helper methods.
487 | bluePrints.Add(bpTemp);
488 |
489 | relCodeStatements = pair.OpenXmlPart
490 | .GenerateRelationshipCodeStatements(varName);
491 | if (relCodeStatements.Count > 0)
492 | {
493 | createParts.Statements.AddRange(relCodeStatements);
494 | }
495 |
496 | // Add a blank line for clarity
497 | createParts.Statements.AddBlankLine();
498 |
499 | // now create the child parts for the current one an continue the loop
500 | // to avoid creating an additional invalid 'AddNewPart' method for the
501 | // current main part.
502 | foreach (var child in pair.OpenXmlPart.Parts)
503 | {
504 | // Check to see if the task has been cancelled.
505 | token.ThrowIfCancellationRequested();
506 |
507 | createParts.Statements.AddRange(
508 | OpenXmlPartExtensions.BuildEntryMethodCodeStatements(
509 | child, settings, partTypeCounts, namespaces, bluePrints,
510 | rootVarType, token));
511 | }
512 | continue;
513 | }
514 |
515 | // Check to see if the task has been cancelled.
516 | token.ThrowIfCancellationRequested();
517 |
518 | rootVarType = new KeyValuePair(pkgVarName, pkgType);
519 | createParts.Statements.AddRange(
520 | OpenXmlPartExtensions.BuildEntryMethodCodeStatements(
521 | pair, settings, partTypeCounts, namespaces, bluePrints,
522 | rootVarType, token));
523 | }
524 | }
525 |
526 | // Setup the main class next
527 | mainClass = new CodeTypeDeclaration($"{pkgTypeName}BuilderClass")
528 | {
529 | IsClass = true,
530 | Attributes = MemberAttributes.Public
531 | };
532 |
533 | // Check to see if the task has been cancelled.
534 | token.ThrowIfCancellationRequested();
535 |
536 | // Setup the main class members
537 | mainClass.Members.Add(entryPoint);
538 | mainClass.Members.Add(createParts);
539 | mainClass.Members.AddRange(OpenXmlPartExtensions.BuildHelperMethods
540 | (bluePrints, settings, namespaces, token));
541 |
542 | // Setup the imports
543 | var codeNameSpaces = new List(namespaces.Count);
544 | foreach (var ns in namespaces)
545 | {
546 | if (!String.IsNullOrWhiteSpace(ns.Value))
547 | {
548 | codeNameSpaces.Add(settings.NamespaceAliasOptions.BuildNamespaceImport(
549 | ns.Key, ns.Value));
550 | }
551 | else
552 | {
553 | codeNameSpaces.Add(new CodeNamespaceImport(ns.Key));
554 | }
555 | }
556 | codeNameSpaces.Sort(new CodeNamespaceImportComparer(settings.NamespaceAliasOptions));
557 |
558 | mainNamespace.Imports.AddRange(codeNameSpaces.ToArray());
559 | mainNamespace.Types.Add(mainClass);
560 |
561 | // Finish up
562 | result.Namespaces.Add(mainNamespace);
563 | return result;
564 | }, token);
565 | }
566 |
567 | ///
568 | /// Converts an into a representation
569 | /// of dotnet source code that can be compiled to build .
570 | ///
571 | ///
572 | /// The object to generate source code for.
573 | ///
574 | ///
575 | /// The object to create the resulting source code.
576 | ///
577 | ///
578 | /// Task cancellation token.
579 | ///
580 | ///
581 | /// A representation of the source code generated by
582 | /// that could create when compiled.
583 | ///
584 | public static async Task GenerateSourceCodeAsync(
585 | this OpenXmlPackage pkg,
586 | CodeDomProvider provider,
587 | CancellationToken token)
588 | {
589 | return await pkg.GenerateSourceCodeAsync(
590 | new DefaultSerializeSettings(), provider, token);
591 | }
592 |
593 | ///
594 | /// Converts an into a representation
595 | /// of dotnet source code that can be compiled to build .
596 | ///
597 | ///
598 | /// The object to generate source code for.
599 | ///
600 | ///
601 | /// The to apply to the resulting source code.
602 | ///
603 | ///
604 | /// The object to create the resulting source code.
605 | ///
606 | ///
607 | /// Task cancellation token.
608 | ///
609 | ///
610 | /// A representation of the source code generated by
611 | /// that could create when compiled.
612 | ///
613 | public static async Task GenerateSourceCodeAsync(
614 | this OpenXmlPackage pkg,
615 | NamespaceAliasOptions opts,
616 | CodeDomProvider provider,
617 | CancellationToken token)
618 | {
619 | return await pkg.GenerateSourceCodeAsync(
620 | new DefaultSerializeSettings(opts), provider, token);
621 | }
622 |
623 | ///
624 | /// Converts an into a representation
625 | /// of dotnet source code that can be compiled to build .
626 | ///
627 | ///
628 | /// The object to generate source code for.
629 | ///
630 | ///
631 | /// The to use during the code generation
632 | /// process.
633 | ///
634 | ///
635 | /// The object to create the resulting source code.
636 | ///
637 | ///
638 | /// Task cancellation token.
639 | ///
640 | ///
641 | /// A representation of the source code generated by
642 | /// that could create when compiled.
643 | ///
644 | public static async Task GenerateSourceCodeAsync(
645 | this OpenXmlPackage pkg,
646 | ISerializeSettings settings,
647 | CodeDomProvider provider,
648 | CancellationToken token)
649 | {
650 | var codeString = new System.Text.StringBuilder();
651 | var code = await pkg.GenerateSourceCodeAsync(settings, token);
652 |
653 | using (var sw = new StringWriter(codeString))
654 | {
655 | provider.GenerateCodeFromCompileUnit(code, sw,
656 | new CodeGeneratorOptions() { BracingStyle = "C" });
657 | }
658 | return codeString.ToString().RemoveOutputHeaders().Trim();
659 | }
660 |
661 | #endregion
662 | }
663 | }
--------------------------------------------------------------------------------
/src/Serialize.OpenXml.CodeGen/OpenXmlPartExtensions.cs:
--------------------------------------------------------------------------------
1 | /* MIT License
2 |
3 | Copyright (c) 2020 Ryan Boggs
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy of this
6 | software and associated documentation files (the "Software"), to deal in the Software
7 | without restriction, including without limitation the rights to use, copy, modify,
8 | merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
9 | permit persons to whom the Software is furnished to do so, subject to the following
10 | conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all copies
13 | or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
16 | INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
17 | PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
18 | FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
19 | OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
20 | DEALINGS IN THE SOFTWARE.
21 | */
22 |
23 | using DocumentFormat.OpenXml.Packaging;
24 | using Serialize.OpenXml.CodeGen.Extentions;
25 | using System;
26 | using System.CodeDom;
27 | using System.CodeDom.Compiler;
28 | using System.Collections.Generic;
29 | using System.IO;
30 | using System.Linq;
31 | using System.Reflection;
32 | using System.Threading;
33 | using System.Threading.Tasks;
34 |
35 | namespace Serialize.OpenXml.CodeGen
36 | {
37 | ///
38 | /// Static class that converts objects
39 | /// into Code DOM objects.
40 | ///
41 | public static class OpenXmlPartExtensions
42 | {
43 | #region Private Static Fields
44 |
45 | ///
46 | /// The default parameter name for an object.
47 | ///
48 | private const string methodParamName = "part";
49 |
50 | #endregion
51 |
52 | #region Public Static Methods
53 |
54 | ///
55 | /// Creates the appropriate code objects needed to create the entry method for the
56 | /// current request.
57 | ///
58 | ///
59 | /// The object and relationship id to build code for.
60 | ///
61 | ///
62 | /// The to use during the code generation
63 | /// process.
64 | ///
65 | /// ///
66 | /// A lookup object containing the
67 | /// number of times a given type was referenced. This is used for variable naming
68 | /// purposes.
69 | ///
70 | ///
71 | /// used to keep track of all openxml
72 | /// namespaces used during the process.
73 | ///
74 | ///
75 | /// The collection of objects that have already been
76 | /// visited.
77 | ///
78 | ///
79 | /// The root variable name and to use when building code
80 | /// statements to create new objects.
81 | ///
82 | ///
83 | /// Task cancellation token from the parent method.
84 | ///
85 | ///
86 | /// A collection of code statements and expressions that could be used to generate
87 | /// a new object from code.
88 | ///
89 | public static CodeStatementCollection BuildEntryMethodCodeStatements(
90 | IdPartPair part,
91 | ISerializeSettings settings,
92 | IDictionary typeCounts,
93 | IDictionary namespaces,
94 | OpenXmlPartBluePrintCollection blueprints,
95 | KeyValuePair rootVar,
96 | CancellationToken token)
97 | {
98 | // Argument validation
99 | if (settings is null) throw new ArgumentNullException(nameof(settings));
100 | if (blueprints is null) throw new ArgumentNullException(nameof(blueprints));
101 |
102 | if (String.IsNullOrWhiteSpace(rootVar.Key))
103 | {
104 | throw new ArgumentNullException(nameof(rootVar.Key));
105 | }
106 |
107 | bool hasHandlers = settings?.Handlers != null;
108 |
109 | // Check to see if the task has been cancelled.
110 | token.ThrowIfCancellationRequested();
111 |
112 | // Use the custom handler methods if present and provide actual code
113 | if (hasHandlers && settings.Handlers.TryGetValue(part.OpenXmlPart.GetType(),
114 | out IOpenXmlHandler h))
115 | {
116 | if (h is IOpenXmlPartHandler partHandler)
117 | {
118 | var customStatements = partHandler.BuildEntryMethodCodeStatements(
119 | part, settings, typeCounts, namespaces, blueprints, rootVar, token);
120 |
121 | if (customStatements != null) return customStatements;
122 | }
123 | }
124 |
125 | var result = new CodeStatementCollection();
126 | var partType = part.OpenXmlPart.GetType();
127 |
128 | CodeMethodReferenceExpression referenceExpression;
129 | CodeMethodInvokeExpression invokeExpression;
130 | CodeMethodReferenceExpression methodReference;
131 |
132 | // Check to see if the task has been cancelled.
133 | if (token.IsCancellationRequested)
134 | {
135 | result.Clear();
136 | throw new OperationCanceledException(token);
137 | }
138 |
139 | // Make sure that the namespace for the current part is captured
140 | if (!namespaces.ContainsKey(partType.Namespace))
141 | {
142 | // All OpenXmlPart objects are in the DocumentFormat.OpenXml.Packaging
143 | // namespace so there shouldn't be any collisions here.
144 | namespaces.Add(partType.Namespace, String.Empty);
145 | }
146 |
147 | // If the URI of the current part has already been included into
148 | // the blue prints collection, build the AddPart invocation
149 | // code statement and exit current method iteration.
150 | if (blueprints.TryGetValue(part.OpenXmlPart.Uri, out OpenXmlPartBluePrint bpTemp))
151 | {
152 | // Surround this snippet with blank lines to make it
153 | // stand out in the current section of code.
154 | result.AddBlankLine();
155 | referenceExpression = new CodeMethodReferenceExpression(
156 | new CodeVariableReferenceExpression(rootVar.Key), "AddPart",
157 | new CodeTypeReference(part.OpenXmlPart.GetType().Name));
158 | invokeExpression = new CodeMethodInvokeExpression(referenceExpression,
159 | new CodeVariableReferenceExpression(bpTemp.VariableName),
160 | new CodePrimitiveExpression(part.RelationshipId));
161 | result.Add(invokeExpression);
162 | result.AddBlankLine();
163 | return result;
164 | }
165 |
166 | var partTypeName = partType.Name;
167 | var partTypeFullName = partType.FullName;
168 | string varName = partType.Name.ToCamelCase();
169 |
170 | // Assign the appropriate variable name
171 | if (typeCounts.ContainsKey(partTypeFullName))
172 | {
173 | varName = String.Concat(varName, typeCounts[partTypeFullName]++);
174 | }
175 | else
176 | {
177 | typeCounts.Add(partTypeFullName, 1);
178 | }
179 |
180 | // Setup the blueprint
181 | bpTemp = new OpenXmlPartBluePrint(part.OpenXmlPart, varName);
182 |
183 | // Need to evaluate the current OpenXmlPart type first to make sure the
184 | // correct "Add" statement is used as not all Parts can be initialized
185 | // using the "AddNewPart" method
186 | var addNewPartExpressions = CreateAddNewPartMethod(part, rootVar);
187 | // referenceExpression = addNewPartExpressions.Item1;
188 | invokeExpression = addNewPartExpressions.Item2;
189 |
190 | result.Add(new CodeVariableDeclarationStatement(
191 | partTypeName, varName, invokeExpression));
192 |
193 | // Because the custom AddNewPart methods don't consistently take in a string relId
194 | // as a parameter, the id needs to be assigned after it is created.
195 | if (addNewPartExpressions.Item3)
196 | {
197 | methodReference = new CodeMethodReferenceExpression(
198 | new CodeVariableReferenceExpression(rootVar.Key), "ChangeIdOfPart");
199 | result.Add(new CodeMethodInvokeExpression(methodReference,
200 | new CodeVariableReferenceExpression(varName),
201 | new CodePrimitiveExpression(part.RelationshipId)));
202 | }
203 |
204 | // Add the call to the method to populate the current OpenXmlPart object
205 | methodReference = new CodeMethodReferenceExpression(new CodeThisReferenceExpression(),
206 | bpTemp.MethodName);
207 | result.Add(new CodeMethodInvokeExpression(methodReference,
208 | new CodeDirectionExpression(FieldDirection.Ref,
209 | new CodeVariableReferenceExpression(varName))));
210 |
211 | // Add the relationships, if applicable
212 | var relCodeStatements = part.OpenXmlPart.GenerateRelationshipCodeStatements(varName);
213 |
214 | if (relCodeStatements.Count > 0)
215 | {
216 | result.AddRange(relCodeStatements);
217 | }
218 |
219 | // put a line break before going through the child parts
220 | result.AddBlankLine();
221 |
222 | // Add the current blueprint to the collection
223 | blueprints.Add(bpTemp);
224 |
225 | // Now check to see if there are any child parts for the current OpenXmlPart object.
226 | if (bpTemp.Part.Parts != null)
227 | {
228 | OpenXmlPartBluePrint childBluePrint;
229 |
230 | foreach (var p in bpTemp.Part.Parts)
231 | {
232 | // Check to see if the task has been cancelled.
233 | if (token.IsCancellationRequested)
234 | {
235 | result.Clear();
236 | throw new OperationCanceledException(token);
237 | }
238 |
239 | // If the current child object has already been created, simply add a reference
240 | // to said object using the AddPart method.
241 | if (blueprints.Contains(p.OpenXmlPart.Uri))
242 | {
243 | childBluePrint = blueprints[p.OpenXmlPart.Uri];
244 |
245 | referenceExpression = new CodeMethodReferenceExpression(
246 | new CodeVariableReferenceExpression(varName), "AddPart",
247 | new CodeTypeReference(p.OpenXmlPart.GetType().Name));
248 |
249 | invokeExpression = new CodeMethodInvokeExpression(referenceExpression,
250 | new CodeVariableReferenceExpression(childBluePrint.VariableName),
251 | new CodePrimitiveExpression(p.RelationshipId));
252 |
253 | result.Add(invokeExpression);
254 | continue;
255 | }
256 |
257 | // If this is a new part, call this method with the current part's details
258 | result.AddRange(BuildEntryMethodCodeStatements(
259 | p, settings, typeCounts, namespaces, blueprints,
260 | new KeyValuePair(varName, partType), token));
261 | }
262 | }
263 |
264 | return result;
265 | }
266 |
267 | ///
268 | /// Creates the appropriate helper methods for all of the objects
269 | /// for the current request.
270 | ///
271 | ///
272 | /// The collection of objects that have already been
273 | /// visited.
274 | ///
275 | ///
276 | /// The to use during the code generation
277 | /// process.
278 | ///
279 | ///
280 | /// collection used to keep track of all openxml
281 | /// namespaces used during the process.
282 | ///
283 | ///
284 | /// Task cancellation token from the parent method.
285 | ///
286 | ///
287 | /// A collection of code helper statements and expressions that could be used to generate a
288 | /// new object from code.
289 | ///
290 | public static CodeTypeMemberCollection BuildHelperMethods(
291 | OpenXmlPartBluePrintCollection bluePrints,
292 | ISerializeSettings settings,
293 | IDictionary namespaces,
294 | CancellationToken token)
295 | {
296 | if (bluePrints == null) throw new ArgumentNullException(nameof(bluePrints));
297 | var result = new CodeTypeMemberCollection();
298 | var localTypes = new TypeMonitorCollection();
299 | Type rootElementType;
300 | CodeMemberMethod method;
301 | bool hasHandlers = settings?.Handlers != null;
302 |
303 | foreach (var bp in bluePrints)
304 | {
305 | // Check to see if the task has been cancelled.
306 | if (token.IsCancellationRequested)
307 | {
308 | result.Clear();
309 | throw new OperationCanceledException(token);
310 | }
311 |
312 | // Implement the custom helper if present
313 | if (hasHandlers && settings.Handlers.TryGetValue(bp.PartType, out IOpenXmlHandler h))
314 | {
315 | if (h is IOpenXmlPartHandler partHandler)
316 | {
317 | method = partHandler.BuildHelperMethod(
318 | bp.Part, bp.MethodName, settings, namespaces, token);
319 |
320 | if (method != null)
321 | {
322 | result.Add(method);
323 | continue;
324 | }
325 | }
326 | }
327 |
328 | // Setup the first method
329 | method = new CodeMemberMethod()
330 | {
331 | Name = bp.MethodName,
332 | Attributes = MemberAttributes.Private | MemberAttributes.Final,
333 | ReturnType = new CodeTypeReference()
334 | };
335 | method.Parameters.Add(
336 | new CodeParameterDeclarationExpression(bp.Part.GetType().Name, methodParamName)
337 | { Direction = FieldDirection.Ref });
338 |
339 | // Code part elements next
340 | if (bp.Part.RootElement is null)
341 | {
342 | // Put all of the pieces together
343 | method.Statements.AddRange(bp.Part.BuildPartFeedData(namespaces));
344 | }
345 | else
346 | {
347 | rootElementType = bp.Part.RootElement?.GetType();
348 | localTypes.Clear();
349 |
350 | // Build the element details of the requested part for the current method
351 | method.Statements.AddRange(
352 | bp.Part.RootElement.BuildCodeStatements(
353 | settings, localTypes, namespaces, token, out string rootElementVar));
354 |
355 | // Now finish up the current method by assigning the OpenXmlElement code
356 | // statements back to the appropriate property of the part parameter
357 | if (rootElementType != null && !String.IsNullOrWhiteSpace(rootElementVar))
358 | {
359 | foreach (var paramProp in bp.Part.GetType().GetProperties())
360 | {
361 | // Check to see if the task has been cancelled.
362 | if (token.IsCancellationRequested)
363 | {
364 | result.Clear();
365 | throw new OperationCanceledException(token);
366 | }
367 |
368 | if (paramProp.PropertyType == rootElementType)
369 | {
370 | var varRef = new CodeVariableReferenceExpression(rootElementVar);
371 | var paramRef = new CodeArgumentReferenceExpression(methodParamName);
372 | var propRef = new CodePropertyReferenceExpression(
373 | paramRef, paramProp.Name);
374 |
375 | method.Statements.Add(new CodeAssignStatement(propRef, varRef));
376 | break;
377 | }
378 | }
379 | }
380 | }
381 | result.Add(method);
382 | }
383 |
384 | return result;
385 | }
386 |
387 | ///
388 | /// Builds code statements that will build using the
389 | /// method.
390 | ///
391 | ///
392 | /// The object to build the source code for.
393 | ///
394 | ///
395 | /// values used to keep
396 | /// track of all openxml namespaces used during the process.
397 | ///
398 | ///
399 | /// A collection of code statements
400 | /// that would regenerate using the
401 | /// method.
402 | ///
403 | public static CodeStatementCollection BuildPartFeedData(
404 | this OpenXmlPart part,
405 | IDictionary namespaces)
406 | {
407 | // Make sure no null values were passed.
408 | if (part == null) throw new ArgumentNullException(nameof(part));
409 | if (namespaces == null) throw new ArgumentNullException(nameof(namespaces));
410 |
411 | // If the root element is not present (aka: null) then perform a simple feed
412 | // dump of the part in the current method
413 | const string memName = "mem";
414 | const string b64Name = "base64";
415 |
416 | var result = new CodeStatementCollection();
417 |
418 | // Add the necessary namespaces by hand to the namespace set
419 | if (!namespaces.ContainsKey("System"))
420 | {
421 | namespaces.Add("System", String.Empty);
422 | }
423 | if (!namespaces.ContainsKey("System.IO"))
424 | {
425 | namespaces.Add("System.IO", String.Empty);
426 | }
427 |
428 | using (var partStream = part.GetStream(FileMode.Open, FileAccess.Read))
429 | {
430 | using (var mem = new MemoryStream())
431 | {
432 | partStream.CopyTo(mem);
433 | result.Add(new CodeVariableDeclarationStatement(typeof(string), b64Name,
434 | new CodePrimitiveExpression(Convert.ToBase64String(mem.ToArray()))));
435 | }
436 | }
437 | result.AddBlankLine();
438 |
439 | var fromBase64 = new CodeMethodReferenceExpression(
440 | new CodeVariableReferenceExpression("Convert"),
441 | "FromBase64String");
442 | var invokeFromBase64 = new CodeMethodInvokeExpression(fromBase64,
443 | new CodeVariableReferenceExpression("base64"));
444 | var createStream = new CodeObjectCreateExpression(new CodeTypeReference("MemoryStream"),
445 | invokeFromBase64, new CodePrimitiveExpression(false));
446 | var feedData = new CodeMethodReferenceExpression(
447 | new CodeArgumentReferenceExpression(methodParamName), "FeedData");
448 | var invokeFeedData = new CodeMethodInvokeExpression(
449 | feedData, new CodeVariableReferenceExpression(memName));
450 | var disposeMem = new CodeMethodReferenceExpression(
451 | new CodeVariableReferenceExpression(memName), "Dispose");
452 | var invokeDisposeMem = new CodeMethodInvokeExpression(disposeMem);
453 |
454 | // Setup the try statement
455 | var tryAndCatch = new CodeTryCatchFinallyStatement();
456 | tryAndCatch.TryStatements.Add(invokeFeedData);
457 | tryAndCatch.FinallyStatements.Add(invokeDisposeMem);
458 |
459 | // Put all of the pieces together
460 | result.Add(new CodeVariableDeclarationStatement("Stream", memName, createStream));
461 | result.Add(tryAndCatch);
462 |
463 | return result;
464 | }
465 |
466 | ///
467 | /// Converts an into a CodeDom object that can be used
468 | /// to build code in a given .NET language to build the referenced .
469 | ///
470 | ///
471 | /// The object to generate source code for.
472 | ///
473 | ///
474 | /// A new containing the instructions to build
475 | /// the referenced .
476 | ///
477 | public static CodeCompileUnit GenerateSourceCode(this OpenXmlPart part)
478 | {
479 | return part.GenerateSourceCode(new DefaultSerializeSettings());
480 | }
481 |
482 | ///
483 | /// Converts an into a CodeDom object that can be used
484 | /// to build code in a given .NET language to build the referenced .
485 | ///
486 | ///
487 | /// The object to generate source code for.
488 | ///
489 | ///
490 | /// The to apply to the resulting source code.
491 | ///
492 | ///
493 | /// A new containing the instructions to build
494 | /// the referenced .
495 | ///
496 | public static CodeCompileUnit GenerateSourceCode(this OpenXmlPart part, NamespaceAliasOptions opts)
497 | {
498 | return part.GenerateSourceCode(new DefaultSerializeSettings(opts));
499 | }
500 |
501 | ///
502 | /// Converts an into a CodeDom object that can be used
503 | /// to build code in a given .NET language to build the referenced .
504 | ///
505 | ///
506 | /// The object to generate source code for.
507 | ///
508 | ///
509 | /// The to use during the code generation
510 | /// process.
511 | ///
512 | ///
513 | /// A new containing the instructions to build
514 | /// the referenced .
515 | ///
516 | public static CodeCompileUnit GenerateSourceCode(
517 | this OpenXmlPart part, ISerializeSettings settings)
518 | {
519 | return DefaultSerializeSettings.TaskIndustry.StartNew(
520 | () => part.GenerateSourceCodeAsync(settings, CancellationToken.None))
521 | .Unwrap()
522 | .GetAwaiter()
523 | .GetResult();
524 | }
525 |
526 | ///
527 | /// Converts an into a representation
528 | /// of dotnet source code that can be compiled to build .
529 | ///
530 | ///
531 | /// The object to generate source code for.
532 | ///
533 | ///
534 | /// The object to create the resulting source code.
535 | ///
536 | ///
537 | /// A representation of the source code generated by
538 | /// that could create when compiled.
539 | ///
540 | public static string GenerateSourceCode(this OpenXmlPart part, CodeDomProvider provider)
541 | {
542 | return part.GenerateSourceCode(new DefaultSerializeSettings(), provider);
543 | }
544 |
545 | ///
546 | /// Converts an into a representation
547 | /// of dotnet source code that can be compiled to build .
548 | ///
549 | ///
550 | /// The object to generate source code for.
551 | ///
552 | ///
553 | /// The to apply to the resulting source code.
554 | ///
555 | ///
556 | /// The object to create the resulting source code.
557 | ///
558 | ///
559 | /// A representation of the source code generated by
560 | /// that could create when compiled.
561 | ///
562 | public static string GenerateSourceCode(
563 | this OpenXmlPart part, NamespaceAliasOptions opts, CodeDomProvider provider)
564 | {
565 | return part.GenerateSourceCode(new DefaultSerializeSettings(opts), provider);
566 | }
567 |
568 | ///
569 | /// Converts an into a representation
570 | /// of dotnet source code that can be compiled to build .
571 | ///
572 | ///
573 | /// The object to generate source code for.
574 | ///
575 | ///
576 | /// The to use during the code generation
577 | /// process.
578 | ///
579 | ///
580 | /// The object to create the resulting source code.
581 | ///
582 | ///
583 | /// A representation of the source code generated by
584 | /// that could create when compiled.
585 | ///
586 | public static string GenerateSourceCode(
587 | this OpenXmlPart part, ISerializeSettings settings, CodeDomProvider provider)
588 | {
589 | var codeString = new System.Text.StringBuilder();
590 | var code = part.GenerateSourceCode(settings);
591 |
592 | using (var sw = new StringWriter(codeString))
593 | {
594 | provider.GenerateCodeFromCompileUnit(code, sw,
595 | new CodeGeneratorOptions() { BracingStyle = "C" });
596 | }
597 | return codeString.ToString().RemoveOutputHeaders().Trim();
598 | }
599 |
600 | ///
601 | /// Converts an into a CodeDom object that can be used
602 | /// to build code in a given .NET language to build the referenced .
603 | ///
604 | ///
605 | /// The object to generate source code for.
606 | ///
607 | ///
608 | /// Task cancellation token.
609 | ///
610 | ///
611 | /// A new containing the instructions to build
612 | /// the referenced .
613 | ///
614 | public static async Task GenerateSourceCodeAsync(
615 | this OpenXmlPart part,
616 | CancellationToken token)
617 | {
618 | return await part.GenerateSourceCodeAsync(new DefaultSerializeSettings(), token);
619 | }
620 |
621 | ///
622 | /// Converts an into a CodeDom object that can be used
623 | /// to build code in a given .NET language to build the referenced .
624 | ///
625 | ///
626 | /// The object to generate source code for.
627 | ///
628 | ///
629 | /// The to apply to the resulting source code.
630 | ///
631 | ///
632 | /// Task cancellation token.
633 | ///
634 | ///
635 | /// A new containing the instructions to build
636 | /// the referenced .
637 | ///
638 | public static async Task GenerateSourceCodeAsync(
639 | this OpenXmlPart part,
640 | NamespaceAliasOptions opts,
641 | CancellationToken token)
642 | {
643 | return await part.GenerateSourceCodeAsync(new DefaultSerializeSettings(opts), token);
644 | }
645 |
646 | ///
647 | /// Converts an into a CodeDom object that can be used
648 | /// to build code in a given .NET language to build the referenced .
649 | ///
650 | ///
651 | /// The object to generate source code for.
652 | ///
653 | ///
654 | /// The to use during the code generation
655 | /// process.
656 | ///
657 | ///
658 | /// Task cancellation token.
659 | ///
660 | ///
661 | /// A new containing the instructions to build
662 | /// the referenced .
663 | ///
664 | public static async Task GenerateSourceCodeAsync(
665 | this OpenXmlPart part,
666 | ISerializeSettings settings,
667 | CancellationToken token)
668 | {
669 | return await Task.Run(() =>
670 | {
671 | CodeMethodReferenceExpression methodRef;
672 | OpenXmlPartBluePrint mainBluePrint;
673 | var result = new CodeCompileUnit();
674 | var eType = part.GetType();
675 | var partTypeName = eType.Name;
676 | var partTypeFullName = eType.FullName;
677 | var varName = eType.Name.ToCamelCase();
678 | var partTypeCounts = new Dictionary();
679 | var namespaces = new Dictionary();
680 | var mainNamespace = new CodeNamespace(settings.NamespaceName);
681 | var bluePrints = new OpenXmlPartBluePrintCollection();
682 |
683 | // Set the var uniqueness indicator
684 | TypeMonitor.UseUniqueVariableNames = settings.UseUniqueVariableNames;
685 |
686 | // Assign the appropriate variable name
687 | if (partTypeCounts.ContainsKey(partTypeFullName))
688 | {
689 | varName = String.Concat(varName, partTypeCounts[partTypeFullName]++);
690 | }
691 | else
692 | {
693 | partTypeCounts.Add(partTypeFullName, 1);
694 | }
695 |
696 | // Generate a new blue print for the current part to help create the main
697 | // method reference then add it to the blue print collection
698 | mainBluePrint = new OpenXmlPartBluePrint(part, varName);
699 | bluePrints.Add(mainBluePrint);
700 | methodRef = new CodeMethodReferenceExpression(
701 | new CodeThisReferenceExpression(), mainBluePrint.MethodName);
702 |
703 | // Build the entry method
704 | var entryMethod = new CodeMemberMethod()
705 | {
706 | Name = $"Create{partTypeName}",
707 | ReturnType = new CodeTypeReference(),
708 | Attributes = MemberAttributes.Public | MemberAttributes.Final
709 | };
710 | entryMethod.Parameters.Add(
711 | new CodeParameterDeclarationExpression(partTypeName, methodParamName)
712 | { Direction = FieldDirection.Ref });
713 |
714 | // Check to see if the task has been cancelled.
715 | token.ThrowIfCancellationRequested();
716 |
717 | var relCodeStatements = part.GenerateRelationshipCodeStatements(
718 | new CodeThisReferenceExpression());
719 | if (relCodeStatements.Count > 0)
720 | {
721 | entryMethod.Statements.AddRange(relCodeStatements);
722 | }
723 |
724 | // Add all of the child part references here
725 | if (part.Parts != null)
726 | {
727 | var rootPartPair = new KeyValuePair(methodParamName, eType);
728 | foreach (var pair in part.Parts)
729 | {
730 | // Check to see if the task has been cancelled.
731 | token.ThrowIfCancellationRequested();
732 |
733 | entryMethod.Statements.AddRange(BuildEntryMethodCodeStatements(
734 | pair,
735 | settings,
736 | partTypeCounts,
737 | namespaces,
738 | bluePrints,
739 | rootPartPair,
740 | token));
741 | }
742 | }
743 |
744 | entryMethod.Statements.Add(new CodeMethodInvokeExpression(methodRef,
745 | new CodeArgumentReferenceExpression(methodParamName)));
746 |
747 | // Setup the main class next
748 | var mainClass = new CodeTypeDeclaration($"{eType.Name}BuilderClass")
749 | {
750 | IsClass = true,
751 | Attributes = MemberAttributes.Public
752 | };
753 | mainClass.Members.Add(entryMethod);
754 | mainClass.Members.AddRange(BuildHelperMethods(
755 | bluePrints, settings, namespaces, token));
756 |
757 | // Setup the imports
758 | var codeNameSpaces = new List(namespaces.Count);
759 | foreach (var ns in namespaces)
760 | {
761 | // Check to see if the task has been cancelled.
762 | token.ThrowIfCancellationRequested();
763 |
764 | if (!String.IsNullOrWhiteSpace(ns.Value))
765 | {
766 | codeNameSpaces.Add(settings.NamespaceAliasOptions.BuildNamespaceImport(
767 | ns.Key, ns.Value));
768 | }
769 | else
770 | {
771 | codeNameSpaces.Add(new CodeNamespaceImport(ns.Key));
772 | }
773 | }
774 | codeNameSpaces.Sort(new CodeNamespaceImportComparer(settings.NamespaceAliasOptions));
775 |
776 | mainNamespace.Imports.AddRange(codeNameSpaces.ToArray());
777 | mainNamespace.Types.Add(mainClass);
778 |
779 | // Finish up
780 | result.Namespaces.Add(mainNamespace);
781 | return result;
782 | }, token);
783 | }
784 |
785 | ///
786 | /// Converts an into a representation
787 | /// of dotnet source code that can be compiled to build .
788 | ///
789 | ///
790 | /// The object to generate source code for.
791 | ///
792 | ///
793 | /// The object to create the resulting source code.
794 | ///
795 | ///
796 | /// Task cancellation token.
797 | ///
798 | ///
799 | /// A representation of the source code generated by
800 | /// that could create when compiled.
801 | ///
802 | public static async Task GenerateSourceCodeAsync(
803 | this OpenXmlPart part,
804 | CodeDomProvider provider,
805 | CancellationToken token)
806 | {
807 | return await part.GenerateSourceCodeAsync(
808 | new DefaultSerializeSettings(), provider, token);
809 | }
810 |
811 | ///
812 | /// Converts an into a representation
813 | /// of dotnet source code that can be compiled to build .
814 | ///
815 | ///
816 | /// The object to generate source code for.
817 | ///
818 | ///
819 | /// The to apply to the resulting source code.
820 | ///
821 | ///
822 | /// The object to create the resulting source code.
823 | ///
824 | ///
825 | /// Task cancellation token.
826 | ///
827 | ///
828 | /// A representation of the source code generated by
829 | /// that could create when compiled.
830 | ///
831 | public static async Task GenerateSourceCodeAsync(
832 | this OpenXmlPart part,
833 | NamespaceAliasOptions opts,
834 | CodeDomProvider provider,
835 | CancellationToken token)
836 | {
837 | return await part.GenerateSourceCodeAsync(
838 | new DefaultSerializeSettings(opts), provider, token);
839 | }
840 |
841 | ///
842 | /// Converts an into a representation
843 | /// of dotnet source code that can be compiled to build .
844 | ///
845 | ///
846 | /// The object to generate source code for.
847 | ///
848 | ///
849 | /// The to use during the code generation
850 | /// process.
851 | ///
852 | ///
853 | /// The object to create the resulting source code.
854 | ///
855 | ///
856 | /// Task cancellation token.
857 | ///
858 | ///
859 | /// A representation of the source code generated by
860 | /// that could create when compiled.
861 | ///
862 | public static async Task GenerateSourceCodeAsync(
863 | this OpenXmlPart part,
864 | ISerializeSettings settings,
865 | CodeDomProvider provider,
866 | CancellationToken token)
867 | {
868 | var codeString = new System.Text.StringBuilder();
869 | var code = await part.GenerateSourceCodeAsync(settings, token);
870 |
871 | using (var sw = new StringWriter(codeString))
872 | {
873 | provider.GenerateCodeFromCompileUnit(code, sw,
874 | new CodeGeneratorOptions() { BracingStyle = "C" });
875 | }
876 | return codeString.ToString().RemoveOutputHeaders().Trim();
877 | }
878 |
879 | #endregion
880 |
881 | #region Private Static Methods
882 |
883 | ///
884 | /// Constructs the appropriate and
885 | /// objects for adding new parts
886 | /// to an existing object.
887 | ///
888 | ///
889 | /// The object and relationship id to build
890 | /// the code expressions for.
891 | ///
892 | ///
893 | /// The root variable name and to use when building code
894 | /// statements to create new objects.
895 | ///
896 | ///
897 | /// A tuple containing the AddNewPart code expressions.
898 | ///
899 | private static (CodeMethodReferenceExpression, CodeMethodInvokeExpression, bool)
900 | CreateAddNewPartMethod(
901 | IdPartPair part, KeyValuePair rootVar)
902 | {
903 | CodeMethodReferenceExpression item1 = null;
904 | CodeMethodInvokeExpression item2 = null;
905 | var pType = part.OpenXmlPart.GetType();
906 | var checkName = $"Add{pType.Name}";
907 | bool check(MethodInfo m) => m.Name.Equals(checkName, StringComparison.OrdinalIgnoreCase);
908 | var methods = rootVar.Value.GetMethods().Where(check).ToArray();
909 |
910 | // If no custom AddNewPart methods exist for the requested part, revert back to
911 | // the default AddNewPart method.
912 | if (methods.Length == 0)
913 | {
914 | item1 = new CodeMethodReferenceExpression(
915 | new CodeVariableReferenceExpression(rootVar.Key), "AddNewPart",
916 | new CodeTypeReference(pType.Name));
917 | item2 = new CodeMethodInvokeExpression(item1);
918 | item2.Parameters.Add(new CodePrimitiveExpression(part.RelationshipId));
919 |
920 | return (item1, item2, false);
921 | }
922 |
923 | // Initialize the custom AddNewPart method reference and invoke expresstions
924 | // before examining the method parameters
925 | item1 = new CodeMethodReferenceExpression(
926 | new CodeVariableReferenceExpression(rootVar.Key), checkName);
927 | item2 = new CodeMethodInvokeExpression(item1);
928 |
929 | // Now figure out what parameters are needed for this method
930 | ParameterInfo[] parameters;
931 | foreach (var m in methods)
932 | {
933 | // Please Note: To my knowledge, the custom AddNewPart methods have 2 main
934 | // types of method parameters common among all custom methods:
935 | // * Default (no parameters)
936 | // * string contentType
937 | // This loop should be looking for the second type primarily and reverting
938 | // to the first type if not found.
939 | parameters = m.GetParameters();
940 |
941 | // Methods with 1 string parameter should be the contentType parameter method
942 | if (parameters != null &&
943 | parameters.Length == 1 &&
944 | parameters[0].ParameterType == typeof(string))
945 | {
946 | item2.Parameters.Add(
947 | new CodePrimitiveExpression(part.OpenXmlPart.ContentType));
948 | break;
949 | }
950 | }
951 | return (item1, item2, true);
952 | }
953 |
954 | #endregion
955 | }
956 | }
--------------------------------------------------------------------------------