Auto status change to "Under Review"
                Closed
            
        
        
    
    
            Pull request !2
            Created on
            Tue, 02 Feb 2021 11:50:08,
            
        
        
    - Fixed Safe.DisposeCollection NullReferenceException
- sync
- Добавлена метка v3.0.14 для набора изменений 95896f882995
- Added tests for Implab.ServiceHost.Unity configuration loader.
- Added IObservable to TraceRegistry
               Pull request versions not available.
               
           | ver | Time | Author | Commit | Description | ||
|---|---|---|---|---|---|---|
| 91 commits hidden, click expand to show them. | ||||||
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 | |
| @@ -0,0 +1,37 | |||
|  | 1 | using System; | |
|  | 2 | using System.Threading.Tasks; | |
|  | 3 | ||
|  | 4 | namespace Implab.Components { | |
|  | 5 | /// <summary> | |
|  | 6 | /// An interface for asynchronous components. | |
|  | 7 | /// </summary> | |
|  | 8 | /// <remarks> | |
|  | 9 | /// <para> | |
|  | 10 | /// Асинхронные компоненты не предназначены для одновременного использования несколькими клиентами, | |
|  | 11 | /// однако существуют внутренние процессы, изменяющее состояние компонент без участия клиента. | |
|  | 12 | /// Данный интерфейс определяет протокол взаимодействия с компонентой, при которм компоненте | |
|  | 13 | /// посылаются сигналы от клиента, в ответ на которые компонента меняет свойство <see cref="Completion"/>, | |
|  | 14 | /// данное свойство содержит в себе новую задачу, выполняемую компонентой и данное свойство | |
|  | 15 | /// может измениться только при получении нового сигнала от клиента. | |
|  | 16 | /// </para> | |
|  | 17 | /// <para> | |
|  | 18 | /// В дополнение к <see cref="Completion"/> компонента может определять другие свойства, в | |
|  | 19 | /// которых будет передаваться информация о результате выполнения операции. | |
|  | 20 | /// </para> | |
|  | 21 | /// <para> | |
|  | 22 | /// Особое внимание следует уделить реализации <see cref="IDisposable"/>, который по своей природе | |
|  | 23 | /// синхронный, данное правило безусловно можно нарушить, но тогда могут возникнуть проблемы с | |
|  | 24 | /// тем, что ресурсы еще не освободились, а ход программы продолжается, что приведет к ошибкам, | |
|  | 25 | /// например при попытке получить ресуср другим объектом, либо при заврешении программы. | |
|  | 26 | /// </para> | |
|  | 27 | /// <seealso href="https://blog.stephencleary.com/2013/01/async-oop-0-introduction.html"/> | |
|  | 28 | /// </remarks> | |
|  | 29 | public interface IAsyncComponent { | |
|  | 30 | /// <summary> | |
|  | 31 | /// The result of the last started operation. This property reflects | |
|  | 32 | /// only the result of the last started operation and therefore should | |
|  | 33 | /// change only if a new operation is initiated. | |
|  | 34 | /// </summary> | |
|  | 35 | Task Completion { get; } | |
|  | 36 | } | |
|  | 37 | } No newline at end of file | |
| @@ -0,0 +1,13 | |||
|  | 1 | using System; | |
|  | 2 | using System.Collections.Generic; | |
|  | 3 | using System.Linq; | |
|  | 4 | using System.Text; | |
|  | 5 | using System.Threading.Tasks; | |
|  | 6 | ||
|  | 7 | namespace Implab.Components { | |
|  | 8 | public interface IServiceLocator: IServiceProvider { | |
|  | 9 | T GetService<T>(); | |
|  | 10 | bool TryGetService<T>(out T service); | |
|  | 11 | bool TryGetService (Type serviceType, out object service); | |
|  | 12 | } | |
|  | 13 | } | |
|  | 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 | |||
| @@ -18,3 +18,12 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,56 +1,61 | |||
|  | 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 | " \"", | 
| @@ -66,22 +71,120 namespace Implab.Format.Test { | |||
|  | 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 | } | 
| @@ -1,13 +1,39 | |||
|  | 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 | } | 
| @@ -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> | |
| @@ -2,296 +2,126 | |||
|  | 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 | 
| @@ -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,7 +1,7 | |||
|  | 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; | 
| @@ -28,6 +28,14 namespace Implab.Automaton { | |||
|  | 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 | |
| @@ -20,7 +20,7 namespace Implab.Automaton { | |||
|  | 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 | } | 
| @@ -46,7 +46,7 namespace Implab.Automaton { | |||
|  | 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 | } | 
| @@ -57,9 +57,9 namespace Implab.Automaton { | |||
|  | 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); | 
| @@ -116,10 +116,10 namespace Implab.Automaton { | |||
|  | 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 | } | 
| @@ -290,11 +290,11 namespace Implab.Automaton { | |||
|  | 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) | 
| @@ -311,7 +311,7 namespace Implab.Automaton { | |||
|  | 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 |  | 
| @@ -326,7 +326,7 namespace Implab.Automaton { | |||
|  | 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("}"); | 
| @@ -343,6 +343,6 namespace Implab.Automaton { | |||
|  | 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 | 
| @@ -54,7 +54,7 namespace Implab.Automaton { | |||
|  | 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 { | 
|  | 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 | 
| @@ -77,14 +77,14 namespace Implab.Automaton.RegularExpres | |||
|  | 77 | 77 | return states.GroupBy(x => m_tags[x] ?? new TTag[0], arrayComparer).Select(g => new HashSet<int>(g)); | 
|  | 78 | 78 | } | 
|  | 79 | 79 | |
|  | 80 | public override string ToString() { | |
|  | 80 | /*public override string ToString() { | |
|  | 81 | 81 | var states = new MapAlphabet<string>(false, null); | 
|  | 82 | 82 |  | 
|  | 83 | 83 | for (int i = 0; i < StateCount; i++) | 
|  | 84 | 84 | states.DefineSymbol(string.Format("s{0}", i), i); | 
|  | 85 | 85 |  | 
|  | 86 | 86 | return string.Format("//[RegularDFA {1} x {2}]\n{0}", PrintDFA(InputAlphabet, states),StateCount, AlphabetSize); | 
|  | 87 |  | |
|  | 87 | }*/ | |
|  | 88 | 88 | |
|  | 89 | 89 | } | 
|  | 90 | 90 | } | 
| @@ -129,7 +129,7 namespace Implab.Automaton.RegularExpres | |||
|  | 129 | 129 | if (m_root == null) | 
|  | 130 | 130 | m_root = token; | 
|  | 131 | 131 | m_idx++; | 
|  | 132 | m_indexes[m_idx] = AutomatonConst.U | |
|  | 132 | m_indexes[m_idx] = AutomatonConst.UnclassifiedInput; | |
|  | 133 | 133 | m_firstpos = new HashSet<int>(new[] { m_idx }); | 
|  | 134 | 134 | m_lastpos = new HashSet<int>(new[] { m_idx }); | 
|  | 135 | 135 | Followpos(m_idx); | 
|  | 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,5 +1,6 | |||
|  | 1 | 1 | using Implab.Diagnostics; | 
|  | 2 | 2 | using System; | 
|  | 3 | using System.Diagnostics.CodeAnalysis; | |
|  | 3 | 4 | using System.Threading; | 
|  | 4 | 5 | |
|  | 5 | 6 | namespace Implab.Components { | 
| @@ -7,16 +8,11 namespace Implab.Components { | |||
|  | 7 | 8 | /// Base class the objects which support disposing. | 
|  | 8 | 9 | /// </summary> | 
|  | 9 | 10 | public class Disposable : IDisposable { | 
|  | 10 | ||
|  | 11 | int m_disposed; | |
|  | 12 | 11 | |
|  | 13 | 12 | public event EventHandler Disposed; | 
|  | 14 | 13 | |
|  | 15 | 14 | public bool IsDisposed { | 
|  | 16 | get | |
|  | 17 | Thread.MemoryBarrier(); | |
|  | 18 | return m_disposed != 0; | |
|  | 19 | } | |
|  | 15 | get; private set; | |
|  | 20 | 16 | } | 
|  | 21 | 17 | |
|  | 22 | 18 | /// <summary> | 
| @@ -24,43 +20,8 namespace Implab.Components { | |||
|  | 24 | 20 | /// </summary> | 
|  | 25 | 21 | /// <exception cref="ObjectDisposedException">The object is disposed</exception> | 
|  | 26 | 22 | /// <remarks> | 
|  | 27 | /// Успешная проверка того, что объект не освобожден еще не гарантирует, что он не | |
|  | 28 | /// будет освобожден сразу после нее, поэтому методы использующие проверку должны | |
|  | 29 | /// учитывать, что объект может быть освобожден из параллельного потока. | |
|  | 30 | /// Данны метод служит для упрощения отладки ошибок при использовании объекта после его | |
|  | 31 | /// освобождения. | |
|  | 32 | /// </remarks> | |
|  | 33 | /// <example> | |
|  | 34 | /// // пример синхронизированного освобождения ресурсов | |
|  | 35 | /// class FileStore : Disposable { | |
|  | 36 | /// readonly TextWriter m_file; | |
|  | 37 | /// readonly obejct m_sync = new object(); | |
|  | 38 | /// | |
|  | 39 | /// public FileStore(string file) { | |
|  | 40 | /// m_file = new TextWriter(File.OpenWrite(file)); | |
|  | 41 | /// } | |
|  | 42 | /// | |
|  | 43 | /// public void Write(string text) { | |
|  | 44 | /// lock(m_sync) { | |
|  | 45 | /// AssertNotDisposed(); | |
|  | 46 | /// m_file.Write(text); | |
|  | 47 | /// } | |
|  | 48 | /// } | |
|  | 49 | /// | |
|  | 50 | /// protected override void Dispose(bool disposing) { | |
|  | 51 | /// if (disposing) | |
|  | 52 | /// lock(m_sync) { | |
|  | 53 | /// m_file.Dipose(); | |
|  | 54 | /// base.Dispose(true); | |
|  | 55 | /// } | |
|  | 56 | /// else | |
|  | 57 | /// base.Dispose(false); | |
|  | 58 | /// } | |
|  | 59 | /// } | |
|  | 60 | /// <example> | |
|  | 61 | 23 | protected void AssertNotDisposed() { | 
|  | 62 | Thread.MemoryBarrier(); | |
|  | 63 | if (m_disposed != 0) | |
|  | 24 | if (IsDisposed) | |
|  | 64 | 25 | throw new ObjectDisposedException(ToString()); | 
|  | 65 | 26 | } | 
|  | 66 | 27 | /// <summary> | 
| @@ -74,30 +35,21 namespace Implab.Components { | |||
|  | 74 | 35 | /// из нескольких потоков. | 
|  | 75 | 36 | /// </remarks> | 
|  | 76 | 37 | protected virtual void Dispose(bool disposing) { | 
|  | 77 | if (disposing) | |
|  | 78 | EventHandler temp = Disposed; | |
|  | 79 | if (temp != null) | |
|  | 80 | temp(this, EventArgs.Empty); | |
|  | 81 | } | |
|  | 38 | if (disposing) | |
|  | 39 | Disposed.DispatchEvent(this, EventArgs.Empty); | |
|  | 82 | 40 | } | 
|  | 83 | 41 | |
|  | 42 | [SuppressMessage("Microsoft.Design", "CA1063:ImplementIDisposableCorrectly", Justification = "Dipose(bool) and GC.SuppessFinalize are called")] | |
|  | 84 | 43 | public void Dispose() { | 
|  | 85 | if (Interlocked.Increment(ref m_disposed) == 1) { | |
|  | 44 | if(!IsDisposed) { | |
|  | 45 | IsDisposed = true; | |
|  | 86 | 46 | Dispose(true); | 
|  | 87 | 47 | GC.SuppressFinalize(this); | 
|  | 88 | 48 | } | 
|  | 89 | 49 | } | 
|  | 90 | 50 | |
|  | 91 | /// <summary> | |
|  | 92 | /// Записывает сообщение об утечке объекта. | |
|  | 93 | /// </summary> | |
|  | 94 | protected virtual void ReportObjectLeaks() { | |
|  | 95 | TraceLog.TraceWarning("The object is marked as disposable but isn't disposed properly: {0}", this); | |
|  | 96 | } | |
|  | 97 | ||
|  | 98 | 51 | ~Disposable() { | 
|  | 99 | 52 | Dispose(false); | 
|  | 100 | ReportObjectLeaks(); | |
|  | 101 | 53 | } | 
|  | 102 | 54 | } | 
|  | 103 | 55 | } No newline at end of file | 
| @@ -6,10 +6,10 using System.Diagnostics.CodeAnalysis; | |||
|  | 6 | 6 | |
|  | 7 | 7 | namespace Implab.Components { | 
|  | 8 | 8 | /// <summary> | 
|  | 9 | /// The base class for implementing pools of disposable objects. | |
|  | 9 | /// The base class for implementing thread-safe pools of disposable objects. | |
|  | 10 | 10 | /// </summary> | 
|  | 11 | 11 | /// <remarks> | 
|  | 12 | /// <para>This class maintains a set of pre-created objects | |
|  | 12 | /// <para>This class maintains a set of pre-created objects which are frequently allocated and released | |
|  | 13 | 13 | /// by clients. The pool maintains maximum number of unsued object, any object above this limit is disposed, | 
|  | 14 | 14 | /// if the pool is empty it will create new objects on demand.</para> | 
|  | 15 | 15 | /// <para>Instances of this class are thread-safe.</para> | 
|  | 1 | NO CONTENT: file renamed from Implab/Components/IFactory.cs to Implab/src/Components/IFactory.cs | 
| @@ -1,21 +1,30 | |||
|  | 1 | 1 | using System; | 
|  | 2 | using System.Threading; | |
|  | 2 | 3 | |
|  | 3 | 4 | namespace Implab.Components { | 
|  | 4 | 5 | /// <summary> | 
|  | 5 | 6 | /// Initializable components are created and initialized in two steps, first we have create the component, | 
|  | 6 | /// then we have to complete it's creation by calling an <see cref="Init()"/> method. All parameters needed | |
|  | 7 | /// to complete the initialization must be passed before the calling <see cref="Init()"/> | |
|  | 7 | /// then we have to complete it's creation by calling an <see cref="Initialize()"/> method. All parameters needed | |
|  | 8 | /// to complete the initialization must be passed before the calling <see cref="Initialize()"/> | |
|  | 8 | 9 | /// </summary> | 
|  | 9 | 10 | public interface IInitializable { | 
|  | 10 | 11 | /// <summary> | 
|  | 11 | 12 | /// Completes initialization. | 
|  | 12 | 13 | /// </summary> | 
|  | 13 | 14 | /// <remarks> | 
|  | 15 | /// <para> | |
|  | 14 | 16 | /// Normally virtual methods shouldn't be called from the constructor, due to the incomplete object state, but | 
|  | 15 | 17 | /// they can be called from this method. This method is also usefull when we constructing a complex grpah | 
|  | 16 | 18 | /// of components where cyclic references may take place. | 
|  | 19 | /// </para> | |
|  | 20 | /// <para> | |
|  | 21 | /// In asyncronous patterns <see cref="Initialize()"/> can be called | |
|  | 22 | /// to start initialization and the <see cref="IRunnable.Completion"/> | |
|  | 23 | /// property can be used to track operation completion. | |
|  | 24 | /// </para> | |
|  | 17 | 25 | /// </remarks> | 
|  | 18 | void Init(); | |
|  | 26 | void Initialize(); | |
|  | 27 | void Initialize(CancellationToken ct); | |
|  | 19 | 28 | } | 
|  | 20 | 29 | } | 
|  | 21 | 30 | |
| @@ -1,13 +1,53 | |||
|  | 1 | 1 | using System; | 
|  | 2 | using System.Threading; | |
|  | 3 | using System.Threading.Tasks; | |
|  | 2 | 4 | |
|  | 3 | 5 | namespace Implab.Components { | 
|  | 6 | /// <summary> | |
|  | 7 | /// Interface for the component which performs a long running task. | |
|  | 8 | /// </summary> | |
|  | 9 | /// <remarks> | |
|  | 10 | /// The access to the runnable component should be sequential, the | |
|  | 11 | /// componet should support asynchronous completion of the initiated | |
|  | 12 | /// operation but operations itself must be initiated sequentially. | |
|  | 13 | /// </remarks> | |
|  | 4 | 14 | public interface IRunnable { | 
|  | 5 | IPromise Start(); | |
|  | 15 | /// <summary> | |
|  | 16 | /// Starts this instance | |
|  | 17 | /// </summary> | |
|  | 18 | /// <remarks> | |
|  | 19 | /// This operation is cancellable and it's expected to move to | |
|  | 20 | /// the failed state or just ignore the cancellation request, | |
|  | 21 | /// </remarks> | |
|  | 22 | void Start(); | |
|  | 23 | void Start(CancellationToken ct); | |
|  | 6 | 24 | |
|  | 7 | IPromise Stop(); | |
|  | 8 | ||
|  | 25 | /// <summary> | |
|  | 26 | /// Stops this instance and releases all resources, after the | |
|  | 27 | /// instance is stopped it is moved to Disposed state and | |
|  | 28 | /// can't be reused. | |
|  | 29 | /// </summary> | |
|  | 30 | /// <remarks> | |
|  | 31 | /// If the componet was in the starting state the pending operation | |
|  | 32 | /// will be requested to cancel. The stop operatin will be | |
|  | 33 | /// performed only if the component in the running state. | |
|  | 34 | /// </remarks> | |
|  | 35 | void Stop(); | |
|  | 36 | void Stop(CancellationToken ct); | |
|  | 37 | ||
|  | 38 | /// <summary> | |
|  | 39 | /// Current state of the componenet, dynamically reflects the current state. | |
|  | 40 | /// </summary> | |
|  | 9 | 41 | ExecutionState State { get; } | 
|  | 10 | 42 | |
|  | 43 | /// <summary> | |
|  | 44 | /// Event to monitor the state of the component. | |
|  | 45 | /// </summary> | |
|  | 46 | event EventHandler<StateChangeEventArgs> StateChanged; | |
|  | 47 | ||
|  | 48 | /// <summary> | |
|  | 49 | /// The last error | |
|  | 50 | /// </summary> | |
|  | 11 | 51 | Exception LastError { get; } | 
|  | 12 | 52 | } | 
|  | 13 | 53 | } | 
| @@ -8,6 +8,7 namespace Implab.Components { | |||
|  | 8 | 8 | /// <remarks> | 
|  | 9 | 9 | /// Usefull when dealing with memory-intensive objects which are frequently used. | 
|  | 10 | 10 | /// This class is similar to <see cref="ObjectPool{T}"/> except it is a singleton. | 
|  | 11 | /// This class can't be used to hold diposable objects. | |
|  | 11 | 12 | /// </remarks> | 
|  | 12 | 13 | public class LazyAndWeak<T> where T : class { | 
|  | 13 | 14 | |
| @@ -26,7 +26,7 namespace Implab.Components { | |||
|  | 26 | 26 | } | 
|  | 27 | 27 | |
|  | 28 | 28 | protected ObjectPool(int size) { | 
|  | 29 | Safe.ArgumentInRange(size, | |
|  | 29 | Safe.ArgumentInRange(size > 0, nameof(size)); | |
|  | 30 | 30 | |
|  | 31 | 31 | m_size = size; | 
|  | 32 | 32 | } | 
|  | 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 3
      
      ok, latest stable version should be in default
Pull request merged and closed
