├── .gitattributes
├── .gitignore
├── LICENSE
├── ObjectMapper
├── ObjectMapper.Framework
│ ├── Extensions
│ │ ├── CollectionExtensions.cs
│ │ └── ObjectMapperExtensions.cs
│ ├── IObjectMapper.cs
│ ├── IObjectMapperAdapter.cs
│ ├── ObjectMapper.Framework.csproj
│ ├── ObjectMapperMethodAttribute.cs
│ └── Properties
│ │ └── AssemblyInfo.cs
├── ObjectMapper.GenerateImplementationAnalyzer
│ ├── ObjectMapper.GenerateImplementationAnalyzer.Test
│ │ ├── AttributeUsageUnitTests.cs
│ │ ├── GenerateImplementationUnitTests.cs
│ │ ├── Helpers
│ │ │ ├── CodeFixVerifier.Helper.cs
│ │ │ ├── DiagnosticResult.cs
│ │ │ └── DiagnosticVerifier.Helper.cs
│ │ ├── ObjectMapper.GenerateImplementationAnalyzer.Test.csproj
│ │ ├── Properties
│ │ │ └── AssemblyInfo.cs
│ │ ├── Verifiers
│ │ │ ├── CodeFixVerifier.cs
│ │ │ └── DiagnosticVerifier.cs
│ │ └── packages.config
│ ├── ObjectMapper.GenerateImplementationAnalyzer.Vsix
│ │ ├── ObjectMapper.GenerateImplementationAnalyzer.Vsix.csproj
│ │ └── source.extension.vsixmanifest
│ └── ObjectMapper.GenerateImplementationAnalyzer
│ │ ├── AttributeUsageCodeAnalyzer.cs
│ │ ├── Diagnostic.nuspec
│ │ ├── GenerateImplementationCodeAnalyzer.cs
│ │ ├── GenerateImplementationCodeGenerator.cs
│ │ ├── ObjectMapper.GenerateImplementationAnalyzer.csproj
│ │ ├── Properties
│ │ └── AssemblyInfo.cs
│ │ ├── Resources.Designer.cs
│ │ ├── Resources.resx
│ │ ├── Utilities
│ │ └── FrameworkHelpers.cs
│ │ ├── lib
│ │ └── ObjectMapper.Framework.dll
│ │ ├── packages.config
│ │ └── tools
│ │ ├── install.ps1
│ │ └── uninstall.ps1
└── ObjectMapper.sln
└── README.md
/.gitattributes:
--------------------------------------------------------------------------------
1 | ###############################################################################
2 | # Set default behavior to automatically normalize line endings.
3 | ###############################################################################
4 | * text=auto
5 |
6 | ###############################################################################
7 | # Set default behavior for command prompt diff.
8 | #
9 | # This is need for earlier builds of msysgit that does not have it on by
10 | # default for csharp files.
11 | # Note: This is only used by command line
12 | ###############################################################################
13 | #*.cs diff=csharp
14 |
15 | ###############################################################################
16 | # Set the merge driver for project and solution files
17 | #
18 | # Merging from the command prompt will add diff markers to the files if there
19 | # are conflicts (Merging from VS is not affected by the settings below, in VS
20 | # the diff markers are never inserted). Diff markers may cause the following
21 | # file extensions to fail to load in VS. An alternative would be to treat
22 | # these files as binary and thus will always conflict and require user
23 | # intervention with every merge. To do so, just uncomment the entries below
24 | ###############################################################################
25 | #*.sln merge=binary
26 | #*.csproj merge=binary
27 | #*.vbproj merge=binary
28 | #*.vcxproj merge=binary
29 | #*.vcproj merge=binary
30 | #*.dbproj merge=binary
31 | #*.fsproj merge=binary
32 | #*.lsproj merge=binary
33 | #*.wixproj merge=binary
34 | #*.modelproj merge=binary
35 | #*.sqlproj merge=binary
36 | #*.wwaproj merge=binary
37 |
38 | ###############################################################################
39 | # behavior for image files
40 | #
41 | # image files are treated as binary by default.
42 | ###############################################################################
43 | #*.jpg binary
44 | #*.png binary
45 | #*.gif binary
46 |
47 | ###############################################################################
48 | # diff behavior for common document formats
49 | #
50 | # Convert binary document formats to text before diffing them. This feature
51 | # is only available from the command line. Turn it on by uncommenting the
52 | # entries below.
53 | ###############################################################################
54 | #*.doc diff=astextplain
55 | #*.DOC diff=astextplain
56 | #*.docx diff=astextplain
57 | #*.DOCX diff=astextplain
58 | #*.dot diff=astextplain
59 | #*.DOT diff=astextplain
60 | #*.pdf diff=astextplain
61 | #*.PDF diff=astextplain
62 | #*.rtf diff=astextplain
63 | #*.RTF diff=astextplain
64 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | ## Ignore Visual Studio temporary files, build results, and
2 | ## files generated by popular Visual Studio add-ons.
3 |
4 | # User-specific files
5 | *.suo
6 | *.user
7 | *.userosscache
8 | *.sln.docstates
9 |
10 | # User-specific files (MonoDevelop/Xamarin Studio)
11 | *.userprefs
12 |
13 | # Build results
14 | [Dd]ebug/
15 | [Dd]ebugPublic/
16 | [Rr]elease/
17 | [Rr]eleases/
18 | x64/
19 | x86/
20 | build/
21 | bld/
22 | [Bb]in/
23 | [Oo]bj/
24 |
25 | # Visual Studio 2015 cache/options directory
26 | .vs/
27 |
28 | # MSTest test Results
29 | [Tt]est[Rr]esult*/
30 | [Bb]uild[Ll]og.*
31 |
32 | # NUNIT
33 | *.VisualState.xml
34 | TestResult.xml
35 |
36 | # Build Results of an ATL Project
37 | [Dd]ebugPS/
38 | [Rr]eleasePS/
39 | dlldata.c
40 |
41 | # DNX
42 | project.lock.json
43 | artifacts/
44 |
45 | *_i.c
46 | *_p.c
47 | *_i.h
48 | *.ilk
49 | *.meta
50 | *.obj
51 | *.pch
52 | *.pdb
53 | *.pgc
54 | *.pgd
55 | *.rsp
56 | *.sbr
57 | *.tlb
58 | *.tli
59 | *.tlh
60 | *.tmp
61 | *.tmp_proj
62 | *.log
63 | *.vspscc
64 | *.vssscc
65 | .builds
66 | *.pidb
67 | *.svclog
68 | *.scc
69 |
70 | # Chutzpah Test files
71 | _Chutzpah*
72 |
73 | # Visual C++ cache files
74 | ipch/
75 | *.aps
76 | *.ncb
77 | *.opensdf
78 | *.sdf
79 | *.cachefile
80 |
81 | # Visual Studio profiler
82 | *.psess
83 | *.vsp
84 | *.vspx
85 |
86 | # TFS 2012 Local Workspace
87 | $tf/
88 |
89 | # Guidance Automation Toolkit
90 | *.gpState
91 |
92 | # ReSharper is a .NET coding add-in
93 | _ReSharper*/
94 | *.[Rr]e[Ss]harper
95 | *.DotSettings.user
96 |
97 | # JustCode is a .NET coding add-in
98 | .JustCode
99 |
100 | # TeamCity is a build add-in
101 | _TeamCity*
102 |
103 | # DotCover is a Code Coverage Tool
104 | *.dotCover
105 |
106 | # NCrunch
107 | _NCrunch_*
108 | .*crunch*.local.xml
109 |
110 | # MightyMoose
111 | *.mm.*
112 | AutoTest.Net/
113 |
114 | # Web workbench (sass)
115 | .sass-cache/
116 |
117 | # Installshield output folder
118 | [Ee]xpress/
119 |
120 | # DocProject is a documentation generator add-in
121 | DocProject/buildhelp/
122 | DocProject/Help/*.HxT
123 | DocProject/Help/*.HxC
124 | DocProject/Help/*.hhc
125 | DocProject/Help/*.hhk
126 | DocProject/Help/*.hhp
127 | DocProject/Help/Html2
128 | DocProject/Help/html
129 |
130 | # Click-Once directory
131 | publish/
132 |
133 | # Publish Web Output
134 | *.[Pp]ublish.xml
135 | *.azurePubxml
136 | ## TODO: Comment the next line if you want to checkin your
137 | ## web deploy settings but do note that will include unencrypted
138 | ## passwords
139 | #*.pubxml
140 |
141 | *.publishproj
142 |
143 | # NuGet Packages
144 | *.nupkg
145 | # The packages folder can be ignored because of Package Restore
146 | **/packages/*
147 | # except build/, which is used as an MSBuild target.
148 | !**/packages/build/
149 | # Uncomment if necessary however generally it will be regenerated when needed
150 | #!**/packages/repositories.config
151 |
152 | # Windows Azure Build Output
153 | csx/
154 | *.build.csdef
155 |
156 | # Windows Store app package directory
157 | AppPackages/
158 |
159 | # Visual Studio cache files
160 | # files ending in .cache can be ignored
161 | *.[Cc]ache
162 | # but keep track of directories ending in .cache
163 | !*.[Cc]ache/
164 |
165 | # Others
166 | ClientBin/
167 | [Ss]tyle[Cc]op.*
168 | ~$*
169 | *~
170 | *.dbmdl
171 | *.dbproj.schemaview
172 | *.pfx
173 | *.publishsettings
174 | node_modules/
175 | orleans.codegen.cs
176 |
177 | # RIA/Silverlight projects
178 | Generated_Code/
179 |
180 | # Backup & report files from converting an old project file
181 | # to a newer Visual Studio version. Backup files are not needed,
182 | # because we have git ;-)
183 | _UpgradeReport_Files/
184 | Backup*/
185 | UpgradeLog*.XML
186 | UpgradeLog*.htm
187 |
188 | # SQL Server files
189 | *.mdf
190 | *.ldf
191 |
192 | # Business Intelligence projects
193 | *.rdl.data
194 | *.bim.layout
195 | *.bim_*.settings
196 |
197 | # Microsoft Fakes
198 | FakesAssemblies/
199 |
200 | # Node.js Tools for Visual Studio
201 | .ntvs_analysis.dat
202 |
203 | # Visual Studio 6 build log
204 | *.plg
205 |
206 | # Visual Studio 6 workspace options file
207 | *.opt
208 |
209 | # LightSwitch generated files
210 | GeneratedArtifacts/
211 | _Pvt_Extensions/
212 | ModelManifest.xml
213 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2016 Nejc Skofic
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 |
--------------------------------------------------------------------------------
/ObjectMapper/ObjectMapper.Framework/Extensions/CollectionExtensions.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Text;
5 | using System.Threading.Tasks;
6 |
7 | namespace ObjectMapper.Framework
8 | {
9 | public static class CollectionExtensions
10 | {
11 | ///
12 | /// Copies elements from source enumerable to list. List is cleared before new elements are copied over.
13 | ///
14 | /// Type of objects in collections.
15 | /// The target.
16 | /// The source.
17 | public static void CopyFrom(this List target, IEnumerable source)
18 | {
19 | if (target == null || source == null) return;
20 | target.Clear();
21 | target.AddRange(source);
22 | }
23 |
24 | ///
25 | /// Copies elements from source enumerable to collection. Collection is cleared before new elements are copied over.
26 | ///
27 | /// Type of objects in collections.
28 | /// The target.
29 | /// The source.
30 | public static void CopyFrom(this ICollection target, IEnumerable source)
31 | {
32 | if (target == null || source == null || target.IsReadOnly) return;
33 | target.Clear();
34 | foreach (var element in source)
35 | {
36 | target.Add(element);
37 | }
38 | }
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/ObjectMapper/ObjectMapper.Framework/Extensions/ObjectMapperExtensions.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Text;
5 | using System.Threading.Tasks;
6 |
7 | namespace ObjectMapper.Framework
8 | {
9 | public static class ObjectMapperExtensions
10 | {
11 | ///
12 | /// Creates new object of type T from mapper object.
13 | ///
14 | /// Type of target
15 | /// The mapper.
16 | /// New object with properties mapped from mapper.
17 | public static T CreateMappedObject(this IObjectMapper mapper) where T : new()
18 | {
19 | if (mapper == null) return default(T);
20 |
21 | T newObject = new T();
22 | mapper.MapObject(newObject);
23 | return newObject;
24 | }
25 |
26 | ///
27 | /// Creates new object of type T from mapper adapter object.
28 | ///
29 | /// Type of source object
30 | /// Type of target object
31 | /// The adapter.
32 | /// The source.
33 | /// New object with properties mapped from mapper adapter.
34 | public static U CreateMappedObject(this IObjectMapperAdapter adapter, T source) where U : new()
35 | {
36 | if (adapter == null) return default(U);
37 |
38 | U newObject = new U();
39 | adapter.MapObject(source, newObject);
40 | return newObject;
41 | }
42 |
43 | ///
44 | /// Creates new object of type U from mapper adapter object.
45 | ///
46 | /// Type of target object
47 | /// Type of source object
48 | /// The adapter.
49 | /// The source.
50 | /// New object with properties mapped from mapper adapter.
51 | public static T CreateMappedObject(this IObjectMapperAdapter adapter, U source) where T : new()
52 | {
53 | if (adapter == null) return default(T);
54 |
55 | T newObject = new T();
56 | adapter.MapObject(source, newObject);
57 | return newObject;
58 | }
59 | }
60 | }
61 |
--------------------------------------------------------------------------------
/ObjectMapper/ObjectMapper.Framework/IObjectMapper.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Text;
5 | using System.Threading.Tasks;
6 |
7 | namespace ObjectMapper.Framework
8 | {
9 | ///
10 | /// Implementing object can be mapped to object of class T.
11 | ///
12 | /// Type of target object
13 | public interface IObjectMapper
14 | {
15 | ///
16 | /// Maps this object to target.
17 | ///
18 | /// The target.
19 | void MapObject(T target);
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/ObjectMapper/ObjectMapper.Framework/IObjectMapperAdapter.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Text;
5 | using System.Threading.Tasks;
6 |
7 | namespace ObjectMapper.Framework
8 | {
9 | ///
10 | /// Provides mapping between objects of type T and U.
11 | ///
12 | /// Type of first object.
13 | /// Type of second object.
14 | public interface IObjectMapperAdapter
15 | {
16 | ///
17 | /// Maps source object to target object.
18 | ///
19 | /// The source.
20 | /// The target.
21 | void MapObject(T source, U target);
22 |
23 | ///
24 | /// Maps source object to target object.
25 | ///
26 | /// The source.
27 | /// The target.
28 | void MapObject(U source, T target);
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/ObjectMapper/ObjectMapper.Framework/ObjectMapper.Framework.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | 11.0
6 | Debug
7 | AnyCPU
8 | {8220C878-68E1-4307-A908-66FBEA9D8B56}
9 | Library
10 | Properties
11 | ObjectMapper.Framework
12 | ObjectMapper.Framework
13 | en-US
14 | 512
15 | {786C830F-07A1-408B-BD7F-6EE04809D6DB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}
16 | Profile7
17 | v4.5
18 |
19 |
20 | true
21 | full
22 | false
23 | bin\Debug\
24 | DEBUG;TRACE
25 | prompt
26 | 4
27 | true
28 | AllRules.ruleset
29 |
30 |
31 | pdbonly
32 | true
33 | bin\Release\
34 | TRACE
35 | prompt
36 | 4
37 | true
38 | AllRules.ruleset
39 |
40 |
41 | true
42 |
43 |
44 | objectmapper.pfx
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
66 |
--------------------------------------------------------------------------------
/ObjectMapper/ObjectMapper.Framework/ObjectMapperMethodAttribute.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Text;
5 | using System.Threading.Tasks;
6 |
7 | namespace ObjectMapper.Framework
8 | {
9 | ///
10 | /// Marks method as mapping method. Method should accept two parameters (source and target) and returns void.
11 | ///
12 | ///
13 | [System.AttributeUsage(AttributeTargets.Method, Inherited = false, AllowMultiple = false)]
14 | public sealed class ObjectMapperMethodAttribute : Attribute
15 | {
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/ObjectMapper/ObjectMapper.Framework/Properties/AssemblyInfo.cs:
--------------------------------------------------------------------------------
1 | using System.Resources;
2 | using System.Reflection;
3 | using System.Runtime.CompilerServices;
4 | using System.Runtime.InteropServices;
5 | using System;
6 |
7 | // General Information about an assembly is controlled through the following
8 | // set of attributes. Change these attribute values to modify the information
9 | // associated with an assembly.
10 | [assembly: AssemblyTitle("ObjectMapper.Framework")]
11 | [assembly: AssemblyDescription("Contains interfaces for mapping one object to another.")]
12 | [assembly: AssemblyConfiguration("")]
13 | [assembly: AssemblyCompany("")]
14 | [assembly: AssemblyProduct("ObjectMapper.Framework")]
15 | [assembly: AssemblyCopyright("Copyright © 2016")]
16 | [assembly: AssemblyTrademark("")]
17 | [assembly: AssemblyCulture("")]
18 | [assembly: NeutralResourcesLanguage("en")]
19 |
20 | [assembly: CLSCompliant(true)]
21 | [assembly: ComVisible(false)]
22 |
23 | [assembly: AssemblyVersion("1.2.2.0")]
24 | [assembly: AssemblyFileVersion("1.2.2.0")]
25 |
--------------------------------------------------------------------------------
/ObjectMapper/ObjectMapper.GenerateImplementationAnalyzer/ObjectMapper.GenerateImplementationAnalyzer.Test/AttributeUsageUnitTests.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.VisualStudio.TestTools.UnitTesting;
2 | using System;
3 | using System.Collections.Generic;
4 | using System.Linq;
5 | using System.Text;
6 | using System.Threading.Tasks;
7 | using TestHelper;
8 | using Microsoft.CodeAnalysis.Diagnostics;
9 | using Microsoft.CodeAnalysis;
10 |
11 | namespace ObjectMapper.GenerateImplementationAnalyzer.Test
12 | {
13 | [TestClass]
14 | public class AttributeUsageUnitTests : DiagnosticVerifier
15 | {
16 | #region TestEmptyCompilationUnit
17 | ///
18 | /// Tests the empty compilation unit.
19 | ///
20 | [TestMethod]
21 | public void TestEmptyCompilationUnit()
22 | {
23 | var test = @"";
24 |
25 | VerifyCSharpDiagnostic(test);
26 | }
27 | #endregion
28 |
29 | #region TestValidUse
30 | ///
31 | /// Tests the valid use.
32 | ///
33 | [TestMethod]
34 | public void TestValidUse()
35 | {
36 | var test = @"
37 | using ObjectMapper.Framework;
38 |
39 | namespace TestClassLibrary
40 | {
41 | public class ClassA
42 | {
43 | }
44 |
45 | public class ClassB
46 | {
47 | }
48 |
49 | public static class MappingClass
50 | {
51 | [ObjectMapperMethod]
52 | public void MapObject(ClassA source, ClassB target)
53 | {
54 | }
55 | }
56 | }";
57 |
58 | VerifyCSharpDiagnostic(test);
59 | }
60 | #endregion
61 |
62 | #region TestUseWithInvalidReturnType
63 | ///
64 | /// Tests use with invalid return type.
65 | ///
66 | [TestMethod]
67 | public void TestUseWithInvalidReturnType()
68 | {
69 | var test = @"
70 | using ObjectMapper.Framework;
71 |
72 | namespace TestClassLibrary
73 | {
74 | public class ClassA
75 | {
76 | }
77 |
78 | public class ClassB
79 | {
80 | }
81 |
82 | public static class MappingClass
83 | {
84 | [ObjectMapperMethod]
85 | public ClassA MapObject(ClassA source, ClassB target)
86 | {
87 | return source;
88 | }
89 | }
90 | }";
91 |
92 | var expected = new DiagnosticResult
93 | {
94 | Id = "OMAU01",
95 | Message = "Attribute cannot be applied to this method.",
96 | Severity = DiagnosticSeverity.Error,
97 | Locations = new[] { new DiagnosticResultLocation("Test0.cs", 16, 10) }
98 | };
99 |
100 | VerifyCSharpDiagnostic(test, expected);
101 | }
102 | #endregion
103 |
104 | #region TestUseWithTooLittleArguments
105 | ///
106 | /// Tests the use with too little arguments.
107 | ///
108 | [TestMethod]
109 | public void TestUseWithTooLittleArguments()
110 | {
111 | var test = @"
112 | using ObjectMapper.Framework;
113 |
114 | namespace TestClassLibrary
115 | {
116 | public class ClassA
117 | {
118 | }
119 |
120 | public class ClassB
121 | {
122 | }
123 |
124 | public static class MappingClass
125 | {
126 | [ObjectMapperMethod]
127 | public void MapObject(ClassA source)
128 | {
129 | }
130 | }
131 | }";
132 |
133 | var expected = new DiagnosticResult
134 | {
135 | Id = "OMAU01",
136 | Message = "Attribute cannot be applied to this method.",
137 | Severity = DiagnosticSeverity.Error,
138 | Locations = new[] { new DiagnosticResultLocation("Test0.cs", 16, 10) }
139 | };
140 |
141 | VerifyCSharpDiagnostic(test, expected);
142 | }
143 | #endregion
144 |
145 | #region TestUseWithTooManyArguments
146 | ///
147 | /// Tests the use with too many arguments.
148 | ///
149 | [TestMethod]
150 | public void TestUseWithTooManyArguments()
151 | {
152 | var test = @"
153 | using ObjectMapper.Framework;
154 |
155 | namespace TestClassLibrary
156 | {
157 | public class ClassA
158 | {
159 | }
160 |
161 | public class ClassB
162 | {
163 | }
164 |
165 | public static class MappingClass
166 | {
167 | [ObjectMapperMethod]
168 | public void MapObject(ClassA source, ClassB target, ClassB tooMany)
169 | {
170 | }
171 | }
172 | }";
173 |
174 | var expected = new DiagnosticResult
175 | {
176 | Id = "OMAU01",
177 | Message = "Attribute cannot be applied to this method.",
178 | Severity = DiagnosticSeverity.Error,
179 | Locations = new[] { new DiagnosticResultLocation("Test0.cs", 16, 10) }
180 | };
181 |
182 | VerifyCSharpDiagnostic(test, expected);
183 | }
184 | #endregion
185 |
186 | #region Setup
187 | ///
188 | /// Get the CSharp analyzer being tested - to be implemented in non-abstract class
189 | ///
190 | ///
191 | protected override DiagnosticAnalyzer GetCSharpDiagnosticAnalyzer()
192 | {
193 | return new AttributeUsageCodeAnalyzer();
194 | }
195 | #endregion
196 | }
197 | }
198 |
--------------------------------------------------------------------------------
/ObjectMapper/ObjectMapper.GenerateImplementationAnalyzer/ObjectMapper.GenerateImplementationAnalyzer.Test/GenerateImplementationUnitTests.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.CodeAnalysis;
2 | using Microsoft.CodeAnalysis.CodeFixes;
3 | using Microsoft.CodeAnalysis.Diagnostics;
4 | using Microsoft.VisualStudio.TestTools.UnitTesting;
5 | using System;
6 | using TestHelper;
7 |
8 | namespace ObjectMapper.GenerateImplementationAnalyzer.Test
9 | {
10 | [TestClass]
11 | public class GenerateImplementationUnitTest : CodeFixVerifier
12 | {
13 | #region TestEmptyCompilationUnit
14 | ///
15 | /// Tests the empty compilation unit.
16 | ///
17 | [TestMethod]
18 | public void TestEmptyCompilationUnit()
19 | {
20 | var test = @"";
21 |
22 | VerifyCSharpDiagnostic(test);
23 | }
24 | #endregion
25 |
26 | #region TestIObjectMapperInterfaceWithoutDefinedMethods
27 | ///
28 | /// Tests the IObjectMapper interface without interface methods defined.
29 | ///
30 | [TestMethod]
31 | public void TestIObjectMapperInterfaceWithoutDefinedMethods()
32 | {
33 | var test = @"
34 | using ObjectMapper.Framework;
35 | using System.Collections.Generic;
36 | using System.Collections.ObjectModel;
37 |
38 | namespace TestClassLibrary
39 | {
40 | public class ClassA : IObjectMapper
41 | {
42 | public int? Prop1 { get; set; }
43 | public string Prop2 { get; set; }
44 | public decimal Prop3 { get; set; }
45 |
46 | private List _prop4;
47 | public List Prop4 { get { if (_prop4 == null) _prop4 = new List(); return _prop4; } }
48 |
49 | private LinkedList _prop5;
50 | public LinkedList Prop5 { get { if (_prop5 == null) _prop5 = new LinkedList(); return _prop5; } }
51 |
52 | private Collection _prop6;
53 | public Collection Prop6 { get { if (_prop6 == null) _prop6 = new Collection(); return _prop6; } }
54 | }
55 |
56 | public class ClassB
57 | {
58 | public int Prop1 { get; set; }
59 | public string Prop2 { get; set; }
60 | public decimal Prop3 { get; set; }
61 |
62 | private List _prop4;
63 | public List Prop4 { get { if (_prop4 == null) _prop4 = new List(); return _prop4; } }
64 |
65 | private LinkedList _prop5;
66 | public LinkedList Prop5 { get { if (_prop4 == null) _prop5 = new LinkedList(); return _prop5; } }
67 |
68 | private Collection _prop6;
69 | public Collection Prop6 { get { if (_prop6 == null) _prop6 = new Collection(); return _prop6; } }
70 | }
71 | }";
72 | var expected = new DiagnosticResult
73 | {
74 | Id = "OMCG01",
75 | Message = "Implementation of mapping method(s) can be generated.",
76 | Severity = DiagnosticSeverity.Hidden,
77 | Locations = new[] { new DiagnosticResultLocation("Test0.cs", 8, 27) }
78 | };
79 |
80 | VerifyCSharpDiagnostic(test, expected);
81 |
82 | var fixtest = @"
83 | using ObjectMapper.Framework;
84 | using System.Collections.Generic;
85 | using System.Collections.ObjectModel;
86 |
87 | namespace TestClassLibrary
88 | {
89 | public class ClassA : IObjectMapper
90 | {
91 | public int? Prop1 { get; set; }
92 | public string Prop2 { get; set; }
93 | public decimal Prop3 { get; set; }
94 |
95 | private List _prop4;
96 | public List Prop4 { get { if (_prop4 == null) _prop4 = new List(); return _prop4; } }
97 |
98 | private LinkedList _prop5;
99 | public LinkedList Prop5 { get { if (_prop5 == null) _prop5 = new LinkedList(); return _prop5; } }
100 |
101 | private Collection _prop6;
102 | public Collection Prop6 { get { if (_prop6 == null) _prop6 = new Collection(); return _prop6; } }
103 |
104 | public void MapObject(ClassB target)
105 | {
106 | target.Prop1 = this.Prop1 ?? default(int);
107 | target.Prop2 = this.Prop2;
108 | target.Prop3 = this.Prop3;
109 | target.Prop4.CopyFrom(this.Prop4);
110 | target.Prop5.CopyFrom(this.Prop5);
111 | target.Prop6.CopyFrom(this.Prop6);
112 | }
113 | }
114 |
115 | public class ClassB
116 | {
117 | public int Prop1 { get; set; }
118 | public string Prop2 { get; set; }
119 | public decimal Prop3 { get; set; }
120 |
121 | private List _prop4;
122 | public List Prop4 { get { if (_prop4 == null) _prop4 = new List(); return _prop4; } }
123 |
124 | private LinkedList _prop5;
125 | public LinkedList Prop5 { get { if (_prop4 == null) _prop5 = new LinkedList(); return _prop5; } }
126 |
127 | private Collection _prop6;
128 | public Collection Prop6 { get { if (_prop6 == null) _prop6 = new Collection(); return _prop6; } }
129 | }
130 | }";
131 | VerifyCSharpFix(test, fixtest);
132 | }
133 | #endregion
134 |
135 | #region TestIObjectMapperInterfaceWithDefinedMethods
136 | ///
137 | /// Tests the IObjectMapper interface with interface methods defined.
138 | ///
139 | [TestMethod]
140 | public void TestIObjectMapperInterfaceWithDefinedMethods()
141 | {
142 | var test = @"
143 | using ObjectMapper.Framework;
144 | using System.Collections.Generic;
145 | using System.Collections.ObjectModel;
146 |
147 | namespace TestClassLibrary
148 | {
149 | public class ClassA : IObjectMapper
150 | {
151 | public int? Prop1 { get; set; }
152 | public string Prop2 { get; set; }
153 | public decimal Prop3 { get; set; }
154 |
155 | private List _prop4;
156 | public List Prop4 { get { if (_prop4 == null) _prop4 = new List(); return _prop4; } }
157 |
158 | private LinkedList _prop5;
159 | public LinkedList Prop5 { get { if (_prop5 == null) _prop5 = new LinkedList(); return _prop5; } }
160 |
161 | private Collection _prop6;
162 | public Collection Prop6 { get { if (_prop6 == null) _prop6 = new Collection(); return _prop6; } }
163 |
164 | public void MapObject(ClassB target)
165 | {
166 | }
167 | }
168 |
169 | public class ClassB
170 | {
171 | public int Prop1 { get; set; }
172 | public string Prop2 { get; set; }
173 | public decimal Prop3 { get; set; }
174 |
175 | private List _prop4;
176 | public List Prop4 { get { if (_prop4 == null) _prop4 = new List(); return _prop4; } }
177 |
178 | private LinkedList _prop5;
179 | public LinkedList Prop5 { get { if (_prop4 == null) _prop5 = new LinkedList(); return _prop5; } }
180 |
181 | private Collection _prop6;
182 | public Collection Prop6 { get { if (_prop6 == null) _prop6 = new Collection(); return _prop6; } }
183 | }
184 | }";
185 | var expectedOnInterface = new DiagnosticResult
186 | {
187 | Id = "OMCG01",
188 | Message = "Implementation of mapping method(s) can be generated.",
189 | Severity = DiagnosticSeverity.Hidden,
190 | Locations = new[] { new DiagnosticResultLocation("Test0.cs", 8, 27) }
191 | };
192 |
193 | var expectedOnMethod = new DiagnosticResult
194 | {
195 | Id = "OMCG01",
196 | Message = "Implementation of mapping method(s) can be generated.",
197 | Severity = DiagnosticSeverity.Hidden,
198 | Locations = new[] { new DiagnosticResultLocation("Test0.cs", 23, 21) }
199 | };
200 |
201 | VerifyCSharpDiagnostic(test, expectedOnInterface, expectedOnMethod);
202 |
203 | var fixtest = @"
204 | using ObjectMapper.Framework;
205 | using System.Collections.Generic;
206 | using System.Collections.ObjectModel;
207 |
208 | namespace TestClassLibrary
209 | {
210 | public class ClassA : IObjectMapper
211 | {
212 | public int? Prop1 { get; set; }
213 | public string Prop2 { get; set; }
214 | public decimal Prop3 { get; set; }
215 |
216 | private List _prop4;
217 | public List Prop4 { get { if (_prop4 == null) _prop4 = new List(); return _prop4; } }
218 |
219 | private LinkedList _prop5;
220 | public LinkedList Prop5 { get { if (_prop5 == null) _prop5 = new LinkedList(); return _prop5; } }
221 |
222 | private Collection _prop6;
223 | public Collection Prop6 { get { if (_prop6 == null) _prop6 = new Collection(); return _prop6; } }
224 |
225 | public void MapObject(ClassB target)
226 | {
227 | target.Prop1 = this.Prop1 ?? default(int);
228 | target.Prop2 = this.Prop2;
229 | target.Prop3 = this.Prop3;
230 | target.Prop4.CopyFrom(this.Prop4);
231 | target.Prop5.CopyFrom(this.Prop5);
232 | target.Prop6.CopyFrom(this.Prop6);
233 | }
234 | }
235 |
236 | public class ClassB
237 | {
238 | public int Prop1 { get; set; }
239 | public string Prop2 { get; set; }
240 | public decimal Prop3 { get; set; }
241 |
242 | private List _prop4;
243 | public List Prop4 { get { if (_prop4 == null) _prop4 = new List(); return _prop4; } }
244 |
245 | private LinkedList _prop5;
246 | public LinkedList Prop5 { get { if (_prop4 == null) _prop5 = new LinkedList(); return _prop5; } }
247 |
248 | private Collection _prop6;
249 | public Collection Prop6 { get { if (_prop6 == null) _prop6 = new Collection(); return _prop6; } }
250 | }
251 | }";
252 | VerifyCSharpFix(test, fixtest);
253 | }
254 | #endregion
255 |
256 | #region TestIObjectMapperAdapterInterfaceWithoutDefinedMethods
257 | ///
258 | /// Tests the IObjectMapperAdapter interface without interface methods defined.
259 | ///
260 | [TestMethod]
261 | public void TestIObjectMapperAdapterInterfaceWithoutDefinedMethods()
262 | {
263 | var test = @"
264 | using ObjectMapper.Framework;
265 | using System.Collections.Generic;
266 | using System.Collections.ObjectModel;
267 |
268 | namespace TestClassLibrary
269 | {
270 | public class ClassA
271 | {
272 | public int? Prop1 { get; set; }
273 | public string Prop2 { get; set; }
274 | public decimal Prop3 { get; set; }
275 |
276 | private List _prop4;
277 | public List Prop4 { get { if (_prop4 == null) _prop4 = new List(); return _prop4; } }
278 |
279 | private LinkedList _prop5;
280 | public LinkedList Prop5 { get { if (_prop5 == null) _prop5 = new LinkedList(); return _prop5; } }
281 |
282 | private Collection _prop6;
283 | public Collection Prop6 { get { if (_prop6 == null) _prop6 = new Collection(); return _prop6; } }
284 | }
285 |
286 | public class ClassB
287 | {
288 | public int Prop1 { get; set; }
289 | public string Prop2 { get; set; }
290 | public decimal Prop3 { get; set; }
291 |
292 | private List _prop4;
293 | public List Prop4 { get { if (_prop4 == null) _prop4 = new List(); return _prop4; } }
294 |
295 | private LinkedList _prop5;
296 | public LinkedList Prop5 { get { if (_prop4 == null) _prop5 = new LinkedList(); return _prop5; } }
297 |
298 | private Collection _prop6;
299 | public Collection Prop6 { get { if (_prop6 == null) _prop6 = new Collection(); return _prop6; } }
300 | }
301 |
302 | public class MapperAdapter : IObjectMapperAdapter
303 | {
304 |
305 | }
306 | }";
307 | var expected = new DiagnosticResult
308 | {
309 | Id = "OMCG01",
310 | Message = "Implementation of mapping method(s) can be generated.",
311 | Severity = DiagnosticSeverity.Hidden,
312 | Locations = new[] { new DiagnosticResultLocation("Test0.cs", 40, 34) }
313 | };
314 |
315 | VerifyCSharpDiagnostic(test, expected);
316 |
317 | var fixtest = @"
318 | using ObjectMapper.Framework;
319 | using System.Collections.Generic;
320 | using System.Collections.ObjectModel;
321 |
322 | namespace TestClassLibrary
323 | {
324 | public class ClassA
325 | {
326 | public int? Prop1 { get; set; }
327 | public string Prop2 { get; set; }
328 | public decimal Prop3 { get; set; }
329 |
330 | private List _prop4;
331 | public List Prop4 { get { if (_prop4 == null) _prop4 = new List(); return _prop4; } }
332 |
333 | private LinkedList _prop5;
334 | public LinkedList Prop5 { get { if (_prop5 == null) _prop5 = new LinkedList(); return _prop5; } }
335 |
336 | private Collection _prop6;
337 | public Collection Prop6 { get { if (_prop6 == null) _prop6 = new Collection(); return _prop6; } }
338 | }
339 |
340 | public class ClassB
341 | {
342 | public int Prop1 { get; set; }
343 | public string Prop2 { get; set; }
344 | public decimal Prop3 { get; set; }
345 |
346 | private List _prop4;
347 | public List Prop4 { get { if (_prop4 == null) _prop4 = new List(); return _prop4; } }
348 |
349 | private LinkedList _prop5;
350 | public LinkedList Prop5 { get { if (_prop4 == null) _prop5 = new LinkedList(); return _prop5; } }
351 |
352 | private Collection _prop6;
353 | public Collection Prop6 { get { if (_prop6 == null) _prop6 = new Collection(); return _prop6; } }
354 | }
355 |
356 | public class MapperAdapter : IObjectMapperAdapter
357 | {
358 | public void MapObject(ClassA source, ClassB target)
359 | {
360 | target.Prop1 = source.Prop1 ?? default(int);
361 | target.Prop2 = source.Prop2;
362 | target.Prop3 = source.Prop3;
363 | target.Prop4.CopyFrom(source.Prop4);
364 | target.Prop5.CopyFrom(source.Prop5);
365 | target.Prop6.CopyFrom(source.Prop6);
366 | }
367 |
368 | public void MapObject(ClassB source, ClassA target)
369 | {
370 | target.Prop1 = source.Prop1;
371 | target.Prop2 = source.Prop2;
372 | target.Prop3 = source.Prop3;
373 | target.Prop4.CopyFrom(source.Prop4);
374 | target.Prop5.CopyFrom(source.Prop5);
375 | target.Prop6.CopyFrom(source.Prop6);
376 | }
377 | }
378 | }";
379 | VerifyCSharpFix(test, fixtest);
380 | }
381 | #endregion
382 |
383 | #region TestIObjectMapperAdapterInterfaceWithDefinedMethods
384 | ///
385 | /// Tests the IObjectMapperAdapter interface with interface methods defined.
386 | ///
387 | [TestMethod]
388 | public void TestIObjectMapperAdapterInterfaceWithDefinedMethods()
389 | {
390 | var test = @"
391 | using ObjectMapper.Framework;
392 | using System.Collections.Generic;
393 | using System.Collections.ObjectModel;
394 |
395 | namespace TestClassLibrary
396 | {
397 | public class ClassA
398 | {
399 | public int? Prop1 { get; set; }
400 | public string Prop2 { get; set; }
401 | public decimal Prop3 { get; set; }
402 |
403 | private List _prop4;
404 | public List Prop4 { get { if (_prop4 == null) _prop4 = new List(); return _prop4; } }
405 |
406 | private LinkedList _prop5;
407 | public LinkedList Prop5 { get { if (_prop5 == null) _prop5 = new LinkedList(); return _prop5; } }
408 |
409 | private Collection _prop6;
410 | public Collection Prop6 { get { if (_prop6 == null) _prop6 = new Collection(); return _prop6; } }
411 | }
412 |
413 | public class ClassB
414 | {
415 | public int Prop1 { get; set; }
416 | public string Prop2 { get; set; }
417 | public decimal Prop3 { get; set; }
418 |
419 | private List _prop4;
420 | public List Prop4 { get { if (_prop4 == null) _prop4 = new List(); return _prop4; } }
421 |
422 | private LinkedList _prop5;
423 | public LinkedList Prop5 { get { if (_prop4 == null) _prop5 = new LinkedList(); return _prop5; } }
424 |
425 | private Collection _prop6;
426 | public Collection Prop6 { get { if (_prop6 == null) _prop6 = new Collection(); return _prop6; } }
427 | }
428 |
429 | public class MapperAdapter : IObjectMapperAdapter
430 | {
431 | public void MapObject(ClassA source, ClassB target)
432 | {
433 | }
434 |
435 | public void MapObject(ClassB source, ClassA target)
436 | {
437 | }
438 | }
439 | }";
440 | var expectedOnInterface = new DiagnosticResult
441 | {
442 | Id = "OMCG01",
443 | Message = "Implementation of mapping method(s) can be generated.",
444 | Severity = DiagnosticSeverity.Hidden,
445 | Locations = new[] { new DiagnosticResultLocation("Test0.cs", 40, 34) }
446 | };
447 |
448 | var expectedOnFirstMethod = new DiagnosticResult
449 | {
450 | Id = "OMCG01",
451 | Message = "Implementation of mapping method(s) can be generated.",
452 | Severity = DiagnosticSeverity.Hidden,
453 | Locations = new[] { new DiagnosticResultLocation("Test0.cs", 42, 21) }
454 | };
455 |
456 | var expectedOnSecondMethod = new DiagnosticResult
457 | {
458 | Id = "OMCG01",
459 | Message = "Implementation of mapping method(s) can be generated.",
460 | Severity = DiagnosticSeverity.Hidden,
461 | Locations = new[] { new DiagnosticResultLocation("Test0.cs", 46, 21) }
462 | };
463 |
464 | VerifyCSharpDiagnostic(test, expectedOnInterface, expectedOnFirstMethod, expectedOnSecondMethod);
465 |
466 | var fixtest = @"
467 | using ObjectMapper.Framework;
468 | using System.Collections.Generic;
469 | using System.Collections.ObjectModel;
470 |
471 | namespace TestClassLibrary
472 | {
473 | public class ClassA
474 | {
475 | public int? Prop1 { get; set; }
476 | public string Prop2 { get; set; }
477 | public decimal Prop3 { get; set; }
478 |
479 | private List _prop4;
480 | public List Prop4 { get { if (_prop4 == null) _prop4 = new List(); return _prop4; } }
481 |
482 | private LinkedList _prop5;
483 | public LinkedList Prop5 { get { if (_prop5 == null) _prop5 = new LinkedList(); return _prop5; } }
484 |
485 | private Collection _prop6;
486 | public Collection Prop6 { get { if (_prop6 == null) _prop6 = new Collection(); return _prop6; } }
487 | }
488 |
489 | public class ClassB
490 | {
491 | public int Prop1 { get; set; }
492 | public string Prop2 { get; set; }
493 | public decimal Prop3 { get; set; }
494 |
495 | private List _prop4;
496 | public List Prop4 { get { if (_prop4 == null) _prop4 = new List(); return _prop4; } }
497 |
498 | private LinkedList _prop5;
499 | public LinkedList Prop5 { get { if (_prop4 == null) _prop5 = new LinkedList(); return _prop5; } }
500 |
501 | private Collection _prop6;
502 | public Collection Prop6 { get { if (_prop6 == null) _prop6 = new Collection(); return _prop6; } }
503 | }
504 |
505 | public class MapperAdapter : IObjectMapperAdapter
506 | {
507 | public void MapObject(ClassA source, ClassB target)
508 | {
509 | target.Prop1 = source.Prop1 ?? default(int);
510 | target.Prop2 = source.Prop2;
511 | target.Prop3 = source.Prop3;
512 | target.Prop4.CopyFrom(source.Prop4);
513 | target.Prop5.CopyFrom(source.Prop5);
514 | target.Prop6.CopyFrom(source.Prop6);
515 | }
516 |
517 | public void MapObject(ClassB source, ClassA target)
518 | {
519 | target.Prop1 = source.Prop1;
520 | target.Prop2 = source.Prop2;
521 | target.Prop3 = source.Prop3;
522 | target.Prop4.CopyFrom(source.Prop4);
523 | target.Prop5.CopyFrom(source.Prop5);
524 | target.Prop6.CopyFrom(source.Prop6);
525 | }
526 | }
527 | }";
528 | VerifyCSharpFix(test, fixtest);
529 | }
530 | #endregion
531 |
532 | #region TestObjectMapperMethodAttribute
533 | ///
534 | /// Tests ObjectMapperMethodAttribute.
535 | ///
536 | [TestMethod]
537 | public void TestObjectMapperMethodAttribute()
538 | {
539 | var test = @"
540 | using ObjectMapper.Framework;
541 | using System.Collections.Generic;
542 | using System.Collections.ObjectModel;
543 |
544 | namespace TestClassLibrary
545 | {
546 | public class ClassA
547 | {
548 | public int? Prop1 { get; set; }
549 | public string Prop2 { get; set; }
550 | public decimal Prop3 { get; set; }
551 |
552 | private List _prop4;
553 | public List Prop4 { get { if (_prop4 == null) _prop4 = new List(); return _prop4; } }
554 |
555 | private LinkedList _prop5;
556 | public LinkedList Prop5 { get { if (_prop5 == null) _prop5 = new LinkedList(); return _prop5; } }
557 |
558 | private Collection _prop6;
559 | public Collection Prop6 { get { if (_prop6 == null) _prop6 = new Collection(); return _prop6; } }
560 | }
561 |
562 | public class ClassB
563 | {
564 | public int Prop1 { get; set; }
565 | public string Prop2 { get; set; }
566 | public decimal Prop3 { get; set; }
567 |
568 | private List _prop4;
569 | public List Prop4 { get { if (_prop4 == null) _prop4 = new List(); return _prop4; } }
570 |
571 | private LinkedList _prop5;
572 | public LinkedList Prop5 { get { if (_prop4 == null) _prop5 = new LinkedList(); return _prop5; } }
573 |
574 | private Collection _prop6;
575 | public Collection Prop6 { get { if (_prop6 == null) _prop6 = new Collection(); return _prop6; } }
576 | }
577 |
578 | public static class MappingClass
579 | {
580 | [ObjectMapperMethod]
581 | public void MapObject(ClassA source, ClassB target)
582 | {
583 | }
584 | }
585 | }";
586 | var expected = new DiagnosticResult
587 | {
588 | Id = "OMCG01",
589 | Message = "Implementation of mapping method(s) can be generated.",
590 | Severity = DiagnosticSeverity.Hidden,
591 | Locations = new[] { new DiagnosticResultLocation("Test0.cs", 43, 21) }
592 | };
593 |
594 | VerifyCSharpDiagnostic(test, expected);
595 |
596 | var fixtest = @"
597 | using ObjectMapper.Framework;
598 | using System.Collections.Generic;
599 | using System.Collections.ObjectModel;
600 |
601 | namespace TestClassLibrary
602 | {
603 | public class ClassA
604 | {
605 | public int? Prop1 { get; set; }
606 | public string Prop2 { get; set; }
607 | public decimal Prop3 { get; set; }
608 |
609 | private List _prop4;
610 | public List Prop4 { get { if (_prop4 == null) _prop4 = new List(); return _prop4; } }
611 |
612 | private LinkedList _prop5;
613 | public LinkedList Prop5 { get { if (_prop5 == null) _prop5 = new LinkedList(); return _prop5; } }
614 |
615 | private Collection _prop6;
616 | public Collection Prop6 { get { if (_prop6 == null) _prop6 = new Collection(); return _prop6; } }
617 | }
618 |
619 | public class ClassB
620 | {
621 | public int Prop1 { get; set; }
622 | public string Prop2 { get; set; }
623 | public decimal Prop3 { get; set; }
624 |
625 | private List _prop4;
626 | public List Prop4 { get { if (_prop4 == null) _prop4 = new List(); return _prop4; } }
627 |
628 | private LinkedList _prop5;
629 | public LinkedList Prop5 { get { if (_prop4 == null) _prop5 = new LinkedList(); return _prop5; } }
630 |
631 | private Collection _prop6;
632 | public Collection Prop6 { get { if (_prop6 == null) _prop6 = new Collection(); return _prop6; } }
633 | }
634 |
635 | public static class MappingClass
636 | {
637 | [ObjectMapperMethod]
638 | public void MapObject(ClassA source, ClassB target)
639 | {
640 | target.Prop1 = source.Prop1 ?? default(int);
641 | target.Prop2 = source.Prop2;
642 | target.Prop3 = source.Prop3;
643 | target.Prop4.CopyFrom(source.Prop4);
644 | target.Prop5.CopyFrom(source.Prop5);
645 | target.Prop6.CopyFrom(source.Prop6);
646 | }
647 | }
648 | }";
649 | VerifyCSharpFix(test, fixtest);
650 | }
651 | #endregion
652 |
653 | #region Setup
654 | protected override CodeFixProvider GetCSharpCodeFixProvider()
655 | {
656 | return new GenerateImplementationCodeGenerator();
657 | }
658 |
659 | protected override DiagnosticAnalyzer GetCSharpDiagnosticAnalyzer()
660 | {
661 | return new GenerateImplementationCodeAnalyzer();
662 | }
663 | #endregion
664 | }
665 | }
--------------------------------------------------------------------------------
/ObjectMapper/ObjectMapper.GenerateImplementationAnalyzer/ObjectMapper.GenerateImplementationAnalyzer.Test/Helpers/CodeFixVerifier.Helper.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.CodeAnalysis;
2 | using Microsoft.CodeAnalysis.CodeActions;
3 | using Microsoft.CodeAnalysis.Formatting;
4 | using Microsoft.CodeAnalysis.Simplification;
5 | using System.Collections.Generic;
6 | using System.Linq;
7 | using System.Threading;
8 |
9 | namespace TestHelper
10 | {
11 | ///
12 | /// Diagnostic Producer class with extra methods dealing with applying codefixes
13 | /// All methods are static
14 | ///
15 | public abstract partial class CodeFixVerifier : DiagnosticVerifier
16 | {
17 | ///
18 | /// Apply the inputted CodeAction to the inputted document.
19 | /// Meant to be used to apply codefixes.
20 | ///
21 | /// The Document to apply the fix on
22 | /// A CodeAction that will be applied to the Document.
23 | /// A Document with the changes from the CodeAction
24 | private static Document ApplyFix(Document document, CodeAction codeAction)
25 | {
26 | var operations = codeAction.GetOperationsAsync(CancellationToken.None).Result;
27 | var solution = operations.OfType().Single().ChangedSolution;
28 | return solution.GetDocument(document.Id);
29 | }
30 |
31 | ///
32 | /// Compare two collections of Diagnostics,and return a list of any new diagnostics that appear only in the second collection.
33 | /// Note: Considers Diagnostics to be the same if they have the same Ids. In the case of multiple diagnostics with the same Id in a row,
34 | /// this method may not necessarily return the new one.
35 | ///
36 | /// The Diagnostics that existed in the code before the CodeFix was applied
37 | /// The Diagnostics that exist in the code after the CodeFix was applied
38 | /// A list of Diagnostics that only surfaced in the code after the CodeFix was applied
39 | private static IEnumerable GetNewDiagnostics(IEnumerable diagnostics, IEnumerable newDiagnostics)
40 | {
41 | var oldArray = diagnostics.OrderBy(d => d.Location.SourceSpan.Start).ToArray();
42 | var newArray = newDiagnostics.OrderBy(d => d.Location.SourceSpan.Start).ToArray();
43 |
44 | int oldIndex = 0;
45 | int newIndex = 0;
46 |
47 | while (newIndex < newArray.Length)
48 | {
49 | if (oldIndex < oldArray.Length && oldArray[oldIndex].Id == newArray[newIndex].Id)
50 | {
51 | ++oldIndex;
52 | ++newIndex;
53 | }
54 | else
55 | {
56 | yield return newArray[newIndex++];
57 | }
58 | }
59 | }
60 |
61 | ///
62 | /// Get the existing compiler diagnostics on the inputted document.
63 | ///
64 | /// The Document to run the compiler diagnostic analyzers on
65 | /// The compiler diagnostics that were found in the code
66 | private static IEnumerable GetCompilerDiagnostics(Document document)
67 | {
68 | return document.GetSemanticModelAsync().Result.GetDiagnostics();
69 | }
70 |
71 | ///
72 | /// Given a document, turn it into a string based on the syntax root
73 | ///
74 | /// The Document to be converted to a string
75 | /// A string containing the syntax of the Document after formatting
76 | private static string GetStringFromDocument(Document document)
77 | {
78 | var simplifiedDoc = Simplifier.ReduceAsync(document, Simplifier.Annotation).Result;
79 | var root = simplifiedDoc.GetSyntaxRootAsync().Result;
80 | root = Formatter.Format(root, Formatter.Annotation, simplifiedDoc.Project.Solution.Workspace);
81 | return root.GetText().ToString();
82 | }
83 | }
84 | }
85 |
86 |
--------------------------------------------------------------------------------
/ObjectMapper/ObjectMapper.GenerateImplementationAnalyzer/ObjectMapper.GenerateImplementationAnalyzer.Test/Helpers/DiagnosticResult.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.CodeAnalysis;
2 | using System;
3 |
4 | namespace TestHelper
5 | {
6 | ///
7 | /// Location where the diagnostic appears, as determined by path, line number, and column number.
8 | ///
9 | public struct DiagnosticResultLocation
10 | {
11 | public DiagnosticResultLocation(string path, int line, int column)
12 | {
13 | if (line < -1)
14 | {
15 | throw new ArgumentOutOfRangeException(nameof(line), "line must be >= -1");
16 | }
17 |
18 | if (column < -1)
19 | {
20 | throw new ArgumentOutOfRangeException(nameof(column), "column must be >= -1");
21 | }
22 |
23 | this.Path = path;
24 | this.Line = line;
25 | this.Column = column;
26 | }
27 |
28 | public string Path { get; }
29 | public int Line { get; }
30 | public int Column { get; }
31 | }
32 |
33 | ///
34 | /// Struct that stores information about a Diagnostic appearing in a source
35 | ///
36 | public struct DiagnosticResult
37 | {
38 | private DiagnosticResultLocation[] locations;
39 |
40 | public DiagnosticResultLocation[] Locations
41 | {
42 | get
43 | {
44 | if (this.locations == null)
45 | {
46 | this.locations = new DiagnosticResultLocation[] { };
47 | }
48 | return this.locations;
49 | }
50 |
51 | set
52 | {
53 | this.locations = value;
54 | }
55 | }
56 |
57 | public DiagnosticSeverity Severity { get; set; }
58 |
59 | public string Id { get; set; }
60 |
61 | public string Message { get; set; }
62 |
63 | public string Path
64 | {
65 | get
66 | {
67 | return this.Locations.Length > 0 ? this.Locations[0].Path : "";
68 | }
69 | }
70 |
71 | public int Line
72 | {
73 | get
74 | {
75 | return this.Locations.Length > 0 ? this.Locations[0].Line : -1;
76 | }
77 | }
78 |
79 | public int Column
80 | {
81 | get
82 | {
83 | return this.Locations.Length > 0 ? this.Locations[0].Column : -1;
84 | }
85 | }
86 | }
87 | }
--------------------------------------------------------------------------------
/ObjectMapper/ObjectMapper.GenerateImplementationAnalyzer/ObjectMapper.GenerateImplementationAnalyzer.Test/Helpers/DiagnosticVerifier.Helper.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.CodeAnalysis;
2 | using Microsoft.CodeAnalysis.CSharp;
3 | using Microsoft.CodeAnalysis.Diagnostics;
4 | using Microsoft.CodeAnalysis.Text;
5 | using System;
6 | using System.Collections.Generic;
7 | using System.Collections.Immutable;
8 | using System.IO;
9 | using System.Linq;
10 |
11 | namespace TestHelper
12 | {
13 | ///
14 | /// Class for turning strings into documents and getting the diagnostics on them
15 | /// All methods are static
16 | ///
17 | public abstract partial class DiagnosticVerifier
18 | {
19 | private static readonly MetadataReference CorlibReference = MetadataReference.CreateFromFile(typeof(object).Assembly.Location);
20 | private static readonly MetadataReference SystemCoreReference = MetadataReference.CreateFromFile(typeof(Enumerable).Assembly.Location);
21 | private static readonly MetadataReference CSharpSymbolsReference = MetadataReference.CreateFromFile(typeof(CSharpCompilation).Assembly.Location);
22 | private static readonly MetadataReference CodeAnalysisReference = MetadataReference.CreateFromFile(typeof(Compilation).Assembly.Location);
23 | private static readonly MetadataReference ObjectMapperFrameworkReference = MetadataReference.CreateFromFile(typeof(ObjectMapper.Framework.IObjectMapper<>).Assembly.Location);
24 | private static readonly MetadataReference SystemReference = MetadataReference.CreateFromFile(typeof(LinkedList<>).Assembly.Location);
25 | private static readonly MetadataReference SystemRuntimeFacadeReference = MetadataReference.CreateFromFile(Path.Combine(Path.GetDirectoryName(typeof(object).Assembly.Location), "System.Runtime.dll"));
26 | private static readonly MetadataReference SystemCollectionsFacadeReference = MetadataReference.CreateFromFile(Path.Combine(Path.GetDirectoryName(typeof(object).Assembly.Location), "System.Collections.dll"));
27 |
28 | internal static string DefaultFilePathPrefix = "Test";
29 | internal static string CSharpDefaultFileExt = "cs";
30 | internal static string VisualBasicDefaultExt = "vb";
31 | internal static string TestProjectName = "TestProject";
32 |
33 | #region Get Diagnostics
34 |
35 | ///
36 | /// Given classes in the form of strings, their language, and an IDiagnosticAnlayzer to apply to it, return the diagnostics found in the string after converting it to a document.
37 | ///
38 | /// Classes in the form of strings
39 | /// The language the source classes are in
40 | /// The analyzer to be run on the sources
41 | /// An IEnumerable of Diagnostics that surfaced in the source code, sorted by Location
42 | private static Diagnostic[] GetSortedDiagnostics(string[] sources, string language, DiagnosticAnalyzer analyzer)
43 | {
44 | return GetSortedDiagnosticsFromDocuments(analyzer, GetDocuments(sources, language));
45 | }
46 |
47 | ///
48 | /// Given an analyzer and a document to apply it to, run the analyzer and gather an array of diagnostics found in it.
49 | /// The returned diagnostics are then ordered by location in the source document.
50 | ///
51 | /// The analyzer to run on the documents
52 | /// The Documents that the analyzer will be run on
53 | /// An IEnumerable of Diagnostics that surfaced in the source code, sorted by Location
54 | protected static Diagnostic[] GetSortedDiagnosticsFromDocuments(DiagnosticAnalyzer analyzer, Document[] documents)
55 | {
56 | var projects = new HashSet();
57 | foreach (var document in documents)
58 | {
59 | projects.Add(document.Project);
60 | }
61 |
62 | var diagnostics = new List();
63 | foreach (var project in projects)
64 | {
65 | var compilationWithAnalyzers = project.GetCompilationAsync().Result.WithAnalyzers(ImmutableArray.Create(analyzer));
66 | var diags = compilationWithAnalyzers.GetAnalyzerDiagnosticsAsync().Result;
67 | foreach (var diag in diags)
68 | {
69 | if (diag.Location == Location.None || diag.Location.IsInMetadata)
70 | {
71 | diagnostics.Add(diag);
72 | }
73 | else
74 | {
75 | for (int i = 0; i < documents.Length; i++)
76 | {
77 | var document = documents[i];
78 | var tree = document.GetSyntaxTreeAsync().Result;
79 | if (tree == diag.Location.SourceTree)
80 | {
81 | diagnostics.Add(diag);
82 | }
83 | }
84 | }
85 | }
86 | }
87 |
88 | var results = SortDiagnostics(diagnostics);
89 | diagnostics.Clear();
90 | return results;
91 | }
92 |
93 | ///
94 | /// Sort diagnostics by location in source document
95 | ///
96 | /// The list of Diagnostics to be sorted
97 | /// An IEnumerable containing the Diagnostics in order of Location
98 | private static Diagnostic[] SortDiagnostics(IEnumerable diagnostics)
99 | {
100 | return diagnostics.OrderBy(d => d.Location.SourceSpan.Start).ToArray();
101 | }
102 |
103 | #endregion
104 |
105 | #region Set up compilation and documents
106 | ///
107 | /// Given an array of strings as sources and a language, turn them into a project and return the documents and spans of it.
108 | ///
109 | /// Classes in the form of strings
110 | /// The language the source code is in
111 | /// A Tuple containing the Documents produced from the sources and their TextSpans if relevant
112 | private static Document[] GetDocuments(string[] sources, string language)
113 | {
114 | if (language != LanguageNames.CSharp && language != LanguageNames.VisualBasic)
115 | {
116 | throw new ArgumentException("Unsupported Language");
117 | }
118 |
119 | var project = CreateProject(sources, language);
120 | var documents = project.Documents.ToArray();
121 |
122 | if (sources.Length != documents.Length)
123 | {
124 | throw new SystemException("Amount of sources did not match amount of Documents created");
125 | }
126 |
127 | return documents;
128 | }
129 |
130 | ///
131 | /// Create a Document from a string through creating a project that contains it.
132 | ///
133 | /// Classes in the form of a string
134 | /// The language the source code is in
135 | /// A Document created from the source string
136 | protected static Document CreateDocument(string source, string language = LanguageNames.CSharp)
137 | {
138 | return CreateProject(new[] { source }, language).Documents.First();
139 | }
140 |
141 | ///
142 | /// Create a project using the inputted strings as sources.
143 | ///
144 | /// Classes in the form of strings
145 | /// The language the source code is in
146 | /// A Project created out of the Documents created from the source strings
147 | private static Project CreateProject(string[] sources, string language = LanguageNames.CSharp)
148 | {
149 | string fileNamePrefix = DefaultFilePathPrefix;
150 | string fileExt = language == LanguageNames.CSharp ? CSharpDefaultFileExt : VisualBasicDefaultExt;
151 |
152 | var projectId = ProjectId.CreateNewId(debugName: TestProjectName);
153 |
154 | var solution = new AdhocWorkspace()
155 | .CurrentSolution
156 | .AddProject(projectId, TestProjectName, TestProjectName, language)
157 | .AddMetadataReference(projectId, CorlibReference)
158 | .AddMetadataReference(projectId, SystemCoreReference)
159 | .AddMetadataReference(projectId, CSharpSymbolsReference)
160 | .AddMetadataReference(projectId, CodeAnalysisReference)
161 | .AddMetadataReference(projectId, SystemReference)
162 | .AddMetadataReference(projectId, ObjectMapperFrameworkReference)
163 | .AddMetadataReference(projectId, SystemRuntimeFacadeReference)
164 | .AddMetadataReference(projectId, SystemCollectionsFacadeReference);
165 |
166 | int count = 0;
167 | foreach (var source in sources)
168 | {
169 | var newFileName = fileNamePrefix + count + "." + fileExt;
170 | var documentId = DocumentId.CreateNewId(projectId, debugName: newFileName);
171 | solution = solution.AddDocument(documentId, newFileName, SourceText.From(source));
172 | count++;
173 | }
174 | return solution.GetProject(projectId);
175 | }
176 | #endregion
177 | }
178 | }
179 |
180 |
--------------------------------------------------------------------------------
/ObjectMapper/ObjectMapper.GenerateImplementationAnalyzer/ObjectMapper.GenerateImplementationAnalyzer.Test/ObjectMapper.GenerateImplementationAnalyzer.Test.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Debug
5 | AnyCPU
6 | 8.0.30703
7 | 2.0
8 | {FCC88640-43A1-4FEE-AE9B-44674C6376F7}
9 | Library
10 | Properties
11 | ObjectMapper.GenerateImplementationAnalyzer.Test
12 | ObjectMapper.GenerateImplementationAnalyzer.Test
13 | v4.5.2
14 | 512
15 |
16 |
17 |
18 | true
19 | full
20 | false
21 | bin\Debug\
22 | DEBUG;TRACE
23 | prompt
24 | 4
25 | false
26 |
27 |
28 | pdbonly
29 | true
30 | bin\Release\
31 | TRACE
32 | prompt
33 | 4
34 | false
35 |
36 |
37 |
38 | ..\..\packages\Microsoft.CodeAnalysis.Common.1.0.0\lib\net45\Microsoft.CodeAnalysis.dll
39 | True
40 |
41 |
42 | ..\..\packages\Microsoft.CodeAnalysis.CSharp.1.0.0\lib\net45\Microsoft.CodeAnalysis.CSharp.dll
43 | True
44 |
45 |
46 | ..\..\packages\Microsoft.CodeAnalysis.CSharp.Workspaces.1.0.0\lib\net45\Microsoft.CodeAnalysis.CSharp.Workspaces.dll
47 | True
48 |
49 |
50 | ..\..\packages\Microsoft.CodeAnalysis.VisualBasic.1.0.0\lib\net45\Microsoft.CodeAnalysis.VisualBasic.dll
51 | True
52 |
53 |
54 | ..\..\packages\Microsoft.CodeAnalysis.VisualBasic.Workspaces.1.0.0\lib\net45\Microsoft.CodeAnalysis.VisualBasic.Workspaces.dll
55 | True
56 |
57 |
58 | ..\..\packages\Microsoft.CodeAnalysis.Workspaces.Common.1.0.0\lib\net45\Microsoft.CodeAnalysis.Workspaces.dll
59 | True
60 |
61 |
62 | ..\..\packages\Microsoft.CodeAnalysis.Workspaces.Common.1.0.0\lib\net45\Microsoft.CodeAnalysis.Workspaces.Desktop.dll
63 | True
64 |
65 |
66 |
67 | ..\..\packages\System.Collections.Immutable.1.1.36\lib\portable-net45+win8+wp8+wpa81\System.Collections.Immutable.dll
68 | True
69 |
70 |
71 | ..\..\packages\Microsoft.Composition.1.0.27\lib\portable-net45+win8+wp8+wpa81\System.Composition.AttributedModel.dll
72 | True
73 |
74 |
75 | ..\..\packages\Microsoft.Composition.1.0.27\lib\portable-net45+win8+wp8+wpa81\System.Composition.Convention.dll
76 | True
77 |
78 |
79 | ..\..\packages\Microsoft.Composition.1.0.27\lib\portable-net45+win8+wp8+wpa81\System.Composition.Hosting.dll
80 | True
81 |
82 |
83 | ..\..\packages\Microsoft.Composition.1.0.27\lib\portable-net45+win8+wp8+wpa81\System.Composition.Runtime.dll
84 | True
85 |
86 |
87 | ..\..\packages\Microsoft.Composition.1.0.27\lib\portable-net45+win8+wp8+wpa81\System.Composition.TypedParts.dll
88 | True
89 |
90 |
91 |
92 | ..\..\packages\System.Reflection.Metadata.1.0.21\lib\portable-net45+win8\System.Reflection.Metadata.dll
93 | True
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 | false
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 |
110 |
111 |
112 |
113 |
114 |
115 |
116 |
117 |
118 |
119 | {8220c878-68e1-4307-a908-66fbea9d8b56}
120 | ObjectMapper.Framework
121 |
122 |
123 | {492F3804-A43F-4E82-B649-8AD0D967A830}
124 | ObjectMapper.GenerateImplementationAnalyzer
125 |
126 |
127 |
128 |
129 |
130 |
131 |
138 |
--------------------------------------------------------------------------------
/ObjectMapper/ObjectMapper.GenerateImplementationAnalyzer/ObjectMapper.GenerateImplementationAnalyzer.Test/Properties/AssemblyInfo.cs:
--------------------------------------------------------------------------------
1 | using System.Reflection;
2 | using System.Runtime.InteropServices;
3 |
4 | // General Information about an assembly is controlled through the following
5 | // set of attributes. Change these attribute values to modify the information
6 | // associated with an assembly.
7 | [assembly: AssemblyTitle("ObjectMapper.GenerateImplementationAnalyzer.Test")]
8 | [assembly: AssemblyDescription("")]
9 | [assembly: AssemblyConfiguration("")]
10 | [assembly: AssemblyCompany("")]
11 | [assembly: AssemblyProduct("ObjectMapper.GenerateImplementationAnalyzer.Test")]
12 | [assembly: AssemblyCopyright("Copyright © 2016")]
13 | [assembly: AssemblyTrademark("")]
14 | [assembly: AssemblyCulture("")]
15 |
16 | [assembly: ComVisible(false)]
17 |
18 | [assembly: AssemblyVersion("1.2.2.0")]
19 | [assembly: AssemblyFileVersion("1.2.2.0")]
--------------------------------------------------------------------------------
/ObjectMapper/ObjectMapper.GenerateImplementationAnalyzer/ObjectMapper.GenerateImplementationAnalyzer.Test/Verifiers/CodeFixVerifier.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.CodeAnalysis;
2 | using Microsoft.CodeAnalysis.CodeActions;
3 | using Microsoft.CodeAnalysis.CodeFixes;
4 | using Microsoft.CodeAnalysis.Diagnostics;
5 | using Microsoft.CodeAnalysis.Formatting;
6 | using Microsoft.VisualStudio.TestTools.UnitTesting;
7 | using System.Collections.Generic;
8 | using System.Linq;
9 | using System.Threading;
10 |
11 | namespace TestHelper
12 | {
13 | ///
14 | /// Superclass of all Unit tests made for diagnostics with codefixes.
15 | /// Contains methods used to verify correctness of codefixes
16 | ///
17 | public abstract partial class CodeFixVerifier : DiagnosticVerifier
18 | {
19 | ///
20 | /// Returns the codefix being tested (C#) - to be implemented in non-abstract class
21 | ///
22 | /// The CodeFixProvider to be used for CSharp code
23 | protected virtual CodeFixProvider GetCSharpCodeFixProvider()
24 | {
25 | return null;
26 | }
27 |
28 | ///
29 | /// Returns the codefix being tested (VB) - to be implemented in non-abstract class
30 | ///
31 | /// The CodeFixProvider to be used for VisualBasic code
32 | protected virtual CodeFixProvider GetBasicCodeFixProvider()
33 | {
34 | return null;
35 | }
36 |
37 | ///
38 | /// Called to test a C# codefix when applied on the inputted string as a source
39 | ///
40 | /// A class in the form of a string before the CodeFix was applied to it
41 | /// A class in the form of a string after the CodeFix was applied to it
42 | /// Index determining which codefix to apply if there are multiple
43 | /// A bool controlling whether or not the test will fail if the CodeFix introduces other warnings after being applied
44 | protected void VerifyCSharpFix(string oldSource, string newSource, int? codeFixIndex = null, bool allowNewCompilerDiagnostics = false)
45 | {
46 | VerifyFix(LanguageNames.CSharp, GetCSharpDiagnosticAnalyzer(), GetCSharpCodeFixProvider(), oldSource, newSource, codeFixIndex, allowNewCompilerDiagnostics);
47 | }
48 |
49 | ///
50 | /// Called to test a VB codefix when applied on the inputted string as a source
51 | ///
52 | /// A class in the form of a string before the CodeFix was applied to it
53 | /// A class in the form of a string after the CodeFix was applied to it
54 | /// Index determining which codefix to apply if there are multiple
55 | /// A bool controlling whether or not the test will fail if the CodeFix introduces other warnings after being applied
56 | protected void VerifyBasicFix(string oldSource, string newSource, int? codeFixIndex = null, bool allowNewCompilerDiagnostics = false)
57 | {
58 | VerifyFix(LanguageNames.VisualBasic, GetBasicDiagnosticAnalyzer(), GetBasicCodeFixProvider(), oldSource, newSource, codeFixIndex, allowNewCompilerDiagnostics);
59 | }
60 |
61 | ///
62 | /// General verifier for codefixes.
63 | /// Creates a Document from the source string, then gets diagnostics on it and applies the relevant codefixes.
64 | /// Then gets the string after the codefix is applied and compares it with the expected result.
65 | /// Note: If any codefix causes new diagnostics to show up, the test fails unless allowNewCompilerDiagnostics is set to true.
66 | ///
67 | /// The language the source code is in
68 | /// The analyzer to be applied to the source code
69 | /// The codefix to be applied to the code wherever the relevant Diagnostic is found
70 | /// A class in the form of a string before the CodeFix was applied to it
71 | /// A class in the form of a string after the CodeFix was applied to it
72 | /// Index determining which codefix to apply if there are multiple
73 | /// A bool controlling whether or not the test will fail if the CodeFix introduces other warnings after being applied
74 | private void VerifyFix(string language, DiagnosticAnalyzer analyzer, CodeFixProvider codeFixProvider, string oldSource, string newSource, int? codeFixIndex, bool allowNewCompilerDiagnostics)
75 | {
76 | var document = CreateDocument(oldSource, language);
77 | var analyzerDiagnostics = GetSortedDiagnosticsFromDocuments(analyzer, new[] { document });
78 | var compilerDiagnostics = GetCompilerDiagnostics(document);
79 | var attempts = analyzerDiagnostics.Length;
80 |
81 | for (int i = 0; i < attempts; ++i)
82 | {
83 | var actions = new List();
84 | var context = new CodeFixContext(document, analyzerDiagnostics[0], (a, d) => actions.Add(a), CancellationToken.None);
85 | codeFixProvider.RegisterCodeFixesAsync(context).Wait();
86 |
87 | if (!actions.Any())
88 | {
89 | break;
90 | }
91 |
92 | if (codeFixIndex != null)
93 | {
94 | document = ApplyFix(document, actions.ElementAt((int)codeFixIndex));
95 | break;
96 | }
97 |
98 | document = ApplyFix(document, actions.ElementAt(0));
99 | analyzerDiagnostics = GetSortedDiagnosticsFromDocuments(analyzer, new[] { document });
100 |
101 | var newCompilerDiagnostics = GetNewDiagnostics(compilerDiagnostics, GetCompilerDiagnostics(document));
102 |
103 | //check if applying the code fix introduced any new compiler diagnostics
104 | if (!allowNewCompilerDiagnostics && newCompilerDiagnostics.Any())
105 | {
106 | // Format and get the compiler diagnostics again so that the locations make sense in the output
107 | document = document.WithSyntaxRoot(Formatter.Format(document.GetSyntaxRootAsync().Result, Formatter.Annotation, document.Project.Solution.Workspace));
108 | newCompilerDiagnostics = GetNewDiagnostics(compilerDiagnostics, GetCompilerDiagnostics(document));
109 |
110 | Assert.IsTrue(false,
111 | string.Format("Fix introduced new compiler diagnostics:\r\n{0}\r\n\r\nNew document:\r\n{1}\r\n",
112 | string.Join("\r\n", newCompilerDiagnostics.Select(d => d.ToString())),
113 | document.GetSyntaxRootAsync().Result.ToFullString()));
114 | }
115 |
116 | //check if there are analyzer diagnostics left after the code fix
117 | if (!analyzerDiagnostics.Any())
118 | {
119 | break;
120 | }
121 | }
122 |
123 | //after applying all of the code fixes, compare the resulting string to the inputted one
124 | var actual = GetStringFromDocument(document);
125 | Assert.AreEqual(newSource, actual);
126 | }
127 | }
128 | }
--------------------------------------------------------------------------------
/ObjectMapper/ObjectMapper.GenerateImplementationAnalyzer/ObjectMapper.GenerateImplementationAnalyzer.Test/Verifiers/DiagnosticVerifier.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.CodeAnalysis;
2 | using Microsoft.CodeAnalysis.CSharp;
3 | using Microsoft.CodeAnalysis.Diagnostics;
4 | using Microsoft.VisualStudio.TestTools.UnitTesting;
5 | using System.Collections.Generic;
6 | using System.Linq;
7 | using System.Text;
8 |
9 | namespace TestHelper
10 | {
11 | ///
12 | /// Superclass of all Unit Tests for DiagnosticAnalyzers
13 | ///
14 | public abstract partial class DiagnosticVerifier
15 | {
16 | #region To be implemented by Test classes
17 | ///
18 | /// Get the CSharp analyzer being tested - to be implemented in non-abstract class
19 | ///
20 | protected virtual DiagnosticAnalyzer GetCSharpDiagnosticAnalyzer()
21 | {
22 | return null;
23 | }
24 |
25 | ///
26 | /// Get the Visual Basic analyzer being tested (C#) - to be implemented in non-abstract class
27 | ///
28 | protected virtual DiagnosticAnalyzer GetBasicDiagnosticAnalyzer()
29 | {
30 | return null;
31 | }
32 | #endregion
33 |
34 | #region Verifier wrappers
35 |
36 | ///
37 | /// Called to test a C# DiagnosticAnalyzer when applied on the single inputted string as a source
38 | /// Note: input a DiagnosticResult for each Diagnostic expected
39 | ///
40 | /// A class in the form of a string to run the analyzer on
41 | /// DiagnosticResults that should appear after the analyzer is run on the source
42 | protected void VerifyCSharpDiagnostic(string source, params DiagnosticResult[] expected)
43 | {
44 | VerifyDiagnostics(new[] { source }, LanguageNames.CSharp, GetCSharpDiagnosticAnalyzer(), expected);
45 | }
46 |
47 | ///
48 | /// Called to test a VB DiagnosticAnalyzer when applied on the single inputted string as a source
49 | /// Note: input a DiagnosticResult for each Diagnostic expected
50 | ///
51 | /// A class in the form of a string to run the analyzer on
52 | /// DiagnosticResults that should appear after the analyzer is run on the source
53 | protected void VerifyBasicDiagnostic(string source, params DiagnosticResult[] expected)
54 | {
55 | VerifyDiagnostics(new[] { source }, LanguageNames.VisualBasic, GetBasicDiagnosticAnalyzer(), expected);
56 | }
57 |
58 | ///
59 | /// Called to test a C# DiagnosticAnalyzer when applied on the inputted strings as a source
60 | /// Note: input a DiagnosticResult for each Diagnostic expected
61 | ///
62 | /// An array of strings to create source documents from to run the analyzers on
63 | /// DiagnosticResults that should appear after the analyzer is run on the sources
64 | protected void VerifyCSharpDiagnostic(string[] sources, params DiagnosticResult[] expected)
65 | {
66 | VerifyDiagnostics(sources, LanguageNames.CSharp, GetCSharpDiagnosticAnalyzer(), expected);
67 | }
68 |
69 | ///
70 | /// Called to test a VB DiagnosticAnalyzer when applied on the inputted strings as a source
71 | /// Note: input a DiagnosticResult for each Diagnostic expected
72 | ///
73 | /// An array of strings to create source documents from to run the analyzers on
74 | /// DiagnosticResults that should appear after the analyzer is run on the sources
75 | protected void VerifyBasicDiagnostic(string[] sources, params DiagnosticResult[] expected)
76 | {
77 | VerifyDiagnostics(sources, LanguageNames.VisualBasic, GetBasicDiagnosticAnalyzer(), expected);
78 | }
79 |
80 | ///
81 | /// General method that gets a collection of actual diagnostics found in the source after the analyzer is run,
82 | /// then verifies each of them.
83 | ///
84 | /// An array of strings to create source documents from to run the analyzers on
85 | /// The language of the classes represented by the source strings
86 | /// The analyzer to be run on the source code
87 | /// DiagnosticResults that should appear after the analyzer is run on the sources
88 | private void VerifyDiagnostics(string[] sources, string language, DiagnosticAnalyzer analyzer, params DiagnosticResult[] expected)
89 | {
90 | var diagnostics = GetSortedDiagnostics(sources, language, analyzer);
91 | VerifyDiagnosticResults(diagnostics, analyzer, expected);
92 | }
93 |
94 | #endregion
95 |
96 | #region Actual comparisons and verifications
97 | ///
98 | /// Checks each of the actual Diagnostics found and compares them with the corresponding DiagnosticResult in the array of expected results.
99 | /// Diagnostics are considered equal only if the DiagnosticResultLocation, Id, Severity, and Message of the DiagnosticResult match the actual diagnostic.
100 | ///
101 | /// The Diagnostics found by the compiler after running the analyzer on the source code
102 | /// The analyzer that was being run on the sources
103 | /// Diagnostic Results that should have appeared in the code
104 | private static void VerifyDiagnosticResults(IEnumerable actualResults, DiagnosticAnalyzer analyzer, params DiagnosticResult[] expectedResults)
105 | {
106 | int expectedCount = expectedResults.Count();
107 | int actualCount = actualResults.Count();
108 |
109 | if (expectedCount != actualCount)
110 | {
111 | string diagnosticsOutput = actualResults.Any() ? FormatDiagnostics(analyzer, actualResults.ToArray()) : " NONE.";
112 |
113 | Assert.IsTrue(false,
114 | string.Format("Mismatch between number of diagnostics returned, expected \"{0}\" actual \"{1}\"\r\n\r\nDiagnostics:\r\n{2}\r\n", expectedCount, actualCount, diagnosticsOutput));
115 | }
116 |
117 | for (int i = 0; i < expectedResults.Length; i++)
118 | {
119 | var actual = actualResults.ElementAt(i);
120 | var expected = expectedResults[i];
121 |
122 | if (expected.Line == -1 && expected.Column == -1)
123 | {
124 | if (actual.Location != Location.None)
125 | {
126 | Assert.IsTrue(false,
127 | string.Format("Expected:\nA project diagnostic with No location\nActual:\n{0}",
128 | FormatDiagnostics(analyzer, actual)));
129 | }
130 | }
131 | else
132 | {
133 | VerifyDiagnosticLocation(analyzer, actual, actual.Location, expected.Locations.First());
134 | var additionalLocations = actual.AdditionalLocations.ToArray();
135 |
136 | if (additionalLocations.Length != expected.Locations.Length - 1)
137 | {
138 | Assert.IsTrue(false,
139 | string.Format("Expected {0} additional locations but got {1} for Diagnostic:\r\n {2}\r\n",
140 | expected.Locations.Length - 1, additionalLocations.Length,
141 | FormatDiagnostics(analyzer, actual)));
142 | }
143 |
144 | for (int j = 0; j < additionalLocations.Length; ++j)
145 | {
146 | VerifyDiagnosticLocation(analyzer, actual, additionalLocations[j], expected.Locations[j + 1]);
147 | }
148 | }
149 |
150 | if (actual.Id != expected.Id)
151 | {
152 | Assert.IsTrue(false,
153 | string.Format("Expected diagnostic id to be \"{0}\" was \"{1}\"\r\n\r\nDiagnostic:\r\n {2}\r\n",
154 | expected.Id, actual.Id, FormatDiagnostics(analyzer, actual)));
155 | }
156 |
157 | if (actual.Severity != expected.Severity)
158 | {
159 | Assert.IsTrue(false,
160 | string.Format("Expected diagnostic severity to be \"{0}\" was \"{1}\"\r\n\r\nDiagnostic:\r\n {2}\r\n",
161 | expected.Severity, actual.Severity, FormatDiagnostics(analyzer, actual)));
162 | }
163 |
164 | if (actual.GetMessage() != expected.Message)
165 | {
166 | Assert.IsTrue(false,
167 | string.Format("Expected diagnostic message to be \"{0}\" was \"{1}\"\r\n\r\nDiagnostic:\r\n {2}\r\n",
168 | expected.Message, actual.GetMessage(), FormatDiagnostics(analyzer, actual)));
169 | }
170 | }
171 | }
172 |
173 | ///
174 | /// Helper method to VerifyDiagnosticResult that checks the location of a diagnostic and compares it with the location in the expected DiagnosticResult.
175 | ///
176 | /// The analyzer that was being run on the sources
177 | /// The diagnostic that was found in the code
178 | /// The Location of the Diagnostic found in the code
179 | /// The DiagnosticResultLocation that should have been found
180 | private static void VerifyDiagnosticLocation(DiagnosticAnalyzer analyzer, Diagnostic diagnostic, Location actual, DiagnosticResultLocation expected)
181 | {
182 | var actualSpan = actual.GetLineSpan();
183 |
184 | Assert.IsTrue(actualSpan.Path == expected.Path || (actualSpan.Path != null && actualSpan.Path.Contains("Test0.") && expected.Path.Contains("Test.")),
185 | string.Format("Expected diagnostic to be in file \"{0}\" was actually in file \"{1}\"\r\n\r\nDiagnostic:\r\n {2}\r\n",
186 | expected.Path, actualSpan.Path, FormatDiagnostics(analyzer, diagnostic)));
187 |
188 | var actualLinePosition = actualSpan.StartLinePosition;
189 |
190 | // Only check line position if there is an actual line in the real diagnostic
191 | if (actualLinePosition.Line > 0)
192 | {
193 | if (actualLinePosition.Line + 1 != expected.Line)
194 | {
195 | Assert.IsTrue(false,
196 | string.Format("Expected diagnostic to be on line \"{0}\" was actually on line \"{1}\"\r\n\r\nDiagnostic:\r\n {2}\r\n",
197 | expected.Line, actualLinePosition.Line + 1, FormatDiagnostics(analyzer, diagnostic)));
198 | }
199 | }
200 |
201 | // Only check column position if there is an actual column position in the real diagnostic
202 | if (actualLinePosition.Character > 0)
203 | {
204 | if (actualLinePosition.Character + 1 != expected.Column)
205 | {
206 | Assert.IsTrue(false,
207 | string.Format("Expected diagnostic to start at column \"{0}\" was actually at column \"{1}\"\r\n\r\nDiagnostic:\r\n {2}\r\n",
208 | expected.Column, actualLinePosition.Character + 1, FormatDiagnostics(analyzer, diagnostic)));
209 | }
210 | }
211 | }
212 | #endregion
213 |
214 | #region Formatting Diagnostics
215 | ///
216 | /// Helper method to format a Diagnostic into an easily readable string
217 | ///
218 | /// The analyzer that this verifier tests
219 | /// The Diagnostics to be formatted
220 | /// The Diagnostics formatted as a string
221 | private static string FormatDiagnostics(DiagnosticAnalyzer analyzer, params Diagnostic[] diagnostics)
222 | {
223 | var builder = new StringBuilder();
224 | for (int i = 0; i < diagnostics.Length; ++i)
225 | {
226 | builder.AppendLine("// " + diagnostics[i].ToString());
227 |
228 | var analyzerType = analyzer.GetType();
229 | var rules = analyzer.SupportedDiagnostics;
230 |
231 | foreach (var rule in rules)
232 | {
233 | if (rule != null && rule.Id == diagnostics[i].Id)
234 | {
235 | var location = diagnostics[i].Location;
236 | if (location == Location.None)
237 | {
238 | builder.AppendFormat("GetGlobalResult({0}.{1})", analyzerType.Name, rule.Id);
239 | }
240 | else
241 | {
242 | Assert.IsTrue(location.IsInSource,
243 | $"Test base does not currently handle diagnostics in metadata locations. Diagnostic in metadata: {diagnostics[i]}\r\n");
244 |
245 | string resultMethodName = diagnostics[i].Location.SourceTree.FilePath.EndsWith(".cs") ? "GetCSharpResultAt" : "GetBasicResultAt";
246 | var linePosition = diagnostics[i].Location.GetLineSpan().StartLinePosition;
247 |
248 | builder.AppendFormat("{0}({1}, {2}, {3}.{4})",
249 | resultMethodName,
250 | linePosition.Line + 1,
251 | linePosition.Character + 1,
252 | analyzerType.Name,
253 | rule.Id);
254 | }
255 |
256 | if (i != diagnostics.Length - 1)
257 | {
258 | builder.Append(',');
259 | }
260 |
261 | builder.AppendLine();
262 | break;
263 | }
264 | }
265 | }
266 | return builder.ToString();
267 | }
268 | #endregion
269 | }
270 | }
271 |
--------------------------------------------------------------------------------
/ObjectMapper/ObjectMapper.GenerateImplementationAnalyzer/ObjectMapper.GenerateImplementationAnalyzer.Test/packages.config:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/ObjectMapper/ObjectMapper.GenerateImplementationAnalyzer/ObjectMapper.GenerateImplementationAnalyzer.Vsix/ObjectMapper.GenerateImplementationAnalyzer.Vsix.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | 14.0
5 | $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)
6 |
7 |
8 |
9 | Debug
10 | AnyCPU
11 | 2.0
12 | {82b43b9b-a64c-4715-b499-d71e9ca2bd60};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}
13 | {2AF839D8-C2E5-45CC-BA8E-04C20DD75B96}
14 | Library
15 | Properties
16 | ObjectMapper.GenerateImplementationAnalyzer.Vsix
17 | ObjectMapper.GenerateImplementationAnalyzer.Vsix
18 | v4.5.2
19 | false
20 | false
21 | false
22 | false
23 | false
24 | false
25 | Roslyn
26 |
27 |
28 | true
29 | full
30 | false
31 | bin\Debug\
32 | DEBUG;TRACE
33 | prompt
34 | 4
35 |
36 |
37 | pdbonly
38 | true
39 | bin\Release\
40 | TRACE
41 | prompt
42 | 4
43 |
44 |
45 | Program
46 | $(DevEnvDir)devenv.exe
47 | /rootsuffix Roslyn
48 |
49 |
50 |
51 | Designer
52 |
53 |
54 |
55 |
56 | {492F3804-A43F-4E82-B649-8AD0D967A830}
57 | ObjectMapper.GenerateImplementationAnalyzer
58 | BuiltProjectOutputGroup%3bBuiltProjectOutputGroupDependencies%3bGetCopyToOutputDirectoryItems%3bSatelliteDllsProjectOutputGroup%3b
59 | DebugSymbolsProjectOutputGroup%3b
60 |
61 |
62 |
63 |
64 |
71 |
--------------------------------------------------------------------------------
/ObjectMapper/ObjectMapper.GenerateImplementationAnalyzer/ObjectMapper.GenerateImplementationAnalyzer.Vsix/source.extension.vsixmanifest:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | ObjectMapper.GenerateImplementationAnalyzer
6 | This is a sample diagnostic extension for the .NET Compiler Platform ("Roslyn").
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
--------------------------------------------------------------------------------
/ObjectMapper/ObjectMapper.GenerateImplementationAnalyzer/ObjectMapper.GenerateImplementationAnalyzer/AttributeUsageCodeAnalyzer.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.CodeAnalysis;
2 | using Microsoft.CodeAnalysis.CSharp;
3 | using Microsoft.CodeAnalysis.CSharp.Syntax;
4 | using Microsoft.CodeAnalysis.Diagnostics;
5 | using ObjectMapper.GenerateImplementationAnalyzer.Utilities;
6 | using System;
7 | using System.Collections.Generic;
8 | using System.Collections.Immutable;
9 | using System.Linq;
10 | using System.Text;
11 | using System.Threading.Tasks;
12 |
13 | namespace ObjectMapper.GenerateImplementationAnalyzer
14 | {
15 | ///
16 | /// Analyzer class for analyzing and reporting mapping attribute for mapping methods missuse.
17 | ///
18 | ///
19 | [DiagnosticAnalyzer(LanguageNames.CSharp)]
20 | public class AttributeUsageCodeAnalyzer : DiagnosticAnalyzer
21 | {
22 | public const string DiagnosticId = "OMAU01";
23 |
24 | private static readonly LocalizableString Title = new LocalizableResourceString(nameof(Resources.AttributeUsageAnalyzerTitle), Resources.ResourceManager, typeof(Resources));
25 | private static readonly LocalizableString MessageFormat = new LocalizableResourceString(nameof(Resources.AttributeUsageAnalyzerMessageFormat), Resources.ResourceManager, typeof(Resources));
26 | private static readonly LocalizableString Description = new LocalizableResourceString(nameof(Resources.AttributeUsageAnalyzerDescription), Resources.ResourceManager, typeof(Resources));
27 | private const string Category = "Attribute usage";
28 |
29 | private static DiagnosticDescriptor Rule = new DiagnosticDescriptor(DiagnosticId, Title, MessageFormat, Category, DiagnosticSeverity.Error, isEnabledByDefault: true, description: Description);
30 |
31 | ///
32 | /// Returns a set of descriptors for the diagnostics that this analyzer is capable of producing.
33 | ///
34 | public override ImmutableArray SupportedDiagnostics { get { return ImmutableArray.Create(Rule); } }
35 |
36 | ///
37 | /// Called once at session start to register actions in the analysis context.
38 | ///
39 | ///
40 | public override void Initialize(AnalysisContext context)
41 | {
42 | if (context == null) return;
43 |
44 | context.RegisterSyntaxNodeAction(AnalyzeNode, SyntaxKind.Attribute);
45 | }
46 |
47 | ///
48 | /// Analyzes the node.
49 | ///
50 | /// The context.
51 | private void AnalyzeNode(SyntaxNodeAnalysisContext context)
52 | {
53 | var attributeNode = context.Node as AttributeSyntax;
54 | SimpleNameSyntax sns = (attributeNode.Name as SimpleNameSyntax) ?? (attributeNode.Name as QualifiedNameSyntax).Right;
55 | var className = sns?.Identifier.Text;
56 | if (className != "ObjectMapperMethod" && className != "ObjectMapperMethodAttribute")
57 | {
58 | return;
59 | }
60 |
61 | // symbol is ctor call
62 | var symbol = context.SemanticModel.GetSymbolInfo(attributeNode).Symbol.ContainingType;
63 | if (symbol == null)
64 | {
65 | return;
66 | }
67 |
68 | var fullSymbolName = symbol.ToDisplayString();
69 | if (fullSymbolName != "ObjectMapper.Framework.ObjectMapperMethodAttribute")
70 | {
71 | return;
72 | }
73 |
74 | if (!FrameworkHelpers.IsObjectMapperFrameworkAssembly(symbol.ContainingAssembly))
75 | {
76 | return;
77 | }
78 |
79 | var methodSyntax = attributeNode.Ancestors().OfType().FirstOrDefault();
80 | if (methodSyntax == null)
81 | {
82 | // missused attribute - compiler will take care of that
83 | return;
84 | }
85 |
86 | var methodSymbol = context.SemanticModel.GetDeclaredSymbol(methodSyntax);
87 | if (methodSymbol.ReturnsVoid && methodSymbol.Parameters.Length == 2)
88 | {
89 | // correct use
90 | return;
91 | }
92 |
93 | var diagnostic = Diagnostic.Create(Rule, attributeNode.GetLocation());
94 | context.ReportDiagnostic(diagnostic);
95 | }
96 | }
97 | }
98 |
--------------------------------------------------------------------------------
/ObjectMapper/ObjectMapper.GenerateImplementationAnalyzer/ObjectMapper.GenerateImplementationAnalyzer/Diagnostic.nuspec:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | SimpleObjectMapper
5 | 1.2.1
6 | Object Mapper
7 | Nejc Skofic
8 | Nejc Skofic
9 | https://github.com/nejcskofic/ObjectMapper/blob/master/LICENSE
10 | https://github.com/nejcskofic/ObjectMapper
11 |
12 | false
13 | Simple object mapping library with included analyser and code generator for generating mapping implementation.
14 | Added mapping attribute used by generator to generate mapping code.
15 | Copyright 2016
16 | object, mapper, analyzers, code, generator
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
--------------------------------------------------------------------------------
/ObjectMapper/ObjectMapper.GenerateImplementationAnalyzer/ObjectMapper.GenerateImplementationAnalyzer/GenerateImplementationCodeAnalyzer.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Collections.Immutable;
4 | using System.Linq;
5 | using System.Threading;
6 | using Microsoft.CodeAnalysis;
7 | using Microsoft.CodeAnalysis.CSharp;
8 | using Microsoft.CodeAnalysis.CSharp.Syntax;
9 | using Microsoft.CodeAnalysis.Diagnostics;
10 | using ObjectMapper.GenerateImplementationAnalyzer.Utilities;
11 |
12 | namespace ObjectMapper.GenerateImplementationAnalyzer
13 | {
14 | ///
15 | /// Analyzer class for registering code generator for object mapper interfaces.
16 | ///
17 | ///
18 | [DiagnosticAnalyzer(LanguageNames.CSharp)]
19 | public class GenerateImplementationCodeAnalyzer : DiagnosticAnalyzer
20 | {
21 | public const string DiagnosticId = "OMCG01";
22 |
23 | private static readonly LocalizableString Title = new LocalizableResourceString(nameof(Resources.GenerateImplementationAnalyzerTitle), Resources.ResourceManager, typeof(Resources));
24 | private static readonly LocalizableString MessageFormat = new LocalizableResourceString(nameof(Resources.GenerateImplementationAnalyzerMessageFormat), Resources.ResourceManager, typeof(Resources));
25 | private static readonly LocalizableString Description = new LocalizableResourceString(nameof(Resources.GenerateImplementationAnalyzerDescription), Resources.ResourceManager, typeof(Resources));
26 | private const string Category = "Code generation";
27 |
28 | private static DiagnosticDescriptor Rule = new DiagnosticDescriptor(DiagnosticId, Title, MessageFormat, Category, DiagnosticSeverity.Hidden, isEnabledByDefault: true, description: Description);
29 |
30 | ///
31 | /// Returns a set of descriptors for the diagnostics that this analyzer is capable of producing.
32 | ///
33 | public override ImmutableArray SupportedDiagnostics { get { return ImmutableArray.Create(Rule); } }
34 |
35 | ///
36 | /// Called once at session start to register actions in the analysis context.
37 | ///
38 | ///
39 | public override void Initialize(AnalysisContext context)
40 | {
41 | if (context == null) return;
42 |
43 | context.RegisterSyntaxNodeAction(AnalyzeNode, SyntaxKind.SimpleBaseType, SyntaxKind.MethodDeclaration);
44 | }
45 |
46 | ///
47 | /// Analyzes the node.
48 | ///
49 | /// The context.
50 | private void AnalyzeNode(SyntaxNodeAnalysisContext context)
51 | {
52 | var baseNode = context.Node as SimpleBaseTypeSyntax;
53 | if (baseNode != null)
54 | {
55 | CheckForObjectMapperBaseInterface(baseNode, context);
56 | return;
57 | }
58 |
59 | var methodNode = context.Node as MethodDeclarationSyntax;
60 | if (methodNode != null && CheckForObjectMapperMethod(methodNode, context))
61 | {
62 | return;
63 | }
64 | if (methodNode != null && CheckForMethodWithObjectMapperAttribute(methodNode, context))
65 | {
66 | return;
67 | }
68 | }
69 |
70 | ///
71 | /// Checks if symbol under carret is object mapper interface.
72 | ///
73 | /// The node.
74 | /// The context.
75 | private static void CheckForObjectMapperBaseInterface(SimpleBaseTypeSyntax node, SyntaxNodeAnalysisContext context)
76 | {
77 | SimpleNameSyntax sns = (node.Type as SimpleNameSyntax) ?? (node.Type as QualifiedNameSyntax).Right;
78 | var className = sns?.Identifier.Text;
79 | if (className != "IObjectMapper" && className != "IObjectMapperAdapter")
80 | {
81 | return;
82 | }
83 | var symbol = context.SemanticModel.GetSymbolInfo(sns).Symbol as INamedTypeSymbol;
84 | if (symbol == null || symbol.TypeKind != TypeKind.Interface || !symbol.IsGenericType)
85 | {
86 | return;
87 | }
88 |
89 | var fullSymbolName = symbol.OriginalDefinition.ToDisplayString();
90 | if (fullSymbolName != "ObjectMapper.Framework.IObjectMapper" && fullSymbolName != "ObjectMapper.Framework.IObjectMapperAdapter")
91 | {
92 | return;
93 | }
94 |
95 | if (!FrameworkHelpers.IsObjectMapperFrameworkAssembly(symbol.OriginalDefinition.ContainingAssembly))
96 | {
97 | return;
98 | }
99 |
100 | var diagnostic = Diagnostic.Create(Rule, node.GetLocation());
101 | context.ReportDiagnostic(diagnostic);
102 | }
103 |
104 | ///
105 | /// Checks if method is implementation of object mapper interface.
106 | ///
107 | /// The node.
108 | /// The context.
109 | private static bool CheckForObjectMapperMethod(MethodDeclarationSyntax node, SyntaxNodeAnalysisContext context)
110 | {
111 | if (node?.Identifier.Text != "MapObject")
112 | {
113 | return false;
114 | }
115 | var symbol = context.SemanticModel.GetDeclaredSymbol(node);
116 | if (symbol == null || symbol.Kind != SymbolKind.Method ||
117 | (symbol.MethodKind != MethodKind.Ordinary && symbol.MethodKind != MethodKind.ExplicitInterfaceImplementation) ||
118 | symbol.DeclaredAccessibility != Accessibility.Public || !symbol.ReturnsVoid ||
119 | (symbol.Parameters.Length != 1 && symbol.Parameters.Length != 2))
120 | {
121 | return false;
122 | }
123 |
124 | // find out if we have implementation of framework interfaces
125 | INamedTypeSymbol mapperInterface = null;
126 | if (symbol.Parameters.Length == 1)
127 | {
128 | mapperInterface = symbol.ContainingType.AllInterfaces.FirstOrDefault(x =>
129 | x.OriginalDefinition.ToDisplayString() == "ObjectMapper.Framework.IObjectMapper" &&
130 | FrameworkHelpers.IsObjectMapperFrameworkAssembly(x.OriginalDefinition.ContainingAssembly) &&
131 | x.TypeArguments[0].Equals(symbol.Parameters[0].Type));
132 | }
133 | else if (symbol.Parameters.Length == 2)
134 | {
135 | mapperInterface = symbol.ContainingType.AllInterfaces.FirstOrDefault(x =>
136 | x.OriginalDefinition.ToDisplayString() == "ObjectMapper.Framework.IObjectMapperAdapter" &&
137 | FrameworkHelpers.IsObjectMapperFrameworkAssembly(x.OriginalDefinition.ContainingAssembly) &&
138 | (x.TypeArguments[0].Equals(symbol.Parameters[0].Type) && x.TypeArguments[1].Equals(symbol.Parameters[1].Type)
139 | || x.TypeArguments[0].Equals(symbol.Parameters[1].Type) && x.TypeArguments[1].Equals(symbol.Parameters[0].Type)));
140 | }
141 | if (mapperInterface == null)
142 | {
143 | return false;
144 | }
145 |
146 | // final check
147 | bool implementsInterfaceMethod = false;
148 | foreach (IMethodSymbol member in mapperInterface.GetMembers().Where(x => x.Kind == SymbolKind.Method))
149 | {
150 | if (symbol.Equals(symbol.ContainingType.FindImplementationForInterfaceMember(member)))
151 | {
152 | implementsInterfaceMethod = true;
153 | break;
154 | }
155 | }
156 | if (!implementsInterfaceMethod)
157 | {
158 | return false;
159 | }
160 |
161 | var diagnostic = Diagnostic.Create(Rule, node.Identifier.GetLocation());
162 | context.ReportDiagnostic(diagnostic);
163 | return true;
164 | }
165 |
166 | ///
167 | /// Checks for method with object mapper attribute.
168 | ///
169 | /// The node.
170 | /// The context.
171 | ///
172 | private static bool CheckForMethodWithObjectMapperAttribute(MethodDeclarationSyntax node, SyntaxNodeAnalysisContext context)
173 | {
174 | if (node.AttributeLists.Count == 0)
175 | {
176 | return false;
177 | }
178 |
179 | var candidateAttributes = node.AttributeLists.SelectMany(x => x.Attributes).Where(x =>
180 | {
181 | SimpleNameSyntax sns = (x.Name as SimpleNameSyntax) ?? (x.Name as QualifiedNameSyntax).Right;
182 | var className = sns?.Identifier.Text;
183 | return className == "ObjectMapperMethod" || className == "ObjectMapperMethodAttribute";
184 | }).ToList();
185 | if (candidateAttributes.Count == 0)
186 | {
187 | return false;
188 | }
189 |
190 | var methodSymbol = context.SemanticModel.GetDeclaredSymbol(node);
191 | if (!methodSymbol.ReturnsVoid || methodSymbol.Parameters.Length != 2)
192 | {
193 | return false;
194 | }
195 |
196 | if (!candidateAttributes.Any(x =>
197 | {
198 | var symbol = context.SemanticModel.GetSymbolInfo(x).Symbol?.ContainingType;
199 | if (symbol == null)
200 | {
201 | return false;
202 | }
203 |
204 | var fullSymbolName = symbol.ToDisplayString();
205 | if (fullSymbolName != "ObjectMapper.Framework.ObjectMapperMethodAttribute")
206 | {
207 | return false;
208 | }
209 |
210 | if (!FrameworkHelpers.IsObjectMapperFrameworkAssembly(symbol.ContainingAssembly))
211 | {
212 | return false;
213 | }
214 |
215 | return true;
216 | }))
217 | {
218 | return false;
219 | }
220 |
221 | var diagnostic = Diagnostic.Create(Rule, node.Identifier.GetLocation());
222 | context.ReportDiagnostic(diagnostic);
223 | return true;
224 | }
225 | }
226 | }
227 |
--------------------------------------------------------------------------------
/ObjectMapper/ObjectMapper.GenerateImplementationAnalyzer/ObjectMapper.GenerateImplementationAnalyzer/GenerateImplementationCodeGenerator.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Collections.Immutable;
4 | using System.Composition;
5 | using System.Linq;
6 | using System.Threading;
7 | using System.Threading.Tasks;
8 | using Microsoft.CodeAnalysis;
9 | using Microsoft.CodeAnalysis.CodeFixes;
10 | using Microsoft.CodeAnalysis.CodeActions;
11 | using Microsoft.CodeAnalysis.CSharp;
12 | using Microsoft.CodeAnalysis.CSharp.Syntax;
13 | using Microsoft.CodeAnalysis.Rename;
14 | using Microsoft.CodeAnalysis.Text;
15 | using Microsoft.CodeAnalysis.Formatting;
16 |
17 | namespace ObjectMapper.GenerateImplementationAnalyzer
18 | {
19 | [ExportCodeFixProvider(LanguageNames.CSharp, Name = nameof(GenerateImplementationCodeGenerator)), Shared]
20 | public class GenerateImplementationCodeGenerator : CodeFixProvider
21 | {
22 | private const string title = "Generate implementation";
23 |
24 | public sealed override ImmutableArray FixableDiagnosticIds
25 | {
26 | get { return ImmutableArray.Create(GenerateImplementationCodeAnalyzer.DiagnosticId); }
27 | }
28 |
29 | public sealed override FixAllProvider GetFixAllProvider()
30 | {
31 | // See https://github.com/dotnet/roslyn/blob/master/docs/analyzers/FixAllProvider.md for more information on Fix All Providers
32 | return WellKnownFixAllProviders.BatchFixer;
33 | }
34 |
35 | public sealed override async Task RegisterCodeFixesAsync(CodeFixContext context)
36 | {
37 | var root = await context.Document.GetSyntaxRootAsync(context.CancellationToken).ConfigureAwait(false);
38 |
39 | var diagnostic = context.Diagnostics.First();
40 | var diagnosticSpan = diagnostic.Location.SourceSpan;
41 |
42 | // Find the type declaration identified by the diagnostic.
43 | var baseTypeDeclaration = root.FindToken(diagnosticSpan.Start).Parent.AncestorsAndSelf().OfType().FirstOrDefault();
44 | if (baseTypeDeclaration != null)
45 | {
46 | context.RegisterCodeFix(
47 | CodeAction.Create(
48 | title: title,
49 | createChangedDocument: c => GenerateInterfaceImplementationAsync(context.Document, baseTypeDeclaration, c),
50 | equivalenceKey: title),
51 | diagnostic);
52 | return;
53 | }
54 |
55 | var methodDeclaration = root.FindToken(diagnosticSpan.Start).Parent.AncestorsAndSelf().OfType().FirstOrDefault();
56 | if (methodDeclaration != null)
57 | {
58 | context.RegisterCodeFix(
59 | CodeAction.Create(
60 | title: title,
61 | createChangedDocument: c => GenerateMethodImplementationAsync(context.Document, methodDeclaration, c),
62 | equivalenceKey: title),
63 | diagnostic);
64 | return;
65 | }
66 | }
67 |
68 | private static async Task GenerateInterfaceImplementationAsync(Document document, SimpleBaseTypeSyntax baseTypeSyntax, CancellationToken cancellationToken)
69 | {
70 | var semanticModel = await document.GetSemanticModelAsync(cancellationToken);
71 |
72 | SimpleNameSyntax sns = (baseTypeSyntax.Type as SimpleNameSyntax) ?? (baseTypeSyntax.Type as QualifiedNameSyntax).Right;
73 | var interfaceSymbol = semanticModel.GetSymbolInfo(sns).Symbol as INamedTypeSymbol;
74 | if (interfaceSymbol == null || interfaceSymbol.TypeKind != TypeKind.Interface) return document;
75 |
76 | var originalClassDefinitionSyntax = (ClassDeclarationSyntax)baseTypeSyntax.Parent.Parent;
77 | ClassDeclarationSyntax modifiedClassDefinitionSyntax = null;
78 | if (interfaceSymbol.Name == "IObjectMapper" && interfaceSymbol.TypeArguments.Length == 1)
79 | {
80 | var sourceClassSymbol = semanticModel.GetDeclaredSymbol(originalClassDefinitionSyntax);
81 | var targetClassSymbol = interfaceSymbol.TypeArguments[0].OriginalDefinition as INamedTypeSymbol;
82 | if (sourceClassSymbol == null || targetClassSymbol == null) return document;
83 |
84 | var matchedProperties = RetrieveMatchedProperties(sourceClassSymbol, targetClassSymbol);
85 |
86 | var updatedMethods = new Dictionary();
87 | var addedMethods = new List();
88 |
89 | foreach (IMethodSymbol member in interfaceSymbol.GetMembers().Where(x => x.Kind == SymbolKind.Method))
90 | {
91 | var method = sourceClassSymbol.FindImplementationForInterfaceMember(member) as IMethodSymbol;
92 | MethodDeclarationSyntax methodSyntax = null;
93 | if (method != null)
94 | {
95 | methodSyntax = await method.DeclaringSyntaxReferences[0].GetSyntaxAsync(cancellationToken) as MethodDeclarationSyntax;
96 | var newMethodSyntax = methodSyntax.WithBody(GenerateMethodBody(member, matchedProperties, semanticModel, originalClassDefinitionSyntax.Span.End - 1));
97 | updatedMethods.Add(methodSyntax, newMethodSyntax);
98 | }
99 | else
100 | {
101 | methodSyntax = GenerateMethodImplementation(member, semanticModel, originalClassDefinitionSyntax.Span.End - 1).
102 | WithBody(GenerateMethodBody(member, matchedProperties, semanticModel, originalClassDefinitionSyntax.Span.End - 1));
103 | addedMethods.Add(methodSyntax);
104 | }
105 | }
106 |
107 | modifiedClassDefinitionSyntax = originalClassDefinitionSyntax.ReplaceNodes(updatedMethods.Keys.AsEnumerable(), (n1, n2) => updatedMethods[n1]).AddMembers(addedMethods.ToArray());
108 | }
109 | else if (interfaceSymbol.Name == "IObjectMapperAdapter" && interfaceSymbol.TypeArguments.Length == 2)
110 | {
111 | var adapterClassSymbol = semanticModel.GetDeclaredSymbol(originalClassDefinitionSyntax);
112 | var sourceClassSymbol = interfaceSymbol.TypeArguments[0].OriginalDefinition as INamedTypeSymbol;
113 | var targetClassSymbol = interfaceSymbol.TypeArguments[1].OriginalDefinition as INamedTypeSymbol;
114 | if (sourceClassSymbol == null || targetClassSymbol == null) return document;
115 |
116 | var matchedProperties = RetrieveMatchedProperties(sourceClassSymbol, targetClassSymbol);
117 |
118 | var updatedMethods = new Dictionary();
119 | var addedMethods = new List();
120 |
121 | foreach (IMethodSymbol member in interfaceSymbol.GetMembers().Where(x => x.Kind == SymbolKind.Method))
122 | {
123 | var matchingPropertyList = matchedProperties;
124 | // check if we have to switch matched properties
125 | if (member.Parameters.Length == 2 && !interfaceSymbol.TypeArguments[0].Equals(member.Parameters[0].Type))
126 | {
127 | matchingPropertyList = matchingPropertyList.Select(x => new MatchedPropertySymbols { Source = x.Target, Target = x.Source });
128 | }
129 |
130 | var method = adapterClassSymbol.FindImplementationForInterfaceMember(member) as IMethodSymbol;
131 | MethodDeclarationSyntax methodSyntax = null;
132 | if (method != null)
133 | {
134 | methodSyntax = await method.DeclaringSyntaxReferences[0].GetSyntaxAsync(cancellationToken) as MethodDeclarationSyntax;
135 | var newMethodSyntax = methodSyntax.WithBody(GenerateMethodBody(member, matchingPropertyList, semanticModel, originalClassDefinitionSyntax.Span.End - 1));
136 | updatedMethods.Add(methodSyntax, newMethodSyntax);
137 | }
138 | else
139 | {
140 | methodSyntax = GenerateMethodImplementation(member, semanticModel, originalClassDefinitionSyntax.Span.End - 1).
141 | WithBody(GenerateMethodBody(member, matchingPropertyList, semanticModel, originalClassDefinitionSyntax.Span.End - 1));
142 | addedMethods.Add(methodSyntax);
143 | }
144 | }
145 |
146 | modifiedClassDefinitionSyntax = originalClassDefinitionSyntax.ReplaceNodes(updatedMethods.Keys.AsEnumerable(), (n1, n2) => updatedMethods[n1]).AddMembers(addedMethods.ToArray());
147 | }
148 |
149 | if (modifiedClassDefinitionSyntax == null)
150 | {
151 | return document;
152 | }
153 |
154 | // replace root and return modified document
155 | var root = await document.GetSyntaxRootAsync(cancellationToken);
156 | var newRoot = root.ReplaceNode(originalClassDefinitionSyntax, modifiedClassDefinitionSyntax);
157 | var newDocument = document.WithSyntaxRoot(newRoot);
158 | return newDocument;
159 | }
160 |
161 | private static async Task GenerateMethodImplementationAsync(Document document, MethodDeclarationSyntax methodSyntax, CancellationToken cancellationToken)
162 | {
163 | var semanticModel = await document.GetSemanticModelAsync(cancellationToken);
164 | IMethodSymbol methodSymbol = semanticModel.GetDeclaredSymbol(methodSyntax);
165 |
166 | MethodDeclarationSyntax modifiedMethodSyntax = methodSyntax;
167 | if (methodSymbol.Parameters.Length == 1)
168 | {
169 | var sourceClassSymbol = methodSymbol.ContainingType;
170 | var targetClassSymbol = methodSymbol.Parameters[0].Type as INamedTypeSymbol;
171 | if (targetClassSymbol == null) return document;
172 |
173 | var matchedProperties = RetrieveMatchedProperties(sourceClassSymbol, targetClassSymbol);
174 | modifiedMethodSyntax = methodSyntax.WithBody(GenerateMethodBody(methodSymbol, matchedProperties, semanticModel, methodSyntax.Body.Span.End - 1));
175 | }
176 | else if (methodSymbol.Parameters.Length == 2)
177 | {
178 | var sourceClassSymbol = methodSymbol.Parameters[0].Type as INamedTypeSymbol;
179 | var targetClassSymbol = methodSymbol.Parameters[1].Type as INamedTypeSymbol;
180 | if (sourceClassSymbol == null || targetClassSymbol == null) return document;
181 |
182 | var matchedProperties = RetrieveMatchedProperties(sourceClassSymbol, targetClassSymbol);
183 | modifiedMethodSyntax = methodSyntax.WithBody(GenerateMethodBody(methodSymbol, matchedProperties, semanticModel, methodSyntax.Body.Span.End - 1));
184 | }
185 |
186 | // replace root and return modified document
187 | var root = await document.GetSyntaxRootAsync(cancellationToken);
188 | var newRoot = root.ReplaceNode(methodSyntax, modifiedMethodSyntax);
189 | var newDocument = document.WithSyntaxRoot(newRoot);
190 | return newDocument;
191 | }
192 |
193 | private static IEnumerable RetrieveMatchedProperties(INamedTypeSymbol source, INamedTypeSymbol target)
194 | {
195 | SortedDictionary propertiesMap = new SortedDictionary();
196 |
197 | foreach (IPropertySymbol mSymbol in source.GetMembers().Where(x => x.Kind == SymbolKind.Property))
198 | {
199 | if (mSymbol.IsStatic || mSymbol.IsIndexer || mSymbol.DeclaredAccessibility != Accessibility.Public)
200 | {
201 | continue;
202 | }
203 | if (!propertiesMap.ContainsKey(mSymbol.Name))
204 | {
205 | // If class definition is invalid, it may happen that we get multiple properties with the same name
206 | // Ignore all but first
207 | propertiesMap.Add(mSymbol.Name, new MatchedPropertySymbols() { Source = mSymbol });
208 | }
209 | }
210 |
211 | foreach (IPropertySymbol mSymbol in target.GetMembers().Where(x => x.Kind == SymbolKind.Property))
212 | {
213 | if (mSymbol.IsStatic || mSymbol.IsIndexer || mSymbol.DeclaredAccessibility != Accessibility.Public)
214 | {
215 | continue;
216 | }
217 | MatchedPropertySymbols sourceProperty = null;
218 | if (!propertiesMap.TryGetValue(mSymbol.Name, out sourceProperty))
219 | {
220 | propertiesMap.Add(mSymbol.Name, new MatchedPropertySymbols { Target = mSymbol });
221 | }
222 | else if (sourceProperty.Target == null)
223 | {
224 | // If class definition is invalid, it may happen that we get multiple properties with the same name
225 | // Ignore all but first
226 | sourceProperty.Target = mSymbol;
227 | }
228 | }
229 |
230 | return propertiesMap.Values;
231 | }
232 |
233 | private static BlockSyntax GenerateMethodBody(IMethodSymbol method, IEnumerable matchedProperties, SemanticModel model, int position)
234 | {
235 | if (method.ReturnsVoid && method.Parameters.Length == 1)
236 | {
237 | return SyntaxFactory.Block(
238 | SyntaxFactory.Token(SyntaxKind.OpenBraceToken),
239 | SyntaxFactory.List(GenerateAssignmentSyntax(SyntaxFactory.ThisExpression(), SyntaxFactory.IdentifierName(method.Parameters[0].Name), matchedProperties, model, position)),
240 | SyntaxFactory.Token(SyntaxKind.CloseBraceToken));
241 | }
242 | else if (method.ReturnsVoid && method.Parameters.Length == 2)
243 | {
244 | return SyntaxFactory.Block(
245 | SyntaxFactory.Token(SyntaxKind.OpenBraceToken),
246 | SyntaxFactory.List(GenerateAssignmentSyntax(SyntaxFactory.IdentifierName(method.Parameters[0].Name), SyntaxFactory.IdentifierName(method.Parameters[1].Name), matchedProperties, model, position)),
247 | SyntaxFactory.Token(SyntaxKind.CloseBraceToken));
248 | }
249 | else
250 | {
251 | return SyntaxFactory.Block();
252 | }
253 | }
254 |
255 | private static IEnumerable GenerateAssignmentSyntax(ExpressionSyntax source, ExpressionSyntax target, IEnumerable matchedPropertySymbols, SemanticModel model, int position)
256 | {
257 | foreach (var matchedProperty in matchedPropertySymbols)
258 | {
259 | if (matchedProperty.Source == null || matchedProperty.Target == null) continue;
260 | if (matchedProperty.Source.GetMethod == null || matchedProperty.Source.GetMethod.DeclaredAccessibility != Accessibility.Public) continue;
261 |
262 | if (matchedProperty.Target.SetMethod != null && matchedProperty.Source.SetMethod.DeclaredAccessibility == Accessibility.Public &&
263 | (matchedProperty.Source.Type.Equals(matchedProperty.Target.Type) || matchedProperty.Target.Type.OriginalDefinition.SpecialType == SpecialType.System_Nullable_T && (matchedProperty.Target.Type as INamedTypeSymbol).TypeArguments[0].Equals(matchedProperty.Source.Type)))
264 | {
265 | yield return SyntaxFactory.ExpressionStatement(SyntaxFactory.AssignmentExpression(
266 | SyntaxKind.SimpleAssignmentExpression,
267 | SyntaxFactory.MemberAccessExpression(SyntaxKind.SimpleMemberAccessExpression, target, SyntaxFactory.IdentifierName(matchedProperty.Target.Name)),
268 | SyntaxFactory.MemberAccessExpression(SyntaxKind.SimpleMemberAccessExpression, source, SyntaxFactory.IdentifierName(matchedProperty.Source.Name))));
269 | }
270 | else if (matchedProperty.Target.SetMethod != null && matchedProperty.Source.SetMethod.DeclaredAccessibility == Accessibility.Public &&
271 | (matchedProperty.Source.Type.OriginalDefinition.SpecialType == SpecialType.System_Nullable_T && (matchedProperty.Source.Type as INamedTypeSymbol).TypeArguments[0].Equals(matchedProperty.Target.Type)))
272 | {
273 | yield return SyntaxFactory.ExpressionStatement(SyntaxFactory.AssignmentExpression(
274 | SyntaxKind.SimpleAssignmentExpression,
275 | SyntaxFactory.MemberAccessExpression(SyntaxKind.SimpleMemberAccessExpression, target, SyntaxFactory.IdentifierName(matchedProperty.Target.Name)),
276 | SyntaxFactory.BinaryExpression(
277 | SyntaxKind.CoalesceExpression,
278 | SyntaxFactory.MemberAccessExpression(SyntaxKind.SimpleMemberAccessExpression, source, SyntaxFactory.IdentifierName(matchedProperty.Source.Name)),
279 | SyntaxFactory.DefaultExpression(SyntaxFactory.ParseTypeName(matchedProperty.Target.Type.ToMinimalDisplayString(model, position))))));
280 | }
281 | else if (matchedProperty.Target.Type.OriginalDefinition.AllInterfaces.Any(x => x.ToDisplayString() == "System.Collections.Generic.ICollection") &&
282 | matchedProperty.Target.GetMethod != null && matchedProperty.Target.GetMethod.DeclaredAccessibility == Accessibility.Public &&
283 | matchedProperty.Source.Type.OriginalDefinition.AllInterfaces.Any(x => x.ToDisplayString() == "System.Collections.Generic.IEnumerable" &&
284 | (matchedProperty.Target.Type as INamedTypeSymbol).TypeArguments[0].Equals((matchedProperty.Source.Type as INamedTypeSymbol).TypeArguments[0])))
285 | {
286 | yield return SyntaxFactory.ExpressionStatement(SyntaxFactory.InvocationExpression(
287 | SyntaxFactory.MemberAccessExpression(
288 | SyntaxKind.SimpleMemberAccessExpression,
289 | SyntaxFactory.MemberAccessExpression(SyntaxKind.SimpleMemberAccessExpression, target, SyntaxFactory.IdentifierName(matchedProperty.Target.Name)),
290 | SyntaxFactory.IdentifierName("CopyFrom"))).
291 | WithArgumentList(SyntaxFactory.ArgumentList(SyntaxFactory.SingletonSeparatedList(SyntaxFactory.Argument(
292 | SyntaxFactory.MemberAccessExpression(SyntaxKind.SimpleMemberAccessExpression, source, SyntaxFactory.IdentifierName(matchedProperty.Source.Name)))))));
293 | }
294 | }
295 | }
296 |
297 | private static MethodDeclarationSyntax GenerateMethodImplementation(IMethodSymbol fromMethod, SemanticModel model, int position)
298 | {
299 | MethodDeclarationSyntax syntax = SyntaxFactory.MethodDeclaration(
300 | fromMethod.ReturnsVoid ? SyntaxFactory.PredefinedType(SyntaxFactory.Token(SyntaxKind.VoidKeyword)) : SyntaxFactory.ParseTypeName(fromMethod.ReturnType.ToMinimalDisplayString(model, position)),
301 | fromMethod.Name)
302 | .AddModifiers(SyntaxFactory.Token(SyntaxKind.PublicKeyword))
303 | .WithParameterList(SyntaxFactory.ParameterList(
304 | SyntaxFactory.Token(SyntaxKind.OpenParenToken),
305 | SyntaxFactory.SeparatedList(fromMethod.Parameters.Select(x => SyntaxFactory.Parameter(SyntaxFactory.Identifier(x.Name)).WithType(SyntaxFactory.ParseTypeName(x.Type.ToMinimalDisplayString(model, position))))),
306 | SyntaxFactory.Token(SyntaxKind.CloseParenToken)))
307 | .WithBody(SyntaxFactory.Block())
308 | .WithAdditionalAnnotations(Formatter.Annotation);
309 |
310 | return syntax;
311 | }
312 |
313 | private sealed class MatchedPropertySymbols
314 | {
315 | public IPropertySymbol Source { get; set; }
316 | public IPropertySymbol Target { get; set; }
317 | }
318 | }
319 | }
--------------------------------------------------------------------------------
/ObjectMapper/ObjectMapper.GenerateImplementationAnalyzer/ObjectMapper.GenerateImplementationAnalyzer/ObjectMapper.GenerateImplementationAnalyzer.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | 11.0
6 | Debug
7 | AnyCPU
8 | {492F3804-A43F-4E82-B649-8AD0D967A830}
9 | Library
10 | Properties
11 | ObjectMapper.GenerateImplementationAnalyzer
12 | ObjectMapper.GenerateImplementationAnalyzer
13 | {786C830F-07A1-408B-BD7F-6EE04809D6DB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}
14 | Profile7
15 | v4.5
16 |
17 |
18 | true
19 | full
20 | false
21 | bin\Debug\
22 | DEBUG;TRACE
23 | prompt
24 | 4
25 | true
26 | AllRules.ruleset
27 |
28 |
29 | pdbonly
30 | true
31 | bin\Release\
32 | TRACE
33 | prompt
34 | 4
35 | true
36 | AllRules.ruleset
37 |
38 |
39 | true
40 |
41 |
42 | objectmapper.pfx
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 | True
51 | True
52 | Resources.resx
53 |
54 |
55 |
56 |
57 |
58 | ResXFileCodeGenerator
59 | Resources.Designer.cs
60 |
61 |
62 |
63 |
64 | Designer
65 | PreserveNewest
66 |
67 |
68 |
69 |
70 | PreserveNewest
71 |
72 |
73 | PreserveNewest
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 | ..\..\packages\Microsoft.CodeAnalysis.Common.1.0.0\lib\portable-net45+win8\Microsoft.CodeAnalysis.dll
83 | False
84 |
85 |
86 | ..\..\packages\Microsoft.CodeAnalysis.CSharp.1.0.0\lib\portable-net45+win8\Microsoft.CodeAnalysis.CSharp.dll
87 | False
88 |
89 |
90 | ..\..\packages\Microsoft.CodeAnalysis.CSharp.Workspaces.1.0.0\lib\portable-net45+win8\Microsoft.CodeAnalysis.CSharp.Workspaces.dll
91 | False
92 |
93 |
94 | ..\..\packages\Microsoft.CodeAnalysis.Workspaces.Common.1.0.0\lib\portable-net45+win8\Microsoft.CodeAnalysis.Workspaces.dll
95 | False
96 |
97 |
98 | ..\..\packages\System.Collections.Immutable.1.1.36\lib\portable-net45+win8+wp8+wpa81\System.Collections.Immutable.dll
99 | False
100 |
101 |
102 | ..\..\packages\Microsoft.Composition.1.0.27\lib\portable-net45+win8+wp8+wpa81\System.Composition.AttributedModel.dll
103 | False
104 |
105 |
106 | ..\..\packages\Microsoft.Composition.1.0.27\lib\portable-net45+win8+wp8+wpa81\System.Composition.Convention.dll
107 | False
108 |
109 |
110 | ..\..\packages\Microsoft.Composition.1.0.27\lib\portable-net45+win8+wp8+wpa81\System.Composition.Hosting.dll
111 | False
112 |
113 |
114 | ..\..\packages\Microsoft.Composition.1.0.27\lib\portable-net45+win8+wp8+wpa81\System.Composition.Runtime.dll
115 | False
116 |
117 |
118 | ..\..\packages\Microsoft.Composition.1.0.27\lib\portable-net45+win8+wp8+wpa81\System.Composition.TypedParts.dll
119 | False
120 |
121 |
122 | ..\..\packages\System.Reflection.Metadata.1.0.21\lib\portable-net45+win8\System.Reflection.Metadata.dll
123 | False
124 |
125 |
126 |
127 |
128 |
129 | PreserveNewest
130 |
131 |
132 |
133 |
134 |
135 |
136 |
137 |
138 |
139 |
140 |
141 |
148 |
--------------------------------------------------------------------------------
/ObjectMapper/ObjectMapper.GenerateImplementationAnalyzer/ObjectMapper.GenerateImplementationAnalyzer/Properties/AssemblyInfo.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Reflection;
3 | using System.Runtime.InteropServices;
4 |
5 | // General Information about an assembly is controlled through the following
6 | // set of attributes. Change these attribute values to modify the information
7 | // associated with an assembly.
8 | [assembly: AssemblyTitle("ObjectMapper.GenerateImplementationAnalyzer")]
9 | [assembly: AssemblyDescription("Contains analyzer and code fix provider for generating mapping boilerplate code.")]
10 | [assembly: AssemblyConfiguration("")]
11 | [assembly: AssemblyCompany("")]
12 | [assembly: AssemblyProduct("ObjectMapper.GenerateImplementationAnalyzer")]
13 | [assembly: AssemblyCopyright("Copyright © 2016")]
14 | [assembly: AssemblyTrademark("")]
15 | [assembly: AssemblyCulture("")]
16 |
17 | [assembly: CLSCompliant(false)]
18 | [assembly: ComVisible(false)]
19 |
20 | [assembly: AssemblyVersion("1.2.2.0")]
21 | [assembly: AssemblyFileVersion("1.2.2.0")]
22 |
--------------------------------------------------------------------------------
/ObjectMapper/ObjectMapper.GenerateImplementationAnalyzer/ObjectMapper.GenerateImplementationAnalyzer/Resources.Designer.cs:
--------------------------------------------------------------------------------
1 | //------------------------------------------------------------------------------
2 | //
3 | // This code was generated by a tool.
4 | // Runtime Version:4.0.30319.42000
5 | //
6 | // Changes to this file may cause incorrect behavior and will be lost if
7 | // the code is regenerated.
8 | //
9 | //------------------------------------------------------------------------------
10 |
11 | namespace ObjectMapper.GenerateImplementationAnalyzer {
12 | using System;
13 | using System.Reflection;
14 |
15 |
16 | ///
17 | /// A strongly-typed resource class, for looking up localized strings, etc.
18 | ///
19 | // This class was auto-generated by the StronglyTypedResourceBuilder
20 | // class via a tool like ResGen or Visual Studio.
21 | // To add or remove a member, edit your .ResX file then rerun ResGen
22 | // with the /str option, or rebuild your VS project.
23 | [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")]
24 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
25 | [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
26 | internal class Resources {
27 |
28 | private static global::System.Resources.ResourceManager resourceMan;
29 |
30 | private static global::System.Globalization.CultureInfo resourceCulture;
31 |
32 | [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
33 | internal Resources() {
34 | }
35 |
36 | ///
37 | /// Returns the cached ResourceManager instance used by this class.
38 | ///
39 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
40 | internal static global::System.Resources.ResourceManager ResourceManager {
41 | get {
42 | if (object.ReferenceEquals(resourceMan, null)) {
43 | global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("ObjectMapper.GenerateImplementationAnalyzer.Resources", typeof(Resources).GetTypeInfo().Assembly);
44 | resourceMan = temp;
45 | }
46 | return resourceMan;
47 | }
48 | }
49 |
50 | ///
51 | /// Overrides the current thread's CurrentUICulture property for all
52 | /// resource lookups using this strongly typed resource class.
53 | ///
54 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
55 | internal static global::System.Globalization.CultureInfo Culture {
56 | get {
57 | return resourceCulture;
58 | }
59 | set {
60 | resourceCulture = value;
61 | }
62 | }
63 |
64 | ///
65 | /// Looks up a localized string similar to Attribute cannot be applied to methods that do not accept exactly two parameters and return void..
66 | ///
67 | internal static string AttributeUsageAnalyzerDescription {
68 | get {
69 | return ResourceManager.GetString("AttributeUsageAnalyzerDescription", resourceCulture);
70 | }
71 | }
72 |
73 | ///
74 | /// Looks up a localized string similar to Attribute cannot be applied to this method..
75 | ///
76 | internal static string AttributeUsageAnalyzerMessageFormat {
77 | get {
78 | return ResourceManager.GetString("AttributeUsageAnalyzerMessageFormat", resourceCulture);
79 | }
80 | }
81 |
82 | ///
83 | /// Looks up a localized string similar to Invalid use of object mapper attribute..
84 | ///
85 | internal static string AttributeUsageAnalyzerTitle {
86 | get {
87 | return ResourceManager.GetString("AttributeUsageAnalyzerTitle", resourceCulture);
88 | }
89 | }
90 |
91 | ///
92 | /// Looks up a localized string similar to Interface implementation can be generated..
93 | ///
94 | internal static string GenerateImplementationAnalyzerDescription {
95 | get {
96 | return ResourceManager.GetString("GenerateImplementationAnalyzerDescription", resourceCulture);
97 | }
98 | }
99 |
100 | ///
101 | /// Looks up a localized string similar to Implementation of mapping method(s) can be generated..
102 | ///
103 | internal static string GenerateImplementationAnalyzerMessageFormat {
104 | get {
105 | return ResourceManager.GetString("GenerateImplementationAnalyzerMessageFormat", resourceCulture);
106 | }
107 | }
108 |
109 | ///
110 | /// Looks up a localized string similar to Type implements object mapper interface..
111 | ///
112 | internal static string GenerateImplementationAnalyzerTitle {
113 | get {
114 | return ResourceManager.GetString("GenerateImplementationAnalyzerTitle", resourceCulture);
115 | }
116 | }
117 | }
118 | }
119 |
--------------------------------------------------------------------------------
/ObjectMapper/ObjectMapper.GenerateImplementationAnalyzer/ObjectMapper.GenerateImplementationAnalyzer/Resources.resx:
--------------------------------------------------------------------------------
1 |
2 |
3 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 | text/microsoft-resx
110 |
111 |
112 | 2.0
113 |
114 |
115 | System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
116 |
117 |
118 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
119 |
120 |
121 | Attribute cannot be applied to methods that do not accept exactly two parameters and return void.
122 |
123 |
124 | Attribute cannot be applied to this method.
125 |
126 |
127 | Invalid use of object mapper attribute.
128 |
129 |
130 | Interface implementation can be generated.
131 | An optional longer localizable description of the diagnostic.
132 |
133 |
134 | Implementation of mapping method(s) can be generated.
135 | The format-able message the diagnostic displays.
136 |
137 |
138 | Type implements object mapper interface.
139 | The title of the diagnostic.
140 |
141 |
--------------------------------------------------------------------------------
/ObjectMapper/ObjectMapper.GenerateImplementationAnalyzer/ObjectMapper.GenerateImplementationAnalyzer/Utilities/FrameworkHelpers.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.CodeAnalysis;
2 | using System;
3 | using System.Collections.Generic;
4 | using System.Collections.Immutable;
5 | using System.Linq;
6 | using System.Text;
7 | using System.Threading.Tasks;
8 |
9 | namespace ObjectMapper.GenerateImplementationAnalyzer.Utilities
10 | {
11 | internal static class FrameworkHelpers
12 | {
13 | private static readonly ImmutableArray _publicKeyToken = ImmutableArray.Create(38, 23, 68, 249, 236, 31, 118, 87);
14 | public static bool IsObjectMapperFrameworkAssembly(IAssemblySymbol assemblySymbol)
15 | {
16 | if (assemblySymbol.Name != "ObjectMapper.Framework")
17 | {
18 | return false;
19 | }
20 | if (!assemblySymbol.Identity.IsStrongName || !_publicKeyToken.SequenceEqual(assemblySymbol.Identity.PublicKeyToken))
21 | {
22 | return false;
23 | }
24 | return true;
25 | }
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/ObjectMapper/ObjectMapper.GenerateImplementationAnalyzer/ObjectMapper.GenerateImplementationAnalyzer/lib/ObjectMapper.Framework.dll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nejcskofic/ObjectMapper/8108d3e309d670d82a17cc219805536bc435cf3e/ObjectMapper/ObjectMapper.GenerateImplementationAnalyzer/ObjectMapper.GenerateImplementationAnalyzer/lib/ObjectMapper.Framework.dll
--------------------------------------------------------------------------------
/ObjectMapper/ObjectMapper.GenerateImplementationAnalyzer/ObjectMapper.GenerateImplementationAnalyzer/packages.config:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/ObjectMapper/ObjectMapper.GenerateImplementationAnalyzer/ObjectMapper.GenerateImplementationAnalyzer/tools/install.ps1:
--------------------------------------------------------------------------------
1 | param($installPath, $toolsPath, $package, $project)
2 |
3 | $analyzersPaths = Join-Path (Join-Path (Split-Path -Path $toolsPath -Parent) "analyzers" ) * -Resolve
4 |
5 | foreach($analyzersPath in $analyzersPaths)
6 | {
7 | # Install the language agnostic analyzers.
8 | if (Test-Path $analyzersPath)
9 | {
10 | foreach ($analyzerFilePath in Get-ChildItem $analyzersPath -Filter *.dll)
11 | {
12 | if($project.Object.AnalyzerReferences)
13 | {
14 | $project.Object.AnalyzerReferences.Add($analyzerFilePath.FullName)
15 | }
16 | }
17 | }
18 | }
19 |
20 | # $project.Type gives the language name like (C# or VB.NET)
21 | $languageFolder = ""
22 | if($project.Type -eq "C#")
23 | {
24 | $languageFolder = "cs"
25 | }
26 | if($project.Type -eq "VB.NET")
27 | {
28 | $languageFolder = "vb"
29 | }
30 | if($languageFolder -eq "")
31 | {
32 | return
33 | }
34 |
35 | foreach($analyzersPath in $analyzersPaths)
36 | {
37 | # Install language specific analyzers.
38 | $languageAnalyzersPath = join-path $analyzersPath $languageFolder
39 | if (Test-Path $languageAnalyzersPath)
40 | {
41 | foreach ($analyzerFilePath in Get-ChildItem $languageAnalyzersPath -Filter *.dll)
42 | {
43 | if($project.Object.AnalyzerReferences)
44 | {
45 | $project.Object.AnalyzerReferences.Add($analyzerFilePath.FullName)
46 | }
47 | }
48 | }
49 | }
--------------------------------------------------------------------------------
/ObjectMapper/ObjectMapper.GenerateImplementationAnalyzer/ObjectMapper.GenerateImplementationAnalyzer/tools/uninstall.ps1:
--------------------------------------------------------------------------------
1 | param($installPath, $toolsPath, $package, $project)
2 |
3 | $analyzersPaths = Join-Path (Join-Path (Split-Path -Path $toolsPath -Parent) "analyzers" ) * -Resolve
4 |
5 | foreach($analyzersPath in $analyzersPaths)
6 | {
7 | # Uninstall the language agnostic analyzers.
8 | if (Test-Path $analyzersPath)
9 | {
10 | foreach ($analyzerFilePath in Get-ChildItem $analyzersPath -Filter *.dll)
11 | {
12 | if($project.Object.AnalyzerReferences)
13 | {
14 | $project.Object.AnalyzerReferences.Remove($analyzerFilePath.FullName)
15 | }
16 | }
17 | }
18 | }
19 |
20 | # $project.Type gives the language name like (C# or VB.NET)
21 | $languageFolder = ""
22 | if($project.Type -eq "C#")
23 | {
24 | $languageFolder = "cs"
25 | }
26 | if($project.Type -eq "VB.NET")
27 | {
28 | $languageFolder = "vb"
29 | }
30 | if($languageFolder -eq "")
31 | {
32 | return
33 | }
34 |
35 | foreach($analyzersPath in $analyzersPaths)
36 | {
37 | # Uninstall language specific analyzers.
38 | $languageAnalyzersPath = join-path $analyzersPath $languageFolder
39 | if (Test-Path $languageAnalyzersPath)
40 | {
41 | foreach ($analyzerFilePath in Get-ChildItem $languageAnalyzersPath -Filter *.dll)
42 | {
43 | if($project.Object.AnalyzerReferences)
44 | {
45 | try
46 | {
47 | $project.Object.AnalyzerReferences.Remove($analyzerFilePath.FullName)
48 | }
49 | catch
50 | {
51 |
52 | }
53 | }
54 | }
55 | }
56 | }
--------------------------------------------------------------------------------
/ObjectMapper/ObjectMapper.sln:
--------------------------------------------------------------------------------
1 |
2 | Microsoft Visual Studio Solution File, Format Version 12.00
3 | # Visual Studio 14
4 | VisualStudioVersion = 14.0.24720.0
5 | MinimumVisualStudioVersion = 10.0.40219.1
6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ObjectMapper.GenerateImplementationAnalyzer", "ObjectMapper.GenerateImplementationAnalyzer\ObjectMapper.GenerateImplementationAnalyzer\ObjectMapper.GenerateImplementationAnalyzer.csproj", "{492F3804-A43F-4E82-B649-8AD0D967A830}"
7 | EndProject
8 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ObjectMapper.GenerateImplementationAnalyzer.Test", "ObjectMapper.GenerateImplementationAnalyzer\ObjectMapper.GenerateImplementationAnalyzer.Test\ObjectMapper.GenerateImplementationAnalyzer.Test.csproj", "{FCC88640-43A1-4FEE-AE9B-44674C6376F7}"
9 | EndProject
10 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ObjectMapper.GenerateImplementationAnalyzer.Vsix", "ObjectMapper.GenerateImplementationAnalyzer\ObjectMapper.GenerateImplementationAnalyzer.Vsix\ObjectMapper.GenerateImplementationAnalyzer.Vsix.csproj", "{2AF839D8-C2E5-45CC-BA8E-04C20DD75B96}"
11 | EndProject
12 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ObjectMapper.Framework", "ObjectMapper.Framework\ObjectMapper.Framework.csproj", "{8220C878-68E1-4307-A908-66FBEA9D8B56}"
13 | EndProject
14 | Global
15 | GlobalSection(SolutionConfigurationPlatforms) = preSolution
16 | Debug|Any CPU = Debug|Any CPU
17 | Release|Any CPU = Release|Any CPU
18 | EndGlobalSection
19 | GlobalSection(ProjectConfigurationPlatforms) = postSolution
20 | {492F3804-A43F-4E82-B649-8AD0D967A830}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
21 | {492F3804-A43F-4E82-B649-8AD0D967A830}.Debug|Any CPU.Build.0 = Debug|Any CPU
22 | {492F3804-A43F-4E82-B649-8AD0D967A830}.Release|Any CPU.ActiveCfg = Release|Any CPU
23 | {492F3804-A43F-4E82-B649-8AD0D967A830}.Release|Any CPU.Build.0 = Release|Any CPU
24 | {FCC88640-43A1-4FEE-AE9B-44674C6376F7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
25 | {FCC88640-43A1-4FEE-AE9B-44674C6376F7}.Debug|Any CPU.Build.0 = Debug|Any CPU
26 | {FCC88640-43A1-4FEE-AE9B-44674C6376F7}.Release|Any CPU.ActiveCfg = Release|Any CPU
27 | {FCC88640-43A1-4FEE-AE9B-44674C6376F7}.Release|Any CPU.Build.0 = Release|Any CPU
28 | {2AF839D8-C2E5-45CC-BA8E-04C20DD75B96}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
29 | {2AF839D8-C2E5-45CC-BA8E-04C20DD75B96}.Debug|Any CPU.Build.0 = Debug|Any CPU
30 | {2AF839D8-C2E5-45CC-BA8E-04C20DD75B96}.Release|Any CPU.ActiveCfg = Release|Any CPU
31 | {2AF839D8-C2E5-45CC-BA8E-04C20DD75B96}.Release|Any CPU.Build.0 = Release|Any CPU
32 | {8220C878-68E1-4307-A908-66FBEA9D8B56}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
33 | {8220C878-68E1-4307-A908-66FBEA9D8B56}.Debug|Any CPU.Build.0 = Debug|Any CPU
34 | {8220C878-68E1-4307-A908-66FBEA9D8B56}.Release|Any CPU.ActiveCfg = Release|Any CPU
35 | {8220C878-68E1-4307-A908-66FBEA9D8B56}.Release|Any CPU.Build.0 = Release|Any CPU
36 | EndGlobalSection
37 | GlobalSection(SolutionProperties) = preSolution
38 | HideSolutionNode = FALSE
39 | EndGlobalSection
40 | EndGlobal
41 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Object Mapper
2 | This is simple library with interfaces for mapping one C# object to another with code analyser feature of .NET compiler platform (aka Roslyn) to provide code generation.
3 |
4 | ## Why another mapper
5 | All other mappers operate in runtime using some kind of reflection to generate code which maps properties between objects. While this removes the need to write boilerplate code and speeds up development, it has number of disadvantages such as:
6 | - Refactoring unfriendly - if someone renames one property but not the other, mapping does not work on that property anymore
7 | - No way to see what will actually get copied - code is generated at the runtime
8 | - Runtime code generation and reflection has performance overhead
9 |
10 | This is why this library consists of simple interfaces for object mapping contract only. Provided code analyser and code fix provider are responsible for generating mapping boilerplate code for developer. Generated code can be modified if desired, you actually see what gets mapped and there is no performance overhead since mapping code is like any other hand written code.
11 |
12 | ## Usage
13 |
14 | Install NuGet package to your project - you can find current release [here](https://www.nuget.org/packages/SimpleObjectMapper/). This will add ObjectMapper.Framework to your references and diagnostic analyser under analyzers node. You can then use mapper interfaces or attribute from ObjectMapper.Framework assembly to define mapping.
15 |
16 | ### IObjectMapper interface
17 |
18 | Let's define two classes with some matching properties:
19 |
20 | ```C#
21 | using ObjectMapper.Framework;
22 | using System.Collections.Generic;
23 |
24 | namespace TestClassLibrary
25 | {
26 | public class ClassA
27 | {
28 | public int? Prop1 { get; set; }
29 | public string Prop2 { get; set; }
30 | public decimal Prop3 { get; set; }
31 |
32 | private List _prop4;
33 | public List Prop4 { get { if (_prop4 == null) _prop4 = new List(); return _prop4; } }
34 | }
35 |
36 | public class ClassB
37 | {
38 | public int Prop1 { get; set; }
39 | public string Prop2 { get; set; }
40 | public decimal Prop3 { get; set; }
41 |
42 | private List _prop4;
43 | public List Prop4 { get { if (_prop4 == null) _prop4 = new List(); return _prop4; } }
44 | }
45 | }
46 | ```
47 |
48 | Let's say that we want to define mapping from class ClassA to ClassB. We specify that ClassA implements IObjectMapper interface. If we have caret on IObjectMapper symbol, Visual Studio will display lightbulb on the right side. By clicking on lightbulb (or using left control + . shortcut), you will get menu of options. If you click 'Generate implementation' interface implementation with mapping code will be generated for you. For the above example result is as follows:
49 |
50 | ```C#
51 | using ObjectMapper.Framework;
52 | using System.Collections.Generic;
53 |
54 | namespace TestClassLibrary
55 | {
56 | public class ClassA : IObjectMapper
57 | {
58 | public int? Prop1 { get; set; }
59 | public string Prop2 { get; set; }
60 | public decimal Prop3 { get; set; }
61 |
62 | private List _prop4;
63 | public List Prop4 { get { if (_prop4 == null) _prop4 = new List(); return _prop4; } }
64 |
65 | public void MapObject(ClassB target)
66 | {
67 | target.Prop1 = this.Prop1 ?? default(int);
68 | target.Prop2 = this.Prop2;
69 | target.Prop3 = this.Prop3;
70 | target.Prop4.CopyFrom(this.Prop4);
71 | }
72 | }
73 |
74 | public class ClassB
75 | {
76 | public int Prop1 { get; set; }
77 | public string Prop2 { get; set; }
78 | public decimal Prop3 { get; set; }
79 |
80 | private List _prop4;
81 | public List Prop4 { get { if (_prop4 == null) _prop4 = new List(); return _prop4; } }
82 | }
83 | }
84 | ```
85 |
86 | If you have method already defined (or generated), you can set caret on method name and 'Generate implementation' action will be available.
87 |
88 | ### IObjectMapperAdapter