The requested changes are too big and content was truncated. Show full diff
@@ -0,0 +1,18 | |||
|
1 | { | |
|
2 | "version": "0.2.0", | |
|
3 | "configurations": [ | |
|
4 | { | |
|
5 | "name": ".NET Core Launch (console)", | |
|
6 | "type": "coreclr", | |
|
7 | "request": "launch", | |
|
8 | "preLaunchTask": "build", | |
|
9 | "program": "${workspaceRoot}/Implab.Playground/bin/Debug/netcoreapp2.0/Implab.Playground.dll", | |
|
10 | "args": [ | |
|
11 | "-f", "netcoreapp2.0" | |
|
12 | ], | |
|
13 | "cwd": "${workspaceRoot}/Implab.Playground", | |
|
14 | "stopAtEntry": false, | |
|
15 | "console": "internalConsole" | |
|
16 | } | |
|
17 | ] | |
|
18 | } No newline at end of file |
@@ -0,0 +1,12 | |||
|
1 | // Поместите параметры в этот файл, чтобы перезаписать параметры по умолчанию и пользовательские параметры. | |
|
2 | { | |
|
3 | "files.exclude": { | |
|
4 | "**/.git": true, | |
|
5 | "**/.svn": true, | |
|
6 | "**/.hg": true, | |
|
7 | "**/CVS": true, | |
|
8 | "**/.DS_Store": true, | |
|
9 | "**/bin": true, | |
|
10 | "**/obj": true | |
|
11 | } | |
|
12 | } No newline at end of file |
@@ -0,0 +1,37 | |||
|
1 | { | |
|
2 | // See https://go.microsoft.com/fwlink/?LinkId=733558 | |
|
3 | // for the documentation about the tasks.json format | |
|
4 | "version": "0.1.0", | |
|
5 | "command": "dotnet", | |
|
6 | "args": [ | |
|
7 | ], | |
|
8 | "showOutput": "silent", | |
|
9 | "tasks": [ | |
|
10 | { | |
|
11 | "taskName": "build", | |
|
12 | // Show the output window only if unrecognized errors occur. | |
|
13 | "showOutput": "always", | |
|
14 | // Use the standard MS compiler pattern to detect errors, warnings and infos | |
|
15 | "problemMatcher": "$msCompile", | |
|
16 | ||
|
17 | "args" : [ | |
|
18 | "/p:Configuration=Debug" | |
|
19 | ] | |
|
20 | }, | |
|
21 | { | |
|
22 | "taskName": "clean", | |
|
23 | // Show the output window only if unrecognized errors occur. | |
|
24 | "showOutput": "always", | |
|
25 | // Use the standard MS compiler pattern to detect errors, warnings and infos | |
|
26 | "problemMatcher": "$msCompile" | |
|
27 | }, | |
|
28 | { | |
|
29 | "taskName": "test", | |
|
30 | "isTestCommand": true, | |
|
31 | // Show the output window only if unrecognized errors occur. | |
|
32 | "showOutput": "always", | |
|
33 | // Use the standard MS compiler pattern to detect errors, warnings and infos | |
|
34 | "problemMatcher": "$msCompile" | |
|
35 | } | |
|
36 | ] | |
|
37 | } No newline at end of file |
@@ -0,0 +1,6 | |||
|
1 | <?xml version="1.0" encoding="utf-8"?> | |
|
2 | <configuration> | |
|
3 | <startup> | |
|
4 | <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.6"/> | |
|
5 | </startup> | |
|
6 | </configuration> |
@@ -0,0 +1,27 | |||
|
1 | <Project Sdk="Microsoft.NET.Sdk"> | |
|
2 | <PropertyGroup Condition="'$(OSTYPE)'=='linux'"> | |
|
3 | <TargetFrameworks>netcoreapp2.0;;net46</TargetFrameworks> | |
|
4 | <FrameworkPathOverride Condition="'$(TargetFramework)'=='net46'">/usr/lib/mono/4.6-api/</FrameworkPathOverride> | |
|
5 | </PropertyGroup> | |
|
6 | ||
|
7 | <PropertyGroup Condition="'$(OSTYPE)'=='windows'"> | |
|
8 | <TargetFrameworks>netcoreapp2.0;net46</TargetFrameworks> | |
|
9 | </PropertyGroup> | |
|
10 | ||
|
11 | <PropertyGroup> | |
|
12 | <OutputType>Exe</OutputType> | |
|
13 | <IsPackable>false</IsPackable> | |
|
14 | </PropertyGroup> | |
|
15 | ||
|
16 | <ItemGroup> | |
|
17 | <ProjectReference Include="../Implab/Implab.csproj" /> | |
|
18 | <ProjectReference Include="..\Implab.ServiceHost\Implab.ServiceHost.csproj" /> | |
|
19 | </ItemGroup> | |
|
20 | ||
|
21 | <ItemGroup> | |
|
22 | <PackageReference Include="Unity" Version="5.8.6" /> | |
|
23 | <PackageReference Include="System.Reactive" Version="4.0.0" /> | |
|
24 | ||
|
25 | </ItemGroup> | |
|
26 | ||
|
27 | </Project> |
@@ -0,0 +1,42 | |||
|
1 | using System; | |
|
2 | using System.Collections.Generic; | |
|
3 | using System.Diagnostics; | |
|
4 | using System.Dynamic; | |
|
5 | using System.Linq; | |
|
6 | using Implab.Components; | |
|
7 | using Implab.Diagnostics; | |
|
8 | using Implab.ServiceHost.Unity; | |
|
9 | using Implab.Xml; | |
|
10 | using Unity; | |
|
11 | using Unity.Injection; | |
|
12 | using Unity.Registration; | |
|
13 | ||
|
14 | namespace Implab.Playground { | |
|
15 | using System.Reactive.Linq; | |
|
16 | using static Trace<Bar>; | |
|
17 | ||
|
18 | class Foo { | |
|
19 | ||
|
20 | } | |
|
21 | ||
|
22 | class Bar : Foo { | |
|
23 | ||
|
24 | } | |
|
25 | public class Program { | |
|
26 | ||
|
27 | static void Main(string[] args) { | |
|
28 | Trace<Foo>.Log("First!"); | |
|
29 | Log("+1!"); | |
|
30 | ||
|
31 | using(TraceRegistry.Global.OfType<TraceSourceChannel>().Subscribe(ch => { | |
|
32 | Console.WriteLine($"{ch.Id}: {ch.Source.Name}"); | |
|
33 | ||
|
34 | })) { | |
|
35 | Trace<Foo>.Log("Hi!"); | |
|
36 | Log("Respect!"); | |
|
37 | } | |
|
38 | } | |
|
39 | ||
|
40 | ||
|
41 | } | |
|
42 | } |
@@ -0,0 +1,74 | |||
|
1 | <?xml version="1.0" encoding="UTF-8"?> | |
|
2 | <container xmlns="http://implab.org/schemas/servicehost/unity.v1.xsd"> | |
|
3 | <namespace name="System"/> | |
|
4 | <namespace name="System.Collections.Generic"/> | |
|
5 | <namespace name="Implab.Components"/> | |
|
6 | <namespace name="Implab.Playground"/> | |
|
7 | ||
|
8 | <!-- foo1 --> | |
|
9 | <register name="foo1" type="Foo"> | |
|
10 | <property name="Name"> | |
|
11 | <value>FOO!</value> | |
|
12 | </property> | |
|
13 | </register> | |
|
14 | ||
|
15 | <!-- foo2 --> | |
|
16 | <register name="foo2" type="Foo"> | |
|
17 | <property name="Name"> | |
|
18 | <value>GOOD</value> | |
|
19 | </property> | |
|
20 | <property name="IntValue"> | |
|
21 | <value>2</value> | |
|
22 | </property> | |
|
23 | </register> | |
|
24 | ||
|
25 | <register type="Foo"> | |
|
26 | <method name="AddRange"> | |
|
27 | <array itemsType="Foo"> | |
|
28 | <dependency name="foo2"/> | |
|
29 | </array> | |
|
30 | </method> | |
|
31 | </register> | |
|
32 | ||
|
33 | <register type="IContainer{}" mapTo="Container{}"> | |
|
34 | <constructor/> | |
|
35 | <method name="SetInstance"> | |
|
36 | <dependency type="T"/> | |
|
37 | </method> | |
|
38 | <method name="AddRange"> | |
|
39 | <array itemsType="T"> | |
|
40 | <dependency name="foo2-bar"/> | |
|
41 | </array> | |
|
42 | </method> | |
|
43 | </register> | |
|
44 | ||
|
45 | <register type="List{}"> | |
|
46 | <constructor /> | |
|
47 | </register> | |
|
48 | ||
|
49 | <register type="IContainer{String}" mapTo="Container{String}"> | |
|
50 | <constructor/> | |
|
51 | <method name="SetInstance"> | |
|
52 | <dependency type="String" name="name1"/> | |
|
53 | </method> | |
|
54 | </register> | |
|
55 | ||
|
56 | <serialized type="Foo+Bar"> | |
|
57 | <Bar xmlns="" id="1"> | |
|
58 | <Name>Baaar</Name> | |
|
59 | </Bar> | |
|
60 | </serialized> | |
|
61 | ||
|
62 | <value name="connection1" type="String"><![CDATA[Connect me <here>!]]></value> | |
|
63 | <value name="name1" type="String" value="Hello!"/> | |
|
64 | ||
|
65 | <factory name="foo3" type="IFactory{Foo}" mapTo="FooFactory"> | |
|
66 | <property name="Connection"> | |
|
67 | <value><![CDATA[Wired "" objecty <> name @#$%^&]]></value> | |
|
68 | </property> | |
|
69 | <property name="UseSsl"> | |
|
70 | <value>false</value> | |
|
71 | </property> | |
|
72 | </factory> | |
|
73 | ||
|
74 | </container> No newline at end of file |
@@ -0,0 +1,29 | |||
|
1 | <Project Sdk="Microsoft.NET.Sdk"> | |
|
2 | <PropertyGroup Condition="'$(OSTYPE)'=='linux'"> | |
|
3 | <TargetFrameworks>netcoreapp2.0;net46</TargetFrameworks> | |
|
4 | <FrameworkPathOverride Condition="'$(TargetFramework)'=='net46'">/usr/lib/mono/4.5/</FrameworkPathOverride> | |
|
5 | </PropertyGroup> | |
|
6 | ||
|
7 | <PropertyGroup Condition="'$(OSTYPE)'=='windows'"> | |
|
8 | <TargetFrameworks>netcoreapp2.0;net46</TargetFrameworks> | |
|
9 | </PropertyGroup> | |
|
10 | ||
|
11 | <PropertyGroup> | |
|
12 | <IsPackable>false</IsPackable> | |
|
13 | </PropertyGroup> | |
|
14 | ||
|
15 | <ItemGroup> | |
|
16 | <PackageReference Include="Microsoft.NET.Test.Sdk" Version="15.6.0-preview-20180109-01" /> | |
|
17 | <PackageReference Include="System.Reactive" Version="4.0.0" /> | |
|
18 | <PackageReference Include="xunit" Version="2.3.1" /> | |
|
19 | <PackageReference Include="xunit.runner.visualstudio" Version="2.3.1" /> | |
|
20 | <ProjectReference Include="../Implab/Implab.csproj"/> | |
|
21 | <ProjectReference Include="../Implab.ServiceHost/Implab.ServiceHost.csproj"/> | |
|
22 | <DotNetCliToolReference Include="dotnet-xunit" Version="2.3.1" /> | |
|
23 | </ItemGroup> | |
|
24 | ||
|
25 | <ItemGroup> | |
|
26 | <None Include="data/**/*.*" CopyToOutputDirectory="PreserveNewest" /> | |
|
27 | </ItemGroup> | |
|
28 | ||
|
29 | </Project> |
@@ -0,0 +1,74 | |||
|
1 | <?xml version="1.0" encoding="UTF-8"?> | |
|
2 | <container xmlns="http://implab.org/schemas/servicehost/unity.v1.xsd"> | |
|
3 | <namespace name="System"/> | |
|
4 | <namespace name="System.Collections.Generic"/> | |
|
5 | <namespace name="Implab.Components"/> | |
|
6 | <namespace name="Implab.ServiceHost.Test.Mock"/> | |
|
7 | ||
|
8 | <!-- Default FooService --> | |
|
9 | <register type="FooService{}"> | |
|
10 | <property name="Name"> | |
|
11 | <value>I'm default!</value> | |
|
12 | </property> | |
|
13 | </register> | |
|
14 | ||
|
15 | <!-- foo2 --> | |
|
16 | <register name="foo2" type="Foo"> | |
|
17 | <property name="Name"> | |
|
18 | <value>GOOD</value> | |
|
19 | </property> | |
|
20 | <property name="IntValue"> | |
|
21 | <value>2</value> | |
|
22 | </property> | |
|
23 | </register> | |
|
24 | ||
|
25 | <register type="Foo"> | |
|
26 | <method name="AddRange"> | |
|
27 | <array itemsType="Foo"> | |
|
28 | <dependency name="foo2"/> | |
|
29 | </array> | |
|
30 | </method> | |
|
31 | </register> | |
|
32 | ||
|
33 | <register type="IContainer{}" mapTo="Container{}"> | |
|
34 | <constructor/> | |
|
35 | <method name="SetInstance"> | |
|
36 | <dependency type="T"/> | |
|
37 | </method> | |
|
38 | <method name="AddRange"> | |
|
39 | <array itemsType="T"> | |
|
40 | <dependency name="foo2-bar"/> | |
|
41 | </array> | |
|
42 | </method> | |
|
43 | </register> | |
|
44 | ||
|
45 | <register type="List{}"> | |
|
46 | <constructor /> | |
|
47 | </register> | |
|
48 | ||
|
49 | <register type="IContainer{String}" mapTo="Container{String}"> | |
|
50 | <constructor/> | |
|
51 | <method name="SetInstance"> | |
|
52 | <dependency type="String" name="name1"/> | |
|
53 | </method> | |
|
54 | </register> | |
|
55 | ||
|
56 | <serialized type="Foo+Bar"> | |
|
57 | <Bar xmlns="" id="1"> | |
|
58 | <Name>Baaar</Name> | |
|
59 | </Bar> | |
|
60 | </serialized> | |
|
61 | ||
|
62 | <value name="connection1" type="String"><![CDATA[Connect me <here>!]]></value> | |
|
63 | <value name="name1" type="String" value="Hello!"/> | |
|
64 | ||
|
65 | <factory name="foo3" type="IFactory{Foo}" mapTo="FooFactory"> | |
|
66 | <property name="Connection"> | |
|
67 | <value><![CDATA[Wired "" objecty <> name @#$%^&]]></value> | |
|
68 | </property> | |
|
69 | <property name="UseSsl"> | |
|
70 | <value>false</value> | |
|
71 | </property> | |
|
72 | </factory> | |
|
73 | ||
|
74 | </container> No newline at end of file |
@@ -0,0 +1,2 | |||
|
1 | <container xmlns="http://implab.org/schemas/servicehost/unity.v1.xsd"> | |
|
2 | </container> No newline at end of file |
@@ -0,0 +1,65 | |||
|
1 | <?xml version="1.0"?> | |
|
2 | <container xmlns="http://implab.org/schemas/servicehost/unity.v1.xsd"> | |
|
3 | <namespace name="System"/> | |
|
4 | <namespace name="System.Collections.Generic"/> | |
|
5 | <namespace name="Implab.Components"/> | |
|
6 | <namespace name="Implab.ServiceHost.Test.Mock"/> | |
|
7 | ||
|
8 | <register name="Big" type="Baz+Nut"> | |
|
9 | <property name="Size"> | |
|
10 | <value>5</value> | |
|
11 | </property> | |
|
12 | </register> | |
|
13 | <register name="Mid" type="Baz+Nut"> | |
|
14 | <property name="Size"> | |
|
15 | <value>3</value> | |
|
16 | </property> | |
|
17 | </register> | |
|
18 | <register name="Small" type="Baz+Nut"> | |
|
19 | <property name="Size"> | |
|
20 | <value>1</value> | |
|
21 | </property> | |
|
22 | </register> | |
|
23 | <register type="Baz+Nut"> | |
|
24 | <property name="Size"> | |
|
25 | <value>2</value> | |
|
26 | </property> | |
|
27 | </register> | |
|
28 | ||
|
29 | <!-- register a generic interface mapping to the generic type --> | |
|
30 | <register type="IBox{}" mapTo="Box{}"> | |
|
31 | <property name="Value"> | |
|
32 | <!-- the dependency type is implied from the property and will be the generic parameter {T} --> | |
|
33 | <dependency optional="true"/> | |
|
34 | </property> | |
|
35 | </register> | |
|
36 | ||
|
37 | <factory name="Box2" type="BoxFactory{}"> | |
|
38 | </factory> | |
|
39 | ||
|
40 | <register type="IBox{String}" mapTo="Box{String}"> | |
|
41 | <property name="Name"> | |
|
42 | <value>boxForString</value> | |
|
43 | </property> | |
|
44 | </register> | |
|
45 | ||
|
46 | <register name="Small" type="IBox{}" mapTo="Box{}"> | |
|
47 | <property name="Value"> | |
|
48 | <dependency name="Small" optional="true"/> | |
|
49 | </property> | |
|
50 | </register> | |
|
51 | ||
|
52 | <register type="SetOfBoxes{}"> | |
|
53 | <constructor/> | |
|
54 | <method name="BoxValues"> | |
|
55 | <!-- only generic parameter or type without unresolved parameters | |
|
56 | can be used, this is a limitation of Unity container. TODO --> | |
|
57 | <array itemsType="T"> | |
|
58 | <default /> | |
|
59 | <dependency optional="true"/> | |
|
60 | <dependency name="Small" optional="true"/> | |
|
61 | </array> | |
|
62 | </method> | |
|
63 | </register> | |
|
64 | ||
|
65 | </container> No newline at end of file |
@@ -0,0 +1,6 | |||
|
1 | <?xml version="1.0"?> | |
|
2 | <Person xmlns="urn:implab:test:model"> | |
|
3 | <FirstName>Trohn</FirstName> | |
|
4 | <LastName>Javolta</LastName> | |
|
5 | <Age>88</Age> | |
|
6 | </Person> No newline at end of file |
@@ -0,0 +1,16 | |||
|
1 | <?xml version="1.0"?> | |
|
2 | <container xmlns="http://implab.org/schemas/servicehost/unity.v1.xsd"> | |
|
3 | <namespace name="System"/> | |
|
4 | <namespace name="System.Collections.Generic"/> | |
|
5 | <namespace name="Implab.Components"/> | |
|
6 | <namespace name="Implab.ServiceHost.Test.Mock"/> | |
|
7 | ||
|
8 | <serialized name="p1" type="Person"> | |
|
9 | <Person xmlns="urn:implab:test:model"> | |
|
10 | <FirstName>Com</FirstName> | |
|
11 | <LastName>Truise</LastName> | |
|
12 | <Age>99</Age> | |
|
13 | </Person> | |
|
14 | </serialized> | |
|
15 | <serialized name="p2" type="Person" href="p2.xml"/> | |
|
16 | </container> No newline at end of file |
@@ -0,0 +1,63 | |||
|
1 | <?xml version="1.0"?> | |
|
2 | <container xmlns="http://implab.org/schemas/servicehost/unity.v1.xsd"> | |
|
3 | <namespace name="System"/> | |
|
4 | <namespace name="System.Collections.Generic"/> | |
|
5 | <namespace name="Implab.Components"/> | |
|
6 | <namespace name="Implab.ServiceHost.Test.Mock"/> | |
|
7 | ||
|
8 | <register name="Baz1" type="Baz"> | |
|
9 | <property name="Numbers"> | |
|
10 | <array> | |
|
11 | <value>1</value> | |
|
12 | <value>2</value> | |
|
13 | <value>3</value> | |
|
14 | </array> | |
|
15 | </property> | |
|
16 | </register> | |
|
17 | ||
|
18 | <register type="Baz"> | |
|
19 | <property name="Nuts"> | |
|
20 | <array> | |
|
21 | <dependency/> | |
|
22 | <dependency name="Big"/> | |
|
23 | <dependency name="Big"/> | |
|
24 | <dependency name="Small"/> | |
|
25 | <dependency name="Mid"/> | |
|
26 | </array> | |
|
27 | </property> | |
|
28 | </register> | |
|
29 | ||
|
30 | <register name="Big" type="Baz+Nut"> | |
|
31 | <property name="Size"> | |
|
32 | <value>5</value> | |
|
33 | </property> | |
|
34 | </register> | |
|
35 | <register name="Mid" type="Baz+Nut"> | |
|
36 | <property name="Size"> | |
|
37 | <value>3</value> | |
|
38 | </property> | |
|
39 | </register> | |
|
40 | <register name="Small" type="Baz+Nut"> | |
|
41 | <property name="Size"> | |
|
42 | <value>1</value> | |
|
43 | </property> | |
|
44 | </register> | |
|
45 | <register type="Baz+Nut"> | |
|
46 | <property name="Size"> | |
|
47 | <value>2</value> | |
|
48 | </property> | |
|
49 | </register> | |
|
50 | <factory type="BazFactory"> | |
|
51 | <provides name="Baz2" type="Baz"/> | |
|
52 | </factory> | |
|
53 | ||
|
54 | <factory name="Baz3" type="BazFactory"> | |
|
55 | <property name="IdGenerator"> | |
|
56 | <dependency/> | |
|
57 | </property> | |
|
58 | </factory> | |
|
59 | ||
|
60 | <factory type="GuidFactory"> | |
|
61 | <singleton/> | |
|
62 | </factory> | |
|
63 | </container> No newline at end of file |
@@ -0,0 +1,23 | |||
|
1 | using System; | |
|
2 | using System.Collections.Generic; | |
|
3 | ||
|
4 | namespace Implab.ServiceHost.Test.Mock { | |
|
5 | public class Baz { | |
|
6 | ||
|
7 | public Guid Id { | |
|
8 | get; set; | |
|
9 | } | |
|
10 | ||
|
11 | public int[] Numbers { | |
|
12 | get; set; | |
|
13 | } | |
|
14 | ||
|
15 | public Nut[] Nuts { | |
|
16 | get; set; | |
|
17 | } | |
|
18 | ||
|
19 | public class Nut { | |
|
20 | public int Size { get; set; } | |
|
21 | } | |
|
22 | } | |
|
23 | } No newline at end of file |
@@ -0,0 +1,17 | |||
|
1 | using System; | |
|
2 | using Implab.Components; | |
|
3 | ||
|
4 | namespace Implab.ServiceHost.Test.Mock { | |
|
5 | public class BazFactory : IFactory<Baz> { | |
|
6 | ||
|
7 | public Func<Guid> IdGenerator { | |
|
8 | get; set; | |
|
9 | } | |
|
10 | ||
|
11 | Baz IFactory<Baz>.Create() { | |
|
12 | return new Baz { | |
|
13 | Id = IdGenerator?.Invoke() ?? Guid.Empty | |
|
14 | }; | |
|
15 | } | |
|
16 | } | |
|
17 | } No newline at end of file |
@@ -0,0 +1,13 | |||
|
1 | using System; | |
|
2 | using Implab.Components; | |
|
3 | ||
|
4 | namespace Implab.ServiceHost.Test.Mock { | |
|
5 | public class BoxFactory<T> : IFactory<IBox<T>> { | |
|
6 | public IBox<T> Create() { | |
|
7 | return new Box<T> { | |
|
8 | Name = "Creepy sugar" | |
|
9 | }; | |
|
10 | } | |
|
11 | } | |
|
12 | ||
|
13 | } No newline at end of file |
@@ -0,0 +1,13 | |||
|
1 | namespace Implab.ServiceHost.Test.Mock { | |
|
2 | public class Box<T> : IBox<T> { | |
|
3 | ||
|
4 | public string Name { get; set; } | |
|
5 | ||
|
6 | public T Value { get; set; } | |
|
7 | ||
|
8 | public T GetBoxValue() { | |
|
9 | return Value; | |
|
10 | } | |
|
11 | ||
|
12 | } | |
|
13 | } No newline at end of file |
@@ -0,0 +1,10 | |||
|
1 | using System; | |
|
2 | using Implab.Components; | |
|
3 | ||
|
4 | namespace Implab.ServiceHost.Test.Mock { | |
|
5 | public class GuidFactory : IFactory<Guid> { | |
|
6 | public Guid Create() { | |
|
7 | return Guid.NewGuid(); | |
|
8 | } | |
|
9 | } | |
|
10 | } No newline at end of file |
@@ -0,0 +1,6 | |||
|
1 | namespace Implab.ServiceHost.Test.Mock { | |
|
2 | public interface IBox<T> { | |
|
3 | string Name { get; } | |
|
4 | T GetBoxValue(); | |
|
5 | } | |
|
6 | } No newline at end of file |
@@ -0,0 +1,20 | |||
|
1 | using System.Xml.Serialization; | |
|
2 | ||
|
3 | namespace Implab.ServiceHost.Test.Mock | |
|
4 | { | |
|
5 | [XmlRoot(Namespace="urn:implab:test:model")] | |
|
6 | public class Person { | |
|
7 | public string FirstName { get; set; } | |
|
8 | ||
|
9 | public string LastName { get; set; } | |
|
10 | ||
|
11 | public int Age { get; set; } | |
|
12 | ||
|
13 | [XmlIgnore] | |
|
14 | public bool AgeSpecified { get; set; } | |
|
15 | ||
|
16 | ||
|
17 | [XmlElement("Tag")] | |
|
18 | public string[] Tags { get; set; } | |
|
19 | } | |
|
20 | } No newline at end of file |
@@ -0,0 +1,21 | |||
|
1 | using System; | |
|
2 | using System.Linq; | |
|
3 | ||
|
4 | namespace Implab.ServiceHost.Test.Mock { | |
|
5 | public class SetOfBoxes<T> { | |
|
6 | ||
|
7 | public Guid Uuid { get; set; } | |
|
8 | ||
|
9 | public IBox<T>[] Boxes { | |
|
10 | get; set; | |
|
11 | } | |
|
12 | ||
|
13 | public void BoxValues(T[] items) { | |
|
14 | if (items == null || items.Length == 0) | |
|
15 | return; | |
|
16 | ||
|
17 | Boxes = items.Select(x => new Box<T> {Value = x}).ToArray(); | |
|
18 | } | |
|
19 | ||
|
20 | } | |
|
21 | } No newline at end of file |
@@ -0,0 +1,94 | |||
|
1 | using System; | |
|
2 | using System.Collections.Generic; | |
|
3 | using System.IO; | |
|
4 | using System.Linq; | |
|
5 | using System.Threading; | |
|
6 | using System.Threading.Tasks; | |
|
7 | using Implab.Components; | |
|
8 | using Implab.Diagnostics; | |
|
9 | using Implab.ServiceHost.Test.Mock; | |
|
10 | using Implab.ServiceHost.Unity; | |
|
11 | using Unity; | |
|
12 | using Xunit; | |
|
13 | ||
|
14 | namespace Implab.Test { | |
|
15 | ||
|
16 | public class UnityConfigTest { | |
|
17 | ||
|
18 | [Fact] | |
|
19 | public void CreateContainer() { | |
|
20 | var container = new UnityContainer(); | |
|
21 | ||
|
22 | container.LoadXmlConfiguration(Path.Combine("data","container","empty.xml")); | |
|
23 | } | |
|
24 | ||
|
25 | [Fact] | |
|
26 | public void SimpleServicesContainer() { | |
|
27 | var container = new UnityContainer(); | |
|
28 | ||
|
29 | container.LoadXmlConfiguration(Path.Combine("data","container","simple.services.xml")); | |
|
30 | ||
|
31 | // named service registration | |
|
32 | var baz1 = container.Resolve<Baz>("Baz1"); | |
|
33 | ||
|
34 | Assert.Equal(new [] {1,2,3}, baz1.Numbers); | |
|
35 | ||
|
36 | // default service registration | |
|
37 | var baz = container.Resolve<Baz>(); | |
|
38 | ||
|
39 | Assert.Equal(new [] {2,5,5,1,3}, baz.Nuts.Select(x => x.Size)); | |
|
40 | ||
|
41 | // ServiceFactory registered without a name | |
|
42 | // explicitly provides 'Baz2' service | |
|
43 | var baz2 = container.Resolve<Baz>("Baz2"); | |
|
44 | ||
|
45 | // ServiceFactory registered with name 'Baz3' | |
|
46 | // provides by default the service registration with same name | |
|
47 | var baz3 = container.Resolve<Baz>("Baz3"); | |
|
48 | ||
|
49 | // This factory uses GuidGenerator registration | |
|
50 | // to generate a new id value | |
|
51 | Assert.NotEqual(Guid.Empty, baz3.Id); | |
|
52 | } | |
|
53 | ||
|
54 | [Fact] | |
|
55 | public void GenericServicesContainer() { | |
|
56 | var container = new UnityContainer(); | |
|
57 | container.LoadXmlConfiguration(Path.Combine("data","container","generic.services.xml")); | |
|
58 | ||
|
59 | // resolve using a generig interface mapping | |
|
60 | var box = container.Resolve<IBox<Baz.Nut>>(); | |
|
61 | ||
|
62 | Assert.NotNull(box.GetBoxValue()); | |
|
63 | ||
|
64 | // registered generic defines dependency of type {T} | |
|
65 | // in this case it should resolve to the default Nut with Size=2 | |
|
66 | Assert.Equal(box.GetBoxValue().Size,2); | |
|
67 | ||
|
68 | // generic factory which provides generic services | |
|
69 | var box2 = container.Resolve<IBox<Baz.Nut>>(); | |
|
70 | ||
|
71 | var set1 = container.Resolve<SetOfBoxes<Baz.Nut>>(); | |
|
72 | ||
|
73 | Assert.Equal(new int?[] {null,2,1}, set1.Boxes?.Select(x => x.GetBoxValue()?.Size)); | |
|
74 | } | |
|
75 | ||
|
76 | [Fact] | |
|
77 | public void SerializedInstances() { | |
|
78 | var container = new UnityContainer(); | |
|
79 | container.LoadXmlConfiguration(Path.Combine("data","container","serialized.instances.xml")); | |
|
80 | ||
|
81 | var p1 = container.Resolve<Person>("p1"); | |
|
82 | var p2 = container.Resolve<Person>("p2"); | |
|
83 | ||
|
84 | Assert.Equal("Com", p1.FirstName); | |
|
85 | Assert.True(p1.AgeSpecified); | |
|
86 | Assert.Equal(99, p1.Age); | |
|
87 | ||
|
88 | Assert.Equal("Javolta", p2.LastName); | |
|
89 | Assert.True(p2.AgeSpecified); | |
|
90 | Assert.Equal(88, p2.Age); | |
|
91 | } | |
|
92 | ||
|
93 | } | |
|
94 | } No newline at end of file |
@@ -0,0 +1,36 | |||
|
1 | <Project Sdk="Microsoft.NET.Sdk"> | |
|
2 | ||
|
3 | <PropertyGroup> | |
|
4 | <Authors>Sergey Smirnov</Authors> | |
|
5 | <Title>Implab.ServiceHost</Title> | |
|
6 | <Description>The configurable application host. | |
|
7 | Provides simple and flexible Xml configuration for UnityContainer. | |
|
8 | </Description> | |
|
9 | <Copyright>2012-2018 Sergey Smirnov</Copyright> | |
|
10 | <Version>1.0.3</Version> | |
|
11 | <PackageLicenseUrl>https://bitbucket.org/wozard/implabnet/src/v3/Implab/license.txt</PackageLicenseUrl> | |
|
12 | <PackageProjectUrl>https://bitbucket.org/wozard/implabnet</PackageProjectUrl> | |
|
13 | <RepositoryUrl>https://bitbucket.org/wozard/implabnet</RepositoryUrl> | |
|
14 | <RepositoryType>mercurial</RepositoryType> | |
|
15 | <PackageTags>Implab;Xml configuration;IoC;Unity container</PackageTags> | |
|
16 | </PropertyGroup> | |
|
17 | ||
|
18 | ||
|
19 | <PropertyGroup Condition="'$(OSTYPE)'=='linux'"> | |
|
20 | <TargetFrameworks>netcoreapp2.0;net46</TargetFrameworks> | |
|
21 | <FrameworkPathOverride Condition="'$(TargetFramework)'=='net46'">/usr/lib/mono/4.5/</FrameworkPathOverride> | |
|
22 | </PropertyGroup> | |
|
23 | ||
|
24 | <PropertyGroup Condition="'$(OSTYPE)'=='windows'"> | |
|
25 | <TargetFrameworks>netcoreapp2.0;net46</TargetFrameworks> | |
|
26 | </PropertyGroup> | |
|
27 | ||
|
28 | <ItemGroup> | |
|
29 | <PackageReference Include="Unity" Version="5.8.6" /> | |
|
30 | </ItemGroup> | |
|
31 | ||
|
32 | <ItemGroup> | |
|
33 | <ProjectReference Include="..\Implab\Implab.csproj" /> | |
|
34 | </ItemGroup> | |
|
35 | ||
|
36 | </Project> |
@@ -0,0 +1,113 | |||
|
1 | # XML Конфигурация IoC контейнера | |
|
2 | ||
|
3 | Библиоетка создавалась для загрузки и применения конфигурации к IoC контейнеру, она не предназначена для конфигурирования | |
|
4 | контейнера во время выполнения, большинство контейнеров уже обладают данным функционалом. | |
|
5 | ||
|
6 | На данный момент поддерживается единственный вид контейнера - [Unity Container](https://unitycontainer.github.io/) | |
|
7 | ||
|
8 | Unity уже обладает средствами загрузки конфигурации из XML, данная библиотека реализует аналогичный функционал, кроме того, позволяет решать следующие задачи: | |
|
9 | ||
|
10 | - Конфигурация является простым Xml документом, который можно получить из любого источика и не ограничивается `.config` файлами приложения | |
|
11 | - Включение существующе конфигурации `<include href='config.xml'/>` в текущую | |
|
12 | - Поддержка сериализованных объектов в качестве регистраций сервисов и параметров | |
|
13 | - Поддержка специфиакции генериков любой вложенности, например `Dictionary{Foo,Bar{Int32}}` | |
|
14 | - Поддержка фабрик в XML конфигурации | |
|
15 | - Расширение схемы XML конфигурации собственными элементами | |
|
16 | ||
|
17 | ## Общая архитектура | |
|
18 | ||
|
19 | Пространство имен `Implab.ServiceHost.Unity` содержит в себе классы для загрузки и применения XML конфигурации к IoC контейнеру. | |
|
20 | ||
|
21 | Применение конфигурации к контейнеру состоит из следующих основных шагов: | |
|
22 | ||
|
23 | 1. Настраивается схема `ContainerConfigurationSchema` XML конфигурации контейнера | |
|
24 | 2. Загружается документ и десереализуется в виде `ContainerElement` | |
|
25 | 3. Создается `ContainerBuilder` при помощи которого применяются настройки из `ContainerElement` к контейнеру | |
|
26 | ||
|
27 | Схема `ContainerConfigurationSchema` определяет элементы документа, которые могут быть использованы в конфигурации контейнера, позволяет создать `XmlSerializer`, который используется для загрузки конфигурации. | |
|
28 | ||
|
29 | `ContainerBuilder` - основной класс, который используется для применения конфигурации к контейнеру, он добавляет записи регистрации сервисов из конфигурации в контейнер. | |
|
30 | ||
|
31 | `ContainerElement` - Конфигурация контейнера, которая загружается из документа, состоит из директив для `ContainerBuilder`, а также из записей регистрации сервисов. | |
|
32 | ||
|
33 | В библиотеки приняты следующие правила именования классов: | |
|
34 | ||
|
35 | - `***Builder` используются для применения конфигурации к контейнеру, | |
|
36 | - `***Element` содержат информацию о конфигурации и десериализуются из исходного документа. | |
|
37 | ||
|
38 | ## Структура конфигурации контейнера | |
|
39 | ||
|
40 | Элемент верхнего уровня всегда `container`, он используется для хранения набора элеметов, которые распознаются и выполняются `ContainerBuilder`. | |
|
41 | ||
|
42 | Все элементы, входящие в контейнер наследуются от абстрактного класса `ContainerItemElement`, | |
|
43 | который позволяет получить текущий `ContainerBuilder` и выполнить над ним какие-либо действия. | |
|
44 | ||
|
45 | Основные элементы конфигурации контенейра | |
|
46 | ||
|
47 | - `<include href='config.xml'/>` - включение конфигурации из указанного места | |
|
48 | - `<namespace name='My.App'/>` - добавление пространства имен для поиска типов | |
|
49 | - `<register type='MyGenericType{}'/>` - добавление в контейнер регистрации типа ``My.App.MyGenericType`1`` | |
|
50 | - `<factory type='IFactory{Foo}' mapTo ='MyFactory'/>` - фабрика, регистрирует свой тип, а также тип `Foo` | |
|
51 | - `<serialized />` - сериализованный экземпляр объекта, использует `XmlSerializer` для десериализации | |
|
52 | - `<value />` - значение объекта в виде строки, используется для простых типов | |
|
53 | ||
|
54 | Полный набор элементов, доступных для использования в контейнере определяет схема `ContainerConfigurationSchema`, | |
|
55 | разработчик может расширить контейнер совими собственными элементами зарегистрировав их в схеме. | |
|
56 | ||
|
57 | ### register | |
|
58 | ||
|
59 | О | |
|
60 | ||
|
61 | ||
|
62 | Например, мы используем компоненту `MyHttpClient` для загрузки данных | |
|
63 | ||
|
64 | ```xml | |
|
65 | <register type="IClient" mapTo="MyHttpClient"> | |
|
66 | <property name="proxy"> | |
|
67 | <value>socks5://proxy1.my.company</value> | |
|
68 | </property> | |
|
69 | </register> | |
|
70 | ``` | |
|
71 | ||
|
72 | При частом использовании можно сделать описание конфигурации несколько проще, | |
|
73 | описав новый эелемент конфигурации | |
|
74 | ||
|
75 | ```csharp | |
|
76 | public class MyHttpClientElement : ContainerItemElement { | |
|
77 | ||
|
78 | public string Proxy { get; set; } | |
|
79 | ||
|
80 | public override void Visit(ContainerBuilder builder) { | |
|
81 | // создаем описание элемента | |
|
82 | var registration = new RegistrationElement { | |
|
83 | RegistrationType = "IClient", | |
|
84 | ImplementationType = "My.App.MyHttpClient", | |
|
85 | Injectors = new [] { | |
|
86 | new PropertyInjectionElement { | |
|
87 | Name = "Proxy", | |
|
88 | Value = ValueParameterElement { | |
|
89 | Value = Proxy | |
|
90 | } | |
|
91 | } | |
|
92 | } | |
|
93 | }; | |
|
94 | ||
|
95 | // применяем созданное описание к контейнеру | |
|
96 | builder.Visit(registration) | |
|
97 | } | |
|
98 | } | |
|
99 | ``` | |
|
100 | ||
|
101 | Регистрируем новый элемент в схеме | |
|
102 | ||
|
103 | ```csharp | |
|
104 | schema.RegisterContainerElement<MyHttpClientElement>("http"); | |
|
105 | ``` | |
|
106 | ||
|
107 | Используем новый элемент в конфигурации | |
|
108 | ||
|
109 | ```xml | |
|
110 | <http> | |
|
111 | <proxy>socks5://proxy1.my.company</propxy> | |
|
112 | </http> | |
|
113 | ``` No newline at end of file |
@@ -0,0 +1,12 | |||
|
1 | namespace Implab.ServiceHost.Unity { | |
|
2 | ||
|
3 | /// <summary> | |
|
4 | /// Базовый класс для элеметов контейнера. | |
|
5 | /// </summary> | |
|
6 | /// <remarks> | |
|
7 | /// XmlSerializer требует использования классов при объявлении свойств, которые будут сериализованы <see cref="ContainerElement.Items"/> | |
|
8 | /// </remarks> | |
|
9 | public abstract class AbstractContainerItem { | |
|
10 | public abstract void Visit(ContainerBuilder builder); | |
|
11 | } | |
|
12 | } No newline at end of file |
@@ -0,0 +1,12 | |||
|
1 | using System; | |
|
2 | using System.Xml.Serialization; | |
|
3 | ||
|
4 | namespace Implab.ServiceHost.Unity { | |
|
5 | public abstract class AbstractInjectionParameter : IInjectionParameter { | |
|
6 | ||
|
7 | [XmlAttribute("type")] | |
|
8 | public string TypeName { get; set; } | |
|
9 | ||
|
10 | public abstract void Visit(InjectionParameterBuilder builder); | |
|
11 | } | |
|
12 | } No newline at end of file |
@@ -0,0 +1,10 | |||
|
1 | using System; | |
|
2 | ||
|
3 | namespace Implab.ServiceHost.Unity { | |
|
4 | /// <summary> | |
|
5 | /// Base class for injections, each injection is applied to the type registration context. | |
|
6 | /// </summary> | |
|
7 | public abstract class AbstractMemberInjection : ITypeMemberInjection { | |
|
8 | public abstract void Visit(TypeRegistrationBuilder builder); | |
|
9 | } | |
|
10 | } No newline at end of file |
@@ -0,0 +1,48 | |||
|
1 | using System; | |
|
2 | using System.Xml.Serialization; | |
|
3 | using Unity.Lifetime; | |
|
4 | using Unity.Registration; | |
|
5 | ||
|
6 | namespace Implab.ServiceHost.Unity | |
|
7 | { | |
|
8 | /// <summary> | |
|
9 | /// Базовая информаци о регистрации в контейнере: тип, имя и время жизни | |
|
10 | /// </summary> | |
|
11 | public abstract class AbstractRegistration : AbstractContainerItem, IRegistration { | |
|
12 | ||
|
13 | /// <summary> | |
|
14 | /// An optional name for a registration in the container | |
|
15 | /// </summary> | |
|
16 | [XmlAttribute("name")] | |
|
17 | public string Name { | |
|
18 | get; set; | |
|
19 | } | |
|
20 | ||
|
21 | [XmlElement("signleton", typeof(SingletonLifetimeElement))] | |
|
22 | [XmlElement("context", typeof(ContextLifetimeElement))] | |
|
23 | [XmlElement("container", typeof(ContainerLifetimeElement))] | |
|
24 | [XmlElement("hierarchy", typeof(HierarchicalLifetimeElement))] | |
|
25 | public LifetimeElement Lifetime {get; set;} | |
|
26 | ||
|
27 | /// <summary> | |
|
28 | /// A type specification for the service registration, | |
|
29 | /// </summary> | |
|
30 | [XmlAttribute("type")] | |
|
31 | public string RegistrationType { get; set; } | |
|
32 | ||
|
33 | public virtual LifetimeManager GetLifetime(ContainerBuilder builder) { | |
|
34 | return Lifetime?.GetLifetime(builder); | |
|
35 | } | |
|
36 | ||
|
37 | public virtual Type GetRegistrationType(Func<string,Type> resolver) { | |
|
38 | return resolver(RegistrationType); | |
|
39 | } | |
|
40 | ||
|
41 | public virtual Type GetRegistrationType(ContainerBuilder builder) { | |
|
42 | return builder.ResolveType(RegistrationType); | |
|
43 | } | |
|
44 | ||
|
45 | ||
|
46 | ||
|
47 | } | |
|
48 | } No newline at end of file |
@@ -0,0 +1,20 | |||
|
1 | using System.Xml.Serialization; | |
|
2 | ||
|
3 | namespace Implab.ServiceHost.Unity | |
|
4 | { | |
|
5 | public class ArrayParameterElement : AbstractInjectionParameter { | |
|
6 | ||
|
7 | [XmlAttribute("itemsType")] | |
|
8 | public string ItemsType { get; set; } | |
|
9 | ||
|
10 | [XmlElement("dependency", typeof(DependencyParameterElement))] | |
|
11 | [XmlElement("value", typeof(ValueParameterElement))] | |
|
12 | [XmlElement("serialized", typeof(SerializedParameterElement))] | |
|
13 | [XmlElement("default", typeof(DefaultParameterElement))] | |
|
14 | public AbstractInjectionParameter[] Items { get; set; } | |
|
15 | ||
|
16 | public override void Visit(InjectionParameterBuilder builder) { | |
|
17 | builder.Visit(this); | |
|
18 | } | |
|
19 | } | |
|
20 | } No newline at end of file |
@@ -0,0 +1,57 | |||
|
1 | using System; | |
|
2 | using System.Text; | |
|
3 | ||
|
4 | namespace Implab.ServiceHost.Unity { | |
|
5 | public class ArrayTypeReference : TypeReference { | |
|
6 | public int Rank { get; private set; } | |
|
7 | ||
|
8 | public TypeReference ItemsType { get; private set; } | |
|
9 | ||
|
10 | public override string Name { | |
|
11 | get { | |
|
12 | return ItemsType.Name; | |
|
13 | } | |
|
14 | } | |
|
15 | ||
|
16 | public override string ClrName { | |
|
17 | get { | |
|
18 | return new StringBuilder() | |
|
19 | .Append(ItemsType.ClrName) | |
|
20 | .Append("[") | |
|
21 | .Append(',', Rank - 1) | |
|
22 | .Append("]") | |
|
23 | .ToString(); | |
|
24 | } | |
|
25 | } | |
|
26 | ||
|
27 | public override string Namespace { | |
|
28 | get { | |
|
29 | return ItemsType.Namespace; | |
|
30 | } | |
|
31 | } | |
|
32 | ||
|
33 | public override int GenericParametersCount { | |
|
34 | get { | |
|
35 | return 0; | |
|
36 | } | |
|
37 | } | |
|
38 | ||
|
39 | internal ArrayTypeReference(TypeReference itemsType, int rank) { | |
|
40 | ItemsType = itemsType; | |
|
41 | Rank = rank; | |
|
42 | } | |
|
43 | ||
|
44 | internal override void Visit(TypeResolutionContext visitor) { | |
|
45 | visitor.Visit(this); | |
|
46 | } | |
|
47 | ||
|
48 | override public string ToString() { | |
|
49 | return new StringBuilder() | |
|
50 | .Append(ItemsType.ToString()) | |
|
51 | .Append('[') | |
|
52 | .Append(',', Rank - 1) | |
|
53 | .Append(']') | |
|
54 | .ToString(); | |
|
55 | } | |
|
56 | } | |
|
57 | } No newline at end of file |
@@ -0,0 +1,14 | |||
|
1 | using System.Xml.Serialization; | |
|
2 | ||
|
3 | namespace Implab.ServiceHost.Unity | |
|
4 | { | |
|
5 | [XmlRoot("assembly", Namespace = Schema.ContainerConfigurationNamespace)] | |
|
6 | public class AssemblyElement : AbstractContainerItem { | |
|
7 | [XmlAttribute("name")] | |
|
8 | public string AssemblyName { get; set; } | |
|
9 | ||
|
10 | public override void Visit(ContainerBuilder builder) { | |
|
11 | builder.AddAssembly(AssemblyName); | |
|
12 | } | |
|
13 | } | |
|
14 | } No newline at end of file |
@@ -0,0 +1,17 | |||
|
1 | using System.Xml.Serialization; | |
|
2 | ||
|
3 | namespace Implab.ServiceHost.Unity { | |
|
4 | public class ConstructorInjectionElement : AbstractMemberInjection { | |
|
5 | ||
|
6 | [XmlElement("dependency", typeof(DependencyParameterElement))] | |
|
7 | [XmlElement("value", typeof(ValueParameterElement))] | |
|
8 | [XmlElement("serialized", typeof(SerializedParameterElement))] | |
|
9 | [XmlElement("default", typeof(DefaultParameterElement))] | |
|
10 | [XmlElement("array", typeof(ArrayParameterElement))] | |
|
11 | public AbstractInjectionParameter[] Parameters { get; set; } | |
|
12 | ||
|
13 | public override void Visit(TypeRegistrationBuilder builder) { | |
|
14 | builder.Visit(this); | |
|
15 | } | |
|
16 | } | |
|
17 | } No newline at end of file |
@@ -0,0 +1,161 | |||
|
1 | using System; | |
|
2 | using System.IO; | |
|
3 | using System.Reflection; | |
|
4 | using Implab.Diagnostics; | |
|
5 | using Unity; | |
|
6 | ||
|
7 | namespace Implab.ServiceHost.Unity { | |
|
8 | using Log = Trace<ContainerBuilder>; | |
|
9 | ||
|
10 | public class ContainerBuilder { | |
|
11 | ||
|
12 | readonly TypeResolver m_resolver; | |
|
13 | ||
|
14 | readonly IUnityContainer m_container; | |
|
15 | ||
|
16 | readonly ContainerConfigurationSchema m_schema; | |
|
17 | ||
|
18 | Uri m_location; | |
|
19 | ||
|
20 | public IUnityContainer Container { | |
|
21 | get { | |
|
22 | return m_container; | |
|
23 | } | |
|
24 | } | |
|
25 | ||
|
26 | public ContainerBuilder() : this(null, null) { | |
|
27 | } | |
|
28 | ||
|
29 | public ContainerBuilder(IUnityContainer container, ContainerConfigurationSchema schema) { | |
|
30 | m_container = container ?? new UnityContainer(); | |
|
31 | m_resolver = new TypeResolver(); | |
|
32 | m_schema = schema ?? ContainerConfigurationSchema.Default; | |
|
33 | } | |
|
34 | ||
|
35 | public Type ResolveType(string typeReference) { | |
|
36 | var resolved = string.IsNullOrEmpty(typeReference) ? null : m_resolver.Resolve(typeReference, true); | |
|
37 | Log.Debug("ResolveType('{0}'): {1}", typeReference, resolved?.FullName); | |
|
38 | return resolved; | |
|
39 | } | |
|
40 | ||
|
41 | public void Visit(ITypeRegistration registration) { | |
|
42 | Safe.ArgumentNotNull(registration, nameof(registration)); | |
|
43 | ||
|
44 | var registrationType = registration.GetRegistrationType(this); | |
|
45 | var implementationType = registration.GetImplementationType(this) ?? registrationType; | |
|
46 | ||
|
47 | if (registrationType == null) | |
|
48 | throw new Exception($"A type must be specified for the registration {registration.Name}"); | |
|
49 | ||
|
50 | var builder = new TypeRegistrationBuilder( | |
|
51 | m_resolver, | |
|
52 | registrationType, | |
|
53 | implementationType, | |
|
54 | this | |
|
55 | ); | |
|
56 | ||
|
57 | builder.Lifetime = registration.GetLifetime(this); | |
|
58 | ||
|
59 | if (registration.MemberInjections != null) { | |
|
60 | foreach(var member in registration.MemberInjections) | |
|
61 | member.Visit(builder); | |
|
62 | } | |
|
63 | ||
|
64 | m_container.RegisterType( | |
|
65 | builder.RegistrationType, | |
|
66 | builder.ImplementationType, | |
|
67 | registration.Name, | |
|
68 | builder.Lifetime, | |
|
69 | builder.Injections | |
|
70 | ); | |
|
71 | } | |
|
72 | ||
|
73 | public void Visit(IInstanceRegistration registration) { | |
|
74 | Safe.ArgumentNotNull(registration, nameof(registration)); | |
|
75 | ||
|
76 | var registrationType = registration.GetRegistrationType(this); | |
|
77 | ||
|
78 | var builder = new InstanceRegistrationBuilder ( | |
|
79 | m_resolver, | |
|
80 | registrationType, | |
|
81 | this | |
|
82 | ); | |
|
83 | ||
|
84 | builder.Lifetime = registration.GetLifetime(this); | |
|
85 | ||
|
86 | if (registration.MemberInjections != null) { | |
|
87 | foreach(var member in registration.MemberInjections) | |
|
88 | member.Visit(builder.ValueBuilder); | |
|
89 | } | |
|
90 | ||
|
91 | if (builder.RegistrationType == null && builder.ValueBuilder.ValueType == null) | |
|
92 | throw new Exception($"A type must be specified for the registration {registration.Name}"); | |
|
93 | ||
|
94 | m_container.RegisterInstance( | |
|
95 | builder.RegistrationType ?? builder.ValueBuilder.ValueType, | |
|
96 | registration.Name, | |
|
97 | builder.ValueBuilder.Value, | |
|
98 | builder.Lifetime | |
|
99 | ); | |
|
100 | } | |
|
101 | ||
|
102 | public void AddNamespace(string ns) { | |
|
103 | m_resolver.AddNamespace(ns); | |
|
104 | Log.Log($"AddNamespace: {ns}"); | |
|
105 | } | |
|
106 | ||
|
107 | public void AddAssembly(string assembly) { | |
|
108 | var asm = Assembly.Load(assembly); | |
|
109 | Log.Log($"AddAssembly: {assembly} -> {asm.FullName}"); | |
|
110 | } | |
|
111 | ||
|
112 | /// <summary> | |
|
113 | /// Includes the confguration. Creates a new <see cref="ContainerBuilder"/>, | |
|
114 | /// and loads the configuration to it. The created builder will share the | |
|
115 | /// container and will have its own isolated type resolver. | |
|
116 | /// </summary> | |
|
117 | /// <param name="file">A path to configuration relative to the current configuration.</param> | |
|
118 | public void Include(string file) { | |
|
119 | var includeContext = new ContainerBuilder(m_container, m_schema); | |
|
120 | ||
|
121 | if (m_location != null) { | |
|
122 | var uri = new Uri(m_location, file); | |
|
123 | includeContext.LoadConfig(uri); | |
|
124 | } else { | |
|
125 | includeContext.LoadConfig(file); | |
|
126 | } | |
|
127 | } | |
|
128 | ||
|
129 | /// <summary> | |
|
130 | /// Resolves a path ralatively to the current container configuration location. | |
|
131 | /// </summary> | |
|
132 | /// <param name="location">A path yto resolve</param> | |
|
133 | /// <returns>Resolved Uri fot the specified location</returns> | |
|
134 | public Uri MakeLocationUri(string location) { | |
|
135 | return m_location != null ? new Uri(m_location, location) : new Uri(location); | |
|
136 | } | |
|
137 | ||
|
138 | /// <summary> | |
|
139 | /// Loads a configuration from the specified local file. | |
|
140 | /// </summary> | |
|
141 | /// <param name="file">The path to the configuration file.</param> | |
|
142 | public void LoadConfig(string file) { | |
|
143 | Safe.ArgumentNotEmpty(file, nameof(file)); | |
|
144 | ||
|
145 | LoadConfig(new Uri(Path.GetFullPath(file))); | |
|
146 | } | |
|
147 | ||
|
148 | public void LoadConfig(Uri location) { | |
|
149 | Safe.ArgumentNotNull(location, nameof(location)); | |
|
150 | ||
|
151 | Log.Log($"LoadConfig {location}"); | |
|
152 | Safe.ArgumentNotNull(location, nameof(location)); | |
|
153 | ||
|
154 | m_location = location; | |
|
155 | ||
|
156 | var config = m_schema.LoadConfig(location.ToString()); | |
|
157 | config.Visit(this); | |
|
158 | } | |
|
159 | ||
|
160 | } | |
|
161 | } No newline at end of file |
@@ -0,0 +1,70 | |||
|
1 | using System; | |
|
2 | using System.Collections.Generic; | |
|
3 | using System.IO; | |
|
4 | using System.Reflection; | |
|
5 | using System.Xml; | |
|
6 | using System.Xml.Serialization; | |
|
7 | using Implab.Components; | |
|
8 | ||
|
9 | namespace Implab.ServiceHost.Unity { | |
|
10 | public class ContainerConfigurationSchema { | |
|
11 | ||
|
12 | public static ContainerConfigurationSchema Default { get; private set; } = CreateDefault(); | |
|
13 | ||
|
14 | readonly LazyAndWeak<XmlSerializer> m_seralizer; | |
|
15 | ||
|
16 | readonly XmlAttributeOverrides m_overrides = new XmlAttributeOverrides(); | |
|
17 | ||
|
18 | readonly XmlAttributes m_containerItems = new XmlAttributes(); | |
|
19 | ||
|
20 | public XmlSerializer Serializer { | |
|
21 | get { | |
|
22 | return m_seralizer.Value; | |
|
23 | } | |
|
24 | } | |
|
25 | ||
|
26 | public ContainerConfigurationSchema() { | |
|
27 | m_overrides.Add(typeof(ContainerElement), nameof(ContainerElement.Items), m_containerItems); | |
|
28 | ||
|
29 | m_seralizer = new LazyAndWeak<XmlSerializer>(() => new XmlSerializer(typeof(ContainerElement), m_overrides)); | |
|
30 | } | |
|
31 | ||
|
32 | public void RegisterContainerElement(Type type, string name) { | |
|
33 | Safe.ArgumentNotNull(type, nameof(type)); | |
|
34 | Safe.ArgumentNotEmpty(name, nameof(name)); | |
|
35 | ||
|
36 | if(!type.IsSubclassOf(typeof(AbstractContainerItem))) | |
|
37 | throw new Exception($"RegisterContainerElement '{name}': {type} must be subclass of {typeof(AbstractContainerItem)}"); | |
|
38 | ||
|
39 | m_containerItems.XmlElements.Add( | |
|
40 | new XmlElementAttribute(name, type) | |
|
41 | ); | |
|
42 | } | |
|
43 | ||
|
44 | public void RegisterContainerElement<T>(string name) where T : AbstractContainerItem { | |
|
45 | RegisterContainerElement(typeof(T), name); | |
|
46 | } | |
|
47 | ||
|
48 | public ContainerElement LoadConfig(string uri) { | |
|
49 | using (var reader = XmlReader.Create(uri)) { | |
|
50 | return (ContainerElement)Serializer.Deserialize(reader); | |
|
51 | } | |
|
52 | } | |
|
53 | ||
|
54 | static ContainerConfigurationSchema CreateDefault() { | |
|
55 | var schema = new ContainerConfigurationSchema(); | |
|
56 | ||
|
57 | schema.RegisterContainerElement<RegisterElement>("register"); | |
|
58 | schema.RegisterContainerElement<FactoryElement>("factory"); | |
|
59 | schema.RegisterContainerElement<SerializedElement>("serialized"); | |
|
60 | schema.RegisterContainerElement<ValueElement>("value"); | |
|
61 | schema.RegisterContainerElement<IncludeElement>("include"); | |
|
62 | schema.RegisterContainerElement<AssemblyElement>("assembly"); | |
|
63 | schema.RegisterContainerElement<NamespaceElement>("namespace"); | |
|
64 | ||
|
65 | return schema; | |
|
66 | } | |
|
67 | ||
|
68 | ||
|
69 | } | |
|
70 | } No newline at end of file |
@@ -0,0 +1,19 | |||
|
1 | using Implab.Xml; | |
|
2 | using System.Collections.Generic; | |
|
3 | using System.Xml; | |
|
4 | using System.Xml.Schema; | |
|
5 | using System.Xml.Serialization; | |
|
6 | ||
|
7 | namespace Implab.ServiceHost.Unity { | |
|
8 | [XmlRoot("container", Namespace = Schema.ContainerConfigurationNamespace)] | |
|
9 | public class ContainerElement : AbstractContainerItem { | |
|
10 | ||
|
11 | public List<AbstractContainerItem> Items { get; set; } = new List<AbstractContainerItem>(); | |
|
12 | ||
|
13 | public override void Visit(ContainerBuilder context) { | |
|
14 | if (Items != null) | |
|
15 | foreach(var item in Items) | |
|
16 | item.Visit(context); | |
|
17 | } | |
|
18 | } | |
|
19 | } No newline at end of file |
@@ -0,0 +1,12 | |||
|
1 | using Unity.Lifetime; | |
|
2 | ||
|
3 | namespace Implab.ServiceHost.Unity | |
|
4 | { | |
|
5 | public class ContainerLifetimeElement : LifetimeElement { | |
|
6 | public override LifetimeManager GetLifetime(ContainerBuilder builder) { | |
|
7 | return new ContainerControlledLifetimeManager(); | |
|
8 | } | |
|
9 | ||
|
10 | ||
|
11 | } | |
|
12 | } No newline at end of file |
@@ -0,0 +1,10 | |||
|
1 | using Unity.Lifetime; | |
|
2 | ||
|
3 | namespace Implab.ServiceHost.Unity | |
|
4 | { | |
|
5 | public class ContextLifetimeElement : LifetimeElement { | |
|
6 | public override LifetimeManager GetLifetime(ContainerBuilder builder) { | |
|
7 | return new PerResolveLifetimeManager(); | |
|
8 | } | |
|
9 | } | |
|
10 | } No newline at end of file |
@@ -0,0 +1,13 | |||
|
1 | namespace Implab.ServiceHost.Unity | |
|
2 | { | |
|
3 | public class DefaultParameterElement : AbstractInjectionParameter { | |
|
4 | public string Value { | |
|
5 | get { return null; } | |
|
6 | } | |
|
7 | ||
|
8 | public override void Visit(InjectionParameterBuilder builder) { | |
|
9 | var type = builder.ResolveInjectedValueType(TypeName); | |
|
10 | builder.SetValue(type, Safe.CreateDefaultValue(type)); | |
|
11 | } | |
|
12 | } | |
|
13 | } No newline at end of file |
@@ -0,0 +1,17 | |||
|
1 | using System.Xml.Serialization; | |
|
2 | ||
|
3 | namespace Implab.ServiceHost.Unity { | |
|
4 | public class DependencyParameterElement : AbstractInjectionParameter { | |
|
5 | ||
|
6 | [XmlAttribute("name")] | |
|
7 | public string DependencyName { get; set; } | |
|
8 | ||
|
9 | [XmlAttribute("optional")] | |
|
10 | public bool Optional { get; set; } | |
|
11 | ||
|
12 | public override void Visit(InjectionParameterBuilder builder) { | |
|
13 | var type = builder.ResolveInjectedValueType(TypeName); | |
|
14 | builder.SetDependency(type, DependencyName, Optional); | |
|
15 | } | |
|
16 | } | |
|
17 | } No newline at end of file |
@@ -0,0 +1,78 | |||
|
1 | using System; | |
|
2 | using System.Collections.Generic; | |
|
3 | using System.Reflection; | |
|
4 | using Implab.Components; | |
|
5 | using Unity; | |
|
6 | using Unity.Injection; | |
|
7 | using Unity.Lifetime; | |
|
8 | ||
|
9 | namespace Implab.ServiceHost.Unity { | |
|
10 | public class FactoryActivator : ITypeRegistration { | |
|
11 | ||
|
12 | class FactoryInjector : ITypeMemberInjection { | |
|
13 | public InjectionFactory Factory { get; set; } | |
|
14 | public void Visit(TypeRegistrationBuilder builder) { | |
|
15 | builder.AddInjectionMember(Factory); | |
|
16 | } | |
|
17 | } | |
|
18 | ||
|
19 | ||
|
20 | public Type FactoryType { get; set; } | |
|
21 | ||
|
22 | public string FactoryName { get; set; } | |
|
23 | ||
|
24 | public Type RegistrationType { get; set; } | |
|
25 | ||
|
26 | public LifetimeManager Lifetime { get; set; } | |
|
27 | ||
|
28 | public IEnumerable<ITypeMemberInjection> MemberInjections { | |
|
29 | get { | |
|
30 | if (RegistrationType == null) | |
|
31 | throw new Exception($"RegistrationType must be specified"); | |
|
32 | if (!typeof(IFactory<>).MakeGenericType(RegistrationType).IsAssignableFrom(FactoryType)) | |
|
33 | throw new Exception($"The factory {FactoryType} can't be used to create {RegistrationType} instances"); | |
|
34 | ||
|
35 | if (FactoryType.ContainsGenericParameters) { | |
|
36 | yield return new FactoryInjector { | |
|
37 | Factory = CreateDynamicInjectionFactory(FactoryName) | |
|
38 | }; | |
|
39 | } else { | |
|
40 | yield return new FactoryInjector { | |
|
41 | Factory = (InjectionFactory)GetType() | |
|
42 | .GetMethod(nameof(CreateInjectionFactory), BindingFlags.Static | BindingFlags.NonPublic) | |
|
43 | .MakeGenericMethod(FactoryType, RegistrationType) | |
|
44 | .Invoke(null, new [] { FactoryName }) | |
|
45 | }; | |
|
46 | } | |
|
47 | } | |
|
48 | } | |
|
49 | ||
|
50 | public string Name { get; set; } | |
|
51 | ||
|
52 | public Type GetRegistrationType(ContainerBuilder builder) { | |
|
53 | return RegistrationType; | |
|
54 | } | |
|
55 | ||
|
56 | public LifetimeManager GetLifetime(ContainerBuilder builder) { | |
|
57 | return Lifetime; | |
|
58 | } | |
|
59 | ||
|
60 | public Type GetImplementationType(ContainerBuilder builder) { | |
|
61 | return null; | |
|
62 | } | |
|
63 | ||
|
64 | /// <summary> | |
|
65 | /// Указывает зависимость, реализующую интерфейс <see cref="IFactory{TObj}"/>, | |
|
66 | /// которая будет использоваться в качестве фабрики для создания объектов | |
|
67 | /// </summary> | |
|
68 | /// <param name="dependencyName"></param> | |
|
69 | static InjectionFactory CreateInjectionFactory<TFac, TObj>(string dependencyName) where TFac : IFactory<TObj> { | |
|
70 | ||
|
71 | return new InjectionFactory(c => c.Resolve<TFac>(dependencyName).Create()); | |
|
72 | } | |
|
73 | ||
|
74 | static InjectionFactory CreateDynamicInjectionFactory(string dependencyName) { | |
|
75 | return new InjectionFactory((c,t,name) => ((IFactory<object>)c.Resolve(t, dependencyName)).Create()); | |
|
76 | } | |
|
77 | } | |
|
78 | } No newline at end of file |
@@ -0,0 +1,73 | |||
|
1 | using System; | |
|
2 | using System.Xml.Serialization; | |
|
3 | using Implab.Components; | |
|
4 | ||
|
5 | namespace Implab.ServiceHost.Unity { | |
|
6 | /// <summary> | |
|
7 | /// Расширяет стандартную регистрацию типа до фабрики, вместе с регистрацией | |
|
8 | /// самой фабрики создаются регистрации сервисов, которые она предоставляет. | |
|
9 | /// </summary> | |
|
10 | public class FactoryElement : RegisterElement, ITypeRegistration { | |
|
11 | ||
|
12 | /// <summary> | |
|
13 | /// Записи о сервисах, которые создаются данной фабрикой. | |
|
14 | /// </summary> | |
|
15 | /// <remarks> | |
|
16 | /// Сервисы, которые указаны в регистарциях они должны соответсвовать тому, | |
|
17 | /// что фабрика возвращает, но это остается на откуп контейнера | |
|
18 | /// </remarks> | |
|
19 | [XmlElement("provides")] | |
|
20 | public ProvidesElement[] Provides { get; set; } | |
|
21 | ||
|
22 | /// <summary> | |
|
23 | /// Переопределяет стандарное поведение регистрации типа в контейнере, | |
|
24 | /// дополняя его регистрацией фабричных методов для получения типов. | |
|
25 | /// </summary> | |
|
26 | /// <param name="builder">Объект для конфигурирования контейнера.</param> | |
|
27 | public override void Visit(ContainerBuilder builder) { | |
|
28 | var factoryType = GetRegistrationType(builder.ResolveType); | |
|
29 | ||
|
30 | base.Visit(builder); | |
|
31 | ||
|
32 | if (Provides != null && Provides.Length > 0) { | |
|
33 | // если регистрации явно заданы, используеися информация из них | |
|
34 | foreach(var item in Provides) { | |
|
35 | var activator = new FactoryActivator { | |
|
36 | Name = item.RegistrationName, | |
|
37 | RegistrationType = builder.ResolveType(item.RegistrationType), | |
|
38 | FactoryName = Name, | |
|
39 | FactoryType = factoryType, | |
|
40 | Lifetime = item.Lifetime?.GetLifetime(builder) | |
|
41 | }; | |
|
42 | builder.Visit(activator); | |
|
43 | } | |
|
44 | } else { | |
|
45 | // если регистрация явно не задана, в качестве сервиса для регистрации | |
|
46 | // используется тип создаваемый фабрикой, который будет добавлен в контейнер | |
|
47 | // с темже именем, что и сама фабрика (разные типы могут иметь одно имя для регистрации) | |
|
48 | var providedType = ( | |
|
49 | factoryType.IsGenericType && factoryType.GetGenericTypeDefinition() == typeof(IFactory<>) ? | |
|
50 | factoryType : | |
|
51 | factoryType.GetInterface(typeof(IFactory<>).FullName) | |
|
52 | )? | |
|
53 | .GetGenericArguments()[0]; | |
|
54 | ||
|
55 | // не удалось определеить тип | |
|
56 | if (providedType == null) | |
|
57 | throw new ArgumentException("Failed to determine a type provided by the factory"); | |
|
58 | ||
|
59 | if (providedType.IsGenericParameter) | |
|
60 | throw new ArgumentException("Can't register a generic type paramter as a service"); | |
|
61 | ||
|
62 | var activator = new FactoryActivator { | |
|
63 | Name = Name, | |
|
64 | RegistrationType = providedType, | |
|
65 | FactoryName = Name, | |
|
66 | FactoryType = factoryType | |
|
67 | }; | |
|
68 | ||
|
69 | builder.Visit(activator); | |
|
70 | } | |
|
71 | } | |
|
72 | } | |
|
73 | } No newline at end of file |
@@ -0,0 +1,10 | |||
|
1 | using Unity.Lifetime; | |
|
2 | ||
|
3 | namespace Implab.ServiceHost.Unity | |
|
4 | { | |
|
5 | public class HierarchicalLifetimeElement : LifetimeElement { | |
|
6 | public override LifetimeManager GetLifetime(ContainerBuilder builder) { | |
|
7 | return new HierarchicalLifetimeManager(); | |
|
8 | } | |
|
9 | } | |
|
10 | } No newline at end of file |
@@ -0,0 +1,5 | |||
|
1 | namespace Implab.ServiceHost.Unity { | |
|
2 | public interface IInjectionParameter { | |
|
3 | void Visit(InjectionParameterBuilder builder); | |
|
4 | } | |
|
5 | } No newline at end of file |
@@ -0,0 +1,8 | |||
|
1 | using System.Collections.Generic; | |
|
2 | ||
|
3 | namespace Implab.ServiceHost.Unity { | |
|
4 | public interface IInstanceRegistration : IRegistration { | |
|
5 | ||
|
6 | IEnumerable<IInjectionParameter> MemberInjections { get; } | |
|
7 | } | |
|
8 | } No newline at end of file |
@@ -0,0 +1,12 | |||
|
1 | using System; | |
|
2 | using Unity.Lifetime; | |
|
3 | ||
|
4 | namespace Implab.ServiceHost.Unity { | |
|
5 | public interface IRegistration { | |
|
6 | string Name { get; } | |
|
7 | ||
|
8 | Type GetRegistrationType(ContainerBuilder builder); | |
|
9 | ||
|
10 | LifetimeManager GetLifetime(ContainerBuilder builder); | |
|
11 | } | |
|
12 | } No newline at end of file |
@@ -0,0 +1,6 | |||
|
1 | namespace Implab.ServiceHost.Unity | |
|
2 | { | |
|
3 | public interface ITypeMemberInjection { | |
|
4 | void Visit(TypeRegistrationBuilder builder); | |
|
5 | } | |
|
6 | } No newline at end of file |
@@ -0,0 +1,10 | |||
|
1 | using System; | |
|
2 | using System.Collections.Generic; | |
|
3 | ||
|
4 | namespace Implab.ServiceHost.Unity { | |
|
5 | public interface ITypeRegistration : IRegistration { | |
|
6 | Type GetImplementationType(ContainerBuilder builder); | |
|
7 | ||
|
8 | IEnumerable<ITypeMemberInjection> MemberInjections { get; } | |
|
9 | } | |
|
10 | } No newline at end of file |
@@ -0,0 +1,13 | |||
|
1 | using System.Xml.Serialization; | |
|
2 | ||
|
3 | namespace Implab.ServiceHost.Unity { | |
|
4 | [XmlRoot("include", Namespace = Schema.ContainerConfigurationNamespace)] | |
|
5 | public class IncludeElement : AbstractContainerItem { | |
|
6 | [XmlAttribute("href")] | |
|
7 | public string Href { get; set; } | |
|
8 | ||
|
9 | public override void Visit(ContainerBuilder builder) { | |
|
10 | builder.Include(Href); | |
|
11 | } | |
|
12 | } | |
|
13 | } No newline at end of file |
@@ -0,0 +1,142 | |||
|
1 | using System; | |
|
2 | using System.Collections; | |
|
3 | using System.Collections.Generic; | |
|
4 | using System.ComponentModel; | |
|
5 | using System.Linq; | |
|
6 | using System.Xml.Serialization; | |
|
7 | using Unity.Injection; | |
|
8 | ||
|
9 | namespace Implab.ServiceHost.Unity { | |
|
10 | ||
|
11 | public class InjectionParameterBuilder { | |
|
12 | ||
|
13 | readonly TypeResolver m_resolver; | |
|
14 | ||
|
15 | public Type DefaultType { get; private set; } | |
|
16 | ||
|
17 | public Type ValueType { get; private set; } | |
|
18 | ||
|
19 | public ContainerBuilder Root { get; private set; } | |
|
20 | ||
|
21 | object m_value; | |
|
22 | ||
|
23 | public object Value { | |
|
24 | get { | |
|
25 | if (!ValueSpecified) | |
|
26 | throw new InvalidOperationException("The regular value must be set (dependency or array are not situable in this context)"); | |
|
27 | return m_value; | |
|
28 | } | |
|
29 | } | |
|
30 | ||
|
31 | public bool ValueSpecified { get; private set; } | |
|
32 | ||
|
33 | InjectionParameterValue m_injection; | |
|
34 | ||
|
35 | public InjectionParameterValue Injection { | |
|
36 | get { | |
|
37 | if (m_injection == null) | |
|
38 | throw new InvalidOperationException("The injection parameter is not specified"); | |
|
39 | return m_injection; | |
|
40 | } | |
|
41 | } | |
|
42 | ||
|
43 | public bool InjectionSpecified { | |
|
44 | get { return m_injection != null; } | |
|
45 | } | |
|
46 | ||
|
47 | internal InjectionParameterBuilder(TypeResolver resolver, Type defaultType, ContainerBuilder root) { | |
|
48 | m_resolver = resolver; | |
|
49 | DefaultType = defaultType; | |
|
50 | Root = root; | |
|
51 | } | |
|
52 | ||
|
53 | public Type ResolveInjectedValueType(string typeSpec) { | |
|
54 | if (string.IsNullOrEmpty(typeSpec)) { | |
|
55 | if (DefaultType == null) | |
|
56 | throw new Exception("The type must be specified"); | |
|
57 | return DefaultType; | |
|
58 | } | |
|
59 | return m_resolver.Resolve(typeSpec, true); | |
|
60 | } | |
|
61 | ||
|
62 | public Type ResolveType(string typeSpec) { | |
|
63 | return string.IsNullOrEmpty(typeSpec) ? null : m_resolver.Resolve(typeSpec, true); | |
|
64 | } | |
|
65 | ||
|
66 | public void SetValue(Type type, object value) { | |
|
67 | Safe.ArgumentNotNull(type, nameof(type)); | |
|
68 | ||
|
69 | ValueType = type; | |
|
70 | m_value = value; | |
|
71 | ValueSpecified = true; | |
|
72 | ||
|
73 | m_injection = new InjectionParameter(type, value); | |
|
74 | } | |
|
75 | ||
|
76 | public void SetDependency(Type type, string name, bool optional) { | |
|
77 | Safe.ArgumentNotNull(type, nameof(type)); | |
|
78 | ||
|
79 | ValueType = type; | |
|
80 | ValueSpecified = false; | |
|
81 | m_value = null; | |
|
82 | ||
|
83 | m_injection = optional ? | |
|
84 | type.IsGenericParameter ? | |
|
85 | new OptionalGenericParameter(type.Name, name) | |
|
86 | : (InjectionParameterValue)new OptionalParameter(type, name) | |
|
87 | : new ResolvedParameter(type, name); | |
|
88 | } | |
|
89 | ||
|
90 | internal void Visit(ArrayParameterElement arrayParameter) { | |
|
91 | Type itemsType = null; | |
|
92 | var arrayType = string.IsNullOrEmpty(arrayParameter.TypeName) ? null : ResolveType(arrayParameter.TypeName); | |
|
93 | ||
|
94 | if (arrayType == null) | |
|
95 | arrayType = DefaultType; | |
|
96 | ||
|
97 | ||
|
98 | if (!string.IsNullOrEmpty(arrayParameter.ItemsType)) { | |
|
99 | itemsType = ResolveType(arrayParameter.ItemsType); | |
|
100 | arrayType = itemsType.MakeArrayType(); | |
|
101 | } else { | |
|
102 | itemsType = GetItemsType(arrayType); | |
|
103 | } | |
|
104 | ||
|
105 | if (itemsType == null) | |
|
106 | throw new Exception("Failed to determine array elements type"); | |
|
107 | ||
|
108 | InjectionParameterValue[] injections = (arrayParameter.Items ?? new AbstractInjectionParameter[0]) | |
|
109 | .Select(x => { | |
|
110 | var builder = new InjectionParameterBuilder(m_resolver, itemsType, Root); | |
|
111 | x.Visit(builder); | |
|
112 | return builder.Injection; | |
|
113 | }) | |
|
114 | .ToArray(); | |
|
115 | ||
|
116 | m_injection = itemsType.IsGenericParameter ? | |
|
117 | (InjectionParameterValue)new GenericResolvedArrayParameter(itemsType.Name, injections) : | |
|
118 | new ResolvedArrayParameter(itemsType, injections); | |
|
119 | ||
|
120 | ValueType = arrayType; | |
|
121 | m_value = null; | |
|
122 | ValueSpecified = false; | |
|
123 | } | |
|
124 | ||
|
125 | Type GetItemsType(Type collectionType) { | |
|
126 | if (collectionType == null) | |
|
127 | return null; | |
|
128 | ||
|
129 | Type itemsType = null; | |
|
130 | ||
|
131 | if (collectionType.IsGenericType && collectionType.GetGenericTypeDefinition() == typeof(IEnumerable<>)) { | |
|
132 | itemsType = collectionType.GetGenericArguments()[0]; | |
|
133 | } else if (collectionType == typeof(IEnumerable)) { | |
|
134 | itemsType = typeof(object); | |
|
135 | } else { | |
|
136 | itemsType = collectionType.GetInterface(typeof(IEnumerable<>).FullName)?.GetGenericArguments()[0]; | |
|
137 | } | |
|
138 | ||
|
139 | return itemsType; | |
|
140 | } | |
|
141 | } | |
|
142 | } No newline at end of file |
@@ -0,0 +1,13 | |||
|
1 | using System; | |
|
2 | ||
|
3 | namespace Implab.ServiceHost.Unity | |
|
4 | { | |
|
5 | public class InstanceRegistrationBuilder : RegistrationBuilder { | |
|
6 | ||
|
7 | public InjectionParameterBuilder ValueBuilder { get; private set; } | |
|
8 | ||
|
9 | internal InstanceRegistrationBuilder(TypeResolver typeResolver, Type registrationType, ContainerBuilder root) : base(registrationType, root) { | |
|
10 | ValueBuilder = new InjectionParameterBuilder(typeResolver, registrationType, root); | |
|
11 | } | |
|
12 | } | |
|
13 | } No newline at end of file |
@@ -0,0 +1,7 | |||
|
1 | using Unity.Lifetime; | |
|
2 | ||
|
3 | namespace Implab.ServiceHost.Unity { | |
|
4 | public abstract class LifetimeElement { | |
|
5 | public abstract LifetimeManager GetLifetime(ContainerBuilder builder); | |
|
6 | } | |
|
7 | } No newline at end of file |
@@ -0,0 +1,20 | |||
|
1 | using System.Xml.Serialization; | |
|
2 | ||
|
3 | namespace Implab.ServiceHost.Unity { | |
|
4 | public class MethodInjectionElement : AbstractMemberInjection { | |
|
5 | ||
|
6 | [XmlAttribute("name")] | |
|
7 | public string Name { get; set; } | |
|
8 | ||
|
9 | [XmlElement("dependency", typeof(DependencyParameterElement))] | |
|
10 | [XmlElement("value", typeof(ValueParameterElement))] | |
|
11 | [XmlElement("serialized", typeof(SerializedParameterElement))] | |
|
12 | [XmlElement("default", typeof(DefaultParameterElement))] | |
|
13 | [XmlElement("array", typeof(ArrayParameterElement))] | |
|
14 | public AbstractInjectionParameter[] Parameters { get; set; } | |
|
15 | ||
|
16 | public override void Visit(TypeRegistrationBuilder context) { | |
|
17 | context.Visit(this); | |
|
18 | } | |
|
19 | } | |
|
20 | } No newline at end of file |
@@ -0,0 +1,15 | |||
|
1 | using System.Xml.Serialization; | |
|
2 | ||
|
3 | namespace Implab.ServiceHost.Unity | |
|
4 | { | |
|
5 | [XmlRoot("namespace", Namespace = Schema.ContainerConfigurationNamespace)] | |
|
6 | public class NamespaceElement : AbstractContainerItem { | |
|
7 | ||
|
8 | [XmlAttribute("name")] | |
|
9 | public string Name { get; set; } | |
|
10 | ||
|
11 | public override void Visit(ContainerBuilder builder) { | |
|
12 | builder.AddNamespace(Name); | |
|
13 | } | |
|
14 | } | |
|
15 | } No newline at end of file |
@@ -0,0 +1,48 | |||
|
1 | using System; | |
|
2 | using System.Text; | |
|
3 | ||
|
4 | namespace Implab.ServiceHost.Unity { | |
|
5 | public class NestedTypeReference : TypeReference { | |
|
6 | ||
|
7 | readonly string m_name; | |
|
8 | ||
|
9 | readonly int m_genericParametersCount; | |
|
10 | ||
|
11 | public TypeReference DeclaringType { get; private set; } | |
|
12 | ||
|
13 | public override string Name { | |
|
14 | get { | |
|
15 | return m_name; | |
|
16 | } | |
|
17 | } | |
|
18 | ||
|
19 | public override string Namespace { | |
|
20 | get { | |
|
21 | return DeclaringType.Namespace; | |
|
22 | } | |
|
23 | } | |
|
24 | ||
|
25 | public override int GenericParametersCount { | |
|
26 | get { | |
|
27 | return m_genericParametersCount; | |
|
28 | } | |
|
29 | } | |
|
30 | ||
|
31 | internal NestedTypeReference(TypeReference declaringType, string name, int parametersCount) { | |
|
32 | DeclaringType = declaringType; | |
|
33 | m_name = name; | |
|
34 | m_genericParametersCount = parametersCount; | |
|
35 | } | |
|
36 | ||
|
37 | internal override void Visit(TypeResolutionContext visitor) { | |
|
38 | visitor.Visit(this); | |
|
39 | } | |
|
40 | ||
|
41 | internal override void WriteTypeName(StringBuilder builder) { | |
|
42 | builder | |
|
43 | .Append(DeclaringType) | |
|
44 | .Append('+') | |
|
45 | .Append(Name); | |
|
46 | } | |
|
47 | } | |
|
48 | } No newline at end of file |
@@ -0,0 +1,20 | |||
|
1 | using System.Xml.Serialization; | |
|
2 | ||
|
3 | namespace Implab.ServiceHost.Unity { | |
|
4 | public class PropertyInjectionElement : AbstractMemberInjection { | |
|
5 | ||
|
6 | [XmlAttribute("name")] | |
|
7 | public string Name { get; set; } | |
|
8 | ||
|
9 | [XmlElement("dependency", typeof(DependencyParameterElement))] | |
|
10 | [XmlElement("value", typeof(ValueParameterElement))] | |
|
11 | [XmlElement("serialized", typeof(SerializedParameterElement))] | |
|
12 | [XmlElement("default", typeof(DefaultParameterElement))] | |
|
13 | [XmlElement("array", typeof(ArrayParameterElement))] | |
|
14 | public AbstractInjectionParameter Value { get; set; } | |
|
15 | ||
|
16 | public override void Visit(TypeRegistrationBuilder context) { | |
|
17 | context.Visit(this); | |
|
18 | } | |
|
19 | } | |
|
20 | } No newline at end of file |
@@ -0,0 +1,17 | |||
|
1 | using System.Xml.Serialization; | |
|
2 | ||
|
3 | namespace Implab.ServiceHost.Unity { | |
|
4 | public class ProvidesElement { | |
|
5 | [XmlAttribute("type")] | |
|
6 | public string RegistrationType { get; set; } | |
|
7 | ||
|
8 | [XmlAttribute("name")] | |
|
9 | public string RegistrationName { get; set; } | |
|
10 | ||
|
11 | [XmlElement("signleton", typeof(SingletonLifetimeElement))] | |
|
12 | [XmlElement("context", typeof(ContextLifetimeElement))] | |
|
13 | [XmlElement("container", typeof(ContainerLifetimeElement))] | |
|
14 | [XmlElement("hierarchy", typeof(HierarchicalLifetimeElement))] | |
|
15 | public LifetimeElement Lifetime {get; set;} | |
|
16 | } | |
|
17 | } No newline at end of file |
@@ -0,0 +1,39 | |||
|
1 | using System; | |
|
2 | using System.Collections.Generic; | |
|
3 | using System.Xml.Serialization; | |
|
4 | using Unity.Lifetime; | |
|
5 | using Unity.Registration; | |
|
6 | ||
|
7 | namespace Implab.ServiceHost.Unity { | |
|
8 | ||
|
9 | [XmlRoot("register", Namespace = Schema.ContainerConfigurationNamespace)] | |
|
10 | public class RegisterElement : AbstractRegistration, ITypeRegistration { | |
|
11 | ||
|
12 | /// <summary> | |
|
13 | /// An optional type which is registered as a service in the container, must be assignable to <see cref="ProvidesType">. | |
|
14 | /// </summary> | |
|
15 | [XmlAttribute("mapTo")] | |
|
16 | public string MapToType { get; set; } | |
|
17 | ||
|
18 | ||
|
19 | [XmlElement("constructor", typeof(ConstructorInjectionElement))] | |
|
20 | [XmlElement("property", typeof(PropertyInjectionElement))] | |
|
21 | [XmlElement("method", typeof(MethodInjectionElement))] | |
|
22 | public AbstractMemberInjection[] Injectors { get; set; } | |
|
23 | ||
|
24 | IEnumerable<ITypeMemberInjection> ITypeRegistration.MemberInjections { | |
|
25 | get { | |
|
26 | return Injectors; | |
|
27 | } | |
|
28 | } | |
|
29 | ||
|
30 | public Type GetImplementationType(ContainerBuilder builder) { | |
|
31 | return builder.ResolveType(MapToType); | |
|
32 | } | |
|
33 | ||
|
34 | public override void Visit(ContainerBuilder builder) { | |
|
35 | builder.Visit(this); | |
|
36 | } | |
|
37 | } | |
|
38 | ||
|
39 | } No newline at end of file |
@@ -0,0 +1,32 | |||
|
1 | using System; | |
|
2 | using System.Collections.Generic; | |
|
3 | using System.ComponentModel; | |
|
4 | using System.Linq; | |
|
5 | using System.Xml.Serialization; | |
|
6 | using Implab.Xml; | |
|
7 | using Unity.Injection; | |
|
8 | using Unity.Lifetime; | |
|
9 | using Unity.Registration; | |
|
10 | ||
|
11 | namespace Implab.ServiceHost.Unity { | |
|
12 | /// <summary> | |
|
13 | /// Базовый класс для формирования записей в контейнере, созволяет указать время жизни для записи | |
|
14 | /// </summary> | |
|
15 | public abstract class RegistrationBuilder { | |
|
16 | public ContainerBuilder Root { | |
|
17 | get; private set; | |
|
18 | } | |
|
19 | ||
|
20 | public Type RegistrationType { | |
|
21 | get; | |
|
22 | private set; | |
|
23 | } | |
|
24 | ||
|
25 | internal LifetimeManager Lifetime { get; set; } | |
|
26 | ||
|
27 | protected RegistrationBuilder(Type registrationType, ContainerBuilder root) { | |
|
28 | Root = root; | |
|
29 | RegistrationType = registrationType; | |
|
30 | } | |
|
31 | } | |
|
32 | } No newline at end of file |
@@ -0,0 +1,36 | |||
|
1 | using System; | |
|
2 | ||
|
3 | namespace Implab.ServiceHost.Unity | |
|
4 | { | |
|
5 | public class RootTypeReference : TypeReference { | |
|
6 | readonly string m_name; | |
|
7 | ||
|
8 | readonly string m_namespace; | |
|
9 | ||
|
10 | readonly int m_genericParametersCount; | |
|
11 | ||
|
12 | public override string Name { | |
|
13 | get { return m_name; } | |
|
14 | } | |
|
15 | ||
|
16 | public override string Namespace { | |
|
17 | get { return m_namespace; } | |
|
18 | } | |
|
19 | ||
|
20 | public override int GenericParametersCount { | |
|
21 | get { return m_genericParametersCount; } | |
|
22 | } | |
|
23 | ||
|
24 | internal RootTypeReference(string ns, string name, int genericParameters) { | |
|
25 | m_name = name; | |
|
26 | m_genericParametersCount = genericParameters; | |
|
27 | m_namespace = ns; | |
|
28 | } | |
|
29 | ||
|
30 | internal override void Visit(TypeResolutionContext visitor) { | |
|
31 | visitor.Visit(this); | |
|
32 | } | |
|
33 | ||
|
34 | ||
|
35 | } | |
|
36 | } No newline at end of file |
@@ -0,0 +1,5 | |||
|
1 | namespace Implab.ServiceHost.Unity { | |
|
2 | public static class Schema { | |
|
3 | public const string ContainerConfigurationNamespace = "http://implab.org/schemas/servicehost/unity.v1.xsd"; | |
|
4 | } | |
|
5 | } No newline at end of file |
@@ -0,0 +1,32 | |||
|
1 | using System; | |
|
2 | using System.Collections.Generic; | |
|
3 | using System.Xml; | |
|
4 | using System.Xml.Serialization; | |
|
5 | ||
|
6 | namespace Implab.ServiceHost.Unity { | |
|
7 | public class SerializedElement : AbstractRegistration, IInstanceRegistration { | |
|
8 | [XmlAttribute("href")] | |
|
9 | public string Location { get; set; } | |
|
10 | ||
|
11 | [XmlAttribute("serializedType")] | |
|
12 | public string SerializedType { get; set; } | |
|
13 | ||
|
14 | ||
|
15 | [XmlAnyElement] | |
|
16 | public XmlElement[] Content { get; set; } | |
|
17 | ||
|
18 | public IEnumerable<IInjectionParameter> MemberInjections { | |
|
19 | get { | |
|
20 | yield return new SerializedParameterElement { | |
|
21 | TypeName = SerializedType, | |
|
22 | Location = Location, | |
|
23 | Content = Content | |
|
24 | }; | |
|
25 | } | |
|
26 | } | |
|
27 | ||
|
28 | public override void Visit(ContainerBuilder builder) { | |
|
29 | builder.Visit(this); | |
|
30 | } | |
|
31 | } | |
|
32 | } No newline at end of file |
@@ -0,0 +1,33 | |||
|
1 | using System; | |
|
2 | using System.Xml; | |
|
3 | using System.Xml.Schema; | |
|
4 | using System.Xml.Serialization; | |
|
5 | ||
|
6 | namespace Implab.ServiceHost.Unity | |
|
7 | { | |
|
8 | public class SerializedParameterElement : AbstractInjectionParameter { | |
|
9 | [XmlAttribute("href")] | |
|
10 | public string Location { get; set; } | |
|
11 | ||
|
12 | [XmlAnyElement] | |
|
13 | public XmlElement[] Content { get; set; } | |
|
14 | ||
|
15 | public XmlReader GetReader(InjectionParameterBuilder builder) { | |
|
16 | if (!string.IsNullOrEmpty(Location)) | |
|
17 | return XmlReader.Create(builder.Root.MakeLocationUri(Location).ToString()); | |
|
18 | if (Content != null && Content.Length > 0) | |
|
19 | return Content[0].CreateNavigator().ReadSubtree(); | |
|
20 | ||
|
21 | throw new Exception("No content found, expected XML document"); | |
|
22 | } | |
|
23 | ||
|
24 | public override void Visit(InjectionParameterBuilder builder) { | |
|
25 | var type = builder.ResolveInjectedValueType(TypeName); | |
|
26 | ||
|
27 | var serializer = new XmlSerializer(type); | |
|
28 | using(var reader = GetReader(builder)) | |
|
29 | builder.SetValue(type, serializer.Deserialize(reader)); | |
|
30 | ||
|
31 | } | |
|
32 | } | |
|
33 | } No newline at end of file |
@@ -0,0 +1,10 | |||
|
1 | using Unity.Lifetime; | |
|
2 | ||
|
3 | namespace Implab.ServiceHost.Unity | |
|
4 | { | |
|
5 | public class SingletonLifetimeElement : LifetimeElement { | |
|
6 | public override LifetimeManager GetLifetime(ContainerBuilder builder) { | |
|
7 | return new SingletonLifetimeManager(); | |
|
8 | } | |
|
9 | } | |
|
10 | } No newline at end of file |
@@ -0,0 +1,52 | |||
|
1 | using System; | |
|
2 | using System.Linq; | |
|
3 | using System.Text; | |
|
4 | ||
|
5 | namespace Implab.ServiceHost.Unity { | |
|
6 | public class SpecializedTypeReference : TypeReference { | |
|
7 | public override string Name { | |
|
8 | get { | |
|
9 | return GenericType.Name; | |
|
10 | } | |
|
11 | } | |
|
12 | ||
|
13 | public override string Namespace { | |
|
14 | get { | |
|
15 | return GenericType.Namespace; | |
|
16 | } | |
|
17 | } | |
|
18 | ||
|
19 | public override int GenericParametersCount { | |
|
20 | get { | |
|
21 | return GenericParameters.Length; | |
|
22 | } | |
|
23 | } | |
|
24 | ||
|
25 | public TypeReference GenericType { get; private set; } | |
|
26 | ||
|
27 | public TypeReference[] GenericParameters { get; private set; } | |
|
28 | ||
|
29 | internal SpecializedTypeReference(TypeReference genericType, TypeReference[] genericParameters) { | |
|
30 | GenericType = genericType; | |
|
31 | GenericParameters = genericParameters; | |
|
32 | } | |
|
33 | ||
|
34 | internal override void Visit(TypeResolutionContext visitor) { | |
|
35 | visitor.Visit(this); | |
|
36 | } | |
|
37 | ||
|
38 | internal override void WriteTypeName(StringBuilder builder) { | |
|
39 | GenericType.WriteTypeName(builder); | |
|
40 | } | |
|
41 | ||
|
42 | internal override void WriteTypeParams(StringBuilder builder) { | |
|
43 | builder.Append('{'); | |
|
44 | for (var i = 0; i < GenericParameters.Length; i++) { | |
|
45 | if (i > 0) | |
|
46 | builder.Append(','); | |
|
47 | builder.Append(GenericParameters[i]); | |
|
48 | } | |
|
49 | builder.Append('}'); | |
|
50 | } | |
|
51 | } | |
|
52 | } No newline at end of file |
@@ -0,0 +1,182 | |||
|
1 | using System; | |
|
2 | using System.Linq; | |
|
3 | using System.Text; | |
|
4 | ||
|
5 | namespace Implab.ServiceHost.Unity { | |
|
6 | /// <summary> | |
|
7 | /// Ссылка на тип, является абстрактной записью имени CLR типа. | |
|
8 | /// </summary> | |
|
9 | /// <remarks> | |
|
10 | /// Ссылка на тип содержит сокращенную информацию о типе и для ее интерпретации | |
|
11 | /// требуется некоторый контекст, который позволит превратить ее в полноценный | |
|
12 | /// <see cref="Type"/>. Ссылки на тип позволяют записать: | |
|
13 | /// <list> | |
|
14 | /// <item><description>общие типы, их специализации</description></item> | |
|
15 | /// <item><description>вложенные типы</description></item> | |
|
16 | /// <item><description>массивы</description></item> | |
|
17 | /// </list> | |
|
18 | /// <para> | |
|
19 | /// Для получения из ссылки на тип конкретного CLR типа используется <see cref="TypeResolver.Resolve(TypeReference, bool)"/>. | |
|
20 | /// </para> | |
|
21 | /// <para> | |
|
22 | /// Ссылку на тип можно создать либо програмно при помощи методов <see cref="Create(string, string, int)"/>, | |
|
23 | /// <see cref="Create(string, int)"/>, <see cref="MakeArrayType(int)"/>, <see cref="MakeGenericType(TypeReference[])"/>, | |
|
24 | /// либо разобрав строку со спецификацией при помощи метода <see cref="Parse(string)"/>. | |
|
25 | /// </para> | |
|
26 | /// <para> | |
|
27 | /// Спецификация ссыдки на тип имеет следующий вид <c>Name.Space.MyType+Nested{String}[][]</c>, где: | |
|
28 | /// <list type="table"> | |
|
29 | /// <item> | |
|
30 | /// <term><c>.</c></term> | |
|
31 | /// <description>Разделяет элементы пространства имен</description> | |
|
32 | /// <item> | |
|
33 | /// <item> | |
|
34 | /// <term><c>+</c></term> | |
|
35 | /// <description>Разделяет вложенные типы</description> | |
|
36 | /// <item> | |
|
37 | /// <item> | |
|
38 | /// <term><c>[]</c>, <c>[,,,]</c></term> | |
|
39 | /// <description>Указывает на то, что тип является массивом, также указывается его размерность</description> | |
|
40 | /// <item> | |
|
41 | /// <item> | |
|
42 | /// <term><c>{}</c>, <c>{,,}</c>, <c>{Int32,String}</c></term> | |
|
43 | /// <description>Указывает на то, что тип является общим, также | |
|
44 | /// указывается количество параметров, либо конкретные типы для | |
|
45 | /// специализации</description> | |
|
46 | /// <item> | |
|
47 | /// </list> | |
|
48 | /// </para> | |
|
49 | /// </remarks> | |
|
50 | public abstract class TypeReference { | |
|
51 | ||
|
52 | /// <summary> | |
|
53 | /// Имя типа без дополнительных элементов, указывающих на то, что он общий или массив. | |
|
54 | /// </summary> | |
|
55 | /// <remarks> | |
|
56 | /// Для массивов это имя его элементов. | |
|
57 | /// </remarks> | |
|
58 | public abstract string Name { get; } | |
|
59 | ||
|
60 | /// <summary> | |
|
61 | /// Пространство имен в котором нахожится тип. | |
|
62 | /// </summary> | |
|
63 | /// <remarks> | |
|
64 | /// Для вложенных типов это пространтство имен типа самого верхнего уровня, | |
|
65 | /// для массивов - пространство имен его элементов. | |
|
66 | /// </remarks> | |
|
67 | public abstract string Namespace { get; } | |
|
68 | ||
|
69 | /// <summary> | |
|
70 | /// Количество параметров общего типа. | |
|
71 | /// </summary> | |
|
72 | /// <remarks> | |
|
73 | /// <para> | |
|
74 | /// Вложенные типы неявно получают параметры от типов в которых они объявлены, | |
|
75 | /// данное свойство это не учитывает, возвращается только количество собственных | |
|
76 | /// параметров. | |
|
77 | /// </para> | |
|
78 | /// <para> | |
|
79 | /// Данное свойство используется для получения CRL имени типа. | |
|
80 | /// </para> | |
|
81 | /// </remarks> | |
|
82 | public abstract int GenericParametersCount { get; } | |
|
83 | ||
|
84 | public virtual string ClrName { | |
|
85 | get { | |
|
86 | return GenericParametersCount != 0 ? $"{Name}`{GenericParametersCount}" : Name; | |
|
87 | } | |
|
88 | } | |
|
89 | ||
|
90 | /// <summary> | |
|
91 | /// Создает ссылку на специализацию текущего типа. | |
|
92 | /// </summary> | |
|
93 | /// <param name="genericParameters">Ссылки на типы, которые будут использоваться для специализации текущего типа.</param> | |
|
94 | /// <returns>Специализация данного типа.</returns> | |
|
95 | public virtual SpecializedTypeReference MakeGenericType(TypeReference[] genericParameters) { | |
|
96 | if (GenericParametersCount == 0) | |
|
97 | throw new InvalidOperationException("Can't specialize a non-geneic type"); | |
|
98 | ||
|
99 | if (genericParameters == null || GenericParametersCount != genericParameters.Length) | |
|
100 | throw new InvalidOperationException("Generic parameters count mismatch"); | |
|
101 | ||
|
102 | return new SpecializedTypeReference(this, genericParameters); | |
|
103 | } | |
|
104 | ||
|
105 | /// <summary> | |
|
106 | /// Создает ссылку на тип массива указанной размерности, элементами которого являются экземпаляры даннго типа. | |
|
107 | /// </summary> | |
|
108 | /// <param name="rank">Размерность, если размерность <c>1</c> создается вектор (<see cref="Type.MakeArrayType()"/>).</param> | |
|
109 | /// <returns>Ссылка на тип массива</returns> | |
|
110 | public ArrayTypeReference MakeArrayType(int rank) { | |
|
111 | Safe.ArgumentInRange(rank > 0, nameof(rank)); | |
|
112 | ||
|
113 | return new ArrayTypeReference(this, rank); | |
|
114 | } | |
|
115 | ||
|
116 | /// <summary> | |
|
117 | /// Создает ссылку на вложенный тип. | |
|
118 | /// </summary> | |
|
119 | /// <param name="name">Имя типа</param> | |
|
120 | /// <param name="genericParameters">Количество параметров, если это общий тип, иначе 0.</param> | |
|
121 | /// <returns>Ссылка на вложенный тип.</returns> | |
|
122 | public TypeReference Create(string name, int genericParameters) { | |
|
123 | Safe.ArgumentNotEmpty(name, nameof(name)); | |
|
124 | Safe.ArgumentInRange(genericParameters >= 0, nameof(genericParameters)); | |
|
125 | ||
|
126 | return new NestedTypeReference(this, name, genericParameters); | |
|
127 | } | |
|
128 | ||
|
129 | /// <summary> | |
|
130 | /// Возвращает строковое представление ссылки на тип. | |
|
131 | /// </summary> | |
|
132 | /// <returns></returns> | |
|
133 | public override string ToString() { | |
|
134 | var builder = new StringBuilder(); | |
|
135 | WriteTypeName(builder); | |
|
136 | WriteTypeParams(builder); | |
|
137 | return builder.ToString(); | |
|
138 | } | |
|
139 | ||
|
140 | internal virtual void WriteTypeName(StringBuilder builder) { | |
|
141 | if (!string.IsNullOrEmpty(Namespace)) | |
|
142 | builder | |
|
143 | .Append(Namespace) | |
|
144 | .Append('.'); | |
|
145 | builder.Append(Name); | |
|
146 | } | |
|
147 | ||
|
148 | internal virtual void WriteTypeParams(StringBuilder builder) { | |
|
149 | if (GenericParametersCount > 0) | |
|
150 | builder | |
|
151 | .Append('{') | |
|
152 | .Append(',', GenericParametersCount-1) | |
|
153 | .Append('}'); | |
|
154 | } | |
|
155 | ||
|
156 | internal abstract void Visit(TypeResolutionContext visitor); | |
|
157 | ||
|
158 | /// <summary> | |
|
159 | /// Создает ссылку на тип. | |
|
160 | /// </summary> | |
|
161 | /// <param name="ns">Пространство имен, либо его фрагмент.</param> | |
|
162 | /// <param name="name">Имя типа без указания на количество параметров, либо на то, что это массив.</param> | |
|
163 | /// <param name="genericParameters">Количество параметров типа, если это общий тип, иначе 0.</param> | |
|
164 | /// <returns>Ссылка на тип.</returns> | |
|
165 | public static TypeReference Create(string ns, string name, int genericParameters) { | |
|
166 | Safe.ArgumentNotEmpty(name, nameof(name)); | |
|
167 | Safe.ArgumentInRange(genericParameters >= 0, nameof(genericParameters)); | |
|
168 | return new RootTypeReference(ns, name, genericParameters); | |
|
169 | } | |
|
170 | ||
|
171 | /// <summary> | |
|
172 | /// Разирает строковую запись ссылки на тип. | |
|
173 | /// </summary> | |
|
174 | /// <param name="typeSpec">Строковая запись ссылки на тип, например <c>Dictionary{String,String}</c></param> | |
|
175 | /// <returns>Ссылка на тип.</returns> | |
|
176 | public static TypeReference Parse(string typeSpec) { | |
|
177 | var parser = new TypeReferenceParser(typeSpec); | |
|
178 | return parser.Parse(); | |
|
179 | } | |
|
180 | ||
|
181 | } | |
|
182 | } No newline at end of file |
@@ -0,0 +1,245 | |||
|
1 | using System; | |
|
2 | using System.Collections.Generic; | |
|
3 | using System.Linq; | |
|
4 | using System.Text.RegularExpressions; | |
|
5 | ||
|
6 | namespace Implab.ServiceHost.Unity { | |
|
7 | internal class TypeReferenceParser { | |
|
8 | enum TokenType { | |
|
9 | None, | |
|
10 | ||
|
11 | Word, | |
|
12 | ||
|
13 | Dot, | |
|
14 | ||
|
15 | Comma, | |
|
16 | ||
|
17 | OpenList, | |
|
18 | ||
|
19 | CloseList, | |
|
20 | ||
|
21 | OpenArray, | |
|
22 | ||
|
23 | CloseArray, | |
|
24 | ||
|
25 | Plus, | |
|
26 | ||
|
27 | Eof | |
|
28 | } | |
|
29 | ||
|
30 | readonly Regex _tokens = new Regex(@"\G(?:([\w]+)|\s*([\+\.{},\[\]])\s*)", RegexOptions.Compiled); | |
|
31 | ||
|
32 | TokenType m_token; | |
|
33 | ||
|
34 | string m_tokenValue; | |
|
35 | ||
|
36 | int m_pos; | |
|
37 | ||
|
38 | int m_tokenPos; | |
|
39 | ||
|
40 | readonly string m_text; | |
|
41 | ||
|
42 | TokenType Token { get { return m_token; } } | |
|
43 | ||
|
44 | string TokenValue { get { return m_tokenValue; } } | |
|
45 | ||
|
46 | int TokenPos { get { return m_tokenPos; } } | |
|
47 | ||
|
48 | public TypeReferenceParser(string text) { | |
|
49 | Safe.ArgumentNotEmpty(text, nameof(text)); | |
|
50 | m_text = text; | |
|
51 | } | |
|
52 | ||
|
53 | bool ReadToken() { | |
|
54 | if (m_pos >= m_text.Length) { | |
|
55 | m_token = TokenType.Eof; | |
|
56 | m_tokenValue = null; | |
|
57 | return false; | |
|
58 | } | |
|
59 | ||
|
60 | var m = _tokens.Match(m_text, m_pos); | |
|
61 | ||
|
62 | if (m.Success) { | |
|
63 | m_tokenPos = m_pos; | |
|
64 | m_pos += m.Length; | |
|
65 | if (m.Groups[1].Success) { | |
|
66 | m_token = TokenType.Word; | |
|
67 | m_tokenValue = m.Groups[1].Value; | |
|
68 | } else if (m.Groups[2].Success) { | |
|
69 | m_tokenValue = null; | |
|
70 | switch (m.Groups[2].Value) { | |
|
71 | case "{": | |
|
72 | m_token = TokenType.OpenList; | |
|
73 | break; | |
|
74 | case "}": | |
|
75 | m_token = TokenType.CloseList; | |
|
76 | break; | |
|
77 | case ".": | |
|
78 | m_token = TokenType.Dot; | |
|
79 | break; | |
|
80 | case ",": | |
|
81 | m_token = TokenType.Comma; | |
|
82 | break; | |
|
83 | case "[": | |
|
84 | m_token = TokenType.OpenArray; | |
|
85 | break; | |
|
86 | case "]": | |
|
87 | m_token = TokenType.CloseArray; | |
|
88 | break; | |
|
89 | case "+": | |
|
90 | m_token = TokenType.Plus; | |
|
91 | break; | |
|
92 | } | |
|
93 | } | |
|
94 | return true; | |
|
95 | } | |
|
96 | throw new FormatException($"Failed to parse '{m_text}' at pos {m_pos}"); | |
|
97 | } | |
|
98 | ||
|
99 | public TypeReference Parse() { | |
|
100 | var result = ReadTypeReference(); | |
|
101 | if (Token != TokenType.Eof) | |
|
102 | ThrowUnexpectedToken(); | |
|
103 | return result; | |
|
104 | } | |
|
105 | ||
|
106 | string[] ReadQTypeName() { | |
|
107 | var parts = new List<string>(); | |
|
108 | ||
|
109 | string current = null; | |
|
110 | bool stop = false; | |
|
111 | while ((!stop) && ReadToken()) { | |
|
112 | switch (Token) { | |
|
113 | case TokenType.Word: | |
|
114 | if (current != null) | |
|
115 | ThrowUnexpectedToken(); | |
|
116 | current = TokenValue; | |
|
117 | break; | |
|
118 | case TokenType.Dot: | |
|
119 | if (current == null) | |
|
120 | ThrowUnexpectedToken(); | |
|
121 | parts.Add(current); | |
|
122 | current = null; | |
|
123 | break; | |
|
124 | default: | |
|
125 | stop = true; | |
|
126 | break; | |
|
127 | } | |
|
128 | } | |
|
129 | if (current != null) | |
|
130 | parts.Add(current); | |
|
131 | ||
|
132 | if (parts.Count == 0) | |
|
133 | return null; | |
|
134 | ||
|
135 | return parts.ToArray(); | |
|
136 | } | |
|
137 | ||
|
138 | string ReadNQTypeName() { | |
|
139 | ReadToken(); | |
|
140 | if (Token != TokenType.Word) | |
|
141 | ThrowUnexpectedToken(); | |
|
142 | return TokenValue; | |
|
143 | } | |
|
144 | ||
|
145 | TypeReference ReadTypeReference() { | |
|
146 | ||
|
147 | var parts = ReadQTypeName(); | |
|
148 | if (parts == null) | |
|
149 | return null; | |
|
150 | ||
|
151 | var genericParameters = ReadGenericParams(); | |
|
152 | ||
|
153 | var typeReference = TypeReference.Create( | |
|
154 | string.Join(".", parts, 0, parts.Length - 1), | |
|
155 | parts[parts.Length - 1], | |
|
156 | genericParameters.Length | |
|
157 | ); | |
|
158 | ||
|
159 | if (genericParameters.Length > 0 && genericParameters.All(x => x != null)) | |
|
160 | typeReference = typeReference.MakeGenericType(genericParameters); | |
|
161 | ||
|
162 | typeReference = ReadArraySpec(typeReference); | |
|
163 | ||
|
164 | if(Token == TokenType.Plus) | |
|
165 | return ReadNestedType(typeReference); | |
|
166 | ||
|
167 | return typeReference; | |
|
168 | } | |
|
169 | ||
|
170 | TypeReference ReadNestedType(TypeReference declaringType) { | |
|
171 | var name = ReadNQTypeName(); | |
|
172 | if(string.IsNullOrEmpty(name)) | |
|
173 | throw new FormatException("Nested type name can't be empty"); | |
|
174 | ReadToken(); | |
|
175 | ||
|
176 | var genericParameters = ReadGenericParams(); | |
|
177 | ||
|
178 | var typeReference = declaringType.Create( | |
|
179 | name, | |
|
180 | genericParameters.Length | |
|
181 | ); | |
|
182 | ||
|
183 | if (genericParameters.Length > 0 && genericParameters.All(x => x != null)) | |
|
184 | typeReference = typeReference.MakeGenericType(genericParameters); | |
|
185 | ||
|
186 | typeReference = ReadArraySpec(typeReference); | |
|
187 | ||
|
188 | if(Token == TokenType.Plus) | |
|
189 | return ReadNestedType(typeReference); | |
|
190 | ||
|
191 | return typeReference; | |
|
192 | } | |
|
193 | ||
|
194 | TypeReference[] ReadGenericParams() { | |
|
195 | if (Token == TokenType.OpenList) { | |
|
196 | var genericParameters = ReadTypeReferenceList(); | |
|
197 | if (Token != TokenType.CloseList) | |
|
198 | ThrowUnexpectedToken(); | |
|
199 | ReadToken(); | |
|
200 | ||
|
201 | return genericParameters; | |
|
202 | } | |
|
203 | ||
|
204 | return Array.Empty<TypeReference>(); | |
|
205 | } | |
|
206 | ||
|
207 | TypeReference ReadArraySpec(TypeReference typeReference) { | |
|
208 | while (Token == TokenType.OpenArray) { | |
|
209 | var rank = CountRank(); | |
|
210 | if (Token != TokenType.CloseArray) | |
|
211 | ThrowUnexpectedToken(); | |
|
212 | ||
|
213 | typeReference = typeReference.MakeArrayType(rank); | |
|
214 | ||
|
215 | ReadToken(); | |
|
216 | } | |
|
217 | ||
|
218 | return typeReference; | |
|
219 | } | |
|
220 | ||
|
221 | int CountRank() { | |
|
222 | int rank = 0; | |
|
223 | do { | |
|
224 | rank++; | |
|
225 | } while(ReadToken() && Token == TokenType.Comma); | |
|
226 | return rank; | |
|
227 | } | |
|
228 | ||
|
229 | TypeReference[] ReadTypeReferenceList() { | |
|
230 | var list = new List<TypeReference>(); | |
|
231 | ||
|
232 | do { | |
|
233 | var typeReference = ReadTypeReference(); | |
|
234 | list.Add(typeReference); | |
|
235 | } while (Token == TokenType.Comma); | |
|
236 | ||
|
237 | return list.ToArray(); | |
|
238 | } | |
|
239 | ||
|
240 | void ThrowUnexpectedToken() { | |
|
241 | throw new FormatException($"Unexpected '{Token}' at pos {TokenPos}: -->{m_text.Substring(TokenPos, Math.Min(m_text.Length - TokenPos, 10))}"); | |
|
242 | } | |
|
243 | ||
|
244 | } | |
|
245 | } No newline at end of file |
@@ -0,0 +1,81 | |||
|
1 | using System; | |
|
2 | using System.Collections.Generic; | |
|
3 | using System.Linq; | |
|
4 | using Unity.Injection; | |
|
5 | using Unity.Registration; | |
|
6 | ||
|
7 | namespace Implab.ServiceHost.Unity { | |
|
8 | public class TypeRegistrationBuilder : RegistrationBuilder { | |
|
9 | ||
|
10 | readonly TypeResolver m_resolver; | |
|
11 | ||
|
12 | readonly List<InjectionMember> m_injections = new List<InjectionMember>(); | |
|
13 | ||
|
14 | internal InjectionMember[] Injections { get { return m_injections.ToArray(); } } | |
|
15 | ||
|
16 | public Type ImplementationType { | |
|
17 | get; | |
|
18 | private set; | |
|
19 | } | |
|
20 | ||
|
21 | internal TypeRegistrationBuilder(TypeResolver resolver, Type registrationType, Type implementationType, ContainerBuilder root) : base(registrationType, root) { | |
|
22 | ImplementationType = implementationType; | |
|
23 | ||
|
24 | // when registering a generic mapping, register all generic parameter names as local types | |
|
25 | if (ImplementationType.IsGenericTypeDefinition) { | |
|
26 | m_resolver = new TypeResolver(resolver); | |
|
27 | ||
|
28 | foreach (var p in ImplementationType.GetGenericArguments()) | |
|
29 | m_resolver.AddMapping(p.Name, p); | |
|
30 | } else { | |
|
31 | m_resolver = resolver; | |
|
32 | } | |
|
33 | } | |
|
34 | ||
|
35 | internal void Visit(ConstructorInjectionElement constructorInjection) { | |
|
36 | ||
|
37 | ||
|
38 | var parameters = constructorInjection.Parameters? | |
|
39 | .Select(x => { | |
|
40 | var valueBuilder = new InjectionParameterBuilder(m_resolver, null, Root); | |
|
41 | x.Visit(valueBuilder); | |
|
42 | return valueBuilder.Injection; | |
|
43 | }) | |
|
44 | .ToArray(); | |
|
45 | ||
|
46 | var injection = parameters != null ? new InjectionConstructor(parameters) : new InjectionConstructor(); | |
|
47 | m_injections.Add(injection); | |
|
48 | } | |
|
49 | ||
|
50 | internal void Visit(MethodInjectionElement methodInjection) { | |
|
51 | var parameters = methodInjection.Parameters? | |
|
52 | .Select(x => { | |
|
53 | var valueBuilder = new InjectionParameterBuilder(m_resolver, null, Root); | |
|
54 | x.Visit(valueBuilder); | |
|
55 | return valueBuilder.Injection; | |
|
56 | }) | |
|
57 | .ToArray(); | |
|
58 | ||
|
59 | var injection = parameters != null ? new InjectionMethod(methodInjection.Name, parameters) : new InjectionMethod(methodInjection.Name); | |
|
60 | m_injections.Add(injection); | |
|
61 | } | |
|
62 | ||
|
63 | internal void Visit(PropertyInjectionElement propertyInjection) { | |
|
64 | if (propertyInjection.Value == null) | |
|
65 | throw new Exception($"A value value must be specified for the property '{propertyInjection.Name}'"); | |
|
66 | ||
|
67 | var propertyType = ImplementationType.GetProperty(propertyInjection.Name)?.PropertyType; | |
|
68 | var valueContext = new InjectionParameterBuilder(m_resolver, propertyType, Root); | |
|
69 | ||
|
70 | propertyInjection.Value.Visit(valueContext); | |
|
71 | var injection = new InjectionProperty(propertyInjection.Name, valueContext.Injection); | |
|
72 | m_injections.Add(injection); | |
|
73 | } | |
|
74 | ||
|
75 | public void AddInjectionMember(InjectionMember injection) { | |
|
76 | Safe.ArgumentNotNull(injection, nameof(injection)); | |
|
77 | ||
|
78 | m_injections.Add(injection); | |
|
79 | } | |
|
80 | } | |
|
81 | } No newline at end of file |
@@ -0,0 +1,75 | |||
|
1 | using System; | |
|
2 | using System.Collections.Generic; | |
|
3 | using Implab.Diagnostics; | |
|
4 | ||
|
5 | namespace Implab.ServiceHost.Unity { | |
|
6 | using static Trace<TypeResolver>; | |
|
7 | ||
|
8 | /// <summary> | |
|
9 | /// Позволяет обойти вложенные типы и собрать цеочку из типов и параметров генериков, которые они предлагают | |
|
10 | /// </summary> | |
|
11 | internal class TypeResolutionContext { | |
|
12 | readonly TypeResolver m_resolver; | |
|
13 | ||
|
14 | ||
|
15 | readonly List<Type> m_genericParameters = new List<Type>(); | |
|
16 | ||
|
17 | public IEnumerable<Type> GenericParameters { get { return m_genericParameters; } } | |
|
18 | ||
|
19 | public Type ResolvedType { get; private set; } | |
|
20 | ||
|
21 | ||
|
22 | public int ArrayRank { get; private set; } | |
|
23 | ||
|
24 | ||
|
25 | public bool ThrowOnFail { get; private set; } | |
|
26 | ||
|
27 | public TypeResolutionContext(TypeResolver resolver, bool throwOnFail) { | |
|
28 | m_resolver = resolver; | |
|
29 | ThrowOnFail = throwOnFail; | |
|
30 | } | |
|
31 | ||
|
32 | public Type MakeType() { | |
|
33 | return m_genericParameters.Count > 0 ? | |
|
34 | ResolvedType?.MakeGenericType(m_genericParameters.ToArray()) : | |
|
35 | ResolvedType; | |
|
36 | } | |
|
37 | ||
|
38 | public void Visit(SpecializedTypeReference typeReference) { | |
|
39 | typeReference.GenericType.Visit(this); | |
|
40 | ||
|
41 | if (ResolvedType != null) { | |
|
42 | foreach (var genericParamRef in typeReference.GenericParameters) { | |
|
43 | var context = new TypeResolutionContext(m_resolver, ThrowOnFail); | |
|
44 | genericParamRef.Visit(context); | |
|
45 | m_genericParameters.Add(context.MakeType()); | |
|
46 | } | |
|
47 | } | |
|
48 | } | |
|
49 | ||
|
50 | public void Visit(NestedTypeReference typeReference) { | |
|
51 | typeReference.DeclaringType.Visit(this); | |
|
52 | if (ResolvedType != null) | |
|
53 | ResolvedType = ResolvedType?.GetNestedType(typeReference.ClrName) ?? Failed(typeReference); | |
|
54 | } | |
|
55 | ||
|
56 | public void Visit(RootTypeReference typeReference) { | |
|
57 | ResolvedType = m_resolver.Resolve(typeReference.Namespace, typeReference.ClrName) ?? Failed(typeReference); | |
|
58 | } | |
|
59 | ||
|
60 | public void Visit(ArrayTypeReference typeReference) { | |
|
61 | var context = new TypeResolutionContext(m_resolver, ThrowOnFail); | |
|
62 | typeReference.ItemsType.Visit(context); | |
|
63 | ResolvedType = typeReference.Rank == 1 ? | |
|
64 | context.MakeType()?.MakeArrayType() : | |
|
65 | context.MakeType()?.MakeArrayType(typeReference.Rank); | |
|
66 | } | |
|
67 | ||
|
68 | Type Failed(TypeReference reference) { | |
|
69 | Log($"Falied to resolve {reference}"); | |
|
70 | if (ThrowOnFail) | |
|
71 | throw new Exception($"Failed to resolve {reference}"); | |
|
72 | return null; | |
|
73 | } | |
|
74 | } | |
|
75 | } No newline at end of file |
@@ -0,0 +1,99 | |||
|
1 | using System; | |
|
2 | using System.Collections.Generic; | |
|
3 | using System.Linq; | |
|
4 | using System.Text; | |
|
5 | using System.Text.RegularExpressions; | |
|
6 | using Implab.Diagnostics; | |
|
7 | ||
|
8 | namespace Implab.ServiceHost.Unity { | |
|
9 | using System.Diagnostics; | |
|
10 | using static Trace<TypeResolver>; | |
|
11 | public class TypeResolver { | |
|
12 | readonly Dictionary<string, Type> m_cache = new Dictionary<string, Type>(); | |
|
13 | ||
|
14 | Regex _nsRx = new Regex(@"^\w+(\.\w+)*$", RegexOptions.Compiled); | |
|
15 | readonly LinkedList<string> m_namespases = new LinkedList<string>(); | |
|
16 | ||
|
17 | internal Type Resolve(string ns, string typeName) { | |
|
18 | var fullName = string.IsNullOrEmpty(ns) ? typeName : $"{ns}.{typeName}"; | |
|
19 | ||
|
20 | return ProbeInNamespaces(fullName); | |
|
21 | } | |
|
22 | ||
|
23 | public Type Resolve(TypeReference typeReference, bool throwOnFail) { | |
|
24 | var context = new TypeResolutionContext(this, throwOnFail); | |
|
25 | typeReference.Visit(context); | |
|
26 | return context.MakeType(); | |
|
27 | } | |
|
28 | ||
|
29 | public Type Resolve(string typeSpec, bool throwOnFail) { | |
|
30 | Safe.ArgumentNotEmpty(typeSpec, nameof(typeSpec)); | |
|
31 | var typeReference = TypeReference.Parse(typeSpec); | |
|
32 | return Resolve(typeReference, throwOnFail); | |
|
33 | } | |
|
34 | ||
|
35 | LinkedListNode<string> m_insertAt; | |
|
36 | ||
|
37 | readonly TypeResolver m_parent; | |
|
38 | ||
|
39 | public TypeResolver() : this(null) { | |
|
40 | } | |
|
41 | ||
|
42 | public TypeResolver(TypeResolver parent) { | |
|
43 | m_parent = parent; | |
|
44 | m_insertAt = new LinkedListNode<string>(string.Empty); | |
|
45 | m_namespases.AddFirst(m_insertAt); | |
|
46 | } | |
|
47 | ||
|
48 | public void AddNamespace(string ns) { | |
|
49 | Safe.ArgumentMatch(ns, nameof(ns), _nsRx); | |
|
50 | if (m_insertAt != null) | |
|
51 | m_namespases.AddAfter(m_insertAt, ns); | |
|
52 | else | |
|
53 | m_namespases.AddFirst(ns); | |
|
54 | } | |
|
55 | ||
|
56 | public void AddMapping(string typeName, Type type) { | |
|
57 | Safe.ArgumentNotEmpty(typeName, nameof(typeName)); | |
|
58 | Safe.ArgumentNotNull(type, nameof(type)); | |
|
59 | ||
|
60 | m_cache[typeName] = type; | |
|
61 | } | |
|
62 | ||
|
63 | Type ProbeInNamespaces(string localName) { | |
|
64 | ||
|
65 | Type resolved; | |
|
66 | if (!m_cache.TryGetValue(localName, out resolved)) { | |
|
67 | foreach (var ns in m_namespases) { | |
|
68 | var typeName = string.IsNullOrEmpty(ns) ? localName : $"{ns}.{localName}"; | |
|
69 | resolved = Probe(typeName); | |
|
70 | if (resolved != null) { | |
|
71 | Log($"Probe '{localName}' -> '{resolved.FullName}'"); | |
|
72 | break; | |
|
73 | } | |
|
74 | } | |
|
75 | ||
|
76 | if (resolved == null && m_parent != null) | |
|
77 | resolved = m_parent.ProbeInNamespaces(localName); | |
|
78 | ||
|
79 | if(resolved == null) | |
|
80 | Log($"Probe '{localName}' failed"); | |
|
81 | ||
|
82 | m_cache[localName] = resolved; | |
|
83 | } | |
|
84 | ||
|
85 | return resolved; | |
|
86 | } | |
|
87 | ||
|
88 | Type Probe(string typeName) { | |
|
89 | var assemblies = AppDomain.CurrentDomain.GetAssemblies(); | |
|
90 | ||
|
91 | foreach (var assembly in assemblies) { | |
|
92 | var type = assembly.GetType(typeName); | |
|
93 | if (type != null) | |
|
94 | return type; | |
|
95 | } | |
|
96 | return null; | |
|
97 | } | |
|
98 | } | |
|
99 | } No newline at end of file |
@@ -0,0 +1,40 | |||
|
1 | using System.IO; | |
|
2 | using Unity; | |
|
3 | ||
|
4 | namespace Implab.ServiceHost.Unity | |
|
5 | { | |
|
6 | public static class UnityContainerExtensions | |
|
7 | { | |
|
8 | public static IUnityContainer LoadXmlConfiguration(this IUnityContainer container, string file, ContainerConfigurationSchema schema) { | |
|
9 | Safe.ArgumentNotNull(container, nameof(container)); | |
|
10 | var builder = new ContainerBuilder(container,schema); | |
|
11 | builder.LoadConfig(file); | |
|
12 | return builder.Container; | |
|
13 | } | |
|
14 | ||
|
15 | public static IUnityContainer LoadXmlConfiguration(this IUnityContainer container, Stream stream, ContainerConfigurationSchema schema) { | |
|
16 | Safe.ArgumentNotNull(container, nameof(container)); | |
|
17 | Safe.ArgumentNotNull(stream, nameof(stream)); | |
|
18 | ||
|
19 | if (schema == null) | |
|
20 | schema = ContainerConfigurationSchema.Default; | |
|
21 | ||
|
22 | var builder = new ContainerBuilder(container,schema); | |
|
23 | var config = (ContainerElement)schema.Serializer.Deserialize(stream); | |
|
24 | if (config.Items != null) { | |
|
25 | foreach(var item in config.Items) | |
|
26 | item?.Visit(builder); | |
|
27 | } | |
|
28 | ||
|
29 | return builder.Container; | |
|
30 | } | |
|
31 | ||
|
32 | public static IUnityContainer LoadXmlConfiguration(this IUnityContainer container, Stream stream) { | |
|
33 | return LoadXmlConfiguration(container, stream, ContainerConfigurationSchema.Default); | |
|
34 | } | |
|
35 | ||
|
36 | public static IUnityContainer LoadXmlConfiguration(this IUnityContainer container, string file) { | |
|
37 | return LoadXmlConfiguration(container, file, ContainerConfigurationSchema.Default); | |
|
38 | } | |
|
39 | } | |
|
40 | } No newline at end of file |
@@ -0,0 +1,36 | |||
|
1 | using System.Collections.Generic; | |
|
2 | using System.Xml.Serialization; | |
|
3 | ||
|
4 | namespace Implab.ServiceHost.Unity { | |
|
5 | public class ValueElement : AbstractRegistration, IInstanceRegistration { | |
|
6 | ||
|
7 | [XmlAttribute("value")] | |
|
8 | public string Value { get; set; } | |
|
9 | ||
|
10 | [XmlText] | |
|
11 | public string Text { get; set; } | |
|
12 | ||
|
13 | string GetTextValue() { | |
|
14 | return string.IsNullOrEmpty(Value) ? Text : Value; | |
|
15 | } | |
|
16 | ||
|
17 | public string TypeName { | |
|
18 | get { | |
|
19 | return RegistrationType; | |
|
20 | } | |
|
21 | } | |
|
22 | ||
|
23 | public IEnumerable<IInjectionParameter> MemberInjections { | |
|
24 | get { | |
|
25 | yield return new ValueParameterElement { | |
|
26 | Value = Value, | |
|
27 | Text = Text | |
|
28 | }; | |
|
29 | } | |
|
30 | } | |
|
31 | ||
|
32 | public override void Visit(ContainerBuilder builder) { | |
|
33 | builder.Visit(this); | |
|
34 | } | |
|
35 | } | |
|
36 | } No newline at end of file |
@@ -0,0 +1,21 | |||
|
1 | using System.ComponentModel; | |
|
2 | using System.Xml.Serialization; | |
|
3 | ||
|
4 | namespace Implab.ServiceHost.Unity { | |
|
5 | public class ValueParameterElement : AbstractInjectionParameter { | |
|
6 | [XmlAttribute("value")] | |
|
7 | public string Value { get; set; } | |
|
8 | ||
|
9 | [XmlText] | |
|
10 | public string Text { get; set; } | |
|
11 | ||
|
12 | string GetTextValue() { | |
|
13 | return string.IsNullOrEmpty(Value) ? Text : Value; | |
|
14 | } | |
|
15 | ||
|
16 | public override void Visit(InjectionParameterBuilder builder) { | |
|
17 | var type = builder.ResolveInjectedValueType(TypeName); | |
|
18 | builder.SetValue(type, TypeDescriptor.GetConverter(type).ConvertFromString(GetTextValue())); | |
|
19 | } | |
|
20 | } | |
|
21 | } No newline at end of file |
@@ -0,0 +1,34 | |||
|
1 | using System; | |
|
2 | using System.Threading; | |
|
3 | using System.Threading.Tasks; | |
|
4 | using Implab.Components; | |
|
5 | using Implab.Diagnostics; | |
|
6 | using Xunit; | |
|
7 | ||
|
8 | namespace Implab.Test { | |
|
9 | ||
|
10 | public class DiagnosticsTest { | |
|
11 | class Foo {} | |
|
12 | ||
|
13 | [Fact] | |
|
14 | public void TestRegistration() { | |
|
15 | var channel = TraceSourceChannel<Foo>.Default; | |
|
16 | ||
|
17 | Assert.Equal(typeof(Foo), channel.Id); | |
|
18 | Assert.Equal(typeof(Foo).FullName, channel.Source.Name); | |
|
19 | ||
|
20 | TraceSourceChannel found = null; | |
|
21 | int visited = 0; | |
|
22 | ||
|
23 | TraceRegistry.Global.Subscribe(x => { | |
|
24 | visited++; | |
|
25 | found = x as TraceSourceChannel; | |
|
26 | }); | |
|
27 | ||
|
28 | Assert.Equal(1,visited); | |
|
29 | Assert.Equal(channel, found); | |
|
30 | ||
|
31 | } | |
|
32 | ||
|
33 | } | |
|
34 | } No newline at end of file |
@@ -0,0 +1,37 | |||
|
1 | using System; | |
|
2 | using System.Threading; | |
|
3 | using System.Threading.Tasks; | |
|
4 | using Implab.Components; | |
|
5 | ||
|
6 | namespace Implab.Test { | |
|
7 | public class MockPollComponent : PollingComponent { | |
|
8 | ||
|
9 | public Func<CancellationToken,Task> PollWorker { get; set;} | |
|
10 | ||
|
11 | public Func<CancellationToken, Task> StartWorker { get; set; } | |
|
12 | ||
|
13 | public Func<CancellationToken, Task> StopWorker { get; set; } | |
|
14 | ||
|
15 | public MockPollComponent(bool initialized) : base(initialized) { | |
|
16 | } | |
|
17 | ||
|
18 | protected async override Task Poll(CancellationToken ct) { | |
|
19 | if(PollWorker!= null) | |
|
20 | await PollWorker.Invoke(ct); | |
|
21 | } | |
|
22 | ||
|
23 | protected async override Task StopInternalAsync(CancellationToken ct) { | |
|
24 | await base.StopInternalAsync(ct); | |
|
25 | if (StopWorker != null) | |
|
26 | await StopWorker.Invoke(ct); | |
|
27 | } | |
|
28 | ||
|
29 | protected async override Task StartInternalAsync(CancellationToken ct) { | |
|
30 | await base.StartInternalAsync(ct); | |
|
31 | if (StartWorker != null) | |
|
32 | await StartWorker.Invoke(ct); | |
|
33 | } | |
|
34 | ||
|
35 | ||
|
36 | } | |
|
37 | } No newline at end of file |
@@ -0,0 +1,20 | |||
|
1 | using System.Xml.Serialization; | |
|
2 | ||
|
3 | namespace Implab.Test.Model { | |
|
4 | ||
|
5 | [XmlRoot(Namespace="urn:implab:test:model")] | |
|
6 | public class Person { | |
|
7 | public string FirstName { get; set; } | |
|
8 | ||
|
9 | public string LastName { get; set; } | |
|
10 | ||
|
11 | public int Age { get; set; } | |
|
12 | ||
|
13 | [XmlIgnore] | |
|
14 | public bool AgeSpecified { get; set; } | |
|
15 | ||
|
16 | ||
|
17 | [XmlElement("Tag")] | |
|
18 | public string[] Tags { get; set; } | |
|
19 | } | |
|
20 | } No newline at end of file |
@@ -0,0 +1,264 | |||
|
1 | using System; | |
|
2 | using System.Threading; | |
|
3 | using System.Threading.Tasks; | |
|
4 | using Implab.Components; | |
|
5 | using Xunit; | |
|
6 | ||
|
7 | namespace Implab.Test { | |
|
8 | ||
|
9 | public class RunnableComponentTests { | |
|
10 | [Fact] | |
|
11 | public async Task SimpleStartStop() { | |
|
12 | ||
|
13 | using (var m = new MockPollComponent(true)) { | |
|
14 | m.StartWorker = async (ct) => await Task.Yield(); | |
|
15 | m.StopWorker = async (ct) => await Task.Yield(); | |
|
16 | ||
|
17 | Assert.Equal(ExecutionState.Ready, m.State); | |
|
18 | Assert.NotNull(m.Completion); | |
|
19 | ||
|
20 | m.Start(); | |
|
21 | await m.Completion; | |
|
22 | Assert.Equal(ExecutionState.Running, m.State); | |
|
23 | ||
|
24 | m.Stop(); | |
|
25 | await m.Completion; | |
|
26 | Assert.Equal(ExecutionState.Stopped, m.State); | |
|
27 | } | |
|
28 | } | |
|
29 | ||
|
30 | [Fact] | |
|
31 | public async Task SyncStart() { | |
|
32 | using (var m = new MockPollComponent(true)) { | |
|
33 | m.Start(); | |
|
34 | Assert.Equal(ExecutionState.Running, m.State); | |
|
35 | await m.Completion; | |
|
36 | } | |
|
37 | } | |
|
38 | ||
|
39 | [Fact] | |
|
40 | public async Task AsyncStarting() { | |
|
41 | using (var m = new MockPollComponent(true)) { | |
|
42 | var signal = Safe.CreateTask(); | |
|
43 | ||
|
44 | m.StartWorker = async (ct) => await signal; | |
|
45 | m.Start(); | |
|
46 | ||
|
47 | Assert.Equal(ExecutionState.Starting, m.State); | |
|
48 | Assert.False(m.Completion.IsCompleted); | |
|
49 | ||
|
50 | signal.Start(); | |
|
51 | ||
|
52 | await m.Completion; | |
|
53 | ||
|
54 | Assert.Equal(ExecutionState.Running, m.State); | |
|
55 | } | |
|
56 | } | |
|
57 | ||
|
58 | [Fact] | |
|
59 | public async Task FailWhileStarting() { | |
|
60 | using (var m = new MockPollComponent(true)) { | |
|
61 | const string failMessage = "Fail me"; | |
|
62 | var signal = new Task(() => { | |
|
63 | throw new Exception(failMessage); | |
|
64 | }); | |
|
65 | ||
|
66 | m.StartWorker = async (ct) => await signal; | |
|
67 | m.Start(); | |
|
68 | ||
|
69 | Assert.Equal(ExecutionState.Starting, m.State); | |
|
70 | Assert.False(m.Completion.IsCompleted); | |
|
71 | ||
|
72 | signal.Start(); | |
|
73 | try { | |
|
74 | await m.Completion; | |
|
75 | Assert.True(false); | |
|
76 | } catch (Exception e) { | |
|
77 | Assert.Equal(failMessage, e.Message); | |
|
78 | } | |
|
79 | ||
|
80 | Assert.Equal(ExecutionState.Failed, m.State); | |
|
81 | } | |
|
82 | } | |
|
83 | ||
|
84 | [Fact] | |
|
85 | public async Task SyncStop() { | |
|
86 | using (var m = new MockPollComponent(true)) { | |
|
87 | m.Start(); | |
|
88 | Assert.Equal(ExecutionState.Running, m.State); | |
|
89 | m.Stop(); | |
|
90 | Assert.Equal(ExecutionState.Stopped, m.State); | |
|
91 | await m.Completion; | |
|
92 | } | |
|
93 | } | |
|
94 | ||
|
95 | [Fact] | |
|
96 | public async Task AsyncStopping() { | |
|
97 | using (var m = new MockPollComponent(true)) { | |
|
98 | var signal = Safe.CreateTask(); | |
|
99 | ||
|
100 | m.StopWorker = async (ct) => await signal; | |
|
101 | ||
|
102 | // Start | |
|
103 | m.Start(); | |
|
104 | Assert.Equal(ExecutionState.Running, m.State); | |
|
105 | ||
|
106 | // Stop | |
|
107 | m.Stop(); | |
|
108 | Assert.Equal(ExecutionState.Stopping, m.State); | |
|
109 | Assert.False(m.Completion.IsCompleted); | |
|
110 | signal.Start(); | |
|
111 | ||
|
112 | await m.Completion; | |
|
113 | ||
|
114 | Assert.Equal(ExecutionState.Stopped, m.State); | |
|
115 | } | |
|
116 | } | |
|
117 | ||
|
118 | [Fact] | |
|
119 | public async Task FailWhileStopping() { | |
|
120 | using (var m = new MockPollComponent(true)) { | |
|
121 | const string failMessage = "Fail me"; | |
|
122 | var signal = new Task(() => { | |
|
123 | throw new Exception(failMessage); | |
|
124 | }); | |
|
125 | ||
|
126 | m.StopWorker = async (ct) => await signal; | |
|
127 | ||
|
128 | // Start | |
|
129 | m.Start(); | |
|
130 | Assert.Equal(ExecutionState.Running, m.State); | |
|
131 | ||
|
132 | // Stop | |
|
133 | m.Stop(); | |
|
134 | Assert.Equal(ExecutionState.Stopping, m.State); | |
|
135 | Assert.False(m.Completion.IsCompleted); | |
|
136 | ||
|
137 | signal.Start(); | |
|
138 | try { | |
|
139 | await m.Completion; | |
|
140 | Assert.True(false); | |
|
141 | } catch (Exception e) { | |
|
142 | Assert.Equal(failMessage, e.Message); | |
|
143 | } | |
|
144 | ||
|
145 | Assert.Equal(ExecutionState.Failed, m.State); | |
|
146 | } | |
|
147 | } | |
|
148 | ||
|
149 | [Fact] | |
|
150 | public async Task ThrowOnInvalidTrasition() { | |
|
151 | using (var m = new MockPollComponent(false)) { | |
|
152 | var started = Safe.CreateTask(); | |
|
153 | var stopped = Safe.CreateTask(); | |
|
154 | ||
|
155 | m.StartWorker = async (ct) => await started; | |
|
156 | m.StopWorker = async (ct) => await stopped; | |
|
157 | ||
|
158 | Assert.Throws<InvalidOperationException>(() => m.Start()); | |
|
159 | ||
|
160 | // Initialize | |
|
161 | m.Initialize(); | |
|
162 | await m.Completion; | |
|
163 | ||
|
164 | // Start | |
|
165 | m.Start(); | |
|
166 | Assert.Equal(ExecutionState.Starting, m.State); | |
|
167 | ||
|
168 | // Check invalid transitions | |
|
169 | Assert.Throws<InvalidOperationException>(() => m.Start()); | |
|
170 | ||
|
171 | // Component can be stopped before started | |
|
172 | // m.Stop(CancellationToken.None); | |
|
173 | ||
|
174 | // Running | |
|
175 | started.Start(); | |
|
176 | await m.Completion; | |
|
177 | Assert.Equal(ExecutionState.Running, m.State); | |
|
178 | ||
|
179 | ||
|
180 | Assert.Throws<InvalidOperationException>(() => m.Start()); | |
|
181 | ||
|
182 | // Stop | |
|
183 | m.Stop(); | |
|
184 | ||
|
185 | // Check invalid transitions | |
|
186 | Assert.Throws<InvalidOperationException>(() => m.Start()); | |
|
187 | Assert.Throws<InvalidOperationException>(() => m.Stop()); | |
|
188 | ||
|
189 | // Stopped | |
|
190 | stopped.Start(); | |
|
191 | await m.Completion; | |
|
192 | Assert.Equal(ExecutionState.Stopped, m.State); | |
|
193 | ||
|
194 | // Check invalid transitions | |
|
195 | Assert.Throws<InvalidOperationException>(() => m.Start()); | |
|
196 | Assert.Throws<InvalidOperationException>(() => m.Stop()); | |
|
197 | } | |
|
198 | } | |
|
199 | ||
|
200 | [Fact] | |
|
201 | public async Task CancelStart() { | |
|
202 | using (var m = new MockPollComponent(true)) { | |
|
203 | m.StartWorker = (ct) => Safe.CreateTask(ct); | |
|
204 | ||
|
205 | m.Start(); | |
|
206 | var start = m.Completion; | |
|
207 | ||
|
208 | Assert.Equal(ExecutionState.Starting, m.State); | |
|
209 | m.Stop(); | |
|
210 | await m.Completion; | |
|
211 | Assert.Equal(ExecutionState.Stopped, m.State); | |
|
212 | Assert.True(start.IsCompleted); | |
|
213 | Assert.True(start.IsCanceled); | |
|
214 | } | |
|
215 | } | |
|
216 | ||
|
217 | [Fact] | |
|
218 | public async Task AwaitWorker() { | |
|
219 | using (var m = new MockPollComponent(true)) { | |
|
220 | var worker = Safe.CreateTask(); | |
|
221 | ||
|
222 | m.PollWorker = (ct) => worker; | |
|
223 | ||
|
224 | m.Start(CancellationToken.None); | |
|
225 | await m.Completion; | |
|
226 | ||
|
227 | Assert.Equal(ExecutionState.Running, m.State); | |
|
228 | ||
|
229 | m.Stop(CancellationToken.None); | |
|
230 | Assert.Equal(ExecutionState.Stopping, m.State); | |
|
231 | worker.Start(); | |
|
232 | await m.Completion; | |
|
233 | Assert.Equal(ExecutionState.Stopped, m.State); | |
|
234 | } | |
|
235 | } | |
|
236 | ||
|
237 | [Fact] | |
|
238 | public async Task CancelWorker() { | |
|
239 | using (var m = new MockPollComponent(true)) { | |
|
240 | var worker = Safe.CreateTask(); | |
|
241 | ||
|
242 | var started = Safe.CreateTask(); | |
|
243 | ||
|
244 | m.PollWorker = async (ct) => { | |
|
245 | started.Start(); | |
|
246 | await worker; | |
|
247 | ct.ThrowIfCancellationRequested(); | |
|
248 | }; | |
|
249 | ||
|
250 | m.Start(CancellationToken.None); | |
|
251 | await m.Completion; | |
|
252 | await started; // await for the poll worker to start | |
|
253 | ||
|
254 | Assert.Equal(ExecutionState.Running, m.State); | |
|
255 | ||
|
256 | m.Stop(CancellationToken.None); | |
|
257 | Assert.Equal(ExecutionState.Stopping, m.State); | |
|
258 | worker.Start(); | |
|
259 | await m.Completion; | |
|
260 | Assert.Equal(ExecutionState.Stopped, m.State); | |
|
261 | } | |
|
262 | } | |
|
263 | } | |
|
264 | } |
@@ -0,0 +1,17 | |||
|
1 | <?xml version="1.0"?> | |
|
2 | <package > | |
|
3 | <metadata> | |
|
4 | <id>Implab</id> | |
|
5 | <version>$version$</version> | |
|
6 | <title>$title$</title> | |
|
7 | <authors>Implab team</authors> | |
|
8 | <owners>Implab team</owners> | |
|
9 | <projectUrl>https://implab.org/</projectUrl> | |
|
10 | <!-- <iconUrl>http://ICON_URL_HERE_OR_DELETE_THIS_LINE</iconUrl> --> | |
|
11 | <requireLicenseAcceptance>false</requireLicenseAcceptance> | |
|
12 | <description>Common components for asynchronous applications, tracing, logging, json and xml traits.</description> | |
|
13 | <releaseNotes>Added strong name.</releaseNotes> | |
|
14 | <copyright>Copyright 2017</copyright> | |
|
15 | <tags>async xml json</tags> | |
|
16 | </metadata> | |
|
17 | </package> No newline at end of file |
|
1 | NO CONTENT: new file 100644, binary diff hidden |
@@ -0,0 +1,22 | |||
|
1 | Copyright 2012-2018 Sergey Smirnov | |
|
2 | ||
|
3 | Redistribution and use in source and binary forms, with or without | |
|
4 | modification, are permitted provided that the following conditions are met: | |
|
5 | ||
|
6 | 1. Redistributions of source code must retain the above copyright notice, this | |
|
7 | list of conditions and the following disclaimer. | |
|
8 | ||
|
9 | 2. Redistributions in binary form must reproduce the above copyright notice, | |
|
10 | this list of conditions and the following disclaimer in the documentation | |
|
11 | and/or other materials provided with the distribution. | |
|
12 | ||
|
13 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND | |
|
14 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED | |
|
15 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | |
|
16 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE | |
|
17 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | |
|
18 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR | |
|
19 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER | |
|
20 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, | |
|
21 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | |
|
22 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. No newline at end of file |
|
1 | NO CONTENT: new file 100644 | |
The requested commit or file is too big and content was truncated. Show full diff |
|
1 | NO CONTENT: new file 100644 | |
The requested commit or file is too big and content was truncated. Show full diff |
|
1 | NO CONTENT: new file 100644 | |
The requested commit or file is too big and content was truncated. Show full diff |
|
1 | NO CONTENT: new file 100644 | |
The requested commit or file is too big and content was truncated. Show full diff |
|
1 | NO CONTENT: new file 100644 | |
The requested commit or file is too big and content was truncated. Show full diff |
|
1 | NO CONTENT: new file 100644 | |
The requested commit or file is too big and content was truncated. Show full diff |
|
1 | NO CONTENT: new file 100644 | |
The requested commit or file is too big and content was truncated. Show full diff |
|
1 | NO CONTENT: new file 100644 | |
The requested commit or file is too big and content was truncated. Show full diff |
|
1 | NO CONTENT: new file 100644 | |
The requested commit or file is too big and content was truncated. Show full diff |
|
1 | NO CONTENT: new file 100644 | |
The requested commit or file is too big and content was truncated. Show full diff |
|
1 | NO CONTENT: new file 100644 | |
The requested commit or file is too big and content was truncated. Show full diff |
|
1 | NO CONTENT: new file 100644 | |
The requested commit or file is too big and content was truncated. Show full diff |
|
1 | NO CONTENT: new file 100644 | |
The requested commit or file is too big and content was truncated. Show full diff |
|
1 | NO CONTENT: new file 100644 | |
The requested commit or file is too big and content was truncated. Show full diff |
|
1 | NO CONTENT: new file 100644 | |
The requested commit or file is too big and content was truncated. Show full diff |
|
1 | NO CONTENT: new file 100644 | |
The requested commit or file is too big and content was truncated. Show full diff |
|
1 | NO CONTENT: new file 100644 | |
The requested commit or file is too big and content was truncated. Show full diff |
|
1 | NO CONTENT: new file 100644 | |
The requested commit or file is too big and content was truncated. Show full diff |
|
1 | NO CONTENT: new file 100644 | |
The requested commit or file is too big and content was truncated. Show full diff |
|
1 | NO CONTENT: new file 100644 | |
The requested commit or file is too big and content was truncated. Show full diff |
|
1 | NO CONTENT: new file 100644 | |
The requested commit or file is too big and content was truncated. Show full diff |
|
1 | NO CONTENT: new file 100644 | |
The requested commit or file is too big and content was truncated. Show full diff |
|
1 | NO CONTENT: new file 100644 | |
The requested commit or file is too big and content was truncated. Show full diff |
|
1 | NO CONTENT: new file 100644 | |
The requested commit or file is too big and content was truncated. Show full diff |
|
1 | NO CONTENT: new file 100644 | |
The requested commit or file is too big and content was truncated. Show full diff |
|
1 | NO CONTENT: new file 100644 | |
The requested commit or file is too big and content was truncated. Show full diff |
|
1 | NO CONTENT: new file 100644 | |
The requested commit or file is too big and content was truncated. Show full diff |
|
1 | NO CONTENT: new file 100644 | |
The requested commit or file is too big and content was truncated. Show full diff |
|
1 | NO CONTENT: new file 100644 | |
The requested commit or file is too big and content was truncated. Show full diff |
|
1 | NO CONTENT: new file 100644 | |
The requested commit or file is too big and content was truncated. Show full diff |
|
1 | NO CONTENT: new file 100644 | |
The requested commit or file is too big and content was truncated. Show full diff |
|
1 | NO CONTENT: new file 100644 | |
The requested commit or file is too big and content was truncated. Show full diff |
|
1 | NO CONTENT: new file 100644 | |
The requested commit or file is too big and content was truncated. Show full diff |
|
1 | NO CONTENT: new file 100644 | |
The requested commit or file is too big and content was truncated. Show full diff |
|
1 | NO CONTENT: new file 100644 | |
The requested commit or file is too big and content was truncated. Show full diff |
|
1 | NO CONTENT: new file 100644 | |
The requested commit or file is too big and content was truncated. Show full diff |
|
1 | NO CONTENT: new file 100644 | |
The requested commit or file is too big and content was truncated. Show full diff |
|
1 | NO CONTENT: new file 100644 | |
The requested commit or file is too big and content was truncated. Show full diff |
|
1 | NO CONTENT: new file 100644 | |
The requested commit or file is too big and content was truncated. Show full diff |
|
1 | NO CONTENT: new file 100644 | |
The requested commit or file is too big and content was truncated. Show full diff |
|
1 | NO CONTENT: new file 100644 | |
The requested commit or file is too big and content was truncated. Show full diff |
|
1 | NO CONTENT: new file 100644 | |
The requested commit or file is too big and content was truncated. Show full diff |
|
1 | NO CONTENT: new file 100644 | |
The requested commit or file is too big and content was truncated. Show full diff |
|
1 | NO CONTENT: new file 100644 | |
The requested commit or file is too big and content was truncated. Show full diff |
|
1 | NO CONTENT: new file 100644 | |
The requested commit or file is too big and content was truncated. Show full diff |
|
1 | NO CONTENT: new file 100644 | |
The requested commit or file is too big and content was truncated. Show full diff |
|
1 | NO CONTENT: new file 100644 | |
The requested commit or file is too big and content was truncated. Show full diff |
|
1 | NO CONTENT: new file 100644 | |
The requested commit or file is too big and content was truncated. Show full diff |
|
1 | NO CONTENT: new file 100644 | |
The requested commit or file is too big and content was truncated. Show full diff |
|
1 | NO CONTENT: new file 100644 | |
The requested commit or file is too big and content was truncated. Show full diff |
|
1 | NO CONTENT: new file 100644 | |
The requested commit or file is too big and content was truncated. Show full diff |
|
1 | NO CONTENT: new file 100644 | |
The requested commit or file is too big and content was truncated. Show full diff |
|
1 | NO CONTENT: new file 100644 | |
The requested commit or file is too big and content was truncated. Show full diff |
|
1 | NO CONTENT: new file 100644 | |
The requested commit or file is too big and content was truncated. Show full diff |
|
1 | NO CONTENT: new file 100644 | |
The requested commit or file is too big and content was truncated. Show full diff |
|
1 | NO CONTENT: new file 100644 | |
The requested commit or file is too big and content was truncated. Show full diff |
|
1 | NO CONTENT: new file 100644 | |
The requested commit or file is too big and content was truncated. Show full diff |
|
1 | NO CONTENT: new file 100644 | |
The requested commit or file is too big and content was truncated. Show full diff |
|
1 | NO CONTENT: new file 100644 | |
The requested commit or file is too big and content was truncated. Show full diff |
|
1 | NO CONTENT: new file 100644 | |
The requested commit or file is too big and content was truncated. Show full diff |
|
1 | NO CONTENT: new file 100644 | |
The requested commit or file is too big and content was truncated. Show full diff |
|
1 | NO CONTENT: new file 100644 | |
The requested commit or file is too big and content was truncated. Show full diff |
|
1 | NO CONTENT: new file 100644 | |
The requested commit or file is too big and content was truncated. Show full diff |
|
1 | NO CONTENT: new file 100644 | |
The requested commit or file is too big and content was truncated. Show full diff |
|
1 | NO CONTENT: new file 100644 | |
The requested commit or file is too big and content was truncated. Show full diff |
@@ -1,20 +1,29 | |||
|
1 | 1 | syntax: glob |
|
2 | 2 | Implab.Test/bin/ |
|
3 | 3 | *.user |
|
4 | 4 | Implab.Test/obj/ |
|
5 | 5 | *.userprefs |
|
6 | 6 | Implab/bin/ |
|
7 | 7 | Implab/obj/ |
|
8 | 8 | TestResults/ |
|
9 | 9 | Implab.Fx/obj/ |
|
10 | 10 | Implab.Fx/bin/ |
|
11 | 11 | Implab.Fx.Test/bin/ |
|
12 | 12 | Implab.Fx.Test/obj/ |
|
13 | 13 | _ReSharper.Implab/ |
|
14 | 14 | Implab.Diagnostics.Interactive/bin/ |
|
15 | 15 | Implab.Diagnostics.Interactive/obj/ |
|
16 | 16 | MonoPlay/bin/ |
|
17 | 17 | MonoPlay/obj/ |
|
18 | 18 | Implab.Test/Implab.Format.Test/bin/ |
|
19 | 19 | Implab.Test/Implab.Format.Test/obj/ |
|
20 | 20 | *.suo |
|
21 | Implab.Format.Test/bin/ | |
|
22 | Implab.Format.Test/obj/ | |
|
23 | packages/ | |
|
24 | Implab.Playground/obj/ | |
|
25 | Implab.Playground/bin/ | |
|
26 | Implab.ServiceHost/bin/ | |
|
27 | Implab.ServiceHost/obj/ | |
|
28 | Implab.ServiceHost.Test/bin/ | |
|
29 | Implab.ServiceHost.Test/obj/ |
@@ -1,1 +1,6 | |||
|
1 | 1 | f1da3afc3521e0e1631ac19e09690bc0a241841a release v2.1 |
|
2 | 34df34841225f14ec65f4a5f28585d32b55829ad v3.0.1-beta | |
|
3 | 547a2fc0d93ea5f867c778d7eeaa5888cc24fb9e v3.0.6 | |
|
4 | f1696cdc3d7a5a9e19569567722285926c5d61b0 v3.0.8 | |
|
5 | 74e048cbaac8cdd5a0825b1fd8b47dd932a05ae8 v3.0.10 | |
|
6 | 95896f882995c74202bded87392585d287b16e82 v3.0.14 |
@@ -1,84 +1,24 | |||
|
1 | <?xml version="1.0" encoding="utf-8"?> | |
|
2 | <Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> | |
|
3 | <PropertyGroup> | |
|
4 | <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration> | |
|
5 | <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform> | |
|
6 | <ProductVersion>8.0.30703</ProductVersion> | |
|
7 | <SchemaVersion>2.0</SchemaVersion> | |
|
8 | <ProjectGuid>{63F92C0C-61BF-48C0-A377-8D67C3C661D0}</ProjectGuid> | |
|
9 | <OutputType>Library</OutputType> | |
|
10 | <AppDesignerFolder>Properties</AppDesignerFolder> | |
|
11 | <RootNamespace>Implab.Test</RootNamespace> | |
|
12 | <AssemblyName>Implab.Test</AssemblyName> | |
|
13 | <TargetFrameworkVersion>v4.5</TargetFrameworkVersion> | |
|
14 | <FileAlignment>512</FileAlignment> | |
|
15 | <ProjectTypeGuids>{3AC096D0-A1C2-E12C-1390-A8335801FDAB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids> | |
|
16 | <TargetFrameworkProfile /> | |
|
1 | <Project Sdk="Microsoft.NET.Sdk"> | |
|
2 | <PropertyGroup Condition="'$(OSTYPE)'=='linux'"> | |
|
3 | <TargetFrameworks>netcoreapp2.0;net46</TargetFrameworks> | |
|
4 | <FrameworkPathOverride Condition="'$(TargetFramework)'=='net46'">/usr/lib/mono/4.5/</FrameworkPathOverride> | |
|
17 | 5 | </PropertyGroup> |
|
18 | <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' "> | |
|
19 | <DebugSymbols>true</DebugSymbols> | |
|
20 | <DebugType>full</DebugType> | |
|
21 | <Optimize>false</Optimize> | |
|
22 | <OutputPath>bin\Debug\</OutputPath> | |
|
23 | <DefineConstants>DEBUG;TRACE</DefineConstants> | |
|
24 | <ErrorReport>prompt</ErrorReport> | |
|
25 | <WarningLevel>4</WarningLevel> | |
|
26 | <Prefer32Bit>false</Prefer32Bit> | |
|
27 | </PropertyGroup> | |
|
28 | <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' "> | |
|
29 | <DebugType>pdbonly</DebugType> | |
|
30 | <Optimize>true</Optimize> | |
|
31 | <OutputPath>bin\Release\</OutputPath> | |
|
32 | <DefineConstants>TRACE</DefineConstants> | |
|
33 | <ErrorReport>prompt</ErrorReport> | |
|
34 | <WarningLevel>4</WarningLevel> | |
|
35 | <Prefer32Bit>false</Prefer32Bit> | |
|
6 | ||
|
7 | <PropertyGroup Condition="'$(OSTYPE)'=='windows'"> | |
|
8 | <TargetFrameworks>netcoreapp2.0;net46</TargetFrameworks> | |
|
36 | 9 | </PropertyGroup> |
|
37 | <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug 4.5|AnyCPU' "> | |
|
38 | <DebugSymbols>true</DebugSymbols> | |
|
39 | <DebugType>full</DebugType> | |
|
40 | <Optimize>false</Optimize> | |
|
41 | <OutputPath>bin\Debug\</OutputPath> | |
|
42 | <DefineConstants>DEBUG;TRACE</DefineConstants> | |
|
43 | <ErrorReport>prompt</ErrorReport> | |
|
44 | <WarningLevel>4</WarningLevel> | |
|
45 | <Prefer32Bit>false</Prefer32Bit> | |
|
10 | ||
|
11 | <PropertyGroup> | |
|
12 | <IsPackable>false</IsPackable> | |
|
46 | 13 | </PropertyGroup> |
|
47 | <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release 4.5|AnyCPU' "> | |
|
48 | <DebugType>pdbonly</DebugType> | |
|
49 | <Optimize>true</Optimize> | |
|
50 | <OutputPath>bin\Release\</OutputPath> | |
|
51 | <DefineConstants>TRACE</DefineConstants> | |
|
52 | <ErrorReport>prompt</ErrorReport> | |
|
53 | <WarningLevel>4</WarningLevel> | |
|
54 | <Prefer32Bit>false</Prefer32Bit> | |
|
55 | </PropertyGroup> | |
|
14 | ||
|
56 | 15 | <ItemGroup> |
|
57 | <Reference Include="Microsoft.VisualStudio.QualityTools.UnitTestFramework, Version=10.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL" /> | |
|
58 | <Reference Include="System" /> | |
|
59 |
<Reference Include=" |
|
|
60 | <RequiredTargetFramework>3.5</RequiredTargetFramework> | |
|
61 | </Reference> | |
|
62 | </ItemGroup> | |
|
63 | <ItemGroup> | |
|
64 | <Compile Include="AsyncTests.cs" /> | |
|
65 | <Compile Include="CancelationTests.cs" /> | |
|
66 | <Compile Include="PromiseHelper.cs" /> | |
|
67 | <Compile Include="Properties\AssemblyInfo.cs" /> | |
|
68 | <Compile Include="RunnableComponentTests.cs" /> | |
|
16 | <PackageReference Include="Microsoft.NET.Test.Sdk" Version="15.6.0-preview-20180109-01" /> | |
|
17 | <PackageReference Include="System.Reactive" Version="4.0.0" /> | |
|
18 | <PackageReference Include="xunit" Version="2.3.1" /> | |
|
19 | <PackageReference Include="xunit.runner.visualstudio" Version="2.3.1" /> | |
|
20 | <ProjectReference Include="../Implab/Implab.csproj"/> | |
|
21 | <DotNetCliToolReference Include="dotnet-xunit" Version="2.3.1" /> | |
|
69 | 22 | </ItemGroup> |
|
70 | <ItemGroup> | |
|
71 | <ProjectReference Include="..\Implab\Implab.csproj"> | |
|
72 | <Project>{99B95D0D-9CF9-4F70-8ADF-F4D0AA5CB0D9}</Project> | |
|
73 | <Name>Implab</Name> | |
|
74 | </ProjectReference> | |
|
75 | </ItemGroup> | |
|
76 | <Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" /> | |
|
77 | <!-- To modify your build process, add your task inside one of the targets below and uncomment it. | |
|
78 | Other similar extension points exist, see Microsoft.Common.targets. | |
|
79 | <Target Name="BeforeBuild"> | |
|
80 | </Target> | |
|
81 | <Target Name="AfterBuild"> | |
|
82 | </Target> | |
|
83 | --> | |
|
84 | </Project> No newline at end of file | |
|
23 | ||
|
24 | </Project> |
@@ -1,88 +1,191 | |||
|
1 | using NUnit.Framework; | |
|
1 | using Xunit; | |
|
2 | 2 | using System; |
|
3 | using Implab.Formats.JSON; | |
|
4 | 3 | using Implab.Automaton; |
|
4 | using Implab.Xml; | |
|
5 | using System.Xml; | |
|
6 | using Implab.Formats; | |
|
7 | using Implab.Formats.Json; | |
|
8 | using System.IO; | |
|
9 | using Implab.Test.Model; | |
|
5 | 10 | |
|
6 |
namespace Implab. |
|
|
7 | [TestFixture] | |
|
11 | namespace Implab.Test { | |
|
8 | 12 | public class JsonTests { |
|
9 | [Test] | |
|
13 | ||
|
14 | [Fact] | |
|
10 | 15 | public void TestScannerValidTokens() { |
|
11 |
using (var scanner = |
|
|
16 | using (var scanner = JsonStringScanner.Create(@"9123, -123, 0, 0.1, -0.2, -0.1e3, 1.3E-3, ""some \t\n\u0020 text"", literal []{}:")) { | |
|
12 | 17 | |
|
13 | Tuple<JsonTokenType,object>[] expexted = { | |
|
14 |
new Tuple<JsonTokenType,object>(JsonTokenType.Number, |
|
|
15 |
new Tuple<JsonTokenType,object>(JsonTokenType.ValueSeparator, |
|
|
16 |
new Tuple<JsonTokenType,object>(JsonTokenType.Number, |
|
|
17 |
new Tuple<JsonTokenType,object>(JsonTokenType.ValueSeparator, |
|
|
18 |
new Tuple<JsonTokenType,object>(JsonTokenType.Number, |
|
|
19 |
new Tuple<JsonTokenType,object>(JsonTokenType.ValueSeparator, |
|
|
20 |
new Tuple<JsonTokenType,object>(JsonTokenType.Number, |
|
|
21 |
new Tuple<JsonTokenType,object>(JsonTokenType.ValueSeparator, |
|
|
22 |
new Tuple<JsonTokenType,object>(JsonTokenType.Number, |
|
|
23 |
new Tuple<JsonTokenType,object>(JsonTokenType.ValueSeparator, |
|
|
24 |
new Tuple<JsonTokenType,object>(JsonTokenType.Number, -0.1e3 |
|
|
25 |
new Tuple<JsonTokenType,object>(JsonTokenType.ValueSeparator, |
|
|
26 |
new Tuple<JsonTokenType,object>(JsonTokenType.Number, 1.3E-3 |
|
|
27 |
new Tuple<JsonTokenType,object>(JsonTokenType.ValueSeparator, |
|
|
18 | Tuple<JsonTokenType, object>[] expexted = { | |
|
19 | new Tuple<JsonTokenType,object>(JsonTokenType.Number, "9123"), | |
|
20 | new Tuple<JsonTokenType,object>(JsonTokenType.ValueSeparator, null), | |
|
21 | new Tuple<JsonTokenType,object>(JsonTokenType.Number, "-123"), | |
|
22 | new Tuple<JsonTokenType,object>(JsonTokenType.ValueSeparator, null), | |
|
23 | new Tuple<JsonTokenType,object>(JsonTokenType.Number, "0"), | |
|
24 | new Tuple<JsonTokenType,object>(JsonTokenType.ValueSeparator, null), | |
|
25 | new Tuple<JsonTokenType,object>(JsonTokenType.Number, "0.1"), | |
|
26 | new Tuple<JsonTokenType,object>(JsonTokenType.ValueSeparator, null), | |
|
27 | new Tuple<JsonTokenType,object>(JsonTokenType.Number, "-0.2"), | |
|
28 | new Tuple<JsonTokenType,object>(JsonTokenType.ValueSeparator, null), | |
|
29 | new Tuple<JsonTokenType,object>(JsonTokenType.Number, "-0.1e3"), | |
|
30 | new Tuple<JsonTokenType,object>(JsonTokenType.ValueSeparator, null), | |
|
31 | new Tuple<JsonTokenType,object>(JsonTokenType.Number, "1.3E-3"), | |
|
32 | new Tuple<JsonTokenType,object>(JsonTokenType.ValueSeparator, null), | |
|
28 | 33 | new Tuple<JsonTokenType,object>(JsonTokenType.String, "some \t\n text"), |
|
29 |
new Tuple<JsonTokenType,object>(JsonTokenType.ValueSeparator, |
|
|
34 | new Tuple<JsonTokenType,object>(JsonTokenType.ValueSeparator, null), | |
|
30 | 35 | new Tuple<JsonTokenType,object>(JsonTokenType.Literal, "literal"), |
|
31 |
new Tuple<JsonTokenType,object>(JsonTokenType.BeginArray, |
|
|
32 |
new Tuple<JsonTokenType,object>(JsonTokenType.EndArray, |
|
|
33 |
new Tuple<JsonTokenType,object>(JsonTokenType.BeginObject, |
|
|
34 |
new Tuple<JsonTokenType,object>(JsonTokenType.EndObject, |
|
|
35 |
new Tuple<JsonTokenType,object>(JsonTokenType.NameSeparator, |
|
|
36 | new Tuple<JsonTokenType,object>(JsonTokenType.BeginArray, null), | |
|
37 | new Tuple<JsonTokenType,object>(JsonTokenType.EndArray, null), | |
|
38 | new Tuple<JsonTokenType,object>(JsonTokenType.BeginObject, null), | |
|
39 | new Tuple<JsonTokenType,object>(JsonTokenType.EndObject, null), | |
|
40 | new Tuple<JsonTokenType,object>(JsonTokenType.NameSeparator, null) | |
|
36 | 41 | }; |
|
37 | 42 | |
|
38 |
|
|
|
43 | string value; | |
|
39 | 44 | JsonTokenType tokenType; |
|
40 | 45 | for (var i = 0; i < expexted.Length; i++) { |
|
41 | ||
|
42 |
Assert. |
|
|
43 |
Assert. |
|
|
44 |
Assert. |
|
|
46 | ||
|
47 | Assert.True(scanner.ReadToken(out value, out tokenType)); | |
|
48 | Assert.Equal(expexted[i].Item1, tokenType); | |
|
49 | Assert.Equal(expexted[i].Item2, value); | |
|
45 | 50 | } |
|
46 | 51 | |
|
47 |
Assert. |
|
|
52 | Assert.False(scanner.ReadToken(out value, out tokenType)); | |
|
48 | 53 | } |
|
49 | 54 | } |
|
50 | 55 | |
|
51 |
[ |
|
|
56 | [Fact] | |
|
52 | 57 | public void TestScannerBadTokens() { |
|
53 |
var bad = new |
|
|
58 | var bad = new[] { | |
|
54 | 59 | " 1", |
|
55 | 60 | " literal", |
|
56 | 61 | " \"", |
|
57 | 62 | "\"unclosed string", |
|
58 | 63 | "1.bad", |
|
59 | 64 | "001", // should be read as three numbers |
|
60 | 65 | "--10", |
|
61 | 66 | "+10", |
|
62 | 67 | "1.0.0", |
|
63 | 68 | "1e1.0", |
|
64 | 69 | "l1teral0", |
|
65 | 70 | ".123", |
|
66 | 71 | "-.123" |
|
67 | 72 | }; |
|
68 | 73 | |
|
69 | foreach (var json in bad) | |
|
70 |
using (var scanner = |
|
|
74 | foreach (var json in bad) { | |
|
75 | using (var scanner = JsonStringScanner.Create(json)) { | |
|
71 | 76 | try { |
|
72 |
|
|
|
77 | string value; | |
|
73 | 78 | JsonTokenType token; |
|
74 | 79 | scanner.ReadToken(out value, out token); |
|
75 | if (!Object.Equals(value,json)) { | |
|
76 |
Console.WriteLine("'{0}' is read as {1}", json, value is String ? String.Format("'{0}'", value) : value |
|
|
80 | if (!Object.Equals(value, json)) { | |
|
81 | Console.WriteLine("'{0}' is read as {1}", json, value is String ? String.Format("'{0}'", value) : value); | |
|
77 | 82 | continue; |
|
78 | 83 | } |
|
79 |
Assert. |
|
|
84 | Assert.True(false, $"Token '{json}' shouldn't pass"); | |
|
80 | 85 | } catch (ParserException e) { |
|
81 | 86 | Console.WriteLine(e.Message); |
|
82 | 87 | } |
|
83 | 88 | } |
|
84 | ||
|
89 | } | |
|
90 | } | |
|
91 | ||
|
92 | [Fact] | |
|
93 | public void JsonXmlReaderSimpleTest() { | |
|
94 | //var json = "\"some text\""; | |
|
95 | //Console.WriteLine($"JSON: {json}"); | |
|
96 | //Console.WriteLine("XML"); | |
|
97 | /*using (var xmlReader = new JsonXmlReader(new JSONParser(json), new JsonXmlReaderOptions { NamespaceUri = "JsonXmlReaderSimpleTest", RootName = "string", NodesPrefix = "json" })) { | |
|
98 | Assert.AreEqual(xmlReader.ReadState, System.Xml.ReadState.Initial); | |
|
99 | ||
|
100 | AssertRead(xmlReader, XmlNodeType.XmlDeclaration); | |
|
101 | AssertRead(xmlReader, XmlNodeType.Element); | |
|
102 | AssertRead(xmlReader, XmlNodeType.Text); | |
|
103 | AssertRead(xmlReader, XmlNodeType.EndElement); | |
|
104 | Assert.IsFalse(xmlReader.Read()); | |
|
105 | }*/ | |
|
106 | ||
|
107 | DumpJsonParse("\"text value\""); | |
|
108 | DumpJsonParse("null"); | |
|
109 | DumpJsonParse("true"); | |
|
110 | DumpJsonParse("{}"); | |
|
111 | DumpJsonParse("[]"); | |
|
112 | DumpJsonParse("{\"one\":1, \"two\":2}"); | |
|
113 | DumpJsonParse("[1,\"\",2,3]"); | |
|
114 | DumpJsonParse("[{\"info\": [7,8,9]}]"); | |
|
115 | DumpJsonFlatParse("[1,2,\"\",[3,4],{\"info\": [5,6]},{\"num\": [7,8,null]}, null,[null]]"); | |
|
116 | } | |
|
117 | ||
|
118 | [Fact] | |
|
119 | public void XmlToJsonTransform() { | |
|
120 | var person = new Person { | |
|
121 | FirstName = "Charlie", | |
|
122 | LastName = "Brown", | |
|
123 | Age = 19, | |
|
124 | AgeSpecified = true | |
|
125 | }; | |
|
126 | ||
|
127 | var doc = SerializationHelpers.SerializeAsXmlDocument(person); | |
|
128 | ||
|
129 | using (var writer = new StringWriter()) { | |
|
130 | XmlToJson.Default.Transform(doc,null, writer); | |
|
131 | Console.WriteLine(writer.ToString()); | |
|
132 | } | |
|
133 | } | |
|
134 | ||
|
135 | [Fact] | |
|
136 | public void JsonSerialization() { | |
|
137 | var person = new Person { | |
|
138 | FirstName = "Charlie", | |
|
139 | LastName = "Brown", | |
|
140 | Age = 19, | |
|
141 | AgeSpecified = true, | |
|
142 | Tags = new [] { "brave", "stupid" } | |
|
143 | }; | |
|
144 | ||
|
145 | var data = SerializationHelpers.SerializeJsonAsString(person); | |
|
146 | Console.WriteLine(data); | |
|
147 | var clone = SerializationHelpers.DeserializeJsonFromString<Person>(data); | |
|
148 | ||
|
149 | Assert.Equal(person.FirstName, clone.FirstName); | |
|
150 | Assert.Equal(person.LastName, clone.LastName); | |
|
151 | Assert.Equal(person.Age, clone.Age); | |
|
152 | Assert.Equal(person.AgeSpecified, clone.AgeSpecified); | |
|
153 | Assert.Equal(person.Tags, person.Tags); | |
|
154 | } | |
|
155 | ||
|
156 | void AssertRead(XmlReader reader, XmlNodeType expected) { | |
|
157 | Assert.True(reader.Read()); | |
|
158 | Console.WriteLine($"{new string(' ', reader.Depth * 2)}{reader}"); | |
|
159 | Assert.Equal(expected, reader.NodeType); | |
|
160 | } | |
|
161 | ||
|
162 | void DumpJsonParse(string json) { | |
|
163 | Console.WriteLine($"JSON: {json}"); | |
|
164 | Console.WriteLine("XML"); | |
|
165 | using (var xmlWriter = XmlWriter.Create(Console.Out, new XmlWriterSettings { | |
|
166 | Indent = true, | |
|
167 | CloseOutput = false, | |
|
168 | ConformanceLevel = ConformanceLevel.Document | |
|
169 | })) | |
|
170 | using (var xmlReader = new JsonXmlReader(JsonReader.ParseString(json), new JsonXmlReaderOptions { NamespaceUri = "JsonXmlReaderSimpleTest", RootName = "Data", NodesPrefix = "json", CaseTransform = JsonXmlCaseTransform.UcFirst, ArrayItemName = "Item" })) { | |
|
171 | xmlWriter.WriteNode(xmlReader, false); | |
|
172 | } | |
|
173 | Console.WriteLine(); | |
|
174 | } | |
|
175 | ||
|
176 | void DumpJsonFlatParse(string json) { | |
|
177 | Console.WriteLine($"JSON: {json}"); | |
|
178 | Console.WriteLine("XML"); | |
|
179 | using (var xmlWriter = XmlWriter.Create(Console.Out, new XmlWriterSettings { | |
|
180 | Indent = true, | |
|
181 | CloseOutput = false, | |
|
182 | ConformanceLevel = ConformanceLevel.Document | |
|
183 | })) | |
|
184 | using (var xmlReader = new JsonXmlReader(JsonReader.ParseString(json), new JsonXmlReaderOptions { NamespaceUri = "JsonXmlReaderSimpleTest", NodesPrefix = "", FlattenArrays = true })) { | |
|
185 | xmlWriter.WriteNode(xmlReader, false); | |
|
186 | } | |
|
187 | Console.WriteLine(); | |
|
85 | 188 | } |
|
86 | 189 | } |
|
87 | 190 | } |
|
88 | 191 |
@@ -1,14 +1,40 | |||
|
1 |
using Implab |
|
|
1 | using Implab; | |
|
2 | using System; | |
|
2 | 3 | using System.Threading; |
|
3 | 4 | |
|
4 | 5 | namespace Implab.Test { |
|
5 | 6 | static class PromiseHelper { |
|
6 | public static IPromise<T> Sleep<T>(int timeout, T retVal) { | |
|
7 | return AsyncPool.Invoke((ct) => { | |
|
8 | ct.CancellationRequested(ct.CancelOperation); | |
|
9 | Thread.Sleep(timeout); | |
|
10 | return retVal; | |
|
7 | public static IPromise<T> Sleep<T>(int timeout, T retVal, CancellationToken ct = default(CancellationToken)) { | |
|
8 | ||
|
9 | Timer timer = null; | |
|
10 | ||
|
11 | return Promise.Create<T>((d) => { | |
|
12 | timer = new Timer(x => { | |
|
13 | d.Resolve(retVal); | |
|
14 | }, null, timeout, Timeout.Infinite); | |
|
15 | ||
|
16 | if(ct.CanBeCanceled) | |
|
17 | ct.Register(d.Cancel); | |
|
18 | ||
|
19 | }).Finally(() => { | |
|
20 | Safe.Dispose(timer); | |
|
21 | }); | |
|
22 | } | |
|
23 | ||
|
24 | public static IPromise Sleep(int timeout, CancellationToken ct = default(CancellationToken)) { | |
|
25 | Timer timer = null; | |
|
26 | ||
|
27 | return Promise.Create((d) => { | |
|
28 | timer = new Timer(x => { | |
|
29 | d.Resolve(); | |
|
30 | }, null, timeout, Timeout.Infinite); | |
|
31 | ||
|
32 | if(ct.CanBeCanceled) | |
|
33 | ct.Register(d.Cancel); | |
|
34 | ||
|
35 | }).Finally(() => { | |
|
36 | Safe.Dispose(timer); | |
|
11 | 37 | }); |
|
12 | 38 | } |
|
13 | 39 | } |
|
14 | 40 | } |
@@ -1,270 +1,69 | |||
|
1 |
|
|
|
2 |
Microsoft Visual Studio Solution File, Format Version 1 |
|
|
3 |
# Visual Studio |
|
|
4 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Implab", "Implab\Implab.csproj", "{F550F1F8-8746-4AD0-9614-855F4C4B7F05}" | |
|
1 | ||
|
2 | Microsoft Visual Studio Solution File, Format Version 12.00 | |
|
3 | # Visual Studio 15 | |
|
4 | VisualStudioVersion = 15.0.27428.2005 | |
|
5 | MinimumVisualStudioVersion = 10.0.40219.1 | |
|
6 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Implab", "Implab\Implab.csproj", "{FF2052B6-9C8F-4022-A347-F07ABF635885}" | |
|
5 | 7 | EndProject |
|
6 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{CE8D8D18-437A-445C-B662-4C2CE79A76F6}" | |
|
7 | ProjectSection(SolutionItems) = preProject | |
|
8 | Implab.vsmdi = Implab.vsmdi | |
|
9 | Local.testsettings = Local.testsettings | |
|
10 | TraceAndTestImpact.testsettings = TraceAndTestImpact.testsettings | |
|
11 | EndProjectSection | |
|
8 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Implab.Test", "Implab.Test\Implab.Test.csproj", "{6CD0DA18-8D9B-4AA8-A3DC-17322E27335E}" | |
|
9 | EndProject | |
|
10 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Implab.Playground", "Implab.Playground\Implab.Playground.csproj", "{100DFEB0-75BE-436F-ADDF-1F46EF433F46}" | |
|
12 | 11 | EndProject |
|
13 |
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Implab. |
|
|
12 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Implab.ServiceHost", "Implab.ServiceHost\Implab.ServiceHost.csproj", "{8B79FCBE-50DD-40A0-9B5E-E572072E4868}" | |
|
14 | 13 | EndProject |
|
15 |
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Implab. |
|
|
16 | EndProject | |
|
17 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Implab.Fx.Test", "Implab.Fx.Test\Implab.Fx.Test.csproj", "{2F31E405-E267-4195-A05D-574093C21209}" | |
|
14 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Implab.ServiceHost.Test", "Implab.ServiceHost.Test\Implab.ServiceHost.Test.csproj", "{CB844F94-E555-4F25-A932-7CB85C98CF86}" | |
|
18 | 15 | EndProject |
|
19 | 16 | Global |
|
20 | 17 | GlobalSection(SolutionConfigurationPlatforms) = preSolution |
|
21 | 18 | Debug|Any CPU = Debug|Any CPU |
|
22 | 19 | Release|Any CPU = Release|Any CPU |
|
23 | Debug 4.5|Any CPU = Debug 4.5|Any CPU | |
|
24 | Release 4.5|Any CPU = Release 4.5|Any CPU | |
|
20 | Debug|x64 = Debug|x64 | |
|
21 | Debug|x86 = Debug|x86 | |
|
22 | Release|x64 = Release|x64 | |
|
23 | Release|x86 = Release|x86 | |
|
25 | 24 | EndGlobalSection |
|
26 | 25 | GlobalSection(ProjectConfigurationPlatforms) = postSolution |
|
27 |
{ |
|
|
28 |
{ |
|
|
29 | {06E706F8-6881-43EB-927E-FFC503AF6ABC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU | |
|
30 | {06E706F8-6881-43EB-927E-FFC503AF6ABC}.Debug|Any CPU.Build.0 = Debug|Any CPU | |
|
31 | {06E706F8-6881-43EB-927E-FFC503AF6ABC}.Release 4.5|Any CPU.ActiveCfg = Release 4.5|Any CPU | |
|
32 | {06E706F8-6881-43EB-927E-FFC503AF6ABC}.Release 4.5|Any CPU.Build.0 = Release 4.5|Any CPU | |
|
33 |
{ |
|
|
34 |
{ |
|
|
35 |
{ |
|
|
36 |
{ |
|
|
37 |
{ |
|
|
38 | {2F31E405-E267-4195-A05D-574093C21209}.Debug|Any CPU.Build.0 = Debug|Any CPU | |
|
39 | {2F31E405-E267-4195-A05D-574093C21209}.Release 4.5|Any CPU.ActiveCfg = Release 4.5|Any CPU | |
|
40 | {2F31E405-E267-4195-A05D-574093C21209}.Release 4.5|Any CPU.Build.0 = Release 4.5|Any CPU | |
|
41 | {2F31E405-E267-4195-A05D-574093C21209}.Release|Any CPU.ActiveCfg = Release|Any CPU | |
|
42 | {2F31E405-E267-4195-A05D-574093C21209}.Release|Any CPU.Build.0 = Release|Any CPU | |
|
43 | {63F92C0C-61BF-48C0-A377-8D67C3C661D0}.Debug 4.5|Any CPU.ActiveCfg = Debug 4.5|Any CPU | |
|
44 | {63F92C0C-61BF-48C0-A377-8D67C3C661D0}.Debug 4.5|Any CPU.Build.0 = Debug 4.5|Any CPU | |
|
45 | {63F92C0C-61BF-48C0-A377-8D67C3C661D0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU | |
|
46 | {63F92C0C-61BF-48C0-A377-8D67C3C661D0}.Debug|Any CPU.Build.0 = Debug|Any CPU | |
|
47 | {63F92C0C-61BF-48C0-A377-8D67C3C661D0}.Release 4.5|Any CPU.ActiveCfg = Release 4.5|Any CPU | |
|
48 | {63F92C0C-61BF-48C0-A377-8D67C3C661D0}.Release 4.5|Any CPU.Build.0 = Release 4.5|Any CPU | |
|
49 | {63F92C0C-61BF-48C0-A377-8D67C3C661D0}.Release|Any CPU.ActiveCfg = Release|Any CPU | |
|
50 | {63F92C0C-61BF-48C0-A377-8D67C3C661D0}.Release|Any CPU.Build.0 = Release|Any CPU | |
|
51 |
{ |
|
|
52 |
{ |
|
|
53 | {F550F1F8-8746-4AD0-9614-855F4C4B7F05}.Debug|Any CPU.ActiveCfg = Debug|Any CPU | |
|
54 | {F550F1F8-8746-4AD0-9614-855F4C4B7F05}.Debug|Any CPU.Build.0 = Debug|Any CPU | |
|
55 | {F550F1F8-8746-4AD0-9614-855F4C4B7F05}.Release 4.5|Any CPU.ActiveCfg = Release 4.5|Any CPU | |
|
56 | {F550F1F8-8746-4AD0-9614-855F4C4B7F05}.Release 4.5|Any CPU.Build.0 = Release 4.5|Any CPU | |
|
57 |
{ |
|
|
58 |
{ |
|
|
59 | EndGlobalSection | |
|
60 | GlobalSection(NestedProjects) = preSolution | |
|
61 | EndGlobalSection | |
|
62 | GlobalSection(MonoDevelopProperties) = preSolution | |
|
63 | StartupItem = Implab\Implab.csproj | |
|
64 | Policies = $0 | |
|
65 | $0.CSharpFormattingPolicy = $1 | |
|
66 | $1.IndentSwitchBody = True | |
|
67 | $1.NamespaceBraceStyle = EndOfLine | |
|
68 | $1.ClassBraceStyle = EndOfLine | |
|
69 | $1.InterfaceBraceStyle = EndOfLine | |
|
70 | $1.StructBraceStyle = EndOfLine | |
|
71 | $1.EnumBraceStyle = EndOfLine | |
|
72 | $1.MethodBraceStyle = EndOfLine | |
|
73 | $1.ConstructorBraceStyle = EndOfLine | |
|
74 | $1.DestructorBraceStyle = EndOfLine | |
|
75 | $1.BeforeMethodDeclarationParentheses = False | |
|
76 | $1.BeforeMethodCallParentheses = False | |
|
77 | $1.BeforeConstructorDeclarationParentheses = False | |
|
78 | $1.NewLineBeforeConstructorInitializerColon = NewLine | |
|
79 | $1.NewLineAfterConstructorInitializerColon = SameLine | |
|
80 | $1.BeforeIndexerDeclarationBracket = False | |
|
81 | $1.BeforeDelegateDeclarationParentheses = False | |
|
82 | $1.NewParentheses = False | |
|
83 | $1.SpacesBeforeBrackets = False | |
|
84 | $1.inheritsSet = Mono | |
|
85 | $1.inheritsScope = text/x-csharp | |
|
86 | $1.scope = text/x-csharp | |
|
87 | $0.TextStylePolicy = $2 | |
|
88 | $2.FileWidth = 120 | |
|
89 | $2.EolMarker = Unix | |
|
90 | $2.inheritsSet = VisualStudio | |
|
91 | $2.inheritsScope = text/plain | |
|
92 | $2.scope = text/x-csharp | |
|
93 | $0.DotNetNamingPolicy = $3 | |
|
94 | $3.DirectoryNamespaceAssociation = PrefixedHierarchical | |
|
95 | $3.ResourceNamePolicy = MSBuild | |
|
96 | $0.TextStylePolicy = $4 | |
|
97 | $4.FileWidth = 120 | |
|
98 | $4.TabsToSpaces = False | |
|
99 | $4.inheritsSet = VisualStudio | |
|
100 | $4.inheritsScope = text/plain | |
|
101 | $4.scope = application/xml | |
|
102 | $0.XmlFormattingPolicy = $5 | |
|
103 | $5.inheritsSet = Mono | |
|
104 | $5.inheritsScope = application/xml | |
|
105 | $5.scope = application/xml | |
|
106 | $0.TextStylePolicy = $6 | |
|
107 | $6.FileWidth = 120 | |
|
108 | $6.TabsToSpaces = False | |
|
109 | $6.inheritsSet = VisualStudio | |
|
110 | $6.inheritsScope = text/plain | |
|
111 | $6.scope = text/plain | |
|
112 | $0.NameConventionPolicy = $7 | |
|
113 | $7.Rules = $8 | |
|
114 | $8.NamingRule = $9 | |
|
115 | $9.Name = Namespaces | |
|
116 | $9.AffectedEntity = Namespace | |
|
117 | $9.VisibilityMask = VisibilityMask | |
|
118 | $9.NamingStyle = PascalCase | |
|
119 | $9.IncludeInstanceMembers = True | |
|
120 | $9.IncludeStaticEntities = True | |
|
121 | $8.NamingRule = $10 | |
|
122 | $10.Name = Types | |
|
123 | $10.AffectedEntity = Class, Struct, Enum, Delegate | |
|
124 | $10.VisibilityMask = VisibilityMask | |
|
125 | $10.NamingStyle = PascalCase | |
|
126 | $10.IncludeInstanceMembers = True | |
|
127 | $10.IncludeStaticEntities = True | |
|
128 | $8.NamingRule = $11 | |
|
129 | $11.Name = Interfaces | |
|
130 | $11.RequiredPrefixes = $12 | |
|
131 | $12.String = I | |
|
132 | $11.AffectedEntity = Interface | |
|
133 | $11.VisibilityMask = VisibilityMask | |
|
134 | $11.NamingStyle = PascalCase | |
|
135 | $11.IncludeInstanceMembers = True | |
|
136 | $11.IncludeStaticEntities = True | |
|
137 | $8.NamingRule = $13 | |
|
138 | $13.Name = Attributes | |
|
139 | $13.RequiredSuffixes = $14 | |
|
140 | $14.String = Attribute | |
|
141 | $13.AffectedEntity = CustomAttributes | |
|
142 | $13.VisibilityMask = VisibilityMask | |
|
143 | $13.NamingStyle = PascalCase | |
|
144 | $13.IncludeInstanceMembers = True | |
|
145 | $13.IncludeStaticEntities = True | |
|
146 | $8.NamingRule = $15 | |
|
147 | $15.Name = Event Arguments | |
|
148 | $15.RequiredSuffixes = $16 | |
|
149 | $16.String = EventArgs | |
|
150 | $15.AffectedEntity = CustomEventArgs | |
|
151 | $15.VisibilityMask = VisibilityMask | |
|
152 | $15.NamingStyle = PascalCase | |
|
153 | $15.IncludeInstanceMembers = True | |
|
154 | $15.IncludeStaticEntities = True | |
|
155 | $8.NamingRule = $17 | |
|
156 | $17.Name = Exceptions | |
|
157 | $17.RequiredSuffixes = $18 | |
|
158 | $18.String = Exception | |
|
159 | $17.AffectedEntity = CustomExceptions | |
|
160 | $17.VisibilityMask = VisibilityMask | |
|
161 | $17.NamingStyle = PascalCase | |
|
162 | $17.IncludeInstanceMembers = True | |
|
163 | $17.IncludeStaticEntities = True | |
|
164 | $8.NamingRule = $19 | |
|
165 | $19.Name = Methods | |
|
166 | $19.AffectedEntity = Methods | |
|
167 | $19.VisibilityMask = VisibilityMask | |
|
168 | $19.NamingStyle = PascalCase | |
|
169 | $19.IncludeInstanceMembers = True | |
|
170 | $19.IncludeStaticEntities = True | |
|
171 | $8.NamingRule = $20 | |
|
172 | $20.Name = Static Readonly Fields | |
|
173 | $20.AffectedEntity = ReadonlyField | |
|
174 | $20.VisibilityMask = Internal, Protected, Public | |
|
175 | $20.NamingStyle = CamelCase | |
|
176 | $20.IncludeInstanceMembers = False | |
|
177 | $20.IncludeStaticEntities = True | |
|
178 | $8.NamingRule = $21 | |
|
179 | $21.Name = Fields (Non Private) | |
|
180 | $21.AffectedEntity = Field | |
|
181 | $21.VisibilityMask = Internal, Public | |
|
182 | $21.NamingStyle = CamelCase | |
|
183 | $21.IncludeInstanceMembers = True | |
|
184 | $21.IncludeStaticEntities = True | |
|
185 | $8.NamingRule = $22 | |
|
186 | $22.Name = ReadOnly Fields (Non Private) | |
|
187 | $22.AffectedEntity = ReadonlyField | |
|
188 | $22.VisibilityMask = Internal, Public | |
|
189 | $22.NamingStyle = CamelCase | |
|
190 | $22.IncludeInstanceMembers = True | |
|
191 | $22.IncludeStaticEntities = False | |
|
192 | $8.NamingRule = $23 | |
|
193 | $23.Name = Fields (Private) | |
|
194 | $23.RequiredPrefixes = $24 | |
|
195 | $24.String = m_ | |
|
196 | $23.AffectedEntity = Field, ReadonlyField | |
|
197 | $23.VisibilityMask = Private, Protected | |
|
198 | $23.NamingStyle = CamelCase | |
|
199 | $23.IncludeInstanceMembers = True | |
|
200 | $23.IncludeStaticEntities = False | |
|
201 | $8.NamingRule = $25 | |
|
202 | $25.Name = Static Fields (Private) | |
|
203 | $25.RequiredPrefixes = $26 | |
|
204 | $26.String = _ | |
|
205 | $25.AffectedEntity = Field | |
|
206 | $25.VisibilityMask = Private | |
|
207 | $25.NamingStyle = CamelCase | |
|
208 | $25.IncludeInstanceMembers = False | |
|
209 | $25.IncludeStaticEntities = True | |
|
210 | $8.NamingRule = $27 | |
|
211 | $27.Name = ReadOnly Fields (Private) | |
|
212 | $27.RequiredPrefixes = $28 | |
|
213 | $28.String = m_ | |
|
214 | $27.AffectedEntity = ReadonlyField | |
|
215 | $27.VisibilityMask = Private, Protected | |
|
216 | $27.NamingStyle = CamelCase | |
|
217 | $27.IncludeInstanceMembers = True | |
|
218 | $27.IncludeStaticEntities = False | |
|
219 | $8.NamingRule = $29 | |
|
220 | $29.Name = Constant Fields | |
|
221 | $29.AffectedEntity = ConstantField | |
|
222 | $29.VisibilityMask = VisibilityMask | |
|
223 | $29.NamingStyle = AllUpper | |
|
224 | $29.IncludeInstanceMembers = True | |
|
225 | $29.IncludeStaticEntities = True | |
|
226 | $8.NamingRule = $30 | |
|
227 | $30.Name = Properties | |
|
228 | $30.AffectedEntity = Property | |
|
229 | $30.VisibilityMask = VisibilityMask | |
|
230 | $30.NamingStyle = PascalCase | |
|
231 | $30.IncludeInstanceMembers = True | |
|
232 | $30.IncludeStaticEntities = True | |
|
233 | $8.NamingRule = $31 | |
|
234 | $31.Name = Events | |
|
235 | $31.AffectedEntity = Event | |
|
236 | $31.VisibilityMask = VisibilityMask | |
|
237 | $31.NamingStyle = PascalCase | |
|
238 | $31.IncludeInstanceMembers = True | |
|
239 | $31.IncludeStaticEntities = True | |
|
240 | $8.NamingRule = $32 | |
|
241 | $32.Name = Enum Members | |
|
242 | $32.AffectedEntity = EnumMember | |
|
243 | $32.VisibilityMask = VisibilityMask | |
|
244 | $32.NamingStyle = PascalCase | |
|
245 | $32.IncludeInstanceMembers = True | |
|
246 | $32.IncludeStaticEntities = True | |
|
247 | $8.NamingRule = $33 | |
|
248 | $33.Name = Parameters | |
|
249 | $33.AffectedEntity = Parameter, LocalVariable | |
|
250 | $33.VisibilityMask = VisibilityMask | |
|
251 | $33.NamingStyle = CamelCase | |
|
252 | $33.IncludeInstanceMembers = True | |
|
253 | $33.IncludeStaticEntities = True | |
|
254 | $8.NamingRule = $34 | |
|
255 | $34.Name = Type Parameters | |
|
256 | $34.RequiredPrefixes = $35 | |
|
257 | $35.String = T | |
|
258 | $34.AffectedEntity = TypeParameter | |
|
259 | $34.VisibilityMask = VisibilityMask | |
|
260 | $34.NamingStyle = PascalCase | |
|
261 | $34.IncludeInstanceMembers = True | |
|
262 | $34.IncludeStaticEntities = True | |
|
263 | EndGlobalSection | |
|
264 | GlobalSection(TestCaseManagementSettings) = postSolution | |
|
265 | CategoryFile = Implab.vsmdi | |
|
26 | {FF2052B6-9C8F-4022-A347-F07ABF635885}.Debug|Any CPU.ActiveCfg = Debug|Any CPU | |
|
27 | {FF2052B6-9C8F-4022-A347-F07ABF635885}.Debug|Any CPU.Build.0 = Debug|Any CPU | |
|
28 | {FF2052B6-9C8F-4022-A347-F07ABF635885}.Release|Any CPU.ActiveCfg = Release|Any CPU | |
|
29 | {FF2052B6-9C8F-4022-A347-F07ABF635885}.Release|Any CPU.Build.0 = Release|Any CPU | |
|
30 | {6CD0DA18-8D9B-4AA8-A3DC-17322E27335E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU | |
|
31 | {6CD0DA18-8D9B-4AA8-A3DC-17322E27335E}.Debug|Any CPU.Build.0 = Debug|Any CPU | |
|
32 | {6CD0DA18-8D9B-4AA8-A3DC-17322E27335E}.Release|Any CPU.ActiveCfg = Release|Any CPU | |
|
33 | {6CD0DA18-8D9B-4AA8-A3DC-17322E27335E}.Release|Any CPU.Build.0 = Release|Any CPU | |
|
34 | {100DFEB0-75BE-436F-ADDF-1F46EF433F46}.Debug|Any CPU.ActiveCfg = Debug|Any CPU | |
|
35 | {100DFEB0-75BE-436F-ADDF-1F46EF433F46}.Debug|Any CPU.Build.0 = Debug|Any CPU | |
|
36 | {100DFEB0-75BE-436F-ADDF-1F46EF433F46}.Release|Any CPU.ActiveCfg = Release|Any CPU | |
|
37 | {100DFEB0-75BE-436F-ADDF-1F46EF433F46}.Release|Any CPU.Build.0 = Release|Any CPU | |
|
38 | {8B79FCBE-50DD-40A0-9B5E-E572072E4868}.Debug|Any CPU.ActiveCfg = Debug|Any CPU | |
|
39 | {8B79FCBE-50DD-40A0-9B5E-E572072E4868}.Debug|Any CPU.Build.0 = Debug|Any CPU | |
|
40 | {8B79FCBE-50DD-40A0-9B5E-E572072E4868}.Debug|x64.ActiveCfg = Debug|x64 | |
|
41 | {8B79FCBE-50DD-40A0-9B5E-E572072E4868}.Debug|x64.Build.0 = Debug|x64 | |
|
42 | {8B79FCBE-50DD-40A0-9B5E-E572072E4868}.Debug|x86.ActiveCfg = Debug|x86 | |
|
43 | {8B79FCBE-50DD-40A0-9B5E-E572072E4868}.Debug|x86.Build.0 = Debug|x86 | |
|
44 | {8B79FCBE-50DD-40A0-9B5E-E572072E4868}.Release|Any CPU.ActiveCfg = Release|Any CPU | |
|
45 | {8B79FCBE-50DD-40A0-9B5E-E572072E4868}.Release|Any CPU.Build.0 = Release|Any CPU | |
|
46 | {8B79FCBE-50DD-40A0-9B5E-E572072E4868}.Release|x64.ActiveCfg = Release|x64 | |
|
47 | {8B79FCBE-50DD-40A0-9B5E-E572072E4868}.Release|x64.Build.0 = Release|x64 | |
|
48 | {8B79FCBE-50DD-40A0-9B5E-E572072E4868}.Release|x86.ActiveCfg = Release|x86 | |
|
49 | {8B79FCBE-50DD-40A0-9B5E-E572072E4868}.Release|x86.Build.0 = Release|x86 | |
|
50 | {CB844F94-E555-4F25-A932-7CB85C98CF86}.Debug|Any CPU.ActiveCfg = Debug|Any CPU | |
|
51 | {CB844F94-E555-4F25-A932-7CB85C98CF86}.Debug|Any CPU.Build.0 = Debug|Any CPU | |
|
52 | {CB844F94-E555-4F25-A932-7CB85C98CF86}.Debug|x64.ActiveCfg = Debug|x64 | |
|
53 | {CB844F94-E555-4F25-A932-7CB85C98CF86}.Debug|x64.Build.0 = Debug|x64 | |
|
54 | {CB844F94-E555-4F25-A932-7CB85C98CF86}.Debug|x86.ActiveCfg = Debug|x86 | |
|
55 | {CB844F94-E555-4F25-A932-7CB85C98CF86}.Debug|x86.Build.0 = Debug|x86 | |
|
56 | {CB844F94-E555-4F25-A932-7CB85C98CF86}.Release|Any CPU.ActiveCfg = Release|Any CPU | |
|
57 | {CB844F94-E555-4F25-A932-7CB85C98CF86}.Release|Any CPU.Build.0 = Release|Any CPU | |
|
58 | {CB844F94-E555-4F25-A932-7CB85C98CF86}.Release|x64.ActiveCfg = Release|x64 | |
|
59 | {CB844F94-E555-4F25-A932-7CB85C98CF86}.Release|x64.Build.0 = Release|x64 | |
|
60 | {CB844F94-E555-4F25-A932-7CB85C98CF86}.Release|x86.ActiveCfg = Release|x86 | |
|
61 | {CB844F94-E555-4F25-A932-7CB85C98CF86}.Release|x86.Build.0 = Release|x86 | |
|
266 | 62 | EndGlobalSection |
|
267 | 63 | GlobalSection(SolutionProperties) = preSolution |
|
268 | 64 | HideSolutionNode = FALSE |
|
269 | 65 | EndGlobalSection |
|
66 | GlobalSection(ExtensibilityGlobals) = postSolution | |
|
67 | SolutionGuid = {36D837FC-4CDD-4AEA-87BF-F130FEB22E02} | |
|
68 | EndGlobalSection | |
|
270 | 69 | EndGlobal |
@@ -1,277 +1,26 | |||
|
1 | <?xml version="1.0" encoding="utf-8"?> | |
|
2 | <Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> | |
|
1 | <Project Sdk="Microsoft.NET.Sdk"> | |
|
2 | ||
|
3 | 3 | <PropertyGroup> |
|
4 | <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration> | |
|
5 | <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform> | |
|
6 | <ProjectGuid>{F550F1F8-8746-4AD0-9614-855F4C4B7F05}</ProjectGuid> | |
|
7 | <OutputType>Library</OutputType> | |
|
8 | <RootNamespace>Implab</RootNamespace> | |
|
9 | <AssemblyName>Implab</AssemblyName> | |
|
10 | <TargetFrameworkVersion>v4.5</TargetFrameworkVersion> | |
|
11 |
< |
|
|
12 | <SchemaVersion>2.0</SchemaVersion> | |
|
13 | </PropertyGroup> | |
|
14 | <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' "> | |
|
15 | <DebugSymbols>true</DebugSymbols> | |
|
16 | <DebugType>full</DebugType> | |
|
17 | <Optimize>false</Optimize> | |
|
18 | <OutputPath>bin\Debug</OutputPath> | |
|
19 | <DefineConstants>TRACE;DEBUG;</DefineConstants> | |
|
20 | <ErrorReport>prompt</ErrorReport> | |
|
21 | <WarningLevel>4</WarningLevel> | |
|
22 | <ConsolePause>false</ConsolePause> | |
|
23 | <RunCodeAnalysis>true</RunCodeAnalysis> | |
|
24 | </PropertyGroup> | |
|
25 | <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' "> | |
|
26 | <DebugType>full</DebugType> | |
|
27 | <Optimize>true</Optimize> | |
|
28 | <OutputPath>bin\Release</OutputPath> | |
|
29 | <ErrorReport>prompt</ErrorReport> | |
|
30 | <WarningLevel>4</WarningLevel> | |
|
31 | <ConsolePause>false</ConsolePause> | |
|
4 | <Authors>Sergey Smirnov</Authors> | |
|
5 | <Title>Implab library</Title> | |
|
6 | <Description>Provides some helper clesses like XML serialization helpers, JSON XML reader, | |
|
7 | JSON pull-parser, ECMA-style promises, lightweight synchonization routines Signal | |
|
8 | and SharedLock, Trace helpers on top of System.Diagnostics, ObjectPool etc. | |
|
9 | </Description> | |
|
10 | <Copyright>2012-2018 Sergey Smirnov</Copyright> | |
|
11 | <Version>3.0.14</Version> | |
|
12 | <PackageLicenseUrl>https://bitbucket.org/wozard/implabnet/src/v3/Implab/license.txt</PackageLicenseUrl> | |
|
13 | <PackageProjectUrl>https://bitbucket.org/wozard/implabnet</PackageProjectUrl> | |
|
14 | <RepositoryUrl>https://bitbucket.org/wozard/implabnet</RepositoryUrl> | |
|
15 | <RepositoryType>mercurial</RepositoryType> | |
|
16 | <PackageTags>IMPLAB;Json pull-parser;Json Xml;async;diagnostics;serialization;</PackageTags> | |
|
17 | <TargetFrameworks>netstandard2.0;net46</TargetFrameworks> | |
|
18 | <FrameworkPathOverride Condition="'$(TargetFramework)'=='net46' and '$(OSTYPE)'=='linux'">/usr/lib/mono/4.5/</FrameworkPathOverride> | |
|
19 | <DefineConstants Condition="'$(TargetFramework)'=='net46'">NETFX_TRACE_BUG;$(DefineConstants)</DefineConstants> | |
|
32 | 20 | </PropertyGroup> |
|
33 | <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug 4.5|AnyCPU' "> | |
|
34 | <DebugSymbols>true</DebugSymbols> | |
|
35 | <DebugType>full</DebugType> | |
|
36 | <Optimize>false</Optimize> | |
|
37 | <OutputPath>bin\Debug</OutputPath> | |
|
38 | <DefineConstants>TRACE;DEBUG;NET_4_5</DefineConstants> | |
|
39 | <ErrorReport>prompt</ErrorReport> | |
|
40 | <WarningLevel>4</WarningLevel> | |
|
41 | <RunCodeAnalysis>true</RunCodeAnalysis> | |
|
42 | <ConsolePause>false</ConsolePause> | |
|
43 | </PropertyGroup> | |
|
44 | <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release 4.5|AnyCPU' "> | |
|
45 | <Optimize>true</Optimize> | |
|
46 | <OutputPath>bin\Release</OutputPath> | |
|
47 | <ErrorReport>prompt</ErrorReport> | |
|
48 | <WarningLevel>4</WarningLevel> | |
|
49 | <ConsolePause>false</ConsolePause> | |
|
50 | <DefineConstants>NET_4_5</DefineConstants> | |
|
51 | </PropertyGroup> | |
|
52 | <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'DebugMono|AnyCPU' "> | |
|
53 | <DebugSymbols>true</DebugSymbols> | |
|
54 | <DebugType>full</DebugType> | |
|
55 | <Optimize>false</Optimize> | |
|
56 | <OutputPath>bin\Debug</OutputPath> | |
|
57 | <DefineConstants>TRACE;DEBUG;NET_4_5;MONO</DefineConstants> | |
|
58 | <ErrorReport>prompt</ErrorReport> | |
|
59 | <WarningLevel>4</WarningLevel> | |
|
60 | <RunCodeAnalysis>true</RunCodeAnalysis> | |
|
61 | <ConsolePause>false</ConsolePause> | |
|
62 | </PropertyGroup> | |
|
63 | <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'ReleaseMono|AnyCPU' "> | |
|
64 | <Optimize>true</Optimize> | |
|
65 | <OutputPath>bin\Release</OutputPath> | |
|
66 | <DefineConstants>NET_4_5;MONO;</DefineConstants> | |
|
67 | <ErrorReport>prompt</ErrorReport> | |
|
68 | <WarningLevel>4</WarningLevel> | |
|
69 | <ConsolePause>false</ConsolePause> | |
|
70 | </PropertyGroup> | |
|
71 | <ItemGroup> | |
|
72 | <Reference Include="System" /> | |
|
73 | <Reference Include="System.Xml" /> | |
|
74 | <Reference Include="mscorlib" /> | |
|
75 | </ItemGroup> | |
|
21 | ||
|
76 | 22 | <ItemGroup> |
|
77 | <Compile Include="CustomEqualityComparer.cs" /> | |
|
78 | <Compile Include="Diagnostics\ConsoleTraceListener.cs" /> | |
|
79 | <Compile Include="Diagnostics\EventText.cs" /> | |
|
80 | <Compile Include="Diagnostics\LogChannel.cs" /> | |
|
81 | <Compile Include="Diagnostics\LogicalOperation.cs" /> | |
|
82 | <Compile Include="Diagnostics\TextFileListener.cs" /> | |
|
83 | <Compile Include="Diagnostics\TraceLog.cs" /> | |
|
84 | <Compile Include="Diagnostics\TraceEvent.cs" /> | |
|
85 | <Compile Include="Diagnostics\TraceEventType.cs" /> | |
|
86 | <Compile Include="ICancellable.cs" /> | |
|
87 | <Compile Include="IProgressHandler.cs" /> | |
|
88 | <Compile Include="IProgressNotifier.cs" /> | |
|
89 | <Compile Include="IPromiseT.cs" /> | |
|
90 | <Compile Include="IPromise.cs" /> | |
|
91 | <Compile Include="IServiceLocator.cs" /> | |
|
92 | <Compile Include="ITaskController.cs" /> | |
|
93 | <Compile Include="Parallels\DispatchPool.cs" /> | |
|
94 | <Compile Include="Parallels\ArrayTraits.cs" /> | |
|
95 | <Compile Include="Parallels\MTQueue.cs" /> | |
|
96 | <Compile Include="Parallels\WorkerPool.cs" /> | |
|
97 | <Compile Include="ProgressInitEventArgs.cs" /> | |
|
98 | <Compile Include="Properties\AssemblyInfo.cs" /> | |
|
99 | <Compile Include="Parallels\AsyncPool.cs" /> | |
|
100 | <Compile Include="Safe.cs" /> | |
|
101 | <Compile Include="ValueEventArgs.cs" /> | |
|
102 | <Compile Include="PromiseExtensions.cs" /> | |
|
103 | <Compile Include="SyncContextPromise.cs" /> | |
|
104 | <Compile Include="Diagnostics\OperationContext.cs" /> | |
|
105 | <Compile Include="Diagnostics\TraceContext.cs" /> | |
|
106 | <Compile Include="Diagnostics\LogEventArgs.cs" /> | |
|
107 | <Compile Include="Diagnostics\LogEventArgsT.cs" /> | |
|
108 | <Compile Include="Diagnostics\Extensions.cs" /> | |
|
109 | <Compile Include="PromiseEventType.cs" /> | |
|
110 | <Compile Include="Parallels\AsyncQueue.cs" /> | |
|
111 | <Compile Include="PromiseT.cs" /> | |
|
112 | <Compile Include="IDeferred.cs" /> | |
|
113 | <Compile Include="IDeferredT.cs" /> | |
|
114 | <Compile Include="Promise.cs" /> | |
|
115 | <Compile Include="PromiseTransientException.cs" /> | |
|
116 | <Compile Include="Parallels\Signal.cs" /> | |
|
117 | <Compile Include="Parallels\SharedLock.cs" /> | |
|
118 | <Compile Include="Diagnostics\ILogWriter.cs" /> | |
|
119 | <Compile Include="Diagnostics\ListenerBase.cs" /> | |
|
120 | <Compile Include="Parallels\BlockingQueue.cs" /> | |
|
121 | <Compile Include="AbstractEvent.cs" /> | |
|
122 | <Compile Include="AbstractPromise.cs" /> | |
|
123 | <Compile Include="AbstractPromiseT.cs" /> | |
|
124 | <Compile Include="FuncTask.cs" /> | |
|
125 | <Compile Include="FuncTaskBase.cs" /> | |
|
126 | <Compile Include="FuncTaskT.cs" /> | |
|
127 | <Compile Include="ActionChainTaskBase.cs" /> | |
|
128 | <Compile Include="ActionChainTask.cs" /> | |
|
129 | <Compile Include="ActionChainTaskT.cs" /> | |
|
130 | <Compile Include="FuncChainTaskBase.cs" /> | |
|
131 | <Compile Include="FuncChainTask.cs" /> | |
|
132 | <Compile Include="FuncChainTaskT.cs" /> | |
|
133 | <Compile Include="ActionTaskBase.cs" /> | |
|
134 | <Compile Include="ActionTask.cs" /> | |
|
135 | <Compile Include="ActionTaskT.cs" /> | |
|
136 | <Compile Include="ICancellationToken.cs" /> | |
|
137 | <Compile Include="SuccessPromise.cs" /> | |
|
138 | <Compile Include="SuccessPromiseT.cs" /> | |
|
139 | <Compile Include="PromiseAwaiterT.cs" /> | |
|
140 | <Compile Include="PromiseAwaiter.cs" /> | |
|
141 | <Compile Include="Components\ComponentContainer.cs" /> | |
|
142 | <Compile Include="Components\Disposable.cs" /> | |
|
143 | <Compile Include="Components\DisposablePool.cs" /> | |
|
144 | <Compile Include="Components\ObjectPool.cs" /> | |
|
145 | <Compile Include="Components\ServiceLocator.cs" /> | |
|
146 | <Compile Include="Components\IInitializable.cs" /> | |
|
147 | <Compile Include="TaskController.cs" /> | |
|
148 | <Compile Include="Components\App.cs" /> | |
|
149 | <Compile Include="Components\IRunnable.cs" /> | |
|
150 | <Compile Include="Components\ExecutionState.cs" /> | |
|
151 | <Compile Include="Components\RunnableComponent.cs" /> | |
|
152 | <Compile Include="Components\IFactory.cs" /> | |
|
153 | <Compile Include="Automaton\IAlphabet.cs" /> | |
|
154 | <Compile Include="Automaton\ParserException.cs" /> | |
|
155 | <Compile Include="Automaton\IndexedAlphabetBase.cs" /> | |
|
156 | <Compile Include="Automaton\IAlphabetBuilder.cs" /> | |
|
157 | <Compile Include="Automaton\RegularExpressions\AltToken.cs" /> | |
|
158 | <Compile Include="Automaton\RegularExpressions\BinaryToken.cs" /> | |
|
159 | <Compile Include="Automaton\RegularExpressions\CatToken.cs" /> | |
|
160 | <Compile Include="Automaton\RegularExpressions\StarToken.cs" /> | |
|
161 | <Compile Include="Automaton\RegularExpressions\SymbolToken.cs" /> | |
|
162 | <Compile Include="Automaton\RegularExpressions\EmptyToken.cs" /> | |
|
163 | <Compile Include="Automaton\RegularExpressions\Token.cs" /> | |
|
164 | <Compile Include="Automaton\RegularExpressions\IVisitor.cs" /> | |
|
165 | <Compile Include="Automaton\AutomatonTransition.cs" /> | |
|
166 | <Compile Include="Formats\JSON\JSONElementContext.cs" /> | |
|
167 | <Compile Include="Formats\JSON\JSONElementType.cs" /> | |
|
168 | <Compile Include="Formats\JSON\JSONGrammar.cs" /> | |
|
169 | <Compile Include="Formats\JSON\JSONParser.cs" /> | |
|
170 | <Compile Include="Formats\JSON\JSONScanner.cs" /> | |
|
171 | <Compile Include="Formats\JSON\JsonTokenType.cs" /> | |
|
172 | <Compile Include="Formats\JSON\JSONWriter.cs" /> | |
|
173 | <Compile Include="Formats\JSON\JSONXmlReader.cs" /> | |
|
174 | <Compile Include="Formats\JSON\JSONXmlReaderOptions.cs" /> | |
|
175 | <Compile Include="Formats\JSON\StringTranslator.cs" /> | |
|
176 | <Compile Include="Automaton\MapAlphabet.cs" /> | |
|
177 | <Compile Include="Formats\CharAlphabet.cs" /> | |
|
178 | <Compile Include="Formats\ByteAlphabet.cs" /> | |
|
179 | <Compile Include="Automaton\IDFATable.cs" /> | |
|
180 | <Compile Include="Automaton\IDFATableBuilder.cs" /> | |
|
181 | <Compile Include="Automaton\DFATable.cs" /> | |
|
182 | <Compile Include="Automaton\RegularExpressions\RegularExpressionVisitor.cs" /> | |
|
183 | <Compile Include="Automaton\RegularExpressions\ITaggedDFABuilder.cs" /> | |
|
184 | <Compile Include="Formats\TextScanner.cs" /> | |
|
185 | <Compile Include="Formats\StringScanner.cs" /> | |
|
186 | <Compile Include="Formats\ReaderScanner.cs" /> | |
|
187 | <Compile Include="Formats\ScannerContext.cs" /> | |
|
188 | <Compile Include="Formats\Grammar.cs" /> | |
|
189 | <Compile Include="Automaton\RegularExpressions\EndTokenT.cs" /> | |
|
190 | <Compile Include="Automaton\RegularExpressions\EndToken.cs" /> | |
|
191 | <Compile Include="Automaton\RegularExpressions\RegularExpressionVisitorT.cs" /> | |
|
192 | <Compile Include="Automaton\AutomatonConst.cs" /> | |
|
193 | <Compile Include="Automaton\RegularExpressions\RegularDFA.cs" /> | |
|
194 | <Compile Include="Components\LazyAndWeak.cs" /> | |
|
195 | <Compile Include="AbstractTask.cs" /> | |
|
196 | <Compile Include="AbstractTaskT.cs" /> | |
|
23 | <EmbeddedResource Include="src\Xml\json.xsl"/> | |
|
197 | 24 | </ItemGroup> |
|
198 | <Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" /> | |
|
199 | <ItemGroup /> | |
|
200 | <ProjectExtensions> | |
|
201 | <MonoDevelop> | |
|
202 | <Properties> | |
|
203 | <Policies> | |
|
204 | <CSharpFormattingPolicy IndentBlock="True" IndentBraces="False" IndentSwitchSection="False" IndentSwitchCaseSection="True" LabelPositioning="OneLess" NewLinesForBracesInTypes="True" NewLinesForBracesInMethods="True" NewLinesForBracesInProperties="False" NewLinesForBracesInAccessors="False" NewLinesForBracesInAnonymousMethods="False" NewLinesForBracesInControlBlocks="False" NewLinesForBracesInAnonymousTypes="False" NewLinesForBracesInObjectCollectionArrayInitializers="False" NewLinesForBracesInLambdaExpressionBody="False" NewLineForElse="False" NewLineForCatch="False" NewLineForFinally="False" NewLineForMembersInObjectInit="False" NewLineForMembersInAnonymousTypes="False" NewLineForClausesInQuery="False" SpacingAfterMethodDeclarationName="True" SpaceWithinMethodDeclarationParenthesis="False" SpaceBetweenEmptyMethodDeclarationParentheses="False" SpaceAfterMethodCallName="True" SpaceWithinMethodCallParentheses="False" SpaceBetweenEmptyMethodCallParentheses="False" SpaceAfterControlFlowStatementKeyword="True" SpaceWithinExpressionParentheses="False" SpaceWithinCastParentheses="False" SpaceWithinOtherParentheses="False" SpaceAfterCast="False" SpacesIgnoreAroundVariableDeclaration="False" SpaceBeforeOpenSquareBracket="True" SpaceBetweenEmptySquareBrackets="False" SpaceWithinSquareBrackets="False" SpaceAfterColonInBaseTypeDeclaration="True" SpaceAfterComma="True" SpaceAfterDot="False" SpaceAfterSemicolonsInForStatement="True" SpaceBeforeColonInBaseTypeDeclaration="True" SpaceBeforeComma="False" SpaceBeforeDot="False" SpaceBeforeSemicolonsInForStatement="False" SpacingAroundBinaryOperator="Single" WrappingPreserveSingleLine="True" WrappingKeepStatementsOnSingleLine="True" PlaceSystemDirectiveFirst="True" scope="text/x-csharp" /> | |
|
205 | <TextStylePolicy FileWidth="120" TabWidth="4" IndentWidth="4" RemoveTrailingWhitespace="True" NoTabsAfterNonTabs="False" TabsToSpaces="True" EolMarker="Unix" scope="text/x-csharp" /> | |
|
206 | <DotNetNamingPolicy DirectoryNamespaceAssociation="PrefixedHierarchical" ResourceNamePolicy="MSBuild" /> | |
|
207 | <TextStylePolicy FileWidth="120" TabWidth="4" TabsToSpaces="False" IndentWidth="4" RemoveTrailingWhitespace="True" NoTabsAfterNonTabs="False" EolMarker="Native" scope="application/xml" /> | |
|
208 | <XmlFormattingPolicy scope="application/xml"> | |
|
209 | <DefaultFormat OmitXmlDeclaration="False" NewLineChars="
" IndentContent="True" ContentIndentString="	" AttributesInNewLine="False" MaxAttributesPerLine="10" AttributesIndentString="	" WrapAttributes="False" AlignAttributes="False" AlignAttributeValues="False" QuoteChar=""" SpacesBeforeAssignment="0" SpacesAfterAssignment="0" EmptyLinesBeforeStart="0" EmptyLinesAfterStart="0" EmptyLinesBeforeEnd="0" EmptyLinesAfterEnd="0" /> | |
|
210 | </XmlFormattingPolicy> | |
|
211 | <TextStylePolicy FileWidth="120" TabWidth="4" TabsToSpaces="False" IndentWidth="4" RemoveTrailingWhitespace="True" NoTabsAfterNonTabs="False" EolMarker="Native" scope="text/plain" /> | |
|
212 | <NameConventionPolicy> | |
|
213 | <Rules> | |
|
214 | <NamingRule Name="Namespaces" AffectedEntity="Namespace" VisibilityMask="VisibilityMask" NamingStyle="PascalCase" IncludeInstanceMembers="True" IncludeStaticEntities="True" /> | |
|
215 | <NamingRule Name="Types" AffectedEntity="Class, Struct, Enum, Delegate" VisibilityMask="VisibilityMask" NamingStyle="PascalCase" IncludeInstanceMembers="True" IncludeStaticEntities="True" /> | |
|
216 | <NamingRule Name="Interfaces" AffectedEntity="Interface" VisibilityMask="VisibilityMask" NamingStyle="PascalCase" IncludeInstanceMembers="True" IncludeStaticEntities="True"> | |
|
217 | <RequiredPrefixes> | |
|
218 | <String>I</String> | |
|
219 | </RequiredPrefixes> | |
|
220 | </NamingRule> | |
|
221 | <NamingRule Name="Attributes" AffectedEntity="CustomAttributes" VisibilityMask="VisibilityMask" NamingStyle="PascalCase" IncludeInstanceMembers="True" IncludeStaticEntities="True"> | |
|
222 | <RequiredSuffixes> | |
|
223 | <String>Attribute</String> | |
|
224 | </RequiredSuffixes> | |
|
225 | </NamingRule> | |
|
226 | <NamingRule Name="Event Arguments" AffectedEntity="CustomEventArgs" VisibilityMask="VisibilityMask" NamingStyle="PascalCase" IncludeInstanceMembers="True" IncludeStaticEntities="True"> | |
|
227 | <RequiredSuffixes> | |
|
228 | <String>EventArgs</String> | |
|
229 | </RequiredSuffixes> | |
|
230 | </NamingRule> | |
|
231 | <NamingRule Name="Exceptions" AffectedEntity="CustomExceptions" VisibilityMask="VisibilityMask" NamingStyle="PascalCase" IncludeInstanceMembers="True" IncludeStaticEntities="True"> | |
|
232 | <RequiredSuffixes> | |
|
233 | <String>Exception</String> | |
|
234 | </RequiredSuffixes> | |
|
235 | </NamingRule> | |
|
236 | <NamingRule Name="Methods" AffectedEntity="Methods" VisibilityMask="VisibilityMask" NamingStyle="PascalCase" IncludeInstanceMembers="True" IncludeStaticEntities="True" /> | |
|
237 | <NamingRule Name="Static Readonly Fields" AffectedEntity="ReadonlyField" VisibilityMask="Internal, Protected, Public" NamingStyle="CamelCase" IncludeInstanceMembers="False" IncludeStaticEntities="True" /> | |
|
238 | <NamingRule Name="Fields (Non Private)" AffectedEntity="Field" VisibilityMask="Internal, Public" NamingStyle="CamelCase" IncludeInstanceMembers="True" IncludeStaticEntities="True" /> | |
|
239 | <NamingRule Name="ReadOnly Fields (Non Private)" AffectedEntity="ReadonlyField" VisibilityMask="Internal, Public" NamingStyle="CamelCase" IncludeInstanceMembers="True" IncludeStaticEntities="False" /> | |
|
240 | <NamingRule Name="Fields (Private)" AffectedEntity="Field, ReadonlyField" VisibilityMask="Private, Protected" NamingStyle="CamelCase" IncludeInstanceMembers="True" IncludeStaticEntities="False"> | |
|
241 | <RequiredPrefixes> | |
|
242 | <String>m_</String> | |
|
243 | </RequiredPrefixes> | |
|
244 | </NamingRule> | |
|
245 | <NamingRule Name="Static Fields (Private)" AffectedEntity="Field" VisibilityMask="Private" NamingStyle="CamelCase" IncludeInstanceMembers="False" IncludeStaticEntities="True"> | |
|
246 | <RequiredPrefixes> | |
|
247 | <String>_</String> | |
|
248 | </RequiredPrefixes> | |
|
249 | </NamingRule> | |
|
250 | <NamingRule Name="ReadOnly Fields (Private)" AffectedEntity="ReadonlyField" VisibilityMask="Private, Protected" NamingStyle="CamelCase" IncludeInstanceMembers="True" IncludeStaticEntities="False"> | |
|
251 | <RequiredPrefixes> | |
|
252 | <String>m_</String> | |
|
253 | </RequiredPrefixes> | |
|
254 | </NamingRule> | |
|
255 | <NamingRule Name="Constant Fields" AffectedEntity="ConstantField" VisibilityMask="VisibilityMask" NamingStyle="AllUpper" IncludeInstanceMembers="True" IncludeStaticEntities="True" /> | |
|
256 | <NamingRule Name="Properties" AffectedEntity="Property" VisibilityMask="VisibilityMask" NamingStyle="PascalCase" IncludeInstanceMembers="True" IncludeStaticEntities="True" /> | |
|
257 | <NamingRule Name="Events" AffectedEntity="Event" VisibilityMask="VisibilityMask" NamingStyle="PascalCase" IncludeInstanceMembers="True" IncludeStaticEntities="True" /> | |
|
258 | <NamingRule Name="Enum Members" AffectedEntity="EnumMember" VisibilityMask="VisibilityMask" NamingStyle="PascalCase" IncludeInstanceMembers="True" IncludeStaticEntities="True" /> | |
|
259 | <NamingRule Name="Parameters" AffectedEntity="Parameter, LocalVariable" VisibilityMask="VisibilityMask" NamingStyle="CamelCase" IncludeInstanceMembers="True" IncludeStaticEntities="True" /> | |
|
260 | <NamingRule Name="Type Parameters" AffectedEntity="TypeParameter" VisibilityMask="VisibilityMask" NamingStyle="PascalCase" IncludeInstanceMembers="True" IncludeStaticEntities="True"> | |
|
261 | <RequiredPrefixes> | |
|
262 | <String>T</String> | |
|
263 | </RequiredPrefixes> | |
|
264 | </NamingRule> | |
|
265 | </Rules> | |
|
266 | </NameConventionPolicy> | |
|
267 | </Policies> | |
|
268 | </Properties> | |
|
269 | </MonoDevelop> | |
|
270 | </ProjectExtensions> | |
|
271 | <ItemGroup> | |
|
272 | <Folder Include="Components\" /> | |
|
273 | <Folder Include="Automaton\RegularExpressions\" /> | |
|
274 | <Folder Include="Formats\" /> | |
|
275 | <Folder Include="Formats\JSON\" /> | |
|
276 | </ItemGroup> | |
|
277 | </Project> No newline at end of file | |
|
25 | ||
|
26 | </Project> |
@@ -1,300 +1,130 | |||
|
1 | 1 | using System; |
|
2 | 2 | using Implab.Parallels; |
|
3 | 3 | using System.Threading; |
|
4 | 4 | using System.Reflection; |
|
5 | using System.Diagnostics; | |
|
5 | 6 | |
|
6 | 7 | namespace Implab { |
|
7 | public abstract class AbstractEvent<THandler> : ICancellationToken, ICancellable { | |
|
8 | ||
|
9 | const int UNRESOLVED_SATE = 0; | |
|
10 | const int TRANSITIONAL_STATE = 1; | |
|
11 | protected const int SUCCEEDED_STATE = 2; | |
|
12 | protected const int REJECTED_STATE = 3; | |
|
13 | protected const int CANCELLED_STATE = 4; | |
|
14 | ||
|
15 | const int CANCEL_NOT_REQUESTED = 0; | |
|
16 | const int CANCEL_REQUESTING = 1; | |
|
17 | const int CANCEL_REQUESTED = 2; | |
|
18 | ||
|
19 | const int RESERVED_HANDLERS_COUNT = 4; | |
|
20 | ||
|
21 | int m_state; | |
|
22 | Exception m_error; | |
|
23 | int m_handlersCount; | |
|
8 | /// <summary> | |
|
9 | /// Abstract class for creation of custom one-shot thread safe events. | |
|
10 | /// </summary> | |
|
11 | /// <remarks> | |
|
12 | /// <para> | |
|
13 | /// An event is something that should happen in the future and the | |
|
14 | /// triggering of the event causes execution of some pending actions | |
|
15 | /// which are formely event handlers. One-shot events occur only once | |
|
16 | /// and any handler added after the event is triggered should run | |
|
17 | /// without a delay. | |
|
18 | /// </para> | |
|
19 | /// <para> | |
|
20 | /// The lifecycle of the one-shot event is tipically consists of following | |
|
21 | /// phases. | |
|
22 | /// <list> | |
|
23 | /// <description>Pending state. This is the initial state of the event. Any | |
|
24 | /// handler added to the event will be queued for the future execution. | |
|
25 | /// </description> | |
|
26 | /// <description>Transitional state. This is intermediate state between pending | |
|
27 | /// and fulfilled states, during this state internal initialization and storing | |
|
28 | /// of the result occurs. | |
|
29 | /// </description> | |
|
30 | /// <description>Fulfilled state. The event contains the result, all queued | |
|
31 | /// handlers are signalled to run and newly added handlers are executed | |
|
32 | /// immediatelly. | |
|
33 | /// </description> | |
|
34 | /// </list> | |
|
35 | /// </para> | |
|
36 | /// </remarks> | |
|
37 | public abstract class AbstractEvent<THandler> where THandler : class { | |
|
38 | const int PendingState = 0; | |
|
24 | 39 | |
|
25 | //readonly THandler[] m_handlers = new THandler[RESERVED_HANDLERS_COUNT]; | |
|
26 | THandler[] m_handlers; | |
|
27 | MTQueue<THandler> m_extraHandlers; | |
|
28 | int m_handlerPointer = -1; | |
|
29 | int m_handlersCommited; | |
|
40 | const int TransitionalState = 1; | |
|
30 | 41 | |
|
31 | int m_cancelRequest; | |
|
32 | Exception m_cancelationReason; | |
|
33 | MTQueue<Action<Exception>> m_cancelationHandlers; | |
|
42 | const int ResolvedState = 2; | |
|
34 | 43 | |
|
44 | volatile int m_state; | |
|
35 | 45 | |
|
36 | #region state managment | |
|
37 | bool BeginTransit() { | |
|
38 | return UNRESOLVED_SATE == Interlocked.CompareExchange(ref m_state, TRANSITIONAL_STATE, UNRESOLVED_SATE); | |
|
39 | } | |
|
46 | THandler m_handler; | |
|
47 | SimpleAsyncQueue<THandler> m_extraHandlers; | |
|
40 | 48 | |
|
41 | void CompleteTransit(int state) { | |
|
42 | if (TRANSITIONAL_STATE != Interlocked.CompareExchange(ref m_state, state, TRANSITIONAL_STATE)) | |
|
43 | throw new InvalidOperationException("Can't complete transition when the object isn't in the transitional state"); | |
|
44 | } | |
|
45 | ||
|
46 | void WaitTransition() { | |
|
47 | while (m_state == TRANSITIONAL_STATE) { | |
|
48 | Thread.MemoryBarrier(); | |
|
49 | public bool IsResolved { | |
|
50 | get { | |
|
51 | return m_state > TransitionalState; | |
|
49 | 52 | } |
|
50 | 53 | } |
|
51 | 54 | |
|
52 | protected bool BeginSetResult() { | |
|
53 |
|
|
|
54 | WaitTransition(); | |
|
55 | if (m_state != CANCELLED_STATE) | |
|
56 | throw new InvalidOperationException("The promise is already resolved"); | |
|
57 | return false; | |
|
58 | } | |
|
59 | return true; | |
|
55 | #region state managment | |
|
56 | protected bool BeginTransit() { | |
|
57 | return PendingState == Interlocked.CompareExchange(ref m_state, TransitionalState, PendingState); | |
|
60 | 58 | } |
|
61 | 59 | |
|
62 |
protected void |
|
|
63 | CompleteTransit(SUCCEEDED_STATE); | |
|
60 | protected void CompleteTransit() { | |
|
61 | #if DEBUG | |
|
62 | if (TransitionalState != Interlocked.CompareExchange(ref m_state, ResolvedState, TransitionalState)) | |
|
63 | throw new InvalidOperationException("Can't complete transition when the object isn't in the transitional state"); | |
|
64 | #else | |
|
65 | m_state = ResolvedState; | |
|
66 | #endif | |
|
64 | 67 | Signal(); |
|
65 | 68 | } |
|
66 | 69 | |
|
70 | protected void WaitTransition() { | |
|
71 | if (m_state == TransitionalState) { | |
|
72 | SpinWait spin = new SpinWait(); | |
|
73 | do { | |
|
74 | spin.SpinOnce(); | |
|
75 | } while (m_state == TransitionalState); | |
|
76 | } | |
|
77 | } | |
|
78 | ||
|
67 | 79 | |
|
68 | ||
|
69 | /// <summary> | |
|
70 | /// Выполняет обещание, сообщая об ошибке | |
|
71 | /// </summary> | |
|
72 | /// <remarks> | |
|
73 | /// Поскольку обещание должно работать в многопточной среде, при его выполнении сразу несколько потоков | |
|
74 | /// могу вернуть ошибку, при этом только первая будет использована в качестве результата, остальные | |
|
75 | /// будут проигнорированы. | |
|
76 | /// </remarks> | |
|
77 | /// <param name="error">Исключение возникшее при выполнении операции</param> | |
|
78 | /// <exception cref="InvalidOperationException">Данное обещание уже выполнено</exception> | |
|
79 | protected void SetError(Exception error) { | |
|
80 | if (BeginTransit()) { | |
|
81 | m_error = error; | |
|
82 | CompleteTransit(REJECTED_STATE); | |
|
83 | ||
|
84 | Signal(); | |
|
85 | } else { | |
|
86 | WaitTransition(); | |
|
87 | if (m_state == SUCCEEDED_STATE) | |
|
88 | throw new InvalidOperationException("The promise is already resolved"); | |
|
89 | } | |
|
90 | } | |
|
91 | ||
|
92 | /// <summary> | |
|
93 | /// Отменяет операцию, если это возможно. | |
|
94 | /// </summary> | |
|
95 | /// <remarks>Для определения была ли операция отменена следует использовать свойство <see cref="IsCancelled"/>.</remarks> | |
|
96 | protected void SetCancelled(Exception reason) { | |
|
97 | if (BeginTransit()) { | |
|
98 | m_error = reason; | |
|
99 | CompleteTransit(CANCELLED_STATE); | |
|
100 | Signal(); | |
|
101 | } | |
|
102 | } | |
|
103 | ||
|
104 | protected abstract void SignalHandler(THandler handler, int signal); | |
|
80 | protected abstract void SignalHandler(THandler handler); | |
|
105 | 81 | |
|
106 | 82 | void Signal() { |
|
107 |
|
|
|
108 | var slot = hp +1 ; | |
|
109 | while (slot < m_handlersCommited) { | |
|
110 | if (Interlocked.CompareExchange(ref m_handlerPointer, slot, hp) == hp) { | |
|
111 | SignalHandler(m_handlers[slot], m_state); | |
|
112 | } | |
|
113 | hp = m_handlerPointer; | |
|
114 | slot = hp +1 ; | |
|
115 | } | |
|
116 | ||
|
117 | ||
|
118 | if (m_extraHandlers != null) { | |
|
119 | THandler handler; | |
|
120 | while (m_extraHandlers.TryDequeue(out handler)) | |
|
121 | SignalHandler(handler, m_state); | |
|
122 | } | |
|
83 | THandler handler; | |
|
84 | while (TryDequeueHandler(out handler)) | |
|
85 | SignalHandler(handler); | |
|
123 | 86 | } |
|
124 | 87 | |
|
125 | 88 | #endregion |
|
126 | 89 | |
|
127 | protected abstract Signal GetResolveSignal(); | |
|
128 | ||
|
129 | #region synchronization traits | |
|
130 | protected void WaitResult(int timeout) { | |
|
131 | if (!(IsResolved || GetResolveSignal().Wait(timeout))) | |
|
132 | throw new TimeoutException(); | |
|
133 | ||
|
134 | switch (m_state) { | |
|
135 | case SUCCEEDED_STATE: | |
|
136 | return; | |
|
137 | case CANCELLED_STATE: | |
|
138 | throw new OperationCanceledException("The operation has been cancelled", m_error); | |
|
139 | case REJECTED_STATE: | |
|
140 | throw new TargetInvocationException(m_error); | |
|
141 | default: | |
|
142 | throw new ApplicationException(String.Format("The promise state {0} is invalid", m_state)); | |
|
143 | } | |
|
144 | } | |
|
145 | #endregion | |
|
146 | ||
|
147 | 90 | #region handlers managment |
|
148 | 91 | |
|
149 | 92 | protected void AddHandler(THandler handler) { |
|
150 | 93 | |
|
151 |
if ( |
|
|
94 | if (IsResolved) { | |
|
152 | 95 | // the promise is in the resolved state, just invoke the handler |
|
153 |
SignalHandler(handler |
|
|
96 | SignalHandler(handler); | |
|
154 | 97 | } else { |
|
155 | var slot = Interlocked.Increment(ref m_handlersCount) - 1; | |
|
156 | ||
|
157 | if (slot < RESERVED_HANDLERS_COUNT) { | |
|
158 | ||
|
159 | if (slot == 0) { | |
|
160 | m_handlers = new THandler[RESERVED_HANDLERS_COUNT]; | |
|
161 | } else { | |
|
162 | while (m_handlers == null) | |
|
163 | Thread.MemoryBarrier(); | |
|
164 | } | |
|
165 | ||
|
166 | m_handlers[slot] = handler; | |
|
167 | ||
|
168 | while (slot != Interlocked.CompareExchange(ref m_handlersCommited, slot + 1, slot)) { | |
|
169 | } | |
|
98 | EnqueueHandler(handler); | |
|
170 | 99 | |
|
171 | if (m_state > 1) { | |
|
172 | do { | |
|
173 | var hp = m_handlerPointer; | |
|
174 | slot = hp + 1; | |
|
175 | if (slot < m_handlersCommited) { | |
|
176 | if (Interlocked.CompareExchange(ref m_handlerPointer, slot, hp) != hp) | |
|
177 | continue; | |
|
178 | SignalHandler(m_handlers[slot], m_state); | |
|
179 | } | |
|
180 | break; | |
|
181 | } while(true); | |
|
182 | } | |
|
183 | } else { | |
|
184 | if (slot == RESERVED_HANDLERS_COUNT) { | |
|
185 | m_extraHandlers = new MTQueue<THandler>(); | |
|
186 | } else { | |
|
187 | while (m_extraHandlers == null) | |
|
188 | Thread.MemoryBarrier(); | |
|
189 | } | |
|
190 | ||
|
191 | m_extraHandlers.Enqueue(handler); | |
|
192 | ||
|
193 | if (m_state > 1 && m_extraHandlers.TryDequeue(out handler)) | |
|
100 | if (IsResolved && TryDequeueHandler(out handler)) | |
|
194 | 101 | // if the promise have been resolved while we was adding the handler to the queue |
|
195 | 102 | // we can't guarantee that someone is still processing it |
|
196 | 103 | // therefore we need to fetch a handler from the queue and execute it |
|
197 | 104 | // note that fetched handler may be not the one that we have added |
|
198 | 105 | // even we can fetch no handlers at all :) |
|
199 |
|
|
|
200 | } | |
|
106 | SignalHandler(handler); | |
|
201 | 107 | } |
|
108 | ||
|
202 | 109 | } |
|
203 | 110 | |
|
204 | #endregion | |
|
205 | ||
|
206 | #region IPromise implementation | |
|
207 | ||
|
208 | public bool IsResolved { | |
|
209 | get { | |
|
210 | Thread.MemoryBarrier(); | |
|
211 | return m_state > 1; | |
|
212 | } | |
|
213 | } | |
|
214 | ||
|
215 | public bool IsCancelled { | |
|
216 | get { | |
|
217 | Thread.MemoryBarrier(); | |
|
218 | return m_state == CANCELLED_STATE; | |
|
219 | } | |
|
220 | } | |
|
221 | ||
|
222 | #endregion | |
|
223 | ||
|
224 | public Exception Error { | |
|
225 | get { | |
|
226 | return m_error; | |
|
111 | void EnqueueHandler(THandler handler) { | |
|
112 | if (Interlocked.CompareExchange(ref m_handler, handler, null) != null) { | |
|
113 | if (m_extraHandlers == null) | |
|
114 | // compare-exchange will protect from loosing already created queue | |
|
115 | Interlocked.CompareExchange(ref m_extraHandlers, new SimpleAsyncQueue<THandler>(), null); | |
|
116 | m_extraHandlers.Enqueue(handler); | |
|
227 | 117 | } |
|
228 | 118 | } |
|
229 | 119 | |
|
230 | public bool CancelOperationIfRequested() { | |
|
231 | if (IsCancellationRequested) { | |
|
232 | CancelOperation(CancellationReason); | |
|
120 | bool TryDequeueHandler(out THandler handler) { | |
|
121 | handler = Interlocked.Exchange(ref m_handler, null); | |
|
122 | if (handler != null) | |
|
233 | 123 | return true; |
|
234 | } | |
|
235 | return false; | |
|
236 | } | |
|
237 | ||
|
238 | public virtual void CancelOperation(Exception reason) { | |
|
239 | SetCancelled(reason); | |
|
240 | } | |
|
241 | ||
|
242 | public void CancellationRequested(Action<Exception> handler) { | |
|
243 | Safe.ArgumentNotNull(handler, "handler"); | |
|
244 | if (IsCancellationRequested) | |
|
245 | handler(CancellationReason); | |
|
246 | ||
|
247 | if (m_cancelationHandlers == null) | |
|
248 | Interlocked.CompareExchange(ref m_cancelationHandlers, new MTQueue<Action<Exception>>(), null); | |
|
249 | ||
|
250 | m_cancelationHandlers.Enqueue(handler); | |
|
251 | ||
|
252 | if (IsCancellationRequested && m_cancelationHandlers.TryDequeue(out handler)) | |
|
253 | // TryDeque implies MemoryBarrier() | |
|
254 | handler(m_cancelationReason); | |
|
255 | } | |
|
256 | ||
|
257 | public bool IsCancellationRequested { | |
|
258 | get { | |
|
259 | do { | |
|
260 | if (m_cancelRequest == CANCEL_NOT_REQUESTED) | |
|
261 | return false; | |
|
262 | if (m_cancelRequest == CANCEL_REQUESTED) | |
|
263 | return true; | |
|
264 | Thread.MemoryBarrier(); | |
|
265 | } while(true); | |
|
266 | } | |
|
267 | } | |
|
268 | ||
|
269 | public Exception CancellationReason { | |
|
270 | get { | |
|
271 | do { | |
|
272 | Thread.MemoryBarrier(); | |
|
273 | } while(m_cancelRequest == CANCEL_REQUESTING); | |
|
274 | ||
|
275 | return m_cancelationReason; | |
|
276 | } | |
|
277 | } | |
|
278 | ||
|
279 | #region ICancellable implementation | |
|
280 | ||
|
281 | public void Cancel() { | |
|
282 | Cancel(null); | |
|
283 | } | |
|
284 | ||
|
285 | public void Cancel(Exception reason) { | |
|
286 | if (CANCEL_NOT_REQUESTED == Interlocked.CompareExchange(ref m_cancelRequest, CANCEL_REQUESTING, CANCEL_NOT_REQUESTED)) { | |
|
287 | m_cancelationReason = reason; | |
|
288 | m_cancelRequest = CANCEL_REQUESTED; | |
|
289 | if (m_cancelationHandlers != null) { | |
|
290 | Action<Exception> handler; | |
|
291 | while (m_cancelationHandlers.TryDequeue(out handler)) | |
|
292 | handler(m_cancelationReason); | |
|
293 | } | |
|
294 | } | |
|
124 | return m_extraHandlers != null && m_extraHandlers.TryDequeue(out handler); | |
|
295 | 125 | } |
|
296 | 126 | |
|
297 | 127 | #endregion |
|
298 | 128 | } |
|
299 | 129 | } |
|
300 | 130 |
@@ -1,9 +1,9 | |||
|
1 | 1 | |
|
2 | 2 | namespace Implab.Automaton { |
|
3 | 3 | public static class AutomatonConst { |
|
4 |
public const int U |
|
|
4 | public const int UnreachableState = -1; | |
|
5 | 5 | |
|
6 |
public const int U |
|
|
6 | public const int UnclassifiedInput = 0; | |
|
7 | 7 | } |
|
8 | 8 | } |
|
9 | 9 |
@@ -1,33 +1,41 | |||
|
1 | 1 | using System; |
|
2 | 2 | |
|
3 | 3 | namespace Implab.Automaton { |
|
4 |
public |
|
|
4 | public class AutomatonTransition : IEquatable<AutomatonTransition> { | |
|
5 | 5 | public readonly int s1; |
|
6 | 6 | public readonly int s2; |
|
7 | 7 | public readonly int edge; |
|
8 | 8 | |
|
9 | 9 | public AutomatonTransition(int s1, int s2, int edge) { |
|
10 | 10 | this.s1 = s1; |
|
11 | 11 | this.s2 = s2; |
|
12 | 12 | this.edge = edge; |
|
13 | 13 | } |
|
14 | 14 | |
|
15 | 15 | |
|
16 | 16 | #region IEquatable implementation |
|
17 | 17 | public bool Equals(AutomatonTransition other) { |
|
18 | 18 | return other.s1 == s1 && other.s2 == s2 && other.edge == edge ; |
|
19 | 19 | } |
|
20 | 20 | #endregion |
|
21 | 21 | |
|
22 | 22 | public override bool Equals(object obj) { |
|
23 | 23 | if (obj is AutomatonTransition) |
|
24 | 24 | return Equals((AutomatonTransition)obj); |
|
25 | 25 | return base.Equals(obj); |
|
26 | 26 | } |
|
27 | 27 | |
|
28 | 28 | public override int GetHashCode() { |
|
29 | 29 | return s1 + s2 + edge; |
|
30 | 30 | } |
|
31 | ||
|
32 | public static bool operator == (AutomatonTransition rv, AutomatonTransition lv) { | |
|
33 | return rv.Equals(lv); | |
|
34 | } | |
|
35 | ||
|
36 | public static bool operator !=(AutomatonTransition rv, AutomatonTransition lv) { | |
|
37 | return rv.Equals(lv); | |
|
38 | } | |
|
31 | 39 | } |
|
32 | 40 | } |
|
33 | 41 |
@@ -1,348 +1,348 | |||
|
1 | 1 | using Implab; |
|
2 | 2 | using System; |
|
3 | 3 | using System.Collections.Generic; |
|
4 | 4 | using System.Linq; |
|
5 | 5 | using System.Diagnostics; |
|
6 | 6 | using System.IO; |
|
7 | 7 | using System.CodeDom.Compiler; |
|
8 | 8 | using System.CodeDom; |
|
9 | 9 | |
|
10 | 10 | namespace Implab.Automaton { |
|
11 | 11 | public class DFATable : IDFATableBuilder { |
|
12 | 12 | int m_stateCount; |
|
13 | 13 | int m_symbolCount; |
|
14 | 14 | int m_initialState; |
|
15 | 15 | |
|
16 | 16 | readonly HashSet<int> m_finalStates = new HashSet<int>(); |
|
17 | 17 | readonly HashSet<AutomatonTransition> m_transitions = new HashSet<AutomatonTransition>(); |
|
18 | 18 | |
|
19 | 19 | |
|
20 | 20 | #region IDFADefinition implementation |
|
21 | 21 | |
|
22 | 22 | public bool IsFinalState(int s) { |
|
23 |
Safe.ArgumentInRange(s |
|
|
23 | Safe.ArgumentInRange(s >= 0 && s < m_stateCount, nameof(s)); | |
|
24 | 24 | |
|
25 | 25 | return m_finalStates.Contains(s); |
|
26 | 26 | } |
|
27 | 27 | |
|
28 | 28 | public IEnumerable<int> FinalStates { |
|
29 | 29 | get { |
|
30 | 30 | return m_finalStates; |
|
31 | 31 | } |
|
32 | 32 | } |
|
33 | 33 | |
|
34 | 34 | public int StateCount { |
|
35 | 35 | get { return m_stateCount; } |
|
36 | 36 | } |
|
37 | 37 | |
|
38 | 38 | public int AlphabetSize { |
|
39 | 39 | get { return m_symbolCount; } |
|
40 | 40 | } |
|
41 | 41 | |
|
42 | 42 | public int InitialState { |
|
43 | 43 | get { return m_initialState; } |
|
44 | 44 | } |
|
45 | 45 | |
|
46 | 46 | #endregion |
|
47 | 47 | |
|
48 | 48 | public void SetInitialState(int s) { |
|
49 |
Safe.Argument |
|
|
49 | Safe.ArgumentInRange(s >= 0, nameof(s)); | |
|
50 | 50 | m_stateCount = Math.Max(m_stateCount, s + 1); |
|
51 | 51 | m_initialState = s; |
|
52 | 52 | } |
|
53 | 53 | |
|
54 | 54 | public void MarkFinalState(int state) { |
|
55 | 55 | m_stateCount = Math.Max(m_stateCount, state + 1); |
|
56 | 56 | m_finalStates.Add(state); |
|
57 | 57 | } |
|
58 | 58 | |
|
59 | 59 | public void Add(AutomatonTransition item) { |
|
60 |
Safe.ArgumentAssert(item.s1 >= 0, |
|
|
61 |
Safe.ArgumentAssert(item.s2 >= 0, |
|
|
62 |
Safe.ArgumentAssert(item.edge >= 0, |
|
|
60 | Safe.ArgumentAssert(item.s1 >= 0, nameof(item)); | |
|
61 | Safe.ArgumentAssert(item.s2 >= 0, nameof(item)); | |
|
62 | Safe.ArgumentAssert(item.edge >= 0, nameof(item)); | |
|
63 | 63 | |
|
64 | 64 | m_stateCount = Math.Max(m_stateCount, Math.Max(item.s1, item.s2) + 1); |
|
65 | 65 | m_symbolCount = Math.Max(m_symbolCount, item.edge + 1); |
|
66 | 66 | |
|
67 | 67 | m_transitions.Add(item); |
|
68 | 68 | } |
|
69 | 69 | |
|
70 | 70 | public void Clear() { |
|
71 | 71 | m_stateCount = 0; |
|
72 | 72 | m_symbolCount = 0; |
|
73 | 73 | m_finalStates.Clear(); |
|
74 | 74 | m_transitions.Clear(); |
|
75 | 75 | } |
|
76 | 76 | |
|
77 | 77 | public bool Contains(AutomatonTransition item) { |
|
78 | 78 | return m_transitions.Contains(item); |
|
79 | 79 | } |
|
80 | 80 | |
|
81 | 81 | public void CopyTo(AutomatonTransition[] array, int arrayIndex) { |
|
82 | 82 | m_transitions.CopyTo(array, arrayIndex); |
|
83 | 83 | } |
|
84 | 84 | |
|
85 | 85 | public bool Remove(AutomatonTransition item) { |
|
86 | 86 | return m_transitions.Remove(item); |
|
87 | 87 | } |
|
88 | 88 | |
|
89 | 89 | public int Count { |
|
90 | 90 | get { |
|
91 | 91 | return m_transitions.Count; |
|
92 | 92 | } |
|
93 | 93 | } |
|
94 | 94 | |
|
95 | 95 | public bool IsReadOnly { |
|
96 | 96 | get { |
|
97 | 97 | return false; |
|
98 | 98 | } |
|
99 | 99 | } |
|
100 | 100 | |
|
101 | 101 | public IEnumerator<AutomatonTransition> GetEnumerator() { |
|
102 | 102 | return m_transitions.GetEnumerator(); |
|
103 | 103 | } |
|
104 | 104 | |
|
105 | 105 | System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() { |
|
106 | 106 | return GetEnumerator(); |
|
107 | 107 | } |
|
108 | 108 | |
|
109 | 109 | public void AddSymbol(int symbol) { |
|
110 | 110 | Safe.ArgumentAssert(symbol >= 0, "symbol"); |
|
111 | 111 | m_symbolCount = Math.Max(symbol + 1, m_symbolCount); |
|
112 | 112 | } |
|
113 | 113 | |
|
114 | 114 | public int[,] CreateTransitionTable() { |
|
115 | 115 | var table = new int[StateCount,AlphabetSize]; |
|
116 | 116 | |
|
117 | 117 | for (int i = 0; i < StateCount; i++) |
|
118 | 118 | for (int j = 0; j < AlphabetSize; j++) |
|
119 |
table[i, j] = AutomatonConst.U |
|
|
119 | table[i, j] = AutomatonConst.UnreachableState; | |
|
120 | 120 | |
|
121 | 121 | foreach (var t in this) |
|
122 | table[t.s1,t.edge] = t.s2; | |
|
122 | table[t.s1,t.edge] = (byte)t.s2; | |
|
123 | 123 | |
|
124 | 124 | return table; |
|
125 | 125 | } |
|
126 | 126 | |
|
127 | 127 | public bool[] CreateFinalStateTable() { |
|
128 | 128 | var table = new bool[StateCount]; |
|
129 | 129 | |
|
130 | 130 | foreach (var s in FinalStates) |
|
131 | 131 | table[s] = true; |
|
132 | 132 | |
|
133 | 133 | return table; |
|
134 | 134 | } |
|
135 | 135 | |
|
136 | 136 | /// <summary>Формирует множества конечных состояний перед началом работы алгоритма минимизации.</summary> |
|
137 | 137 | /// <remarks> |
|
138 | 138 | /// В процессе построения минимального автомата требуется разделить множество состояний, |
|
139 | 139 | /// на два подмножества - конечные состояния и все остальные, после чего эти подмножества |
|
140 | 140 | /// будут резделены на более мелкие. Иногда требуется гарантировать различия конечных сосотяний, |
|
141 | 141 | /// для этого необходимо переопределить даннцю фукнцию, для получения множеств конечных состояний. |
|
142 | 142 | /// </remarks> |
|
143 | 143 | /// <returns>The final states.</returns> |
|
144 | 144 | protected virtual IEnumerable<HashSet<int>> SplitFinalStates(IEnumerable<int> states) { |
|
145 | 145 | return new [] { new HashSet<int>(states) }; |
|
146 | 146 | } |
|
147 | 147 | |
|
148 | 148 | protected void Optimize( |
|
149 | 149 | IDFATableBuilder optimalDFA, |
|
150 | 150 | IDictionary<int,int> alphabetMap, |
|
151 | 151 | IDictionary<int,int> stateMap |
|
152 | 152 | ) { |
|
153 | 153 | Safe.ArgumentNotNull(optimalDFA, "dfa"); |
|
154 | 154 | Safe.ArgumentNotNull(alphabetMap, "alphabetMap"); |
|
155 | 155 | Safe.ArgumentNotNull(stateMap, "stateMap"); |
|
156 | 156 | |
|
157 | 157 | |
|
158 | 158 | var setComparer = new CustomEqualityComparer<HashSet<int>>( |
|
159 | 159 | (x, y) => x.SetEquals(y), |
|
160 | 160 | s => s.Sum(x => x.GetHashCode()) |
|
161 | 161 | ); |
|
162 | 162 | |
|
163 | 163 | var optimalStates = new HashSet<HashSet<int>>(setComparer); |
|
164 | 164 | var queue = new HashSet<HashSet<int>>(setComparer); |
|
165 | 165 | |
|
166 | 166 | optimalStates.Add(new HashSet<int>(FinalStates)); |
|
167 | 167 | |
|
168 | 168 | var state = new HashSet<int>( |
|
169 | 169 | Enumerable |
|
170 | 170 | .Range(0, m_stateCount) |
|
171 | 171 | .Where(i => !m_finalStates.Contains(i)) |
|
172 | 172 | ); |
|
173 | 173 | |
|
174 | 174 | optimalStates.Add(state); |
|
175 | 175 | queue.Add(state); |
|
176 | 176 | |
|
177 | 177 | var rmap = m_transitions |
|
178 | 178 | .GroupBy(t => t.s2) |
|
179 | 179 | .ToDictionary( |
|
180 | 180 | g => g.Key, // s2 |
|
181 | 181 | g => g.ToLookup(t => t.edge, t => t.s1)//.ToDictionary(p => p.Key) |
|
182 | 182 | ); |
|
183 | 183 | |
|
184 | 184 | while (queue.Count > 0) { |
|
185 | 185 | var stateA = queue.First(); |
|
186 | 186 | queue.Remove(stateA); |
|
187 | 187 | |
|
188 | 188 | for (int c = 0; c < m_symbolCount; c++) { |
|
189 | 189 | var stateX = new HashSet<int>(); |
|
190 | 190 | foreach(var a in stateA.Where(rmap.ContainsKey)) |
|
191 | 191 | stateX.UnionWith(rmap[a][c]); // all states from wich the symbol 'c' leads to the state 'a' |
|
192 | 192 | |
|
193 | 193 | var tmp = optimalStates.ToArray(); |
|
194 | 194 | foreach (var stateY in tmp) { |
|
195 | 195 | var stateR1 = new HashSet<int>(stateY); |
|
196 | 196 | var stateR2 = new HashSet<int>(stateY); |
|
197 | 197 | |
|
198 | 198 | stateR1.IntersectWith(stateX); |
|
199 | 199 | stateR2.ExceptWith(stateX); |
|
200 | 200 | |
|
201 | 201 | if (stateR1.Count > 0 && stateR2.Count > 0) { |
|
202 | 202 | |
|
203 | 203 | |
|
204 | 204 | optimalStates.Remove(stateY); |
|
205 | 205 | optimalStates.Add(stateR1); |
|
206 | 206 | optimalStates.Add(stateR2); |
|
207 | 207 | |
|
208 | 208 | if (queue.Contains(stateY)) { |
|
209 | 209 | queue.Remove(stateY); |
|
210 | 210 | queue.Add(stateR1); |
|
211 | 211 | queue.Add(stateR2); |
|
212 | 212 | } else { |
|
213 | 213 | queue.Add(stateR1.Count <= stateR2.Count ? stateR1 : stateR2); |
|
214 | 214 | } |
|
215 | 215 | } |
|
216 | 216 | } |
|
217 | 217 | } |
|
218 | 218 | } |
|
219 | 219 | |
|
220 | 220 | // дополнительно разбиваем конечные состояния |
|
221 | 221 | foreach (var final in optimalStates.Where(s => s.Overlaps(m_finalStates)).ToArray()) { |
|
222 | 222 | optimalStates.Remove(final); |
|
223 | 223 | foreach (var split in SplitFinalStates(final)) |
|
224 | 224 | optimalStates.Add(split); |
|
225 | 225 | } |
|
226 | 226 | |
|
227 | 227 | |
|
228 | 228 | // карта получения оптимального состояния по соотвествующему ему простому состоянию |
|
229 | 229 | var nextState = 0; |
|
230 | 230 | foreach (var item in optimalStates) { |
|
231 | 231 | var id = nextState++; |
|
232 | 232 | foreach (var s in item) |
|
233 | 233 | stateMap[s] = id; |
|
234 | 234 | } |
|
235 | 235 | |
|
236 | 236 | // получаем минимальный алфавит |
|
237 | 237 | // входные символы не различимы, если Move(s,a1) == Move(s,a2), для любого s |
|
238 | 238 | // для этого используем алгоритм кластеризации, сначала |
|
239 | 239 | // считаем, что все символы не различимы |
|
240 | 240 | |
|
241 | 241 | var minClasses = new HashSet<HashSet<int>>(setComparer); |
|
242 | 242 | var alphaQueue = new Queue<HashSet<int>>(); |
|
243 | 243 | alphaQueue.Enqueue(new HashSet<int>(Enumerable.Range(0,AlphabetSize))); |
|
244 | 244 | |
|
245 | 245 | // для всех состояний, будем проверять каждый класс на различимость, |
|
246 | 246 | // т.е. символы различимы, если они приводят к разным состояниям |
|
247 | 247 | for (int s = 0 ; s < optimalStates.Count; s++) { |
|
248 | 248 | var newQueue = new Queue<HashSet<int>>(); |
|
249 | 249 | |
|
250 | 250 | foreach (var A in alphaQueue) { |
|
251 | 251 | // классы из одного символа делить бесполезно, переводим их сразу в |
|
252 | 252 | // результирующий алфавит |
|
253 | 253 | if (A.Count == 1) { |
|
254 | 254 | minClasses.Add(A); |
|
255 | 255 | continue; |
|
256 | 256 | } |
|
257 | 257 | |
|
258 | 258 | // различаем классы символов, которые переводят в различные оптимальные состояния |
|
259 | 259 | // optimalState -> alphaClass |
|
260 | 260 | var classes = new Dictionary<int, HashSet<int>>(); |
|
261 | 261 | |
|
262 | 262 | foreach (var term in A) { |
|
263 | 263 | // ищем все переходы класса по символу term |
|
264 | 264 | var s2 = m_transitions.Where(t => stateMap[t.s1] == s && t.edge == term).Select(t => stateMap[t.s2]).DefaultIfEmpty(-1).First(); |
|
265 | 265 | |
|
266 | 266 | HashSet<int> a2; |
|
267 | 267 | if (!classes.TryGetValue(s2, out a2)) { |
|
268 | 268 | a2 = new HashSet<int>(); |
|
269 | 269 | newQueue.Enqueue(a2); |
|
270 | 270 | classes[s2] = a2; |
|
271 | 271 | } |
|
272 | 272 | a2.Add(term); |
|
273 | 273 | } |
|
274 | 274 | } |
|
275 | 275 | |
|
276 | 276 | if (newQueue.Count == 0) |
|
277 | 277 | break; |
|
278 | 278 | alphaQueue = newQueue; |
|
279 | 279 | } |
|
280 | 280 | |
|
281 | 281 | // после окончания работы алгоритма в очереди останутся минимальные различимые классы |
|
282 | 282 | // входных символов |
|
283 | 283 | foreach (var A in alphaQueue) |
|
284 | 284 | minClasses.Add(A); |
|
285 | 285 | |
|
286 | 286 | // построение отображения алфавитов входных символов. |
|
287 | 287 | // поскольку символ DFAConst.UNCLASSIFIED_INPUT может иметь |
|
288 | 288 | // специальное значение, тогда сохраним минимальный класс, |
|
289 | 289 | // содержащий этот символ на томже месте. |
|
290 | 290 | |
|
291 | 291 | var nextCls = 0; |
|
292 | 292 | foreach (var item in minClasses) { |
|
293 |
if (nextCls == AutomatonConst.U |
|
|
293 | if (nextCls == AutomatonConst.UnclassifiedInput) | |
|
294 | 294 | nextCls++; |
|
295 | 295 | |
|
296 | 296 | // сохраняем DFAConst.UNCLASSIFIED_INPUT |
|
297 |
var cls = item.Contains(AutomatonConst.U |
|
|
297 | var cls = item.Contains(AutomatonConst.UnclassifiedInput) ? AutomatonConst.UnclassifiedInput : nextCls++; | |
|
298 | 298 | optimalDFA.AddSymbol(cls); |
|
299 | 299 | |
|
300 | 300 | foreach (var a in item) |
|
301 | 301 | alphabetMap[a] = cls; |
|
302 | 302 | } |
|
303 | 303 | |
|
304 | 304 | // построение автомата |
|
305 | 305 | optimalDFA.SetInitialState(stateMap[m_initialState]); |
|
306 | 306 | |
|
307 | 307 | foreach (var sf in m_finalStates.Select(s => stateMap[s]).Distinct()) |
|
308 | 308 | optimalDFA.MarkFinalState(sf); |
|
309 | 309 | |
|
310 | 310 | foreach (var t in m_transitions.Select(t => new AutomatonTransition(stateMap[t.s1],stateMap[t.s2],alphabetMap[t.edge])).Distinct()) |
|
311 | 311 | optimalDFA.Add(t); |
|
312 | 312 | } |
|
313 | 313 | |
|
314 | protected string PrintDFA<TInput, TState>(IAlphabet<TInput> inputAlphabet, IAlphabet<TState> stateAlphabet) { | |
|
314 | /*protected string PrintDFA<TInput, TState>(IAlphabet<TInput> inputAlphabet, IAlphabet<TState> stateAlphabet) { | |
|
315 | 315 | Safe.ArgumentNotNull(inputAlphabet, "inputAlphabet"); |
|
316 | 316 | Safe.ArgumentNotNull(stateAlphabet, "stateAlphabet"); |
|
317 | 317 |
|
|
318 | 318 | var data = new List<string>(); |
|
319 | 319 |
|
|
320 | 320 | data.Add("digraph dfa {"); |
|
321 | 321 |
|
|
322 | 322 | foreach (var final in m_finalStates) |
|
323 | 323 | data.Add(String.Format("{0} [shape=box];",String.Join("", stateAlphabet.GetSymbols(final)))); |
|
324 | 324 |
|
|
325 | 325 | foreach (var t in m_transitions) |
|
326 | 326 | data.Add(String.Format( |
|
327 | 327 | "{0} -> {2} [label={1}];", |
|
328 | 328 | String.Join("", stateAlphabet.GetSymbols(t.s1)), |
|
329 |
ToLiteral(ToLiteral(String.Join("", t.edge == AutomatonConst.U |
|
|
329 | ToLiteral(ToLiteral(String.Join("", t.edge == AutomatonConst.UnclassifiedInput ? new [] { "@" } : inputAlphabet.GetSymbols(t.edge).Select(x => x.ToString())))), | |
|
330 | 330 | String.Join("", stateAlphabet.GetSymbols(t.s2)) |
|
331 | 331 | )); |
|
332 | 332 | data.Add("}"); |
|
333 | 333 | return String.Join("\n", data); |
|
334 | 334 | } |
|
335 | 335 |
|
|
336 | 336 | static string ToLiteral(string input) |
|
337 | 337 | { |
|
338 | 338 | using (var writer = new StringWriter()) |
|
339 | 339 | { |
|
340 | 340 | using (var provider = CodeDomProvider.CreateProvider("CSharp")) |
|
341 | 341 | { |
|
342 | 342 | provider.GenerateCodeFromExpression(new CodePrimitiveExpression(input), writer, null); |
|
343 | 343 | return writer.ToString(); |
|
344 | 344 | } |
|
345 | 345 | } |
|
346 |
|
|
|
346 | }*/ | |
|
347 | 347 | } |
|
348 | 348 | } |
|
1 | NO CONTENT: file renamed from Implab/Automaton/IAlphabet.cs to Implab/src/Automaton/IAlphabet.cs |
|
1 | NO CONTENT: file renamed from Implab/Automaton/IAlphabetBuilder.cs to Implab/src/Automaton/IAlphabetBuilder.cs |
|
1 | NO CONTENT: file renamed from Implab/Automaton/IDFATable.cs to Implab/src/Automaton/IDFATable.cs |
|
1 | NO CONTENT: file renamed from Implab/Automaton/IDFATableBuilder.cs to Implab/src/Automaton/IDFATableBuilder.cs |
|
1 | NO CONTENT: file renamed from Implab/Automaton/IndexedAlphabetBase.cs to Implab/src/Automaton/IndexedAlphabetBase.cs |
@@ -1,84 +1,84 | |||
|
1 | 1 | using System; |
|
2 | 2 | using System.Collections.Generic; |
|
3 | 3 | using System.Linq; |
|
4 | 4 | |
|
5 | 5 | namespace Implab.Automaton { |
|
6 | 6 | public class MapAlphabet<T> : IAlphabetBuilder<T> { |
|
7 | 7 | readonly Dictionary<T,int> m_map; |
|
8 | 8 | int m_nextCls; |
|
9 | 9 | readonly bool m_supportUnclassified; |
|
10 | 10 | |
|
11 | 11 | public MapAlphabet(bool supportUnclassified, IEqualityComparer<T> comparer) { |
|
12 | 12 | m_map = comparer != null ? new Dictionary<T, int>(comparer) : new Dictionary<T,int>(); |
|
13 | 13 | m_supportUnclassified = supportUnclassified; |
|
14 | 14 | m_nextCls = supportUnclassified ? 1 : 0; |
|
15 | 15 | } |
|
16 | 16 | |
|
17 | 17 | #region IAlphabetBuilder implementation |
|
18 | 18 | |
|
19 | 19 | public int DefineSymbol(T symbol) { |
|
20 | 20 | int cls; |
|
21 | 21 | return m_map.TryGetValue(symbol, out cls) ? cls : DefineSymbol(symbol, m_nextCls); |
|
22 | 22 | } |
|
23 | 23 | |
|
24 | 24 | public int DefineSymbol(T symbol, int cls) { |
|
25 | 25 | Safe.ArgumentAssert(cls >= 0, "cls"); |
|
26 | 26 | |
|
27 | 27 | m_nextCls = Math.Max(cls + 1, m_nextCls); |
|
28 | 28 | m_map.Add(symbol, cls); |
|
29 | 29 | return cls; |
|
30 | 30 | } |
|
31 | 31 | |
|
32 | 32 | public int DefineClass(IEnumerable<T> symbols) { |
|
33 | 33 | return DefineClass(symbols, m_nextCls); |
|
34 | 34 | } |
|
35 | 35 | |
|
36 | 36 | public int DefineClass(IEnumerable<T> symbols, int cls) { |
|
37 | 37 | Safe.ArgumentAssert(cls >= 0, "cls"); |
|
38 | 38 | Safe.ArgumentNotNull(symbols, "symbols"); |
|
39 | 39 | |
|
40 | 40 | m_nextCls = Math.Max(cls + 1, m_nextCls); |
|
41 | 41 | |
|
42 | 42 | foreach (var symbol in symbols) |
|
43 | 43 | m_map[symbol] = cls; |
|
44 | 44 | return cls; |
|
45 | 45 | } |
|
46 | 46 | |
|
47 | 47 | #endregion |
|
48 | 48 | |
|
49 | 49 | #region IAlphabet implementation |
|
50 | 50 | |
|
51 | 51 | public int Translate(T symbol) { |
|
52 | 52 | int cls; |
|
53 | 53 | if (m_map.TryGetValue(symbol, out cls)) |
|
54 | 54 | return cls; |
|
55 | 55 | if (!m_supportUnclassified) |
|
56 | 56 | throw new ArgumentOutOfRangeException("symbol", "The specified symbol isn't in the alphabet"); |
|
57 |
return AutomatonConst.U |
|
|
57 | return AutomatonConst.UnclassifiedInput; | |
|
58 | 58 | } |
|
59 | 59 | |
|
60 | 60 | public int Count { |
|
61 | 61 | get { |
|
62 | 62 | return m_nextCls; |
|
63 | 63 | } |
|
64 | 64 | } |
|
65 | 65 | |
|
66 | 66 | public bool Contains(T symbol) { |
|
67 | 67 | return m_supportUnclassified || m_map.ContainsKey(symbol); |
|
68 | 68 | } |
|
69 | 69 | |
|
70 | 70 | |
|
71 | 71 | public IEnumerable<T> GetSymbols(int cls) { |
|
72 | 72 | Safe.ArgumentAssert(!m_supportUnclassified || cls > 0, "cls"); |
|
73 | 73 | return m_map.Where(p => p.Value == cls).Select(p => p.Key); |
|
74 | 74 | } |
|
75 | 75 | #endregion |
|
76 | 76 | |
|
77 | 77 | public IEnumerable<KeyValuePair<T,int>> Mappings { |
|
78 | 78 | get { |
|
79 | 79 | return m_map; |
|
80 | 80 | } |
|
81 | 81 | } |
|
82 | 82 | } |
|
83 | 83 | } |
|
84 | 84 |
|
1 | NO CONTENT: file renamed from Implab/Automaton/ParserException.cs to Implab/src/Automaton/ParserException.cs |
|
1 | NO CONTENT: file renamed from Implab/Automaton/RegularExpressions/AltToken.cs to Implab/src/Automaton/RegularExpressions/AltToken.cs |
|
1 | NO CONTENT: file renamed from Implab/Automaton/RegularExpressions/BinaryToken.cs to Implab/src/Automaton/RegularExpressions/BinaryToken.cs |
|
1 | NO CONTENT: file renamed from Implab/Automaton/RegularExpressions/CatToken.cs to Implab/src/Automaton/RegularExpressions/CatToken.cs |
|
1 | NO CONTENT: file renamed from Implab/Automaton/RegularExpressions/EmptyToken.cs to Implab/src/Automaton/RegularExpressions/EmptyToken.cs |
|
1 | NO CONTENT: file renamed from Implab/Automaton/RegularExpressions/EndToken.cs to Implab/src/Automaton/RegularExpressions/EndToken.cs |
|
1 | NO CONTENT: file renamed from Implab/Automaton/RegularExpressions/EndTokenT.cs to Implab/src/Automaton/RegularExpressions/EndTokenT.cs |
|
1 | NO CONTENT: file renamed from Implab/Automaton/RegularExpressions/ITaggedDFABuilder.cs to Implab/src/Automaton/RegularExpressions/ITaggedDFABuilder.cs |
|
1 | NO CONTENT: file renamed from Implab/Automaton/RegularExpressions/IVisitor.cs to Implab/src/Automaton/RegularExpressions/IVisitor.cs |
|
1 | NO CONTENT: file renamed from Implab/Automaton/RegularExpressions/RegularDFA.cs to Implab/src/Automaton/RegularExpressions/RegularDFA.cs | |
The requested commit or file is too big and content was truncated. Show full diff |
|
1 | NO CONTENT: file renamed from Implab/Automaton/RegularExpressions/RegularExpressionVisitor.cs to Implab/src/Automaton/RegularExpressions/RegularExpressionVisitor.cs | |
The requested commit or file is too big and content was truncated. Show full diff |
|
1 | NO CONTENT: file renamed from Implab/Automaton/RegularExpressions/RegularExpressionVisitorT.cs to Implab/src/Automaton/RegularExpressions/RegularExpressionVisitorT.cs |
|
1 | NO CONTENT: file renamed from Implab/Automaton/RegularExpressions/StarToken.cs to Implab/src/Automaton/RegularExpressions/StarToken.cs |
|
1 | NO CONTENT: file renamed from Implab/Automaton/RegularExpressions/SymbolToken.cs to Implab/src/Automaton/RegularExpressions/SymbolToken.cs |
|
1 | NO CONTENT: file renamed from Implab/Automaton/RegularExpressions/Token.cs to Implab/src/Automaton/RegularExpressions/Token.cs |
|
1 | NO CONTENT: file renamed from Implab/Components/Disposable.cs to Implab/src/Components/Disposable.cs | |
The requested commit or file is too big and content was truncated. Show full diff |
|
1 | NO CONTENT: file renamed from Implab/Components/DisposablePool.cs to Implab/src/Components/DisposablePool.cs | |
The requested commit or file is too big and content was truncated. Show full diff |
|
1 | NO CONTENT: file renamed from Implab/Components/ExecutionState.cs to Implab/src/Components/ExecutionState.cs | |
The requested commit or file is too big and content was truncated. Show full diff |
|
1 | NO CONTENT: file renamed from Implab/Components/IFactory.cs to Implab/src/Components/IFactory.cs |
|
1 | NO CONTENT: file renamed from Implab/Components/IInitializable.cs to Implab/src/Components/IInitializable.cs | |
The requested commit or file is too big and content was truncated. Show full diff |
|
1 | NO CONTENT: file renamed from Implab/Components/IRunnable.cs to Implab/src/Components/IRunnable.cs | |
The requested commit or file is too big and content was truncated. Show full diff |
|
1 | NO CONTENT: file renamed from Implab/Components/LazyAndWeak.cs to Implab/src/Components/LazyAndWeak.cs | |
The requested commit or file is too big and content was truncated. Show full diff |
|
1 | NO CONTENT: file renamed from Implab/Components/ObjectPool.cs to Implab/src/Components/ObjectPool.cs | |
The requested commit or file is too big and content was truncated. Show full diff |
|
1 | NO CONTENT: file renamed from Implab/Components/ServiceLocator.cs to Implab/src/Components/ServiceLocator.cs | |
The requested commit or file is too big and content was truncated. Show full diff |
|
1 | NO CONTENT: file renamed from Implab/CustomEqualityComparer.cs to Implab/src/CustomEqualityComparer.cs |
|
1 | NO CONTENT: file renamed from Implab/Formats/ByteAlphabet.cs to Implab/src/Formats/ByteAlphabet.cs |
|
1 | NO CONTENT: file renamed from Implab/Formats/CharAlphabet.cs to Implab/src/Formats/CharAlphabet.cs | |
The requested commit or file is too big and content was truncated. Show full diff |
|
1 | NO CONTENT: file renamed from Implab/Formats/Grammar.cs to Implab/src/Formats/Grammar.cs | |
The requested commit or file is too big and content was truncated. Show full diff |
|
1 | NO CONTENT: file renamed from Implab/Formats/JSON/JSONElementContext.cs to Implab/src/Formats/Json/JsonElementContext.cs | |
The requested commit or file is too big and content was truncated. Show full diff |
|
1 | NO CONTENT: file renamed from Implab/Formats/JSON/JSONElementType.cs to Implab/src/Formats/Json/JsonElementType.cs | |
The requested commit or file is too big and content was truncated. Show full diff |
|
1 | NO CONTENT: file renamed from Implab/Formats/JSON/JSONGrammar.cs to Implab/src/Formats/Json/JsonGrammar.cs | |
The requested commit or file is too big and content was truncated. Show full diff |
|
1 | NO CONTENT: file renamed from Implab/Formats/JSON/JSONParser.cs to Implab/src/Formats/Json/JsonReader.cs | |
The requested commit or file is too big and content was truncated. Show full diff |
|
1 | NO CONTENT: file renamed from Implab/Formats/JSON/JSONScanner.cs to Implab/src/Formats/Json/JsonScanner.cs | |
The requested commit or file is too big and content was truncated. Show full diff |
|
1 | NO CONTENT: file renamed from Implab/Formats/JSON/JsonTokenType.cs to Implab/src/Formats/Json/JsonTokenType.cs | |
The requested commit or file is too big and content was truncated. Show full diff |
|
1 | NO CONTENT: file renamed from Implab/Formats/JSON/JSONWriter.cs to Implab/src/Formats/Json/JsonWriter.cs | |
The requested commit or file is too big and content was truncated. Show full diff |
|
1 | NO CONTENT: file renamed from Implab/Formats/JSON/StringTranslator.cs to Implab/src/Formats/Json/StringTranslator.cs | |
The requested commit or file is too big and content was truncated. Show full diff |
|
1 | NO CONTENT: file renamed from Implab/IPromise.cs to Implab/src/IPromise.cs | |
The requested commit or file is too big and content was truncated. Show full diff |
|
1 | NO CONTENT: file renamed from Implab/IPromiseT.cs to Implab/src/IPromiseT.cs | |
The requested commit or file is too big and content was truncated. Show full diff |
|
1 | NO CONTENT: file renamed from Implab/Parallels/AsyncQueue.cs to Implab/src/Parallels/AsyncQueue.cs | |
The requested commit or file is too big and content was truncated. Show full diff |
|
1 | NO CONTENT: file renamed from Implab/Parallels/BlockingQueue.cs to Implab/src/Parallels/BlockingQueue.cs | |
The requested commit or file is too big and content was truncated. Show full diff |
|
1 | NO CONTENT: file renamed from Implab/Parallels/DispatchPool.cs to Implab/src/Parallels/DispatchPool.cs |
|
1 | NO CONTENT: file renamed from Implab/Parallels/SharedLock.cs to Implab/src/Parallels/SharedLock.cs |
|
1 | NO CONTENT: file renamed from Implab/Parallels/Signal.cs to Implab/src/Parallels/Signal.cs |
|
1 | NO CONTENT: file renamed from Implab/PromiseExtensions.cs to Implab/src/PromiseExtensions.cs | |
The requested commit or file is too big and content was truncated. Show full diff |
|
1 | NO CONTENT: file renamed from Implab/PromiseTransientException.cs to Implab/src/PromiseTransientException.cs |
|
1 | NO CONTENT: file renamed from Implab/Safe.cs to Implab/src/Safe.cs | |
The requested commit or file is too big and content was truncated. Show full diff |
|
1 | NO CONTENT: file was removed |
|
1 | NO CONTENT: file was removed |
|
1 | NO CONTENT: file was removed |
|
1 | NO CONTENT: file was removed |
|
1 | NO CONTENT: file was removed |
|
1 | NO CONTENT: file was removed |
|
1 | NO CONTENT: file was removed |
|
1 | NO CONTENT: file was removed |
|
1 | NO CONTENT: file was removed |
|
1 | NO CONTENT: file was removed |
|
1 | NO CONTENT: file was removed |
|
1 | NO CONTENT: file was removed |
|
1 | NO CONTENT: file was removed |
|
1 | NO CONTENT: file was removed |
|
1 | NO CONTENT: file was removed |
|
1 | NO CONTENT: file was removed |
|
1 | NO CONTENT: file was removed |
|
1 | NO CONTENT: file was removed |
|
1 | NO CONTENT: file was removed |
|
1 | NO CONTENT: file was removed |
|
1 | NO CONTENT: file was removed |
|
1 | NO CONTENT: file was removed |
|
1 | NO CONTENT: file was removed |
|
1 | NO CONTENT: file was removed |
|
1 | NO CONTENT: file was removed |
|
1 | NO CONTENT: file was removed | |
This diff has been collapsed as it changes many lines, (863 lines changed) Show them Hide them |
|
1 | NO CONTENT: file was removed |
|
1 | NO CONTENT: file was removed |
|
1 | NO CONTENT: file was removed |
|
1 | NO CONTENT: file was removed |
|
1 | NO CONTENT: file was removed |
|
1 | NO CONTENT: file was removed |
|
1 | NO CONTENT: file was removed |
|
1 | NO CONTENT: file was removed, binary diff hidden |
|
1 | NO CONTENT: file was removed |
|
1 | NO CONTENT: file was removed |
|
1 | NO CONTENT: file was removed |
|
1 | NO CONTENT: file was removed |
|
1 | NO CONTENT: file was removed |
|
1 | NO CONTENT: file was removed |
|
1 | NO CONTENT: file was removed |
|
1 | NO CONTENT: file was removed |
|
1 | NO CONTENT: file was removed |
|
1 | NO CONTENT: file was removed |
|
1 | NO CONTENT: file was removed |
|
1 | NO CONTENT: file was removed |
|
1 | NO CONTENT: file was removed |
|
1 | NO CONTENT: file was removed |
|
1 | NO CONTENT: file was removed |
|
1 | NO CONTENT: file was removed |
|
1 | NO CONTENT: file was removed |
|
1 | NO CONTENT: file was removed |
|
1 | NO CONTENT: file was removed |
|
1 | NO CONTENT: file was removed |
|
1 | NO CONTENT: file was removed |
|
1 | NO CONTENT: file was removed |
|
1 | NO CONTENT: file was removed |
|
1 | NO CONTENT: file was removed |
|
1 | NO CONTENT: file was removed |
|
1 | NO CONTENT: file was removed |
|
1 | NO CONTENT: file was removed |
|
1 | NO CONTENT: file was removed |
|
1 | NO CONTENT: file was removed |
|
1 | NO CONTENT: file was removed |
|
1 | NO CONTENT: file was removed |
|
1 | NO CONTENT: file was removed |
|
1 | NO CONTENT: file was removed |
|
1 | NO CONTENT: file was removed |
|
1 | NO CONTENT: file was removed |
|
1 | NO CONTENT: file was removed |
|
1 | NO CONTENT: file was removed |
|
1 | NO CONTENT: file was removed |
|
1 | NO CONTENT: file was removed |
|
1 | NO CONTENT: file was removed |
|
1 | NO CONTENT: file was removed |
|
1 | NO CONTENT: file was removed |
|
1 | NO CONTENT: file was removed |
|
1 | NO CONTENT: file was removed |
|
1 | NO CONTENT: file was removed |
|
1 | NO CONTENT: file was removed |
|
1 | NO CONTENT: file was removed |
|
1 | NO CONTENT: file was removed |
|
1 | NO CONTENT: file was removed |
|
1 | NO CONTENT: file was removed |
|
1 | NO CONTENT: file was removed |
|
1 | NO CONTENT: file was removed |
|
1 | NO CONTENT: file was removed |
|
1 | NO CONTENT: file was removed |
|
1 | NO CONTENT: file was removed |
|
1 | NO CONTENT: file was removed |
|
1 | NO CONTENT: file was removed |
|
1 | NO CONTENT: file was removed |
|
1 | NO CONTENT: file was removed |
|
1 | NO CONTENT: file was removed |
|
1 | NO CONTENT: file was removed |
|
1 | NO CONTENT: file was removed |
|
1 | NO CONTENT: file was removed |
|
1 | NO CONTENT: file was removed |
|
1 | NO CONTENT: file was removed |
|
1 | NO CONTENT: file was removed |
|
1 | NO CONTENT: file was removed | |
The requested commit or file is too big and content was truncated. Show full diff |
|
1 | NO CONTENT: file was removed | |
The requested commit or file is too big and content was truncated. Show full diff |
|
1 | NO CONTENT: file was removed | |
The requested commit or file is too big and content was truncated. Show full diff |
|
1 | NO CONTENT: file was removed | |
The requested commit or file is too big and content was truncated. Show full diff |
|
1 | NO CONTENT: file was removed | |
The requested commit or file is too big and content was truncated. Show full diff |
|
1 | NO CONTENT: file was removed | |
The requested commit or file is too big and content was truncated. Show full diff |
|
1 | NO CONTENT: file was removed, binary diff hidden |
|
1 | NO CONTENT: file was removed, binary diff hidden |
|
1 | NO CONTENT: file was removed | |
The requested commit or file is too big and content was truncated. Show full diff |
|
1 | NO CONTENT: file was removed | |
The requested commit or file is too big and content was truncated. Show full diff |
|
1 | NO CONTENT: file was removed | |
The requested commit or file is too big and content was truncated. Show full diff |
|
1 | NO CONTENT: file was removed | |
The requested commit or file is too big and content was truncated. Show full diff |
|
1 | NO CONTENT: file was removed | |
The requested commit or file is too big and content was truncated. Show full diff |
|
1 | NO CONTENT: file was removed, binary diff hidden |
|
1 | NO CONTENT: file was removed, binary diff hidden |
|
1 | NO CONTENT: file was removed | |
The requested commit or file is too big and content was truncated. Show full diff |
|
1 | NO CONTENT: file was removed, binary diff hidden |
|
1 | NO CONTENT: file was removed | |
The requested commit or file is too big and content was truncated. Show full diff |
|
1 | NO CONTENT: file was removed, binary diff hidden |
|
1 | NO CONTENT: file was removed | |
The requested commit or file is too big and content was truncated. Show full diff |
|
1 | NO CONTENT: file was removed, binary diff hidden |
|
1 | NO CONTENT: file was removed | |
The requested commit or file is too big and content was truncated. Show full diff |
|
1 | NO CONTENT: file was removed, binary diff hidden |
|
1 | NO CONTENT: file was removed | |
The requested commit or file is too big and content was truncated. Show full diff |
|
1 | NO CONTENT: file was removed, binary diff hidden |
|
1 | NO CONTENT: file was removed, binary diff hidden |
|
1 | NO CONTENT: file was removed | |
The requested commit or file is too big and content was truncated. Show full diff |
General Comments 0
You need to be logged in to leave comments.
Login now